HAL Component to bicubic interpolation of a matrix

More
18 Jul 2024 21:17 #305598 by EragonPower
Hello everyone,
I'm currently trying to correct a warp in the ways of my machine using a matrix and applying it at external offset.
The thought process is the following: have a XY matrix that contains how much the Z axis should move so that the bed of the machine results flat(I've probed this matrix considering that the bed of my diy CNC is flat as it has just been machined from an outside CNC), I load the matrix into a rt HAL component, the Hal component gets the XY coordinates of the machine, does bicubic interpolation on the matrix to get an accurate Z offset, than I get that offset and put it into external offsets.

Pretty straight forward, no?
Well, not exactly. Not fully understanding how to code HAL components using .comp, I coded this component in C, language that I barely understand better. 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hal.h"
#include "rtapi.h"
#include "rtapi_app.h"
#include "rtapi_math.h"

#define MAX_ROWS 240

typedef struct {
    hal_float_t *x_coord;
    hal_float_t *y_coord;
    hal_float_t *z_output;
    float x_values[MAX_ROWS];
    float y_values[MAX_ROWS];
    float z_values[MAX_ROWS];
    int rows;
} z_offset_t;

static z_offset_t *comp_data;
static int comp_id;

// Function to load the CSV file
void load_csv(const char *filename) {
    FILE *file = fopen(filename, "r");
    if (!file) {
        rtapi_print_msg(RTAPI_MSG_ERR, "Error opening CSV file\n");
        return;
    }

    comp_data->rows = 0;
    while (fscanf(file, "%f,%f,%f",
                  &comp_data->x_values[comp_data->rows],
                  &comp_data->y_values[comp_data->rows],
                  &comp_data->z_values[comp_data->rows]) == 3) {
        comp_data->rows++;
        if (comp_data->rows >= MAX_ROWS) {
            break;
        }
    }

    fclose(file);
}

// Helper function to clamp values to a range
int clamp(int value, int min, int max) {
    if (value < min) return min;
    if (value > max) return max;
    return value;
}

// Function to perform cubic interpolation
float cubicInterpolate(float p[4], float x) {
    return p[1] + 0.5 * x * (p[2] - p[0] + x * (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] + x * (3.0 * (p[1] - p[2]) + p[3] - p[0])));
}

// Function to perform bicubic interpolation
float bicubicInterpolate(float p[4][4], float x, float y) {
    float arr[4];
    arr[0] = cubicInterpolate(p[0], y);
    arr[1] = cubicInterpolate(p[1], y);
    arr[2] = cubicInterpolate(p[2], y);
    arr[3] = cubicInterpolate(p[3], y);
    return cubicInterpolate(arr, x);
}

// Function to interpolate the Z value using bicubic interpolation
float interpolate(float x, float y) {
    int i, j;
    for (i = 0; i < comp_data->rows - 1; i++) {
        if (x < comp_data->x_values[i + 1]) break;
    }
    for (j = 0; j < comp_data->rows - 1; j++) {
        if (y < comp_data->y_values[j + 1]) break;
    }

    // If coordinates are out of bounds, return 0.0
    if (x < comp_data->x_values[0] || x > comp_data->x_values[comp_data->rows - 1] ||
        y < comp_data->y_values[0] || y > comp_data->y_values[comp_data->rows - 1]) {
        return 0.0;
    }

    float p[4][4];
    for (int m = -1; m <= 2; m++) {
        for (int n = -1; n <= 2; n++) {
            int xi = clamp(i + m, 0, comp_data->rows - 1);
            int yj = clamp(j + n, 0, comp_data->rows - 1);
            p[m + 1][n + 1] = comp_data->z_values[xi];
        }
    }

    float x1 = comp_data->x_values;
    float x2 = comp_data->x_values[i + 1];
    float y1 = comp_data->y_values[j];
    float y2 = comp_data->y_values[j + 1];

    float x_frac = (x - x1) / (x2 - x1);
    float y_frac = (y - y1) / (y2 - y1);

    return bicubicInterpolate(p, x_frac, y_frac);
}

// Update function to read the current X and Y coordinates, interpolate the Z value, and set the Z output
void update(void *arg, long period) {
    if (!comp_data || !comp_data->x_coord || !comp_data->y_coord || !comp_data->z_output) {
        rtapi_print_msg(RTAPI_MSG_ERR, "Null pointer in update function\n");
        return;
    }

    float x = *(comp_data->x_coord);
    float y = *(comp_data->y_coord);

    rtapi_print_msg(RTAPI_MSG_INFO, "Update called with x: %f, y: %f\n", x, y);

    *(comp_data->z_output) = interpolate(x, y);

    rtapi_print_msg(RTAPI_MSG_INFO, "Interpolated z value: %f\n", *(comp_data->z_output));
}

// Component initialization
int rtapi_app_main(void) {
    comp_id = hal_init("z_offset");
    if (comp_id < 0) {
        rtapi_print_msg(RTAPI_MSG_ERR, "hal_init() failed\n");
        return -1;
    }

    comp_data = hal_malloc(sizeof(z_offset_t));
    if (!comp_data) {
        rtapi_print_msg(RTAPI_MSG_ERR, "hal_malloc() failed\n");
        return -1;
    }

    memset(comp_data, 0, sizeof(z_offset_t)); // Ensure comp_data is zero-initialized

    int res = hal_pin_float_new("z_offset.x_coord", HAL_IN, &comp_data->x_coord), comp_id);
    if (res < 0) return res;

    res = hal_pin_float_new("z_offset.y_coord", HAL_IN, &comp_data->y_coord), comp_id);
    if (res < 0) return res;

    res = hal_pin_float_new("z_offset.z_output", HAL_OUT, &comp_data->z_output), comp_id);
    if (res < 0) return res;

    load_csv("/home/linuxcnc/Desktop/CNC/offset_matrix.csv");

    hal_export_funct("z_offset.update", update, comp_data, 1, 0, comp_id);
    hal_ready(comp_id);

    return 0;
}

// Component cleanup
void rtapi_app_exit(void) {
    hal_exit(comp_id);
}

Now. that the code does is to get the matrix from a .csv file (it consists in a three column x 240 rows matrix, containing the x,y,z column and the relative values), get the xy coordinates of the machine, and interpolate the corrisponding z value.

Using this component results in strange values, ranging from absurdly huge ones, to very tiny (in the order of 10E-42) numbers. Has someone an idea on what is messing with this code?

At some point i thought that having negative z values in the input matrix might cause errors, so i've added 10 to the input z value, and subtracted it afterwards, but with no difference, so i've removed it from the code.

I hope that someone more skilled than me could give me some info to move forward.

Thanks a lot in advance to everyone that looks into this
 

Please Log in or Create an account to join the conversation.

More
19 Jul 2024 05:48 #305613 by Aciera

Please Log in or Create an account to join the conversation.

Time to create page: 0.107 seconds
Powered by Kunena Forum