これは何?
SpaceROSに付属するCuriosityローバーのデモ(Gazebo + ROS2)に、NASA JPLの ROSA(Robot Operating System Agent) を接続し、自然言語で「目的」だけ与えると、エージェントがツール実行とフィードバックを繰り返してタスクを完遂する、というデモを作りました。
今回の狙いは「ロボット自律化のフライト適用」ではなく、論理的な解決能力の実証(フィージビリティ) です。計算資源や保証観点から、現時点でフライトコード適用は想定していません。
デモの課題設定(最小の“フレーム問題”)
ユーザーはエージェントに、次のようにだけ指示します。
「地面のテクスチャを調査して(Analyze the ground texture)」
ただし環境には “隠れ条件” があり、開始地点は日陰で暗く、十分に明るい画像が撮れない。明るい領域に移動すれば撮れる。
しかも(本来は)「マスト展開中は移動不可」などの制約が入るので、行動の前提条件を事前に全部If-Thenで書こうとすると破綻します。
このときエージェントに与えるのは「機能(ツール)」だけで、組み合わせ方は教えません。
つまり、無限にあり得る状況×行動の組み合わせから、今この目的に関係する因果だけを抽出できるか(関連性の問題)、さらに 前提条件を事前列挙せず、実行時エラーから回復できるか(限定の問題)を、動く形で確かめる意図です。
今回のVariant(まずは動く最小構成:Variant A)
いきなり「マスト展開/収納の排他制御」まで入れると、デバッグの難易度が上がりすぎるので、まずは Variant A として、
- mast open/close(展開/収納)と排他制御は無効化
- mast rotate(旋回)は有効
- 明るさ探索(暗い→回す→ダメなら移動)に焦点
という形で、安定に再現できるデモにしています。
システム構成:最小追加で「環境」「IF変換」「観測」を足す
SpaceROSの既存Curiosityデモを壊さず、オーバーレイとして以下を追加しました。
-
simulator_node:ローバーのX座標(TF)から明るさスコアを計算し、画像を暗くしたりスコアを焼き込んだりする。さらに/capture_and_scoreを提供する -
adapter_node:Curiosity本来のROSサービス(std_srvs/srv/Empty)を、LLM側が扱いやすいTriggerにラップして公開する(/adapter/*) -
agent_node:ROSAベースのLLMエージェント。ツール呼び出しとトレース出力を担当 -
visualizer_node:/trace/eventsを受けて可視化(思考・行動・結果、正解領域など)
トピック/サービスのI/Fは概ねこんな感じです(抜粋)。
-
/capture/image_raw(画像) -
/trace/events(JSON文字列のトレース) -
/capture_and_score(スコアと合否を返すカスタムsrv) -
/adapter/mast_rotateや/adapter/move_forwardなど(Trigger)
LLMに渡す「ツール」と「観測」
ツールは4つに絞りました。
-
capture_and_score:撮影して明るさスコアを得る(合否is_goodを返す) -
mast_rotate:カメラアームを回転させる -
move_nudge:短時間だけ前進(時間は固定) -
get_status:状態取得
また、「行動→観測→次の行動」を必ず踏ませるために、ROSAの構造化プロンプト(RobotSystemPrompts)の考え方を前提に、結果確認を最優先する指示やReAct形式を入れています。
実行手順(Docker Composeで再現)
このリポジトリ側で .env を用意して、オーバーレイをビルド・起動します。
cp .env.example .env
# または ./overlay_ws/scripts/gen_env.sh を使う
docker compose run --rm curiosity_demo /workspace/overlay_ws/scripts/build_overlay.sh
docker compose run --rm curiosity_demo /workspace/overlay_ws/scripts/run_demo.sh
# RVizありの場合:
docker compose run --rm curiosity_demo /workspace/overlay_ws/scripts/run_demo.sh use_rviz:=true
別ターミナルでエージェントを起動します。
docker compose run --rm curiosity_demo /workspace/overlay_ws/scripts/run_agent.sh
動作例:暗い→旋回→まだ暗い→移動→明るい(が勝手に出る)
実行ログ(例)では、エージェントが capture_and_score のスコアを観測し、mast_rotate、move_nudge を順に呼び出し、最後にスコア1.0で成功しています。
特筆すべき点は、「暗いから移動しろ」という手続き型プログラムを与えていないのに、環境のフィードバックから行動が組み上がる点です。
なぜAdapterを挟むのか(Trigger↔Empty変換と責務分離)
Curiosityデモ側のサービス型と、LLM側で「単純に呼びやすい型」が一致しないことがあります。今回の構成では、
- ロボット側(Curiosity既存)をなるべく触らない
- LLM側は
Triggerで統一して扱う - 変換は
adapter_nodeに閉じ込める
という割り切りにしました。
さらに、SpaceROS(堅牢性が欲しい部分)はコンテナで隔離しつつ、LLM側(柔軟性が欲しい部分)をホスト側/別プロセスとして扱う、という説明も発表資料に含めています。
観測可能性:/trace/events を最初から用意する
LLMエージェントのデモは「動いたように見える」で終わりがちなので、最初から /trace/events にイベントをJSONで流す設計にしました。
イベント種別(OBSERVE/HYPOTHESIZE/DECIDE/ACT/RESULT/ERROR)と、ツール名・エラー理由・スコアなどを載せます。
これにより、RVizの可視化や、ログの後追いがやりやすくなります。
既知の制約と、次にやること(Variant B)
現状はVariant Aで、マスト展開/収納と排他制御は無効です。
次のステップとして、Variant Bでは
-
mast_open / mast_closeを復活 - 「マスト展開中は移動不可」などの排他制御を復活
- 失敗(Need to close mast)から、収納→移動→展開を組み立てられるか検証
を予定しています。
また、より大きな話としては、センサデータと言語概念の結びつけ(記号接地)や、未知失敗検知など、認識・認知側の課題も残ります。
おわりに
「既存ROS(2)デモにLLMエージェントを載せる」話は、華やかさの割に再現性が低くなりがちです。
今回の構成は、最小追加(Simulator/Adapter/Trace/Agent)に絞って、(1)動く、(2)観測できる、(3)拡張できる を優先しました。
同じやり方で、他のSpaceROSデモ(SSRMSなど)にも横展開できるはずなので、次は“制約のあるタスク”に寄せていく予定です。
(リポジトリ、動画、trace例などは追って追記します)