# Simulação: local ou servida

Um ambiente simulado pode ser controlado de duas maneiras:

- como um **env em lote** (`--env.*`): muitos ambientes para uma única tarefa, avançados juntos por cada chamada de `step()`. O `lerobot-eval` e a avaliação durante o treinamento usam esse modo.
- como um **robô simulado** (`--robot.type=sim`): um ambiente atrás da interface `Robot`, substituível por hardware real. `lerobot-rollout`, `lerobot-record`, `lerobot-teleoperate` e `lerobot-replay` o utilizam.

Executa **localmente**, por meio da API de ambiente, ou **servido** por `lerobot-env-server`, geralmente em sua própria imagem Docker, para que as dependências do simulador nunca se encontrem com as da política. Definir um `endpoint` seleciona a execução servida; ambos os caminhos expõem as mesmas observações canônicas (imagens HWC uint8, estado float32 plano), fins de episódio e convenções de controle.

O código mantém três camadas separadas:

- um **env** (`lerobot.envs`, `--env.type`) é o que um comando do LeRobot executa, configurado por um `EnvConfig`. Nem todo env é um simulador: `gym_manipulator` controla um robô real.
- a **sim** (`lerobot.sim`) é o simulador por trás de um ambiente simulado: um `SimConfig` que nomeia sua integração, executado por `LocalVectorEnv`, sem torch.
- o **env server** (`lerobot.env_server`) serve uma simulação; `RemoteVectorEnv` a controla com a mesma API de `LocalVectorEnv`.

## Avaliar um checkpoint

Localmente, com o simulador instalado ao lado do LeRobot (veja [LIBERO](./libero)):

```bash
lerobot-eval \
  --policy.path=lerobot/smolvla_libero \
  --policy.pretrained_revision=a24f1f380461b665158d52acd140152c79e8b36d \
  --policy.device=cuda \
  --env.type=libero --env.task=libero_spatial --env.task_ids='[0]' \
  --rename_map='{"observation.images.image": "observation.images.camera1", "observation.images.image2": "observation.images.camera2"}' \
  --eval.n_episodes=1 --eval.batch_size=1 \
  --output_dir=outputs/eval_libero
```

Servido: inicie o servidor com o mesmo ambiente (veja [Servir um ambiente](#serve-an-environment)) e adicione seu endpoint:

```bash
lerobot-env-server --config_path=docker/sim/configs/libero.yaml --docker=true
lerobot-eval ... --env.server.endpoint=tcp/127.0.0.1:7448
```

Todas as opções do servidor estão sob `--env.server.*`, e nenhuma se aplica a uma execução local. O cliente solicita o deployment nomeado conforme `--env.type`, como as configurações do servidor o nomeiam; passe `--env.server.deployment` para outro. Um endpoint ou deployment incorreto falha dentro de `--env.server.handshake_timeout_s` (10 s) e indica o que o endpoint serve. Contra um servidor protegido com TLS, passe seu arquivo Zenoh JSON5 com `--env.server.zenoh_config_path`, como para inferência remota. O servidor deve executar o ambiente descrito pelo comando: o mesmo type, task, convenções de controle e corpo simulado (`robot_type`), ou a avaliação se recusa a iniciar e lista as diferenças. Outras opções que diferem (resolução, comprimento do episódio) são registradas como avisos.

Os nomes das observações vêm do ambiente (`observation.images.<camera>`, `observation.state`). Quando um checkpoint foi treinado com outros nomes de câmeras, mapeie-os com `--rename_map`, como para qualquer robô, e preserve a identidade de cada câmera, mesmo quando várias têm o mesmo formato. Câmeras de preenchimento (`empty_cameras`) são uma configuração de política salva com o checkpoint; substitua-a com `--policy.empty_cameras` apenas quando necessário.

Antes do primeiro passo, cada checkpoint de entrada deve existir no ambiente após a renomeação, com dimensões de estado e ação compatíveis e o mesmo número de canais RGB. A altura e a largura da imagem podem diferir: o pré-processamento da política é responsável pelo redimensionamento. As convenções do controlador (ações delta versus absolutas, formato de rotação) não são verificadas; use um checkpoint treinado na convenção do ambiente.

Cada `step()` avança uma vez todos os ambientes não finalizados no lote. Um ambiente que termina mantém sua observação terminal até o próximo reset; avançar os outros nunca o reinicia. Com `--eval.use_async_envs=true` (o padrão), cada ambiente é executado em seu próprio processo de worker, localmente ou no servidor, e o lote avança em paralelo. Um servidor aceita até `max_envs` ambientes por lote (32 por padrão). `--env.server.encoding=jpeg` (ou `--robot.env.server.encoding=jpeg`, com `--env.server.jpeg_quality`, 90 por padrão) envia os quadros de câmera servidos como JPEG: respostas menores para um servidor remoto e lotes maiores, ao custo de tempo de codificação e quadros com perda. No localhost, quadros brutos são mais rápidos. Uma resposta carrega os quadros de todos os ambientes no lote e deve caber em `max_payload_bytes` (32 MiB por padrão) em ambas as extremidades, ou o servidor recusa o lote quando ele é aberto. Para lotes grandes ou câmeras de alta resolução, aumente-o no servidor (`max_payload_bytes` na seção `zenoh` do YAML, ao lado de `listen_endpoints`) e no cliente (`--env.server.max_payload_bytes`, ou `--robot.env.server.max_payload_bytes`).

## Executar um robô de simulação

O mesmo ambiente como um ambiente em lote e como um robô de simulação:

```bash
lerobot-eval    --env.type=libero --env.task=libero_spatial --env.task_ids='[0]' ...
lerobot-rollout --robot.type=sim --robot.env.type=libero --robot.env.task=libero_spatial --robot.task_id=0 ...
```

Por exemplo:

```bash
lerobot-rollout \
  --robot.type=sim --robot.env.type=libero --robot.env.task=libero_spatial --robot.task_id=0 \
  --task="pick up the black bowl between the plate and the ramekin and place it on the plate" \
  --policy.path=lerobot/smolvla_libero \
  --policy.pretrained_revision=a24f1f380461b665158d52acd140152c79e8b36d \
  --rename_map='{"observation.images.image": "observation.images.camera1", "observation.images.image2": "observation.images.camera2"}' \
  --device=cuda --fps=20 --inference.type=sync \
  --display_data=true --display_mode=rerun \
  --return_to_initial_position=false --play_sounds=false
```

- `--robot.env.*` aceita as mesmas opções de `--env.*` na avaliação. `--robot.task_id` seleciona a única tarefa que o robô executa, enquanto a avaliação executa todas as tarefas da suíte ou aquelas em `--env.task_ids`. Os ambientes do EnvHub são `--robot.env.type=envhub --robot.env.hub_path=<user/repo> --robot.env.trust_remote_code=true`.
- Algumas políticas exigem os processadores do ambiente (XVLA e Cosmos3 no LIBERO, Isaac Lab Arena). O `lerobot-eval` os aplica, assim como um robô de simulação com `--inference.type=sync`. A inferência remota não os aplica, portanto recusa esse checkpoint na inicialização.
- `--robot.env.server.endpoint=tcp/...` executa o ambiente em um servidor de ambiente, assim como `--env.server.endpoint` faz para um ambiente em lote. O robô verifica se o servidor executa esse ambiente.
- Cada `send_action()` avança o ambiente exatamente uma vez. Leitura de observações, inferência da política e espera não avançam a simulação; o loop de controle existente é responsável pelo ritmo em tempo real.
- `--fps` deve ser igual à taxa de controle do ambiente, dividida por `--interpolation_multiplier` ao interpolar, pois cada comando avança o ambiente uma vez. Ao gravar, `--dataset.fps` deve ser igual a `--fps`. Caso contrário, o rollout se recusa a iniciar.

Um robô de simulação é um robô: substitua `--robot.type=sim --robot.env.*` pelos parâmetros `--robot.*` do seguidor real, e o restante permanece inalterado. Um teleoperador o controla da mesma forma que controla o seguidor real (`--teleop.type=so101_leader` para um gêmeo SO-101), quando o ambiente declara os nomes e as unidades das ações do seguidor; uma incompatibilidade é reportada na primeira ação. O `lerobot-record` armazena o corpo declarado pelo ambiente como o `robot_type` do conjunto de dados (`panda` para LIBERO), ou `sim_robot` quando nenhum é declarado.

Os episódios gravados seguem os do ambiente. `lerobot-record` e a estratégia de rollout episódica reiniciam o ambiente simulado no início de cada episódio e o encerram quando o episódio do ambiente termina (sucesso, terminação ou seu limite de passos) ou quando sua duração se esgota, o que ocorrer primeiro. Cada episódio gravado é, portanto, um episódio simulado, a partir de seu estado inicial. Nada precisa ser reiniciado manualmente, então pule a fase de reset entre episódios com `--dataset.reset_time_s=0` e, com a estratégia de rollout episódica, também passe `--strategy.reset_to_initial_position=false`. Quando um episódio termina, o robô para: ele ignora ações e mantém seu estado final, e `latest` mantém a recompensa final, a terminação e o sucesso. A teleoperação e a estratégia de rollout base então iniciam um novo episódio (a estratégia também reinicia a política), e o replay para. As estratégias sentry, highlight e DAgger ainda não suportam robôs simulados, pois gravam por tempo ou sob demanda, em vez de por episódio; use a estratégia episódica para gravar. Robôs reais não são afetados: seus episódios são cronometrados e sua cena é reiniciada manualmente. Para taxas de sucesso, avalie com `lerobot-eval` e um ambiente em lote.

Os recursos da câmera do robô usam os mesmos nomes `observation.images.<camera>` que a gravação em hardware real. Uma única câmera sem nome é `observation.images.image`. A avaliação em lote mantém as próprias chaves do ambiente, como `observation.image` para uma única câmera; ao implantar um checkpoint com esse nome por meio de `SimRobot`, use `--rename_map='{"observation.images.image": "observation.image"}'`.

Para inferência remota, mantenha as mesmas opções `--robot.*` e substitua as opções locais de política e inferência por:

```bash
--inference.type=remote \
--inference.endpoint=tcp/127.0.0.1:7447 \
--inference.deployment=libero \
--inference.semantics=libero-delta-v1
```

Inicie o servidor de política separadamente usando [remote inference setup](./remote_inference). Seu esquema deve corresponder às observações do robô após `--rename_map`. Omita `--policy.path` do comando de rollout, pois o servidor de política carrega o checkpoint. Para RTC local, use `--inference.type=rtc` com as configurações de RTC da política.

Quando a política está atrasada, manter um robô de simulação não avança a física. Falhas no simulador propagam-se para o encerramento do rollout.

## Servir um ambiente

A partir de um checkout da fonte do LeRobot (a imagem é construída a partir dele), execute a partir da raiz do repositório:

```bash
lerobot-env-server --config_path=docker/sim/configs/libero.yaml --docker=true
```

`--docker=true` usa `docker/sim/compose.server.yaml`, seleciona a imagem a partir de `sim.type`, monta o YAML efetivo e executa o container na rede do host, de modo que o servidor escuta em `localhost:7448`, a menos que `--zenoh.listen_endpoints` indique o contrário. Troque o YAML para selecionar outro simulador. A taxa de controle, a convenção de ação, os nomes dos componentes e o tipo de robô de cada simulador vêm do `conventions()` da sua integração, de modo que um YAML apenas nomeia a tarefa e as opções do simulador.

No que diz respeito ao `lerobot-policy-server`, cada campo YAML aceita uma substituição aninhada na linha de comando, e o arquivo permanece inalterado. Um valor de dicionário como `sim.kwargs` é mesclado ao do YAML:

```bash
lerobot-env-server --config_path=docker/sim/configs/libero.yaml --docker=true \
  --sim.task=libero_goal \
  --sim.kwargs='{gym_kwargs: {observation_height: 256, observation_width: 256}}'
```

A configuração efetiva é mantida em `$HF_LEROBOT_HOME/sim`, de modo que um container reiniciado a encontre. Alterar a configuração recria o servidor e encerra as sessões ativas. A imagem é construída quando ausente; adicione `--build=true` para reconstruí-la após alterar o simulador ou o código-fonte do LeRobot, ou `--stop=true` para desligar o servidor. O container requer acesso à GPU do Docker para renderização EGL. Os repositórios do [EnvHub](./envhub) são servidos com `--envhub=<user/repo>`; consulte [EnvHub e o servidor do simulador](./envhub#envhub-and-the-simulator-server).

> [!TIP]
> Se um simulador que renderiza com Vulkan (Isaac Sim, ou ManiSkill/SAPIEN como em RoboMME, cuja imagem inclui seu próprio arquivo Vulkan) falhar no contêiner com `vkCreateInstance failed` ou `ERROR_INCOMPATIBLE_DRIVER` enquanto funciona fora do Docker, o contêiner possui as bibliotecas do driver da NVIDIA, mas não os arquivos de loader que apontam para elas (`/usr/share/vulkan/icd.d/nvidia_icd.json`, `/usr/share/glvnd/egl_vendor.d/10_nvidia.json`). O modo padrão do NVIDIA Container Toolkit, usado por `gpus: all`, pode não montá-los; seu [modo CDI](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/cdi-support.html) sim. Solicite a GPU através do CDI (`--device nvidia.com/gpu=all` para `docker run`, `devices: [nvidia.com/gpu=all]` no lugar de `gpus: all` em `docker/sim/compose.server.yaml`), ou adicione os dois arquivos à imagem.

Sem `--docker=true`, o mesmo comando executa o servidor no ambiente atual: instala as dependências nativas do simulador em um ambiente isolado, instala o LeRobot com `--no-deps` e instala `docker/sim/requirements.txt`. De qualquer forma, ele escuta em localhost; para aceitar outras máquinas, passe o endereço da interface de rede que elas alcançam, como em `--zenoh.listen_endpoints='[tcp/192.168.1.20:7448]'`, e adicione TLS e controle de acesso com um `zenoh.config_file` no YAML.

As imagens de runtime do LIBERO usam arquivos de estado inicial convertidos do NumPy. A compilação da imagem converte os ativos confiáveis de upstream com `scripts/ci/convert_libero_init_states.py`; o servidor rejeita arquivos convertidos ausentes. O RoboTwin e o RoboMME exigem seu próprio torch para planejamento nativo e ManiSkill, respectivamente; suas instalações de torch são isoladas do ambiente de policy.

As solicitações do servidor nunca são repetidas. Após um tempo limite ambíguo, o cliente falha; feche-o e abra uma nova sessão.

## Adicionar um benchmark

Consulte [Adicionando um benchmark](./adding_benchmarks) para módulos de simulador, convenções, imagens e integração de CI.

