Choreonoid でのシミュレーションをシェルスクリプトから実行し結果を保存する
オープンソースのロボット用統合 GUI ソフトウェア Choreonoid をシミュレーションに活用している. 複数条件での実験をまとめて行う場合,GUI からの操作を繰り返し行うのは手間がかかり,誤操作のリスクもある. もし,シェルスクリプトからシミュレーションを実行し,結果を自動で保存できれば,このような問題を避けられる. Choreonoid にはバックエンドのシミュレータとして活用できるヘッドレスモードが存在するという情報はあるものの,具体的な使用法をまとめたドキュメントは見つけられなかった. そこで,スクリプトから Choreonoid を起動し,シミュレーションの実行と結果の保存を試し,将来,参考にできるように整理しておく.
使用できるスクリプトは Actat/choreonoid-headless-simulation にまとめた. このスクリプトでは GUI での実行と完全一致する結果が得られることを確認したので, GUI での実行を置き換えるものとして活用できる.
実装
Shell script と Python script の 2 つを用いる構成としている. Shell script は外部の実験管理を行い Choreonoid を呼び出す. Python script は Choreonoid の内部 API を操作して,シミュレーションを制御し結果を保存する.
Shell script
この Shell script は行う実験に合わせて変更することを前提としている. シミュレーションの実行だけでなく実験管理用の処理も含めている.
実験管理の面での主な処理内容は以下である.
- 実験ごとのディレクトリ作成
- choreonoid のバージョン記録
- git リポジトリの状態(commit, diff, untracked file)の保存
実際に Choreonoid を起動する部分は下記である.
ウインドウを表示させないで起動するために --no-window オプションを付けている.
また,--python "${script_dir}/run_choreonoid_sim.py" で起動時に Python script を実行させている.
choreonoid \
--no-window \
"${cnoid_path}" \
--python "${script_dir}/run_choreonoid_sim.py" \
2>&1 | tee "${working_dir}/choreonoid.log"
Python script
Python script は Choreonoid の機能を呼び出して以下を行う.
SimulatorItemの取得- シミュレーションの開始と終了待ち
- シミュレーション結果の保存
- Choreonoid の終了
SimulatorItem の取得
RootItem 以下から SimulatorItem を探索して取得する.
sim = None
for item in root.getDescendantItems():
if isinstance(item, SimulatorItem):
sim = item
break
シミュレーションの開始と終了待ち
シミュレーションを開始し,終了まで待機する.
シミュレーション条件はプロジェクトファイル(.cnoid)で定義されたものをそのまま利用する構成としている.
print("start simulation")
sim.startSimulation()
while sim.isRunning():
time.sleep(0.001)
print("Simulation finished")
シミュレーション結果の保存
シミュレーション結果は BodyMotionItem として生成される.
これを探して取得し seq ファイルに保存する.
motion_item = None
for i in range(100):
for item in root.getDescendantItems():
if isinstance(item, BodyMotionItem):
motion_item = item
break
if motion_item is not None:
break
time.sleep(0.01)
if motion_item is None:
print("Dumping items:")
for item in root.getDescendantItems():
print(type(item), item.name)
raise RuntimeError("BodyMotionItem not found")
motion_item.notifyUpdate()
ok = motion_item.motion.save(seq_path)
print("seq file is saved: ", ok)
Choreonoid の終了
app.exit() も試したものの,シェルスクリプトへ制御が戻らず停止してしまった.
そのため,プロセスを明示的に終了することとした.
os._exit(0)
課題
Python script の busy wait
シミュレーションの終了を待つ,モーションアイテムの追加を待つ,の 2 か所で busy wait が使用されている. 理想的にはイベントフックやコールバックで駆動したい.
また,待ち時間や試行回数の設定は根拠なく経験的に行われており,タイムアウト設定としては改善の余地がある.
シミュレーションや結果保存失敗時の処理
現状では,以下のような失敗ケースに対する検出が不十分である.
- シミュレーションの異常終了
- シミュレーションが終了しない
- 結果の保存に失敗
これらを検出し, Shell script への通知できる仕組みを整備する必要がある.
Choreonoid のバージョン記録
再現のための情報として meta.txt にバージョンを記録するようにしている.
現在の実装は apt install を前提としており,ローカルでビルドした Choreonoid には対応できていない.
おわりに
まとまった情報を見つけられず,手探りでの作業となった. 自分は Choreonoid の内部構造を十分には把握していないが,生成 AI の活用で動作するスクリプトを得るところまでたどり着けた. AI 様様である.
本記事は同様の課題に直面している人の参考になることを願う. また,よりよい実装に向けた情報提供をいただけると幸いである.