添加新基准测试
本指南将引导您向 LeRobot 添加新的仿真基准测试。按顺序遵循这些步骤,并使用现有基准测试作为模板。
LeRobot 中的基准测试是一组 Gymnasium 环境,它们在标准 gym.Env 接口后面包装第三方模拟器(如 LIBERO 或 Meta-World)。然后 lerobot-eval CLI 在所有基准测试中统一运行评估。
现有基准测试概览
在深入之前,这里是已经集成的内容:
| 基准测试 | 环境文件 | 配置类 | 任务 | 动作维度 | 处理器 |
|---|---|---|---|---|---|
| LIBERO | envs/libero.py |
LiberoEnv |
5 个套件共 130 个 | 7 | LiberoProcessorStep |
| Meta-World | envs/metaworld.py |
MetaworldEnv |
50 (MT50) | 4 | None |
| IsaacLab Arena | Hub 托管 | IsaaclabArenaEnv |
可配置 | 可配置 | IsaaclabArenaProcessorStep |
使用 src/lerobot/envs/libero.py 和 src/lerobot/envs/metaworld.py 作为参考实现。
整体架构
数据流
在评估期间,数据经过四个阶段:
1. gym.Env ──→ 原始观测(numpy 字典)
2. 预处理 ──→ 标准 LeRobot 键 + 任务描述
(envs/utils.py 中的 preprocess_observation, env.call("task_description"))
3. 处理器 ──→ 环境特定然后策略特定的转换
(env_preprocessor, policy_preprocessor)
4. 策略 ──→ select_action() ──→ 动作张量
然后反向:policy_postprocessor → env_postprocessor → numpy 动作 → env.step()
大多数基准测试只需要关心阶段 1(以正确格式生成观测)和可选的阶段 3(如果需要环境特定的转换)。
环境结构
make_env() 返回一个嵌套的向量化环境字典:
dict[str, dict[int, gym.vector.VectorEnv]]
# ^套件 ^任务ID
单任务环境(例如 PushT)看起来像 {"pusht": {0: vec_env}}。
多任务基准测试(例如 LIBERO)看起来像 {"libero_spatial": {0: vec0, 1: vec1, ...}, ...}。
评估如何运行
所有基准测试都由 lerobot-eval 以相同方式评估:
make_env()构建嵌套的{suite: {task_id: VectorEnv}}字典。eval_policy_all()遍历每个套件和任务。- 对于每个任务,它通过
rollout()运行n_episodes次推演。 - 结果按层次聚合:回合、任务、套件、总体。
- 指标包括
pc_success(成功率)、avg_sum_reward和avg_max_reward。
关键部分:您的环境必须在每次 step() 调用时返回 info["is_success"]。这是评估循环如何知道任务是否完成的方式。
您的环境必须提供什么
LeRobot 不强制执行严格的观测模式。相反,它依赖于所有基准测试遵循的一组约定。
环境属性
您的 gym.Env 必须设置这些属性:
| 属性 | 类型 | 原因 |
|---|---|---|
_max_episode_steps |
int |
rollout() 使用它来限制回合长度 |
task_description |
str |
作为语言指令传递给 VLA 策略 |
task |
str |
如果未设置 task_description,则为后备标识符 |
成功报告
您的 step() 和 reset() 必须在 info 字典中包含 "is_success":
info = {"is_success": True} # 或 False
return observation, reward, terminated, truncated, info
观测
最简单的方法是将模拟器的输出映射到 preprocess_observation() 已经理解的标准键。在您的 gym.Env 内部执行此操作(例如在 _format_raw_obs() 辅助函数中):
| 您的环境应输出 | LeRobot 将其映射到 | 它是什么 |
|---|---|---|
"pixels"(单个数组) |
observation.image |
单个相机图像,HWC uint8 |
"pixels"(字典) |
observation.images.<cam> |
多个相机,每个 HWC uint8 |
"agent_pos" |
observation.state |
本体感受状态向量 |
"environment_state" |
observation.env_state |
完整环境状态(例如 PushT) |
"robot_state" |
observation.robot_state |
嵌套机器人状态字典(例如 LIBERO) |
如果您的模拟器使用不同的键名,您有两个选择:
- 推荐:在您的
gym.Env包装器内部将它们重命名为标准键。 - 替代方案:编写一个环境处理器,在
preprocess_observation()运行后转换观测(见下面的步骤 4)。
动作
动作是 gym.spaces.Box 中的连续 numpy 数组。维度取决于您的基准测试(LIBERO 为 7,Meta-World 为 4 等)。策略通过其 input_features / output_features 配置适应不同的动作维度。
特征声明
每个 EnvConfig 子类声明两个字典,告诉策略期望什么:
features— 将特征名称映射到PolicyFeature(type, shape)(例如动作维度、图像形状)。features_map— 将原始观测键映射到 LeRobot 约定键(例如"agent_pos"到"observation.state")。
分步指南
Tip
至少,您需要两个文件:一个 gym.Env 包装器和一个带有 create_envs() 覆盖的
EnvConfig 子类。其他所有内容都是可选的或文档。不需要更改 factory.py。
检查清单
| 文件 | 必需 | 原因 |
|---|---|---|
src/lerobot/envs/<benchmark>.py |
是 | 将模拟器包装为标准 gym.Env |
src/lerobot/envs/configs.py |
是 | 为 CLI 注册您的基准测试及其 create_envs() |
src/lerobot/processor/env_processor.py |
可选 | 自定义观测/动作转换 |
src/lerobot/envs/utils.py |
可选 | 仅当您需要新的原始观测键时 |
pyproject.toml |
是 | 声明基准测试特定的依赖项 |
docs/source/<benchmark>.mdx |
是 | 面向用户的文档页面 |
docs/source/_toctree.yml |
是 | 将您的页面添加到文档侧边栏 |
1. gym.Env 包装器(src/lerobot/envs/<benchmark>.py)
创建一个包装第三方模拟器的 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)
def reset(self, seed=None, **kwargs):
... # 返回 (observation, info) — info 必须包含 {"is_success": False}
def step(self, action: np.ndarray):
... # 返回 (obs, reward, terminated, truncated, info) — info 必须包含 {"is_success": <bool>}
def render(self):
... # 返回 RGB 图像作为 numpy 数组
def close(self):
...
基于 GPU 的模拟器(例如使用 EGL 渲染的 MuJoCo):如果您的模拟器在 __init__ 期间分配 GPU/EGL 上下文,请将该分配推迟到在第一次 reset()/step() 时调用的 _ensure_env() 辅助函数。这避免了当 AsyncVectorEnv 生成工作进程时继承陈旧的 GPU 句柄。参见 LiberoEnv._ensure_env() 的模式。
还提供一个返回嵌套字典结构的工厂函数:
def create_mybenchmark_envs(
task: str,
n_envs: int,
gym_kwargs: dict | None = None,
env_cls: type | None = None,
) -> dict[str, dict[int, Any]]:
"""为 MyBenchmark 创建 {suite_name: {task_id: VectorEnv}}。"""
...
参见 create_libero_envs()(多套件、多任务)和 create_metaworld_envs()(难度分组任务)作为参考。
2. 配置(src/lerobot/envs/configs.py)
注册一个配置数据类,以便用户可以使用 --env.type=<name> 选择您的基准测试。每个配置通过两个方法拥有其环境创建和处理器逻辑:
create_envs(n_envs, use_async_envs)— 返回{suite: {task_id: VectorEnv}}。基类默认使用gym.make()用于单任务环境。多任务基准测试覆盖此方法。get_env_processors()— 返回(preprocessor, postprocessor)。基类默认返回恒等(无操作)管道。如果您的基准测试需要观测/动作转换,则覆盖。
@EnvConfig.register_subclass("<benchmark_name>")
@dataclass
class MyBenchmarkEnvConfig(EnvConfig):
task: str = "<default_task>"
fps: int = <fps>
obs_type: str = "pixels_agent_pos"
features: dict[str, PolicyFeature] = field(default_factory=lambda: {
ACTION: PolicyFeature(type=FeatureType.ACTION, shape=(<action_dim>,)),
})
features_map: dict[str, str] = field(default_factory=lambda: {
ACTION: ACTION,
"agent_pos": OBS_STATE,
"pixels": OBS_IMAGE,
})
def __post_init__(self):
... # 根据 obs_type 填充特征
@property
def gym_kwargs(self) -> dict:
return {"obs_type": self.obs_type, "render_mode": self.render_mode}
def create_envs(self, n_envs: int, use_async_envs: bool = True):
"""为多任务基准测试或自定义环境创建覆盖。"""
from lerobot.envs.<benchmark> import create_<benchmark>_envs
return create_<benchmark>_envs(task=self.task, n_envs=n_envs, ...)
def get_env_processors(self):
"""如果您的基准测试需要观测/动作转换,则覆盖。"""
from lerobot.processor import PolicyProcessorPipeline
from lerobot.processor.env_processor import MyBenchmarkProcessorStep
return (
PolicyProcessorPipeline(steps=[MyBenchmarkProcessorStep()]),
PolicyProcessorPipeline(steps=[]),
)
关键点:
register_subclass名称是用户在 CLI 上传递的内容(--env.type=<name>)。features告诉策略环境产生什么。features_map将原始观测键映射到 LeRobot 约定键。- 不需要更改
factory.py— 工厂自动委托给cfg.create_envs()和cfg.get_env_processors()。
3. 环境处理器(可选 — src/lerobot/processor/env_processor.py)
仅当您的基准测试需要超出 preprocess_observation() 处理的观测转换时才需要(例如图像翻转、坐标转换)。在此处定义处理器步骤,并从配置中的 get_env_processors() 返回它(见步骤 2):
@dataclass
@ProcessorStepRegistry.register(name="<benchmark>_processor")
class MyBenchmarkProcessorStep(ObservationProcessorStep):
def _process_observation(self, observation):
processed = observation.copy()
# 您的转换在这里
return processed
def transform_features(self, features):
return features # 如果形状改变则更新
def observation(self, observation):
return self._process_observation(observation)
参见 LiberoProcessorStep 的完整示例(图像旋转、四元数到轴角转换)。
4. 依赖项(pyproject.toml)
添加一个新的可选依赖组:
mybenchmark = ["my-benchmark-pkg==1.2.3", "lerobot[scipy-dep]"]
固定规则:
- 始终固定基准测试包到确切版本以实现可重现性(例如
metaworld==3.0.0)。 - 在需要时添加平台标记(例如
; sys_platform == 'linux')。 - 固定脆弱的传递依赖(如果已知)(例如 Meta-World 的
gymnasium==1.1.0)。 - 在基准测试文档页面中记录约束。
用户安装:
pip install -e ".[mybenchmark]"
5. 文档(docs/source/<benchmark>.mdx)
按照下一节中的模板编写面向用户的页面。参见 docs/source/libero.mdx 和 docs/source/metaworld.mdx 的完整示例。
6. 目录(docs/source/_toctree.yml)
将您的基准测试添加到"基准测试"部分:
- sections:
- local: libero
title: LIBERO
- local: metaworld
title: Meta-World
- local: envhub_isaaclab_arena
title: NVIDIA IsaacLab Arena Environments
- local: <your_benchmark>
title: <Your Benchmark Name>
title: "Benchmarks"
验证您的集成
完成上述步骤后,确认一切正常:
- 安装 —
pip install -e ".[mybenchmark]"并验证依赖组安装干净。 - 冒烟测试环境创建 — 在 Python 中使用您的配置调用
make_env(),检查返回的字典是否具有预期的{suite: {task_id: VectorEnv}}形状,并且reset()返回具有正确键的观测。 - 运行完整评估 —
lerobot-eval --env.type=<name> --env.task=<task> --eval.n_episodes=1 --policy.path=<any_compatible_policy>以端到端地执行完整管道。(batch_size默认为基于 CPU 核心的自动调整;传递--eval.batch_size=1以强制单个环境。) - 检查成功检测 — 验证当任务实际完成时
info["is_success"]翻转为True。这是评估循环用于计算成功率的内容。
编写基准测试文档页面
每个基准测试 .mdx 页面应包括:
- 标题和描述 — 1-2 段关于基准测试测试什么以及为什么重要。
- 链接 — 论文、GitHub 仓库、项目网站(如果可用)。
- 概览图像或 GIF。
- 可用任务 — 任务套件表,包含计数和简要描述。
- 安装 —
pip install -e ".[<benchmark>]"加上任何额外步骤(环境变量、系统包)。 - 评估 — 推荐的
lerobot-eval命令,包含用于可重现结果的n_episodes。batch_size默认为自动;仅在需要时指定。包括单任务和多任务示例(如果适用)。 - 策略输入和输出 — 带形状的观测键、动作空间描述。
- 推荐的评估回合 — 每个任务多少回合是标准的。
- 训练 — 示例
lerobot-train命令。 - 重现已发布结果 — 链接到预训练模型、评估命令、结果表(如果可用)。
参见 docs/source/libero.mdx 和 docs/source/metaworld.mdx 的完整示例。