格安エンコーダをRaspberry Piで使う
- 2020.07.11
- Raspberry Pi
Amazonにエンコーダ付きDCモータが売っていたので使ってみました。
パーツ
今回の準備したパーツは下記の通りです。
エンコーダについて
実はエンコーダを使うのは初めてで、色々と調べました。なんとなく原理は知っていましたが、下記のサイトはイラスト付きでとてもわかり易いと思います。
回転の方向を知るには前のA相、B相の状態を記録しておいて現在の値と比較する必要があります。
また、角度の計算では1回転当たりのパルス数が必要ですが、今回の場合は3 PPRでした。そして減速比も必要なのですが、何故か書いてなかったので、秋月電子で売っていたギヤードモータを参考にしました。
プログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
import RPi.GPIO as GPIO import RPi.GPIO as GPIO import time pin_a=14 pin_b=15 pin_pwm=2 pin_m1=17 pin_m2=27 pwm=None prev_data=0 angle=0. delta=360./(4*3*298) def main(): init() try: while(True): motor_cntl() time.sleep(0.1) except KeyboardInterrupt: print ("break") GPIO.remove_event_detect(pin_a) GPIO.remove_event_detect(pin_b) GPIO.cleanup() def init(): GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(pin_a, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(pin_b, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(pin_pwm, GPIO.OUT) GPIO.setup(pin_m1, GPIO.OUT) GPIO.setup(pin_m2, GPIO.OUT) global pwm, prev_a, prev_b pwm=GPIO.PWM(pin_pwm, 100) pwm.start(100) GPIO.add_event_detect(pin_a, GPIO.BOTH, callback=callback) GPIO.add_event_detect(pin_b, GPIO.BOTH, callback=callback) GPIO.output(pin_m1, GPIO.LOW) GPIO.output(pin_m2, GPIO.HIGH) def motor_cntl(): global angle if angle>180: GPIO.output(pin_m1, GPIO.HIGH) GPIO.output(pin_m2, GPIO.LOW) elif angle<-180: GPIO.output(pin_m1, GPIO.LOW) GPIO.output(pin_m2, GPIO.HIGH) def callback(gpio_pin): global angle, prev_data current_a=GPIO.input(pin_a) current_b=GPIO.input(pin_b) encoded=(current_a<<1)|current_b sum=(prev_data<<2)|encoded print(bin(sum)) if (sum==0b0010 or sum==0b1011 or sum==0b1101 or sum==0b0100): angle+=delta print ("plus", gpio_pin, angle) elif(sum==0b0001 or sum==0b0111 or sum==0b1110 or sum==0b1000): angle-=delta print ("minus", gpio_pin, angle) prev_data=encoded if __name__ == "__main__": main() |
上記プログラムのdeltaが角度計算に使う増加量となっています。減速比は298としています。エンコーダのA相、B相の値はコールバック関数を設定して立ち上がり、もしくは立ち下がり時に角度計算を行うようにします。また、モーターの制御として±180度に達したら回転の向きを逆にします。
モータドライバを使うプログラムについては下記の記事にも記載しています。
動作
やっとエンコーダ使えるようになった pic.twitter.com/4vV89KfMdD
— Ken@エンジニア投資家 (@Ken_okinawan) July 11, 2020
配線はごちゃごちゃしていますが、ちゃんと動きました。
所感
今回のプログラムを作成した時、最初は正しく動きませんでした。1方向にしか動かしていないのに、向きを計算すると5回に1回くらい逆向きだと判定する事があり、角度も正しく出ませんでした。安物だから壊れてるのかと思ってしまいましたが、実際にはGPIOのセットアップの時にpull_up_down=GPIO.PUD_UPを指定すると安定しました。今まで使ったことが無い設定なので、勉強になりました。
#2021.09.12追記
もう少し便利に使えるようにクラス化しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
import RPi.GPIO as GPIO class Encoder: def __init__(self,a,b,pwm,m1,m2): self.pin_a=a self.pin_b=b self.pin_pwm=pwm self.pin_m1=m1 self.pin_m2=m2 self.pwm=None self.prev_data=0 self.angle=0. self.delta=360./(45*3*4) GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(self.pin_a, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.pin_b, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.pin_pwm, GPIO.OUT) GPIO.setup(self.pin_m1, GPIO.OUT) GPIO.setup(self.pin_m2, GPIO.OUT) self.pwm=GPIO.PWM(self.pin_pwm, 100) self.pwm.start(100) GPIO.add_event_detect(self.pin_a, GPIO.BOTH, callback=self.callback) GPIO.add_event_detect(self.pin_b, GPIO.BOTH, callback=self.callback) def callback(self,gpio_pin): current_a=GPIO.input(self.pin_a) current_b=GPIO.input(self.pin_b) encoded=(current_a<<1)|current_b sum=(self.prev_data<<2)|encoded print (current_a, current_b) if (sum==0b0010 or sum==0b1011 or sum==0b1101 or sum==0b0100): self.angle+=self.delta print ("plus", gpio_pin, self.angle) elif(sum==0b0001 or sum==0b0111 or sum==0b1110 or sum==0b1000): self.angle-=self.delta print ("minus", gpio_pin, self.angle) self.prev_data=encoded def move1(self): GPIO.output(self.pin_m1, GPIO.HIGH) GPIO.output(self.pin_m2, GPIO.LOW) def move2(self): GPIO.output(self.pin_m1, GPIO.LOW) GPIO.output(self.pin_m2, GPIO.HIGH) def stop(self): GPIO.output(self.pin_m1, GPIO.LOW) GPIO.output(self.pin_m2, GPIO.LOW) def close(self): GPIO.remove_event_detect(self.pin_a) GPIO.remove_event_detect(self.pin_b) GPIO.cleanup() |
以下のような感じで使えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
import motor_encoder import RPi.GPIO as GPIO import time m1=None cnt=0 def main(): global m1 m1=motor_encoder.Encoder(2,3,22,17,27) m1.move1() try: while(True): motor_cntl(m1) time.sleep(0.1) except KeyboardInterrupt: m1.close() def motor_cntl(m1): global cnt if cnt==5: m1.stop() return 0 if m1.angle>180: m1.move1() cnt+=1 elif m1.angle<-180: m1.move2() cnt+=1 if __name__ == "__main__": main() |
-
前の記事
WordPressで下書きをプレビューすると404になる 2020.06.28
-
次の記事
Raspberry PiにROSをインストール 2020.07.19