Remap 3D Cubic Spline Interpolation code by Python

More
19 Sep 2024 11:01 #310440 by jurod
Hi, I don't know how to create something functional. My idea is as follows:
Create a Gcod remap for example G5.4/5.5 (start and end of block in gcode)
Using the project from BigJohnT from this page forum.linuxcnc.org/21-axis/30986-axis-position-logger?start=100
After creating the gcode, I would like to easily edit this code to spline using the remap code.
Original from position Logger:
G40  G80 G98 
M6  T1  G43            
S 20000 M3 


G1 Z-100.0 F5000
G1  X583.8160   Y2785.3929   Z-80.0100  B-0.0060   C-180.6420   
G1   X569.5160   Y2551.3630   Z-20.8590  B-0.0060  C-180.6420    
G1   X569.5160   Y2551.3630   Z-534.4340  B-0.0060 C-180.6420   
G1   X569.5160   Y2551.3630   Z-563.0960  B-0.0060 C-180.6420   
G1   X587.9240   Y2551.3630   Z-562.6310  B-0.0060 C-180.6420   
G1   X602.8020   Y2531.2934   Z-547.8260  B-0.0060 C-180.6420   
G1   X616.4620   Y2550.4623   Z-563.0180  B-0.0060 C-180.6420   
G1   X630.8120   Y2550.4623   Z-563.9780  B-0.0060 C-180.6420   
G1   X601.7700   Y1631.0440   Z-563.0410  B-0.0060 C-180.6420   
G1   X586.6920   Y1633.2648   Z-561.9940  B-0.0060 C-180.6420   
G1   X574.5840   Y1656.0775   Z-546.8950  B-0.0060 C-180.6420   
G1   X560.3560   Y1634.1061   Z-561.9290  B-0.0060 C-180.6420   
G1   X545.8120   Y1634.7045   Z-562.6420  B-0.0060 C-180.6420   
G1   X575.0200   Y2556.2819   Z-563.3090  B-0.0060 C-180.6420   
G1   X575.0200   Y2556.2819   Z-543.9050  B-0.0060 C-180.6420   
;
G1 Z-100.0 F20000
G1   X583.8160   Y2785.3929   Z-80.0100  B-0.0060 C-180.6420   

M2
Edited:
G40  G80 G98 
M6  T1  G43            
S 20000 M3 


G1 Z-100.0 F5000
G5.4    X583.8160   Y2785.3929   Z-80.0100  B-0.0060  C-180.6420   
    X569.5160   Y2551.3630   Z-20.8590  B-0.0060  C-180.6420        
    X569.5160   Y2551.3630   Z-534.4340  B-0.0060 C-180.6420   
    X569.5160   Y2551.3630   Z-563.0960  B-0.0060 C-180.6420   
    X587.9240   Y2551.3630   Z-562.6310  B-0.0060 C-180.6420   
    X602.8020   Y2531.2934   Z-547.8260  B-0.0060 C-180.6420   
    X616.4620   Y2550.4623   Z-563.0180  B-0.0060 C-180.6420   
    X630.8120   Y2550.4623   Z-563.9780  B-0.0060 C-180.6420   
    X601.7700   Y1631.0440   Z-563.0410  B-0.0060 C-180.6420   
    X586.6920   Y1633.2648   Z-561.9940  B-0.0060 C-180.6420   
    X574.5840   Y1656.0775   Z-546.8950  B-0.0060 C-180.6420   
    X560.3560   Y1634.1061   Z-561.9290  B-0.0060 C-180.6420   
    X545.8120   Y1634.7045   Z-562.6420  B-0.0060 C-180.6420   
    X575.0200   Y2556.2819   Z-563.3090  B-0.0060 C-180.6420   
G5.5    X575.0200   Y2556.2819   Z-543.9050  B-0.0060 C-180.6420   
;
G1 Z-100.0 F20000
G1   X583.8160   Y2785.3929   Z-80.0100  B-0.0060 C-180.6420   

M2
From site stackoverflow.com/questions/31543775/how...on/48085583#48085583
Example Python cede:
[code]def my_cubic_interp1d(x0, x, y):
    """
    Interpolate a 1-D function using cubic splines.
      x0 : a 1d-array of floats to interpolate at
      x : a 1-D array of floats sorted in increasing order
      y : A 1-D array of floats. The length of y along the
          interpolation axis must be equal to the length of x.

    Implement a trick to generate at first step the cholesky matrice L of
    the tridiagonal matrice A (thus L is a bidiagonal matrice that
    can be solved in two distinct loops).

    additional ref: www.math.uh.edu/~jingqiu/math4364/spline.pdf 
    # original function code at: https://stackoverflow.com/a/48085583/36061
    
    
    This function is licenced under: Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
    https://creativecommons.org/licenses/by-sa/3.0/
    Original Author raphael valentin
    Date 3 Jan 2018
    
    
    Modifications made to remove numpy dependencies:
        -all sub-functions by MR
        
    This function, and all sub-functions, are licenced under: Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)        
        
    Mod author: Matthew Rowles
    Date 3 May 2021
    
    """
    def diff(lst):
        """
        numpy.diff with default settings
        """
        size = len(lst)-1
        r = [0]*size
        for i in range(size):
            r[i] = lst[i+1] - lst[i] 
        return r
    
    def list_searchsorted(listToInsert, insertInto):
        """
        numpy.searchsorted with default settings
        """
        def float_searchsorted(floatToInsert, insertInto):
            for i in range(len(insertInto)):
                if floatToInsert <= insertInto[i]:
                    return i
            return len(insertInto)
        return [float_searchsorted(i, insertInto) for i in listToInsert]
    
    def clip(lst, min_val, max_val, inPlace = False):    
        """
        numpy.clip
        """
        if not inPlace:
            lst = lst[:]  
        for i in range(len(lst)):
            if lst[i] < min_val:
                lst[i] = min_val
            elif lst[i] > max_val:
                lst[i] = max_val  
        return lst
    
    def subtract(a,b):
        """
        returns a - b
        """
        return a - b
    
    size = len(x)

    xdiff = diff(x)
    ydiff = diff(y)

    # allocate buffer matrices
    Li   = [0]*size
    Li_1 = [0]*(size-1)
    z    = [0]*(size)

    # fill diagonals Li and Li-1 and solve [L][y] = [B]
    Li[0]   = sqrt(2*xdiff[0])
    Li_1[0] = 0.0
    B0      = 0.0 # natural boundary
    z[0]    = B0 / Li[0]

    for i in range(1, size-1, 1):
        Li_1[i] = xdiff[i-1] / Li[i-1]
        Li[i] = sqrt(2*(xdiff[i-1]+xdiff[i]) - Li_1[i-1] * Li_1[i-1])
        Bi = 6*(ydiff[i]/xdiff[i] - ydiff[i-1]/xdiff[i-1])
        z[i] = (Bi - Li_1[i-1]*z[i-1])/Li[i]

    i = size - 1
    Li_1[i-1] = xdiff[-1] / Li[i-1]
    Li[i]     = sqrt(2*xdiff[-1] - Li_1[i-1] * Li_1[i-1])
    Bi        = 0.0 # natural boundary
    z[i]      = (Bi - Li_1[i-1]*z[i-1])/Li[i]

    # solve [L.T][x] = [y]
    i = size-1
    z[i] = z[i] / Li[i]
    for i in range(size-2, -1, -1):
        z[i] = (z[i] - Li_1[i-1]*z[i+1])/Li[i]

    # find index
    index = list_searchsorted(x0,x)
    index = clip(index, 1, size-1)

    xi1 = [x[num]   for num in index]
    xi0 = [x[num-1] for num in index]
    yi1 = [y[num]   for num in index]
    yi0 = [y[num-1] for num in index]
    zi1 = [z[num]   for num in index]
    zi0 = [z[num-1] for num in index]
    hi1 = list( map(subtract, xi1, xi0) )

    # calculate cubic - all element-wise multiplication
    f0 = [0]*len(hi1)
    for j in range(len(f0)):
        f0[j] = zi0[j]/(6*hi1[j])*(xi1[j]-x0[j])**3 + \
                zi1[j]/(6*hi1[j])*(x0[j]-xi0[j])**3 + \
                (yi1[j]/hi1[j] - zi1[j]*hi1[j]/6)*(x0[j]-xi0[j]) + \
                (yi0[j]/hi1[j] - zi0[j]*hi1[j]/6)*(xi1[j]-x0[j])        
    
    return f0
[/code]

I'm not a python programmer. Is it possible to make something similar work?
Attachments:

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

More
19 Sep 2024 15:08 - 19 Sep 2024 15:53 #310461 by Aciera
I cannot comment on the example python code.
A python remap can certainly parse a file of logged point coordinates, do calculations on those points and then output the results as, say, a list of G1 X.. Y.. Z.. moves and write these into a ngc subroutine that is called later in the gcode.
The issue with python is the time it takes to do all these things so if you expect to be able to do something like this:

G01 X.. Y.. Z.. F...
G5.4 ...

and expect this to blend without pause then that is likely not possible because of the lag inherent to python. However if you can pause gcode execution for the time it takes the python script to do its thing then I don't see a problem.

In a python remap controlling the read-ahead is VERY important. The first thing you want to do is stop the read-ahead before it ingests the python remap. Hence there won't be any blending between the Gcode before the remap and the remap itself.
Last edit: 19 Sep 2024 15:53 by Aciera.

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

More
19 Sep 2024 17:36 #310467 by Todd Zuercher
I don't think the way to do this is with a g-code remap. Seems to me it should be job of a file filter that reads and translates the g-code file on loading. That is perfectly acceptable as a python script, or even a bash sed script. Then the time to process is only at file loading and it doesn't get in the way of the running realtime code.

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

More
20 Sep 2024 09:01 #310502 by jurod
You are probably right. I didn't realize the delay in the code, but that's not a bug. It can be a few seconds.
The method according to Todd is also a solution.
I'm not sure which way to go.
I don't know how to work well in Python (only a couple of basic simple things and editing)
Is there anyone who would help me with this problem?

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

More
20 Sep 2024 10:46 - 20 Sep 2024 10:47 #310503 by Aciera
If you don't need to log the points and calculate the interpolation in the same gcode then a filter is likely easier. The other thing is that your python script is for a planar curve (eg XY) but you want to interpolate in 3D. I'm not familiar with cubic interpolation for a 3d curve so I'm not sure if it would require interpolating XY and XZ separately and then combine the two.
If you had a clear description of how to calculate the way points for the interpolated curve (even if it's just in words) I might be able to put something together but I don't have the time to research the math right now.
Last edit: 20 Sep 2024 10:47 by Aciera.

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

More
20 Sep 2024 10:56 #310504 by Aciera
There was a fairly long discussion about 3d splines/clothoids here:
forum.linuxcnc.org/38-general-linuxcnc-q...lib?start=500#305223

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

Time to create page: 0.096 seconds
Powered by Kunena Forum