Deploying a Policy¶
This tutorial takes a trained checkpoint and runs it against an environment. Inference uses a server-client architecture: an environment server owns the environment and steps it, while the policy client (VersatIL) receives observations, runs inference, and sends actions back over ZMQ. The environment behind the server is irrelevant to the client: a simulated benchmark, a real robot wrapping its drivers and cameras, or anything else that implements the transport protocol. Server and client can run on different machines, within the same network.
This tutorial uses a simulated benchmark as the running example, but only Step 1 is simulation-specific; the client side is identical everywhere. For how the client works internally (transports, preprocessing, temporal aggregation) and how to implement a server or a custom transport for your own hardware, see the inference architecture.
Prerequisites¶
- A training checkpoint directory produced by
versatil.endpoints.train. It contains the checkpoint file (last.ckptby default), the resolvedconfig.yaml, and the fitted normalizer and tokenizer state. The client rebuilds the policy from this directory. - An environment server. For the simulated benchmarks, ready-made servers are installed from their own repositories:
| Benchmark | Server repository |
|---|---|
| LIBERO / LIBERO-PRO | simulation_libero |
| LIBERO-Plus | simulation_libero_plus |
| Meta-World | simulation_metaworld |
| PushT | simulation_pusht |
| Block Pushing | simulation_block_push |
| Multimodal Ant | simulation_multimodal_ant |
| Franka Kitchen | simulation_kitchen |
| UR3 Block Push | simulation_ur3_block_push |
Each server repository documents its own installation and benchmark-specific options. The servers run environments in parallel batches, track per-task success rates, and record rollout videos and trajectory CSVs.
Step 1: Start the Simulation Server¶
On the simulation machine, start the server for your benchmark. For LIBERO:
python -m versatil_inference.run_evaluation \
--task_suite_name libero_spatial \
--num_trials_per_task 10 \
--max_parallel_envs 10 \
--port 5556 \
--output_folder ./results
On headless machines (clusters without a display), select a headless MuJoCo rendering backend first:
The server binds the port and waits for a client to register. Argument names
vary slightly per benchmark (for example Meta-World uses --benchmark_name
and --number_of_trials); check the server repository's README.
Step 2: Run the Policy Client¶
On the policy machine, point the deployment endpoint at the checkpoint and pass the server IP and port:
python -m versatil.endpoints.deploy \
checkpoint_path=/path/to/checkpoint_dir \
client.model_server_address=127.0.0.1 \
client.model_server_port=5556
The client registers with the server and drives the evaluation loop until the
server reports completion. Results (per-task success rates, videos,
trajectory CSVs) are written on the server side, in --output_folder or in
the checkpoint's rollouts/ directory when unset.
The endpoint is Hydra-based, so every setting is a key=value override:
| Override | Default | Meaning |
|---|---|---|
checkpoint_path |
required | Checkpoint directory to load. |
checkpoint_name |
last.ckpt |
Checkpoint file inside the directory. |
device |
auto | cuda when available, else cpu; set explicitly to pin. |
compile_model |
true |
Compile the policy with torch.compile. The first inference call is slow while kernels compile. |
client.model_server_address |
127.0.0.1 |
Environment server address. |
client.model_server_port |
5555 |
Environment server port. |
client.temporal_aggregation |
false |
Query the policy every step and ensemble overlapping chunk predictions with exponentially weighted averaging; one action is executed per step. |
client.action_execution_horizon |
full chunk | Actions executed from each predicted chunk before re-predicting. Only used when client.temporal_aggregation=false. |
client.compression_type |
raw |
Wire compression for camera observations (raw, jpeg, png); match the server setting. |
client.request_timeout_seconds |
none | Fail instead of blocking forever when the server dies. |
client.update_rate_hz |
none | Fixed action-send rate for real-time control; leave unset for simulation, where the server paces the loop. |
Chunk Execution Modes¶
The two settings select between mutually exclusive execution modes:
- Chunked execution (
temporal_aggregation=false, the default): the policy predicts a chunk and the client executesaction_execution_horizonactions from it before re-predicting. The default executes the full chunk open-loop; lowering it re-predicts more often and reacts faster at the cost of more inference calls. It cannot exceed the policy's prediction horizon. - Temporal ensemble (
temporal_aggregation=true): the policy is queried at every environment step and the overlapping predictions for the current timestep are averaged with exponential weighting, smoothing chunk boundaries. Exactly one action is executed per step, soaction_execution_horizonis ignored.
Warning
With chunked execution, a policy trained with observation_horizon > 1
receives history frames spaced action_execution_horizon steps apart,
while its training windows were contiguous. The client logs a warning
for this combination; prefer the temporal ensemble for such policies.
Re-predicting after every 8 executed actions:
python -m versatil.endpoints.deploy \
checkpoint_path=/path/to/checkpoint_dir \
client.model_server_address=10.0.0.1 \
client.model_server_port=5556 \
client.action_execution_horizon=8
Compressed Checkpoints¶
The endpoint detects post-training compressed
artifacts automatically: pass the compressed/<timestamp>/ directory as
checkpoint_path and the client loads the compressed runtime instead of the
floating-point policy, typically with device=cpu.
Beyond Simulation¶
Nothing above changes on real hardware. A robot-side environment server
publishes camera and proprioception observations and executes received
actions, speaking the same protocol the simulation servers speak (the
tso-robotics-sockets package provides the ZMQ implementation). The client
command stays exactly the same; for real-time control, set
client.update_rate_hz to the robot's control rate so actions are sent at a
fixed frequency instead of as fast as inference allows.
Troubleshooting¶
- Client hangs at startup: the server is not reachable. Check address,
port, and firewalls, and set
client.request_timeout_seconds=30to fail fast instead of blocking. - Server crashes on rendering: set
MUJOCO_GL=egl(orosmesa) on headless machines. If EGL still fails to initialize a device display (common on nodes without the nvidia EGL userspace driver), enumerate the EGL devices and pointMUJOCO_EGL_DEVICE_IDat the Mesa software device; rendering then runs on the CPU. - Slow first prediction: expected with
compile_model=true; disable it for quick smoke tests. - Observation key errors: the checkpoint's observation space must match what the server provides. Evaluate checkpoints against the benchmark they were trained on, for example a LIBERO-trained policy against the LIBERO server.