Roller485 Liteでロボットカー本体を作る

概要

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.pyWindows
(Python)
物理コントローラ (Joystick制御)
入力 (HW): ゲームパッド (Pygame)
出力 (MQTT): Roller485/twin_rpm (手動操作), Roller485/cmd_sw (停止/リセット)
入力 (MQTT): Roller485/position (現在地表示用)
mqtt_to_odom_node.pyWSL2
(ROS 2)
足の感覚 (オドメトリ計算)
入力 (MQTT): Roller485/position (エンコーダ値), Roller485/cmd_sw (リセット)
出力 (ROS): /odom, /tf, /joint_states
処理: 差動二輪モデルに基づく位置推定、座標変換(TF)の配信。
cmd_vel_to_mqtt_node.pyWSL2
(ROS 2)
操縦士 (指令変換ドライバ)
入力 (ROS): /cmd_vel (速度指令)
出力 (MQTT): Roller485/twin_rpm (左右回転数)
処理: 逆運動学計算、スムージング、不感帯処理、通信断絶時の自動停止。
goal_pose_to_cmd_vel_node.pyWSL2
(ROS 2)
司令塔 (ナビゲーション制御)
入力 (ROS): /goal_pose (目的地), /odom (現在地)
入力 (MQTT): Roller485/cmd_sw (緊急停止)
出力 (ROS): /cmd_vel
処理: 予約機能付きP制御移動、RPM80制限、到達後の旋回制御。
mqtt_to_scan_node.pyWSL2
(ROS 2)
目 (Lidarデータ受信)
入力 (MQTT): Roller485/scan (距離データ配列)
出力 (ROS): /scan (LaserScan)
処理: JSON形式の距離データをROS標準のLidar形式に変換。
waypoints_to_goal_pose.pyWSL2
(ROS 2)
ミッションプランナー (巡回指示)
入力: 内部設定リスト (座標・向き)
出力 (ROS): /goal_pose
処理: 定義されたウェイポイントを順次配信し、司令塔に予約させる。
poses_list_to_cmd_vel_node.pyWSL2
(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.py
My_Navi.py (Joy)
My_Navi.ino (実機)ロボットへの移動命令。
前進時は L:負, R:正。
Roller485/position{"Lpos": int, "Rpos": int}
エンコーダの累積カウント値
My_Navi.ino (実機)mqtt_to_odom_node.py
My_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.inopositionトピックの送信開始/停止。
Roller485/scan{"ranges": [float...], ...}
Lidarの距離データ配列
My_Navi.ino (予定)
pub_fake_scan.py
mqtt_to_scan_node.pyLidar実装後に使用。

ROS 2 トピック一覧 (PC内部通信)

トピック名データ型 / ペイロード内容パブリッシュ (送信)サブスクライブ (受信)備考
/cmd_velgeometry_msgs/Twist
並進速度(m/s), 旋回速度(rad/s)
goal_pose_to_cmd_velcmd_vel_to_mqtt「司令塔」から「操縦士」への命令。
IDLE時は送信停止。
/goal_posegeometry_msgs/PoseStamped
目標地点の座標(x,y)と向き
RViz2 (2D Goal Pose)
waypoints_to_goal_pose
goal_pose_to_cmd_velユーザーや巡回スクリプトからの行き先指示。
予約(Queue)可能。
/odomnav_msgs/Odometry
現在位置(x,y)と速度
mqtt_to_odomgoal_pose_to_cmd_vel
RViz2
P制御のフィードバック源。
TFの計算元。
/scansensor_msgs/LaserScan
Lidarの点群データ
mqtt_to_scanRViz2
(将来: Nav2)
障害物の可視化。
実機Lidar実装待ち。
/joint_statessensor_msgs/JointState
タイヤの回転角度
mqtt_to_odomrobot_state_publisher
RViz2
ロボットモデルのタイヤを回して表示するため。
/tftf2_msgs/TFMessage
座標変換 (odom->base_link等)
mqtt_to_odom
robot_state_publisher
RViz2
全ノード
ロボットの位置やパーツ関係を定義する根幹データ。
/robot_descriptionstd_msgs/String
URDF(XML)の記述内容
robot_state_publisherRViz2ロボットの形(青い箱など)を表示するため。
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制御)の計算に利用します。