简体中文 | English
Use eval/libero/ as the reference for simulator benchmarks. A sim integration
usually owns its environment construction, rollout loop, success metrics, and
observation adapter. It should reuse model-server for inference instead of
loading model weights in Python.
Recommended layout:
eval/my_sim/
├── README.md
├── environment.yaml
├── policy/
│ └── model_server.py
├── runners/
│ └── run_model_server.py
├── scripts/
│ └── run_model_server.sh
└── utils/
├── common.py
└── environment.py
Keep the Python side responsible for simulator I/O only:
utils/environment.py: simulator config, env construction, reset helpers, task text, max episode length, success extraction.policy/model_server.py: simulator observation tomodel-serverrequest.runners/run_model_server.py: rollout loop, result JSON, timing summaries.scripts/run_model_server.sh: optional convenience wrapper for launching the server and runner together.
Every policy adapter should build this request shape:
{
"images": [
{"name": "observation.images.image", "image": rgb_hwc_u8},
],
"state": state_vector,
"prompt": task_text,
}Notes:
- Images must be RGB HWC
uint8, or a dict accepted byrobot_client.python.model_client.image_to_rgb_hwc_u8_bytes. - Image
namevalues must match the image keys stored in the GGUF metadata. - State should be the simulator/model state in the model's expected order. Do not add Python-side padding unless the simulator contract explicitly requires it.
promptshould be the task instruction used by the training/eval benchmark.
Reference: eval/libero/policy/model_server.py.
Subclass BasePolicy when the default RobotPolicy cannot represent the sim
observation directly.
from typing import Any
from robot_client.policy.base_policy import BasePolicy
from robot_client.python.model_client import ModelClient
class MySimModelServerPolicy(BasePolicy):
def __init__(self, host: str, port: int, action_dim: int):
super().__init__(ModelClient(host=host, port=port, timeout=120.0))
self.action_dim = action_dim
def build_observation(self, observation: dict[str, Any], *, platform: Any, task: str) -> dict[str, Any]:
del platform
return {
"images": [
{"name": "observation.images.image", "image": make_rgb_image(observation)},
],
"state": make_state_vector(observation),
"prompt": task,
}BasePolicy.select_action() already handles action chunk caching. Set
action_dim to the number of action values accepted by the simulator, so any
extra values returned by the model are ignored before env.step().
A sim runner should be explicit about the benchmark lifecycle:
- Parse runner args: host, port, suite/task ids, seed, episode count, max steps, image/state/action settings, output path.
- Apply runtime environment variables before importing or constructing the sim.
- Construct envs.
- Wait for an existing
model-serveror launch one from--server-command. - For each episode:
- reset the sim;
- call
policy.reset(reset_server=True); - get the task text;
- call
policy.select_action(observation, platform=platform, task=task); - step the sim;
- record success, reward, steps, predict calls, and timing.
- Write a JSON payload with config, per-episode rows, aggregate success, and timing summaries.
- Close envs and stop any server process launched by the runner.
If the adapter does not use platform fields, pass a plain BasePlatform() stub,
as LIBERO does in eval/libero/runners/run_model_server.py.
Keep result files easy to compare across sims:
{
"runner": "model-server",
"config": {...},
"episodes": [
{
"episode": 0,
"seed": 1000,
"task": "...",
"success": True,
"sum_reward": 1.0,
"steps": 123,
"predict_calls": 16,
"server_timing_avg_ms": {...},
}
],
"timing_ms": {...},
"overall": {...},
"per_task": [...],
}Use helpers like write_json() and aggregate_episodes() from
eval/libero/utils/common.py if they fit the new benchmark.
- Add an
environment.yamlif the sim has nontrivial Python dependencies. - Make image keys configurable from the runner.
- Make state and action dimensions configurable from the runner.
- Support
--host,--port,--launch-server, and--server-command. - Reset both the sim and model-server between episodes.
- Write outputs under
eval/results/by default. - Add a one-episode smoke command to the sim README.
- Keep generated results, videos, and checkpoints out of git.