Vol.1 ラズパイとPythonでラジコンを作ってみる

Robot Car with RaspberryPi4 RaspberryPi

ラズパイを購入したらやってみたかったことの1つにラジコン作成がありました。
プログラミングで制御したラジコンを走らせてみたかったので作成してみました。

今回はラジコンの組み立てとキーボードの矢印キーでラジコンを操作するところまで進めてみます。

初めての電子工作で配線の接続不良だとうまく動かないことが多々あったので反省も含めて苦労したことは全部記載しています。
同じ部品をそろえて、同じように進めていけば簡単に動くようになります。

必要な材料

必要な材料はAmazonや秋月電子でオンラインショップですべて集められます。

Amazonで購入できるようにリンクをまとめていますが、部品によっては秋月電子やRaspberry Pi Shopでも購入可能なので価格と材料で購入場所を判断してみてください。

材料目的と用途購入可能場所
Raspberry Pi4 ModelB 4GBラジコン操作を制御する本体。モーターの動きを制御する。Amazon
ユニバーサルプレートモーターも固定したり電源などを設置する車を支える土台Amazon
トラック&ホイールセット車体を動かすキャタピラーAmazon
ツインモーターギヤーボックスタイヤを動かすモーターAmazon
電池ボックス 単3電池×4モーターを動かす電源。ラズパイとは電源を分けるので電池ボックスを用意。Amazon
六角真鍮スペーサーユニバーサルプレートを2段にする際に便利な部品ラズパイ固定も可能なので必須アイテムAmazon
DRV8835 モータードライバモーター用回路とモーターからラズパイへのノイズを制御するドライバ。Amazonで購入したい場合はDRV8833で代替可能。今回はDRV8835で進める。秋月電子
ブレッドボードラズパイとモータードライバなどを接続するための接続Amazon
ジャンパーワイヤラズパイとモータードライバなどを接続するための配線Amazon
コネクタ/デュポンコネクタ セットモータとブレッドボードを接続するためのコネクタピン。電線ケーブルに取り付ける。ジャンパー線を改造して利用する場合は不要。Amazon
電線ケーブルモーターとブレッドボードを接続ための配線。ジャンパー線を改造して利用する場合は不要。Amazon
はんだこてモータードライバ、モーターと電線ケーブルを固定するためAmazon
モバイルバッテリーラズパイ用の電源。5000mAhを選ぶ。Amazon


モーターの組み立て

材料がそろったらラジコンカーを作成しています。

まずはモーターの準備をしていきます。
ツインモーターギヤーボックスを説明書通りに組み立てていきます。

ギヤは4速あって今回はCを選びました。タイプAは本当に爆速です。

タイプギヤ比回転トルク回転数
A12.7 : 194 gf・cm1039 rpm
B38.2 : 1278 gf・cm345 rpm
C114.7 : 1809 gf・cm115 rpm
D344.2 : 12276 gf・cm38 rpm

説明書通りにモーターの組み立てが完了したらモーターと電線をつなげてはんだ付けします。
導線の片側にはコネクタピンとコネクタハウジングを取り付けます。
購入したジャンパー線を利用するなら片側を切断して、モーターとつなげてもいいと思います。

画像のように取り付けができたら次は車体を作成します。



車の組み立て

タミヤのユニバーサルプレートとキャタピラーで車体を作ります。

ユニバーサルプレートにモーターを取り付ける位置が決まったら、モーター+キャタピラー+ユニバーサルプレートをつなげていきます。

ユニバーサルプレートは2枚入っているので六角真鍮スペーサーを利用して2階建てにします。
ラズパイやその他電源などを乗せることができるので完成時にごちゃごちゃしなくて済みます。
六角真鍮スペーサーは見た目もよくて汎用性高いのでお勧めです。


DCモータードライバをはんだ付けする

DCモータードライバーは車輪を動かすモーターを制御します。
左右のモーターが正転/逆転することで直進やターンを実現することができます。
ここは実際にモーターと配線をつなげて動かしてみるとわかりやすいです。

GPIO18GPIO23GPIO24GPIO13
直進
後退
右ターン
左ターン
停止

DCモータードライバーも付属のピンとはんだ付けをしておきます。
ブレッドボードに接続するときにはんだ付けをしていた方が接続が安定していたようだったので迷っている方ははんだ付けしたほうが良いです。



すべての回路をつなぐ

準備が整ったのですべての回答を接続します。
モーター、DCモータードライバー、乾電池、ラズパイのGPIOを下図のようにブレッドボード上で組み合わせます。

私が困ったこと

すべての配線を図のように組み合わせるのは苦労しました。理由はどの配線が何の役割で、どこに接続するべきなのかを理解していなかったからです。
最低限と大体でいいのでわかっているとよいです。


ABOUT1/2、BOUT1/2は片方のモーターを制御しています。

モータを動かす動力は乾電池によるものです。そのため1番,6番のVMとGNDは乾電池に接続しています。

そのDCモータードライバーの6番(GND)に対して、乾電池とラズパイ側からの2本が接続しています。この2本は両方ともブレッドボード上のマイナスに接続し、DCモータードライバーの6番からもブレッドボード上のマイナスに接続することで先述の2本と接続できます。(下画像のブレッドボード上の手前の青い配線が該当部分です。)

AIN1/2、BIN1/2はそれぞれのモーターを制御するためのラズパイ側と接続します。

この後のコーディング後の動作確認ではモーターが動かないことが多々ありました。
理由はブレッドボード上での接続不良が原因でした。
DCモータードライバー、モータなどはんだ付けできる箇所は必ず行い、それぞれの配線が正しいことを必ず確認すると解決することができます。


ラジコンを動かすプログラム

今回はラズパイに接続しているキーボードの矢印キーでラジコンカーを操作するようにします。
Pythonのpygameモジュールを使って矢印キーの認識と操作画面を表示してみます。
仮想環境を作って必要なモジュールをインストールしていきます。

# Pythonのバージョンを確認
python -V

# 仮想環境を作成
python3.9 -m venv (環境名)
source (環境名)/bin/activate

# モジュールのインストール
pip install pygame
pip install gpiozero
pip install RPi.GPIO 

モジュールの追加ができたらラジコンカーを操作するプログラムを作成します。
pyファイルを作成して次のコードをコピペすれば大丈夫です。

# 必要なライブラリをインポート
import pygame as pg
import RPi.GPIO as GPIO
import time
import sys


# Pygameの初期化
pg.init()

# 画面を設定
screen = pg.display.set_mode((800, 600))

# フォントとテキストイメージを設定
font = pg.font.Font(None, 50)
descriptionimg = font.render("Command Arrow Key", True, pg.Color("Black"))
going = font.render("GO", True, pg.Color("Blue"))
rightimg = font.render("RIGHT", True, pg.Color("Blue"))
leftimg = font.render("LEFT", True, pg.Color("Blue"))
back = font.render("BACK", True, pg.Color("Blue"))

# ボタンの設定
button_rect = pg.Rect(300, 500, 150, 50)
button_text = font.render("Stop", True, pg.Color("Red"))

# モーターのGPIOピンの設定
motorGpioA1 = 18
motorGpioA2 = 23
motorGpioB1 = 24
motorGpioB2 = 13

# GPIOをBCMモードに設定
GPIO.setmode(GPIO.BCM)

# モーターのGPIOピンを出力モードに設定
GPIO.setup(motorGpioA1, GPIO.OUT)
GPIO.setup(motorGpioA2, GPIO.OUT)
GPIO.setup(motorGpioB1, GPIO.OUT)
GPIO.setup(motorGpioB2, GPIO.OUT)

# モーターを停止状態に初期化
GPIO.output(motorGpioA1, False)
GPIO.output(motorGpioA2, False)
GPIO.output(motorGpioB1, False)
GPIO.output(motorGpioB2, False)

try:
    # メインループ
    while True:
        # 画面を白色で塗りつぶし
        screen.fill(pg.Color("Orange"))

        # 操作説明を表示
        screen.blit(descriptionimg, (200, 100))

        # ボタンを描画
        pg.draw.rect(screen, pg.Color("Grey"), button_rect)
        screen.blit(button_text, (button_rect.x + 30, button_rect.y + 10))

        # キーボード入力を取得
        key = pg.key.get_pressed()

        # キーに応じてモーターの動作を制御
        if key[pg.K_UP]:        
            screen.blit(going, (200, 150))
            GPIO.output(motorGpioA1, True)
            GPIO.output(motorGpioA2, False)
            GPIO.output(motorGpioB1, True)
            GPIO.output(motorGpioB2, False)
        elif key[pg.K_RIGHT]:
            screen.blit(rightimg, (200, 150))
            GPIO.output(motorGpioA1, True)
            GPIO.output(motorGpioA2, False)
            GPIO.output(motorGpioB1, False)
            GPIO.output(motorGpioB2, True)
        elif key[pg.K_LEFT]:
            screen.blit(leftimg, (200, 150))
            GPIO.output(motorGpioA1, False)
            GPIO.output(motorGpioA2, True)
            GPIO.output(motorGpioB1, True)
            GPIO.output(motorGpioB2, False)
        elif key[pg.K_DOWN]:
            screen.blit(back, (200, 150))
            GPIO.output(motorGpioA1, False)
            GPIO.output(motorGpioA2, True)
            GPIO.output(motorGpioB1, False)
            GPIO.output(motorGpioB2, True)
        else:
            # キーが押されていない場合、モーターを停止
            GPIO.output(motorGpioA1, False)
            GPIO.output(motorGpioA2, False)
            GPIO.output(motorGpioB1, False)
            GPIO.output(motorGpioB2, False)

        # 画面を更新
        pg.display.update()

        # ゲームループを制御するためのFPS設定
        pg.time.Clock().tick(60)
        
        # イベントハンドリング
        for event in pg.event.get():
            if event.type == pg.QUIT:
                # プログラム終了時にGPIOをクリーンアップ
                pg.quit()
                GPIO.cleanup()
                sys.exit()
            elif event.type == pg.MOUSEBUTTONDOWN:
                if button_rect.collidepoint(event.pos):
                    # ボタンがクリックされた場合、プログラムを終了
                    pg.quit()
                    GPIO.cleanup()
                    sys.exit()
            elif event.type == pg.KEYDOWN:
                if event.key == pg.K_x:
                    # 'X'キーが押された場合、プログラムを終了
                    pg.quit()
                    GPIO.cleanup()
                    sys.exit()

except KeyboardInterrupt:
    print("KeyboardInterruptが発生しました")

finally:
    # プログラム終了時にGPIOをクリーンアップ
    pg.quit()
    GPIO.cleanup()
    sys.exit()




Pythonを実行してラジコンを動かしてみる

プログラムも完成したので実際にラジコンをプログラムで操作してみます。

Pythonのファイルを実行する前に、ラズパイ、モーター、モータードライバ、乾電池ボックスなどがそれぞれ接続していることを確認します。

準備万端であれば次のコードを実行します。

python rasp_pi_car.py 

実行するとPygameの画面が表示されます。


この時矢印キーを長押しするとプログラム通りにラジコンカーが動きます。
こちらが手元のキーボードで操作しているときの様子です。

思ったよりも俊敏に動いて操作性もよかったです。
ここからまだまだ拡張できそうなので遊びの幅が広がりますね。

次は作ったラジコンをスマホで操作できるようにしてみます。

Vol.2 ラズパイで動くラジコンをスマホで操作してみる




参考にしたサイト

私がラズパイでラジコンカーを作成するにあたって参考にさせていただいたサイトになります。
https://www.kbaba1001.com/entry/2022/03/22/042824
https://zenn.dev/kotaproj/articles/06011c432f9214

タイトルとURLをコピーしました