今回は複数のサーボモーターを扱う方法を記事にしてみたいと思います。Arduino UNOは、精度の高いPWM信号が6つも出力できるのに対し、Raspberry Piには精度の高いPWM信号を出力できるピンが1つしかなく、Raspberry Piを使って複数のサーボモーターを制御する場合には、PCA9685等を実装したサーボドライバーはなくてはならないものとなっています。特にロボットやラジコンを製作するような場合にはまさに必須と言えますね。

PCA9685を搭載したサーボドライバーで最も有名なのは、Adafruit製だと思いますが、amazonでは安い互換ボードが入手可能なので、今回はそちらを使ってみることにします。互換ボードなのでAdafruitのライブラリもそのまま使うことができ、少ないコード量でサーボモーターを制御できます。

ちなみにPCA9685搭載のサーボドライバーを使えば、1つのドライバーにつき16台のサーボモーターを接続することが可能で、さらに連結すれば、理論的には最大992台のサーボモーターを制御することができます。

スポンサーリンク




目次

  1. PCA9685サーボドライバーとサーボモーターを入手
  2. PCA9685サーボドライバーを使うための準備
  3. 動作テスト用のサンプルコード
  4. まとめ

1. PCA9685サーボドライバーとサーボモーターを入手

サーボドライバー:PCA9685

まずは、PCA9685を準備しましょう。自分が今回購入したのは下記の製品です。なんと2つで1,399円という破格の安さ。実は以前より若干値上がりしてしまったのですが、それでも一つあたり700円という破格さです。amazonの評価には不安になるものもありますが、2つとも動作的には問題ありませんでした!

※上記のリンクはamazonへのリンクとなっています。

お次はサーボモーターです。電子工作で使う小型サーボの決定版といえば、やはりSG90ではないでしょうか。秋月電子通商amazonで簡単に入手することができます。

サーボモーター:SG90

※上記のリンクはamazonへのリンクとなっています。

サーボモーター:GH-S37D

ちなみに、amazonではSG90よりさらに小さいサーボモーターも入手できます。なるべく小さなものが欲しかったので、試しに買って使ってみたところ、これがなかなかいい感じでした。というのも、プラモデルなどにSG90を組み込んで使うような場合、あともう少し小さければいいのにな…ということがよくあるからです。安いですし用途によってはこれは使えるな!と思いました。

ご注意:上記のリンクは「RC450」という製品名になっていますが、実際に注文するとなぜか「GH-S37D」という商品名のものが届きました。購入される場合は、amazonレビューも参照の上、各自十分ご検討ください。ただ、届いた製品の動作に問題はありませんでした

ラズパイ本体:Raspberry Pi Zero W

国内では在庫が切れていることの多いRaspberry Pi Zero Wですが、amazonに在庫があることもあるようです。

※上記のリンクはamazonへのリンクとなっています。

2. PCA9685サーボドライバーを使うための準備

サーボモーターとPCA9685サーボドライバーが準備できたら、今度はRaspberry Pi側の準備です。PCA9685サーボドライバーはラズパイとシリアル通信(I2C)で接続する必要があるので、ラズパイ側の設定でI2Cが有効になっていなければなりません。ちなみに「I2C」は「アイ・スクエアド・シー」または「アイ・アイ・シー」と読みます。

$ sudo raspi-config
「3 Interface Option」を選択
「P5 I2C」を選択
「はい」を選択

I2Cを有効にしたら、今度はAdafruitのライブラリをインストールしましょう。

$ sudo apt-get install git build-essential python-dev
$ git clone https://github.com/adafruit/Adafruit_Python_PCA9685.git
$ cd Adafruit_Python_PCA9685
$ sudo python setup.py install

git cloneでダウンロードしたファイルからインストールがうまくいかない場合は、pipを使ってインストールすることもできます。(自分の場合は、なぜかインストール中にエラーが出たためpipでインストールしています。)

$ sudo pip install adafruit-pca9685

では今度は、回路を組んでみましょう。

  • GND:電源のマイナスとラズパイのGNDに接続
  • OE:未接続(Hiで全ての動作を停止)
  • SCL:GPIO2番ピンへ接続
  • SDA:GPIO3番ピンへ接続
  • VCC:回路用電源(3.3V or 5V)
  • V+:サーボモーター用電源

サーボモーター用の電源「V+」についてですが、SG90は小型サーボモーターなので、動作させるのに大きな電流は必要としません。それで経験上SG90を1個繋げてテストするだけなら、ラズパイの5Vピンから電源を取っても特に問題ないようですが、ラズパイに過電流を流して壊さないためにも電源は別途用意しましょう。

特に、ラジコン用のレギュラーサイズのサーボモーターや複数のSG90を繋げて動作させたいような場合は、別電源の確保は必須です。ちなみに16チャンネル全てにサーボモーターを繋ぐと使用するサーボモーターにもよりますが、最大で3Aほどの電力を消費するらしく、台数を増やす場合は電源のこともしっかりと考慮に入れる必要があります。

3. 動作テスト用のサンプルコード

では、Pythonで動作テスト用のプログラムを準備ましょう。基本的なプログラムの流れとしては、先ほどインストールしたライブラリ「Adafruit_PCA9685」をインポートして、「set_pwm_freq」という関数でPWM周波数をセットし、「set_pwm」という関数に3つの引数、チャンネル番号、PWM信号の開始値、PWM信号の終了値をセットしてやります。

参考:
Adafruit 16 Channel Servo Driver with Raspberry Pi Library Reference

まず、第1引数のサーボモーターを接続してある「チャンネル番号」ですが、ボードに印字された数字(0〜15番)で指定してください。

第2、第3引数の「PWM信号」のそれぞれの値ですが、第2引数は開始値ということで通常は「0」を指定します。第3引数が重要で、サーボモーターの動作終了時のパルス値を入力します。ただ、この値はサーボモーターの角度に対応したパルス値である必要があるため、きちんと計算してやる必要があります。

他の方のブログ記事を拝見すると、例えば、最小値150、最大値600のような、キリのよい値で指定して、浅い角度はその概数に応じて計算した値で指定している方もいるようですが、このやり方だと、ひとまず動くには動くのですが、全ての値が概数となってしまい、サーボモーターの仕様に沿った角度指定をしていることにはなりません

特に困るのが、SG90-HVローテーションサーボのような場合で、仕様に沿って正確に中央値を計算しないと、止まらずにどちらかの方向に常に回転し続けてしまいます

秋月電子通商にアップされているSG90データーシートによれば、50Hzの場合、最小値(-90度)0.5ms、最大値(90度)2.4msと書いてあり、set_pwm関数の第3引数に渡すためのPWMパルス値を正確に指定するには、下記のような計算が必要となります。

set_pwm関数の第3引数に渡すためのPWMパルス値設定角度のパルス幅[ms] / (周波数のパルス幅[ms] / 分解能[ステップ数])

設定角度のパルス幅[ms]は、前述の最小値と最大値から計算で求めます。例えば、90度にしたい場合は、最大値の「2.4」がそのまま入ります。

周波数のパルス幅[ms]は、例えば、50Hzの場合、1秒 / 50なので0.02秒、つまりミリ秒にするには1000を掛けて「20」となります。

分解能[ステップ数]は、12bit(2の12乗)のため「4096」で固定値です。

これらの点を踏まえ、関数を作ってテストコードを書いてみると、例えば、下記のようになります。

# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
import Adafruit_PCA9685
from time import sleep

# 設定周波数(Hz)
set_freq = 50

# 動作角度からPCA9685に渡す値を変換
def convert_deg(deg,freq):

	# 分解能(ステップ数)
	step = 4096

	# 接続サーボモーターの最大最小角度時のパルス間隔(ms)
	# この数値はSG90の仕様に基づくものですが、必要に応じて調整してください。
	max_pulse = 2.4 # 90°時
	min_pulse = 0.5 # -90°時

	# サーボモーターの0°時のパルス間隔(ms)
	center_pulse = (max_pulse - min_pulse) / 2 + min_pulse

	#サーボモーター1°あたりのパルス間隔(ms)
	one_pulse = round((max_pulse - min_pulse) / 180, 2)

	# 要求角度のパルス間隔(ms)を算出しPCA9685に渡す値を算出
	deg_pulse = center_pulse + deg * one_pulse
	deg_num = int(deg_pulse / (1.0 / freq * 1000 / step))

	# デバッグ
	print('deg:' + str(deg) + '(' + str(deg_num) + ')')

	return deg_num

pwm = Adafruit_PCA9685.PCA9685()
pwm.set_pwm_freq(set_freq)

try:
	while True:
		pwm.set_pwm(0, 0, convert_deg(0,set_freq))
		sleep(1)
		pwm.set_pwm(0, 0, convert_deg(45,set_freq))
		sleep(1)
		pwm.set_pwm(0, 0, convert_deg(0,set_freq))
		sleep(1)
		pwm.set_pwm(0, 0, convert_deg(-45,set_freq))
		sleep(1)

except KeyboardInterrupt:
	pwm.set_pwm(0, 0, 0)
	print("KeyboardInterrupt")
	pass

このコードを実行すると、1秒毎に45度〜0度〜-45度の動作をCtrl + Cでプログラムを中断するまで繰り返します。

SG90-HVローテーションサーボの場合など、0度の指定なのにサーボが止まらない場合は、恐らく個体差だと思うので、設定の最大値と最小値を0.1〜0.5程度それぞれ増減させてみて、0度の位置でピタッとサーボが止まるように調整してみてください。ちなみに、この記事で紹介したGH-S37Dサーボですが、SG90とほぼ仕様が同じらしく、最大値と最小値の設定はこのままでひとまず動きました。

上記のテストプログラム実行時に使用したサーボ用電源ですが、写真では単4エネループ3本が入った電池ボックスを使用しているのですが、電圧が少々低めなので、昇圧回路(ラズパイの後ろにある小さな回路)を使って5Vまで電圧を上げてあります。昇圧回路の使い方に関しては、こちらの記事「Raspberry Pi Zeroを乾電池や7.2Vバッテリーで起動させる2つの方法」も参考にしてみてください。

4. まとめ

今回はPCA9685を搭載したサーボドライバーの使い方について扱ってみました。複数のサーボモーターを扱う場合、Raspberry Piにはまさに必須とも言えるサーボドライバー。サーボモーターを動作させる場合、どちらかというとArduinoを使うことをまず思いつきそうですが、これさえあればRaspberry Piでもロボットを作れそうですね!

番外編:PCA9685サーボドライバーの事例紹介

余談ですが、今回紹介したPCA9685サーボドライバー、SG90-HVローテーションサーボ、GH-S37Dサーボのプラモデルへの組み込み例です。PCA9685サーボドライバーはRaspberry Pi Zero Wと同サイズと非常にコンパクトですし、GH-S37Dサーボは、SG90よりさらに小さいのでプラモデルなどの組み込みに最適です。

ちなみにPCA9685サーボドライバーは各チャンネルのPWMピンから15mA程度の信号を出力(3.3V時)できるため、LEDドライバーとしても使うことができます。つまりライトやテールランプなどの電飾にも使えるわけです。このような複数のLEDをコントロールする方法についてはこちらの『PCA9685を使って複数のLEDをRaspberry Piで制御する方法』という記事も参照してみてください!