箱庭のカメラ|作ってみた ― カメラより先に、コアを直した


DEVELOPMENT NOTE / 2026.10.25

2 日で、走る車の「目」ができた。その途中で、箱庭のコアも一段強くなった。

第 1 回では、カメラを「時刻を渡すと絵が返ってくる関数」にした、という話をしました。とはいえ、最初からそう見えていたわけではありません。第 2 回は、作り始めた 2 日間(2026 年 10 月 23 日と 24 日)の記録です。

出発点:カメラの設定は、もうあった

朝起きて、さあ作ろうとしたところで思い出しました。箱庭の MuJoCo ロボットには、カメラの設定ファイルが既にありました。形は SDF に似せてあります。SDF は、ロボットやセンサーの姿を書き表すための形式で、ロボットのシミュレータ Gazebo などで使われています。画角、画像の大きさ、写る距離の範囲、ノイズ、撮る間隔を書けます。

これを、自動運転のシミュレータ CARLA や、シナリオの標準 OpenSCENARIO で扱うくらいまで広げればいい。そう考えて、まずは設定の決まり(スキーマ)から手を付けました。

設定は、カメラの物理で書く

CARLA のカメラの設定と、SDF の最新版(1.12)のカメラを調べ、3 つのことを決めました。

  • 「カメラ」と「何を取り出すか」を分ける:ISO 感度、シャッター速度、絞り(F 値)、色温度、レンズといったカメラそのものの性質と、色の絵・距離・写っている物の種類といった取り出したい情報を、別々に書きます。
  • 物理の単位で書く:描画ソフトの設定値ではなく、秒、メートル、度のような物理の量で書きます。あとで本物のカメラや別の描画ソフトに合わせるときも、設定を書き直さずに済みます。
  • CARLA に合わせた値は、別に持つ:CARLA と同じ設定にすることはできます。ただ、同じ設定にしても、同じ絵になるとは限りません(描画のソフトも、明るさの整え方も違うからです)。そこで、CARLA に合わせた値は「CARLA 互換」の設定として分けて持ちます。

Godot につなぐ

絵を描くのは、ゲームエンジンの Godot です。箱庭は既に Godot と共有メモリでつながっていたので、つなぐ部分は少しの改造で済みました。

ただ、つなぎ方には工夫が要りました。箱庭のサンプルの多くは、箱庭のほうから決まったタイミングで呼び出してもらう作りです。ゲームエンジンは自分で画面を回し続けているので、外から呼び出してもらう形にはできません。そこで、ゲームエンジンのほうから箱庭の様子を見に行き、そのとき使ってよい機能だけを使う作りにしました。

小さなつまずきもありました。Godot を画面を出さずに起動すると、絵を描いてくれないのです。カメラ用の Godot は、画面を出して起動することにしました。

カメラより先に、コアを直した

1 日目の夜、思わぬものが見つかりました。AI と一緒にカメラをつないでいたところ、AI が、これまでの箱庭ではほとんどしなかった使い方を始めたのです。ゲームエンジンを起動したまま、シミュレーションを止めては始め直す。これを繰り返すと、2 回目からは、箱庭の呼び出しの仕組み(サービス)がつながらなくなりました。

その晩のうちに、2 時間ほどで、起きたことを再現する小さなプログラムを作り、原因を突き止めて直しました。始め直しを確かめるテストも、自動テストに加えました。第 1 回で「最初の半日はコアのバグと戦っていた」と書きましたが、記録を見直すと、実際は 2 時間ほどでした。笑

この直しは、同じコアを使う製品(Drone PRO)にも入れて、回帰テストまで確かめました。詳しくは issue にまとめてあります。カメラを作るつもりが、まずは箱庭のコアを一段強くすることになりました。

カメラの作り:撮る時刻を決めるのは、カメラ係だけ

ここが、箱庭のカメラでいちばん伝えたいところです。

箱庭のカメラの作り。シミュレーションの世界から、カメラ係が時刻 t の車の位置を読む。カメラ係は Godot と MuJoCo Filament に同じ依頼を出し、返ってきた絵を、時刻のそろった 4 つの PDU として書く
箱庭のカメラの作り。撮る時刻と位置を決めるのはカメラ係だけで、2 つの描画の係には同じ依頼が届きます。
  • カメラ係:箱庭の時計に合わせて動く、カメラの本体です。撮る時刻 t になると、車の位置と向きを箱庭のデータ(PDU。シミュレーションの部品どうしがやりとりするデータ)から読み、どこから撮るかを決めます。
  • 描画の係(Godot と MuJoCo):カメラ係から「時刻 t に、この位置と向きから見た絵を描いてほしい」という依頼を受けて、絵を描いて返すだけです。自分では時刻を持ちません。色の絵は Godot、距離と物の種類は MuJoCo に付いている描画の仕組み(Filament)が描きます。どちらにも、まったく同じ依頼が届きます。
  • 時刻のそろった 4 つの PDU:カメラ係は、返ってきた絵を、色の絵・距離・物の種類・撮影情報の 4 つのデータとして書き出します。撮影情報を最後に書くので、それが「1 組そろった」という合図になります。読む側(たとえば E2E AI)は、同じ撮影時刻の 4 つを 1 組として受け取れます。

時刻の流れで見ると、こうなります。

時刻の流れ。箱庭の時計が時刻 t になると、カメラ係が車の位置を読み、Godot と MuJoCo Filament に同じ依頼を出す。描き終わるまで時刻 t で待ち、4 つの PDU を書いてから時刻を進める。AI は同じ撮影時刻の 4 つを読む
1 枚を撮る流れ。描き終わるまで、箱庭は時刻 t のまま待ちます。

ゲームエンジンと物理エンジンという、まったく別の 2 つの描画を使っても、絵は同じ瞬間のものになります。描画の係が時刻を持たず、カメラ係から渡された時刻と位置だけで描くからです。描画の係は依頼の形さえ守れば差し替えられるので、別のゲームエンジンや描画ソフトに替えられる作りです。

依頼の形は、箱庭のデータの定義集(PDU レジストリ)に、新しいサービス RenderCameraFrame として足しました。依頼にはカメラの名前、時刻、位置と向きが入り、返事として絵が返ってきます。

同じ時刻の 3 枚。左は Godot の色の絵、中央は MuJoCo の距離、右は MuJoCo の物の種類の塗り分け。都庁の前の道路
2 日目の朝。同じ依頼で描いた 3 枚です。左は Godot の色の絵、中央は MuJoCo の距離(近いほど明るい)、右は MuJoCo の物の種類(地形、建物、橋、道路、信号)。

動いた:走る車の「目」

1 日目は、都庁のまわりの街(PLATEAU の 3D 都市モデル)で、道路に沿ってカメラを動かし、Godot で描きました。日付が変わった夜中には、Urban Mobility の車にカメラを載せ、走る車から見た映像を、箱庭のデータとして取り出せるようになりました。取り出した絵は Python から読めます。ここが、E2E AI の入り口になります。

最初の車載カメラの画面。上は車内からの眺め、左下は Godot が描いたカメラの絵、右下はシミュレーション時刻 5.51 秒、コマ 161、1 枚の往復 10.7 ms などの表示
最初に動いた車載カメラ(2 日目の夜中)。左下が Godot の描いた 1 枚です。シミュレーション時刻 5.51 秒、30 Hz で 161 コマ目。依頼してから絵がデータになって戻るまで、1 枚 10.7 ms でした。

このとき、はっきりしました。ゲームエンジンが画面を描き換える速さ(フレームレート)を、シミュレーションに合わせる必要はない。時刻を渡して 1 枚頼めば、その時刻の絵が返ってくる。第 1 回に書いた気づきは、ここから来ています。

このシリーズ

  1. カメラを「関数」にした ― なぜ箱庭にカメラが要ったのか
  2. 作ってみた ― カメラより先に、コアを直した(この記事)
  3. 真実はシミュレーションの中に ― カメラは、撮影の瞬間を読んで写すだけ
  4. CARLA と比べてみた ― 同じ場所、同じ条件で測った

次回は、動いたあとで「これは箱庭の考え方と違う」と気づき、半日かけてカメラの作りを直した話です。

※ 箱庭カメラは開発中です。製品としての提供範囲・時期は未定です。シミュレーションの画像は、実際のカメラの映像や、それを使うソフトウェアの性能・安全性を保証しません。
※ Godot は MIT ライセンスのオープンソースです。SDF(SDFormat)は Open Robotics の記述形式です。CARLA は、その開発元・権利者のものです。3D 都市モデルは PLATEAU を使っています。

関連:箱庭Godot / Urban Mobility

← Now Buildingに戻る


トップへ戻る