まちん

主にAndroidアプリを作ってます。

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 モーターに対応していれば別のドライバでも大丈夫ですが、ステッピングモーターなどの、別の種類のモーターしか対応していないドライバも存在するので、注意が必要です。

プロペラを回してみる

とりあえず、タイヤを動かすより先に、プロペラを制御してみました。

プロペラなんて持ってないので、わくわくさんみたいに紙で作りました。

キーボードを入力として受け取って、回転数を少しずつあげています。

ラズパイカーの回路図

以下のように回路を組みました。

car_electric_circuit

モーター用に外部電源として、単 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 のモーターカーを動作させることができました。

次回は、左、右に曲げてみようと思います。

← ホームへ