## 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()
```