Raspberry Pi Pico(PicoWでも同じ)でDCモーターを制御する方法を詳しく紹介します。
モーターは3Vのものを使用し、制御には低電圧(2〜10V)で駆動する「MX1502」を搭載したドライバ基板を使用して、基本的な正逆転動作から、PWM制御で速度制御(ボリュームで速度可変)、簡易的な過負荷保護まで行います。
回路図とサンプルプログラム(コピペ)も準備してますので、実際に動かしながらDCモーターの使い方について確認してみましょう。
「ラズパイPico」の開発環境の準備や端子配列等の基本情報、基本的なプログラムについては、以下のリンクで詳しく紹介しています。
「C言語」を使用した「Arduinoコマンド」でのDCモーターの速度制御方法についても、以下のリンクで詳しく紹介しています。
1.動作紹介
・乾電池駆動
・三端子レギュレータ駆動
・簡易過負荷保護について
2.配線図
3.使用部品について
・DCモータードライバ MX1508
・DCモーター
・三端子レギュレータ(DC3.3V出力)
・その他
4.MicroPythoのサンプルプログラム(コピペ)
5.CircuitPythonのサンプルプログラム(コピペ)
6.モーターの速度制御、過負荷検知プログラムについて
・速度制御
・過負荷検知
7.まとめ
1.動作紹介
今回動作確認には「乾電池駆動」と「三端子レギュレータ駆動」の2種類で動作確認しています。
それぞれについて実際に配線した状態の写真を使って以下から紹介します。
(配線についての詳細は「2.配線図」を参照してください。)
モーターは模型や工作用で一般的な3V用DCモーター(130モーター)であればどんなものでも動作可能と思います。今回はギヤ付きのものを使用しています。
今回使用したモーター制御用ドライバ(MX1508搭載)は2つのモーターが制御できるため、モーターは2つ使用しています。
モーターの速度はボリュームで調整してPWM制御します。
合計4つのスイッチがあり、モーターごとに正転スイッチ、逆転スイッチとして使用します。
2つ同時押しで回生(ショート)ブレーキで停止時間を短くすることができます。
・乾電池駆動
乾電池駆動の配線はブレッドボードを使用して下写真のように行ないました。
乾電池の「マイナス(ー)」とラズパイPicoの「GND」は接続して共通にしておきます。
・三端子レギュレータ駆動
三端子レギュレータ駆動の配線はブレッドボードを使用して下写真のように行ないました。
モバイルバッテリー等の5Vから三端子レギュレータで3.3Vに変換してドライバ基板の電源として使用します。
モバイルバッテリーの5VをUSB-DIP変換基板を使用して3.3V用の三端子レギュレータに入力すると3.3Vの電圧を得ることができるため、これをドライバ基板の入力電源に使用しています。
三端子レギュレータはコンデンサ等の周辺部品が実装済のものを使用すると便利です。
LED(上写真では赤点灯)も実装されているため電源ON/OFFの確認にも使用できます。
・簡易過負荷保護について
今回簡易的な過負荷保護機能を実装しています。
目的は、モーターの保護もありますが、どちらかというとモーター用電源を得るための三端子レギュレータICの保護用です。
乾電池駆動の場合は、使用するモータにもよりますが、駆動部の過負荷による破損と、モーターの発熱(触れなければNG)だけ注意しておけば無効にしても特に問題ないと思います。
モーター駆動時に、開発環境のシリアルモニタにモーターの速度指定電圧とドライバ基板への入力電圧を出力するようにしています。
下画像は開発環境「Tonny」でモータ駆動時に表示されるシリアルモニタ出力の様子です。
「speed_volt」はボリュームの電圧をアナログ入力で取得して換算した電圧で、モーターの速度指定に使用します。
「protect_volt」はドライバ基板への入力電圧をアナログ入力で取得して換算した電圧で、この電圧が低下した時は過負荷としてモーターを停止させます。
今回使用したDCモーター(ギヤ付き)の「Geekservo」を1つロック(出力軸を拘束)させた時の状態ですが「protect_volt」が3.1V程度まで下がってます。
この時にモーターを停止させて保護したい場合は、サンプルプログラムの過負荷保護レベル(OVERLOAD_VOLT)を3.2(V)に設定するとモーターが停止して保護できます。
2.配線図
今回使用した配線図は下図のようになります。
スイッチはブレッドボードの都合でそれぞれGND端子が使えるような使い方をしています。
モーター用の「3V電源」は単独で準備しています。
「簡易過負荷検知」はドライバ基板の入力電圧をラズパイPicoのアナログ入力で監視して、モーター過負荷時に低下する電源電圧を検知してモーターを停止させるようにしています。
3.使用部品について
今回使用した主要部品について紹介します。
・DCモータードライバ MX1508
DCモーターを制御するためのドライバ基板(MX1508搭載)は下写真のようになります。
電源電圧範囲はDC2〜10V(制御電圧は1.8V〜7.0V)で、この基板1枚で2つのモーターの制御ができます。
今回は下写真のように端子を半田付けしています。
基板裏には高さ合わせのためにゴム足(100均)を取り付けています。
ドライバ基板の電源(+、ー)はブレッドボードに差し込み、制御用端子(INT1〜4)はジャンパー線でラズパイPicoと接続、モーターは直接「MOTORーA、B」に接続します。
DCモーターを制御するドライバ基板としては「L298N」が有名ですが、電源電圧は5V〜35Vのため、模型や工作用によく使用されるDC1.5Vや3Vモーターの駆動には「Duty比」の調整が必要で扱いにくいですし寸法も大きいです。
下写真は「L298N(左)」と「MX1508(右)」の寸法を比較したものです。
今回使用したドライバ基板(MX1508搭載)は電源電圧2V〜10Vで動作し、寸法も小さいため組み込み用途にも最適です。
・DCモーター
DCモーターは3Vモーターが推奨ですが、1.5Vでも制御可能です。
今回使用したものは以下の「Geekservoモータ」です。
「servo」と書かれてますが「サーボモーター」ではなく「DC(直流)モーター」です。
配線は2本でコネクタもついています。ギヤも付いているので大きなトルクが出せます。
「LEGOブロック」互換でブロックと組み合わせて使用できます。
出力軸もクロスシャフトになっているため「LEGOテクニックシリーズ」のタイヤや部品を取り付けることができます。
仕様について、販売元のサイトでは以下のようになってますが、見るサイトによって内容が違うようなので、参考までにご確認ください。
- 最高回転数:70rpm
- 動作電圧:3.3V~6V
- 公称電圧:4.8V
- 公称電流:100mA
- ロータロック電流:550mA
- 最大トルク:500g.cm
- スリップ電流:450mA
- 重量:20g
モーターは配線も端子も付いているものがそのまま使えて便利と思います。
DCモーターの動作原理や構造については、以下のリンクで動画も交えて詳しく紹介しています。
・三端子レギュレータ(DC3.3V出力)
ドライバ基板への入力電圧を得るためには、以下写真の三端子レギュレータ(10個セット)を使用しました。
3.3V出力のものですが3Vモーターでも特に問題なく使用できます。調整が必要な場合はPWM制御のDuty比で行います。
基板には溝が付いているので、手で割ると下写真のように1つづつ使用できます。
端子配列は「VIN」が電圧入力「OUT」が電圧出力「GND」は0Vです。
・その他
今回使用した、その他部品については以下のようになります。
「スイッチ」は基本的には何でも良いです。「ボリューム(10kΩ〜100kΩ程度)」はブレッドボードに挿せて指で回せるものが良いので、以下サンハヤト製の「スイッチ」と「ボリューム」のセットがおすすめです。
ブレッドボードは今回部品点数が多いので、一般的な1列の穴数が5個だと収めるのが大変なので、1列6穴のサンハヤト製がおすすめです。
「ラズパイPico」本体は基本的に端子は半田付けされていません。
ブレッドボードで使用する場合は、別途ピンヘッダーを購入して半田付けするか、以下のような端子が実装済のものを使用しましょう。
4.MicroPythoのサンプルプログラム(コピペ)
「MicroPython」を使用したサンプルプログラムはそれぞれ以下になります。
コピペで貼り付けて書き込んで実行してください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでコピーできます。
from machine import Pin, ADC, PWM # 入出力とアナログ入出力、PWM制御用モジュールを準備
import time # タイマーモジュールを準備
OVERLOAD_VOLT = 3.0 # 過負荷保護レベル(V) ※0.0で無効(乾電池駆動は無効推奨)
#(モータ用電源電圧の電圧降下時の値を設定、電圧降下しない電源では使用不可)
OVERLOAD_COUNT = 5 # 過負荷検知確認回数(この回数過負荷が続くとモータ停止)
# 入力ピン設定(スイッチ1〜4の入力設定)
sw1 = Pin(0, Pin.IN, Pin.PULL_UP) # スイッチのピン番号を指定してswとして入力設定(プルアップ)
sw2 = Pin(4, Pin.IN, Pin.PULL_UP)
sw3 = Pin(11, Pin.IN, Pin.PULL_UP)
sw4 = Pin(15, Pin.IN, Pin.PULL_UP)
# 出力ピン設定
led = Pin("LED", Pin.OUT) # 本体LEDピンをledとして出力に設定
# アナログ入力ピン設定
adc_protect = ADC(Pin(27)) # 過負荷保護レベル(モータ電源電圧)アナログ入力番号を設定
adc_speed = ADC(Pin(28)) # モータ速度指示電圧用アナログ入力番号を設定
# PWMピン設定
pwm1 = PWM(Pin(18 ,Pin.OUT)) # PWM出力ピン番号を指定してpwmとしてPWM出力設定
pwm1.freq(500) # PWM周波数を設定(隣り合った端子番号奇数と偶数は同じ周波数にする)
pwm2 = PWM(Pin(19 ,Pin.OUT))
pwm2.freq(500)
pwm3 = PWM(Pin(20 ,Pin.OUT))
pwm3.freq(500)
pwm4 = PWM(Pin(21 ,Pin.OUT))
pwm4.freq(500)
# アナログ入力(ADC)確認関数 *******************************************
def adcCheck():
adc_speed_val = adc_speed.read_u16() # ボリュームのアナログ入力(ADC)値を取得
adc_speed_volt = (adc_speed_val * 3.3) / 65536 # 換算してボリュームの電圧を取得
adc_protect_val = adc_protect.read_u16() # モータ電源電圧(3.3V)のアナログ入力(ADC)値を取得
adc_protect_volt = (adc_protect_val * 3.3) / 65536 # 換算してモータ電源電圧を取得
print("speed_volt = {:5.2f}V /protect_volt = {:5.2f}V".format(adc_speed_volt, adc_protect_volt)) # 各電圧表示
return adc_protect_volt # モータ電源電圧を返す
# メイン処理 ---------------------------------------------------------
led.value(True) # LEDを点灯
protect_count = 0 #過負荷保護カウント
overload = False #過負荷フラグ
while True: # ずっと繰り返し
adc_speed_val = adc_speed.read_u16() # ボリュームのアナログ入力(ADC)値を取得
# スイッチ操作、モータ駆動
if sw1.value() or overload: # スイッチが押されてない、または過負荷なら
pwm1.duty_u16(0) # PWM1出力オフ(Duty比を0に設定)
else: # スイッチが押されていれば
pwm1.duty_u16(adc_speed_val) # アナログ入力(ADC)値をPWMのDuty比に設定
if sw2.value() or overload: # スイッチが押されてない、または過負荷なら
pwm2.duty_u16(0) # PWM1出力オフ(Duty比を0に設定)
else: # スイッチが押されていれば
pwm2.duty_u16(adc_speed_val) # アナログ入力(ADC)値をPWMのDuty比に設定
if sw3.value() or overload: # スイッチが押されてない、または過負荷なら
pwm3.duty_u16(0) # PWM1出力オフ(Duty比を0に設定)
else: # スイッチが押されていれば
pwm3.duty_u16(adc_speed_val) # アナログ入力(ADC)値をPWMのDuty比に設定
if sw4.value() or overload: # スイッチが押されてない、または過負荷なら
pwm4.duty_u16(0) # PWM1出力オフ(Duty比を0に設定)
else: # スイッチが押されていれば
pwm4.duty_u16(adc_speed_val) # アナログ入力(ADC)値をPWMのDuty比に設定
# モータ過負荷保護(モータ用電源の電圧降下を検知)
if not sw1.value() or not sw2.value() or not sw3.value() or not sw4.value(): # sw1〜sw4のどれかが押されている場合
protect_vol = adcCheck() # アナログ入力(ADC)確認関数呼び出し。モータ電源電圧取得
if protect_vol <= OVERLOAD_VOLT: # モータ電源電圧が過負荷保護レベル以下なら
protect_count += 1 # 過負荷保護カウント+1
if protect_count == OVERLOAD_COUNT: # 過負荷保護カウントが5なら
overload = True # 過負荷フラグTrue
led.value(False) # LEDを消灯
else: # スイッチが押されてなければ
protect_count = 0 # 過負荷保護カウント0リセット
else: # スイッチが押されてなければ
protect_count = 0 # 過負荷保護カウント0リセット
overload = False # 過負荷フラグFalse
led.value(True) # LEDを点灯
time.sleep(0.1) # 待ち時間
「sw1,2」を押すことで「Motor-A」を正逆転、「sw1,2」を同時に押すことで回生ブレーキで減速停止できます。
「sw3,4」を押すことで「Motor-B」を正逆転、「sw3,4」を同時に押すことで回生ブレーキで減速停止できます。
ボリュームの電圧値をアナログ入力で取得して、モータの速度指令として使用しています。
ボリュームの電圧を可変させることでPWM制御のDuty比を可変させて速度を変更しています。
アナログ入力とPWM制御のプログラムについては、以下のリンクで詳しく紹介しています。
PWM制御の技術的な情報は以下のリンクで詳しく紹介しています。(プログラム言語はC言語です)
5.CircuitPythonのサンプルプログラム(コピペ)
「CircuitPython」を使用したサンプルプログラムはそれぞれ以下になります。
コピペで貼り付けて書き込んで実行してください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでコピーできます。
import digitalio # 入出力ピン制御用モジュールを準備
import analogio # アナログ入出力制御用モジュールを準備
import pwmio # PWM制御用モジュールを準備
from board import * # boardモジュールからすべてのピンの定義を準備
import time # 時間に関連する操作を行うためのモジュールを準備
OVERLOAD_VOLT = 3.0 # 過負荷保護レベル(V) ※0.0で無効(乾電池駆動は無効推奨)
#(モータ用電源電圧の電圧降下時の値を設定、電圧降下しない電源では使用不可)
OVERLOAD_COUNT = 5 # 過負荷検知確認回数(この回数過負荷が続くとモータ停止)
# 入力ピン設定(スイッチ1〜4の入力設定)
sw1 = digitalio.DigitalInOut(GP0) # スイッチのピン番号を設定
sw1.direction = digitalio.Direction.INPUT # スイッチを入力モードに設定
sw1.pull = digitalio.Pull.UP # 内部プルアップ抵抗を有効化
sw2 = digitalio.DigitalInOut(GP4)
sw2.direction = digitalio.Direction.INPUT
sw2.pull = digitalio.Pull.UP
sw3 = digitalio.DigitalInOut(GP11)
sw3.direction = digitalio.Direction.INPUT
sw3.pull = digitalio.Pull.UP
sw4 = digitalio.DigitalInOut(GP15)
sw4.direction = digitalio.Direction.INPUT
sw4.pull = digitalio.Pull.UP
# 出力ピン設定
led = digitalio.DigitalInOut(LED) # 本体LEDのピンをledに設定
led.direction = digitalio.Direction.OUTPUT # ledを出力モードに設定
# アナログ入力ピン設定
adc_protect = analogio.AnalogIn(A1) # 過負荷保護レベル(モータ電源電圧)アナログ入力番号を設定
adc_speed = analogio.AnalogIn(A2) # モータ速度指示電圧用アナログ入力番号を設定
# PWMピン設定
pwm1 = pwmio.PWMOut(GP18, frequency=500) # PWM出力ピン番号と周波数を設定
pwm2 = pwmio.PWMOut(GP19, frequency=500) #(隣り合った端子番号奇数と偶数は同じ周波数にする)
pwm3 = pwmio.PWMOut(GP20, frequency=500)
pwm4 = pwmio.PWMOut(GP21, frequency=500)
# アナログ入力(ADC)確認関数 *******************************************
def adcCheck():
adc_speed_val = adc_speed.value # ボリュームのアナログ入力(ADC)値を取得
adc_speed_volt = (adc_speed_val * 3.3) / 65536 # 換算してボリュームの電圧を取得
adc_protect_val = adc_protect.value # モータ電源電圧(3.3V)のアナログ入力(ADC)値を取得
adc_protect_volt = (adc_protect_val * 3.3) / 65536 # 換算してモータ電源電圧を取得
print("speed_volt = {:5.2f}V /protect_volt = {:5.2f}V".format(adc_speed_volt, adc_protect_volt)) # 各電圧表示
return adc_protect_volt # モータ電源電圧を返す
# メイン処理 ---------------------------------------------------------
led.value = True # LEDを点灯
protect_count = 0 #過負荷保護カウント
overload = False #過負荷フラグ
while True: # ずっと繰り返し
# スイッチ操作、モータ駆動
if sw1.value or overload: # スイッチが押されてない、または過負荷なら
pwm1.duty_cycle = 0 # PWM1出力オフ(Duty比を0に設定)
else: # スイッチが押されていれば
pwm1.duty_cycle = adc_speed.value # アナログ入力(ADC)値をPWMのDuty比に設定
if sw2.value or overload: # スイッチが押されてない、または過負荷なら
pwm2.duty_cycle = 0 # PWM2出力オフ(Duty比を0に設定)
else: # スイッチが押されていれば
pwm2.duty_cycle = adc_speed.value # アナログ入力(ADC)値をPWMのDuty比に設定
if sw3.value or overload: # スイッチが押されてない、または過負荷なら
pwm3.duty_cycle = 0 # PWM1出力オフ(Duty比を0に設定)
else: # スイッチが押されていれば
pwm3.duty_cycle = adc_speed.value # アナログ入力(ADC)値をPWMのDuty比に設定
if sw4.value or overload: # スイッチが押されてない、または過負荷なら
pwm4.duty_cycle = 0 # PWM2出力オフ(Duty比を0に設定)
else: # スイッチが押されていれば
pwm4.duty_cycle = adc_speed.value # アナログ入力(ADC)値をPWMのDuty比に設定
# モータ過負荷保護(モータ用電源の電圧降下を検知)
if not sw1.value or not sw2.value or not sw3.value or not sw4.value: # sw1〜sw4のどれかが押されている場合
protect_vol = adcCheck() # アナログ入力(ADC)確認関数呼び出し。モータ電源電圧取得
if protect_vol <= OVERLOAD_VOLT: # モータ電源電圧が過負荷保護レベル以下なら
protect_count += 1 # 過負荷保護カウント+1
if protect_count == OVERLOAD_COUNT: # 過負荷保護カウントが5なら
overload = True # 過負荷フラグTrue
led.value = False # LEDを消灯
else: # スイッチが押されてなければ
protect_count = 0 # 過負荷保護カウント0リセット
else: # スイッチが押されてなければ
protect_count = 0 # 過負荷保護カウント0リセット
overload = False # 過負荷フラグFalse
led.value = True # LEDを点灯
time.sleep(0.1) # 待ち時間
「sw1,2」を押すことで「Motor-A」を正逆転、「sw1,2」を同時に押すことで回生ブレーキで減速停止できます。
「sw3,4」を押すことで「Motor-B」を正逆転、「sw3,4」を同時に押すことで回生ブレーキで減速停止できます。
ボリュームの電圧値をアナログ入力で取得して、モータの速度指令として使用しています。
ボリュームの電圧を可変させることでPWM制御のDuty比を可変させて速度を変更しています。
アナログ入力とPWM制御のプログラムについては、以下のリンクで詳しく紹介しています。
PWM制御の技術的な情報は以下のリンクで詳しく紹介しています。(プログラム言語はC言語です)
6.モーターの速度制御、過負荷検知プログラムについて
モーターの速度制御方法と、過負荷検知のプログラムの詳細は以下のようになります。
・速度制御
モーターの速度制御はボリュームの電圧をアナログ入力で取得して行います。
ボリュームの電圧は0〜3.3Vの範囲で可変するため、アナログ入力で取得して「speed_volt」として電圧換算します。
換算した電圧はモーター駆動時に開発環境のシリアルモニタで確認できます。
以下は開発環境「Thonny」のシリアルモニタ表示です。
ボリュームを調整してボリュームの電圧換算値(speed_volt)を約1.5Vに調整しています。
(「protect_volt」はドライバ基板の入力電圧で過負荷検知に使用します。)
1.5Vモータを使用する場合はドライバ基板の入力電圧(3V)はそのままで、ボリュームのメモリを半分程度に合わせてPWM制御のDuty比を調整することで、ドライバ基板からモータへ約1.5Vが出力されて動作させることができます。
・過負荷検知
過負荷検知はドライバ基板への電源電圧の低下を検知して行なっています。
モーターの保護というより、ドライバ基板へ供給する電源回路の保護を目的としています。
特にドライバ基板への電源供給に三端子レギュレータを使用する場合は、モーター過負荷時に電流が増えると、三端子レギュレータはそれなりに発熱します。
許容電流を超えると出力電圧が低下するため、この時にモータを停止させて保護するようにしています。
以下は開発環境「Thonny」のシリアルモニタ表示です。
今回使用したモーター「Geekservo」を2つ駆動して、出力軸をロックさせた状態です。
「protect_volt」は3.3Vから下がっていき、設定(OVERLOAD_VOLTで設定)した3.0Vを下回って停止しました。
7.まとめ
Raspberry Pi Pico(PicoW)を使用して、簡単にDCモータの正逆転、速度制御(PWM制御、ドライバ基板MX1508使用)や過負荷保護をする方法をサンプルプログラム(コピペ)で詳しく紹介しました。
プログラム言語は「Python」を使用して「MicroPython」と「CircuitPython」の両方で紹介しました。「MicroPython」の方がシンプルで分かりやすいと思います。
DCモーターの速度制御を行うには「PWM制御」を使用します。
ラズパイPicoのようなマイコンボードは扱える電流が小さいため、モーターのような比較的大きな電流を制御するには専用のドライバ基板を使用する必要があります。
今回使用したドライバ基板(MX1508搭載)は電源電圧範囲が「2V〜10V」で模型や工作用の3Vモータの駆動に最適です。PWM制御で調整すれば1.5Vモータの駆動も可能です。
1つのドライバ基板で2つのモーターを制御できるため、左右のモーターを個別に駆動させるラジコンに最適なので、今後はWi-Fi搭載のラズパイPicoWを使って遠隔操作できるラジコンなんかも作って紹介していきたいと思います。
コメント