LeRobot documentation

Adicionando uma Política

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.7.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Adicionando uma Política

Este guia orienta você na implementação de uma política personalizada e na sua integração com as ferramentas de treinamento, avaliação e implantação do LeRobot. Há dois caminhos:

  • Plugin (fora da árvore) — distribua sua política como um pacote independente lerobot_policy_*. Mais rápido, sem necessidade de PR, fácil de iterar. Ideal para experimentação, uso interno ou quando você deseja publicar de forma independente.
  • Integrado (contribuído para o LeRobot) — inclua sua política diretamente em src/lerobot/policies/. Exige um PR, mas torna sua política um cidadão de primeira classe da biblioteca.

A rota do plugin geralmente é o ponto de partida certo — promova para o in-tree quando a política tiver se estabilizado e houver um valor claro em distribuí-la junto com a biblioteca.

De qualquer forma, os blocos de construção são os mesmos: uma classe de configuração, uma classe de política e uma fábrica de processadores. A primeira metade deste guia aborda essas partes compartilhadas; a segunda metade aborda a estrutura específica de cada caminho (Caminho A, Caminho B).

Uma observação sobre o tom: robot-learning é um campo em evolução ativa, e “como uma política se apresenta” pode mudar com cada nova arquitetura. As convenções descritas aqui existem porque permitem que lerobot-train e lerobot-eval funcionem de forma uniforme entre modelos muito diferentes. Quando uma nova política realmente não se encaixa nelas, reporte isso (em seu PR ou em uma issue) — as convenções não são sagradas.


Anatomia de uma política

Três blocos de construção compõem toda política. Os nomes abaixo usam my_policy como um marcador de posição — substitua pelo nome da sua política. Esse nome é fundamental: ele deve corresponder à string que você passa para @PreTrainedConfig.register_subclass, ao atributo de classe MyPolicy.name e à função de fábrica make_<name>_pre_post_processors (mais detalhes sobre cada um abaixo).

Classe de configuração

Herde de PreTrainedConfig e registre o tipo da sua política. Aqui está um modelo — personalize os parâmetros e métodos conforme necessário para a arquitetura e os requisitos de treinamento da sua política.

# 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

A string que você passa para @register_subclass deve corresponder a MyPolicy.name (próxima seção) e é o que os usuários fornecem como --policy.type na CLI. Use AdamW de lerobot.optim como padrão para get_optimizer_preset, a menos que você realmente precise de outra coisa. PreTrainedConfig já declara dtype: torch.dtype | None; para dar à sua política uma precisão padrão, redeclare-a com um valor de torch.dtype (por exemplo, dtype: torch.dtype | None = torch.bfloat16).

Classe de política

Herde de PreTrainedPolicy e defina dois atributos de classe — ambos são verificados por __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()}

Os métodos chamados pelos loops de treino/avaliação:

MétodoUsado porO que ele faz
reset() -> Nonelerobot-evalLimpe o estado por episódio no início de cada episódio.
select_action(batch, **kwargs) -> Tensorlerobot-evalRetorna a próxima ação (B, action_dim). Chamado a cada etapa.
predict_action_chunk(batch, **kwargs) -> Tensora própria políticaRetorne um chunk de ações (B, chunk_size, action_dim). Atualmente é abstrato na classe base — lance NotImplementedError se sua política não usar chunks.
forward(batch, reduction="mean") -> tuple[Tensor, dict \| None]lerobot-trainRetorna (loss, output_dict). Aceite reduction="none" se você quiser suportar ponderação por amostra.
get_optim_params() -> dicto otimizadorRetorna self.parameters() para políticas simples; retorna um dicionário de parâmetros nomeados para políticas com múltiplos otimizadores (veja get_optim_params em modeling_act.py para um exemplo de taxa de aprendizado por grupo).
update() -> None (opcional)lerobot-trainChamado após cada passo do otimizador se definido. Use para EMA, redes alvo e buffers de replay (o TDMPC usa isso).

Os lotes são dicionários planos indexados pelas constantes em lerobot.utils.constants: OBS_STATE (observation.state.<motor>), OBS_IMAGES (observation.images.<camera>), OBS_LANGUAGE, ACTION, etc. Reutilize as constantes — não invente novos prefixos.

Se o seu modelo for grande o suficiente para justificar treinamento multi-GPU com sharding, também declare as unidades de wrap do FSDP — as classes de bloco repetidas sobre as quais o sharding opera:

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

Com essa única declaração, --parallelism.dp_shard=N funciona imediatamente para sua política (os usuários ainda podem sobrescrevê-lo com --accelerator.fsdp.wrap_modules). Sem nenhuma fonte de wrap, execuções com sharding falham na inicialização por design.

Funções do processador

LeRobot usa PolicyProcessorPipelines para normalizar entradas e desnormalizar saídas em torno da sua policy. Para uma referência concreta, veja processor_act.py ou processor_diffusion.py.

Preste muita atenção aqui: os processadores são o ponto de dor mais comum em termos de reprodutibilidade. Uma incompatibilidade no modo de normalização (IDENTITY vs MEAN_STD vs MIN_MAX vs QUANTILES/QUANTILE10) ou em quais recursos são normalizados fará com que o treinamento e a avaliação ocorram sem erros, mas destruirão silenciosamente os resultados. Certifique-se de que os modos correspondam à forma como o checkpoint foi treinado, de que as estatísticas necessárias existam (por exemplo, QUANTILES requer q01/q99) e de que os pré e pós-processadores permaneçam consistentes.

# 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

Importante — nomenclatura da função: O LeRobot descobre seu processador pelo nome. A função deve ser chamada de make_{policy_name}_pre_post_processors (correspondendo à string que você passou para @PreTrainedConfig.register_subclass).

Opcional: ajustando processadores carregados de um checkpoint

make_{policy_name}_pre_post_processors é executado apenas quando os processadores são construídos do zero. Sempre que um checkpoint está envolvido (fine-tuning, retomada ou avaliação), o LeRobot carrega os pipelines serializados ao lado dos pesos e aplica as substituições do chamador.

Se sua política precisa ajustar aqueles pipelines carregados, adicione uma segunda função opcional ao mesmo módulo, nomeada make_{policy_name}_pre_post_processors_from_pretrained. O LeRobot a encontra pela mesma convenção de nomenclatura e a chama no lugar do load genérico. Políticas que não precisam de nada especial simplesmente a omitem.

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

Para exemplos funcionais, consulte processor_evo1.py (re-normaliza estatísticas de padding), processor_groot.py (processa checkpoints upstream brutos) e processor_molmoact2.py (renomeia chaves de substituição).

Retornar uma previsão além das ações (quadros imaginados, subtarefas, caixas)

Uma política pode retornar, além da ação que executa, aquilo que ela espera ou acredita, como os quadros imaginados por um modelo de mundo, um subtarefa prevista ou caixas delimitadoras. Assim, --display_data exibe isso ao vivo no Rerun ou no Foxglove. select_action e predict_action_chunk recebem um lote e retornam ou um tensor de ação puro (o padrão para toda política) ou um PolicyOutput (lerobot.lerobot_types): um lote, com chaves semelhantes às do lote de entrada, contendo a ação sob "action" e um PolicyPrediction sob "prediction". A previsão é loteada da mesma forma que a ação, com um valor por ambiente, e cada entrada é indexada pelo que ela se refere:

  • observation: a observação futura prevista, por chave de observação ("observation.images.top" -> [B, C, H, W] quadros). É exibida como prediction.images.top.
  • language: uma string por ambiente, por estilo de anotação ("subtask", "plan", "memory"), os estilos das colunas de idioma do pipeline de anotação. É exibido como prediction.subtask.
  • boxes: pelas chaves da imagem de observação em que são desenhadas, uma resposta de bbox por ambiente no esquema de VQA de anotação ({"detections": [{"label", "bbox": [x1, y1, x2, y2]}]}, frações da imagem de 0 a 1).

Os chamadores normalizam os dois tipos de retorno com policy_output_to_transition, que converte um PolicyOutput no EnvTransition usado pelos processadores, de modo que apenas as políticas que preveem algo mudam. Os visualizadores mostram o primeiro ambiente.

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()
  • Uma previsão contém os valores para esta etapa. Aquela que abrange um bloco, como um clipe imaginado, é retornada peça por peça: cada etapa retorna a parte correspondente à ação que ela atende.
  • Cada entrada permanece exibida até ser retornada novamente. Retorne detections vazio para limpar as caixas de uma câmera.
  • A previsão é ativada opcionalmente pela configuração da sua política e não deve ter nenhum custo quando desativada.
  • A previsão nunca altera as ações, e apenas a ação passa pelo pós-processador. O texto que deve orientar a política, como o próximo subtarefa, passa por generate_text (abaixo).

Políticas com capacidade de texto

As políticas de idioma compartilham a renderização de receitas entre duas etapas de processador:

  • RenderTrainingMessagesStep converte as anotações de idioma do conjunto de dados em mensagens renderizadas.
  • RenderRuntimeMessagesStep converte os prompts do usuário em mensagens renderizadas.

Processadores específicos de política aplicam, em seguida, o formatação de chat e a tokenização.

A configuração de política armazena a recipe: TrainingRecipe | None resolvida (de lerobot.datasets.recipe). Um recipe_path opcional pode carregar uma receita YAML; resolva-a antes de construir os processadores. Uma receita resolvida habilita o treinamento por receita, e None torna o renderizador de treinamento um no-op. Serialize a receita resolvida na configuração e no processador para que a implantação não dependa de um caminho YAML local. A renderização baseada em receita requer o extra dataset porque as receitas atualmente estão sob lerobot.datasets.

Coloque os dois renderizadores antes do formatamento de chat nativo da política e da tokenização:

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

O mesmo pipeline é salvo e carregado para treinamento e inferência. A renderização em tempo de execução ocorre apenas para um query_kind explícito. A renderização de treinamento ignora consultas em tempo de execução e, quando uma receita está configurada, processa amostras com anotações brutas ou alvos de ação. Entradas comuns apenas de observação passam direto. O treinamento com apenas texto anotado é suportado sem um tensor de ação. O treinamento sem anotações requer alvos de ação para habilitar o fallback da tarefa.

A fábrica compartilhada apenas constrói ou carrega processadores. O ajuste fino de receitas de linguagem os constrói a partir da configuração ativa e das estatísticas do conjunto de dados, de modo que um processador salvo não possa sobrescrever a nova receita. O retomar e a inferência carregam pipelines salvos, incluindo sua receita e estatísticas. O treinamento não linguístico mantém seu comportamento existente de construção e carregamento de checkpoints. Fábricas novas derivam as configurações de normalização e de ação relativa por conta própria. Fábricas novas usam o dispositivo configurado; o pré-processamento de lote do conjunto de dados existente aplica o mapa de renomeação do treinamento.

messages_rendered contém mensagens semânticas expandidas pela receita, antes do formato de chat nativo ou da tokenização. Os sidecars de treinamento indexam essa mesma lista. Os auxiliares de mensagem estão em lerobot.utils.language.

O rollout interativo adiciona query_kind e query_text. A renderização em tempo de execução suporta:

  • vqa: preserva a pergunta do chamador como uma mensagem de usuário.
  • next_subtask: render a receita do checkpoint até, mas não incluindo, a vez do assistente que supervisiona ${subtask}.

A política então expõe seu decodificador por meio da API de política existente:

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 consome a saída do processador pronta para o modelo; ele não deve reconstruir ou retokenizar o prompt do chamador. Durante o treinamento, o tokenizador nativo da política usa target_message_indices para supervisionar apenas os trechos de assistente selecionados. Armazene a receita na configuração da política de linguagem para que ela seja serializada com o checkpoint e a implantação reconstrua o prompt usado para o treinamento.


Caminho A: Plugin fora da árvore

A maneira mais rápida de disponibilizar uma política: empacote-a como uma distribuição Python independente e instale-a junto com o LeRobot. Não é necessário abrir um PR, você controla o ciclo de lançamento e pode publicar no PyPI sob seu próprio namespace.

Estrutura do pacote

Crie um pacote com o prefixo lerobot_policy_ (IMPORTANTE!) seguido pelo nome da sua política:

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

Pacote __init__.py

Exponha suas classes no __init__.py do pacote e proteja contra a ausência do 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",
]

Instalar e usar

cd lerobot_policy_my_policy
pip install -e .

# Or install from PyPI if published
pip install lerobot_policy_my_policy

Uma vez instalado, sua política se integra automaticamente às ferramentas de treinamento e avaliação do LeRobot:

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

Caminho B: Contribuindo no repositório

Quando sua política estiver estabilizada e houver um valor claro em incluí-la na biblioteca, você pode integrá-la diretamente ao LeRobot. Leia primeiro o guia de contribuição geral e o modelo de PR — é lá que você encontrará as expectativas de teste/qualidade que todo PR deve atender (pre-commit run -a, pytest, a regra de revisão pela comunidade, etc.). O que está abaixo é a camada específica de política por cima disso.

Layout na árvore

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

Duas observações:

  • O README.md ao lado do código-fonte é um symlink para docs/source/policy_<name>_README.md — o arquivo real fica em docs/. As políticas existentes (act, smolvla, diffusion, …) fazem isso; copie um desses symlinks. O README da política é convencionalmente minimalista: link para o paper + citação BibTeX.
  • O tutorial voltado ao usuário — o que instalar, como treinar, hiperparâmetros, números de benchmark — fica separado em docs/source/<my_policy>.mdx e é registrado em _toctree.yml sob “Policies”.

Os nomes dos arquivos são essenciais: a fábrica realiza imports preguiçosos por nome, e o processador é descoberto pela convenção make_<policy_name>_pre_post_processors.

Conexões

Dois lugares precisam conhecer sua política. Todos pelo nome.

  1. policies/__init__.py — reexporte MyPolicyConfig e adicione-o a __all__. É esse import que registra sua policy: @PreTrainedConfig.register_subclass("my_policy") é executado, e a partir de então a factory resolve tudo por convenção. Não reexporte a classe de modeling; ela é carregada de forma preguiçosa através da factory (para que import lerobot permaneça rápido).
  2. templates/lerobot_modelcard_template.md e o README.md raiz — o template é o que o publisher de fim de treinamento renderiza no model card de cada checkpoint treinado com sua policy: adicione uma descrição de uma linha da sua policy nos ramos model_name, mapeie-a em policy_docs para que os cards linkem para seu guia MDX e, opcionalmente, adicione uma imagem de arquitetura em diagrams. Em seguida, adicione sua policy à tabela de modelos no README.md raiz, sob a categoria correta, linkando para sua página de documentação.

Espelhe uma política existente que seja estruturalmente semelhante à sua; a diferença é pequena.

Dependências pesadas / opcionais

A maioria das políticas requer um backbone pesado (transformers, diffusers, um SDK específico de VLM). Sempre que existir, prefira carregá-lo, por exemplo, a partir de transformers ou diffusers, em vez de reimplementar a arquitetura no próprio repositório.

A convenção é gating em duas etapas: uma importação protegida por TYPE_CHECKING no topo do módulo e uma verificação em tempo de execução com require_package no construtor. modeling_diffusion.py é a referência canônica:

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)
        ...

Dessa forma:

  • import lerobot.policies continua funcionando sem a instalação extra (o símbolo é simplesmente vinculado a None).
  • Os verificadores de tipo veem o símbolo real.
  • Instanciar a política sem o extra gera um ImportError claro, apontando para pip install 'lerobot[diffusion]'.

Adicione um extra correspondente em pyproject.toml em [project.optional-dependencies] e inclua-o no extra all para que pip install 'lerobot[all]' continue instalando tudo.

Evite copiar um arquivo de modelagem — herde dele

Se sua política precisa modificar um backbone que já existe em transformers (condicionamento personalizado, entradas extras, um submódulo substituído), não faça um vendor de uma cópia do seu modeling_*.py. Em vez disso, crie uma subclasse da menor unidade upstream e sobrescreva apenas o que muda. pi_gemma.py é a referência canônica: ele injeta o condicionamento AdaRMS em PaliGemma/Gemma em ~370 linhas, criando uma subclasse de GemmaModel/PaliGemmaModel e sobrescrevendo o forward da camada decodificadora, em vez de fazer um fork do arquivo de modelagem de ~2.000 linhas. Cirurgia de modelo em um modelo nativo carregado também é aceitável (truncamento de camadas, expansão do tokenizer, captura de estados ocultos — veja evo1/internvl3_embedder.py, eo1/modeling_eo1.py, groot/groot_n1_7.py para exemplos funcionais). Os revisores pedirão esse padrão quando um PR chegar com um arquivo de modelagem copiado; a única exceção aceita é um modelo que não existe em transformers de forma alguma.

Benchmarks e um checkpoint publicado

Uma nova política é muito mais fácil de revisar — e muito mais útil — quando é lançada com um checkpoint funcional e pelo menos um número que você pode reproduzir.

Escolha pelo menos um benchmark da árvore principal. O LeRobot inclui benchmarks de simulação com imagens Docker por benchmark (LIBERO, LIBERO-plus, Meta-World, RoboTwin 2.0, RoboCasa365, RoboCerebra, RoboMME, VLABench e outros). Escolha o que corresponder à modalidade da sua política — VLAs geralmente usam LIBERO ou VLABench; BC apenas com imagem usa LIBERO ou Meta-World. A lista completa está em Benchmarks na barra lateral da documentação.

Envie o checkpoint e os processadores para o Hub em lerobot/<policy>_<benchmark> (ou no seu namespace, se você não tiver permissão de escrita; um mantenedor pode espelhá-lo). A maneira mais fácil é treinar com --policy.repo_id=<namespace>/<repo> e --policy.push_to_hub=true: o lerobot-train publica o modelo, os dois processadores e um cartão de modelo ao final da execução. Para publicar um checkpoint existente posteriormente, faça o upload do diretório pretrained_model/ (por exemplo, com hf upload), ou use lerobot-convert-dcp --push_to_hub=... para checkpoints em formato segmentado.

Relate os resultados no MDX da sua política, com o comando exato lerobot-eval e o hardware para que qualquer pessoa possa reexecutar:

## 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).

Use n_episodes ≥ 50 por suite para estimativas estáveis de taxa de sucesso.

Se sua política é apenas para robôs reais e nenhum benchmark de simulação se aplica, substitua a avaliação de simulação por: um conjunto de dados de treinamento público no Hub, o comando lerobot-train, o checkpoint e uma taxa de sucesso em robôs reais em ≥10 episódios via lerobot-rollout --policy.path=....

Checklist de PR

As expectativas gerais estão em CONTRIBUTING.md e no modelo de PR. Além disso, os revisores procurarão:

  • MyPolicy e MyPolicyConfig cobrem a superfície acima; __init_subclass__ aceita a classe.
  • policies/__init__.py reexporta a configuração (isso registra a policy; a factory resolve modeling/processor por convenção de nomenclatura).
  • make_my_policy_pre_post_processors segue a convenção de nomenclatura.
  • As dependências opcionais ficam atrás de um extra [project.optional-dependencies] e da guarda TYPE_CHECKING + require_package.
  • tests/policies/ atualizado; artefato de compatibilidade reversa commitado & testes específicos de política.
  • src/lerobot/policies/<name>/README.md vinculado por symlink em docs/source/policy_<name>_README.md; docs/source/<name>.mdx voltado para o usuário escrito e adicionado a _toctree.yml.
  • lerobot-train --policy.type my_policy ... executa de ponta a ponta por pelo menos alguns passos + salva um checkpoint que pode ser carregado e executado por lerobot-eval ou lerobot-rollout.
  • templates/lerobot_modelcard_template.md tem uma entrada de descrição e um link policy_docs para sua política.
  • A tabela de modelos no README.md raiz lista sua política na categoria correta, com um link para a página do seu documento.
  • Pelo menos um benchmark reprodutível de avaliação na MDX da política com um checkpoint publicado (benchmark de simulação, ou conjunto de dados de robô real + checkpoint).

A maneira mais rápida de obter um PR limpo é copiar o diretório da política existente mais próxima da sua, renomear e substituir o conteúdo método por método. Não espere até que tudo esteja polido — abra um PR de rascunho cedo e itere conosco; os revisores preferem muito mais dar feedback em um ramo parcialmente concluído do que em um totalmente mesclado.


Exemplos e contribuições da comunidade

Confira estas implementações de exemplo de políticas:

Obrigado por dedicar tempo para trazer uma nova política para o LeRobot. Cada arquitetura que chega ao main — e cada plugin publicado pela comunidade — torna a biblioteca um pouco mais útil para a próxima pessoa e um pouco mais representativa de para onde a aprendizagem de robôs está se direcionando. Estamos ansiosos para ver o que você vai disponibilizar. 🤗

Update on GitHub