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

```bash
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

Use `--device=cpu` ou `--device=cuda` para substituir 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ô.

```bash
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
```

| Flag             | Descrição                                            | Padrão |
| ---------------- | ------------------------------------------------------ | ------- |
| `--duration`     | Tempo de execução em segundos (0 = infinito)                     | 0       |
| `--task`         | Descrição da tarefa passada para a política                  | --      |
| `--display_data` | Transmita observações/ações para o Rerun para visualização | false   |

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

```bash
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
```

| Flag                                   | Descrição                                           | Padrão |
| -------------------------------------- | ----------------------------------------------------- | ------- |
| `--strategy.upload_every_n_episodes`   | Fazer push para o Hub a cada N episódios                          | 5       |
| `--strategy.target_video_file_size_mb` | Tamanho do arquivo de vídeo alvo para rotação de episódios           | auto    |
| `--dataset.repo_id`                    | **Obrigatório.** Repositório do Hub para o conjunto de dados gravado | --      |
| `--dataset.push_to_hub`                | Se deve enviar para o Hub ao desmontar                    | true    |

### Highlight (`--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.

```bash
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:**

| Chave                | Ação                                                   |
| ------------------ | -------------------------------------------------------- |
| `s` (configurável) | Iniciar gravação (limpa o buffer) / parar e salvar episódio |
| `h` (configurável) | Enviar dataset para o Hub                                      |
| `ESC`              | Encerrar a sessão                                         |

| Flag                                   | Descrição                                    |
| -------------------------------------- | ---------------------------------------------- |
| `--strategy.ring_buffer_seconds`       | Duração da telemetria em buffer (padrão: 10)   |
| `--strategy.ring_buffer_max_memory_mb` | Limite de memória para o buffer circular (padrão: 1024) |
| `--strategy.save_key`                  | Tecla para alternar a gravação (padrão: `s`)         |
| `--strategy.push_key`                  | Tecla 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](./hil_data_collection) 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.

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

```bash
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):

| Chave     | Ação                                      |
| ------- | ------------------------------------------- |
| `Space` | Pausar / retomar a execução da política             |
| `Tab`   | Iniciar / parar a correção humana               |
| `Enter` | Enviar dataset para o Hub (modo apenas correções) |
| `ESC`   | Encerrar 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.

| Flag                                 | Descrição                                                                                                                                                    |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--strategy.num_episodes`            | Número de episódios de correção a serem registrados (padrão: `--dataset.num_episodes`)                                                                                    |
| `--strategy.record_autonomous`       | Registrar também os frames autônomos (padrão: false)                                                                                                                  |
| `--strategy.upload_every_n_episodes` | Fazer push para o Hub a cada N episódios (padrão: 5)                                                                                                                      |
| `--strategy.input_device`            | Dispositivo de entrada: `keyboard` ou `pedal` (padrão: keyboard)                                                                                                        |
| `--strategy.smooth_handover`         | Transfira 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.type`                      | **Obrigatório.** Tipo de teleoperador                                                                                                                                |

### Episodic (`--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.

```bash
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:**

| Chave         | Ação                           |
| ----------- | -------------------------------- |
| `→` (direita) | Encerrar o episódio atual antecipadamente    |
| `←` (esquerda)  | Descartar o episódio e gravá-lo novamente |
| `ESC`       | Pare a sessão de gravação       |

| Flag                                            | Descrição                                                                                                                                                |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--dataset.num_episodes`                        | Número de episódios a gravar                                                                                                                               |
| `--dataset.episode_time_s`                      | Duração de cada episódio de gravação em segundos. O padrão é `--duration` quando esse é definido e este não é                                               |
| `--dataset.reset_time_s`                        | Duração da fase de reinício entre episódios, em segundos                                                                                                    |
| `--teleop.type`                                 | Opcional. Teleoperador para controlar o robô durante os resets                                                                                                    |
| `--strategy.reset_to_initial_position`          | Se deve redefinir o robô para sua posição inicial entre episódios                                                                                        |
| `--strategy.smooth_leader_to_follower_handover` | Se o comportamento de transição suave de líder para seguidor deve ser ativado ou desativado.                                                                                 |
| `--strategy.smooth_handover`                    | Transfira 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](./integrate_hardware#the-4-core-conventions), resolvida a partir do módulo ou pacote da configuração.

```python
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ção                     | Efeito                                                                                                                                     |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `dataset_mode`                  | `"none"` (padrão) rejeita as flags `--dataset.*`, `"optional"` as registra quando fornecidas, `"required"` torna `--dataset.repo_id` obrigatório |
| `requires_teleop`               | Torna `--teleop.type` obrigatório                                                                                                            |
| `supports_interactive`          | Permite `--interactive=true`; `run()` deve então respeitar o contrato de reiniciabilidade documentado em `RolloutStrategy`                             |
| `supports_sim_robot`            | Permite `--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.*`:

```bash
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, 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](./bring_your_own_policies) 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](./remote_inference) obtém seu contrato de política de um servidor e não carrega pesos no computador do robô.

### Sync (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.

```bash
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
```

| Flag                                        | Descrição                                                                                         |
| ------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `--inference.rtc.execution_horizon`         | Etapas para mesclar com o chunk anterior (padrão: varia conforme a política)                                      |
| `--inference.rtc.mode`                      | `guided` (padrão) ou trained-prefix `trained` para checkpoints Pi05 compatíveis                      |
| `--inference.rtc.max_guidance_weight`       | Força de aplicação da consistência (padrão: varia conforme a política)                                        |
| `--inference.rtc.prefix_attention_schedule` | Agendamento de mistura: `LINEAR`, `EXP`, `ONES`, `ZEROS`                                                    |
| `--inference.queue_threshold`               | Limiar de backpressure; o RTC treinado requer pelo menos seu atraso máximo                             |
| `--inference.action_starvation_grace_s`     | Aguarde 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](./rtc) 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](./remote_inference) para alinhamento/mistura de blocos e o [comportamento de espera](./remote_inference#what-the-robot-does-while-waiting-or-stopping) 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](./integrate_hardware#waiting-between-predictions). 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âmetro               | Padrão | Significado                                      |
| ----------------------- | ------- | -------------------------------------------- |
| `max_observation_age_s` | `5 s`   | Idade máxima da fonte por meio da execução da ação  |
| `action_timeout_s`      | `10 s`  | Solicitação de ação pendente                   |
| `startup_timeout_s`     | `120 s` | Preparação inicial e inicialização de ação imediata |
| `language_timeout_s`    | `120 s` | Operaçã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.

Alterações de compilação e instruções

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 o 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 se esgotar. 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.

```bash
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
```

| Comando             | Ação                                                                                                                                                                                                                                                                                                                  |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/start`            | Iniciar (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](#external-planner), `/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](#external-planner)                                                                                                                                                       |
| `/autosteer <goal>` | Entregue um objetivo de alto nível e deixe que o cabeçalho de texto da política ou o [planejador externo](#external-planner) escolham as subtarefas. `/autosteer off` devolve o controle; sem argumento, relata o objetivo atual                                                                                                                  |
| `/reset`            | Pare o movimento, retorne o robô à posição de inicialização e restaure a instrução `--task`                                                                                                                                                                                                                           |
| `/stop`             | Encerre a sessão e execute as rotinas normais de desligamento                                                                                                                                                                                                                                                                    |
| `/help`             | Liste os comandos                                                                                                                                                                                                                                                                                                       |

```text
> /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:

```bash
(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](#external-planner). 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, para que você possa continuar 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 tem 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](#cadence-reporting) 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.

```bash
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` através do 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:

```bash
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":

```bash
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. O direcionamento multinível 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:

```python
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

| Flag                              | Descrição                                                       | Padrão |
| --------------------------------- | ----------------------------------------------------------------- | ------- |
| `--policy.path`                   | **Obrigatório.** ID do modelo no HF Hub ou caminho local do checkpoint            | --      |
| `--robot.type`                    | **Obrigatório.** Tipo de robô (por exemplo, `so100_follower`, `koch_follower`) | --      |
| `--robot.port`                    | Porta serial para o robô                                         | --      |
| `--robot.cameras`                 | Configuração da câmera (dicionário JSON)                                  | --      |
| `--fps`                           | Frequência de inferência da política e gravação do conjunto de dados                  | 30      |
| `--status_interval_s`             | Segundos entre registros de saúde (0 desativa; apenas não interativo)    | 5       |
| `--duration`                      | Tempo de execução em segundos (0 = infinito; por segmento se interativo)    | 0       |
| `--device`                        | Dispositivo Torch (`cpu`, `cuda`, `mps`)                               | auto    |
| `--task`                          | Descrição da tarefa (usada quando nenhum conjunto de dados é fornecido)               | --      |
| `--display_data`                  | Transmita telemetria para a visualização do Rerun                           | false   |
| `--display_ip` / `--display_port` | Endereço do servidor Rerun remoto                                       | --      |
| `--interpolation_multiplier`      | Comandos de robô por ação de política (controle em `fps × N` Hz)        | 1       |
| `--interactive`                   | Sessão de chat no stdin; o robô fica ocioso até `/start` (base, sentry)  | false   |
| `--use_torch_compile`             | Ative `torch.compile` para inferência                              | false   |
| `--resume`                        | Retomar uma sessão de gravação anterior                               | false   |
| `--play_sounds`                   | Síntese vocal para eventos                                        | true    |

---

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

```text
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 uma ação de política em termos de ticks (`--interpolation_multiplier` deles). Somente ciclos inteiros que excedem o orçamento de `1/fps` podem custar quadros; um único tick que ultrapassa seu intervalo 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 essa distinção e conta ticks ao longo de todo o processo.

Em [sessões interativas](#interactive-sessions), 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:

```python
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.

