並行輸入mBotトラブルを乗り越えて!Raspberry Pi Zero Wで作るBluetooth対応自作ラジコン
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に技適マークがついていませんでした。そのまま使うと違法電波を出してしまうので、取り外して使用しています。
解決策
①raspberry pi zero Wを用いてbluetooth接続機能を備えたロボットを作成する
②mbotに搭載されているGUIベースの動作組み立て機能は、Node-REDなどのヴィジュアルフローエディタで代替する
今回は①を実施する。
材料リスト
- Raspberry Pi Zero W(最新のRaspberry Pi OSをインストール)
- MX1508モータドライバモジュール (2チャンネル、1.8–5 V駆動)
MX1508
- DCモータ ×2(車軸用)
モーター - 3Dプリンタ筐体、タイヤホイール(自作データを3Dプリンタで出力)
- キャスター保護用シリコンゴムタイヤ(Seria)

- Bluetoothリモコン
エレコム VRリモコン コントローラー - ワイヤ、ジャンパーケーブル、バッテリー(5–9 V)
ハードウェア組み立て
シャーシとホイールの3Dプリント
3Dプリンタで設計データを出力し、ホイールと筐体を作成します。筐体上部にPi Zero Wを固定するスペースを確保するのがポイントです。
モータとドライバ基板の配線
MX1508モジュールのVCC/GNDをバッテリー供給に接続し、IN1/IN2をPiのGPIOへ配線します。モータはOUT1/OUT2にそれぞれ接続します。ドライバ基板の仕様は公式チュートリアルを参照してください。
タイヤの取り付け
100円ショップのシリコンゴムタイヤをホイールに取り付け、ダミーホイールを前中央に配置して後輪駆動構成にします。ゴムタイヤはSlip防止に効果的です。
本家のmbot
ソフトウェア設定
Bluetoothゲームパッドのペアリング
Raspberry Pi のbluetoothdでコントローラをスキャンし、ペアリング・トラスト設定を行います。
Bluetoothの接続に関しては、いろいろと課題があるので、後日詳しい記事を掲載予定です。
コード解説
- 入力取得 (
evdev)/dev/input/event2からREL_X/REL_Yイベントを読み込み、スティックの傾きとボタン押下を検知します。
- PWM制御 (
pigpio.hardware_PWM)- 各GPIOに50 HzのPWM信号を出力し、速度指令をデューティ比に変換します。
- 差動走行計算
- 前後(throttle)と旋回(steer)の値から左右モータ速度を計算し、クリッピングで±100%に収めています。
- 停止・緊急停止
- 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精神あふれるロボット工作を楽しめる内容となっています。今後は制御アルゴリズムの高度化やセンサ統合を進め、さらなる自動化を目指しましょう。















ディスカッション
コメント一覧
まだ、コメントがありません