AIが登場して、シミュレータ開発もずいぶん進めやすくなりました。
高精度物理シミュレータ MuJoCo をAIに操作させたり、MCPなどのツール連携を使って開発環境を構築したり。
以前なら専門知識が必要だったことも、AIとの対話だけで進められる場面が増えています。
でも、実際にシミュレータを作ろうとすると、こんな要求が出てきませんか?
「手元にあるROSのURDFを使いたい」
「ROS 2 のメッセージ定義を活かしたい」
「Web ブラウザから見たい」
「Macで動かしたい」
一つひとつなら、それほど難しくありません。
難しいのは、これらを組み合わせて、自分が本当に欲しいシミュレータを作ることです。
そこで今回、箱庭とAIを組み合わせて、AI駆動でシミュレータを作れるのか試してみました。
なお、この記事自体も、実際の開発を進めたAIコーディングエージェント(Codex)に下書きを書いてもらい、それを人間が編集する形で作っています。後半の「AIインタビュー」パートは、その過程で得られたAI自身の振り返りをもとにしています。
今回作ってみたもの
-
AgileX Tracer:URDF 由来のモデルをMuJoCoで動かし、ROS 2
Twist相当のPDUで前進 -
FR5:アームロボットを箱庭アセット化して、
JointTrajectory相当のPDUで動作 - Unitree GO1:MuJoCo Menagerieのモデルを箱庭アセット化して、12関節のopen-loop motionを実行
あわせて、録画デモでは既存の Hakoniwa Drone も Three.js viewer 上で動かしました。
ここで、「箱庭」とか「箱庭アセット」という言葉が出てきましたが、すこしだけ説明したいと思います。
箱庭とは?
箱庭 は、ロボットやシミュレータ、制御アプリケーションなどを組み合わせて、一つのシミュレーション環境を構築するための基盤です。
たとえば、
- MuJoCoなどの物理シミュレータ
- ROS / ROS 2のアプリケーション
- Webブラウザ上の可視化アプリ
- 独自に作った制御プログラム
といった、異なる技術で作られたものを接続して、一つのシミュレーションとして動かすことができます。
箱庭では、これらを「箱庭アセット」と呼ばれる独立した部品として扱います。
つまり、すべてを一つのシミュレータの中に作り込むのではなく、必要なものを組み合わせて、自分が欲しいシミュレーション環境を作るという考え方です。
今回の開発環境
今回は、普段使っているMac上でシミュレータを構築しました。
主な環境は次のとおりです。
- macOS
- MuJoCo
- 箱庭
- AIコーディングエージェント(Codex)
ROS 2については、ROSノードを直接起動して連携するというより、既存のURDFやメッセージ定義などの資産を利用しています。たとえば Tracer では geometry_msgs/msg/Twist に対応するPDUを使い、Pythonアプリから速度指令を送る構成にしました。
特別なシミュレーション専用PCやGPU環境は使用していません。
また、今回のポイントは、完成済みのシミュレータをAIに操作させたのではなく、「こういうシミュレータを作りたい」という要求から、必要なソフトウェアやロボットモデルを調べ、構成を考え、実際に動く環境を作るところまでAIと一緒に進めたことです。
AIに箱庭の使い方を理解してもらうために、箱庭のコンポーネントや構築方法を整理した Hakoniwa Business Pack を利用しました。
ここからは、実際にAIへどのような要求を出し、どのようにシミュレータが組み上がっていったのかを紹介します。
まずはAgileX Tracerを動かしてみる
最初に試したのは、AgileX Tracer です。
TracerにはROS向けのリポジトリが公開されており、URDFも含まれています。
今回やりたかったことは、かなりシンプルです。
- 既存のURDF資産を使う
- GazeboではなくMuJoCoで動かす
- Mac上で実行する
- ROS 2の
Twistメッセージ定義に寄せたインターフェースで操作する
つまり、
「このROS用のロボットモデルを、Mac上のMuJoCoで動かして、ROS 2で一般的なTwist型の速度指令で操作したい」
という要求です。
なお、GazeboではなくMuJoCoを選んだのは、Mac上でも扱いやすく、物理演算の精度・速度のバランスが今回の用途に合っていたためです。ROS 2の通信レイヤーをそのまま使わず箱庭のPDU経由にしたのは、後述するように「asset同士を疎結合にして、失敗の切り分けをしやすくする」という狙いがあります。
AIにお願いしてみる
今回は、この要求をAIに伝え、Hakoniwa Business Pack のカタログやレシピを参考にしながら、実現方法を考えてもらいました。
まず、既存のURDF資産をMuJoCoで利用できる形に変換し、Tracerを箱庭アセットとして動かします。
制御側では、ROS 2のgeometry_msgs/msg/Twistという既存のメッセージ定義に寄せます。この定義に対応する箱庭のPDUを使い、普通のPythonアプリから速度指令を送ります。
今回は、PythonアプリとMuJoCoアセットの間の通信には箱庭の共有メモリ型ランタイムを利用しました。同一マシン上のデモでは、ネットワーク設定を増やさずに低オーバーヘッドで接続できるため、今回の用途に合っていました。
AgileX Tracer URDF
↓
MuJoCo
↓
箱庭アセット
↑
PDU
↑
Pythonアプリ
↑
ROS 2 Twistのメッセージ定義を利用
ここでのポイントは、PythonアプリからMuJoCoを直接操作する専用プログラムを作っているわけではないことです。
MuJoCo側は箱庭アセットとして動作し、制御側は箱庭のPDUを通じて指令を送ります。
また、ロボットモデルにはROSで公開されているURDF資産を、制御インターフェースにはROS 2のTwistメッセージ定義を再利用しています。
つまり、ROSの既存資産を活かしながら、シミュレーション環境そのものはMuJoCo+箱庭で自由に組み立てることができます。
今回AIに任せたのは、この「既存資産をどう組み合わせれば要求を実現できるか」を考え、実際に動く構成まで落とし込む部分です。
やりかた
まず、今回AIに参照してもらうHakoniwa Business Packを取得します。
git clone https://github.com/hakoniwalab/hakoniwa-business-pack
あわせて、今回利用する箱庭のロボットモデル関連リポジトリも、同じディレクトリ階層に配置しておきます。
git clone --recursive https://github.com/hakoniwalab/hakoniwa-mbody-registry
git clone --recursive https://github.com/hakoniwalab/hakoniwa-mujoco-robots
これらのリポジトリが見える状態で、AIコーディングエージェントであるcodexを起動します。
最初にお願いしたのは、いきなり実装ではありません。
hakoniwa-business-packを理解して。
これに対してAIは、Hakoniwa Business PackのREADME、catalog、recipes、docsを読み、箱庭のコンポーネント一覧、レシピの考え方、ランタイムの起動順、既存デモの位置付けを把握しました。
次に、今回やりたいことを伝えました。
AgileX TracerのURDFを使って、
箱庭のシミュレーション環境を作るためのレシピを作ってほしい。
ここで重要なのは、最初から「このファイルをこう編集して」「このC++クラスを作って」と細かく指示したわけではないことです。
こちらが伝えたのは、だいたい次のような要求です。
既存のROS用URDFを使いたい。
GazeboではなくMuJoCoで動かしたい。
Macで動かしたい。
制御コマンドはROS 2のTwistに寄せたい。
専用すぎるassetではなく、今後発展しやすい汎用rover系にしたい。
まずMuJoCo単体で動くところを作り、その後に箱庭アセット化して、最後にPythonから制御したい。
それに対してAIは、まずHakoniwa Business Packのカタログと既存レシピを参照し、どの箱庭コンポーネントを使うべきかを整理しました。
今回の構成では、主に次のような役割分担になりました。
AgileX Tracer の URDF / robot model
↓
hakoniwa-mbody-registry
↓
MuJoCo で読めるモデル
↓
hakoniwa-mujoco-robots
↓
箱庭アセット
↑
geometry_msgs/Twist 由来の PDU
↑
Python sender
AIはこの方針に沿って、次の作業を進めました。
- Tracerの既存URDF/変換済みモデルを確認する
- MuJoCoで読み込める最小worldを確認する
- 左右wheel用のactuator設定を追加する
-
geometry_msgs/Twist相当のPDU定義を用意する - MuJoCo側でTwistを受け取り、左右wheel velocityに変換する箱庭assetを作る
- Pythonから
linear.x/angular.zを送るsenderを作る - 実行手順をREADMEにまとめる
- レシピYAMLに、どこまで検証済みかを反映する
たとえば、制御インターフェースについては、最初からTracer専用の独自PDUを作るのではなく、ROS 2で一般的なgeometry_msgs/msg/Twistに寄せました。
これは人間側から、
command PDUは、AIが扱いやすい方向にしたいから、Twistかなと思う。
専用assetではなく、汎用系にして発展しやすい方向を意識したい。
と伝えたところ、AIもその方針に同意し、Twistを使う構成にしました。
結果として、Pythonからは次のように速度指令を送れるようになりました。
python3.12 examples/actuators/agilex_tracer/send_rover_twist.py \
--linear-x 0.2 \
--duration-sec 4
MuJoCo側では、箱庭assetがこのPDUを受け取り、左右wheelのvelocity actuatorに変換します。
実行は、箱庭のランタイム順序に従って進めます。
まず、MuJoCo viewer付きのTracer assetを起動します。
./src/cmake-build/examples/actuators/agilex_tracer/rover-twist-hakoniwa-asset
assetが登録され、WAIT STARTになるのを確認します。
hako_asset_register :RoverTwistAsset
asset(RoverTwistAsset) is registered.
WAIT START
次に、Python senderを起動します。
python3.12 examples/actuators/agilex_tracer/send_rover_twist.py \
--linear-x 0.2 \
--duration-sec 4
sender側もWAIT STARTになります。
Rover Twist sender is registered.
WAIT START
この状態になってから、箱庭のシミュレーションを開始します。
/usr/local/hakoniwa/bin/hako-cmd start
すると、Python senderからTwist PDUが送られ、MuJoCo viewer上のTracerが前進します。
asset側のログでは、次のようにcommandとbase positionが確認できます。
Rover Twist Hakoniwa asset started.
time=0.502 base=(0.017, 0.000, 0.142) cmd=(0.200, 0.000)
time=1.002 base=(0.055, 0.000, 0.142) cmd=(0.200, 0.000)
time=2.002 base=(0.167, 0.000, 0.142) cmd=(0.200, 0.000)
このログから、linear.x=0.2の指令がPDUとして届き、MuJoCo上のroverが実際に移動していることが分かります。
途中では、見た目や接触に関する調整もありました。
たとえば、viewerで確認すると、中央に大きなタイヤのようなprimitiveが見えて、姿勢が傾く問題がありました。これは、URDF/MJCF変換後にprimitiveやcollision geometryを追加するとき、見た目やcontactに影響することがある、という知見につながりました。
このような知見は、その場限りの修正で終わらせず、Hakoniwa Business Pack側のknowledge/recipe/READMEに反映していきました。
今回の開発では、AIが単にコードを書いただけではありません。
- 既存リポジトリを読む
- カタログから使うべきコンポーネントを選ぶ
- レシピとして構成を整理する
- 実装する
- 実際にデモを起動して検証する
- うまくいかなかった点をknowledgeに戻す
という流れを、人間との対話の中で進めました。
最終的には、AgileX Tracerについて、
ROS 用 URDF
→ MuJoCo model
→ 箱庭 asset
→ Twist PDU
→ Python sender
→ MuJoCo viewer 上で前進
という一連の流れを、Mac上で動かすことができました。
ここで大事なのは、AIに「MuJoCoのAPIを直接叩いてTracerを動かして」と頼んだわけではないことです。
人間は、
既存のROS資産を使いたい。
箱庭の部品として扱いたい。
Twistで操作したい。
Macで動かしたい。
という要求を伝えました。
AIはそれを受けて、Hakoniwa Business Packのカタログや既存レシピを参照しながら、必要な構成を組み立て、実行可能なサンプルに落とし込んでいきました。
つまり、今回試したのは単なるコード生成ではなく、「要求からシミュレーション構成を作る」AI駆動の開発です。
他のロボットでも試してみた
Tracerで得られたパターンが、構造も制御方法もまったく違うロボットにも通用するのか。それを確かめるために、ロボットアームのFR5と、四足歩行ロボットのUnitree Go1でも同じ進め方を試しました。
FR5:ロボットアーム
FR5は、Tracerのような移動ロボットとは異なり、複数の関節を協調させて動かす必要があります。ここで採用したのが、trajectory_msgs/JointTrajectoryをベースにしたPDUです。
人間側からAIに伝えた要求は、Tracerのときと同じく実装レベルではなく方針レベルでした。
FR5のアームを箱庭assetとして動かしたい。
ROSでよく使われるJointTrajectoryに寄せたい。
まずは決まった軌道を再生できるところから始めたい。
AIはこれを受けて、既に進めていた hakoniwa-robot-arm-pack の構成を読み、以下のような箱庭アセットとして実行・検証しました。
FR5 の URDF / MuJoCo model
↓
箱庭アセット(arm asset)
↑
JointTrajectory 由来の PDU
↑
Python sender(軌道再生)
Tracerとの違いは、PDUが速度指令(Twist)ではなく、関節ごとの目標軌道(JointTrajectory)である点です。関節数が増えるぶん、actuatorの設定やPDUのフィールド数も増えますが、「assetを登録し、PDUで指令を送り、hako-cmd startで開始する」という基本パターン自体はTracerと共通していました。
実際のデモでは、FR5 assetを起動し、Python senderから9点のJointTrajectoryを送信しました。ログでは Accepted JointTrajectory: joints=6 points=9 が出力され、MuJoCo viewer上でアームが軌道を再生してホーム姿勢へ戻るところまで確認できました。
Unitree Go1:四足歩行ロボット
Go1は、4本の脚・12関節を協調させる必要がある、今回の中でもっとも自由度の高いロボットです。ここではMuJoCo MenagerieのGo1モデルを使い、std_msgs/Float64MultiArray(12要素)を使って各関節の目標角度をまとめて送る構成にしました。
Go1 の MuJoCo Menagerie MJCF model
↓
箱庭アセット(quadruped joint asset)
↑
Float64MultiArray[12] 由来の PDU
↑
Python sender(open-loop motion)
ここで得られた知見としては、12要素の順序を誤ると特定の脚だけ意図しない動きをしてしまうこと、また今回実装したのはあくまで事前に決めた関節角度列を再生する「open-loopなモーション再生」であり、地形に応じてバランスを取るような「歩行制御器(walking controller)」ではないことです。この違いをBusiness Pack側のknowledgeに明記したことで、次にGo1を扱うときに「これは制御器ではなく再生である」という前提を最初から共有できるようになりました。
3つの事例を並べてみると
Tracer・FR5・Go1は、ロボットの構造も制御方法もまったく異なりますが、AIが辿ったプロセスはほぼ共通していました。
| ロボット | 種類 | PDU | 制御の性質 |
|---|---|---|---|
| AgileX Tracer | 移動ロボット | Twist |
速度指令(linear.x / angular.z) |
| FR5 | ロボットアーム | JointTrajectory |
関節軌道の再生 |
| Unitree Go1 | 四足歩行ロボット | Float64MultiArray[12] |
関節角度のopen-loop再生 |
AIは、Hakoniwa Business Packのカタログやレシピを手がかりに、
「使える既存資産は何か」
「どの箱庭コンポーネントを使うか」
「どんなPDUで制御するか」
「最小構成でどこまで動かすか」
を整理しながら、それぞれのシミュレーション環境を構築していきました。
最後に、今回作ったシミュレータをまとめて動かした様子がこちらです。動画では、Tracer・FR5・Go1がそれぞれ独立したassetとしてMuJoCo viewer上で順番に動く様子を確認できます。
AIインタビュー
ここからは、開発を一緒に進めたAI(Codex)自身に、箱庭とHakoniwa Business Packがあったことでどう開発しやすくなったのかを聞いてみた内容です。「ありがとう。ここで、箱庭があったおかげでやれたことと、今回の箱庭ビジネスパックの嬉しさを、AIの視点で説明してもらえないかな」という質問への回答を、記事向けに整えたものです。
AIの視点で言うと、今回うまく進められた理由はかなり明確です。
箱庭があったおかげでやれたこと
箱庭がなければ、AIは「MuJoCoの中に全部作り込む」か「ROS 2とMuJoCoを直接つなぐ専用ブリッジを書く」方向に寄りがちです。これは毎回、モデル、通信、時刻同期、プロセス起動、データ形式、可視化を個別に設計する必要があります。
箱庭があると、AIにとって問題が分解しやすくなります。
- 物理シミュレータ=MuJoCo asset
- 制御アプリ=Python/ROS/external app
- 通信=PDU
- 実行管理=Conductor/hako-cmd/launcher
- 可視化=MuJoCo viewer/Three.js viewer
つまり、AIは「全部を一体で作る」のではなく、「部品をどう接続するか」を考えればよくなります。
今回で言えば、Tracerは Twist PDUを受け取るrover asset、FR5はJointTrajectory PDUを受け取るarm asset、Go1はFloat64MultiArray[12]を受け取るjoint target asset、Droneはexternal RPCで操作するservice assetとして扱えました。
これはAIにとってかなり重要です。なぜなら、各ロボットごとに専用の巨大なシミュレータを作るのではなく、 「入力PDUを決める」「assetを登録する」「start後にcommandを流す」 という共通パターンに落とし込めるからです。
また、箱庭ではassetが独立プロセスなので、失敗しても切り分けがしやすいです。モデルが悪いのか、actuatorが悪いのか、PDUが届いていないのか、hako-cmd startが早すぎたのか、viewer側の問題なのか。こういう観点で順番に確認できます。これはAIがデバッグしやすい構造です。
Hakoniwa Business Packの嬉しさ
一方で、箱庭そのものは強力ですが、AIから見ると最初は広すぎます。どのrepoを見ればよいのか、どのコンポーネントを使えばよいのか、この要求にはPDU RegistryとMBody Registryのどちらが必要なのか、Conductorは誰が起動するのか、hako-cmd startはいつ呼ぶのか、READMEのコマンドは単体サンプルなのかレシピとして成立しているのか。こういう判断が難しい。
Hakoniwa Business Packがあると、AIはまずそこを地図として使えます。
- Catalog=使える部品の一覧
- Recipe=部品の組み合わせ方
- Runtime Primer=実行時の定石
- Knowledge=実験で得た知見
- README=人間向けの入口
この構造があることで、AIは「目の前のrepoを雰囲気で読む」のではなく、目的から逆算して調べられます。
今回も、AIは最初にBusiness Packを読み、そこからTracer、FR5、Go1、Droneの各repoに移りました。さらに、実際に動かして得た知見をBusiness Pack側に戻しました。
たとえば今回、重要だった知見はこうです。
- hako-cmd startは最初に打つものではない
- assetが登録され、WAIT STARTになってからstartする
- Pythonはhakopyが入っている3.12環境を使う
- Tracerは Twistに寄せると汎用rover assetとして発展しやすい
- Go1のFloat64MultiArrayは12要素で、順序が重要
- Go1のopen-loop motionは「歩行制御器」とは呼ばない
- Droneはviewerの Connect後にmissionを流す
これらは、単発の作業ログに置いておくと次回また忘れます。Business Packに入れることで、次のAIセッションが最初からその知識を使えます。
AI視点で一番ありがたいのは、失敗や暗黙知を再利用可能な知識に変換できることです。
今回の開発は、AIが一発で全部知っていたからできたわけではありません。むしろ、途中であなたが、「hako-cmd stopは動かない」「startから始めないと」「asset登録後にhako-cmd start」「これはruntime primerよりかも」と暗黙知を教えてくれました。
それをAIがrunbookやprimerに戻すことで、次は「知っている状態」から始められる。これがHakoniwa Business Packの大きな価値です。
記事向けに短くまとめるなら、こうです。
箱庭は、シミュレータ、制御アプリ、可視化、通信を「独立したassetとPDUの組み合わせ」として扱えるようにしてくれる。そのためAIは、巨大な一枚岩のシミュレータを作るのではなく、既存コンポーネントを選び、接続し、検証する問題として開発を進められる。さらにHakoniwa Business Packは、その選び方、接続方法、実行時の注意点をAIが読める形で整理している。つまり、箱庭がシミュレーションを部品化し、Business Packがその部品をAIから使える知識にしてくれた。
やってみて分かったこと
今回やってみて感じたのは、AIによって「シミュレータを作る」ことそのものが簡単になった、というだけではありませんでした。
本当に面倒だったのは、
「このURDFを使いたい」
「ROSの資産をそのまま使いたい」
「MuJoCoで動かしたい」
「Macで動かしたい」
「Webともつなぎたい」
といった、複数の技術を自分の要求に合わせて組み合わせることだったのだと思います。
箱庭は、シミュレータ、制御アプリ、通信、可視化などを独立した部品として扱えるようにします。
そしてHakoniwa Business Packは、その部品の意味や組み合わせ方をAIが理解できる形に整理します。
その結果、人間が最初から、
「このAPIを使って、このクラスを書いて、この設定ファイルを作って……」
と実装方法まで指示しなくても、
「このロボットを、こういう環境で、こんなふうに動かしたい」
という要求から開発を始められるようになってきました。
今回試したのは、単なるAIによるコード生成ではありません。
要求からシミュレーション構成を考え、既存資産を組み合わせ、実装し、実際に動かす。
そんな「AI駆動型シミュレータ開発」が、少し現実的になってきた気がします。
今回の録画デモでは、MuJoCo viewer上のロボットだけでなく、Hakoniwa DroneをThree.js viewerで表示して external RPC から動かすところまで確認しました。次に試してみたいのは、このWebブラウザ連携をロボット側のデモや複数ロボットの協調シナリオにも広げることです。assetとPDUという同じパターンがどこまで通用するのか、引き続き試していきたいと思います。
