RaspberryPi Zeroでサーボモータを動かす
Simple Wiki Based Contents Management System
関心分野 >> Raspberry Pi Zeroについて >> RaspberryPi Zeroでサーボモータを動かす

RaspberryPi Zeroでサーボモータを動かす

Raspberryp PiのGPIO(Genraral Purpose Input/Output)のを使ってものを動作させてみたいと思います。
まず、最初に簡単な例として、LEDを点灯させ、次にPWMでのLEDの制御を行った後に、サーボーモータのを動かすところまでやっていきたいと思います。
GPIOを操作するプログラムの作成では、C言語で作ることが多いと思いますが、簡単に使いたいと思いますので、ここでは、Pythonを使っていきます。

GPIOを扱うためのライブラリ

RaspberryPiのGPIOをPythonでコントロールする場合に、よく使われるライブラリとして
などが公開されています。
ここでは、WiringPiを使います。

WiringPiのインストール

WiringPiは、Githubには、最新版が公開されていますが、pipコマンドでインストールします。
pi@raspberrypi:~ $ sudo apt-get install python-dev
pi@raspberrypi:~ $ sudo apt-get install python-pip
pi@raspberrypi:~ $ sudo pip install wiringpi2
pipコマンドでのインストールでは、パッケージのダウンロードとライブラリのコンパイル&インストールが順次行われます。最終的に、下記のように表示されて終了すれば、パッケージインストールは完了です。
Successfully installed wiringpi2 wiringpi
Cleaning up...
次に動作確認のためにPythonを起動して、wiringpiをimportしてみましょう。ここで少し気を付けなければいけないのは、wiringpiの利用には、管理者権限が必要になります。
そのため、
pi@raspberrypi:~ $ sudo python
で起動して、動作確認をしてください。無事にwinringpiのインストールが終了したら、GPIOの操作を行っていきます。

まずは、LEDを点灯させる

WiringPiの準備が整ったところで、RaspberryPi Zeroを使って、LEDを転送させてみたいと思います。RaspberryPiのGPIOには、
  • GPIOピン1本あたりの最大電流は16mA
  • 複数本のGPIOピンを使用する場合、同時に流せる電流の合計は50mAまで
という制限がありますが、LED 1つくらいであれば、GPIOピンからの電源供給でも動作すると思います。RaspberryPiのGPIOの電気的な仕様は、ピンを出力設定で動作させるとHIGH(1)の時には、3.3Vの電圧が出力されます。
そのため、下図のような簡単な回路でLEDを点灯させられると思います。
この例では、LEDのアノード(正極)を抵抗を介して18ピンに接続し、カソードを20ピンに接続しています。上図からも分かると思いますが、20ピンはグランド(接地)で18ピンはGPIOの番号では24になります。
RaspberryPiでは、物理的なピンの番号とGPIOの番号は異なりますので、プログラム時には注意してください。LEDとGPIOとの間の抵抗は、LEDの仕様にもよりますが、330Ωから1kΩくらいの間であれば大丈夫だと思います。
回路が完成したら、コンソールを開いてPythonでLEDを制御してみましょう。
まずは管理者モードでPythonを起動します。
pi@raspberrypi:~ $ sudo python
Python 2.7.9 (default, Sep 17 2016, 20:26:04)
[GCC 4.9.2] on linux2 
Type "help", "copyright", "credits" or "license" for more information.
>>>
次に、wiringpiのパッケージをインポートし、GPIOの初期化を行います。GPIOの初期化に関しては、
  • int wiringPiSetup (void) ;
  • int wiringPiSetupGpio (void) ;
  • int wiringPiSetupPhys (void) ;
  • int wiringPiSetupSys (void) ;
の4つがありますが、GPIOの番号で制御したいと思いますので、wiringPiSetupGpioを使用します。
>>> import wiringpi
>>> wiringpi.wiringPiSetupGpio()
0
初期化は、0が返ってくれば正常終了です。また、初期化コマンドを再度実行するとPythonが終了してしまいます。
次に、制御するGPIOのピンを出力モードに変更します。
>>> wiringpi.pinMode(24, wiringpi.GPIO.OUTPUT)
ここまででGPIOの出力準備が整いましたので、digitalWriteコマンドでGPIOを制御します。
  • LEDの点灯
    >>> wiringpi.digitalWrite(24, 1)
    
  • LEDの消灯
    >>> wiringpi.digitalWrite(24, 0)
    
以上でLEDの制御ができました。

PWMを使ってLEDを制御する

前述の例では、GPIOをOn/Offを行うことで、LEDの点灯/消灯を制御しました。この機能でモータを動かすと駆動と停止のみしか制御できませんので、位置制御のサーボモータでは使うことができません。位置制御を行うサーボモータを動かすには、PWM(Pulse Width Modulation)という電力制御方式を使います。
PWMを用いるとLEDの場合には、明るさを自由に変更させることができます。そこで、サーボモータに取り掛かる前に、LEDをPWMで動作させてみましょう。
RaspberryPiのGPIOでは、上図からわかるようにハードウェアPWMをサポートしたピンが2つあります。そこで、PWM0(GPIO18、物理番号12)のピンを使ってLEDの制御をしてみましょう。
RaspberryPi ZeroとLEDの結線を下図のように変更します。
RasbperryPi ZeroとLEDの接続が終了したら、LEDの点灯の時と同様にPythonを起動して、GPIOを初期化します。
pi@raspberrypi:~ $ sudo python
Python 2.7.9 (default, Sep 17 2016, 20:26:04)
[GCC 4.9.2] on linux2 
Type "help", "copyright", "credits" or "license" for more information.
>>> import wiringpi
>>> wiringpi.wiringPiSetupGpio()
0
次に、LEDを接続したGPIO18をPWMモードに設定します。
>>> wiringpi.pinMode(18, wiringpi.GPIO.PWM_OUTPUT)
次に、PWMのモードを変更します。これは、RaspberryPi ZeroのPWMは、デフォルトでは、Balanceモードになっています。Balanceモードは、Duty比ベースで制御するモードでLEDの制御には向いているそうですが、サーボモータの制御には不向きだそうです。今、LEDをPWMで制御しようとしていますが、サーボモータ制御の前段階ということもあり、従来モードであるmark-spaceにしたいと思います。
>>> wiringpi.pwmSetMode(wiringpi.PWM_MODE_MS)
最後に、PWM信号の周波数を設定したいのですが、ラジコンサーボモータの多くが 20msec(50Hz)のようですので、ここでも50Hzに設定します。
RaspberryPiのPWMのベースクロックが 19.2Hz になっていますので、PWM信号の周波数は、下記の式で求められます。
 f(Hz) = 19.2 / pwmClock / pwmRange 
したがって、下記のように設定してみます。
>>> wiringpi.pwmSetRange(1920)
>>> wiringpi.pwmSetClock(200)
これで、RapsberryPi ZeroのPWMを使う準備ができましたので、PWM信号の出力を行います。
>>> wiringpi.pwmWrite(18,100)
PWM信号を止めるとき(消灯させる)には、0を書き込めば良いようです。
>>> wiringpi.pwmWrite(18,100)
これで、LEDをPWMで制御することができました。

サーボモータを回す

これまで、RaspberryPi ZeroのGPIOの簡単な使い方として、LEDを使ってテストをしてきました。いよいよ本題であるサーボモータを回したいと思います。

サーボモータ

今回利用したサーボモータは、ラジコン用の一般的に販売されているものを使います。トルクが大きく安く手に入れられることを念頭において、SG-5010を購入してみました。
このサーボモータは、下のような仕様になっております。
  • 質量: 47g、 本体大きさ: 40.6×20.5×38mm、コード長さ: 32cm
  • 動作速度: 0.19秒/60度 (4.8v); 0.15秒/60度 (6.0v)
  • ストール・トルク: 5.5kg/cm (4.8v)、デッドバンド幅: 1μs
  • PWMサイクル:20ms(50Hz)
  • 制御パルス:0.5ms ~ 2.4ms
  • 制御角:±約90°(180°)
  • 動作電圧: 4.8~ 6v
  • プラグ形状: JR (JR 及び Futaba に適合)、ギア材質: 金属、付属品: ホーン、ビスほか
このモータは、5V電源で動作しますので、RaspberryPi Zeroから直接電源をとれそうですが、ノイズ等の問題もありますので、ブレッドボードを使って簡単な回路を組んでみます。
電源は、簡単に利用したいと思いますので、単4 4本の乾電池ボックスを使っています。(充電池 4本でほぼ5Vになります)また、モータの電源部の配線に0.1μFのコンデンサを入れてノイズ対策を行っています。
Raspberry Pi Zeroの方には、モータの信号線を GIOP18(PWM0)のピンに接続し、GNDのピン(39番ピン)をブレッドボードの接地(アース)と接続しています。
上の仕様からも分かるように、このモータのPWMサイクルは、50Hzとなっています。LEDをPWMで動作させたときもこれと同じ50HzのPWMサイクルを用いていましたので、同じコードでモータが動作するはずです。
では、LEDの時と同様にWiringPiの初期化を行っていきます。
pi@raspberrypi:~ $ sudo python
Python 2.7.9 (default, Sep 17 2016, 20:26:04)
[GCC 4.9.2] on linux2 
Type "help", "copyright", "credits" or "license" for more information.
>>> import wiringpi
>>> wiringpi.wiringPiSetupGpio()
0
>>> wiringpi.pinMode(18, wiringpi.GPIO.PWM_OUTPUT)
>>> wiringpi.pwmSetMode(wiringpi.PWM_MODE_MS)
>>> wiringpi.pwmSetRange(1920)
>>> wiringpi.pwmSetClock(200)
これで準備が整いましたので、実際にモータを動かしてみます。
>>> wiringpi.pwmWrite(18,100)
モータは正常に動作したでしょうか?この状態だとサーボがONの状態をキープしますので、下のようにしてサーボOFFにします。
>>> wiringpi.pwmWrite(18,0)
私がテストしたところ、44~232の間で動作しました。角度を直接入力できませんが、ほぼ比例しますので、これで十分かと思います。
コマンドラインでモータを回したい場合には、下記のようなプログラム(motor.py)を作成すれば、良いと思います。
#
# motor.py
#
import sys
import time
from wiringpi import *
 
PWM_PIN=[18, 13]

def setupMotor(id):
  wiringPiSetupGpio()
  pinMode(id, GPIO.PWM_OUTPUT)
  pwmSetMode(GPIO.PWM_MODE_MS)
  pwmSetRange(1920)
  pwmSetClock(200) 

 
if __name__ == '__main__' :
#
#  SG-5010(44 -- 232)
#
  try:
    id    =  PWM_PIN[int(sys.argv[1])]
    angle =  int(sys.argv[2])
 
 
    if angle == 0:
      setupMotor(id)
      pwmWrite(id, 0)
 
    elif angle < 44 or angle > 232:
      print "ERROR: over range"
    else:
      setupMotor(id)
      pwmWrite(id, angle)
      time.sleep(0.6)
      pwmWrite(id, 0)
 
 except:
   print "Usage: argv[0] PWM_ID Count"

あとは、コンソールから
pi@raspberrypi:~ $ sudo python ./motor.py 0 100
とすれば、サーボモータを動かすことができます。
SoftPWMを使う
上の例では、RaspberryPiのハードウェアPWMを使ってモータを動作させましたが、WiringPiには、ソフトウェアでPWMを行い、他のGPIOからでもサーボモータを駆動させることができます。
ただし、WiringPiでは、CPUへの負荷への配慮から比較的低速のPWMのみサポートされており、サーボモータを駆動すると振動を起こす可能性が非常に大きくなります。このような場合には、目標角度に制御をOFFにすることである程度対処することができます。
もちろんLEDの場合には、制御OFFにする必要はありません。(消灯してしまいます)
それでは、GPIO4のピン(物理7番ピン)にモータの制御信号線を接続して、以下のようなプログラムで動作させてみましょう。ハードウェアPWMの時と同じように、コマンドラインで制御するためのプログラムを書くと下のようになります。
基本的には、ハードウェアPWMの初期化時におこなったpwmSetRangeおよびpwmSetClockが、softPwmCreate関数になり、pwmWritesoftPwmWriteになるくらいなのですが、SoftPWMの場合には、指定できるRange幅が変更になります。
そのため、サーボの指令値が下のように、5~25になってしまいますので気を付けてください。
#
# motor.py
#
import sys
import time
from wiringpi import *

def setupMotor(id):
  wiringPiSetupGpio()
  pinMode(id, GPIO.PWM_OUTPUT)
  softPwmCreat(id, 0, 100)
 
if __name__ == '__main__' :
#
#  SG-5010(5 -- 25)
#
  try:
    id    =  int(sys.argv[1])
    angle =  int(sys.argv[2])
 
 
    if angle == 0:
      setupMotor(id)
      softPwmWrite(id, 0)
 
    elif angle < 5 or angle > 25:
      print "ERROR: over range"
    else:
      setupMotor(id)
      softPwmWrite(id, angle)
      time.sleep(0.6)
      softPwmWrite(id, 0)
 
 except:
   print "Usage: argv[0] PWM_ID Count"

上のプログラムでGPIO4に接続されたサーボモータを動かす場合は、コンソールから
pi@raspberrypi:~ $ sudo python ./motor.py 4 10
とすれば、サーボモータを動かすことができます。