ラズパイでGコードからパルス列を生成してステッピングモータを駆動してみた
2021/01/23 categories:Python| tags:Python|PyQt5|G code|Raspberry Pi|
NumpyでGコードをパルスの配列に変換してみたのプログラムにラズパイのGPIO出力のプログラムを追加して、Gコードを元にステッピングモータを駆動してみました。
回路構成
回路構成のイメージは下記の通りで、XY軸の2軸駆動を想定しています。
GPIO出力
ラズパイのIO出力にはpigpioを使い、モータードライバにはDRV8825を使いました。ドライバへの入力はSTEP、DIRの2つでラズパイのIOはどちらも出力に設定します。今回はXYの2軸を動かすのでIOは4つ使用します。IOの入出力設定は以下の通りです。
pi = pigpio.pi()
pi.set_mode(X, pigpio.OUTPUT)
pi.set_mode(Y, pigpio.OUTPUT)
pi.set_mode(X_DIR, pigpio.OUTPUT)
pi.set_mode(Y_DIR, pigpio.OUTPUT)
パルスの出力は単純にtime.sleep()でwaitを入れて出力しました。wait時間は一定にならないかもしれませんが、テストとしてtime.sleep()でパルスを生成してみました。1回のパルス出力の処理は下記の通りです。
x_val, y_val = pulse[0], pulse[1]
pi.write( X_DIR, int((np.sign(x_val) + 1) / 2) )
pi.write( Y_DIR, int((np.sign(y_val) + 1) / 2) )
pi.write( X, abs(x_val) )
pi.write( Y, abs(y_val) )
time.sleep(velocity)
pi.write(X, 0)
pi.write(Y, 0)
time.sleep(velocity)
このパルス出力の処理を全パルス分行ってステッピングモータを駆動します。
動作の様子
先日作ったGコードをパルス列の配列に変換するプログラムにGPIO出力を追加してRaspberry Pi 4 Model BとDRV8825とNEMA17のステッピングモータで動作させたら3Dプリンタっぽい動きした。簡単なXYロボットのハードを用意してXYの動きとして確認してみよう。 pic.twitter.com/bN9vZoUdWX
— ymt-lab (@ymt_lab) January 23, 2021
ソースコード
import numpy as np
import pigpio, time
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, 1) ),
np.full( array_length, mm_to_pulse_factor_for_belt_pulley(2, 20, 200, 1) ),
np.full( array_length, mm_to_pulse_factor_for_belt_pulley(2, 20, 200, 1) )
]).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 move_motor(X, Y, X_DIR, Y_DIR, pulse_array, velocity):
pi = pigpio.pi()
pi.set_mode(X, pigpio.OUTPUT)
pi.set_mode(Y, pigpio.OUTPUT)
pi.set_mode(X_DIR, pigpio.OUTPUT)
pi.set_mode(Y_DIR, pigpio.OUTPUT)
try:
print(pulse_array.shape)
for pulse in pulse_array:
x_val, y_val = pulse[0], pulse[1]
pi.write( X_DIR, int((np.sign(x_val) + 1) / 2) )
pi.write( Y_DIR, int((np.sign(y_val) + 1) / 2) )
pi.write( X, abs(x_val) )
pi.write( Y, abs(y_val) )
time.sleep(velocity)
pi.write(X, 0)
pi.write(Y, 0)
time.sleep(velocity)
#print(pulse)
except(KeyboardInterrupt): # when ctrl+c is pressed
pi.write(X, 0)
pi.write(Y, 0)
pi.stop()
print('\nstop motor')
def main():
print('calcurate pulse array')
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)
pulse_array = pulse_array.T
print('move motor')
move_motor(17, 27, 22, 23, pulse_array, 0.002)
if __name__ == '__main__':
main()