AIエージェントでROS2を動作させてみた〜1.ROS2とは〜
こんにちは、竹内 です。
ロボットAIエージェントを作るために、まずは基本になる ROS 2 を勉強してみました。
作ろうとしているのは「自然言語で指示すると自律的に動くロボット」です。LLM に「2メートル前進して」と伝えると、シミュレータの中のロボットが実際に走り出す——そんな構成を目指しています。
この手の話は「どの LLM を使うか」「プロンプトをどう書くか」に目が行きがちなんですが、実際に手を動かしてみて分かったのは、ハマるところはほとんど LLM の外側にあるということでした。
LLM が出した「前進せよ」という判断を、どうやってモータを回す信号まで届けるのか。その配管を担っているのが ROS 2 です。
今回はその第1回として、AIエージェントを載せる土台として ROS 2 の何を分かっていればいいのかを整理してみます。まずは、やりたいことの全体像から。
ROS 2 は「ロボットの OS」ではない
名前が Robot Operating System なので、私も最初は OS の一種かと思っていました。でも違うんです。Linux や macOS の上で動くソフトウェア群で、一言でいえば「分散プロセス間通信のミドルウェア+共通規約+ツール群」です。
ROS 2 が用意してくれるのは、大きく次の3つ。
- 通信機構:プロセス間・マシン間でメッセージをやり取りする仕組み
- 共通の型と名前:
geometry_msgs/msg/Twistのような標準メッセージ型と、/cmd_velのような慣習的なトピック名 - ツール群:
ros2CLI、RViz(可視化)、rosbag(記録・再生)。通信を覗く口が最初から共通
「ロボットを動かすフレームワーク」というより、「ロボットを構成するたくさんのプログラムを、共通の作法で喋らせるための約束事」。そう捉え直したら、AIエージェントをどこに置けばいいのかもすんなり決まりました。
最小単位の「ノード」は、互いに独立している
ROS 2 の最小単位は ノード(node) で、その実体はひとつの OS プロセスです。ロボットの機能ごとにノードを立てて、それをメッセージで繋いで1台のロボットにしていきます。
この「独立している」という性質が、ROS 2 の設計思想のほぼすべてだと思っています。効いてくるのは次の3点です。
- 1つ落ちても他は生き続ける。 認識ノードがクラッシュしても、制御ノードは動き続けます。
- 起動順を気にしなくていい。 送る側と受け取る側、どちらが先に立ち上がっても大丈夫。相手がまだ居なければ、メッセージがどこにも届かないだけでエラーにはなりません。
- 別マシンでも、別言語でもいい。 同じ LAN・同じ
ROS_DOMAIN_IDなら、コードを1行も変えずにノードを別 PC へ移せます。Python(rclpy)のノードと C++(rclcpp)のノードが、同じトピックで普通に会話します。
ノード同士は関数呼び出しではなくメッセージだけで繋がるので、片方を丸ごと差し替えても、もう片方は気づきません。私のシステムでロボットを二輪車からドローンに変えても、上流のAIエージェントが無改造で済んだのは、まさにこの性質のおかげでした。
繋ぎ方は4種類。図で見るのが早い
ノード同士を繋ぐ手段は4種類あります。ここを選び間違えると後から構造ごと作り直しになるので、最初に押さえておく価値ありです。文章で説明すると長くなるので、ひとつずつ図にしてみました。
① トピック — 投げっぱなしで流し続ける
いちばん使うのがこれです。センサの値やロボットへの速度指令のように、ずっと流れ続けるものはだいたいトピックになります。届いたかどうかは分かりませんが、その代わり速くて軽い。
② サービス — 一問一答
設定の変更や状態の問い合わせなど、すぐ返事が来るもの向けです。逆にいうと、数十秒かかる処理をサービスでやると待たされっぱなしで詰みます。
③ アクション — 時間がかかって、途中でやめたいもの
サービスとの決定的な違いが ②途中経過と④中断です。
代表例が Nav2 の NavigateToPose。「あの座標まで行って」は数十秒かかるし、残り距離は知りたいし、障害物で詰まったらやめたい。まさにアクションの出番です。
④ パラメータ — ノードが持つ設定値
速度の上限や use_sim_time のような設定値です。データを流す通り道ではないので、ここにセンサ値を入れたりしないよう注意です。
迷ったときの目安。流れ続けるならトピック、一問一答ならサービス、時間がかかって中断したいならアクション、外から値を変えたいだけならパラメータ。
通信の中身は DDS。そして QoS でハマる
ROS 2 の通信は自前実装ではなく、DDS(Data Distribution Service) という産業標準の Pub/Sub ミドルウェアの上に乗っています。ROS 2 はその上に薄い抽象層をかぶせて、DDS 実装を差し替えられるようにしているんですね。
各ノードは起動時に「自分はこのトピックをこの型で発行/購読します」とネットワークに広告して、条件が合う相手と勝手に繋がります。設定ファイルに相手の IP を書く必要はありません。ここは本当に楽です。
ただし ROS_DOMAIN_ID(0〜101、既定 0)だけは要注意。同じ LAN で複数人が ROS 2 を動かすとき、これを分けておかないと他人のノードと勝手に繋がります。
そして、繋がる条件は名前だけではありません。型が一致しないと繋がりませんし、QoS(Quality of Service) という通信の性質設定も揃っている必要があります。
QoS では「再送してでも確実に届けるか(RELIABLE)/落ちても構わないか(BEST_EFFORT)」などをトピックごとに指定します。
⚠️ 最大の罠:QoS が合っていないと、名前も型も正しいのに繋がりません。
典型例はセンサ系です。カメラや LiDAR は BEST_EFFORT(落ちてもいいから低遅延)で発行されるのに、購読側を既定の RELIABLE のままにすると一切データが来ません。しかもエラーは出ず、ただ黙るんです。これ、原因に辿り着くまでけっこう時間を溶かしました……。
「トピック名は合っているのにデータが来ない」ときは、型か QoS を疑って ros2 topic info <名前> -v で両側を突き合わせるのが早いです。
この作り、何が嬉しいのか
「関数呼び出しではなく、名前付きのメッセージで繋ぐ」ことの見返りは3つありました。
- 差し替えが効く。 シミュレータを実機ドライバに交換しても、
/cmd_velを出す側は無改造。私のシステムでロボット4種(二輪車/ドローン/メカナム/クローラ)を上流そのままで切り替えられるのも同じ理屈です。 - 覗ける・記録できる・再生できる。 通信の口が共通なので、どのトピックでも同じコマンドで中身を見られます。自前でプロセス間通信を書いていたら、この道具は全部自作でした。
- 割り込める。 手でメッセージを流せば、上流のノードを止めずに動作確認できます。「LLM が悪いのか、ロボット側が悪いのか」を切り分けるとき、これがとにかく効きました。
実際によく使っているのはこのあたりです。
ros2 node list # 動いているノード
ros2 topic list -t # トピックと型
ros2 topic info /cmd_vel -v # 発行者/購読者と QoS(詰まったらまずこれ)
ros2 topic echo /odom # 中身を覗く
# 上流を止めずに、手で操縦してみる
ros2 topic pub -r 10 /cmd_vel geometry_msgs/msg/Twist '{linear: {x: 0.3}}'
AIエージェントは、ROS 2 から見れば「ただのノード1個」
ここまで分かると、冒頭の問い——LLM をどうやってロボットに繋ぐのか——の答えが出ます。
私が作ったAIエージェントのノードは、/agent/instruction(自然言語のテキスト)を購読して /cmd_vel(速度指令)を出す、ただそれだけのノードです。
中で HTTPS を叩いて LLM と会話していることは、他のノードからは見えませんし、知る必要もありません。
AI をノードの内側に閉じ込めて、外との境界は標準の型で保つ。これが設計の肝でした。おかげでロボットもシミュレータも LLM も、それぞれ独立して差し替えられます。
実際、LLM を使わないルールベース実装に切り替えても、下流は一切変わりませんでした。
そして「AIエージェントへの命令」も、ROS 2 から見ればただのメッセージ1本です。UI も LLM も無しで、この一行だけで配線を試せます。
ros2 topic pub --once /agent/instruction std_msgs/String \
"{data: '少し前進してから左に90度回って'}"
まとめ
- ROS 2 は OS ではなく、分散プロセス間通信のミドルウェア+共通規約+ツール群。
- ノードは互いに独立したプロセスで、名前と型が合うメッセージだけで繋がる。だから差し替えが効いて、覗けて、割り込める。
- 繋ぎ方は4種類。流れ続けるならトピック/一問一答ならサービス/時間がかかって中断したいならアクション/値を変えたいだけならパラメータ。
- ハマるのはだいたい型の不一致・QoS の非互換・
ROS_DOMAIN_IDの衝突。どれもエラーではなく「沈黙」で出てくるのが厄介。 - AIエージェントは ROS 2 から見ればただのノード1個。知能はノードの内側に閉じ込める。
次回は、この上に乗せるシミュレータ(Gazebo)の話です。今回のエージェントは「言われた秒数だけ速度を出す」だけなので、目の前に壁があっても平気で突っ込んでいきます。
そこから「地図を持って、自分の位置を知って、障害物を避けて目的地へ行く」段階へどう進むのか——というところに入っていきます。お楽しみに!
