LeRobot documentation
Ajouter un benchmark
Ajouter un benchmark
Un benchmark est un simulateur que LeRobot pilote en un seul endroit, lerobot.sim, afin que chaque mode d’utilisation se comporte de manière identique : lerobot-eval (un environnement par lots, --env.type=<benchmark>), le déroulement et l’enregistrement (un robot simulé, --robot.type=sim --robot.env.type=<benchmark>), en local ou servi par lerobot-env-server depuis sa propre image Docker. Consultez Simulation : en local ou servie pour savoir comment il est utilisé.
À ajouter
| Composant | Emplacement | À ajouter |
|---|---|---|
| Intégration | src/lerobot/sim/integrations/<benchmark>.py | Le gym.Env, les usines env_fns(), l'adaptateur canonical_observation() et les conventions() pour ce simulateur |
| Configuration | src/lerobot/envs/configs.py | Une sous-classe de EnvConfig dont sim_settings() mappe ses options vers les kwargs de la fabrique |
| Configuration du serveur | docker/sim/configs/<benchmark>.yaml | Les arguments de tâche et d'usine que le serveur et l'CI utilisent |
| Image du simulateur | docker/Dockerfile.sim | Une étape <benchmark>_native : dépendances natives épinglées (y compris le kit CUDA, car la base est un Ubuntu standard) et révisions des ressources ; l'étape finale ajoute le serveur |
| Manifeste CI | benchmarks/benchmarks.yaml | Nom, tâches, révision du point de contrôle, caméra rename_map et parity facultatif ; le reste est défini par défaut à partir du nom |
| Dépendances | pyproject.toml | Groupe de dépendances facultatives pour un usage local |
| Documentation | docs/source/<benchmark>.mdx, _toctree.yml | Une page destinée aux utilisateurs |
| Provenance | docs/source/<benchmark>.mdx | Dépôts sources, révisions de code et d'actifs épinglées avec leurs licences, et un taux de réussite publié pour un point de contrôle que vous pouvez exécuter |
Les deux chemins exposent le même comportement d'environnement :
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
Placez l’orientation des images, la conversion des quaternions et l’ordre des états dans l’adaptateur canonical_observation() de ce simulateur, et ses conventions de contrôle dans son conventions(), afin que l’évaluation et le déroulement (rollout) soient cohérents. Réutilisez sim/integrations/utils.py uniquement pour les correspondances partagées par les intégrations ; le code d’environnement partagé ne doit pas comporter de branches conditionnelles basées sur les noms de simulateurs. Un nouveau benchmark n’a pas besoin d’un processeur d’environnement : les observations atteignent les processeurs propres à la politique déjà sous forme canonique.
Comment s'exécutent les évaluations
lerobot-eval évalue chaque benchmark de la même manière :
make_env()construit le dictionnaire imbriqué{suite: {task_id: vector env}}.eval_policy_all()exécuten_episodesdéroulements pour chaque tâche, une tâche à la fois (ou--env.max_parallel_tasksà la fois).- Les résultats sont agrégés par épisode, par tâche, par suite et globalement :
pc_success(taux de réussite),avg_sum_rewardetavg_max_reward. Chaque taux de réussite est accompagné de ses compteurs (n_successsurn_episodes) et d’un intervalle de confiance de Wilson à 95 % en pourcentage (pc_success_ci95). L’intervalle ne couvre que le bruit d’échantillonnage entre les épisodes ; il ne renseigne en rien sur la variation entre les exécutions d’entraînement d’une même recette.
Le succès provient de info["is_success"], que votre environnement signale à chaque step(). Votre environnement peut également signaler ses propres nombres. Il les place dans info["metrics"], une carte associant chaque nom à un float par environnement. Ils atteignent l’entrée de chaque épisode sous per_episode[i]["metrics"], capturés à l’étape où l’épisode s’est terminé.
Ce que votre environnement doit fournir
Structure de l'environnement
env_fns() renvoie les fabriques d’environnement de chaque tâche, sans les instancier :
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."""Un benchmark à tâche unique renvoie {"<task>": {0: [...]}} ; un benchmark à plusieurs tâches (comme LIBERO) renvoie {"libero_spatial": {0: [...], 1: [...], ...}, ...}. LocalVectorEnv appelle chaque fabrique make_single_env lors de la première utilisation, localement ou sur le serveur et, avec --eval.use_async_envs=true, dans un processus distinct par environnement.
Le gym.Env
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)| Attribut | Type | Pourquoi |
|---|---|---|
_max_episode_steps | int | Met fin (tronque) à un épisode |
task_description | str | Transmise aux politiques VLA en tant qu'instruction linguistique |
task | str | Identifiant de secours si task_description n'est pas défini |
step() signale le succès dans info["is_success"], et tout ce qu’il mesure dans info["metrics"]. Les actions sont une Box continue unidimensionnelle.
Les observations sont un dict que l’adaptateur canonical_observation() de l’intégration comprend. L’adaptateur par défaut réutilisable mappe :
| Vos sorties d'environnement | Clé canonique | Ce que c'est |
|---|---|---|
"pixels" (tableau unique) | observation.image | Image de caméra unique, HWC uint8 |
"pixels" (dict) | observation.images.<cam> | Plusieurs caméras, chacune HWC uint8 |
"agent_pos" | observation.state | Vecteur d'état proprioceptif |
"environment_state" | observation.environment_state | État complet de l'environnement (par ex. PushT) |
Tout autre tableau est transmis sous la forme observation.<key>. Renommez les sorties de votre simulateur avec ces clés à l’intérieur de gym.Env, ou gérez sa représentation native dans l’adaptateur de cette intégration.
Simulateurs basés sur GPU (par exemple MuJoCo avec rendu EGL) : si votre simulateur alloue des contextes GPU/EGL lors de __init__, reportez cette allocation à un helper _ensure_env() appelé lors du premier reset()/step(), afin qu'un environnement construit dans un processus worker n'hérite pas de poignées GPU obsolètes. Consultez LiberoEnv._ensure_env() pour le modèle à suivre.
Conventions (conventions() dans l'intégration)
Retourne le taux de contrôle, le contrôleur et les noms de composants ordonnés du simulateur à partir de conventions(kwargs), une fonction des options du simulateur (elles peuvent dépendre d’un mode d’action, comme pour RoboTwin). Elles complètent les paramètres que la configuration omet et nomment les composants d’état et d’action pour les robots, les jeux de données et les points de contrôle. semantics est par défaut <type>-<control>-v1. robot_type nomme le corps simulé, enregistré par un robot de simulation : le type que le robot réel enregistre lorsque lerobot dispose d’une classe pour celui-ci (so_follower), sinon un nom court (panda, sawyer) ; s’il est omis, les jeux de données enregistrent sim_robot.
La configuration ( src/lerobot/envs/configs.py )
Enregistrez une configuration pour que les utilisateurs puissent sélectionner votre benchmark avec --env.type=<name> (et --robot.env.type=<name>). Sa sim_settings() décrit le simulateur comme la section sim d’un YAML de serveur ; LocalVectorEnv s’occupe du reste :
@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}}Gardez ses valeurs par défaut et celles du YAML du serveur cohérentes : une évaluation servie refuse un serveur qui exécute un environnement différent de celui décrit par la commande. Les fonctionnalités proviennent de la description de l’environnement lors de sa création, la configuration n’en déclare donc aucune.
Admission des points de contrôle
--rename_map associe les clés de caméra de l’environnement aux clés entraînées du checkpoint, comme pour tout robot ; en CI, il provient de l’entrée rename_map dans benchmarks/benchmarks.yaml. Les entrées du checkpoint doivent exister après le renommage, et les dimensions d’état/action doivent correspondre ; le renommage ne peut pas réparer les métadonnées du checkpoint. Le nombre et la disposition des canaux RGB sont vérifiés à l’admission. La hauteur/largeur d’acquisition peut différer des métadonnées d’entraînement ; le redimensionnement relève du prétraitement de la politique.
Vérification et CI
- Vérifiez l’adaptateur d’observation de l’intégration par rapport aux sorties brutes de votre simulateur, en particulier l’orientation de la caméra et l’ordre de l’état, et vérifiez les actions appliquées par l’enveloppe d’environnement.
- Comparez localement les séquences de réinitialisation/étape avec graine et celles servies (
scripts/ci/check_sim_parity.py) ; définissezparity: truedans l’entrée du manifeste d’un simulateur déterministe afin que CI l’exécute. Vérifiez le gel terminal, le succès, la longueur de l’épisode, le rendu et le nettoyage des ressources. - Exécutez les gardes d’importation désactivant torch pour le code du serveur et votre module natif. Documentez les simulateurs qui nécessitent leur propre runtime torch, tels que RoboTwin et RoboMME.
- Exécutez
lerobot-eval --env.type=<name> --env.task=<task> --eval.n_episodes=1 --policy.path=<checkpoint>localement, ainsi qu’avec--env.server.endpointcontre le serveur. Inspectez les descriptions des tâches et les vues de caméra rendues. - Exécutez le travail de benchmark généré sur le runner GPU. Conservez le smoke de formation LIBERO lors de toute modification de l'intégration partagée d'évaluation/formation.
benchmarks/benchmarks.yaml génère la matrice via scripts/ci/benchmark_matrix.py ; .github/workflows/benchmark_run.yml exécute chaque entrée. Ajoutez des entrées au manifeste plutôt que de copier les tâches CI. La CI limite le nombre d’épisodes pour vérifier le pipeline de bout en bout ; son metrics.json enregistre la révision du checkpoint, l’étiquette d’image du simulateur, le commit et la limite, et marque ces exécutions smoke : leurs taux de réussite ne sont pas des scores de politique. Les images de simulateur ont des épingles et des étiquettes de contenu distinctes ; le cache BuildKit ne persiste que sur les runners persistants.
Changer un benchmark
La canonical_observation(), les conventions() et le wrapper d’environnement d’une intégration déterminent ce que la politique perçoit et comment ses actions sont appliquées. Une mauvaise orientation d’image, un ordre d’état incorrect ou un taux de contrôle inadapté ne provoque pas de plantage : la politique reçoit des entrées légèrement erronées, et son taux de réussite diminue. Toute modification de l’un de ces éléments s’accompagne de :
- Un test de régression comparant les anciennes et nouvelles sorties sur une observation enregistrée ou synthétique.
test_libero_numpy_adapter_paritydanstests/sim/integrations/test_libero.pyen est le modèle : il démontre que l’adaptateur LIBERO sans torch produit exactement ce que faisait l’ancien processeur LIBERO. - L’effet attendu sur les scores dans la pull request : aucun, ou quels scores devraient évoluer et pourquoi.
Dépendances ( pyproject.toml )
Ajouter un nouveau groupe de dépendances facultatives pour un usage local :
mybenchmark = ["my-benchmark-pkg==1.2.3", "lerobot[scipy-dep]"]Règles de verrouillage :
- Toujours épingler les paquets de benchmark à des versions exactes pour la reproductibilité (par ex.
metaworld==3.0.0). - Ajoutez des marqueurs de plateforme si nécessaire (par exemple
; sys_platform == 'linux'). - Épingler les dépendances transitaires fragiles si elles sont connues (par exemple, l’image Meta-World épingle
mujoco==3.15.0, car MuJoCo 3.17 casse ses soudures de mocap). - Contraintes du document dans votre page de document de benchmark. Lorsque les dépendances sont en conflit avec celles de LeRobot, le chemin servi est celui pris en charge.
Rédiger une page de documentation de référence
Ajoutez la page à la section « Benchmarks » de docs/source/_toctree.yml. Chaque page de benchmark doit inclure :
- Titre et description — 1 à 2 paragraphes sur ce que le benchmark teste et pourquoi c'est important.
- Liens — article, dépôt GitHub, site web du projet (le cas échéant).
- Provenance et licences — dépôts sources, révisions de code et d’actifs verrouillées, et la licence de chacun. N’ajoutez pas de benchmark dont la licence interdit l’utilisation.
- Image ou GIF d’aperçu.
- Tâches disponibles — tableau des suites de tâches avec les compteurs et de brèves descriptions.
- Installation —
pip install -e ".[<benchmark>]", ou la commande serveur. - Évaluation — commande
lerobot-evalrecommandée avecn_episodespour des résultats reproductibles. - Entrées et sorties de la politique — clés d'observation avec leurs formes, description de l'espace d'action.
- Entraînement — exemple de commande
lerobot-train. - Résultats de référence — le taux de réussite publié d’au moins un point de contrôle préentraîné exécutable, avec sa révision et la commande
lerobot-evalqui le reproduit. Une intégration qui ne se situe pas à proximité de ce résultat n’est pas terminée.
Voir docs/source/libero.mdx et docs/source/metaworld.mdx pour des exemples complets.