# Menambahkan Benchmark

Benchmark adalah simulator yang dijalankan oleh LeRobot di satu tempat, `lerobot.sim`, sehingga setiap cara penggunaannya berperilaku sama: `lerobot-eval` (env batched, `--env.type=<benchmark>`), rollout dan perekaman (robot sim, `--robot.type=sim --robot.env.type=<benchmark>`), secara lokal atau disajikan oleh `lerobot-env-server` dari image Docker-nya sendiri. Lihat [Simulasi: lokal atau disajikan](./simulation) untuk cara penggunaannya.

## Yang perlu ditambahkan

| Komponen            | Lokasi                                      | Yang perlu ditambahkan                                                                                                                                                             |
| -------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Integrasi          | `src/lerobot/sim/integrations/<benchmark>.py` | `gym.Env`, pabrik `env_fns()`, adapter `canonical_observation()` dan `conventions()` untuk simulator tersebut                                                          |
| Konfigurasi               | `src/lerobot/envs/configs.py`                 | Subkelas `EnvConfig` yang `sim_settings()`-nya memetakan opsi ke kwargs pabrik                                                                                 |
| Konfigurasi server | `docker/sim/configs/<benchmark>.yaml`         | Kata kunci tugas dan pabrik yang digunakan oleh server dan CI                                                                                                                       |
| Gambar simulator      | `docker/Dockerfile.sim`                       | Tahap `<benchmark>_native`: dependensi native yang dipin (termasuk toolkit CUDA mana pun, karena basisnya adalah Ubuntu polos) dan revisi aset; tahap akhir menambahkan server |
| Manifest CI          | `benchmarks/benchmarks.yaml`                  | Nama, tugas, revisi checkpoint, kamera `rename_map` dan `parity` opsional; sisanya menggunakan nilai default dari nama                                                            |
| Dependensi         | `pyproject.toml`                              | Grup dependensi opsional untuk penggunaan lokal                                                                                                                              |
| Dokumentasi        | `docs/source/<benchmark>.mdx`, `_toctree.yml` | Halaman yang ditujukan untuk pengguna                                                                                                                                                      |
| Asal-usul           | `docs/source/<benchmark>.mdx`                 | Repositori sumber, revisi kode dan aset yang dikunci beserta lisensinya, serta tingkat keberhasilan yang dipublikasikan untuk checkpoint yang dapat Anda jalankan                                     |

Kedua jalur tersebut mengekspos perilaku lingkungan yang sama:

```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
```

Letakkan orientasi gambar, konversi kuaternion, dan urutan state di dalam adapter `canonical_observation()` simulator tersebut, dan konvensi kendalinya di dalam `conventions()`, sehingga evaluasi dan rollout sesuai. Gunakan `sim/integrations/utils.py` hanya untuk pemetaan yang dibagikan oleh integrasi; kode lingkungan bersama tidak boleh bercabang berdasarkan nama simulator. Benchmark baru tidak memerlukan [environment processor](./env_processor): observasi mencapai prosesor milik kebijakan sendiri yang sudah kanonik.

## Cara evaluasi dijalankan

`lerobot-eval` mengevaluasi setiap benchmark dengan cara yang sama:

1. `make_env()` membangun dict bersarang `{suite: {task_id: vector env}}`.
2. `eval_policy_all()` menjalankan `n_episodes` rollout untuk setiap tugas, satu tugas pada satu waktu (atau `--env.max_parallel_tasks` sekaligus).
3. Hasil diagregasi per episode, tugas, suite, dan keseluruhan: `pc_success` (tingkat keberhasilan), `avg_sum_reward`, dan `avg_max_reward`. Setiap tingkat keberhasilan disertai dengan jumlahannya (`n_success` dari `n_episodes`) dan interval Wilson 95% dalam persen (`pc_success_ci95`). Interval ini hanya mencakup noise sampling antar episode; interval ini tidak memberikan informasi tentang variasi antar run pelatihan dari resep yang sama.

Kesuksesan berasal dari `info["is_success"]`, yang dilaporkan oleh lingkungan Anda pada setiap `step()`. Lingkungan Anda juga dapat melaporkan angka-angka miliknya sendiri. Angka-angka tersebut ditempatkan di `info["metrics"]`, yaitu peta dari setiap nama ke satu float per lingkungan. Angka-angka tersebut mencapai entri episode masing-masing di bawah `per_episode[i]["metrics"]`, diambil pada langkah tempat episode berakhir.

## Yang harus disediakan oleh lingkungan Anda

### Struktur lingkungan

`env_fns()` mengembalikan pabrik lingkungan untuk setiap tugas, tanpa membangunnya:

```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."""
```

Benchmark tugas tunggal mengembalikan `{"<task>": {0: [...]}}`; benchmark multi-tugas (seperti LIBERO) mengembalikan `{"libero_spatial": {0: [...], 1: [...], ...}, ...}`. `LocalVectorEnv` memanggil setiap pabrik `make_single_env` pada penggunaan pertama, baik secara lokal maupun di server, dan, dengan `--eval.use_async_envs=true`, dalam proses terpisah untuk setiap lingkungan.

### The 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)
```

| Atribut            | Tipe  | Mengapa                                                  |
| -------------------- | ----- | ---------------------------------------------------- |
| `_max_episode_steps` | `int` | Mengakhiri (memotong) episode                          |
| `task_description`   | `str` | Diteruskan ke kebijakan VLA sebagai instruksi bahasa     |
| `task`               | `str` | Pengenal cadangan jika `task_description` tidak diatur |

`step()` melaporkan keberhasilan di `info["is_success"]`, dan hal lain yang diukurnya di `info["metrics"]`. Aksi adalah `Box` kontinu satu dimensi.

Observations adalah dict yang dipahami oleh adapter `canonical_observation()` dari integrasi tersebut. Adapter default yang dapat digunakan kembali memetakan:

| Output env Anda          | Kunci kanonik                   | Apa itu                          |
| ------------------------- | ------------------------------- | ----------------------------------- |
| `"pixels"` (array tunggal) | `observation.image`             | Gambar kamera tunggal, HWC uint8      |
| `"pixels"` (dict)         | `observation.images.<cam>`      | Beberapa kamera, masing-masing HWC uint8    |
| `"agent_pos"`             | `observation.state`             | Vektor keadaan proprioseptif         |
| `"environment_state"`     | `observation.environment_state` | Lingkungan penuh (misalnya PushT) |

Array lain diteruskan sebagai `observation.<key>`. Ubah nama output simulator Anda menjadi kunci-kunci ini di dalam `gym.Env`, atau tangani representasi natifnya di adapter integrasi tersebut.

**Simulator berbasis GPU (misalnya MuJoCo dengan rendering EGL):** jika simulator Anda mengalokasikan konteks GPU/EGL selama `__init__`, tunda alokasi tersebut ke helper `_ensure_env()` yang dipanggil pada `reset()`/`step()` pertama, sehingga lingkungan yang dibangun di proses worker tidak mewarisi handle GPU yang sudah kedaluwarsa. Lihat `LiberoEnv._ensure_env()` untuk polanya.

### Conventions (`conventions()` dalam integrasi)

Kembalikan tingkat kontrol, pengontrol, dan nama komponen yang terurut dari simulator melalui `conventions(kwargs)`, sebuah fungsi dari opsi simulator (mereka dapat bergantung pada mode aksi, seperti pada RoboTwin). Mereka mengisi pengaturan yang tidak tercakup oleh konfigurasi, serta menamai komponen state dan action untuk robot, dataset, dan checkpoint. `semantics` secara default adalah `<type>-<control>-v1`. `robot_type` menamai tubuh yang disimulasikan, yang dicatat oleh robot simulasi: tipe yang dicatat oleh robot fisik ketika lerobot memiliki kelas untuknya (`so_follower`), jika tidak, gunakan nama singkat (`panda`, `sawyer`); jika dibiarkan kosong, dataset akan mencatat `sim_robot`.

### Konfigurasi (`src/lerobot/envs/configs.py`)

Daftarkan config agar pengguna dapat memilih benchmark Anda dengan `--env.type=<name>` (dan `--robot.env.type=<name>`). `sim_settings()` mendeskripsikan simulator sebagai bagian `sim` dari YAML server; `LocalVectorEnv` menangani sisanya:

```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}}
```

Jaga agar nilai default-nya dan YAML server tetap konsisten: evaluasi yang disajikan akan menolak server yang menjalankan lingkungan lain dari yang dijelaskan oleh perintah. Fitur berasal dari deskripsi lingkungan saat dibuat, sehingga konfigurasi tidak mendeklarasikan apa pun.

## Penerimaan checkpoint

`--rename_map` memberi alias pada kunci kamera lingkungan ke kunci terlatih dari checkpoint, seperti untuk robot mana pun; dalam CI, nilai ini berasal dari `rename_map` entri di `benchmarks/benchmarks.yaml`. Input checkpoint harus ada setelah penamaan ulang, dan dimensi state/action harus cocok; penamaan ulang tidak dapat memperbaiki metadata checkpoint. Jumlah dan tata letak kanal RGB diperiksa saat penerimaan. Tinggi/lebar akuisisi dapat berbeda dari metadata pelatihan; resizing merupakan bagian dari pra-pemrosesan kebijakan.

## Verifikasi dan CI

1. Periksa adapter observasi integrasi terhadap output mentah simulator Anda, terutama orientasi kamera dan urutan state, serta periksa aksi yang diterapkan oleh wrapper lingkungan.
2. Bandingkan urutan reset/step berbenih secara lokal dan yang disajikan (`scripts/ci/check_sim_parity.py`); atur `parity: true` pada entri manifest simulator deterministik agar CI menjalankannya. Periksa pembekuan terminal, keberhasilan, panjang episode, rendering, dan pembersihan sumber daya.
3. Jalankan guard impor torch-disabled untuk kode server dan modul native Anda. Dokumentasikan simulator yang memerlukan runtime torch mereka sendiri, seperti RoboTwin dan RoboMME.
4. Jalankan `lerobot-eval --env.type=<name> --env.task=<task> --eval.n_episodes=1 --policy.path=<checkpoint>` secara lokal, dan dengan `--env.server.endpoint` terhadap server. Periksa deskripsi tugas dan tampilan kamera yang dirender.
5. Jalankan tugas benchmark yang dihasilkan pada runner GPU. Pertahankan smoke pelatihan LIBERO saat mengubah integrasi evaluasi/pelatihan bersama.

`benchmarks/benchmarks.yaml` menghasilkan matriks melalui `scripts/ci/benchmark_matrix.py`; `.github/workflows/benchmark_run.yml` menjalankan setiap entri. Tambahkan entri ke manifest alih-alih menyalin tugas CI. CI membatasi episode untuk memeriksa pipeline dari awal hingga akhir; `metrics.json` miliknya mencatat revisi checkpoint, tag gambar simulator, commit, dan batas, serta menandai run ini sebagai `smoke`: tingkat keberhasilannya bukan skor kebijakan. Gambar simulator memiliki pin dan tag konten yang terpisah; cache BuildKit hanya bertahan pada runner persisten.

## Mengubah benchmark

An integration's `canonical_observation()`, `conventions()` and environment wrapper decide what the policy sees and how its actions apply. A wrong image orientation, state order or control rate does not crash: the policy sees slightly wrong inputs, and its success rate drops. A change to any of them comes with:

- **Uji regresi** yang membandingkan output lama dan baru pada observasi yang direkam atau sintetis. `test_libero_numpy_adapter_parity` di `tests/sim/integrations/test_libero.py` adalah modelnya: menunjukkan bahwa adapter LIBERO tanpa torch menghasilkan persis apa yang dilakukan oleh prosesor LIBERO sebelumnya.
- **Dampak yang diharapkan pada skor** dalam pull request: tidak ada, atau skor mana yang harus berubah dan alasannya.

## Dependencies (`pyproject.toml`)

Tambahkan grup dependensi opsional baru untuk penggunaan lokal:

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

Aturan pinning:

- **Selalu kunci** paket benchmark ke versi yang tepat untuk reproduktibilitas (mis. `metaworld==3.0.0`).
- **Tambahkan penanda platform** saat diperlukan (mis. `; sys_platform == 'linux'`).
- **Pin dependensi transitive yang rapuh** jika diketahui (misalnya, image Meta-World mem-pin `mujoco==3.15.0`, karena MuJoCo 3.17 merusak weld mocap-nya).
- **Batasan dokumen** di halaman dokumen benchmark Anda. Ketika dependensi bertentangan dengan LeRobot, jalur yang disajikan adalah yang didukung.

## Menulis halaman dokumen benchmark

Tambahkan halaman ke bagian "Benchmarks" di `docs/source/_toctree.yml`. Setiap halaman benchmark harus menyertakan:

- **Judul dan deskripsi** — 1-2 paragraf tentang apa yang diuji oleh benchmark dan mengapa hal itu penting.
- **Tautan** — makalah, repositori GitHub, situs web proyek (jika tersedia).
- **Provenance dan lisensi** — repositori sumber, revisi kode dan aset yang dipatok, serta lisensi masing-masing. Jangan menambahkan benchmark yang lisensinya melarang penggunaan.
- **Gambar atau GIF ringkasan.**
- **Tugas yang tersedia** — tabel kumpulan tugas dengan jumlah dan deskripsi singkat.
- **Instalasi** — `pip install -e ".[<benchmark>]"`, atau perintah server.
- **Evaluasi** — perintah `lerobot-eval` yang direkomendasikan dengan `n_episodes` untuk hasil yang dapat direproduksi.
- **Masukan dan keluaran kebijakan** — kunci observasi dengan bentuknya, deskripsi ruang aksi.
- **Pelatihan** — contoh perintah `lerobot-train`.
- **Hasil referensi** — tingkat keberhasilan yang dipublikasikan untuk setidaknya satu checkpoint pretrained yang dapat Anda jalankan, beserta revisinya dan perintah `lerobot-eval` yang mereplikasinya. Integrasi yang tidak menghasilkan nilai di dekatnya dianggap belum selesai.

Lihat `docs/source/libero.mdx` dan `docs/source/metaworld.mdx` untuk contoh lengkap.

