LeRobot documentation

Ajouter un benchmark

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

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

ComposantEmplacementÀ ajouter
Intégrationsrc/lerobot/sim/integrations/<benchmark>.pyLe gym.Env, les usines env_fns(), l'adaptateur canonical_observation() et les conventions() pour ce simulateur
Configurationsrc/lerobot/envs/configs.pyUne sous-classe de EnvConfig dont sim_settings() mappe ses options vers les kwargs de la fabrique
Configuration du serveurdocker/sim/configs/<benchmark>.yamlLes arguments de tâche et d'usine que le serveur et l'CI utilisent
Image du simulateurdocker/Dockerfile.simUne é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 CIbenchmarks/benchmarks.yamlNom, 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épendancespyproject.tomlGroupe de dépendances facultatives pour un usage local
Documentationdocs/source/<benchmark>.mdx, _toctree.ymlUne page destinée aux utilisateurs
Provenancedocs/source/<benchmark>.mdxDé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 :

  1. make_env() construit le dictionnaire imbriqué {suite: {task_id: vector env}}.
  2. eval_policy_all() exécute n_episodes déroulements pour chaque tâche, une tâche à la fois (ou --env.max_parallel_tasks à la fois).
  3. Les résultats sont agrégés par épisode, par tâche, par suite et globalement : pc_success (taux de réussite), avg_sum_reward et avg_max_reward. Chaque taux de réussite est accompagné de ses compteurs (n_success sur n_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)
AttributTypePourquoi
_max_episode_stepsintMet fin (tronque) à un épisode
task_descriptionstrTransmise aux politiques VLA en tant qu'instruction linguistique
taskstrIdentifiant 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'environnementClé canoniqueCe que c'est
"pixels" (tableau unique)observation.imageImage de caméra unique, HWC uint8
"pixels" (dict)observation.images.<cam>Plusieurs caméras, chacune HWC uint8
"agent_pos"observation.stateVecteur 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

  1. 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.
  2. Comparez localement les séquences de réinitialisation/étape avec graine et celles servies (scripts/ci/check_sim_parity.py) ; définissez parity: true dans 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.
  3. 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.
  4. Exécutez lerobot-eval --env.type=<name> --env.task=<task> --eval.n_episodes=1 --policy.path=<checkpoint> localement, ainsi qu’avec --env.server.endpoint contre le serveur. Inspectez les descriptions des tâches et les vues de caméra rendues.
  5. 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_parity dans tests/sim/integrations/test_libero.py en 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-eval recommandée avec n_episodes pour 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-eval qui 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.

Update on GitHub