LeRobot documentation

Implantação de Política (lerobot-rollout)

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Implantação de Política (lerobot-rollout)

lerobot-rollout é a única CLI para implantar políticas treinadas em robôs reais. Ele suporta múltiplas estratégias de execução e backends de inferência, desde avaliação rápida até gravação contínua e coleta de dados com intervenção humana.

Início rápido

Nenhuma dependência extra é necessária além dos extras do seu robô e da sua política.

lerobot-rollout \
    --strategy.type=base \
    --policy.path=lerobot/act_koch_real \
    --robot.type=koch_follower \
    --robot.port=/dev/ttyACM0 \
    --task="pick up cube" \
    --duration=30

Isso executa a política por 30 segundos sem gravação.

Escolhendo um dispositivo

Pass --device=cpu ou --device=cuda para sobrescrever o dispositivo do checkpoint em um host onde esse dispositivo está disponível. O dispositivo resolvido é aplicado à configuração da política antes de carregar a política e construir seus processadores. Por exemplo, adicione --device=cpu ao comando acima para executar um checkpoint treinado em CUDA em um host apenas com CPU, se a política suportar execução em CPU. Verifique se a inferência em CPU pode atender à taxa de controle necessária antes de implantar em um robô.

Quando --device é omitido, o rollout usa o dispositivo da configuração da política, se definido; caso contrário, ele seleciona automaticamente um dispositivo disponível. O mesmo comportamento se aplica a RolloutConfig(device=...) com build_rollout_context.


Estratégias

Selecione uma estratégia com --strategy.type=<name>. Cada estratégia define um loop de controle diferente, com suas próprias semânticas de gravação e interação.

Base ( --strategy.type=base )

Execução autônoma de política sem gravação de dados. Use isso para avaliações rápidas, demonstrações ou quando você precisa apenas observar o robô.

lerobot-rollout \
    --strategy.type=base \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
    --task="Put lego brick into the box" \
    --duration=60
FlagDescriçãoPadrão
--durationTempo de execução em segundos (0 = infinito)0
--taskDescrição da tarefa passada para a política—
--display_dataTransmita observações/ações para o Rerun para visualizaçãofalse

Sentry ( --strategy.type=sentry )

Gravação autônoma contínua com upload periódico para o Hugging Face Hub. As fronteiras dos episódios são calculadas automaticamente a partir da resolução da câmera e do FPS, de modo que cada episódio salvo gere um arquivo de vídeo completo, mantendo os uploads eficientes.

O estado da política (estado oculto, fila RTC) persiste entre as fronteiras dos episódios: o robô não é reiniciado entre os episódios.

lerobot-rollout \
    --strategy.type=sentry \
    --strategy.upload_every_n_episodes=5 \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
    --dataset.repo_id=${HF_USER}/rollout_eval_data \
    --dataset.single_task="Put lego brick into the box" \
    --duration=3600
FlagDescriçãoPadrão
--strategy.upload_every_n_episodesFazer push para o Hub a cada N episódios5
--strategy.target_video_file_size_mbTamanho do arquivo de vídeo alvo para rotação de episódiosauto
--dataset.repo_idObrigatório. Repositório do Hub para o conjunto de dados gravado—
--dataset.push_to_hubSe deve enviar para o Hub ao desmontartrue

Destaque ( --strategy.type=highlight )

Rollout autônomo com gravação sob demanda por meio de um buffer circular limitado por memória. O buffer captura os últimos N segundos de telemetria. Pressione a tecla de salvar para esvaziar o buffer e iniciar a gravação ao vivo; pressione-a novamente para salvar o episódio. Com um backend assíncrono, cada alternância pausa a inferência, invalida o movimento enfileirado/interpolado e solicita um hold local suportado antes do esvaziamento bloqueante ou da gravação. Uma execução saudável retoma a partir de uma nova captura em seguida.

lerobot-rollout \
    --strategy.type=highlight \
    --strategy.ring_buffer_seconds=30 \
    --strategy.save_key=s \
    --strategy.push_key=h \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=koch_follower \
    --robot.port=/dev/ttyACM0 \
    --dataset.repo_id=${HF_USER}/rollout_highlight_data \
    --dataset.single_task="Pick up the red cube"

Controles de teclado:

ChaveAção
s (configurável)Iniciar gravação (limpa o buffer) / parar e salvar episódio
h (configurável)Enviar dataset para o Hub
ESCEncerrar a sessão
FlagDescrição
--strategy.ring_buffer_secondsDuração da telemetria em buffer (padrão: 10)
--strategy.ring_buffer_max_memory_mbLimite de memória para o buffer circular (padrão: 1024)
--strategy.save_keyTecla para alternar a gravação (padrão: s)
--strategy.push_keyTecla para enviar ao Hub (padrão: h)

DAgger ( --strategy.type=dagger )

Coleta de dados com intervenção humana. Alterna entre a execução autônoma da política e a intervenção humana por meio de um teleoperador. Os quadros de intervenção são marcados com intervention=True. Exige um teleoperador (--teleop.type).

Consulte o guia Coleta de Dados com Participação Humana para um passo a passo detalhado.

Modo somente correções (padrão): Somente janelas de correção humana são registradas. Cada correção se torna um episódio.

lerobot-rollout \
    --strategy.type=dagger \
    --strategy.num_episodes=20 \
    --policy.path=outputs/pretrain/checkpoints/last/pretrained_model \
    --robot.type=bi_openarm_follower \
    --teleop.type=bi_openarm_mini \
    --dataset.repo_id=${HF_USER}/rollout_hil_data \
    --dataset.single_task="Fold the T-shirt"

Modo de gravação contínua (--strategy.record_autonomous=true): Tanto os frames autônomos quanto os de correção são gravados com rotação de episódios baseada em tempo (igual ao Sentry).

lerobot-rollout \
    --strategy.type=dagger \
    --strategy.record_autonomous=true \
    --strategy.num_episodes=50 \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --teleop.type=so101_leader \
    --teleop.port=/dev/ttyACM1 \
    --dataset.repo_id=${HF_USER}/rollout_dagger_data \
    --dataset.single_task="Grasp the block"

Controles de teclado (dispositivo de entrada padrão):

ChaveAção
SpacePausar / retomar a execução da política
TabIniciar / parar a correção humana
EnterEnviar dataset para o Hub (modo apenas correções)
ESCEncerrar a sessão

A entrada por pedal de pé também é suportada via --strategy.input_device=pedal. Configure os códigos do pedal com as flags --strategy.pedal.*.

Quando o robô suporta a retenção de posição durante o rollout, uma pausa atualiza o último alvo de posição efetivamente aplicado pelo driver, incluindo seu recorte e o alvo do garra. Antes do primeiro comando, uma pose medida e validada fornece o alvo. Isso preserva o erro de posição comandado em vez de alterar o alvo para a pose medida; o robô pode concluir a estabilização em direção a esse alvo enquanto o avanço da trajetória da política é interrompido. Valide o comportamento do braço carregado e da garra no robô real; essa retenção de software não garante a manutenção do aperto ou a imobilidade física.

FlagDescrição
--strategy.num_episodesNúmero de episódios de correção a serem registrados (padrão: --dataset.num_episodes)
--strategy.record_autonomousRegistrar também os frames autônomos (padrão: false)
--strategy.upload_every_n_episodesFazer push para o Hub a cada N episódios (padrão: 5)
--strategy.input_deviceDispositivo de entrada: keyboard ou pedal (padrão: keyboard)
--strategy.smooth_handoverTransfira o controle suavemente no início da pausa / correção (padrão: true). Desative para teleops estilo clutch que re-referenciam na pose atual do robô ao engatar
--teleop.typeObrigatório. Tipo de teleoperador

Episódico ( --strategy.type=episodic )

Gravação orientada por episódios que espelha o comportamento de lerobot-record. A política dirige o robô em cada episódio; um teleoperador opcional pode dirigir o robô durante a fase de reset entre os episódios.

lerobot-rollout \
    --strategy.type=episodic \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --teleop.type=so100_leader \
    --teleop.port=/dev/ttyACM1 \
    --dataset.repo_id=${HF_USER}/rollout_eval_data \
    --dataset.num_episodes=20 \
    --dataset.episode_time_s=30 \
    --dataset.reset_time_s=10 \
    --dataset.single_task="Pick up the red cube"

Teleop é opcional — se omitido, o robô mantém sua posição durante a fase de reset.

Controles de teclado:

ChaveAção
→ (direita)Encerrar o episódio atual antecipadamente
← (esquerda)Descartar o episódio e gravá-lo novamente
ESCPare a sessão de gravação
FlagDescrição
--dataset.num_episodesNúmero de episódios a gravar
--dataset.episode_time_sDuração de cada episódio de gravação em segundos. O padrão é --duration quando esse é definido e este não é
--dataset.reset_time_sDuração da fase de reinício entre episódios, em segundos
--teleop.typeOpcional. Teleoperador para controlar o robô durante os resets
--strategy.reset_to_initial_positionSe deve redefinir o robô para sua posição inicial entre episódios
--strategy.smooth_leader_to_follower_handoverSe o comportamento de transição suave de líder para seguidor deve ser ativado ou desativado.
--strategy.smooth_handoverTransfira o controle suavemente para o teleop no início do reset (padrão: true). Desative para teleops do tipo clutch que re-referenciam na pose atual do robô ao engatar

Traga sua própria estratégia

--strategy.type aceita qualquer estratégia registrada no ambiente, não apenas as embutidas acima. Uma estratégia é composta por duas classes em um pacote instalável via pip: uma configuração registrada em RolloutStrategyConfig e uma subclasse de RolloutStrategy nomeada com o mesmo nome, sem o sufixo Config — a mesma convenção SomethingConfig/Something de hardware personalizado, resolvida a partir do módulo ou pacote da configuração.

from dataclasses import dataclass
from typing import ClassVar

from lerobot.rollout import RolloutContext, RolloutStrategy, RolloutStrategyConfig


@RolloutStrategyConfig.register_subclass("patrol")
@dataclass
class PatrolStrategyConfig(RolloutStrategyConfig):
    dataset_mode: ClassVar[str] = "required"  # the engine creates the dataset for you
    legs: int = 2  # becomes --strategy.legs


class PatrolStrategy(RolloutStrategy):
    def run(self, ctx: RolloutContext) -> None:
        ...  # the paced control loop: see the contract on RolloutStrategy.run

    def teardown(self, ctx: RolloutContext) -> None:
        ctx.data.dataset.finalize()
        self._teardown_hardware(ctx.hardware, ctx.runtime.cfg.return_to_initial_position)

A config declara o que o engine organiza em nome da estratégia, portanto nada no LeRobot precisa conhecer seu tipo:

DeclaraçãoEfeito
dataset_mode"none" (padrão) rejeita as flags --dataset.*, "optional" as registra quando fornecidas, "required" torna --dataset.repo_id obrigatório
requires_teleopTorna --teleop.type obrigatório
supports_interactivePermite --interactive=true; run() deve então respeitar o contrato de reiniciabilidade documentado em RolloutStrategy
supports_sim_robotPermite --robot.type=sim; run() deve, então, iniciar um novo episódio sempre que um terminar (veja lerobot.robots.sim_robot)
requires_streaming_encoding()Retorna True quando os quadros são gravados a partir do interior do loop de controle: --dataset.streaming_encoding é forçado
extra_dataset_features()Colunas de dataset pertencentes à estratégia (a intervention do DAgger, por exemplo); cada quadro registrado deve contê-las

Nomeie a distribuição como lerobot_strategy_<name> e o lerobot-rollout importa o pacote antes de analisar a CLI — apenas o __init__.py é executado, portanto importe a configuração ali. Um pacote com qualquer outro nome é carregado, incluindo submódulos, com --strategy.discover_packages_path=<package>. Os campos do dataclass da estratégia se tornam as flags --strategy.*:

lerobot-rollout \
    --strategy.type=patrol \
    --strategy.legs=3 \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --dataset.repo_id=${HF_USER}/rollout_patrol \
    --dataset.single_task="patrol the aisle"

SentryStrategy (gravação, seguro para interação) e BaseStrategy (sem gravação) são as implementações de referência para copiar; send_next_action, safe_push_to_hub e estimate_max_episode_seconds de lerobot.rollout.strategies são os auxiliares nos quais elas se baseiam.

Para backends assíncronos, o contrato de despacho é obrigatório. Publique cada observação capturada com engine.notify_observation(obs_processed), chame send_next_action(...) em cada tick autônomo do motor, incluindo ticks de interpolação e retenção, e então chame engine.pump_query(obs_processed). Chamar apenas engine.get_action() ignora o aperto de mãos de permissão e retenção: uma consulta de texto planejada pode esperar indefinidamente, e uma falha terminal pode nunca sinalizar o encerramento.

Estratégias de terceiros podem chamar self._tick(timer, new_cycle=interpolator.needs_new_action()) no lugar de timer.tick(...) para incluir relatórios periódicos de saúde após _init_engine(ctx).

send_next_action é construído sobre lerobot.inference.dispatch_tick e suas partes (next_interpolated_action, commit_action, dispatch_permitted), que codificam essa ordem uma única vez: dispatch_allowed() antes e depois de obter uma ação e novamente antes de enviá-la, invalidação da interpolação mais um hold local suportado e acknowledge_hold() quando negado, e record_dispatch() após o envio. Um hold que falha nunca é reconhecido. Use esses helpers em vez de reimplementar a sequência; send_next_action apenas adiciona o frame do dataset, o processador de ação do lado do robô e a contabilidade de cadência. Contextos remotos têm ctx.policy.policy, preprocessor e postprocessor definidos como None; use ctx.policy.inference e os pipelines do lado do robô em ctx.processors.

Envolva o trabalho de gravação conhecido por bloquear durante a execução em self._pause_for_recording(ctx), incluindo drenagens de buffer circular e finalização de episódios. Isso invalida o movimento assíncrono antes da parada da gravação e retoma apenas se a execução ainda estiver saudável e ativa. Estratégias com fases controladas pelo operador devem passar sua permissão de movimento atual como resume_allowed. A próxima ação deve vir de uma observação nova; o trabalho salvo não deve reativar uma execução pausada, parada ou com falha.


Visualizando as previsões da política

Com --display_data, o visualizador também exibe o que a política prevê além das ações: quadros de câmera imaginados por um modelo de mundo, uma subtarefa prevista e caixas delimitadoras desenhadas sobre uma câmera. Cada política decide o que prevê e ativa isso por meio de sua própria configuração, por exemplo --policy.return_predicted_video=true para LingBot-VA. Uma previsão é nomeada de acordo com o que ela prevê: o Rerun exibe uma observation.images.top prevista como prediction.images.top, uma subtarefa prevista como prediction.subtask e caixas sob sua câmera (<camera>/prediction); o Foxglove as publica nos tópicos /prediction/... e nas anotações de imagem <camera topic>/prediction. Com RTC, a previsão é atualizada uma vez por chunk. Consulte Traga sua própria política para retornar uma previsão de uma política.

Backends de Inferência

Selecione um backend com --inference.type=<name>. A inferência local síncrona e a inferência RTC usam a política configurada abaixo. O backend remoto obtém seu contrato de política de um servidor e não carrega pesos no computador do robô.

Sincronização (padrão)

Uma chamada de política por tick de controle. O loop principal bloqueia até que a ação seja calculada.

Funciona com todas as políticas. Nenhuma flag extra é necessária.

Chunking em Tempo Real ( --inference.type=rtc )

Uma thread de fundo produz blocos de ações de forma assíncrona. O loop de controle principal consulta a próxima ação pronta enquanto a política calcula o próximo bloco em paralelo.

Use o RTC com modelos VLA compatíveis (Pi0, Pi0.5, SmolVLA) para sobrepor inferência e movimento. A execução contínua ainda requer reprodução de ações utilizável suficiente para cobrir a latência de inferência e sua variação.

lerobot-rollout \
    --strategy.type=base \
    --inference.type=rtc \
    --inference.rtc.execution_horizon=10 \
    --inference.rtc.max_guidance_weight=10.0 \
    --policy.path=${HF_USER}/pi0_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
    --task="Pick up the cube" \
    --duration=60 \
    --device=cuda
FlagDescrição
--inference.rtc.execution_horizonEtapas para mesclar com o chunk anterior (padrão: varia conforme a política)
--inference.rtc.modeguided (padrão) ou trained-prefix trained para checkpoints Pi05 compatíveis
--inference.rtc.max_guidance_weightForça de aplicação da consistência (padrão: varia conforme a política)
--inference.rtc.prefix_attention_scheduleAgendamento de mistura: LINEAR, EXP, ONES, ZEROS
--inference.queue_thresholdLimiar de backpressure; o RTC treinado requer pelo menos seu atraso máximo
--inference.action_starvation_grace_sAguarde por movimento novo após a inanição em um robô suportado (padrão 1.0; 0 falha imediatamente)

Consulte o guia Real-Time Chunking para obter detalhes sobre o ajuste dos parâmetros do RTC.

O RTC local é executado em uma thread em segundo plano no processo de rollout, sem transporte. Um servidor e um cliente no mesmo computador usam --inference.type=remote em vez disso. Consulte o guia remoto para alinhamento/mistura de blocos e o comportamento de espera compartilhado pelos backends assíncronos.

A espera requer um robô compatível. O RTC local comum continua disponível sem essa capacidade, mas as pausas planejadas para alteração de texto/instrução e a inanição recuperável a exigem. Os seguidores de controle de posição SO, OMX, Koch, HopeJr e Rebot a suportam; OpenArm, Reachy2 e os wrappers bimanuais possuem restrições de configuração listadas na tabela de suporte de robôs. Com suporte, um buffer vazio pode reter o último alvo aplicado por action_starvation_grace_s (padrão 1 segundo), incluindo o tempo necessário para drenar inferências antigas e prever a partir de uma captura nova. Após o movimento ter iniciado, uma tolerância zero ou um robô não suportado falha imediatamente na exaustão; a inicialização possui seu próprio prazo. Diferente do antigo caminho de RTC local, a exaustão não é mais um tick indefinidamente ignorado. A tolerância nunca estende limites de expiração de solicitação ou de frescor.

O RTC local expõe esses orçamentos adicionais por meio de --inference.:

ParâmetroPadrãoSignificado
max_observation_age_s5 sIdade máxima da fonte por meio da execução da ação
action_timeout_s10 sSolicitação de ação pendente
startup_timeout_s120 sPreparação inicial e inicialização de ação imediata
language_timeout_s120 sOperação de idioma planejada

Estes são valores iniciais, não garantias de hardware medidas. O RTC deve reter tempo suficiente de reprodução pós-trim para outra inferência e margem de tempo. Aumentar queue_threshold não ajuda se a inferência consome rotineiramente o horizonte de previsão utilizável; reduza a latência ou valide um horizonte mais longo/frequência de ação diferente.

Uma falha terminal nunca retoma automaticamente o movimento da política. Com aquisição de observação utilizável e controle dos atuadores, o desligamento respeita --return_to_initial_position (padrão true) e desconecta. O retorno lê uma observação completa, portanto uma falha persistente na câmera pode impedi-lo. A falha na aplicação de comandos suprime o movimento adicional de desligamento para todos os backends, incluindo sync. Isso não altera a predição síncrona nem introduz recuperação específica de câmera.

Compilation and instruction changes

A compilação RTC aquece tanto as chamadas iniciais quanto as chamadas com prefixo, incluindo um atraso não nulo representativo, antes do movimento. Embora --compile_warmup_inferences tenha valor padrão de 2, o RTC habilitado requer pelo menos 3; os logs de inicialização relatam a contagem efetiva. O aquecimento deve caber em startup_timeout_s. Instruções posteriores que alteram as formas dos tensores podem acionar recompilação e exceder o prazo de reprodução ou de uma operação.

Uma mudança de tarefa durante a inferência descarta o resultado obsoleto. Robôs compatíveis pausam e solicitam novas ações; sem essa capacidade de espera, as ações restantes podem acabar. Prefira mudar as tarefas entre execuções nesses robôs.


Sessões Interativas

Adicione --interactive=true para controlar o rollout a partir do terminal em vez de iniciar imediatamente. O hardware se conecta e a política é carregada como de costume, mas o robô permanece parado até você digitar /start — útil quando você deseja posicionar a cena primeiro, reorientar a política entre tentativas ou executar várias gravações sem pagar o tempo de carregamento novamente.

lerobot-rollout \
    --strategy.type=base \
    --policy.path=${HF_USER}/my_smolvla_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
    --task="pick up the cube" \
    --interactive=true
ComandoAção
/startIniciar (ou reiniciar) o loop de controle de política
/subtask <text>Altere a instrução dentro da execução atual; a inferência assíncrona pode entrar em uma espera planejada. Sem argumento, imprime a tarefa atual. Afeta apenas políticas que condicionam em linguagem (SmolVLA, π0/π0.5 e similares). Com um planejador externo, /subtask também devolve o controle do autosteer
/vqa <text>Faça uma pergunta sobre o que o robô está vendo atualmente. Somente enquanto uma execução estiver em andamento e somente com uma cabeça de texto de política ou um planejador externo
/autosteer <goal>Entregue um objetivo de alto nível e deixe que o cabeçalho de texto da política ou o planejador externo escolham as subtarefas. /autosteer off devolve o controle; sem argumento, relata o objetivo atual
/resetPare o movimento, retorne o robô à posição de inicialização e restaure a instrução --task
/stopEncerre a sessão e execute as rotinas normais de desligamento
/helpListe os comandos
> /start
Rollout running — task 'pick up the cube'. /subtask <text> to change it, ...
> /subtask put the cube in the box
Task: 'pick up the cube' → 'put the cube in the box' (applies from the next policy inference)
> /vqa is the cube inside the box?
Asked: 'is the cube inside the box?' — answering from the next observation...
Q: is the cube inside the box?
A: No — the cube is on the table to the left of the box.
> /reset
Task restored to 'pick up the cube'
Resetting — returning the robot to its initial position...
Robot reset — holding at initial position. /start to run.
> /stop

Linhas que começam com > são o que você digita; a sessão não exibe nenhum prompt próprio.

Ctrl-C ainda encerra o processo como de costume, e fechar o stdin (Ctrl-D, ou o fim de um script via pipe) encerra a sessão — portanto, um script via pipe deve manter o stdin aberto pela duração pretendida:

(printf '/start\n'; sleep 60; printf '/stop\n') | lerobot-rollout ... --interactive=true

/subtask atualiza a tarefa para inferências subsequentes. Com --inference.type=sync, as ações de chunks pré-computadas são descartadas. Com RTC local ou inferência remota, uma alteração durante uma solicitação de ação descarta o resultado obsoleto. Em um robô que suporta retenção local, o mecanismo invalida o movimento enfileirado/interpolado, aguarda o loop de controle confirmar a retenção e, em seguida, solicita um novo movimento sob a nova tarefa. Espere uma parada durante essa transição; não é uma mistura contínua garantida. Uma alteração entre solicitações pode reter ações já aceitas para manter a continuidade. O RTC local em um robô sem suporte a retenção ainda descarta o resultado obsoleto, mas pode esgotar suas ações restantes e terminar. Altere as instruções entre execuções se esse robô não puder reter adequadamente.

Com o local --use_torch_compile=true, uma instrução que altera as formas dos tensores processados pode acionar a recompilação. O preenchimento (padding) pode manter algumas mudanças de tarefa na mesma forma, mas não há garantia de que a compilação posterior se encaixe no restante da reprodução ou no prazo de mudança de instrução: isso pode causar uma falha terminal. Deixe a compilação desativada ao explorar instruções, a menos que essas transições tenham sido validadas. Mudanças durante o aquecimento da compilação inicial não solicitam uma retenção de movimento, porque o movimento da política ainda não começou.

/vqa e /autosteer exigem uma política que declare suporte à geração de texto e implemente PreTrainedPolicy.generate_text(), como um checkpoint EO1 adequadamente treinado, ou um planejador externo. A capacidade é verificada antes de aceitar uma consulta; o comportamento específico do checkpoint ainda precisa de validação.

/vqa responde a partir da próxima observação e imprime quando estiver pronto, permitindo que você continue digitando no meio tempo; apenas uma pergunta está em andamento por vez (as próprias consultas do autosteer compartilham esse slot). Com um planejador externo, a pergunta é respondida em uma thread de worker enquanto o robô continua agindo, e nada abaixo se aplica. Backends assíncronos primeiro entram em uma retenção local reconhecida e invalidam o movimento enfileirado/interpolado, em seguida submetem uma nova observação ao caminho de texto. Uma execução ativa e saudável retoma de um novo chunk de ação após a resposta; execuções pausadas ou paradas permanecem inativas. A geração de texto síncrona é executada inline na thread de controle e descarta as ações pré-computadas da política, de modo que o próximo tick retoma de uma inferência nova. O tempo de consulta conta contra --duration. As perguntas são aceitas apenas enquanto uma execução está em andamento — um engine ocioso não possui uma visão atual. Se a instrução ou o contexto de execução invalidar uma resposta assíncrona, a pergunta recebe um aviso de cancelamento em vez da resposta obsoleta. O fim do segmento também relata perguntas sem resposta, incluindo geração ainda em andamento; relatar cancelamento não interrompe uma chamada de modelo em execução nem libera seu slot de consulta antecipadamente.

/autosteer passa um objetivo à política e solicita o próximo subtask a cada --autosteer_interval_s segundos (padrão 10), aplicando cada um por meio do mesmo caminho de /subtask e anunciando-o no chat. Cada consulta é uma geração de texto completa, portanto o intervalo é um controle de custo: valores menores replanejam mais cedo, mas gastam mais do loop gerando texto em vez de agir. O progresso do plano fica na política, não no rollout, então o plano morre com o segmento; o sequenciador também para em /reset, /subtask, /autosteer off e na primeira falha de planejamento.

Logs abaixo de ERROR são silenciados enquanto a sessão está em execução para que a saída de rotina não se intercale com o que você está digitando; erros e falhas fatais de inferência ainda são exibidos, e o log normal retoma quando a sessão termina. Os resumos de cadência são impressos no chat, e executar sem --interactive retorna o log ao vivo.

Sessões funcionam via SSH e em máquinas sem interface gráfica — nenhum servidor de display é necessário.

Gravação durante a interação. --strategy.type=sentry também aceita --interactive=true e grava continuamente enquanto você controla: cada segmento /start…/reset salva episódios completos mais um episódio parcial final, e o conjunto de dados permanece aberto até a desligamento. Os quadros carregam a procedência da tarefa da ação realmente enviada, de modo que ações já enfileiradas ou interpoladas sob a tarefa antiga mantêm o rótulo antigo.

Salvamentos de episódios no meio da execução bloqueiam a pausa da inferência assíncrona, invalidam ações enfileiradas/interpoladas e solicitam uma retenção local suportada. Eles retomam a partir de uma nova captura apenas se a execução permanecer ativa e saudável. A mesma proteção se aplica a salvamentos do DAgger e a drenagens/salvamentos do buffer circular de Highlight. Pausas de gravação e inanição comum compartilham o comportamento de espera física, mas seus prazos e motivos para retomada permanecem separados.

lerobot-rollout \
    --strategy.type=sentry \
    --policy.path=${HF_USER}/my_smolvla_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --dataset.repo_id=${HF_USER}/rollout_cube_sessions \
    --task="pick up the cube" \
    --interactive=true

As outras estratégias de gravação (episodic, DAgger, highlight) não são suportadas: elas vinculam seus próprios controles de teclado e finalizam o dataset quando run() termina, portanto, não podem ser reiniciadas segmento por segmento.

Planejador externo

--planner.model_id=<name> roteia /vqa e /autosteer pelo cliente VLM do pipeline de anotação (extra annotations) em vez da cabeça de texto da política. As chamadas são executadas em uma thread de worker enquanto o robô continua agindo, e nenhum hold de linguagem é solicitado para elas, com uma exceção deliberada: quando /autosteer inicia, o backend de inferência é pausado até a primeira resposta do planner, porque o objetivo é contexto para o planner, não uma instrução para a política. Backends assíncronos (rtc, remote) descartam ações já enfileiradas ou em cálculo para a instrução anterior; o backend síncrono segura o robô. Assim que o primeiro subtask é aplicado, o backend retoma a partir de uma nova observação. /subtask ainda aplica sua instrução diretamente e cancela o planejamento automático; respostas tardias do planner não podem sobrescrevê-la.

O planner funciona com todos os backends de inferência. Com inferência local, ele reutiliza a receita de subtarefas do processador carregado, quando houver, e caso contrário solicita uma instrução curta; isso não estabelece que um checkpoint pode segui-la. --planner.instructions='["pick up the cup","open the drawer"]' restringe a saída do planner: o VLM é solicitado a escolher uma dessas strings, e uma resposta de subtarefa fora da lista é um erro. Maiúsculas/minúsculas, espaços extras e um ponto final são ignorados, e a própria redação da lista é enviada para a policy. Sem essa flag, a lista é lida dos rótulos de tarefa do conjunto de dados de treinamento do checkpoint (via seu train_config.json). Cada rótulo vai para o prompt, então passe a flag com uma lista mais curta para conjuntos de dados com muitas tarefas. Se o conjunto de dados não puder ser lido, um aviso é registrado e a saída do planner permanece em formato livre. A inferência remota possui a policy, portanto nem a receita nem o vocabulário de treinamento estão disponíveis localmente: passe --planner.instructions explicitamente ou as respostas permanecerão em formato livre. O planner foi testado apenas em hardware com inferência local.

O planejador externo recebe os últimos pares de observação/comando de --planner.history (padrão 4), seguidos pelas imagens atuais. Cada par contém a observação usada para solicitar uma instrução e a instrução posteriormente aceita pelo mecanismo; a aceitação não implica execução bem-sucedida. Instruções repetidas permanecem no histórico para que o VLM possa avaliar a falta de progresso. O histórico é limpo em caso de intervenção manual, reset, parada, fim de segmento ou um novo objetivo de autosteer. Defina --planner.history=0 para desativá-lo. /vqa pode inspecionar esse histórico, mas não adiciona entradas. Para uma solicitação de subtarefa, o VLM deve primeiro descrever a cena e declarar se o comando anterior foi concluído, está em andamento ou falhou, e somente então nomear a próxima instrução. Uma resposta de done significa que o objetivo está completo: o autosteer para, a execução termina e o robô mantém sua pose até /reset ou /start. Um done tardio de antes de um /subtask, /reset ou novo objetivo é descartado. Cada subtarefa aplicada é impressa com a avaliação da cena pelo modelo e a latência da solicitação.

Inicie um servidor de VLM compatível com a OpenAI separadamente e, em seguida, conecte-o:

transformers serve Qwen/Qwen3-VL-8B-Instruct --port 8000
# Alternatively: vllm serve Qwen/Qwen3-VL-8B-Instruct --port 8000
lerobot-rollout ... --task="pick up the cup" --interactive=true \
    --planner.model_id=Qwen/Qwen3-VL-8B-Instruct \
    --planner.api_base=http://localhost:8000/v1
> /start
> /vqa What is the next short instruction to clear the table?
> /subtask put the cup in the sink
> /autosteer clear the table

Um modelo hospedado funciona da mesma maneira. Este exemplo usa os Hugging Face Inference Providers, com um token que tem a permissão “Make calls to Inference Providers”:

export HF_TOKEN=hf_...
lerobot-rollout ... --task="put all colored cubes in the green bin" --interactive=true \
    --planner.model_id=Qwen/Qwen3.8-27B:novita \
    --planner.api_base=https://router.huggingface.co/v1 \
    --planner.api_key_env=HF_TOKEN

/vqa apenas relata uma resposta; /autosteer aplica instruções. Configure o endpoint com --planner.api_base e a chave com --planner.api_key_env=<variable> (preferível, a chave fica fora dos comandos e logs) ou --planner.api_key. --planner.request_timeout_s (padrão 600) e --planner.request_max_retries (padrão 2) limitam cada solicitação; --planner.camera_key seleciona uma câmera; --planner.log_path anexa cada troca como uma linha JSON. As dependências de serviço de modelos pertencem ao ambiente do servidor. --autosteer_interval_s mede o tempo de movimento do robô após a aplicação de uma resposta, além da latência da solicitação. A direção em múltiplos níveis e o inventário automático de cenas estão fora do escopo deste adaptador.

Controle programático

RolloutController expõe as mesmas operações de uma API de biblioteca — start() / reset() / stop() / set_task() seguros para threads, além de um callback RolloutEvent, sem stdin, impressão ou supressão de logs:

from threading import Event, Thread

from lerobot.rollout import (
    LinkedEvent,
    RolloutController,
    RolloutEvent,
    build_rollout_context,
    create_strategy,
)

parent = Event()  # your application's shutdown signal
ctx = build_rollout_context(cfg, LinkedEvent(parent))  # loads policy, connects robot
strategy = create_strategy(cfg.strategy)
strategy.setup(ctx)

controller = RolloutController(strategy, ctx, on_event=print)  # or your own observer
serve_thread = Thread(target=controller.serve)  # serve() blocks; run it where you like
serve_thread.start()

controller.start()                        # robot starts executing the policy
controller.set_task("grab the red cube")  # re-instruct mid-run
controller.reset()                        # stop movement, return home, stay warm
controller.stop()                         # end serve()

serve_thread.join()
strategy.teardown(ctx)  # teardown stays with the caller

RolloutController exige que o evento de encerramento do contexto seja um LinkedEvent, construído conforme descrito acima: o controlador encerra os segmentos de execução por meio de sua flag local, enquanto seu evento parent ainda força um encerramento completo. Defina play_sounds=False, a menos que você queira os anúncios vocais.


Flags comuns

FlagDescriçãoPadrão
--policy.pathObrigatório. ID do modelo no HF Hub ou caminho local do checkpoint—
--robot.typeObrigatório. Tipo de robô (por exemplo, so100_follower, koch_follower)—
--robot.portPorta serial para o robô—
--robot.camerasConfiguração da câmera (dicionário JSON)—
--fpsFrequência de inferência da política e gravação do conjunto de dados30
--status_interval_sSegundos entre registros de saúde (0 desativa; apenas não interativo)5
--durationTempo de execução em segundos (0 = infinito; por segmento se interativo)0
--deviceDispositivo Torch (cpu, cuda, mps)auto
--taskDescrição da tarefa (usada quando nenhum conjunto de dados é fornecido)—
--display_dataTransmita telemetria para a visualização do Rerunfalse
--display_ip / --display_portEndereço do servidor Rerun remoto—
--interpolation_multiplierComandos de robô por ação de política (controle em fps × N Hz)1
--interactiveSessão de chat no stdin; o robô fica ocioso até /start (base, sentry)false
--use_torch_compileAtive torch.compile para inferênciafalse
--resumeRetomar uma sessão de gravação anteriorfalse
--play_soundsSíntese vocal para eventostrue

Relatórios de Cadence

O log de rollout não interativo registra seu status a cada cinco segundos. Altere o intervalo com --status_interval_s=10 ou desative-o com --status_interval_s=0.

Rollout: loop 29.7/30 Hz · over budget 2/149 · starved 0 · infer 18.7 ms (worst 40.1) · process 2.41 GiB · GPU 5.12/79.6 GiB

A latência é a mediana/pior no intervalo (turnaround para RTC/remoto). Campos indisponíveis são omitidos; a memória da GPU é do dispositivo inteiro, e a memória remota é local. O relatório é pausado quando o loop é bloqueado.

Cada estratégia relata o que seu loop de controle realmente alcançou, assim como lerobot-record, lerobot-teleoperate e lerobot-replay — todos seguem o mesmo cronômetro. Cada episódio salvo recebe um resumo de uma linha, e quando o loop termina — inclusive em caso de Ctrl-C — um bloco cobrindo toda a execução:

Cadence (episode 1): 29.84 Hz policy vs 30 Hz target · 1200 ticks, 20.1 s measured · 6/598 cycles over the 33.3 ms budget (work mean 18.7 ms, worst 48.1 ms)
Cadence (episode 2): 29.84 Hz policy vs 30 Hz target · 1200 ticks, 20.1 s measured · 6/599 cycles over the 33.3 ms budget (work mean 18.7 ms, worst 48.1 ms)
Cadence summary — whole run, 2 episodes · target 30 Hz × 2 (16.7 ms tick slot, 33.3 ms cycle budget): 2400 ticks, 1197 cycles judged
  effective cadence: 29.84 Hz policy / 59.67 Hz commands over 40.2 s measured
  cycles over the 33.3 ms work budget: 12/1197 (1.0%) — work mean 18.7 ms, worst 48.1 ms
  ticks over their 16.7 ms slot: 40/2400 (costs interpolation smoothness only)
  loop-body steps (share of measured work):
    observe      mean   3.13 ms · worst   9.00 ms ·  31.7% of work · 2400 calls
    process_obs  mean   0.50 ms · worst   1.00 ms ·   5.1% of work · 2400 calls
    infer        mean   5.18 ms · worst  40.00 ms ·  52.4% of work · 2400 calls
    send         mean   0.40 ms · worst   0.40 ms ·   4.1% of work · 2400 calls
    record       mean   0.67 ms · worst 620.00 ms ·   6.8% of work · 2402 calls
  pacing headroom: 7.4 ms slept per tick on average (max 13.0 ms) — near zero means the loop is saturated

Leia de cima para baixo: cadência efetiva é o que você obteve em relação a --fps, ciclos acima do orçamento é a frequência com que o loop não conseguiu mantê-la, o detalhamento do passo indica para onde o tempo foi e margem de ritmo próxima de zero significa que não há mais nada para absorver um tick lento. Uma alta proporção de infer aponta para a política ou para o dispositivo; uma alta proporção de observe aponta para as câmeras. Um worst de um passo muito acima do seu mean é um evento isolado — acima, record é 0,67 ms por quadro e o outlier de 620 ms é o save_episode bloqueante que encerrou um episódio.

Um ciclo aqui corresponde a um valor de ticks de uma ação de política (--interpolation_multiplier deles). Somente ciclos inteiros que excedem o orçamento de 1/fps podem custar frames; um único tick que ultrapassa seu slot apenas torna o movimento interpolado menos suave, motivo pelo qual os dois são contados separadamente. Sem interpolação, um ciclo é um único tick, de modo que o relatório elimina a distinção e conta ticks ao longo de todo o processo.

Em sessões interativas, cada segmento /start possui seu próprio cronômetro, de modo que o relatório é enviado no chat uma vez por segmento (/reset, /stop ou --duration), em vez de uma vez por processo.


Uso Programático

Para extensões, lerobot.inference é o pacote público para configurações de backend, motores locais e contratos de agendamento compartilhados. lerobot.remote_inference é responsável pelo motor remoto, protocolo, implantação e implementação de sessão; lerobot.rollout é responsável pelo loop do robô, estratégias, interpolação e gravação. Importe os símbolos públicos de seu pacote, em vez dos arquivos de implementação. As dependências remotas opcionais são necessárias apenas quando o backend remoto ou o servidor é utilizado.

Para implantações personalizadas (por exemplo, com processadores de cinemática), use a API do módulo de rollout diretamente:

from lerobot.rollout import BaseStrategyConfig, RolloutConfig, build_rollout_context
from lerobot.inference import SyncInferenceConfig
from lerobot.rollout.strategies import BaseStrategy
from lerobot.utils.process import ProcessSignalHandler

cfg = RolloutConfig(
    robot=my_robot_config,
    policy=my_policy_config,
    strategy=BaseStrategyConfig(),
    inference=SyncInferenceConfig(),
    fps=30,
    duration=60,
    task="my task",
)

signal_handler = ProcessSignalHandler(use_threads=True)
ctx = build_rollout_context(
    cfg,
    signal_handler.shutdown_event,
    robot_action_processor=my_custom_action_processor,       # optional
    robot_observation_processor=my_custom_obs_processor,     # optional
)

strategy = BaseStrategy(cfg.strategy)
try:
    strategy.setup(ctx)
    strategy.run(ctx)
finally:
    strategy.teardown(ctx)

Veja examples/so100_to_so100_EE/rollout.py e examples/phone_to_so100/rollout.py para exemplos completos com processadores de cinemática.

Update on GitHub