概要
Roller485 Liteで差動2輪ロボットカー本体を作ってみた。Roller485 Liteを使うメリットは以下の通り。
➀ モータードライバーが不要
② ArduinoスケッチからモーターにアクセスできAPIがある
回転数(rpm)を指定してモーターを回せる
モーターの回転角度を取得できる
③ モーターからGroveケーブルでマイコン(Atom Matrix)に電源を供給できる
ロボットカーの制御は、WSL2上のROS2(jazzy)で制御する。ただしコントローラによる操作は、WSL2での接続が複雑なのでWindows側で行う。
ロボットカーに搭載するマイコンは、容量に不安があるのでmicroROSは使わずにMQTTを使う。
使い方
1.GMKの電源を入れてMQTTを立ち上げる
2.WSL2のターミナルを2つ起動する
3.ロボットカーに電池をつなぎ起動する
4.コントローラをBluetoothにつなぐ
5.My_Mavi.pyを起動する
6.使用するROSのノードをまとめて起動する
ros2 launch My_Navi_basic goal_pose_launch.py
7.コントローラの三角ボタンを押して、/odomのパブリッシュを開始する。
8.Rvizまたはノードからウェイポイントを指定する。
ros2 run My_Navi_basic waypoints_to_goal_pose

ロボット本体の機能はシンプルで次の2つの機能だけを持つ。
➀MQTTからのコマンドで左右のモーターを指定回転数で回す
②

プログラム一覧
| プログラム名 | 動作環境 | 機能概要 (入出力) |
My_Navi.ino | マイコン (Atom Matrix) | ファームウェア (通信翻訳・モーター制御) ● 入力 (MQTT): Roller485/twin_rpm (目標回転数), Roller485/cmd_sw (制御コマンド)● 出力 (MQTT): Roller485/position (エンコーダ値)● 処理: I2Cモーター駆動、安全な加減速制御、0.1秒周期の位置情報送信。 |
My_Navi.py | Windows (Python) | 物理コントローラ (Joystick制御) ● 入力 (HW): ゲームパッド (Pygame) ● 出力 (MQTT): Roller485/twin_rpm (手動操作), Roller485/cmd_sw (停止/リセット)● 入力 (MQTT): Roller485/position (現在地表示用) |
mqtt_to_odom_node.py | WSL2 (ROS 2) | 足の感覚 (オドメトリ計算) ● 入力 (MQTT): Roller485/position (エンコーダ値), Roller485/cmd_sw (リセット)● 出力 (ROS): /odom, /tf, /joint_states● 処理: 差動二輪モデルに基づく位置推定、座標変換(TF)の配信。 |
cmd_vel_to_mqtt_node.py | WSL2 (ROS 2) | 操縦士 (指令変換ドライバ) ● 入力 (ROS): /cmd_vel (速度指令)● 出力 (MQTT): Roller485/twin_rpm (左右回転数)● 処理: 逆運動学計算、スムージング、不感帯処理、通信断絶時の自動停止。 |
goal_pose_to_cmd_vel_node.py | WSL2 (ROS 2) | 司令塔 (ナビゲーション制御) ● 入力 (ROS): /goal_pose (目的地), /odom (現在地)● 入力 (MQTT): Roller485/cmd_sw (緊急停止)● 出力 (ROS): /cmd_vel● 処理: 予約機能付きP制御移動、RPM80制限、到達後の旋回制御。 |
mqtt_to_scan_node.py | WSL2 (ROS 2) | 目 (Lidarデータ受信) ● 入力 (MQTT): Roller485/scan (距離データ配列)● 出力 (ROS): /scan (LaserScan)● 処理: JSON形式の距離データをROS標準のLidar形式に変換。 |
waypoints_to_goal_pose.py | WSL2 (ROS 2) | ミッションプランナー (巡回指示) ● 入力: 内部設定リスト (座標・向き) ● 出力 (ROS): /goal_pose● 処理: 定義されたウェイポイントを順次配信し、司令塔に予約させる。 |
poses_list_to_cmd_vel_node.py | WSL2 (ROS 2) | 巡回型司令塔 (自律完結型) ※ goal_pose... の前身機能● 入力 (ROS): /odom● 出力 (ROS): /cmd_vel● 処理: 外部からの指示を待たず、固定リストを自律的に巡回する。 |
MQTT トピック一覧 (Wi-Fi通信)
| トピック名 | ペイロード形式 (JSON) | パブリッシュ (送信) | サブスクライブ (受信) | 備考 |
Roller485/twin_rpm | {"Lrpm": int, "Rrpm": int}左右モーターの目標回転数 | cmd_vel_to_mqtt_node.pyMy_Navi.py (Joy) | My_Navi.ino (実機) | ロボットへの移動命令。 前進時は L:負, R:正。 |
Roller485/position | {"Lpos": int, "Rpos": int}エンコーダの累積カウント値 | My_Navi.ino (実機) | mqtt_to_odom_node.pyMy_Navi.py | ロボットの現在状態。 0.1秒周期で送信。 |
Roller485/cmd_sw | {"reset": true}オドメトリのリセット | My_Navi.py (□ボタン) | mqtt_to_odom_node.py | 座標を (0,0) に戻す。 |
Roller485/cmd_sw | {"navi_stop": true}ナビゲーション緊急停止 | My_Navi.py (〇ボタン) | goal_pose_to_cmd_vel_node.py | ※直接MQTT受信 自律移動をキャンセルしIDLEへ。 |
Roller485/cmd_sw | {"pos": "on" / "off"}位置情報送信の制御 | My_Navi.py (△ボタン) | My_Navi.ino | positionトピックの送信開始/停止。 |
Roller485/scan | {"ranges": [float...], ...}Lidarの距離データ配列 | My_Navi.ino (予定)pub_fake_scan.py | mqtt_to_scan_node.py | Lidar実装後に使用。 |
ROS 2 トピック一覧 (PC内部通信)
| トピック名 | データ型 / ペイロード内容 | パブリッシュ (送信) | サブスクライブ (受信) | 備考 |
/cmd_vel | geometry_msgs/Twist並進速度(m/s), 旋回速度(rad/s) | goal_pose_to_cmd_vel | cmd_vel_to_mqtt | 「司令塔」から「操縦士」への命令。 IDLE時は送信停止。 |
/goal_pose | geometry_msgs/PoseStamped目標地点の座標(x,y)と向き | RViz2 (2D Goal Pose)waypoints_to_goal_pose | goal_pose_to_cmd_vel | ユーザーや巡回スクリプトからの行き先指示。 予約(Queue)可能。 |
/odom | nav_msgs/Odometry現在位置(x,y)と速度 | mqtt_to_odom | goal_pose_to_cmd_velRViz2 | P制御のフィードバック源。 TFの計算元。 |
/scan | sensor_msgs/LaserScanLidarの点群データ | mqtt_to_scan | RViz2(将来: Nav2) | 障害物の可視化。 実機Lidar実装待ち。 |
/joint_states | sensor_msgs/JointStateタイヤの回転角度 | mqtt_to_odom | robot_state_publisherRViz2 | ロボットモデルのタイヤを回して表示するため。 |
/tf | tf2_msgs/TFMessage座標変換 (odom->base_link等) | mqtt_to_odomrobot_state_publisher | RViz2全ノード | ロボットの位置やパーツ関係を定義する根幹データ。 |
/robot_description | std_msgs/StringURDF(XML)の記述内容 | robot_state_publisher | RViz2 | ロボットの形(青い箱など)を表示するため。 QoS: Transient Local |
================================================================================
システム全体構成図 (ROS 2 - MQTT - Atom Matrix)
================================================================================
[ PC / WSL2 (ROS 2 System) ] [ Network ] [ Robot (Atom Matrix) ]
============================= =========== =======================
[ User / RViz2 ]
(2D Goal Pose Tool)
|
| /goal_pose (PoseStamped)
| [目標座標, 向き]
v
【司令塔】
goal_pose_to_cmd_vel_node
|
| /cmd_vel (Twist)
| [並進速度, 旋回速度]
v
【操縦士】 【ファームウェア】
cmd_vel_to_mqtt_node ---------------------> [MQTT Broker] -----------------> My_Navi.ino
| ^ |
| 変換: 運動学 & 物理計算 | | 受信: onMqttMessage()
| | Topic: Roller485/twin_rpm | 解析: deserializeJson()
| | Payload: {"Lrpm":-50, "Rrpm":50} | 制御: updateSpeedTransition()
| | v
| | [ Roller485 Motors ]
| | (回転動作)
| |
------------------------------------------------------------------------------------------------------
| | |
【足の感覚】 | Topic: Roller485/position | 計測: getPosReadback()
mqtt_to_odom_node <--------------------- [MQTT Broker] <----------------- My_Navi.ino
| | Payload: {"Lpos":1000, "Rpos":...} | 送信: mqttClient.publish()
| 計算: オドメトリ(位置推定) | | (0.1秒周期)
| |
v
/odom & /tf (TF Tree)
|
+-----------------------+
| |
v v
【可視化】 【フィードバック】
RViz2 goal_pose_to_cmd_vel
(ロボットモデル移動) (現在地として利用)
================================================================================
システム構成要素と My_Navi.ino の役割詳細
================================================================================
このシステムは、操作者(RViz)からの指示をROS 2(脳)が処理し、
MQTT(神経)を介してAtom Matrix(脊髄・筋肉)へ伝達するループ構造になっています。
--------------------------------------------------------------------------------
1. 下り:命令の流れ(User -> ROS 2 -> Atom Matrix)
--------------------------------------------------------------------------------
ユーザーの操作によりロボットが動き出すまでのフローです。
(1) User / RViz2 (操作インターフェース)
- ツール: "2D Goal Pose" ボタン
- 役割: 地図上でクリック&ドラッグを行い、目標地点と向きを指定します。
- 出力: トピック "/goal_pose" (PoseStamped) を発行します。
(2) ROS 2 (司令塔)
- ノード: goal_pose_to_cmd_vel_node
- 役割: "/goal_pose" で目標を受け取り、"/odom" で現在地を確認しながら、
目標に近づくための物理的な速度指令(/cmd_vel)を計算して出力します。
(3) ROS 2 (操縦士)
- ノード: cmd_vel_to_mqtt_node
- 役割: 速度指令を受け取り、ロボットのトレッド幅などを考慮して、
「左タイヤを何RPM、右タイヤを何RPMで回せばよいか」を計算します。
- 出力: 計算結果を JSON形式 {"Lrpm": XX, "Rrpm": XX} にして
MQTT トピック "Roller485/twin_rpm" に投げます。
(4) Atom Matrix (ファームウェア: My_Navi.ino)
- 受信: onMqttMessage 関数で "Roller485/twin_rpm" を受信します。
- 目標設定: ペイロードを解析し、received_rpm_L, received_rpm_R に目標値をセットします。
- 駆動: loop 内の updateSpeedTransition 関数が働き、現在の速度から目標速度へ
滑らかに加速・減速しながらモーター(UnitRollerI2C)を回します。
--------------------------------------------------------------------------------
2. 上り:情報の流れ(Atom Matrix -> ROS 2 -> RViz2)
--------------------------------------------------------------------------------
ロボットが「今どう動いたか」をシステム全体にフィードバックするフローです。
(1) Atom Matrix (ファームウェア: My_Navi.ino)
- 起動: 事前に "Roller485/cmd_sw" トピックで {"pos": "on"} を受け取ると、
送信フラグ sendPositionEnabled がONになります。
- 計測: loop 内で 0.1秒ごとに roller_L.getPosReadback() を呼び出し、
モーターの現在の累積回転角度を取得します。
- 送信: 取得した値を JSON形式 {"Lpos": XX, "Rpos": XX} にして、
MQTT トピック "Roller485/position" に投げます。
(2) ROS 2 (足の感覚)
- ノード: mqtt_to_odom_node
- 役割: データを受信し、前回の値との差分から「進んだ距離・回転した角度」を割り出します。
- 出力: その結果を ROS 2 の標準形式(/odom)と座標変換(TF)として配信します。
(3) ROS 2 (可視化・判断)
- ノード: RViz2 / goal_pose_to_cmd_vel_node
- 役割:
[RViz2]: TFを受け取り、画面上のロボットモデルを実機の動きに合わせて移動させます。
[司令塔]: 自分の出した命令でロボットが正しく進んでいるかを /odom で確認し、
次の瞬間の速度指令(P制御)の計算に利用します。