Convert Gcode to arrays of pulses with Numpy

2021/01/21 categories:Python| tags:Python|G code|

I tried to convert G code to an array of pulses with Numpy.

Purpose

When G code for 3D printer is generated by Repetier, the printed part of the modeled object will be as follows.

G0 X86.4 Y86.4 Z0.3 F9000
G1 X113.6 Y86.4 E1.35701 F1800
.
.
.

Convert the above as follows.

1, 0, 0
1, 0, 0
1, 0, 0
.
.
.

As mentioned above, I would like to generate an array that enumerates the pulses to be sent to the motor driver for each of the XYZ axes with 0 and 1.

Processing content

Convert Gcode to an array of coordinates

Convert Gcode to an array of coordinates using the Gcode class created by Plot G code made by Repetier in PyQtGraph.

with open(filepath, 'r') as f:
    gcode = Gcode( f.read() )

gcode = np.delete( gcode.print_lines[1], [3, 4, 5], 1 )

You will get a numpy array of coordinates as follows:

[[ 86.4    86.4     0.3  ]
 [113.6    86.4     0.3  ]
 [113.6   113.6     0.3  ]
 ...
 [ 91.694  90.739  20.1  ]
 [ 91.128  90.739  20.1  ]
 [ 90.74   91.127  20.1  ]]

Take the difference of coordinates

Execute the following code to take the difference for each element from the numpy array of coordinates.

gcode_diff = np.diff(gcode, n=1, axis=0)

The result is as follows.

[[ 27.2     0.      0.   ]
 [  0.     27.2     0.   ]
 [-27.2     0.      0.   ]
 ...
 [  0.954  -0.954   0.   ]
 [ -0.566   0.      0.   ]
 [ -0.388   0.388   0.   ]]

Calculate the array of pulse numbers assuming timing belt drive

Assuming that the shaft is driven by the timing belt and pulley, calculate the coefficient to be multiplied by the difference in coordinates. The coefficient is obtained from the number of teeth and pitch of the pulley, the angle per pulse of the motor, and the number of microsteps, and the coefficient is created as a numpy array to be multiplied by the array of coordinate differences. Then, multiply this coefficient by the difference in coordinates to calculate the array of pulse numbers.

def mm_to_pulse_factor_for_belt_pulley(belt_pitch, pulley_tooth, motor_pulse_rate, microstep=1):
    '''
    belt_pitch [mm]
    pulley_tooth [T]
    motor_pulse_rate [pulse/rev]
    return []
    '''
    feed = belt_pitch * pulley_tooth # mm/rev
    factor = motor_pulse_rate * microstep / feed # [pulse/rev] / [mm/rev] = [pulse/mm]
    return factor

array_length = gcode_diff.shape[0]
factor_array = np.array([
    np.full( array_length, mm_to_pulse_factor_for_belt_pulley(2, 20, 200, 16) ),
    np.full( array_length, mm_to_pulse_factor_for_belt_pulley(2, 20, 200, 16) ),
    np.full( array_length, mm_to_pulse_factor_for_belt_pulley(2, 20, 200, 16) )
]).T

pulse_array = gcode_diff * factor_array

The calculation result is as follows, the first element is 2176 pulses of X, the second element is 2176 pulses of Y, and so on.

[[ 2176.       0.       0.  ]
 [    0.    2176.       0.  ]
 [-2176.       0.       0.  ]
 ...
 [   76.32   -76.32     0.  ]
 [  -45.28     0.       0.  ]
 [  -31.04    31.04     0.  ]]

Make the array of pulse numbers into an array of pulses

I created the following function to convert the number of pulses to an array of 01 pulses.

def pulse_count_to_pulse_array(pulse_count):
    count = int(max(np.abs(pulse_count)))
    if count == 0:
        return np.array([[0], [0], [0]])
    index = np.arange(count)
    arr = []
    for i in pulse_count:
        if i == 0:
            arr.append( np.zeros(count, np.int8) )
        else:
            tmp = np.mod( index, round(count / abs(i), 0) )
            tmp_max = tmp.max()
            if tmp_max == 0:
                arr.append( np.full(count, 1 * np.sign(i), np.int8) )
            else:
                tmp = tmp / tmp.max() * np.sign(i)
                arr.append( tmp.astype(np.int8) )
    return np.array(arr)

Then, the pulse array created by this function was combined as shown below to create the target array.

pulse_array = np.concatenate([ pulse_count_to_pulse_array(pulse_count) for pulse_count in pulse_counts], 1)

The created array is as follows.

[[ 1  1  1 ... -1 -1 -1]
 [ 0  0  0 ...  1  1  1]
 [ 0  0  0 ...  0  0  0]]

Source code

import numpy as np

class Gcode(object):

    def __init__(self, text):
        self.data = []
        self.count = 0
        self.lines = text.split('\n')
        self.start_lines, self.print_lines, self.end_lines = self.split_lines()

    def split_lines(self):
        def m107count(lines):
            count = 0
            for i, line in enumerate(lines):
                key = line.split(' ')[0]
                if key == 'M107':
                    count = count + 1
                if count > 1:
                    return i
        
        start = m107count(self.lines) + 1
        end = len(self.lines) - m107count(self.lines[::-1]) - 1

        start_lines = self.lines[:start]
        print_lines = self.lines_to_ndarray(self.lines[start:end])
        end_lines = self.lines[end:]

        return start_lines, print_lines, end_lines

    def lines_to_ndarray(self, print_lines):
        arr = [[0, 0, 0, 0, 0, 0]]
        keys = []

        for line in print_lines:
            splited = line.split(' ')
            d = { s[0].strip() : float(s[1:]) for s in splited[1:] }
            a = []
            for i, key in enumerate(['X', 'Y', 'Z', 'E', 'F', 'S']):
                if key in d:
                    a.append( d.get(key) )
                else:
                    a.append( arr[-1][i] )
            keys.append( splited[0] )
            arr.append(a)

        return [ keys, np.array(arr[1:]) ]

def mm_to_pulse_factor_for_belt_pulley(belt_pitch, pulley_tooth, motor_pulse_rate, microstep=1):
    '''
    belt_pitch [mm]
    pulley_tooth [T]
    motor_pulse_rate [pulse/rev]
    return []
    '''
    feed = belt_pitch * pulley_tooth # mm/rev
    factor = motor_pulse_rate * microstep / feed # [pulse/rev] / [mm/rev] = [pulse/mm]
    return factor

def get_pulse_counts_from_gcode(filepath):
    with open(filepath, 'r') as f:
        gcode = Gcode( f.read() )

    # delete other than xyz
    gcode = np.delete( gcode.print_lines[1], [3, 4, 5], 1 )

    # diff elements
    gcode_diff = np.diff(gcode, n=1, axis=0)
    
    # convert factor mm to pulse count
    array_length = gcode_diff.shape[0]
    factor_array = np.array([
        np.full( array_length, mm_to_pulse_factor_for_belt_pulley(2, 20, 200, 16) ),
        np.full( array_length, mm_to_pulse_factor_for_belt_pulley(2, 20, 200, 16) ),
        np.full( array_length, mm_to_pulse_factor_for_belt_pulley(2, 20, 200, 16) )
    ]).T

    # mm to pulse count
    pulse_array = gcode_diff * factor_array

    return np.round(pulse_array)

def pulse_count_to_pulse_array(pulse_count):
    count = int(max(np.abs(pulse_count)))
    if count == 0:
        return np.array([[0], [0], [0]])
    index = np.arange(count)
    arr = []
    for i in pulse_count:
        if i == 0:
            arr.append( np.zeros(count, np.int8) )
        else:
            tmp = np.mod( index, round(count / abs(i), 0) )
            tmp_max = tmp.max()
            if tmp_max == 0:
                arr.append( np.full(count, 1 * np.sign(i), np.int8) )
            else:
                tmp = tmp / tmp.max() * np.sign(i)
                arr.append( tmp.astype(np.int8) )
    return np.array(arr)

def main():

    pulse_counts = get_pulse_counts_from_gcode('programs/data/XYZcube.gco')
    
    pulse_array = np.concatenate([ pulse_count_to_pulse_array(pulse_count) for pulse_count in pulse_counts], 1)

    print(pulse_array)

if __name__ == '__main__':
    main()

Share post

Related Posts

コメント