久しぶりにRaspberry Piの電子工作の投稿です。PWM制御を理解してからもずっと、Raspberry Piをいじってきましたが、なかなか結果がでなくて投稿ができませんでした。
PWM制御を理解してからはDCモーター(いわゆる模型やおもちゃによく使われるモーター)をPWM制御で回そうしていました。PWM制御でDCモーターを自由に加減速できるようにしたいと思っていました。
結論から言うと、一時的には思い通りにDCモーターを加減速できるのですが、しばらく回すとDCモーターが止まってしまうという症状に陥ってしまい、抜け出せません。そういう状態が2か月ほど続きました。トラブルシュートも含めて紹介します。
回路図
購入した入門キットにはDCモーターを回すという学習があります。また、ロータリーエンコーダーを使って入力を学習するというのもあり、この2つを応用してロータリーエンコーダーを回して、それに応じてDCモーターの速度を可変にするというものに取り組みました。
これが機械式ロータリーエンコーダーです。入門キットに含まれているものです。
ロータリーエンコーダーとは、軸を回転させるとデジタルのパルスを出力します。パルスの数え方により正のカウントと負のカウントを数えることができます。つまり、軸を時計回りに回すとカウントが増加し、反時計回りに回すとカウントが減少します。
ボリューム(可変抵抗器)と違って無限に回すことができて、回転角度をカウントすることができるのでいろんな機械動作に応用できるものです。マウスのスクロールも同じ仕組みです。
回路図というか配線図ですね。入門キットでは、「DCモーターを回してみる」と「ロータリーエンコーダーを使ってみる」という2つの学習になっていますが、2つを1つに纏めたため、GPIOのPIN番号は変更しています。
あと、入門キットに同梱のモーター用DC電源モジュールでは、DC9Vの電池が別途必要です。このDC9V電池はいい値段します。需要が少ないからでしょうか、1個¥500前後します。
そこで、写真のように充電池付きの電源モジュールを購入しました。価格は¥1,500前後です。DC9V電池3個分ですが、充電で繰り返し使えるので電池の交換を気にする必要がなく、精神的にいいですね。
ブレッドボードに回路を組んだ状態です。
PWM制御プログラム
次は、ブレッドボードに組んだ回路に合わせてPWM信号を発生させてDCモーターを回すためのプログラムを作成します。プログラム言語はPythonで100行弱のプログラムを作成しました。
import RPi.GPIO as GPIO
import sys
import time
# Set up PWM pins and param
MotorPin1 = 17
MotorPin2 = 27
MotorEnable = 22
frequency = 1000 // 1KHz
duty = 80 // %
# Set up Encoder pins
clkPin = 25 # CLK Pin
dtPin = 24 # DT Pin
swPin = 23 # Button Pin
globalCounter = 0
flag = 0
Last_dt_Status = 0
Current_dt_Status = 0
# Setup PWM GPIO
def setup_gpio():
#GPIO.setmode(GPIO.BCM)
GPIO.setup(MotorPin1, GPIO.OUT)
GPIO.setup(MotorPin2, GPIO.OUT)
GPIO.setup(MotorEnable, GPIO.OUT, initial=GPIO.LOW)
return GPIO.PWM(MotorPin1, frequency), GPIO.PWM(MotorPin2, frequency)
# Setup Encoder GPIO
def setup():
GPIO.setmode(GPIO.BCM) # Numbers GPIOs by physical location
GPIO.setup(clkPin, GPIO.IN) # input mode
GPIO.setup(dtPin, GPIO.IN)
GPIO.setup(swPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# For Encoder
def rotaryDeal():
global flag
global Last_dt_Status
global Current_dt_Status
global globalCounter
Last_dt_Status = GPIO.input(dtPin)
while(not GPIO.input(clkPin)):
Current_dt_Status = GPIO.input(dtPin)
flag = 1
if flag == 1:
flag = 0
if (Last_dt_Status == 0) and (Current_dt_Status == 1):
globalCounter = globalCounter - 1
if (Last_dt_Status == 1) and (Current_dt_Status == 0):
globalCounter = globalCounter + 1
# For Main(Encoder and pWM Motor)
def loop():
global globalCounter
tmp = 0 # Rotary Temperary
while True:
rotaryDeal()
if tmp != globalCounter:
print ('globalCounter = %d' % globalCounter)
tmp = globalCounter
# CW Motor
if globalCounter >= 0 and globalCounter <= 20:
duty = globalCounter * 5
p1.ChangeDutyCycle(duty)
p2.ChangeDutyCycle(0)
# CCW Motor
if globalCounter < 0 and globalCounter >= -20:
duty = globalCounter * -5
p1.ChangeDutyCycle(0)
p2.ChangeDutyCycle(duty)
if __name__ == '__main__': # Program start from here
setup()
# Initialise PWM
p1, p2 = setup_gpio()
p1.start(0)
p2.start(0)
GPIO.output(MotorEnable, GPIO.HIGH)
try:
loop()
except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the program will be executed.
pass
finally:
p1.stop()
p2.stop()
GPIO.cleanup()
プログラムの概要は以下の通りです。
行 数 | 概 要 |
---|---|
1~3 | ライブラリの利用宣言。RPi.GPIOがPWM信号用のライブラリ |
6~10 | モーター用のPIN番号、周波数、デューティ比の変数定義 |
13~20 | RE用のPIN番号、カウンタの変数定義 |
23~28 | モーターのPWM用のGPIOの初期化setup_gpio関数定義 |
31~35 | RE用のGPIOの初期化setup関数定義 |
38~52 | REの回転信号をカウンタ値に変換するrotaryDeal関数定義 |
55~73 | モーターをPWMで回す処理 loop関数の定義、主にREのカウンタ値をデューティ比と回転方向に変換する処理 |
75~90 | メイン処理。setupの実行、loopの実行、終了処理の実行 |
※RE:ロータリーエンコーダーの略。
今回のポイントとしては、周波数の値が回りやすさに影響することでした。結果としては、周波数は1000Hz(=1KHz)が最適でした。100Hzから5000Hzまでサンプリングして確認した結果です。
TI社のL293D データシートの7.パラメータ測定条件によるとPRR=5kHzという記載がありました。PRRはPulse Repetition Rate(パルス繰り返し周波数)という意味です。PRF(Pulse Repetition Frequency)とも言われます。後者の方がわかりやすいですね。また、ST社のL293Dデータシートの説明によると、このデバイス(L293D)は、最⼤ 5 kHz の周波数でのスイッチング アプリケーションでの使⽤に適しています、とありました。 L293Dへの入力周波数の最大は、5kHz(5000Hz)ということなので、5000Hzまで試してみた次第です。実際、DCモーター(たぶんFA-130シリーズ)の特性によると思います。
DCモーターを回してみた
回路作成、プログラム作成ができたので、いよいよDCモーターを回します。動画にしましたのでご覧ください。
動画の字幕にも書いていますが、デューティ比が40%~100%の間でしか回すことができません。また、始動は95%ぐらいにならないと回り始めません。これではモーターを制御するどころでありません。
トラブルシュート
この動画は最終的にトラブルシュートした後の結果です。上述した、最初は回るが途中で回らなくなるトラブルに見舞われてしまいました。
プログラムの確認では、プログラムの使い方をサイトで検索して比較しました。特に問題は見つからず。次にDC電源モジュールの確認では、安定して5V電圧があるかをテスターで確認し問題はなし。
さらに、GPIOの信号線の確認では、プログラムをデバッグ実行して、プログラム通りの信号が出ているかをオシロを使って確認しました。PIN22からイネーブル信号(HIGH)状態か、PIN17またはPIN27からドライバ入力信号(PWM信号)が出ているかを確認し、途中で信号が乱れることもなく、いずれも問題ありませんでした。最終的には、ドライバIC L293Dが原因であることに行きつきました。
入門キットに同梱されていたL293DのICです。印字マークからSTマイクロエレクトロニクス製ということがわかります。予備品がないため購入が必要でしたので、秋月電子通商より購入しました。
HTC社製でL293DN(¥230/個)でした。L293DとL293DNはピン配置と機能は同じです。
これに交換した途端、いままでの不調はどこへやらという感じで、モーターは回り続けました。オシロのお陰で一つひとつ原因を絞り込むことができました。
始動性と可動範囲の改善
いままでの課題を整理すると以下の通りです。
始動性が悪いのは、モーター始動時に必要な電圧、電流が不足しているためデューティ比95%まで必要になっていること。同様にモーター制御の範囲がデューティ比40%~100%と狭い点。40%付近でモーターが停止してしまうと、デューティ比95%まで高めないといけないため、回転を止めないようにデューティ比を下げる必要があります。結局のところ、モーター用DC電源モジュールでは電流が小さいため、モーター制御の電源容量としては不足していることになると考えました。
そこで、まずは見極めるためにDC安定化電源をモーター制御の電源に適用してモーターの始動性や可動範囲の確認をしたいと思います。
モーター用DC電源モジュールをDC安定化電源に置き換えました。電源モジュールは5Vの定電圧だったため、元々の配線図で問題ありませんでしたが、今度は電圧、電流を可変にすることができます。
ドライバIC L293DへのIC自身の内部ロジック動作のための入力電圧(Vcc1、16番ピン)は5Vで固定、モーター駆動用の入力電圧(Vcc2、8番ピン)は4.5V~36Vの範囲で可変にできます。そこで以下のように配線を変更しました。
図の①:16番ピンをGPIOの5V出力ピンに接続(5V固定)
図の②:8番ピンをブレッドボード+側に接続(4.5V~36V可変)
図の③:DC安定化電源の+端子をブレッドボードの+側に接続(可変電圧を印加)
図の④:DC安定化電源の-端子をブレッドボードの-側(両側)に接続
(GPIOのGNDとDC安定化電源のGNDの電圧を合わせるため)
今回の改善で、始動性としてはデューティ比を95%から50%へ改善し、可動範囲の改善では40~100%が27~100%に広がることがわかりました。しかしながら、100%まで使用するには電流が流れ過ぎてドライバIC L293Dの最大電流600mA(ピーク1.2A)では耐えられるか微妙です。この考え方でよいのかも含めてもう少し調べたいと思います。
まとめ
今回は、Raspberry Piを使って、ロータリーエンコーダーでPWM信号の出力を制御してDCモーターを回してみました。入門キットに付属のドライバIC L293Dの不良のため、回せるようになるまでに2か月を要してしまいました。ただ、今回の原因追及するためにオシロによる信号確認など行い勉強することもできました。
DC安定化電源を使って、モーターの始動性やデューティサイクルの可動範囲の改善を行い、PWMによるモーター駆動の挙動などを確認することができました。
コメント