並行輸入mBotトラブルを乗り越えて!Raspberry Pi Zero Wで作るBluetooth対応自作ラジコン

mbotもどき

Amazon で教育用ロボットキットの mBot を購入しました。子ども向けのビジュアルプログラミング入門に最適と評判だったためワクワクしながら組み立ててみたのですが、手元に届いたのは並行輸入品。Bluetooth モジュールを見てみると技適マークがない。そのまま電源を入れると違法電波を出してしまうので、Bluetoothモジュールを外して動かすことに。
結果、当初の目論見であったスマホやタブレットからのワイヤレス操作は封印。赤外線リモコンでの遠隔操作、ライントレーサモード、超音波センサによる障害物検知モード、USB ケーブル経由でのプログラム書き込み、という初期機能だけが残されました。「Bluetooth が使えないなら…」と歯がゆさを噛みしめつつも、ふとアイデアが閃きます——

「mbot がだめなら、自分で Bluetooth 接続できるロボットをつくればいいじゃない!」

こうして並行輸入品トラブルをきっかけに、Raspberry Pi Zero W と市販 DC モータ、3D プリント筐体を組み合わせた自作ラジコンプロジェクトが始まったのでした。Raspberry Pi Zero Wを使い、MX1508(L298相当)の制御基板でDCモータを駆動するラジコンを自作します。3Dプリンタで設計・出力したシャーシとホイール、100円ショップのシリコンゴムタイヤを組み合わせることで、コストを抑えつつ堅牢な車体を作成できます。Bluetoothリモコンは evdev ライブラリで読み取り、pigpio のハードウェアPWM機能で滑らかなモータ制御を実現しています。

目次

課題

Amazonで販売していたピンク色のmbotは並行輸入品のようでbluetoothに技適マークがついていませんでした。そのまま使うと違法電波を出してしまうので、取り外して使用しています。

mbotのbluetoothモジュール

解決策

①raspberry pi zero Wを用いてbluetooth接続機能を備えたロボットを作成する
②mbotに搭載されているGUIベースの動作組み立て機能は、Node-REDなどのヴィジュアルフローエディタで代替する

今回は①を実施する。

材料リスト

ハードウェア組み立て

シャーシとホイールの3Dプリント

3Dプリンタで設計データを出力し、ホイールと筐体を作成します。筐体上部にPi Zero Wを固定するスペースを確保するのがポイントです。


モータとドライバ基板の配線

MX1508モジュールのVCC/GNDをバッテリー供給に接続し、IN1/IN2をPiのGPIOへ配線します。モータはOUT1/OUT2にそれぞれ接続します。ドライバ基板の仕様は公式チュートリアルを参照してください。

タイヤの取り付け

100円ショップのシリコンゴムタイヤをホイールに取り付け、ダミーホイールを前中央に配置して後輪駆動構成にします。ゴムタイヤはSlip防止に効果的です。

mbotもどき

本家のmbot

mbot

ソフトウェア設定

Bluetoothゲームパッドのペアリング

Raspberry Pi のbluetoothdでコントローラをスキャンし、ペアリング・トラスト設定を行います。
Bluetoothの接続に関しては、いろいろと課題があるので、後日詳しい記事を掲載予定です。

コード解説

  1. 入力取得 (evdev)

    • /dev/input/event2 からREL_X/REL_Yイベントを読み込み、スティックの傾きとボタン押下を検知します。

  2. PWM制御 (pigpio.hardware_PWM)

    • 各GPIOに50 HzのPWM信号を出力し、速度指令をデューティ比に変換します。

  3. 差動走行計算

    • 前後(throttle)と旋回(steer)の値から左右モータ速度を計算し、クリッピングで±100%に収めています。

  4. 停止・緊急停止

    • BTN_LEFT押下で全モータの停止フラグを立て、速やかに停止させます。

<pre>from evdev import InputDevice, ecodes
from select import select
import time
import pigpio

pi = pigpio.pi()

# PWM 出力用ピンの BCM 番号(横もち)
LEFT_FWD_PIN  = 19  # PWM1_CH1
LEFT_REV_PIN  = 13  # PWM1_CH0
RIGHT_FWD_PIN = 12  # PWM0_CH0
RIGHT_REV_PIN = 18  # PWM0_CH1

# # PWM 出力用ピンの BCM 番号(縦)
# LEFT_FWD_PIN  = 12  # PWM1_CH1
# LEFT_REV_PIN  = 13  # PWM1_CH0
# RIGHT_FWD_PIN = 18  # PWM0_CH0
# RIGHT_REV_PIN = 19  # PWM0_CH1

for pin in (LEFT_FWD_PIN, LEFT_REV_PIN, RIGHT_FWD_PIN, RIGHT_REV_PIN):
    pi.set_mode(pin, pigpio.OUTPUT)  # ハードウェア PWM チャネルを有効

# PWMインスタンス
def set_pwm(pin_fwd:int, pin_rev:int, speed:int):
    """
    pin_fwd: 前進用ピン
    pin_rev: 後退用ピン
    speed: -100~+100 (%)
      +: 前進, -: 後退, 0: 停止
    """
    # PWM 周波数 50Hz とデューティ比(0~1e6)
    FREQ = 50
    if speed >= 0:
        duty = int(speed / 100 * 1000000)
        print('pin: {}, FREQ: {}, speed: {}'.format(pin_fwd, FREQ, duty))
        print('pin: {}, FREQ: {}, speed: {}'.format(pin_rev, FREQ, duty))
        pi.hardware_PWM(pin_fwd,  FREQ, duty)  # 前進デューティを設定
        # pi.hardware_PWM(pin_rev,  FREQ, 0)
        pi.set_mode(pin_rev, pigpio.OUTPUT)
    else:
        duty = int(-speed / 100 * 1000000)
        print('pin: {}, FREQ: {}, speed: {}'.format(pin_rev, FREQ, duty))
        # pi.hardware_PWM(pin_fwd,  FREQ, 0)
        pi.set_mode(pin_fwd, pigpio.OUTPUT)
        pi.hardware_PWM(pin_rev,  FREQ, duty)  # 後退デューティを設定

def set_left_motor(speed:int):
    set_pwm(LEFT_FWD_PIN, LEFT_REV_PIN, speed)

def set_right_motor(speed:int):
    set_pwm(RIGHT_FWD_PIN, RIGHT_REV_PIN, speed)


dev = InputDevice('/dev/input/event2')  # 実際のデバイスに合わせて
dev.grab()  # 排他制御

def read_stick():
    """
    /dev/input/event2 から REL_X/REL_Y のイベントを読み取り、
    SYN_REPORT が来たところで x, y を返す。
    戻り値の x, y は、それぞれ -31 .. +31 の範囲を想定。
    """
    x = 0
    y = 0
    mstop = True

    # デバイスにイベントが来るまでブロック
    r, _, _ = select([dev.fd], [], [])
    if dev.fd in r:
        for event in dev.read():
            # REL_X/REL_Y イベントを足し込む
            if event.type == ecodes.EV_REL:
                mstop = False
                if   event.code == ecodes.REL_X:
                    x += event.value
                elif event.code == ecodes.REL_Y:
                    y += event.value
            elif event.type == ecodes.EV_KEY and event.code == ecodes.BTN_LEFT and event.value == 1:
                # BTN_LEFT が押されたら停止フラグを立てる
                mstop = True
            # SYN_REPORT で一連のイベントが一区切り
            elif event.type == ecodes.SYN_REPORT:
                break

    # 値をクランプ(念のため)
    # x = max(-31, min(31, x))
    # y = max(-31, min(31, y))
    tx = max(-31, min(31, -y))
    ty = max(-31, min(31, x))
    x = tx
    y = ty
    return x, y ,mstop

try:
    while True:
        # 例:スティックの値から -100~+100 の speed を算出
        lx, ly, mstop = read_stick()      # -31~+31 の値を返す想定
        # 前進後退成分
        throttle = int(ly / 31 * 100)
        # 旋回成分
        steer    = int(lx / 31 * 100)

        if(mstop):
            left_speed = 0
            right_speed = 0
        else:
            # 差動走行の左右速度計算
            left_speed  = throttle + steer
            right_speed = throttle - steer

            # クリッピング
            left_speed  = max(-100, min(100, left_speed))
            right_speed = max(-100, min(100, right_speed))

        # モーター出力
        set_left_motor(left_speed)
        set_right_motor(right_speed)

        time.sleep(0.1)

except KeyboardInterrupt:
    # 停止
    set_left_motor(0)
    set_right_motor(0)
    pi.stop()
</pre>

動作確認と今後の展望

  • 動作確認:平坦路でスティック操作による前後・旋回をテスト。タイヤのグリップとモータ応答を調整してください。

  • 改善案:PID制御による速度安定化や、超音波センサを用いた障害物検知の自律走行化が考えられます。Wi-Fi経由のWeb UI制御やスマホアプリ連携も次のステップです。

まとめ

本稿では、低コストかつ手軽に入手できる部材を組み合わせ、Raspberry Pi Zero Wを用いた自作ラジコンの設計から動作までを解説しました。3Dプリンタでの筐体作成やPythonライブラリの活用により、DIY精神あふれるロボット工作を楽しめる内容となっています。今後は制御アルゴリズムの高度化やセンサ統合を進め、さらなる自動化を目指しましょう。

DIY&修理

Posted by 納戸 工房