読者です 読者をやめる 読者になる 読者になる

Raspberry Pi 備忘録 / Mbedもあるよ!

Raspberry Pi であれこれやった事の記録

サーボモーター(SG-90)を試す

配線

マイクロサーボ9g SG-90
[SG-90]
通販コード M-08761
発売日 2014/12/09
メーカーカテゴリ Tower Pro Pte Ltd
◆主な仕様
・PWMサイクル:20mS
・制御パルス:0.5ms~2.4ms
・制御角:±約90°(180°)
・配線:茶=GND、赤=電源[+]、橙=制御信号 [JRタイプ]
・トルク:1.8kgf・cm
・動作速度:0.1秒/60度
・動作電圧:4.8V(~5V)
・温度範囲:0℃~55℃
・外形寸法:22.2x11.8x31mm
・重量:9g

と、有るが、真ん中がどうにも赤に見えなくて不安になる!

真ん中の赤を 5Vに配線して、茶色をGNDに、橙色をGPIOのどっかに刺す。今回はGPIO21にした。

テックシェアストア(TechShareStore)
windvoice.hatenablog.jp
上記を参考に、下記のスクリプトを書いた。

#!/usr/bin/python
# coding: utf-8 

import RPi.GPIO as GPIO
import time
import signal
import sys

def exit_handler(signal, frame):
        # Ctrl+Cが押されたときにデバイスを初期状態に戻して終了する。
        print("\nExit")
        servo.ChangeDutyCycle(2.0)
        time.sleep(0.5)
        servo.stop()
        GPIO.cleanup()
        sys.exit(0)

# 終了処理用のシグナルハンドラを準備
signal.signal(signal.SIGINT, exit_handler)

GPIO.setmode(GPIO.BCM)

# GPIO 21番を使用
gp_out = 21

GPIO.setup(gp_out, GPIO.OUT)
# pwm = GPIO.PWM([チャンネル], [周波数(Hz)])
servo = GPIO.PWM(gp_out, 50)

# 初期化
servo.start(0.0)

while True:
        for angle in range(0, 180, 30):
                dc =(1.0 + angle/180.0)/20.0*100.0 # calculate duty
                servo.ChangeDutyCycle(dc)
                print("Angle = %d" % angle)
                print("dc = %d" % dc)
                time.sleep(0.5)
        for angle in range(180, 0, -30):
                dc =(1.0 + angle/180.0)/20.0*100.0 # calculate duty
                servo.ChangeDutyCycle(dc)
                print("Angle = %d" % angle)
                print("dc = %d" % dc)
                time.sleep(0.5)

・・・思うような角度にならない。

これは、0度 ~ 180度 ではなくて、
-90度 ~ +90度 で動くからではないかと考えると、

new_duty =(1.0 + angle/180.0)/20.0*100.0 # calculate duty

とか書いてあるから、この式で行くと、

angle    new_duty
-90 2.5
-45 3.75
0   5
45  6.25
90  7.5

と成るはずだが・・・。

実際には dc は 2-12 で180度動く。

データシートを見ると、

PWMサイクル:20mS
制御パルス:0.5ms~2.4ms

の仕様になっている。

デューティ = パルス / 周期

だから、

-90度 = 0.5ms / 20ms =  2.5%。  
+90度 = 2.4ms / 20ms = 12.0%

そうなると、

2.5 ~ 12.0 の間を 180 で割ったのが、狙った角度になるかな。
そうなると、

2.5
4.875
7.25
9.625
12

のリストになるのかな。

んで、書き直したのがこれ。

#!/usr/bin/python
# coding: utf-8 

import RPi.GPIO as GPIO
import time
import signal
import sys 

def exit_handler(signal, frame):
        # Ctrl+Cが押されたときにデバイスを初期状態に戻して終了する。
        print("\nExit")
        servo.ChangeDutyCycle(2.5)
        time.sleep(0.5)
        servo.stop()
        GPIO.cleanup()
        sys.exit(0)

# 終了処理用のシグナルハンドラを準備
signal.signal(signal.SIGINT, exit_handler)

GPIO.setmode(GPIO.BCM)

# GPIO 21番を使用
gp_out = 21

GPIO.setup(gp_out, GPIO.OUT)
# pwm = GPIO.PWM([チャンネル], [周波数(Hz)])
servo = GPIO.PWM(gp_out, 50) 

# 初期化
servo.start(0.0)

val = [2.5,3.6875,4.875,6.0625,7.25,8.4375,9.625,10.8125,12]

servo.ChangeDutyCycle(val[0])
time.sleep(0.5)
servo.ChangeDutyCycle(val[8])
time.sleep(0.5)
servo.ChangeDutyCycle(val[0])
time.sleep(0.5)

while True:
        for i, dc in enumerate(val):
                servo.ChangeDutyCycle(dc)
                print("Angle:" + str(i*22.5)+"  dc = %.4f" % dc) 
                time.sleep(0.5)
        for i, dc in enumerate( reversed(val) ):
                servo.ChangeDutyCycle(dc)
                print("Angle:" + str(180 - i*22.5)+"  dc = %.4f" % dc) 
                time.sleep(0.5)

なお、サーボモーターを動かしていると、 私の環境では電圧低下の警告(画面右上の虹色四角と赤色LEDが消灯)が出ました。

このことは、モーターの電力を外部(USBなどから)取り、GNDを共通にすることで回避します。