AIカーを作りたくて、まずラズパイカーを作った
友達と AI カーを作ろうと画策し、ラズパイカーを作ってみました。
ラズパイカーに搭載した、なんらかのセンサーからの入力(画像や赤外線など)を使って、モデルを作り、走行させてみるという目標です。
電子工作もほとんどやったことないのに、いきなりは AI カーはさすがに難しい。
まず、モーターを制御できないことには始まらないので、まずはそこから。
車の選定
ラズパイで制御するための、おもちゃの車を用意する必要があります。
もうラズパイやらモータードライバ(後述)やら乗ってますが、4WD の DC モーターカーを Amazon で購入しました。
先ほど商品ページを見に行ったら、もう売ってないようでした。こちらの 2 輪の 4 輪版です。
モーターと、タイヤが 4 つずつ、あとシャーシと、電池ケースが付属していました。
どうやって車を走らせるか
4 つのタイヤは、それぞれ別々に DC モーターがつけられており、DC モーターをうまく動かせれば、進むはずです。
しかし、一つ問題があります。この車、ステアリング機構がありません。
通常の車であれば、左右にハンドルを切って、前輪の角度を変えることによって曲がりますが、この車はステアリング機構が存在しません。
しかし、左右のタイヤの回転数を制御すれば、左に曲がったり、右に曲がったりできるはずという結論に至り、上述のモーターカーにしました。
こちらの本で、DC モーターを制御してプロペラを制御していたので、まずはそれから試してみました。
これ 1 冊でできる! ラズベリー・パイ 超入門 改訂第 6 版
モーターを動かすだけなら、ただある程度の大きさの電流を流せば動きますが、それだけだと回転数を制御することができません。
そこで必要になってくるのが、「パルス幅変調」と、「モータードライバ」です。
パルス幅変調(PWM)
ラズパイの GPIO は、デジタル出力のみのようで、電圧のオンオフの 2 値しか制御できません。3.3V の電圧にする、0V にするの 2 通りしか制御できません。
そうなると、一定の回転数しか生み出せないことになります。そこでパルス幅変調の出番です。
パルス幅変調(PWM: Pulse Width Modulation)とは、高速に電圧をオンオフすることによって、目標の電圧(ここでは、0V ~ 3.3V の間)を生み出す方式のことです。
自分の手でやってみると、わかりやすいかもしれません。
「ボタンを押してる間は 3.3V の電圧にする、離してる間は 0V にする。」として、プロペラ(モーター)を回すとします。
連続的にボタンを手で押したり離したりすれば、押している間は加速し、離すと惰性で回り続けるので、中間の回転数を得ることができます。
自分だけかもですが、子供の頃、扇風機でよくやって遊んでいました。(人間がやるので、ムラがあるかもですが。)
モータードライバ
パルス幅変調だけで、電圧を制御できるんだから、これだけで車動かせるじゃん。と思っていましたが、モータードライバというものを使うべきなようです。
モータドライバの主な役割としては、「逆流防止」や、「ノイズの除去」といったものがあるようです。
逆流はわかりやすくて、タイヤを手で回すと、モーターが回転し、電磁誘導でラズパイ側に電流が流れてしまい、故障の原因になります。
前述の書籍に載っていますが、秋月電子でDRV8835のモータドライバを購入しました。
これは 1 つのモータードライバで、モーターを 2 つ動作させることができるため、モーターカーの 4 輪を制御するために、2 つモータードライバを購入しました。
この DRV8835 でなくても、DC モーターに対応していれば別のドライバでも大丈夫ですが、ステッピングモーターなどの、別の種類のモーターしか対応していないドライバも存在するので、注意が必要です。
プロペラを回してみる
とりあえず、タイヤを動かすより先に、プロペラを制御してみました。
プロペラなんて持ってないので、わくわくさんみたいに紙で作りました。
キーボードを入力として受け取って、回転数を少しずつあげています。
ラズパイカーの回路図
以下のように回路を組みました。

モーター用に外部電源として、単 3 電池 4 本を用意しています。
4 輪の DC モーターそれぞれをラズパイの GPIO ピンに対応させ、それぞれ個別に操作するようにしています。
制御方法
4 つのタイヤ(モーター)それぞれ別々に、回転数を制御します。
以下のようにキーボードのタイプすることによって、回転数を変化させるようにしました。
- キーボードの a を押したら、左前タイヤの回転速度を上げる
- キーボードの s を押したら、右前タイヤの回転速度を上げる
- キーボードの d を押したら、左後タイヤの回転速度を上げる
- キーボードの f を押したら、右後タイヤの回転速度を上げる
- キーボードの z を押したら、左前タイヤの回転速度を下げる
- キーボードの x を押したら、右前タイヤの回転速度を下げる
- キーボードの c を押したら、左後タイヤの回転速度を下げる
- キーボードの v を押したら、右後タイヤの回転速度を下げる
少々コードが込み入りましたが、以下のようになりました。(久々に Python コードを書いたので、四苦八苦した)
#! /usr/bin/env python3
import RPi.GPIO as GPIO
import time
import curses
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)
GPIO.setup(23, GPIO.OUT)
GPIO.setup(24, GPIO.OUT)
GPIO.setup(25, GPIO.OUT)
# PWM 50Hz
pwm_left_front_output = GPIO.PWM(18, 50)
pwm_right_front_output = GPIO.PWM(23, 50)
pwm_left_back_output = GPIO.PWM(24, 50)
pwm_right_back_output = GPIO.PWM(25, 50)
pwm_left_front_output.start(0)
pwm_right_front_output.start(0)
pwm_left_back_output.start(0)
pwm_right_back_output.start(0)
stdscr = curses.initscr()
curses.noecho()
left_front_duty = 0
right_front_duty = 0
left_back_duty = 0
right_back_duty = 0
def cleanup():
pwm_left_front_output.stop()
pwm_right_front_output.stop()
pwm_left_back_output.stop()
pwm_right_back_output.stop()
curses.echo()
curses.endwin()
GPIO.cleanup()
try:
while True:
c = stdscr.getch()
if (c == ord('a')) and (left_front_duty < 100):
left_front_duty = left_front_duty + 10
pwm_left_front_output.ChangeDutyCycle(left_front_duty)
elif (c == ord('z')) and (left_front_duty > 0):
left_front_duty = left_front_duty - 10
pwm_left_front_output.ChangeDutyCycle(left_front_duty)
elif (c == ord('s')) and (right_front_duty < 100):
right_front_duty = right_front_duty + 10
pwm_right_front_output.ChangeDutyCycle(right_front_duty)
elif (c == ord('x')) and (right_front_duty > 0):
right_front_duty = right_front_duty - 10
pwm_right_front_output.ChangeDutyCycle(right_front_duty)
elif (c == ord('d')) and (left_back_duty < 100):
left_back_duty = left_back_duty + 10
pwm_left_back_output.ChangeDutyCycle(left_back_duty)
elif (c == ord('c')) and (left_back_duty > 0):
left_back_duty = left_back_duty - 10
pwm_left_back_output.ChangeDutyCycle(left_back_duty)
elif (c == ord('f')) and (right_back_duty < 100):
right_back_duty = right_back_duty + 10
pwm_right_back_output.ChangeDutyCycle(right_back_duty)
elif (c == ord('v')) and (right_back_duty > 0):
right_back_duty = right_back_duty - 10
pwm_right_back_output.ChangeDutyCycle(right_back_duty)
elif c == ord('q'):
cleanup()
break
except KeyboardInterrupt:
cleanup()
動作確認
うまくいきました。
左前、右前、左後、右後の順でタイヤの回転数を上げたり、下げたりしています。
動画を見ればわかりますが、ものが悪いのか、タイヤの軸が曲がっているように見えます。。
走行に影響がないか少し心配ですが、とりあえずこのまま進みます。
まとめ
モーターを制御することさえできれば、4WD のモーターカーを動作させることができました。
次回は、左、右に曲げてみようと思います。