# ベンチマークの追加

ベンチマークは、LeRobotが単一の場所である`lerobot.sim`内で操作するシミュレーターであり、すべての使用法で同じ挙動を示すように設計されています: `lerobot-eval` (バッチ化された環境、`--env.type=<benchmark>`)、ロールアウトとレコーディング (シミュレーションロボット、`--robot.type=sim --robot.env.type=<benchmark>`)、ローカルでの実行、または独自のDockerイメージから`lerobot-env-server`によって提供される場合です。使用法については、[シミュレーション: ローカルまたは提供](./simulation)を参照してください。

## 追加するもの

| コンポーネント            | 場所                                      | 追加するもの                                                                                                                                                             |
| -------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 統合          | `src/lerobot/sim/integrations/<benchmark>.py` | そのシミュレータの`gym.Env`、`env_fns()`ファクトリ、`canonical_observation()`アダプタおよび`conventions()`                                                          |
| 設定               | `src/lerobot/envs/configs.py`                 | `EnvConfig` のサブクラスで、`sim_settings()` がそのオプションをファクトリの kwargs にマッピングします                                                                                 |
| サーバー設定 | `docker/sim/configs/<benchmark>.yaml`         | サーバーとCIが使用するタスクとファクトリのkwargs                                                                                                                       |
| シミュレータ画像      | `docker/Dockerfile.sim`                       | `<benchmark>_native`ステージ: 固定されたネイティブ依存関係(ベースがプレーンなUbuntuであるため、CUDAツールキットを含む)とアセットのリビジョン。最終ステージではサーバーが追加されます |
| CIマニフェスト          | `benchmarks/benchmarks.yaml`                  | 名前、タスク、チェックポイントのリビジョン、カメラの`rename_map`、および任意の`parity`。それ以外は名前からデフォルト値が設定されます                                                            |
| 依存関係         | `pyproject.toml`                              | ローカル利用のための任意の依存関係グループ                                                                                                                              |
| ドキュメント        | `docs/source/<benchmark>.mdx`, `_toctree.yml` | ユーザー向けのページ                                                                                                                                                      |
| 出典           | `docs/source/<benchmark>.mdx`                 | 実行可能なチェックポイントのソースリポジトリ、ライセンス付きの固定されたコードおよびアセットのリビジョン、公開されている成功率                                     |

どちらのパスも同じ環境の動作を公開します:

```text
native simulator → LocalVectorEnv, locally or on an env server → canonical NumPy observations
  → batched env or sim robot → one-time image/tensor preparation → saved policy preprocessing
  → policy → saved action postprocessing → native simulator
```

そのシミュレーターの`canonical_observation()`アダプターに画像の向き、クォータニオンの変換、状態の順序を、その`conventions()`に制御の慣例をそれぞれ配置し、評価とロールアウトが一致するようにします。`sim/integrations/utils.py`は、統合間で共有されるマッピングのみに使用してください。共有環境コードは、シミュレーター名で分岐してはいけません。新しいベンチマークには[environment processor](./env_processor)は不要です。観測は、すでに正規化された状態でポリシー自身のプロセッサーに到達します。

## 評価の実行方法

`lerobot-eval` は、すべてのベンチマークを同じ方法で評価します：

1. `make_env()` は、ネストされた `{suite: {task_id: vector env}}` 辞書を構築します。
2. `eval_policy_all()` は、各タスクについて`n_episodes`回のロールアウトを実行します。タスクは1つずつ（または`--env.max_parallel_tasks`個同時に）処理されます。
3. 結果はエピソードごと、タスクごと、スイートごと、および全体で集計されます: `pc_success`（成功率）、`avg_sum_reward`、および`avg_max_reward`。各成功率には、その件数（`n_episodes` 中の `n_success`）と、パーセント単位の95% Wilson 信頼区間（`pc_success_ci95`）が付随します。この区間はエピソード間のサンプリングノイズのみをカバーしており、同じレシピの異なる学習ラン間のばらつきについては何も示していません。

`info["is_success"]` から取得されます。これは、環境がすべての `step()` で報告します。環境は独自の数値も報告できます。それらは `info["metrics"]` に格納され、各名前を環境ごとの 1 つの float にマッピングします。これらは、エピソードが終了したステップで取得され、各エピソードのエントリである `per_episode[i]["metrics"]` 配下で利用できます。

## 環境が備えている必要があるもの

### 環境の構造

`env_fns()` は、各タスクの環境ファクトリを構築せずに返します:

```python
def env_fns(
    task: str,
    n_envs: int,
    gym_kwargs: dict | None = None,
) -> dict[str, dict[int, list[Callable[[], gym.Env]]]]:
    """{suite: {task_id: [make_single_env] * n_envs}} for MyBenchmark."""
```

単一タスクのベンチマークは`{"<task>": {0: [...]}}`を返し、複数タスクのベンチマーク（LIBEROなど）は`{"libero_spatial": {0: [...], 1: [...], ...}, ...}`を返します。`LocalVectorEnv`は初回使用時に各`make_single_env`ファクトリをローカルまたはサーバー上で呼び出し、`--eval.use_async_envs=true`を指定した場合は、環境ごとに別のプロセスで呼び出します。

### gym.Env

```python
class MyBenchmarkEnv(gym.Env):
    metadata = {"render_modes": ["rgb_array"], "render_fps": <fps>}

    def __init__(self, task_suite, task_id, ...):
        super().__init__()
        self.task = <task_name_string>
        self.task_description = <natural_language_instruction>
        self._max_episode_steps = <max_steps>
        self.observation_space = spaces.Dict({...})
        self.action_space = spaces.Box(low=..., high=..., shape=(...,), dtype=np.float32)
```

| 属性            | 型  | なぜ                                                  |
| -------------------- | ----- | ---------------------------------------------------- |
| `_max_episode_steps` | `int` | エピソードを終了(切り捨て)します                          |
| `task_description`   | `str` | VLAポリシーに言語指示として渡されます     |
| `task`               | `str` | `task_description`が設定されていない場合のフォールバック識別子 |

`step()` は `info["is_success"]` に成功を報告し、それ以外の測定値は `info["metrics"]` に格納されます。アクションは 1 次元の連続 `Box` です。

Observations は、統合の `canonical_observation()` アダプターが理解する dict です。再利用可能なデフォルトのアダプターは、以下をマッピングします:

| envの出力          | 正規化キー                   | 概要                          |
| ------------------------- | ------------------------------- | ----------------------------------- |
| `"pixels"`（単一の配列） | `observation.image`             | 単一カメラ画像、HWC uint8      |
| `"pixels"`（dict）         | `observation.images.<cam>`      | 複数のカメラ、それぞれHWC uint8    |
| `"agent_pos"`             | `observation.state`             | 固有受容性状態ベクトル         |
| `"environment_state"`     | `observation.environment_state` | 完全な環境状態（例: PushT） |

その他の配列は`observation.<key>`として渡されます。シミュレーターの出力を`gym.Env`内でこれらのキーにリネームするか、その統合のアダプターでネイティブな表現を処理してください。

**GPUベースのシミュレーター（EGLレンダリング付きのMuJoCoなど）:** シミュレーターが`__init__`中にGPU/EGLコンテキストを割り当てている場合、その割り当てを、最初の`reset()`/`step()`呼び出し時に実行される`_ensure_env()`ヘルパーに遅延してください。これにより、ワーカープロセス内で構築された環境が、古いGPUハンドルを継承することを防げます。パターンについては`LiberoEnv._ensure_env()`を参照してください。

### Conventions (`conventions()`は統合内で)

`conventions(kwargs)` から、シミュレータの制御レート、コントローラ、および順序付けられたコンポーネント名を返します。この関数はシミュレータのオプションを引数に取ります（RoboTwin のように、アクションモードに依存する場合があります）。この関数は、設定で指定されていない項目を補完し、ロボット、データセット、チェックポイントにおける状態とアクションのコンポーネントに名前を付けます。`semantics` のデフォルト値は `<type>-<control>-v1` です。`robot_type` は、シミュレーションロボットによって記録されるシミュレートされた本体の名前です。lerobot にクラスが存在する場合は、実ロボットが記録するタイプ（`so_follower`）、それ以外の場合は短い名前（`panda`、`sawyer`）を指定します。指定しない場合、データセットには `sim_robot` が記録されます。

### 設定(`src/lerobot/envs/configs.py`)

`--env.type=<name>`（および`--robot.env.type=<name>`）でユーザーがベンチマークを選択できるように、設定を登録します。その`sim_settings()`は、サーバーのYAMLの`sim`セクションとしてシミュレーターを記述します。`LocalVectorEnv`が残りの処理を行います。

```python
@EnvConfig.register_subclass("<benchmark_name>")
@dataclass
class MyBenchmarkEnvConfig(EnvConfig):
    task: str = "<default_task>"
    fps: int = <fps>
    obs_type: str = "pixels_agent_pos"

    @property
    def gym_kwargs(self) -> dict:
        return {"obs_type": self.obs_type}

    def sim_settings(self) -> dict:
        return {"type": "<benchmark_name>", "task": self.task, "kwargs": {"gym_kwargs": self.gym_kwargs}}
```

デフォルト値とサーバーのYAML設定を一致させます。提供される評価は、コマンドが記述する環境と異なる環境で実行されるサーバーを拒否します。機能は環境が作成される際の記述から取得されるため、設定では何も宣言しません。

## チェックポイントの承認

`--rename_map` は、他のロボットと同様に、環境のカメラキーをチェックポイントの学習済みキーにエイリアスします。CI では、`benchmarks/benchmarks.yaml` のエントリの `rename_map` から取得されます。リネーム後、チェックポイントの入力が存在し、状態/アクションの次元が一致している必要があります。リネームではチェックポイントのメタデータを修復できません。RGB チャネル数とレイアウトは、受け入れ時にチェックされます。取得時の高さ/幅は、学習メタデータと異なる場合があります。リサイズはポリシーの前処理に属します。

## 検証とCI

1. 統合の観察アダプターをシミュレーターの生出力と比較して確認し、特にカメラの向きと状態の順序、および環境ラッパーが適用したアクションを確認してください。
2. ローカルとサーブされたシード付きリセット/ステップシーケンスを比較します（`scripts/ci/check_sim_parity.py`）。決定論的シミュレーターのマニフェストエントリに`parity: true`を設定すると、CIで実行されます。ターミナルのフリーズ、成功、エピソード長、レンダリング、リソースのクリーンアップを確認してください。
3. サーバーコードおよびネイティブモジュールに対して、torch無効化されたインポートガードを実行します。RoboTwinやRoboMMEのように、独自のtorchランタイムを必要とするシミュレーターを文書化します。
4. ローカルで`lerobot-eval --env.type=<name> --env.task=<task> --eval.n_episodes=1 --policy.path=<checkpoint>`を実行し、`--env.server.endpoint`を指定してサーバーに対して実行します。タスクの説明とレンダリングされたカメラビューを確認してください。
5. GPUランナーで生成されたベンチマークジョブを実行します。共有の評価/学習統合を変更する際は、LIBEROの学習スモークテストを維持してください。

`benchmarks/benchmarks.yaml` は `scripts/ci/benchmark_matrix.py` によってマトリックスを生成します。`.github/workflows/benchmark_run.yml` が各エントリを実行します。CI ジョブをコピーするのではなく、マニフェストにエントリを追加してください。CI はパイプラインをエンドツーエンドで確認するため、エピソード数を上限設定しています。その `metrics.json` は、チェックポイントのリビジョン、シミュレータのイメージタグ、コミット、上限値を記録し、これらの実行を `smoke` とマークします。これらの成功率はポリシーのスコアではありません。シミュレータのイメージには個別のピン留めとコンテンツタグがあります。BuildKit のキャッシュは、永続的なランナーでのみ保持されます。

## ベンチマークの変更

統合の`canonical_observation()`、`conventions()`および環境ラッパーは、ポリシーが何を見るか、そしてそのアクションがどのように適用されるかを決定します。画像の向き、状態の順序、制御レートが誤っていてもクラッシュはしません。ポリシーはわずかに誤った入力を参照し、その成功率が低下します。これらのいずれかを変更する場合、以下が伴います：

- **回帰テスト**は、記録済みまたは合成された観測データに対して、旧バージョンと新バージョンの出力を比較します。`tests/sim/integrations/test_libero.py` 内の `test_libero_numpy_adapter_parity` がそのモデルです。このテストは、torch を使用しない LIBERO アダプターが、以前の LIBERO プロセッサと完全に同じ出力を生成することを示しています。
- プルリクエストにおける**スコアへの想定される影響**：なし、またはどのスコアがどのように変化するか、その理由。

## Dependencies (`pyproject.toml`)

ローカル利用用の新しいオプション依存関係グループを追加します:

```toml
mybenchmark = ["my-benchmark-pkg==1.2.3", "lerobot[scipy-dep]"]
```

ピン留めルール:

- **常に固定**してください。再現性を確保するために、ベンチマーク用のパッケージは正確なバージョンに固定してください（例: `metaworld==3.0.0`）。
- 必要に応じて（例: `; sys_platform == 'linux'`）、**プラットフォームマーカー**を追加します。
- 既知の脆弱なトランジティブ依存関係は、**ピン留め**してください（例: Meta-World イメージでは、MuJoCo 3.17 がモーションキャプチャのウェルドを壊すため、`mujoco==3.15.0`がピン留めされています）。
- ベンチマークのドキュメントページに**ドキュメントの制約**を記載してください。依存関係がLeRobotのものと競合する場合、サポートされるパスが提供されます。

## ベンチマークのドキュメントページの作成

`docs/source/_toctree.yml`の「Benchmarks」セクションにページを追加します。各ベンチマークページには、以下を含める必要があります：

- **タイトルと説明** — ベンチマークが何をテストし、なぜそれが重要であるかについての1〜2段落。
- **リンク** — 論文、GitHubリポジトリ、プロジェクトのウェブサイト（利用可能な場合）。
- **出典とライセンス** — ソースリポジトリ、固定されたコードおよびアセットのリビジョン、それぞれのライセンス。ライセンスの使用を禁止しているベンチマークを追加しないでください。
- **概要の画像またはGIF。**
- **利用可能なタスク** — タスクスイートの数と簡単な説明をまとめた表。
- **インストール** — `pip install -e ".[<benchmark>]"`、またはサーバーコマンド。
- **評価** — 再現可能な結果を得るために、`n_episodes` を指定した推奨の `lerobot-eval` コマンド。
- **ポリシーの入力と出力** — 形状付きの観測キー、アクション空間の説明。
- **学習** — `lerobot-train` コマンドの例。
- **参照結果** — 実行可能な事前学習済みチェックポイントの少なくとも1つについて、そのリビジョンと、`lerobot-eval` コマンドを用いて再現できる公開済みの成功率です。この値の近傍に到達しない統合は、完成したとはみなされません。

完全な例については、`docs/source/libero.mdx`および`docs/source/metaworld.mdx`を参照してください。

