14,703 views
1. やってみたいこと
Raspberry Piの ハードウェア PWMを使ってサーボモーターを廻してみたい。
2. ハードPWM vs ソフトPWM
(1) ハードPWM
PWMコントローラ(=PWM専用のH/W)で制御するため、指定したパルス幅を安定して維持できる。
(2) ソフトPWM
CPUで制御するため、CPUが高負荷状態のときに PWM周波数が狂ったりパルス幅が広がったりする。
(3) どちらがよいのか?
目的に応じて使い分ければよい。
LED点灯の明るさを PWMで制御する場合、多少パルス幅がぶれても人の目にはよくわからない。
サーボモーターを PWMで制御する場合、パルス幅が一定でないと回転角度が小刻みにぶれてしまう。
3. パーツを調達
(1) モーターを購入
(2) モーターの仕様
このモーターの仕様は以下の通り。
https://akizukidenshi.com/goodsaffix/SG90_a.pdf
まとめると以下の通り。
・周波数 : 50Hz(1周期が 20ms)
・Duty Cycle 0.50ms : -90度回転
・Duty Cycle 1.45ms : 0度回転
・Duty Cycle 2.40ms : +90度回転
・電圧 : 4.8V ~ 5V
・回転速度 : 0.1s / 60度
4. 設計
(1) GPIOはどう使う?
ラズパイ公式ページの情報によれば、ハードPWMを使いたければ GPIO12,13,18,19を使えと記載されている。
https://www.raspberrypi.org/documentation/usage/gpio/README.md
なので…
今回は GPIO12 をサーボモーター制御のための PWM信号線として使う。
でも…
GPIO12の電圧は 3.3Vだ。
上記のモーターのデータシートには PWM信号線の電圧は 4.8V ~ 5Vと書いてあるがよいのか?
(2) モーターは別電源で駆動する。
消費電力の大きなモーターの電源をラズパイから取ると、ラズパイに大きな電流が流れて壊れてしまうかも…
この制限値はどこに書かれているのか?
公式ページでは見つけられなかったが、こちらのページ(↓)では
https://elinux.org/RPi_Low-level_peripherals#Power_pins
・3.3Vのピンからは合計 50mA
・5Vのピンからは合計 1A からラズパイの動作に必要な分を引いた残りだけ
と書かれている。
50mAといったら LED x 3個しか繋げちゃダメってことか?
よくわからないので…
消費電力の大きなデバイスを駆動するときには、ラズパイとは別に電源を用意した方がよさそうだ。
今回はモーター駆動用に 5Vの電源を別に用意 しよう。
5. 実装
・ラズパイとは別に、サーボモーター用に 5Vの電源を用意する。
・サーボモーターの GNDを ラズパイ側の GNDに繋ぐ。(GNDの電位を共通にするため)
6. マイコンで制御してみる!
今回も (23) 【1-3】マイコンで電子工作入門:LED点滅 と同じように RPi.GPIO を使用する。
(1) ターミナルを起動し、Python3を実行する。
python3
(2) 必要な Pythonモジュール(=パッケージ)を読み込む。
import RPi.GPIO as GPIO
(3) GPIOを初期化する。
GPIO制御関数で制御対象のピンを指定する際に、ピン番号ではなく GPIO番号を指定することにする。
・ピン番号指定: GPIO.BOARD
・GPIO番号指定: GPIO.BCM
GPIO.setmode(GPIO.BCM)
GPIO12を出力ポートとして使用する。
GPIO.setup(12, GPIO.OUT)
GPIO12を PWM(50Hz)として使用する。
pwm = GPIO.PWM(12, 50) pwm.start(0)
(4) モーターを廻す。
0度
前述のモーターのデータシートに従えば、
・0度の Duty Cycleは 1.45ms
・周波数 50Hz(=20ms)
なので、設定するパルス幅は…
1.45 / 20 = 7.25[%]
pwm.ChangeDutyCycle(7.25)
ここをホームポジションとして記憶する。
+90度
前述のモーターのデータシートに従えば
・+90度の Duty Cycleは 2.4ms
・周波数 50Hz(=20ms)
なので、設定するパルス幅は…
2.4 / 20 = 12[%]
pwm.ChangeDutyCycle(12)
半時計周りに 90度回転した。
-90度
前述のモーターのデータシートに従えば、
・-90度の Duty Cycleは 0.5ms
・周波数 50Hz(=20ms)
なので、設定するパルス幅は…
0.5 / 20 = 2.5[%]
pwm.ChangeDutyCycle(2.5)
時計周りに 180度回転した。
+45度
前述のモーターのデータシートに従えば、
・0度の Duty Cycleは 1.45ms
・+90度の Duty Cycleは 2.4ms
・周波数 50Hz(=20ms)
なので、パルス幅は…
+45度の Duty Cycleは
(2.4 – 1.45) / 90 x 45 + 1.45 = 1.925[ms] ※リニアだとすれば
なので、設定するパルス幅は…
1.925 / 20 = 9.6[%]
pwm.ChangeDutyCycle(9.6)
確かに 45度の位置まで回転した。
(5) 終了処理
pwm.stop() GPIO.cleanup()
問題発生!
意図した通り、確かにモーターは指定した角度に回転してくれた。
でも…
勝手に動く…
ビリビリと音を立てて振動したり、突然豪快に 180度回転したりする。
パルスの立ち上がりタイミングや幅など時間軸方向で乱れる ジッタ というやつか?
ToDo: ACアダプターが 5V 1.5Aの物を使用していた。電流値 2Aなどの大きな物に変更して試してみる。
回りすぎて振り切って戻ってこなくもなった。
モーターが動かなくなり「壊れたか…」と覚悟したが、これは無理やり手で回して復旧させた。
ひょっとしてソフトPWMで動いている?
RPi.GPIO.PWM(12, 50)
RPi.GPIOモジュールのこの使い方では、ハード PWM制御をしてくれていない?
オシロで PWM信号線を覗き見ると 20ms周期でパルスが流れ、パルス幅が乱れているようには見えない。
こんな状態でも不安定&豪快に回転したりする。
・パルス幅が一定範囲からはみ出したとき、
・信号の立ち上がりのタイミングがずれたとき、
にトリガーをかけてオシロの表示を停止させてみたいが、できない…
ソフトPWMで本当におかしな信号を出力しているのか? が確認できない…
(5) ハードウェアPWMでモーターを廻す。【仕切り直し】
RPi.GPIOの中身を未確認だが、RPi.GPIO.PWM() はソフトPWMであることはほぼ間違いない。
そこで…
wiringpi でハード PWMが出来るという情報に辿り付き、これを試してみる。
Super Userで Pythonを起動する。
sudo python
Python Shellで以下を打ち込む。
import wiringpi wiringpi.wiringPiSetupGpio() wiringpi.pinMode(18, wiringpi.GPIO.PWM_OUTPUT) wiringpi.pwmSetMode(wiringpi.GPIO.PWM_MODE_MS) wiringpi.pwmSetRange(1024) wiringpi.pwmSetClock(375) wiringpi.pwmWrite(18, 74) # 0deg. (1024 x 7.25% = 74) wiringpi.pwmWrite(18, 123) # +90deg. (1024 x 12% = 123) wiringpi.pwmWrite(18, 26) # -90deg. (1024 x 2.5% = 26)
・PWM_MODE_MS : PWM周波数固定モードを選択する。 参考情報
・pwmSetRange(1024) : Duty Cycle = 100% のときに 1024とする。
・pwmSetClock(375) : PWM周波数 = ラズパイ内蔵PWMコントローラの動作クロック周波数{19.2MHz} / (PWM信号の周波数{50Hz} x レンジ{1024})
今度はモーターが ピタッ と止まり、ビリビリしびれたような動きは全く見せない。
これがハード PWMの安定感だ。すばらしい!
(6) もし自分でちゃんと作りたければ…
ラズパイに搭載されているマイコンの仕様書を見て、PWMコントローラのドライバを自作する。(P.138 ~)
https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
wiringpi の中ではこの制御をしているのだろうから、プログラムの中身を覗いて制御方法を理解するのもよい。
いつか余裕が出来たらやってみよう。
【7】Raspberry Pi 5での実装 (2024.11.23追記)
(1) 愚直に手動でPWM制御する場合
import lgpio import time SERVO_PIN = 12 chip = lgpio.gpiochip_open(0) lgpio.gpio_claim_output(chip, SERVO_PIN) # PWM信号を手動で生成 # - frequency : PWM周波数(Hz) # - duty_cycle : デューティサイクル(0〜100) # - duration : PWM信号を出力する時間(秒) def generate_pwm(chip, pin, frequency, duty_cycle, duration): period = 1.0 / frequency # 周期(秒) high_time = period * (duty_cycle / 100.0) # HIGHTの時間 low_time = period - high_time # LOW の時間 end_time = time.time() + duration while time.time() < end_time: lgpio.gpio_write(chip, pin, 1); time.sleep(high_time) lgpio.gpio_write(chip, pin, 0); time.sleep(low_time) # サーボモーターを指定した角度に移動 # - angle: 0〜180度の範囲で指定 def set_angle(angle): pulse_width = 0.5 + (angle / 180.0 * 1.9) # 0度=0.5ms, 180度=2.4ms → 範囲は 1.9 duty_cycle = (pulse_width / 20.0) * 100 # 周波数50Hz(周期20ms) print(f"(PW, DC) = (%.3f, %.3f)" % (pulse_width, duty_cycle)) generate_pwm(chip, SERVO_PIN, 50, duty_cycle, 0.5) # 50HzでPWM生成 try: while True: print("0度に移動") set_angle(0) time.sleep(1) print("90度に移動") set_angle(90) time.sleep(1) print("180度に移動") set_angle(180) time.sleep(1) except KeyboardInterrupt: print("終了します") finally: lgpio.gpiochip_close(chip)
(2) gpiozeroパッケージのPWM制御クラスを利用する場合
from gpiozero import PWMOutputDevice from time import sleep # GPIO12をPWMデバイスとして設定 servo = PWMOutputDevice(12, frequency=50) # 周波数50Hz(SG-90は50Hzで動作) # サーボモーターを指定した角度に移動 # - angle: 0〜180度の範囲で指定 def set_angle(angle): # パルス幅をデューティサイクルに変換 (500us〜2400usを2.5%〜12.0%にマッピング) duty_cycle = (500 + (angle / 180.0) * 1900) / 20000.0 servo.value = duty_cycle # デューティサイクルを設定 sleep(0.5) # サーボの移動待機 servo.off() # 振動防止のためPWM信号を停止 try: while True: print("0度に移動") set_angle(0) sleep(1) print("90度に移動") set_angle(90) sleep(1) print("180度に移動") set_angle(180) sleep(1) except KeyboardInterrupt: print("終了します") finally: servo.off()
【8】所感
今回はモーターがおかしな挙動を見せたので、不良品なのではないかと疑ってしまった。
実際にはラズパイのソフトウェア PWMの不安定さがモーターの誤作動となって表れていた。
※近いうちに「5V出力昇圧DCDCコンバーター」を使ってPWM信号の電圧を上げて実験してみよう。
それに比べてハードウェア PWMは安定感がすばらしい!
いつかラジコンカーのステアリングに使ってみたい。
また、PWMを使えば、アナログデバイスをデジタル信号で制御できる。
例えば、
・LEDの明るさを好きに変える。
・モーターの回転速度を好きに変える。
など、各デバイスに入力する電圧を変えなくても制御できてしまう。
いつかきっと何かの役に立つだろう。
いつかきっとラジコンが作りたくなるはず…
参考情報
ありがとうございます。 m(_ _)m
https://www.slideshare.net/YoshimotoYukiyoshi/raspberry-pipwm
連載
Raspberry Piの初期設定から簡単な動作実験まで、以下はマイコン制御入門の連載です。
(21) 【連載1-1】マイコンで電子工作入門:選定
(22) 【連載1-2】マイコンで電子工作入門:ラズパイ初期設定
(23) 【連載1-3】マイコンで電子工作入門:LED点滅
(24) 【連載1-4】マイコンで電子工作入門:時報を作る
(26) 【連載1-5】マイコンで電子工作入門:サーボモーターを廻す
(27) 【連載1-6】マイコンで電子工作入門:合成音声で時報 ←次回
サーボへの信号レベルが足らないです。
ソフトPWMは悪くないかと、
ありがとうございます。
ソフトPWM, ハードPWMともに出力信号レベルは 3.3Vなのです…
このため、時間軸の方を疑ってしまうのです。