ラズベリーパイは基板上に「GPIO」と呼ばれる、ラズベリーパイ上で作成したプログラムから信号の入力・信号の出力が行えるピンが存在します。
信号の入力とは「スイッチのON/OFF」「温度計で室温の計測」といった、ラズベリーパイの外の情報を入力(インプット)してプログラム上で使用することです。
対して、信号の出力とは「LEDを点灯させる」「モータを回す」といった、ラズベリーパイで制御した結果を外に出力(アウトプット)することです。
この記事では、ラズベリーパイのプログラム(Python)で、スイッチを用いてサーボモータの角度を制御する方法について解説します。
サーボモータを制御する方法は多々ありますが、今回は指定された角度を基にPWMサイクルのデューティ比を求める関数を作成することにより角度を制御します。
スイッチを使用せず、サーボモータを動かす方法については以下のページで解説しておりますので、宜しければご覧ください。
【ラズパイ電子工作】サーボモータを動かす方法(角度指定)角度を指定せずに「直接デューティ比を指定する」方法については以下のページで解説しております。
【ラズパイ電子工作】サーボモータを動かす方法(デューティ比指定)
目次
1. 完成イメージ(サーボモータを動かす)
この記事で完成するものは以下のようになります。
青色スイッチと黄色スイッチ、サーボモータをラズベリーパイのGPIOに接続して「青色スイッチを押すとサーボモータが左に90°」「黄色スイッチを押すとサーボモータが右に90°」旋回します。
2. 使用する部品
今回使用する部品は以下の通りです。
『Raspberry Pi 3 Model B+』を使用します。※2020年3月時点で最新はRaspberry Pi 4になります。
LEDや抵抗といった各種部品や(後述する)ジャンパ線などを穴に差し込み、部品間を電気的に接続する板(ボード)です。
電子工作をする上で必須の部品です。
ブレッドボートに差し込み、電子部品の間を電気的に接続します。
↑の写真では両側が「ピン」になっており、ブレッドボートの穴に差し込んで使用します。
サーボモータ(Servo motor)とは、位置制御や速度制御ができるモータです。
製造業においてサーボモータは、位置決め・速度制御やトルク制御を行う機構によく用いられる大変高額なものですが、今回はラジコンやホビーロボットに用いられる安価で小型なサーボモータ:SG90を使用します。
サーボモータ:SG90の主要なスペックは以下のようになります。
PWMサイクル | :20ms(50Hz) |
制御パルス | :0.5~2.4ms |
制御角度 | :±90°(180°) |
動作電圧 | :4.8V(~5V) |
動作スピード | :0.1s / 60° |
動作角度が±90°のため、モータをグルグル回すことはできません。
スイッチを押すと、内部の回路が繋がり電流を流します。
今回は青色と黄色のスイッチを使用します。
電子工作をするため、これまで解説した部品の他にもスイッチやセンサなど、色々な部品が必要になってきます。個々で購入するには手間がかかるため、最初はセット品を購入することをおススメします。
私は以下のセット品を購入しました!
また、これからラズベリーパイを購入する場合、ラズベリーパイ本体を含めたセット品を購入することをおススメします。
ラズベリーパイ本体を収めるケースや、OSをインストールするためmicroSDカードなど必要なものを個々に購入する手間を省くことができます。
3. 回路図・配線の様子
ラズベリーパイのプログラム(Python)で、スイッチを用いてサーボモータの角度を制御する回路を解説します。
回路図は以下のようになります。
サーボモータの「赤色リード線を5Vピン」「茶色リード線をGNDピン「オレンジ色リード線をGPIO 18番ポート」に接続します。
青色スイッチの片側とGPIO 24番ポートに接続します。(青色のジャンパ線)
黄色スイッチの片側とGPIO 25番ポートに接続します。(黄色のジャンパ線)
各スイッチのもう一方をラズベリーパイの3.3Vに接続します。(白色のジャンパ線)
回路図ではスイッチの色が実際と異なりますがご了承ください。
配線の様子です。こんな感じになりました。
「この画像から何を読み取れっちゅうねん!」って感じですね…。
↑では、フラットケーブルでGPIOの全ピンをブレッドボードに接続しています。回路図と実際の配線は異なる部分がありますがご了承ください。
※電気的には「回路図」と同じ意味です。
4. プログラム(Python)
スイッチを用いてサーボモータの角度を制御するプログラム(Python)は以下のようになります。
#必要なモジュールをインポート
import RPi.GPIO as GPIO #GPIO用のモジュールをインポート
import time #時間制御用のモジュールをインポート
import sys #sysモジュールをインポート
#ポート番号の定義
Servo_pin = 18 #変数"Servo_pin"に18を格納
Blue_sw_pin = 24 #変数"Blue_sw_pin"に24を格納
Yellow_sw_pin = 25 #変数"Yellow_sw_pin"に25を格納
#GPIOの設定
GPIO.setmode(GPIO.BCM) #GPIOのモードを"GPIO.BCM"に設定
GPIO.setup(Servo_pin, GPIO.OUT) #GPIO18を出力モードに設定
#GPIO24を入力モードに設定してプルダウン抵抗を有効にする
GPIO.setup(Blue_sw_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
#GPIO25を入力モードに設定してプルダウン抵抗を有効にする
GPIO.setup(Yellow_sw_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
#PWMの設定
#サーボモータSG90の周波数は50[Hz]
Servo = GPIO.PWM(Servo_pin, 50) #GPIO.PWM(ポート番号, 周波数[Hz])
#初期化処理
Servo.start(0) #Servo.start(デューティ比[0-100%])
deg = 0 #変数"deg"に0を格納
#角度からデューティ比を求める関数
def servo_angle(angle):
duty = 2.5 + (12.0 - 2.5) * (angle + 90) / 180 #角度からデューティ比を求める
Servo.ChangeDutyCycle(duty) #デューティ比を変更
time.sleep(0.3) #0.3秒間待つ
#while文で無限ループ
#サーボモータの角度をデューティ比で制御
#Servo.ChangeDutyCycle(デューティ比[0-100%])
while True:
try:
if(GPIO.input(Blue_sw_pin) == 1): #GPIO24が"1"であれば以下を実行
deg = 90 #変数"deg"に90を格納
if(GPIO.input(Yellow_sw_pin) == 1): #GPIO25が"1"であれば以下を実行
deg = -90 #変数"deg"に-90を格納
servo_angle(deg) #サーボモータを変数"deg"の角度に動作
except KeyboardInterrupt: #Ctrl+Cキーが押された
Servo.stop() #サーボモータをストップ
GPIO.cleanup() #GPIOをクリーンアップ
sys.exit() #プログラムを終了
極端に言うと、GPIOの18番ポートを用いてPWM出力のデューティ比をwhile文で繰り返し変化させているプログラムです。
#必要なモジュールをインポート
import RPi.GPIO as GPIO #GPIO用のモジュールをインポート
import time #時間制御用のモジュールをインポート
import sys #sysモジュールをインポート
2行目~4行目で、今回必要な「モジュール」を宣言します。
#ポート番号の定義
Servo_pin = 18 #変数"Servo_pin"に18を格納
Blue_sw_pin = 24 #変数"Blue_sw_pin"に24を格納
Yellow_sw_pin = 25 #変数"Yellow_sw_pin"に25を格納
以下の通り変数に数値を格納します。この数値はGPIOのポート番号として扱い、後からポート番号を変更する場合はこの数値を変更します。
Servo_pin | :数値18を格納(サーボモータ用) |
Blue_sw_pin | :数値24を格納(青色スイッチ用) |
Yellow_sw_pin | :数値25を格納(黄色スイッチ用) |
#GPIOの設定
GPIO.setmode(GPIO.BCM) #GPIOのモードを"GPIO.BCM"に設定
GPIO.setup(Servo_pin, GPIO.OUT) #GPIO18を出力モードに設定
#GPIO24を入力モードに設定してプルダウン抵抗を有効にする
GPIO.setup(Blue_sw_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
#GPIO25を入力モードに設定してプルダウン抵抗を有効にする
GPIO.setup(Yellow_sw_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIOの設定を行います。
GPIO.setmode(GPIO.BCM)
は、GPIOをポート番号で扱う方法に設定します。
GPIO.setup(Servo_pin, GPIO.OUT)
は、GPIO 18番ポートを出力モードに設定します。
GPIO.setup(Blue_sw_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
は、GPIO 24番ポートを入力モードに設定して、内部にあるプルダウン抵抗を有効にします。
GPIO.setup(Yellow_sw_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
も同様に、GPIO 25番ポートを入力モードに設定&プルダウン抵抗を有効にします。
#PWMの設定
#サーボモータSG90の周波数は50[Hz]
Servo = GPIO.PWM(Servo_pin, 50) #GPIO.PWM(ポート番号, 周波数[Hz])
PWM信号を使用する為の設定を行います。
Servo = GPIO.PWM(Servo_pin, 50)
は、GPIO 18番ポートを50HzのPWM信号に割り当てます。
#初期化処理
Servo.start(0) #Servo.start(デューティ比[0-100%])
deg = 0 #変数"deg"に0を格納
初期化する処理を行います。
Servo.start(0)
は、GPIO 18番ポートのPWM信号のデューティ比を0%に出力します。
#角度からデューティ比を求める関数
def servo_angle(angle):
duty = 2.5 + (12.0 - 2.5) * (angle + 90) / 180 #角度からデューティ比を求める
Servo.ChangeDutyCycle(duty) #デューティ比を変更
time.sleep(0.3) #0.3秒間待つ
サーボモータを任意の角度に動作させるためには、角度からデューティ比を計算で求める必要があります。
逐一計算からデューティ比を求めても問題ありませんが、今回は指定された角度を基にPWMサイクルのデューティ比を求める関数を作成します。
今回の関数servo_angle(angle)
は、引数(angle)に角度を指定することで、関数内でデューティ比dutyを求めます。
求めたデューティ比は、Servo.ChangeDutyCycle(duty)
を用いてGPIO 18番ポートのデューティ比を変更します。
例えば、引数(angle)に”-90”が指定された場合、dutyを求める式により、duty = 2.5になります。SG90はデューティ比を2.5%にすることで約-90°に動作します。
デューティ比の出力を変更した後、time.sleep(0.3)
で何もせずに0.3秒間待つ処理を行います。
#while文で無限ループ
#サーボモータの角度をデューティ比で制御
#Servo.ChangeDutyCycle(デューティ比[0-100%])
while True:
try:
if(GPIO.input(Blue_sw_pin) == 1): #GPIO24が"1"であれば以下を実行
deg = 90 #変数"deg"に90を格納
if(GPIO.input(Yellow_sw_pin) == 1): #GPIO25が"1"であれば以下を実行
deg = -90 #変数"deg"に-90を格納
servo_angle(deg) #サーボモータを変数"deg"の角度に動作
except KeyboardInterrupt: #Ctrl+Cキーが押された
Servo.stop() #サーボモータをストップ
GPIO.cleanup() #GPIOをクリーンアップ
sys.exit() #プログラムを終了
「while文」は条件を指定して、その条件が真の時に繰り返し処理を行うものです。
while 条件式:
繰り返し処理を行うコード
↑の「条件式」にTrueを指定することにより、条件式は常に真となりwhile文は無限に繰り返します。
ただし、このままではプログラム実行中に無限ループから抜け出す方法がありません。そこでwhile文の中にある「try文」と「except文」を使用します。
このtry-except文を用いることにより、while文の処理は以下のようになります。
while True:
try:
繰り返し処理を行うコード
except KeyboardInterrupt: #Ctrl+Cキーが押された
GPIO.cleanup() #GPIOをクリーンアップ
sys.exit() #プログラムを終了
except KeyboardInterrupt:
は、キーボードのCtrl + Cキーが押された時にwhile文の繰り返し処理から抜けて「GPIOのクリーンアップ」と「プログラムの終了」の処理を行います。
Ctrl + Cキーが押されていないとき、try:文の中の処理を繰り返し行います。(以下で解説)
while文の中の「繰り返し処理を行うコード」は以下の通りです。つまり、Ctrl + Cキーが押されるまで以下の処理を繰り返します。
if(GPIO.input(Blue_sw_pin) == 1): #GPIO24が"1"であれば以下を実行
deg = 90 #変数"deg"に90を格納
if(GPIO.input(Yellow_sw_pin) == 1): #GPIO25が"1"であれば以下を実行
deg = -90 #変数"deg"に-90を格納
servo_angle(deg) #サーボモータを変数"deg"の角度に動作
「if文」を使用して、タクトスイッチが押されているか判断して条件分岐を行います。
if 条件式:
条件式が"真"の時に実行するコード
if文の条件式には各タクトスイッチの状態を指定しています。
if(GPIO.input(Blue_sw_pin) == 1):
は、入力モードに設定したGPIO 24番ポートの状態が”1”のとき、つまり青色スイッチが押されているときにif文が実行されます。
青色スイッチが押されるとif文の下の行のdeg = 90
が実行され、変数degに90が格納されます。この変数degは後ほどサーボモータの角度を指定する場合に使用します。
同様の考え方で、黄色スイッチが押されると変数degに-90が格納されます。
servo_angle(deg)
は、先ほどの関数を呼び出す処理です。引数にdegを指定することにより、関数内でサーボモータに出力するデューティ比を求めてサーボモータを動作させます。
引数に指定してある変数degの値を各スイッチが押されるたびに変更することで、サーボモータの旋回する角度を制御します。
5. おわりに
ラズベリーパイのGPIOピンを用いてプログラム(Python)からサーボモータの角度を制御する方法を解説しました。
ラズベリーパイのプログラム(Python)で、スイッチを用いてサーボモータの角度を制御する方法について解説しました。
まだまだラズベリーパイ初心者の私ですが、以下の参考書が大変参考にさせて頂いております。
2冊とも初学者にも易しい内容になっており、ゼロからラズベリーパイを始める方にもオススメできる参考書です。