ゆるエンジニアはいろいろ遊びたい

FAエンジニアが週末にいろいろ遊ぶブログです

Raspberry Pi5でサーボモーターSG90を動かす

ラズパイ5でサーボモーターSG90を動かしてみました。
配線は
赤:2番ピン(5V)
茶:6番ピン(0V)
黄:12番ピン(GPIO18)
です。

コードは

from gpiozero import Servo
from time import sleep


servo = Servo(18, min_pulse_width=0.5/1000, max_pulse_width=2.5/1000)

while True:
    servo.value = -1.0   #(0°)
    sleep(1)
    servo.value = 0.0   # (90°)
    sleep(1)
    servo.value = 1.0   #(180°)
    sleep(1)

実行すると動きましたが、停止した時にジジジジと僅かに振動します。
ジッターと呼ばれる症状で、ソフトウェアPWMだと発生するらしい。
そのためハードウェアPWMで動作させる必要があるのですが、ハードウェアPWMを使用するためには、はじめにIOの割り当てをしなければなりません。
ラズパイ5はハードウェアPWMを2軸使用することができるようです。
PWM1:GPIO12,18,40,52
PWM2:GPIO13,19,41,53
設定するには、ターミナルを開いて、下記を入力

sudo nano /boot/firmware/config.txt

すると、nanoというエディターが開くので、末尾に以下を追記します。

[all]
dtoverlay=pwm-2chan

Ctrl + Oの後、ENTERを押すと保存。Ctrl + Xで閉じます。その後リブートすると適用されます。
適用されたかを確認するには、ターミナルで

pinctl -p

と入力すると、現在割り当てられたIOの項目が表示されます。

 1: 3v3
 2: 5v
 3: no    pu | -- // GPIO2 = none
 4: 5v
 5: no    pu | -- // GPIO3 = none
 6: gnd
 7: no    pu | -- // GPIO4 = none
 8: no    pd | -- // GPIO14 = none
 9: gnd
10: no    pd | -- // GPIO15 = none
11: no    pd | -- // GPIO17 = none
12: a3    pd | lo // GPIO18 = PWM0_CHAN2
13: no    pd | -- // GPIO27 = none
14: gnd
15: no    pd | -- // GPIO22 = none
16: no    pd | -- // GPIO23 = none
17: 3v3
18: no    pd | -- // GPIO24 = none
19: no    pd | -- // GPIO10 = none
20: gnd
21: no    pd | -- // GPIO9 = none
22: no    pd | -- // GPIO25 = none
23: no    pd | -- // GPIO11 = none
24: no    pu | -- // GPIO8 = none
25: gnd
26: no    pu | -- // GPIO7 = none
27: ip    pu | hi // GPIO0 = input
28: ip    pu | hi // GPIO1 = input
29: no    pu | -- // GPIO5 = none
30: gnd
31: no    pu | -- // GPIO6 = none
32: no    pd | -- // GPIO12 = none
33: no    pd | -- // GPIO13 = none
34: gnd
35: a3    pd | lo // GPIO19 = PWM0_CHAN3
36: no    pd | -- // GPIO16 = none
37: no    pd | -- // GPIO26 = none
38: no    pd | -- // GPIO20 = none
39: gnd
40: no    pd | -- // GPIO21 = none

ここで、「12: a3 pd | lo // GPIO18 = PWM0_CHAN2」と「35: a3 pd | lo // GPIO19 = PWM0_CHAN3」にPWMが割り当てられたとわかります。
GPIO18はCHAN2,GPIO19はCHAN3ということを覚えておいてください。
ハードウェアPWMを使う場合は、コードも変わります。
ライブラリのインストールが必要なので、仮想環境下でrpi_hardware.pwmをインストールします。

pip install rpi_hardware.pwm

pythonコードは以下の通り

from rpi_hardware_pwm import HardwarePWM
import time


pwm = HardwarePWM(pwm_channel=2, hz=50)
pwm.start(0)
step = 0

while True:
    pwm.change_duty_cycle(2.5) # 0
    time.sleep(1)  
    pwm.change_duty_cycle(7.5) # 90度
    time.sleep(1)
    pwm.change_duty_cycle(12.5) # 180度
    time.sleep(1)

pwm_channelに、接続したピンが割り当てられたPWMのチャンネル番号を指定します。
GPIO18 = PWM0_CHAN2でしたので、チャンネル番号は2です。
hz=50は、SG90の仕様が20msの周期で動作するため、逆数の50hzを設定します。(周波数=1/周期)
角度の指定は、デューティ比で指定します。SG90は、20msの周期の中に0.5msがONしていると0°、2.5msがONしていると180°動きます。
デューティー比というのは、周期に対するONする時間の割合(パルス幅/周期)ですので、0°は0.5/20*100=2.5、180°は2.5/20*100=12.5という風な計算です。
実行すると、ソフトウェアPWMよりもはるかに滑らかな動作を確認できました。
SG90は速度の変更ができないので、ゆっくり動かすには、少しずつステップさせて動かす制御をすることで、疑似的にスローな動作をさせることができます。

from rpi_hardware_pwm import HardwarePWM
import time

pwm = HardwarePWM(pwm_channel=2, hz=50)
pwm.start(0)
step = 0

while True:
    #ステップ動作(疑似的なスイープ動作)
    for step in range(0, 180, 1): # 0度から180度まで1度ずつ増加
        sita = 5/90*step + 2.5 # 0度から180度まで5/90ずつ増加
        pwm.change_duty_cycle(sita)
        time.sleep(0.01)
    for step in range(180, 0, -10):
        sita = 5/90*step + 2.5
        pwm.change_duty_cycle(sita)
        time.sleep(0.1)

range(0, 180, 1)や、time.sleep(0.01)の数字を変えることで、自分好みの動作が可能となりました。