ロボットのシミュレーション記録を検証できる動画に編集する:Robot Reel の agent director と絵コンテ契約

Robot Reel の agent director は絵コンテが記録を隙間なく覆うか検査し Blender で再生。4 ショットで 180 サンプルを 210 フレームに。

タイムライン図:タイトル「4 ショットで 180 のソースサンプルを覆う」の下に、1 から 4 の番号付きの塗りつぶした円が左から右へ矢印でつながり、矢印はソース記録での順序を示します。下の文字は順に、全景 overview、ソース 0–59、1× → 60 フレーム。追従 tracking、ソース 60–95、1× → 36 フレーム。接触 impact、ソース 96–125、0.5× → 60 フレーム。俯瞰 top、ソース 126–179、1× → 54 フレーム。下部に出典の注記があります。
サンプル絵コンテの 4 つのショットはソースの順に並び、180 個のソースサンプルを隙間も重なりもなく覆います。半速のショットは 30 個のサンプルをそれぞれ 1 回複製します。サンプル範囲は計画の end(その値を含まない)を両端を含む範囲に直したもので、ショットごとの出力フレーム数は本記事が契約の規則から計算したものです。commit c4bbdbb の examples/contact-storyboard.json と docs/director.md をもとに作図。

ロボットのシミュレーション記録を短い解説動画にまとめるとき、よくあるのは編集ソフトで手作業で速度を変え、カメラを切り替え、字幕を入れるやり方です。ただ、仕上がった後では、動画のどのフレームが元の記録のどのサンプルに当たるのか、スロー再生でフレームが抜けたり補われたりしていないかを説明しにくくなります。Robot Reel の agent director は、この作業をプログラムで検査できる絵コンテ計画に分けます。ユーザー、または MCP で接続した言語モデルが概要(brief)とショットを書き、バリデーターがショットが記録全体を隙間も重なりもなく覆っているかを確かめ、そのうえで Blender が編集可能なプロジェクトを組み立てて動画をレンダリングします。本記事では、commit c4bbdbb に固定した docs/director.md とサンプルの絵コンテ examples/contact-storyboard.json をもとに、絵コンテ契約の規則、フレーム対応を手計算で照合する方法、計画から動画までのコマンド、MCP サーバーの権限の範囲を説明します。

本文の数値と機能の説明は、すべてプロジェクト自身の文書によるものです([O]、プロジェクトの自己申告)。本サイトではこれらのコマンドを実行しておらず、再レンダリングもしていません。Robot Reel 全体の紹介は Robot Reel:ロボットの方策シミュレーションを、コマ単位で検証できる再生と 3D シーンに を参照してください。本記事では director という新機能だけを扱います。

サンプル動画:2 回のブレーキ試験、4 つのショット

文書からリンクされた director のデモページには 7 秒の動画があります。素材はリポジトリに既にある、互いに独立した 1 次元 MuJoCo ブレーキ試験の 1 組です。ショットは全景、追従、半速の接触、俯瞰の 4 つで、180 個のソースサンプルをすべて残したまま 210 個の出力フレームに並べています。文書によると、保存された Blender 5.2.1 LTS のプロジェクトで、420 個の車両サンプルすべて、4 か所のカット、追従カメラが検査されています。

ここでは 2 つのことを分けておく必要があります。MuJoCo は記録を生成したシミュレーターであり、Blender は保存されたサンプルを再生するだけです。文書には、director が新しい物理を合成しないと明記されています。講座の フィジカルAIラボ 1.4:シミュレータの地図――刻み幅のコストを数え、接触で軌道が分かれる点を読み、3 つの高速化の比較対象を確かめる では、別のエンジンで計算し直すと接触の瞬間に軌道が分かれうることを扱いました。この動画は何も計算し直さないため、画面上の接触は記録にある接触そのものであり、動画が記録より「本物」になることもありません。

絵コンテ契約:プログラムで検査できる計画

計画はスキーマ robot-reel-director-1 を使い、概要、タイトル、テーマ(midnight または daylight)、1〜8 個のショットを持ちます。各ショットには start、その値を含まない end、camera、rate、caption があります。規則はわずかです。

  • カメラは overview、tracking、impact、top のいずれかです。
  • 速度は 1 か 0.5 です。半速では各サンプルをちょうど 1 回複製します。
  • ショットはソース記録全体を順番どおりに覆い、隙間も重なりもあってはいけません。

サンプル計画のタイトルは “One decision. Two outcomes.”、テーマは midnight で、4 つのショットは次のとおりです(字幕は原文のまま)。

ショットstart–endcameraratecaption
10–60overview1Same start. Different braking decisions.
260–96tracking1Follow the measured approach.
396–126impact0.5Recorded contact, slowed to half speed.
4126–180top1Two outcomes. Every source sample retained.

各ショットの end は次のショットの start と等しく、最後のショットは 180 で終わります。これが「隙間も重なりもない」ということです。契約の範囲は狭く、計画に書けるのはカット位置、カメラ、速度、字幕だけです。そのため契約に違反する誤りは機械的に検出できます。たとえば 126〜130 の区間を抜かしたり、0.25 倍速を指定したりすれば、それだけで契約違反になり、人が動画をコマごとに見なくても気づけます。ただし、カット位置やカメラが概要に合っているかどうかは検査されません。たとえば接触のショットを記録上の接触から離れた位置に置いても、すべての規則を満たします。

フレーム対応を手計算で確かめる

生成される film.json は、出力フレームごとに元のソースフレームとシミュレーターのタイムスタンプを対応づけます。文書には 3 点が書かれています。出力フレームの番号は 0 から、Blender のフレームは 1 から始まること。スロー再生の間は動画内の時間とシミュレーション時間が意図的に異なること。そしてバリデーターはこの対応を計算し直し、ハッシュだけ付け直された内容の食い違う JSON は受け付けないことです。

上の規則を当てはめると、サンプルの対応は次のように求められます(以下は本記事による計算で、プロジェクトが公開している表ではありません)。

  1. ショット 1:ソースサンプル 0〜59 を 1 倍速で、出力フレーム 0〜59、計 60 フレーム。
  2. ショット 2:ソースサンプル 60〜95、出力フレーム 60〜95、計 36 フレーム。
  3. ショット 3:ソースサンプル 96〜125 の 30 個。半速ではそれぞれが 2 回現れるので、出力フレーム 96〜155、計 60 フレーム。出力フレーム 96 と 97 はどちらもソースサンプル 96 に、出力フレーム 155 はソースサンプル 125 に対応します。
  4. ショット 4:ソースサンプル 126〜179、出力フレーム 156〜209、計 54 フレーム。

合計は 60 + 36 + 60 + 54 = 210 フレームで、文書の出力フレーム数 210 と一致します。30 fps なら 7 秒で、「7 秒の動画」とも一致します。このうち半速のショットは動画内で 2 秒を占めます。Blender は 1 から数えるので、ソースサンプル 96 が最初に現れるのは Blender の第 97 フレームです。本記事のカバー画像は左下に “SOURCE 096”、右上に “SHOT 3 / 0.5x” とあり、この対応と合っています。

手計算する意味は、確認する人が動画を信用しなくてよいところにあります。計画と film.json があれば、各フレームをソース記録と突き合わせられます。ソースのトレースとそのチェックサムは、bundle の source/ ディレクトリに残っています。

計画から動画まで

文書に示されたサンプルのビルド手順は次のとおりで、リポジトリのルートで実行します。計画のエクスポーターとベリファイアは Python 標準ライブラリだけを使います。

python3 -m robot_reel.cli direct docs/compare/braking --inspect
python3 -m robot_reel.cli direct docs/compare/braking \
  --plan examples/contact-storyboard.json --output artifacts/director
python3 -m robot_reel.cli direct artifacts/director --verify
blender --background --python artifacts/director/build_directed_scene.py -- \
  --bundle artifacts/director --output artifacts/director/reel.blend
blender --background --python scripts/check_director_blender.py -- \
  --bundle artifacts/director --blend artifacts/director/reel.blend \
  --report artifacts/director/animation-check.json
blender --background --python scripts/render_blender_preview.py -- \
  --blend artifacts/director/reel.blend --frames artifacts/director/frames \
  --scale 100 --samples 6 --threads 2

順に、記録の確認、計画からの bundle の書き出し、bundle の検証、Blender プロジェクト reel.blend の構築、プロジェクト内でのアニメーション検査とレポートの書き出し、各フレームのレンダリングです。文書は CPU Cycles でのレンダリングを指定しており、検査済みのサンプルは 960×540、30 fps、サンプルの値をそのまま保持(constant sample hold)し、モーションブラーはありません。保存されたプロジェクトには、プロシージャルなマテリアル、ソースのテレメトリー、すべてのカメラのバインディング、埋め込まれた計画と対応表の JSON が含まれます。字幕はパッケージング時に MP4 へ焼き込まれ、ネイティブのプロジェクトではタイムラインマーカーのラベルとして保存されます。アニメーションするテキストオブジェクトではありません。

通常の Robot Reel 実行環境を導入したうえで、サイトとしてパッケージングします。

python scripts/build_director_site.py --bundle artifacts/director \
  --output artifacts/director-site

パッケージャーは、ネイティブの検査レポートとレンダリング済みの全フレームがそろうまでエンコードを始めません。動画、ポスター、プレビュー、インタラクティブページ、チェックサム、project.zip を生成します。Linux での既定フォントは DejaVu Sans で、他のシステムでは --font PATH で指定します。生成された index.html はメディアファイルと同じ場所に置いて開きます。

再現する際は、文書に書かれていない点が 2 つあります。サンプルのプロジェクトは Blender 5.2.1 LTS で検査されたとありますが、上のコマンドはインストールする Blender のバージョンを固定していません。また「通常の Robot Reel 実行環境」の導入方法も、この文書には書かれていません。さらに、4 番目のコマンドは bundle 内で生成された build_directed_scene.py を Blender に実行させるもので、手元のマシンでのコード実行に当たります。本サイトとしては、自分で生成したか内容を確認した bundle に対してだけ実行することを勧めます。

エージェントを接続する:MCP サーバーがすること、しないこと

通常の Robot Reel 環境で pip install -e '.[director]' を実行します。文書によると MCP SDK は 2.1.1 に固定され、既存の Strands ランタイムと互換性があります。MCP クライアントにはローカルの stdio サーバーとして設定し、2 つの絶対パスを自分のチェックアウトとインタープリターに置き換えます。

{
  "mcpServers": {
    "robot-reel-director": {
      "command": "/absolute/path/robot-reel/.venv/bin/python",
      "args": ["-m", "robot_reel.cli", "mcp", "--root", "/absolute/path/robot-reel"]
    }
  }
}

文書のリクエスト例は、docs/compare/braking を確認し、両方の試験から始め、接近を追い、記録された接触をスローにし、両方の結果で締めくくる、すべてのソースサンプルを残し、絵コンテを artifacts/my-directed-film に書き出す、というものです。概要は接続した言語モデルが解釈し、2 つのツールの役割ははっきり分かれています。

  • inspect_recording は記録にある実際のイベントフレームと結果の要約を返します。モデルはこれをもとにカット位置を選べます。
  • create_storyboard は型付きの計画を検証し、新しい bundle を書き出します。

権限の範囲も明確です。ツールは設定したルートからの相対パスだけを扱い、シンボリックリンクや .. による脱出を拒否し、空でない出力ディレクトリへの書き込みを拒否します。サーバーはシェルコマンド、任意の Python、Blender を実行しません。前節のビルドとレンダリングのコマンドは、エージェントまたはユーザーがその後に明示的に実行する手順です。MCP サーバーを起動しただけでモデル呼び出しが起きることはありません。stdio の完全な統合テストは python scripts/check_director_mcp.py で、文書によると CI でも実行されています。

デモサイトには言語モデルはホストされておらず、レンダリング済みのサンプルを表示しているだけです。ページ上のエディターは新しいレンダリング用の計画をダウンロードするもので、エディターで編集しても、表示中の動画やそのソースのメタデータは変わりません。

制約と注意点

  • director が現在対応しているのはブレーキの記録とその比較だけです。任意のロボットを演出することはできず、新しい物理を合成することもありません。Blender は保存されたサンプルを再生するだけです。
  • 字幕は編集上の表現であり、独立に検証された記述ではありません。たとえば “Different braking decisions” は 2 回の試験の要約です。バリデーターが確かめるのはフレーム対応で、字幕が正しいかどうかではありません。文章による主張を記録と照合する方法は モデルの説明を元のロボット録画と照合する:Robot Reel の review-claims で示せること を参照してください。
  • チェックサムが示すのはファイルの一貫性であり、内容が独立に本物であることではありません。
  • 7 秒、210 フレーム、420 個の車両サンプル、Blender での検査結果は、プロジェクトが自身の環境で残した記録です。第 3 節のショットごとのフレーム番号は、本記事が契約の規則から計算したものです。本サイトではビルドもレンダリングもやり直していません。
  • ソースは 1 次元のブレーキシミュレーションで、画面上の車線、車体、ブロックはレンダリングされた 3D の造形です。実車試験ではなく、ハードウェアの安全性の検証でもありません。

リンク