ラズパイからDCモーターを制御するプログラム
Contents
DCモーター
Raspberry Pi 3 B+からDCモータの回転方向や回転速度を制御するプログラムを作成した時の備忘録。
モーターの電源は単三電池4本の別電源を確保しており、またモーターの制御にはTOSHIBAのTA7291P(後述)を使っている。
ブレッドボード中央上部の青色の半固定抵抗のつまみを左側に回すとモーター正面から見て反時計回り、右側に回すと時計回りにモーターが回転する。
つまみを回す量によりモーターの回転速度が変わる様にプログラミングしている。
DCモーターは秋月電子通商の「DCモーター FA-130RA-2270L」を使用した。
尚、DCモーターはそのまま電子回路に接続するとノイズが発生して電子回路が誤動作を起こす可能性があるのでノイズ除去用のコンデンサーをハンダ付けしている。
はんだ付けが超下手くそなのは多めに見て欲しい。
コンデンサーがノイズ除去をする仕組みは正直よく理解していないのだが、コンデンサーには電荷を貯める性質(これは知っていた)の他に直流電流は通さずに交流電流を通す性質があるとの事。
この性質を利用して直流電流の中に混在する動作を不安定にする交流成分を取り除く事ができるらしい。(具体的な仕組みはよく理解できず。。。)
上記の画像ではDCモーターの端子の両端をコンデンサーでハンダ付けしただけなのだが一方の端子からモーター本体の金属部へのはんだ付け(両端に対して行う)も、より効果がある模様。
コンデンサーは秋月電子通商の積層セラミックコンデンサー 0.01μF 50Vを使用している。
今回使用したDCモーターのスペックは以下の通り。
電圧範囲 | 1.5~3.0VDC(標準1.5V) |
無負荷時電流 | 0.2A(0.26Amax) |
無負荷時回転数 | 約8100~9900回転/分 |
類似のモーターへのリンクを下記に貼っておく。
モータの制御
モータはRaspberry Piから直接制御をするのでは無くてモータードライバーと呼ばれる制御用のパーツ(TA7291P)を介して制御している。
このパーツは秋月電子通商の「Raspberry Piで学ぶ電子工作 パーツセット」に入っていたのだが、今見ると(2020年8月12日現在)販売中止になっている。
現在購入するのであれば後継のTB67H450FNG等になるのだと思う。
ピンの役割
TA7291Pの10ピンの役割は下記の通り。
後述の回路図と見比べて欲しい。
ピン1 | GND(Raspberry PiのGNDと接続して共通にする) |
ピン2 | 出力1(モーターへ接続) |
ピン3 | 使用せず |
ピン4 | Vref:制御電源端子(0~20V) 下記のIN1、2の代わりにここにPWM信号を入力してDCモーターを制御する方法もあるが今回は使用せず ピン4と8との間に抵抗をいれておく |
ピン5 | IN1(Raspberry Piから) |
ピン6 | IN2(Raspberry Piから) |
ピン7 | Vcc:ロジック側電源端子(モータードライバー用電源 4.5~20V) |
ピン8 | Vs:出力側電源端子(モーター用電源 0~20V) |
ピン9 | 使用せず |
ピン10 | 出力2(モーターへ接続) |
IN1、2での制御
ピン5、6(IN1、2)に下記の信号を送る事でモーターを制御する事ができる。
IN1 | IN2 | モーター動作 |
0 | 0 | ストップ |
PWM | 0 | 正転(速度可変) |
0 | PWM | 反転(速度可変) |
1 | 1 | ブレーキ |
PWMとは
PWM(Pulse Width Modulation)はLOW、HIGHのデジタル信号を擬似的なアナログ信号で出力するための仕組み。
一定の周期で何回もLOW、HIGHを繰り返してアナログ的なモーターの回転速度の変化の制御を行っている。
PWMの詳細については以前の記事を参照して欲しい。
その他主なパーツ
前述のモーター、モータードライバー以外の主なパーツは以下の通り。
電池ボックス
モーターとモータードライバーの電源はRaspberry Piより取ると電流が足りないのとノイズが発生するので別電源を確保している。
その為、単3×4本を直列につないだ電池ボックスを用意している(1.5V×4本=約6V)。
抵抗(10KΩ)
モータードライバーのピン4と8との間に10kΩの抵抗を入れている。
半固定抵抗
つまみを回すことで抵抗値を変えることが出来る抵抗。
モーターの回転方向、回転速度を変えるために使用している。
半固定抵抗についての詳細はこちらの記事を参照して欲しい。
抵抗(330Ω)
10KΩの半固定抵抗の両端にそれぞれ330Ωの抵抗を配置する事で通常だとADコンバーター(MCP3208)経由で0~4095の範囲値になる所が127~3968の範囲になる(分圧抵抗の計算式より)。
これはノイズ対策の一つでDCモーターからのノイズで0や4095の値がランダムに検出されてしまう事があるので、それらをノイズとしてプログラム中で無視するための処置である。
半固定抵抗を操作して抵抗R2が0Ω(R1が10KΩ)の時のVoutの電圧をADコンバーター(MCP3208)経由で数値で取得する(0~4095の数値に置き換える)場合の計算式は、
330÷10660×4095=127
抵抗R2が10kΩ(抵抗R1が0Ω)の時のVoutは、
10330÷10660×4095=3968
となり、上端・下端の値はプログラム中で無視することができる。
ADコンバーター
Raspberry Piでは直接アナログ値を扱えないので半固定抵抗からの電圧(0~3.3V)をアナログ値からデジタル値に変換して0~4095の数値で取得する為に使用している。
MCP3208を利用している。
その他Raspberry Pi本体などについては以前の記事の「必要なモノ」を参考にして欲しい。
回路図
半固定抵抗のつまみでDCモーターを制御する回路図は以下の通り。
参考書籍
回路図は金丸隆志さんの「Raspberry Piで学ぶ電子工作」を参考にさせて貰っている。
この書籍と秋月電子通商の「Raspberry Piで学ぶ電子工作 パーツセット」を一緒に購入して書籍の通りに一通り組み立てたのだがRaspberry Pi初心者にとっては非常に参考になったと感じている。
尚、上記の書籍はRaspberry Pi 3をベースに書かれている。
Raspberry Pi 4対応の書籍も出版されていたので載せておく。
事前準備
Raspberry Piの設定のインターフェースタブでspiを有効化する。
また前述の書籍ではPythonとRaspberry Piの学習のために基本的な命令でデバイスを操作しているのだがPythonからはspidevライブラリーを使ったほうが操作が簡単なのでこの記事のプログラムではspidevをインストールする。
具体的な操作方法はこちらの記事を参照して欲しい。
プログラム
ソースコード
# -*- coding: utf-8 -*-
"""
Created on Wed Aug 12 18:02:32 2020
@author: Souichirou Kikuchi
"""
import spidev
import RPi.GPIO as GPIO
from time import sleep
CHN = 0 # ADコンバーター接続チャンネル
MOTOR_PWM0 = 25 # DC Motor PWM0
MOTOR_PWM1 = 24 # DC Motor PWM1
GPIO.setmode(GPIO.BCM)
GPIO.setup(MOTOR_PWM0, GPIO.OUT)
GPIO.setup(MOTOR_PWM1, GPIO.OUT)
pwm0 = GPIO.PWM(MOTOR_PWM0, 50) # 周波数50Hz
pwm1 = GPIO.PWM(MOTOR_PWM1, 50) # 周波数50Hz
pwm0.start(0)
pwm1.start(0)
spi = spidev.SpiDev()
spi.open(0, 0) # 0:SPI0、0:CE0
spi.max_speed_hz = 1000000 # 1MHz SPIのバージョンアップによりこの指定をしないと動かない
def get_data(channel):
dout = spi.xfer2([((0b1000+channel)>>2)+0b100,((0b1000+channel)&0b0011)<<6,0]) # Din(RasPi→MCP3208)を指定
bit12 = ((dout[1]&0b1111) << 8) + dout[2] # Dout(MCP3208→RasPi)から12ビットを取り出す
return float(bit12) # 通常なら0~4095だが両端に330Ωの抵抗を入れているので120~3975程度の範囲になる
try:
print('--- start program ---')
while True:
val = get_data(CHN)
print('val= ',val)
if val > 100 and val < 2048:
pwm1.ChangeDutyCycle(0)
duty = (2048 - val) * 50 / 2048
pwm0.ChangeDutyCycle(duty)
elif val >= 2048 and val < 4000:
pwm0.ChangeDutyCycle(0)
duty = (val - 2048) * 50 / 2048
pwm1.ChangeDutyCycle(duty)
sleep(0.5)
except KeyboardInterrupt:
pass
finally:
pwm0.stop()
pwm1.stop()
spi.close()
GPIO.cleanup()
print('--- stop program ---')
補足説明
定数の定義
12~14行目で定数を定義している
CHNは半固定抵抗からの電圧をADコンバーターに接続するチャンネル。
向かって一番左なので0(ゼロ)を指定している。
MOTOR_PWM0と1はDCモーターにPWM(Pulse Width Modulation)で信号を送るGPIOの番号を指定している。
PWM
16~22行目はPWMの初期処理。
DCモーターのIN1、2にPWMの信号を送信することでモーターの回転速度を制御できる。
ADコンバーター
28~31行目はADコンバーターから数値を取得するための関数。
Raspberry PiからDinを送るとDoutを12ビットで受け取る。
詳細は以前のこちらの記事を参照して欲しいのだが今回の注意点としては0~4095の数値を受け取るのでは無く両端に330Ωの抵抗を配置した事により127~3968の数値を受け取るという事である。
例外処理
37、47、49のtry~except ~finallyで例外処理を行っている。
exceptではCtrl+Cでプログラムを終了した時に発生する例外を拾って後続の処理を実行するようにしている。
finallyではプログラム終了時に使用したリソースを開放している。
メインの処理
34~46行目がメインの処理。
0.5秒間隔でwhileループを回しながらget_data関数を呼び出してADコンバーターより電圧を変換した数値を受け取っている。
数値が101~2047の時はモータードライバー(TA7291P)のIN2をゼロにしてIN1にはPWMのデューティー比を設定している。
デューティー比についての詳細はこちらの記事を参照して欲しい。
デューティー比は0~100の数値なのだが50/2048をかけて0~50の範囲にしている理由はDCモーターの電圧範囲が1.5~3.0Vの一方、電源は単三電池(1.5V)を直列に4本接続しているので6.0Vになる。
上記の理由からざっと半分のデューティー比50を上限にしているのだが参考までに前述の書籍では70を指定していた。
また2048からget_data関数からの戻り値を引いているが半固定抵抗の中央値(ブレッドボード上で矢印が真上を向いている時)の2048に近いほどPWMのデューティー比を小さい数値にしてDCモーターを停止させたいので2048から引いている。
逆に反時計回りに目一杯回すとget_data関数からの戻り値は小さい数値になりデューティー比は高くなる。
get_data関数からの戻り値が2048~3999の時もIN1とIN2を逆にしているが考え方は一緒である。
get_data関数からの戻り値が100以下や4000以上の時はDCモーターからのノイズと見做して何も処理をない。
実行結果
上記のプログラムを実行した結果は以下の通り。
動画
半固定抵抗のつまみを回すとモーターが回転する様子は以下の動画で確認してみて欲しい。
画面ショット
Raspberry PiからThonnyで上記のプログラムを実行した時の画面ショット。
get_data関数からの戻り値(ADコンバート後の値)をvalとしてprint表示している。
以上で今回の記事は終了とする。
この記事が何処かで誰かの役に立つことを願っている。
尚、当記事中の商品へのリンクはAmazonアソシエイトへのリンクが含まれています。Amazonのアソシエイトとして、当メディアは適格販売により収入を得ていますのでご了承ください。
最近のコメント