LeRobot documentation

添加策略

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

添加策略

本指南将指导您实现自定义策略,并使其能够与 LeRobot 的训练、评估和部署工具配合使用。有两条路径:

  • 插件(树外) — 将您的策略作为独立的 lerobot_policy_* 包发布。这种方式速度更快,无需提交 PR,便于快速迭代。适用于实验、内部使用,或希望独立发布的情况。
  • 内置(贡献给 LeRobot) — 将您的策略直接合并到 src/lerobot/policies/ 中。这需要提交 PR,但会使您的策略成为该库的一等公民。

插件路径通常是正确的起点——一旦策略趋于稳定,且将其随库一起发布具有明确价值,就可以将其提升为库内代码。

无论采用哪种方式,其基本构建模块都是相同的:一个配置类、一个策略类和一个处理器工厂。本指南的前半部分介绍这些共享组件;后半部分则介绍特定路径的脚手架(路径 A、路径 B)。

关于语气的说明:robot-learning 是一个快速演进的领域,“策略(policy)的形态”可能会随着新架构的出现而改变。本文所述的约定之所以存在,是因为它们能让 lerobot-train 和 lerobot-eval 在差异极大的模型上统一工作。如果某个新策略确实不符合这些约定,请提出(在 PR 或 issue 中)——这些约定并非不可更改。


策略的构成

每个策略都由三个基本构建块组成。以下名称使用 my_policy 作为占位符——请将其替换为您的策略名称。该名称至关重要:它必须与您传递给 @PreTrainedConfig.register_subclass 的字符串、MyPolicy.name 类属性以及 make_<name>_pre_post_processors 工厂函数相匹配(下文将分别详细介绍)。

配置类

继承 PreTrainedConfig 并注册您的策略类型。以下是一个模板——请根据您的策略架构和训练需求自定义参数和方法。

# configuration_my_policy.py
from dataclasses import dataclass, field
import torch
from lerobot.configs import PreTrainedConfig
from lerobot.optim import AdamWConfig
from lerobot.optim import CosineDecayWithWarmupSchedulerConfig

@PreTrainedConfig.register_subclass("my_policy")
@dataclass
class MyPolicyConfig(PreTrainedConfig):
    """Configuration class for MyPolicy.

    Args:
        n_obs_steps: Number of observation steps to use as input
        horizon: Action prediction horizon
        n_action_steps: Number of action steps to execute
        hidden_dim: Hidden dimension for the policy network
        # Add your policy-specific parameters here
    """

    dtype: torch.dtype | None = torch.bfloat16

    horizon: int = 50
    n_action_steps: int = 50
    hidden_dim: int = 256

    optimizer_lr: float = 1e-4
    optimizer_weight_decay: float = 1e-4

    def __post_init__(self):
        super().__post_init__()
        if self.n_action_steps > self.horizon:
            raise ValueError("n_action_steps cannot exceed horizon")

    def validate_features(self) -> None:
        """Validate input/output feature compatibility.

        Call this explicitly from your policy's __init__ — the base class does not.
        """
        if not self.image_features:
            raise ValueError("MyPolicy requires at least one image feature.")
        if self.action_feature is None:
            raise ValueError("MyPolicy requires 'action' in output_features.")

    def get_optimizer_preset(self) -> AdamWConfig:
        return AdamWConfig(lr=self.optimizer_lr, weight_decay=self.optimizer_weight_decay)

    def get_scheduler_preset(self):
        """Return a LRSchedulerConfig from lerobot.optim, or None."""
        return None

    @property
    def observation_delta_indices(self) -> list[int] | None:
        """Relative timestep offsets the dataset loader provides per observation.

        Return `None` for single-frame policies. For temporal policies that consume
        multiple past or future frames, return a list of offsets, e.g. `[-20, -10, 0, 10]` for
        3 past frames at stride 10 and 1 future frame at stride 10.
        """
        return None

    @property
    def action_delta_indices(self) -> list[int]:
        """Relative timestep offsets for the action chunk the dataset loader returns."""
        return list(range(self.horizon))

    @property
    def reward_delta_indices(self) -> None:
        return None

传递给 @register_subclass 的字符串必须与 MyPolicy.name(见下一节)匹配,并且是用户在命令行中作为 --policy.type 提供的值。除非确实需要其他设置,否则 get_optimizer_preset 的默认值应为来自 lerobot.optim 的 AdamW。PreTrainedConfig 已经声明了 dtype: torch.dtype | None;要为您的策略设置默认精度,请使用 torch.dtype 值重新声明它(例如 dtype: torch.dtype | None = torch.bfloat16)。

策略类

继承自 PreTrainedPolicy 并设置两个类属性——两者都会由 __init_subclass__ 检查:

# modeling_my_policy.py
import torch
import torch.nn as nn
from typing import Any

from lerobot.policies import PreTrainedPolicy
from lerobot.utils.constants import ACTION
from .configuration_my_policy import MyPolicyConfig

class MyPolicy(PreTrainedPolicy):
    config_class = MyPolicyConfig  # must match the string in @register_subclass
    name = "my_policy"

    def __init__(self, config: MyPolicyConfig, dataset_stats: dict[str, Any] = None):
        super().__init__(config, dataset_stats)
        config.validate_features()  # not called automatically by the base class
        self.config = config
        self.model = ...  # your nn.Module here

    def reset(self):
        """Reset per-episode state. Called by lerobot-eval at the start of each episode."""
        ...

    def get_optim_params(self) -> dict:
        """Return parameters to pass to the optimizer (e.g. with per-group lr/wd)."""
        return {"params": self.parameters()}

    def predict_action_chunk(self, batch: dict[str, torch.Tensor], **kwargs) -> torch.Tensor:
        """Return the full action chunk (B, chunk_size, action_dim) for the current observation."""
        ...

    def select_action(self, batch: dict[str, torch.Tensor], **kwargs) -> torch.Tensor:
        """Return a single action for the current timestep (called every step at inference)."""
        ...

    def forward(self, batch: dict[str, torch.Tensor]) -> tuple[torch.Tensor, dict | None]:
        """Compute the training loss.

        Returns `(loss, output_dict)`. `output_dict` may be `None`; everything in it must be
        logging-friendly Python natives (no tensors with gradients).

        `batch["action_is_pad"]` is a bool mask of shape (B, horizon) that marks
        timesteps padded because the episode ended before `horizon` steps; you
        can exclude those from your loss.
        """
        actions = batch[ACTION]
        action_is_pad = batch.get("action_is_pad")
        ...
        return loss, {"some_loss_component": some_loss_component.item()}

train/eval 循环调用的方法:

方法使用方它的作用
reset() -> Nonelerobot-eval在每个回合开始时清除每个回合的状态。
select_action(batch, **kwargs) -> Tensorlerobot-eval返回下一个动作 (B, action_dim)。每个步骤都会调用。
predict_action_chunk(batch, **kwargs) -> Tensor策略本身返回一个动作块 (B, chunk_size, action_dim)。目前在基类中为抽象方法——如果你的策略不进行分块,请抛出 NotImplementedError。
forward(batch, reduction="mean") -> tuple[Tensor, dict \| None]lerobot-train返回 (loss, output_dict)。如果需要支持按样本加权,请接受 reduction="none"。
get_optim_params() -> dict优化器对于简单策略,返回 self.parameters();对于多优化器策略,返回一个命名参数字典(有关按组设置学习率的示例,请参阅 modeling_act.py 中的 get_optim_params)。
update() -> None (可选)lerobot-train在每次优化器步骤之后调用(如果已定义)。用于 EMA、目标网络、回放缓冲区(TDMPC 使用此功能)。

批次是以 lerobot.utils.constants 中的常量为键的扁平字典:OBS_STATE(observation.state.<motor>)、OBS_IMAGES(observation.images.<camera>)、OBS_LANGUAGE、ACTION 等。请复用这些常量——不要自行发明新的前缀。

如果您的模型规模足以采用分片多 GPU 训练,请同时声明其 FSDP 包装单元——即分片操作所针对的重复块类:

class MyPolicy(PreTrainedPolicy):
    ...
    _fsdp_wrap_modules = ["MyTransformerBlock"]

通过这一项声明,--parallelism.dp_shard=N 即可直接用于您的策略(用户仍可通过 --accelerator.fsdp.wrap_modules 进行覆盖)。在没有指定任何 wrap 源的情况下,分片运行会按设计在启动时失败。

处理器函数

LeRobot 使用 PolicyProcessorPipeline 在策略周围对输入进行归一化,并对输出进行反归一化。具体参考示例,请查看 processor_act.py 或 processor_diffusion.py。

请在此处格外留意:处理器是最常见的可复现性痛点。归一化模式(IDENTITY 与 MEAN_STD、MIN_MAX 或 QUANTILES/QUANTILE10)不一致,或归一化所应用的功能特征不一致,都会导致训练和评估过程不报错,却悄无声息地破坏结果。请确保模式与检查点的训练方式相匹配,所需的统计量存在(例如 QUANTILES 需要 q01/q99),并且预处理和后处理器保持一致。

# processor_my_policy.py
from typing import Any
import torch

from lerobot.processor import PolicyAction, PolicyProcessorPipeline


def make_my_policy_pre_post_processors(
    config,
    dataset_stats: dict[str, dict[str, torch.Tensor]] | None = None,
) -> tuple[
    PolicyProcessorPipeline[dict[str, Any], dict[str, Any]],
    PolicyProcessorPipeline[PolicyAction, PolicyAction],
]:
    preprocessor = ...   # build your PolicyProcessorPipeline for inputs
    postprocessor = ...  # build your PolicyProcessorPipeline for outputs
    return preprocessor, postprocessor

重要 — 函数命名:LeRobot 通过名称来发现你的处理器。函数 必须 命名为 make_{policy_name}_pre_post_processors(与传递给 @PreTrainedConfig.register_subclass 的字符串相匹配)。

可选:调整从检查点加载的处理器

make_{policy_name}_pre_post_processors 仅在从头构建处理器时运行。只要涉及检查点(微调、恢复或评估),LeRobot 就会加载与权重一起序列化的流水线,并应用调用者的覆盖设置。

如果您的策略需要调整这些已加载的流水线,请在同一模块中添加一个可选的第二个函数,命名为 make_{policy_name}_pre_post_processors_from_pretrained。LeRobot 会通过相同的命名约定找到它,并在通用加载处调用它。无需特殊处理的策略可以省略该函数。

from lerobot.processor import load_pretrained_policy_processors
from lerobot.utils.constants import (
    POLICY_POSTPROCESSOR_DEFAULT_NAME,
    POLICY_PREPROCESSOR_DEFAULT_NAME,
)


def make_my_policy_pre_post_processors_from_pretrained(
    config,
    pretrained_path: str,
    *,
    revision: str | None = None,
    dataset_stats: dict[str, dict[str, torch.Tensor]] | None = None,
    dataset_meta: Any | None = None,
    preprocessor_overrides: dict[str, Any] | None = None,
    postprocessor_overrides: dict[str, Any] | None = None,
    preprocessor_config_filename: str = f"{POLICY_PREPROCESSOR_DEFAULT_NAME}.json",
    postprocessor_config_filename: str = f"{POLICY_POSTPROCESSOR_DEFAULT_NAME}.json",
) -> tuple[
    PolicyProcessorPipeline[dict[str, Any], dict[str, Any]],
    PolicyProcessorPipeline[PolicyAction, PolicyAction],
]:
    preprocessor, postprocessor = load_pretrained_policy_processors(
        pretrained_path,
        revision=revision,
        preprocessor_overrides=preprocessor_overrides,
        postprocessor_overrides=postprocessor_overrides,
        preprocessor_config_filename=preprocessor_config_filename,
        postprocessor_config_filename=postprocessor_config_filename,
    )
    ...  # reconcile the loaded pipelines with the current `config`
    return preprocessor, postprocessor

有关可运行的示例,请参阅 processor_evo1.py(重新填充归一化统计量)、processor_groot.py(处理原始上游检查点)以及 processor_molmoact2.py(重命名覆盖键)。

在返回动作的同时返回预测结果(想象帧、子任务、边界框)

策略除了执行的动作外,还可能返回其预期或推断的内容,例如世界模型想象出的帧、预测的子任务或边界框,因此 --display_data 会在 Rerun 或 Foxglove 中实时显示这些内容。select_action 和 predict_action_chunk 接收一个批次,并返回一个裸动作张量(所有策略的默认行为)或一个 PolicyOutput(lerobot.lerobot_types):一个与输入批次具有相同键结构的批次,其中动作位于 "action" 键下,PolicyPrediction 位于 "prediction" 键下。预测值与动作一样按批次组织,每个环境对应一个值,并且每个条目都以其所引用的内容作为键:

  • observation:按观测键划分的预测未来观测("observation.images.top" -> [B, C, H, W] 帧)。它显示为 prediction.images.top。
  • language:每个环境一个字符串,按注释风格("subtask"、"plan"、"memory")区分,即注释流水线中语言列的样式。显示为 prediction.subtask。
  • boxes:按它们所绘制于其上的观察图像键,每个环境在标注 VQA 架构中对应一个 bbox 答案({"detections": [{"label", "bbox": [x1, y1, x2, y2]}]},图像比例为 0 到 1)。

调用方使用 policy_output_to_transition 对两种返回类型进行归一化,该函数将 PolicyOutput 转换为处理器所使用的 EnvTransition,因此只有做出预测的策略会发生变化。查看器显示的是第一个环境。

from lerobot.utils.constants import ACTION, PREDICTION


class MyPolicy(PreTrainedPolicy):
    def select_action(self, batch, **kwargs) -> Tensor | PolicyOutput:
        if not self._action_queue:
            chunk, extras = self.model.sample(batch)
            self._action_queue.extend(chunk.transpose(0, 1))
            if self.config.return_prediction:  # a config field of your choosing
                # The prediction goes with the step that computed it; the others return a bare action.
                prediction = {
                    "observation": {"observation.images.top": extras.next_frames},  # [B, C, H, W]
                    "language": {"subtask": extras.subtasks},  # list[str], one per env
                    "boxes": {
                        "observation.images.top": [
                            {"detections": [{"label": "cube", "bbox": xyxy}]} for xyxy in extras.cube_xyxy
                        ]
                    },
                }
                return {ACTION: self._action_queue.popleft(), PREDICTION: prediction}
        return self._action_queue.popleft()
  • 预测值保存了当前步骤的值。跨越一个块(例如一段想象的片段)的预测值会分块返回:每个步骤返回与其所服务的动作相匹配的部分。
  • 每条记录会持续显示,直到再次返回。返回空的 detections 即可清除某个摄像头的检测框。
  • 预测功能需通过策略配置显式启用,禁用时不会产生任何开销。
  • 预测永远不会改变动作,只有动作会经过后处理器。需要引导策略的文本(例如下一个子任务)会通过 generate_text(见下文)进行处理。

支持文本的策略

语言策略在两个处理器步骤之间共享配方渲染:

  • RenderTrainingMessagesStep 将数据集的语言注释转换为渲染后的消息。
  • RenderRuntimeMessagesStep 将用户提示词转换为渲染后的消息。

策略特定的处理器随后应用聊天格式化和分词。

策略配置存储已解析的 recipe: TrainingRecipe | None(来自 lerobot.datasets.recipe)。可选的 recipe_path 可以加载 YAML 配方;在构建处理器之前对其进行解析。已解析的配方会启用配方训练,而 None 会使训练渲染器变为空操作。在配置和处理器中序列化已解析的配方,以便部署不依赖于本地 YAML 路径。基于配方的渲染需要 dataset 额外依赖,因为配方目前位于 lerobot.datasets 下。

将两个渲染器都放在策略原生的聊天格式化和分词之前:

input_steps = [
    RenderRuntimeMessagesStep(config.recipe),
    RenderTrainingMessagesStep(config.recipe),
    # observation transforms
    # policy-native roles, image placement, chat template, and tokenization
    # device placement
]

相同的流水线在训练和推理中均会被保存和加载。运行时渲染仅在显式指定 query_kind 时执行。训练渲染会跳过运行时查询,并在配置了配方时,处理带有原始标注或动作目标的样本。普通的仅含观测的输入将直接通过。支持仅含标注文本的训练,且无需动作张量。无标注的训练需要动作目标以启用任务回退机制。

共享工厂仅负责构建或加载处理器。语言配方微调会根据当前配置和数据集统计信息来构建它们,因此已保存的处理器无法覆盖新的配方。恢复训练和推理会加载已保存的流水线,包括其配方和统计信息。非语言训练保留其现有的构建和检查点加载行为。 新建的工厂自行推导归一化和相对动作设置。新建的工厂使用已配置的设备;现有的数据集批预处理会应用训练重命名映射。

messages_rendered 包含经过配方扩展的语义消息,位于原生聊天格式化或分词之前。训练侧车索引的是同一个列表。消息辅助函数位于 lerobot.utils.language 中。

交互式 rollout 会添加 query_kind 和 query_text。运行时渲染支持:

  • vqa:将调用者的问题保留为用户消息。
  • next_subtask:渲染检查点配方,直到但不包括监督 ${subtask} 的助手轮次。

随后,该策略通过现有的策略 API 暴露其解码器:

class MyPolicy(PreTrainedPolicy):
    def supports_text_generation(self) -> bool:
        return True

    def generate_text(self, batch) -> str:
        token_ids = self.model.generate(**batch)
        return self.tokenizer.decode(token_ids, skip_special_tokens=True)

generate_text 消耗的是模型就绪的处理器输出;它不得重建或重新分词调用方的提示词。在训练期间,策略原生分词器使用 target_message_indices 仅对选定的助手片段进行监督。请将配方存储在语言策略的配置中,以便其随检查点一起序列化,从而让部署时能够重建用于训练的提示词。


路径 A:树外插件

发布策略的最快方式:将其打包为独立的 Python 发行版,并与 LeRobot 一同安装。无需提交 PR,您完全掌控发布周期,并且可以在自己的命名空间下发布到 PyPI。

包结构

创建一个以 lerobot_policy_ 为前缀(重要!)的包,后跟您的策略名称:

lerobot_policy_my_policy/
├── pyproject.toml
└── src/
    └── lerobot_policy_my_policy/
        ├── __init__.py
        ├── configuration_my_policy.py
        ├── modeling_my_policy.py
        └── processor_my_policy.py

pyproject.toml

[project]
name = "lerobot_policy_my_policy"
version = "0.1.0"
dependencies = [
    # your policy-specific dependencies
]
requires-python = ">= 3.12"

[build-system]
build-backend = # your-build-backend
requires = # your-build-system

包 __init__.py

在包的 __init__.py 中公开你的类,并防止缺少 lerobot:

# __init__.py
"""Custom policy package for LeRobot."""

try:
    import lerobot  # noqa: F401
except ImportError:
    raise ImportError(
        "lerobot is not installed. Please install lerobot to use this policy package."
    )

from .configuration_my_policy import MyPolicyConfig
from .modeling_my_policy import MyPolicy
from .processor_my_policy import make_my_policy_pre_post_processors

__all__ = [
    "MyPolicyConfig",
    "MyPolicy",
    "make_my_policy_pre_post_processors",
]

安装和使用

cd lerobot_policy_my_policy
pip install -e .

# Or install from PyPI if published
pip install lerobot_policy_my_policy

安装完成后,您的策略会自动集成到 LeRobot 的训练和评估工具中:

lerobot-train \
    --policy.type my_policy \
    --dataset.repo_id lerobot/pusht \
    --env.type pusht \
    --steps 200000 \
    --policy.push_to_hub false

路径 B:在树内贡献

当您的策略趋于稳定,且将其随库发布具有明确价值时,您可以直接将其合并到 LeRobot 中。请先阅读通用的贡献指南和PR 模板——那里列出了每个 PR 必须满足的测试/质量要求(pre-commit run -a、pytest、社区审查规则等)。以下是叠加在该基础之上的策略特定层。

树内布局

src/lerobot/policies/my_policy/
├── __init__.py                    # re-exports config + modeling + processor factory
├── configuration_my_policy.py     # MyPolicyConfig + @register_subclass
├── modeling_my_policy.py          # MyPolicy(PreTrainedPolicy)
├── processor_my_policy.py         # make_my_policy_pre_post_processors
└── README.md                      # symlink → ../../../../docs/source/policy_my_policy_README.md

两点说明:

  • 位于源代码旁边的 README.md 是指向 docs/source/policy_<name>_README.md 的 符号链接 —— 实际文件位于 docs/ 目录下。现有的策略(act、smolvla、diffusion 等)均采用此方式;请复制其中一个符号链接。策略的 README 通常保持极简:包含论文链接和 BibTeX 引用。
  • 面向用户的教程——包括安装内容、训练方法、超参数和基准测试数据——单独存放在 docs/source/<my_policy>.mdx 中,并在 _toctree.yml 的“Policies”下注册。

文件名至关重要:工厂通过名称进行延迟导入,而处理器则通过 make_<policy_name>_pre_post_processors 约定被发现。

接线

有两个地方需要知道你的策略。全部按名称指定。

  1. policies/__init__.py — 重新导出 MyPolicyConfig 并将其添加到 __all__ 中。正是这个导入操作注册了你的策略:@PreTrainedConfig.register_subclass("my_policy") 会执行,此后工厂方法将按约定解析所有内容。不要重新导出建模类;它通过工厂方法延迟加载(因此 import lerobot 保持快速)。
  2. templates/lerobot_modelcard_template.md 和根目录下的 README.md — 该模板是训练结束时的发布器用于渲染每个使用您的策略训练的 checkpoint 的模型卡片的:请在 model_name 分支中添加一行对您的策略的描述,在 policy_docs 中将其映射,以便卡片链接到您的 MDX 指南,并可选地在 diagrams 中添加一张架构图。然后,在根目录 README.md 的模型表格中,将您的策略添加到正确的类别下,并链接到您的文档页面。

镜像一个与您的策略在结构上相似的现有策略;差异很小。

重型 / 可选依赖项

大多数策略都需要一个重量级的骨干网络(transformers、diffusers、特定的 VLM SDK)。只要存在可用的实现,请优先从 transformers 或 diffusers 等库中加载,而不是在代码库内重新实现该架构。

约定采用两步门控:在模块顶部使用 TYPE_CHECKING 保护的导入,并在构造函数中进行 require_package 运行时检查。modeling_diffusion.py 是标准参考:

from typing import TYPE_CHECKING
from lerobot.utils.import_utils import _diffusers_available, require_package

if TYPE_CHECKING or _diffusers_available:
    from diffusers.schedulers.scheduling_ddim import DDIMScheduler
else:
    DDIMScheduler = None  # keeps the symbol bindable at import time

class DiffusionPolicy(PreTrainedPolicy):
    def __init__(self, config):
        require_package("diffusers", extra="diffusion")
        super().__init__(config)
        ...

这样:

  • import lerobot.policies 在未额外安装的情况下仍可正常工作(该符号仅被绑定为 None)。
  • 类型检查器看到的是真实的符号。
  • 在未安装额外依赖的情况下实例化策略,会引发一个明确的 ImportError,并指向 pip install 'lerobot[diffusion]'。

在 pyproject.toml 的 [project.optional-dependencies] 中添加一个对应的 extra,并将其包含在 all extra 中,以便 pip install 'lerobot[all]' 仍然可以安装所有内容。

避免复制建模文件——对其进行子类化

如果你的策略需要修改 transformers 中已存在的骨干网络(例如自定义条件、额外输入、替换子模块),请不要复制其 modeling_*.py 文件。相反,应继承最小的上游单元,并仅重写发生变化的部分。pi_gemma.py 是标准参考示例:它通过继承 GemmaModel/PaliGemmaModel 并重写解码器层的前向传播,在约 370 行代码中向 PaliGemma/Gemma 注入了 AdaRMS 条件,而不是分叉约 2,000 行的建模文件。对已加载的原生模型进行模型手术也是可行的(例如层截断、分词器扩展、隐藏状态捕获——可参考 evo1/internvl3_embedder.py、eo1/modeling_eo1.py、groot/groot_n1_7.py 中的实际示例)。如果 PR 中包含复制的建模文件,审查者会要求采用此模式;唯一可接受的例外是 transformers 中完全不存在该模型。

基准测试和已发布的检查点

附带一个可用的检查点以及至少一个可复现的数值,新策略会更容易审查——也更有用。

至少选择一个内置基准测试。LeRobot 随附了针对每个基准测试的 Docker 镜像(LIBERO、LIBERO-plus、Meta-World、RoboTwin 2.0、RoboCasa365、RoboCerebra、RoboMME、VLABench 等)。请选择与您的策略模态相匹配的基准测试——VLA 通常选择 LIBERO 或 VLABench;仅基于图像的 BC 则选择 LIBERO 或 Meta-World。完整列表位于文档侧边栏的 Benchmarks 下。

将检查点 & 处理器 Push the checkpoint & processors 推送到 Hub 的 lerobot/<policy>_<benchmark> 下(如果没有写入权限,请使用您的命名空间;维护者可以将其镜像)。最简单的方法是在训练时使用 --policy.repo_id=<namespace>/<repo> 和 --policy.push_to_hub=true:lerobot-train 会在运行结束时发布模型、两个处理器以及模型卡片。若要事后发布现有的检查点,请上传其 pretrained_model/ 目录(例如使用 hf upload),或者对于分片格式的检查点,使用 lerobot-convert-dcp --push_to_hub=...。

在您的策略的 MDX 中报告结果,附上确切的 lerobot-eval 命令和硬件配置,以便任何人都可以重新运行:

## Results

Evaluated on LIBERO with `lerobot/<policy>_libero`:

| Suite          | Success rate | n_episodes |
| -------------- | -----------: | ---------: |
| libero_spatial |        87.5% |         50 |
| libero_object  |        93.0% |         50 |
| libero_goal    |        81.5% |         50 |
| libero_10      |        62.0% |         50 |
| **average**    |    **81.0%** |        200 |

Reproduce: `lerobot-eval --policy.path=lerobot/<policy>_libero --env.type=libero --env.task=libero_spatial --eval.n_episodes=50` (1× A100 40 GB).

每个套件使用 n_episodes ≥ 50 以获得稳定的成功率估计。

如果您的策略仅适用于真实机器人,且没有适用的仿真基准,请将仿真评估替换为:Hub 上的公开训练数据集、lerobot-train 命令、检查点,以及通过 lerobot-rollout --policy.path=... 在 ≥10 个回合中测得的真实机器人成功率。

PR 检查清单

一般要求见 CONTRIBUTING.md 和 PR 模板。除此之外,审查者还会关注:

  • MyPolicy 和 MyPolicyConfig 涵盖了上述内容;__init_subclass__ 接受该类。
  • policies/__init__.py 重新导出了配置(这会注册该策略;工厂会根据命名约定解析 modeling/processor)。
  • make_my_policy_pre_post_processors 遵循命名约定。
  • 可选依赖位于 [project.optional-dependencies] 额外项以及 TYPE_CHECKING + require_package 守卫之后。
  • tests/policies/ 已更新;已提交向后兼容产物及策略相关的测试。
  • src/lerobot/policies/<name>/README.md 通过符号链接指向 docs/source/policy_<name>_README.md;面向用户的 docs/source/<name>.mdx 已编写并添加到 _toctree.yml 中。
  • lerobot-train --policy.type my_policy ... 能够端到端运行至少几个步骤,并保存一个检查点,该检查点可以被 lerobot-eval 或 lerobot-rollout 加载并运行。
  • templates/lerobot_modelcard_template.md 包含一个描述条目以及指向您策略的 policy_docs 链接。
  • 根目录下的 README.md 中的模型表格会在正确的类别下列出您的策略,并链接到您的文档页面。
  • 策略 MDX 中至少包含一个可复现的基准评估,且需附带已发布的检查点(仿真基准,或真实机器人数据集 + 检查点)。

获取干净 PR 的最快方法是复制与你的策略最接近的现有策略目录,重命名,并逐个方法替换内容。不要等到一切都打磨完美——尽早开启一个草稿 PR 并与我们迭代;评审者更愿意在一个半成品分支上提供反馈,而不是在一个已完全合并的分支上。


示例和社区贡献

查看这些策略实现示例:

感谢您抽出时间将新策略引入 LeRobot。每一个合入 main 的架构——以及社区发布的每一个插件——都让该库对下一位使用者更加实用,也更能体现机器人学习的发展方向。我们期待看到您发布的内容。🤗

Update on GitHub