
Open-loop evaluation scores predicted actions against recorded ones. Closed-loop evaluation runs the policy on the robot. Which to run when, with real commands, defaults and rollout-count statistics.
A fine-tuning run just finished. There are eight checkpoints in the bucket and one arm on the desk. Two different tests are now available, and they answer two different questions. Open-loop evaluation replays a recorded episode and asks how close the model's predicted action is to the action a human actually sent. Closed-loop evaluation hands the model the robot and asks whether the task got done. Teams routinely run the first, report the number, and are then surprised that the arm never closes the gripper.
This page is about which test to run when, what each number really means, and the specific ways both of them mislead you. Every command below was read out of the upstream repository on 24 August 2026; flag names and defaults change, so the version is named wherever it matters.
What you need to know
- •Open-loop evaluation compares predicted actions against recorded actions. No robot, no simulator, no reset. It runs in seconds on one GPU.
- •Closed-loop evaluation lets the policy drive, so its own action decides the next observation. It is the only test that answers whether the task gets done.
- •The two disagree often enough that picking a checkpoint by lowest error is a documented failure mode, not a rare accident.
- •Isaac-GR00T ships both paths: gr00t/eval/open_loop_eval.py prints unnormalized action MSE and MAE, gr00t/eval/rollout_policy.py runs rollouts and reports a success rate.
- •GR00T's open-loop eval is not fully open loop. It re-reads the recorded observation every --execution-horizon steps (default 16), so error cannot compound past one chunk.
- •Rollout count is the hidden cost of closed loop. Twenty rollouts at 70 percent give a 95 percent Wilson interval of roughly 48 to 86 percent.
- •Rule of thumb: open loop to catch broken plumbing, closed loop before making any claim about the task.
The two questions, side by side
Both tests take the same artifact, a trained policy checkpoint, and both produce a single number. That is where the similarity ends. Open loop is a supervised-learning measurement: the model sees a real observation from your LeRobot dataset and its output is scored against the recorded label. Closed loop is a control measurement: the model sees whatever its own previous action produced, which after a few seconds is a state no human demonstrator ever visited.
| Open-loop evaluation | Closed-loop evaluation | |
|---|---|---|
| Question answered | Does the model predict the recorded action? | Does the arm finish the task? |
| Who supplies the next observation | The recorded dataset | The policy's own consequences |
| Output | A regression error (MSE, MAE) per trajectory | A success rate over N rollouts |
| What you need | One GPU and the dataset | A robot or a simulator, plus a reset between episodes |
| Wall clock for one checkpoint | Seconds to minutes | Tens of minutes to hours |
| Do errors compound | No (or only within one chunk) | Yes, that is the whole point |
| Needs a human in the loop | No | On real hardware, yes, to judge success |
| Good at catching | Wrong normalisation, wrong camera keys, dead checkpoints | Drift, timing, grasp failures, task-level behaviour |
The compounding-error asymmetry is the entire reason the two disagree. Imitation learning trains on a distribution of states a human produced. At rollout time the policy leaves that distribution the moment it makes a small mistake, and the errors it makes off-distribution are not the errors the training loss ever penalised. A model can be very good at the first question and useless at the second. See imitation learning for the background, and loss falls, policy does nothing for the version of this that shows up in a support ticket.
What open-loop evaluation actually measures
NVIDIA's GR00T N1.7 repository has a section literally called Open-Loop Evaluation, and one called Closed-Loop Evaluation right underneath it. The open-loop entry point is a tyro CLI at gr00t/eval/open_loop_eval.py. Point it at a dataset and a checkpoint and it walks a trajectory, asks the model for actions, and compares them against the recorded ones.
uv run python gr00t/eval/open_loop_eval.py \
--dataset-path <DATASET_PATH> \
--embodiment-tag NEW_EMBODIMENT \
--model-path <CHECKPOINT_PATH> \
--traj-ids 0 \
--execution-horizon 16Leave out --model-path and the script does not load a checkpoint at all: it falls back to a PolicyClient talking to a running policy server on 127.0.0.1:5555. That is the same server the closed-loop path uses, which is convenient, and it is also the first thing that silently changes the meaning of your run. Here are the real defaults from the ArgsConfig dataclass.
| Flag | Default | What it does to the number |
|---|---|---|
| --traj-ids | [0] | Evaluates exactly one episode, index 0. If episode 0 is in the training set, the result is a fit number, not a held-out number. |
| --steps | 200 | Upper bound on steps evaluated, capped by trajectory length. A shorter window hides late-episode drift. |
| --execution-horizon | 16 | How many predicted actions are consumed before the recorded observation is read again. Lower means more re-anchoring and lower error. |
| --denoising-steps | 4 | Diffusion sampling steps for the action head. Changes the actions, and therefore the MSE. Ignored when talking to a remote server. |
| --embodiment-tag | new_embodiment | Which embodiment head is used. Wrong tag, meaningless number. |
| --host / --port | 127.0.0.1 / 5555 | Used only when --model-path is omitted. |
| --save-plot-path | None | Falls back to /tmp/open_loop_eval/traj_{id}.jpeg. |
The loop is for step_count in range(0, actual_steps, execution_horizon). At every one of those boundaries the observation is pulled from the recorded trajectory again, not from wherever the policy would have driven the arm. The model is teacher-forced back onto the human's path every 16 steps by default. Compounding error is truncated at the chunk boundary, which is exactly the error mode that kills a real rollout. This also means MSE numbers are only comparable at the same --execution-horizon: shorten it and the error drops without the model getting better.
The MSE number and what it hides
The script computes mean squared error and mean absolute error over the unnormalized action vector, averaged across every action dimension and every time step, then averages those per-trajectory values. Four consequences follow, and all four bite in practice.
- Units are whatever your dataset stores. Unnormalized means degrees, or radians, or raw servo ticks. An MSE of 0.8 is meaningless without the unit. It is a within-project number, never a cross-project one.
- All joints are averaged together. On an SO-100 that is five arm joints plus a gripper, so the gripper is one sixth of the score. A policy that tracks the arm perfectly and never closes the gripper still posts a respectable MSE.
- One trajectory by default. --traj-ids defaults to [0]. One episode of one task is a sample size of one.
- It is scored against a human, not against success. Two different valid grasp approaches score as a large error against each other. A slower but correct trajectory scores worse than a fast wrong one.
Montagut Bofi et al. fine-tuned SmolVLA and Pi0.5 for an 11-DoF mobile manipulator and found the checkpoint with the lowest aggregate MSE was not the one that performed best on the real robot. Their expert-only variant had the best total MSE and lost the real-robot comparison across 60 trials. Their conclusion is that per-group error is a more reliable checkpoint-selection signal than total MSE on robots with heterogeneous action spaces (arXiv:2606.00253, May 2026). The older robomimic study reached the same place from the other direction: the best-validation-loss policy is 50 to 100 percent worse than the best-performing policy, so every checkpoint has to be tried on the robot.
What closed-loop evaluation actually measures
Closed loop means the environment steps. GR00T runs it as a server plus a client: the checkpoint is served over ZMQ, and a separate process owns the simulator or the robot and calls get_action in the control loop. The split exists because the simulator and the model usually want incompatible Python environments, and it is the same split the platform's inference pods use.
- 1Serve the checkpoint
One process holds the weights. --use-sim-policy-wrapper adapts the observation format for the simulator client.
bashuv run python gr00t/eval/run_gr00t_server.py \ --model-path checkpoints/GR00T-N1.7-LIBERO/libero_10 \ --embodiment-tag LIBERO_PANDA \ --use-sim-policy-wrapper - 2Run rollouts against it
The client owns the environment. Note that it runs from the benchmark's own virtualenv, not the project root's.
bashgr00t/eval/sim/LIBERO/libero_uv/.venv/bin/python gr00t/eval/rollout_policy.py \ --n-episodes 10 \ --policy-client-host 127.0.0.1 \ --policy-client-port 5555 \ --max-episode-steps 720 \ --env-name libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it \ --n-action-steps 8 \ --n-envs 5 - 3Sanity-check the harness before you trust it
Start the server with --dataset-path and no --model-path and it replays recorded actions instead of predicting them. --execution-horizon is required on this path even though the README example omits it; without it the server raises before it binds. If ReplayPolicy does not solve the task, your environment is wrong and no checkpoint will save you.
bashuv run python gr00t/eval/run_gr00t_server.py \ --embodiment-tag NEW_EMBODIMENT \ --dataset-path <DATASET_PATH> \ --execution-horizon 16
| rollout_policy.py field | Default | Why it matters |
|---|---|---|
| n_episodes | 50 | Rollouts per environment name. The LIBERO example overrides it to 10. |
| n_envs | 8 | Parallel environments. Throughput only, but it changes seeding. |
| n_action_steps | 8 | Actions consumed per inference call, the closed-loop twin of --execution-horizon. |
| max_episode_steps | 720 | Hard timeout. A slow-but-correct policy is scored as a failure. |
| terminate_on_success | not a CLI flag | RolloutConfig has no such field. run_gr00t_sim_policy pins terminate_on_success=True when it builds MultiStepConfig, so the episode stops at the first success. The MultiStepConfig dataclass default is False, which is what you get if you drive the rollout helpers yourself. |
| seed | None | Unset by default, so initial states vary run to run. When None it falls back to the GR00T_EVAL_SEED environment variable; if that is unset too, the run stays non-deterministic. |
Both harnesses accumulate success with an any reduction rather than reading the final state: GR00T does current_successes[env_idx] |= bool(env_success), LeRobot reduces its per-step success tensor with "b n -> b", "any". The shipped GR00T sim entry point then pins terminate_on_success=True, so the episode is cut at the first success; the MultiStepConfig default is False, so a harness you write yourself on top of those helpers silently scores "ever succeeded" instead. LeRobot sidesteps it differently: its rollout docstring says success "can only be True upon environment termination/truncation". Neither reports whether the block was still on the plate when the episode ended. If your task cares about the final state, you need your own criterion, not the benchmark's.
On a real arm there is no is_success
In simulation the environment hands you info["is_success"]. On an SO-100 on a desk, nothing does. Somebody watches the rollout and writes a tick or a cross on paper. That is not a gap in the tooling, it is the actual state of the field, and it is why RoboArena went to double-blind pairwise comparisons across seven institutions rather than trying to standardise an absolute success number.
LeRobot's real-hardware path is lerobot-rollout. In the current docs it is the documented way to deploy a trained policy on the arm, and depending on the strategy it also records what happened. It gives you the rollout; it does not give you a verdict.
lerobot-rollout \
--strategy.type=episodic \
--policy.path=${HF_USER}/my_policy \
--policy.pretrained_revision=010000 \
--robot.type=so100_follower \
--robot.port=/dev/ttyACM1 \
--robot.cameras="{ up: {type: opencv, index_or_path: /dev/video10, width: 640, height: 480, fps: 30}}" \
--task="Put lego brick into the transparent box" \
--duration=600Two details in that command carry more weight than they look. --policy.pretrained_revision lets you pin a specific pushed checkpoint by its step tag, so you can evaluate step 10000 and step 40000 without renaming directories. And all strategies accept --inference.type=rtc, real-time chunking, which exists because a 485 ms model cannot be run naively at 30 Hz. See action chunking and inference latency for what that is compensating for.

How many rollouts is enough
This is the question that decides whether a closed-loop number means anything, and it is almost never stated. A success rate over N rollouts is a binomial proportion. The confidence interval at small N is much wider than people's intuition, which is why two checkpoints that look 15 points apart are frequently indistinguishable.
The table below is the 95 percent Wilson score interval for an observed 70 percent success rate at different rollout counts, plus the smallest gap between two checkpoints that reaches significance in a two-proportion test around 60 percent. Because successes are whole numbers, the achievable gaps are coarse at small N: at 10 rollouts per arm the counts move in 10-point steps, and 4/10 against 8/10 still does not reach significance. These are standard statistics you can recompute in a few lines of Python, not measurements from any particular robot.
| Rollouts | Observed | 95% Wilson interval | Interval width | Smallest achievable gap that is significant vs another arm of the same size |
|---|---|---|---|---|
| 10 | 7 / 10 = 70% | 39.7% to 89.2% | 49.5 points | about 50 points |
| 20 | 14 / 20 = 70% | 48.1% to 85.5% | 37.4 points | about 30 points |
| 50 | 35 / 50 = 70% | 56.2% to 80.9% | 24.6 points | about 20 points |
| 100 | 70 / 100 = 70% | 60.4% to 78.1% | 17.7 points | about 14 points |
| 500 | 350 / 500 = 70% | 65.8% to 73.9% | 8.0 points | about 6 points |
You evaluate checkpoint A over 10 rollouts and get 8/10. You evaluate checkpoint B and get 6/10. You delete B, retrain around A, and spend the next day chasing a difference whose two-sided Fisher exact p is 0.63. At 10 rollouts per arm you cannot resolve anything smaller than 50 points. Either budget 50 rollouts per checkpoint, or stop comparing checkpoints and only ask the binary question: does this one do the task at all. See policy only works in one setup for the related failure where the rollouts were all run from the same starting pose.
The vendors are not consistent about this either, which is worth knowing before you compare published numbers. NVIDIA's LIBERO results for GR00T N1.7 are reported over 200 rollouts per suite, ranging from 189/200 on the long-horizon suite to 197/200 on the object suite. The openpi LIBERO client defaults to num_trials_per_task = 50, which over ten tasks is 500 rollouts per suite. Same benchmark, two and a half times the sample size, and a confidence interval about 1.6 times wider at 200 rollouts than at 500.
Simulation benchmarks sit in between
There is a middle option that is genuinely closed loop but does not require your robot: run the rollouts in a simulator. It removes the reset labour and the human judgement, and it introduces a new question, namely whether the simulator ranks policies the way reality does.
| LIBERO | SimplerEnv | Your own arm | |
|---|---|---|---|
| What it is | 130 tasks in four suites: Spatial, Object, Goal, and LIBERO-100 (split into 90 for pretraining, 10 for testing) | Simulated replicas of Google Robot and WidowX plus Bridge setups | One task, your cameras, your lighting |
| Closed loop | Yes | Yes | Yes |
| Success signal | Automatic | Automatic | A human with a tally sheet |
| Transfers to your task | No, different embodiment and scenes | No, different embodiment | By definition |
| Good for | Comparing methods against published numbers | Checking whether a sim eval pipeline ranks policies like the real world | The only claim that matters for your project |
| Cited protocol | GR00T: 200 rollouts per suite. openpi: 50 trials per task, so 500 per suite | Visual matching and variant aggregation, scored with MMRV and Pearson correlation | Whatever you write down |
SIMPLER is the interesting one methodologically, because it is a benchmark about benchmarks. Li et al. (2024) build simulated environments by green-screening simulated assets onto real backgrounds and matching object textures, then score the evaluation pipeline itself with two metrics: Mean Maximum Rank Violation and the Pearson correlation coefficient, both computed between simulated and real policy performance. The repository ships mean_maximum_rank_violation and pearson_correlation plus a table of real-world performance so you can score your own offline eval approach the same way. If you are building any kind of proxy evaluation, that is the honest way to justify it: show that it ranks policies the way the robot does.
LIBERO runs a simulated Franka Panda. SimplerEnv runs a simulated Google Robot or WidowX. A 97 percent LIBERO score for a checkpoint fine-tuned on LIBERO says the architecture works; it says nothing about your five-joint arm, your task, or your 50 episodes. Cross-embodiment numbers are useful for picking a starting model, which is what the arena and the head-to-head comparisons are for. They are not a substitute for rollouts on your own hardware.
Which test to run when
The decision is not open loop versus closed loop as a philosophy. It is: what am I trying to rule out right now, and what is the cheapest test that rules it out. Open loop is a smoke test with a numeric output. Closed loop is the acceptance test.
| Situation | Run | Why |
|---|---|---|
| The run just finished and you do not know if the checkpoint loads | Open loop, one trajectory | Seconds, and it catches a corrupt or empty checkpoint immediately |
| Normalisation statistics may be wrong | Open loop | A normalisation mismatch shows as predictions in the wrong numeric range, obvious in the plot |
| Camera keys or modality config may be mismatched | Open loop | Wrong image feed produces a large, flat error on every joint |
| Choosing between 8 checkpoints from one run | Open loop to shortlist 2 or 3, then closed loop | Open loop is too cheap to skip and too unreliable to decide |
| Deciding whether to record more episodes | Closed loop | Only rollouts tell you whether failures are data coverage or model capacity |
| Comparing two policy architectures | Closed loop, 50 rollouts each | Anything smaller cannot resolve the difference |
| Reporting a result to anyone else | Closed loop, with N stated | A success rate without a rollout count is not a result |
| The policy freezes mid-motion | Closed loop, watch the video | Open loop cannot represent the failure at all |
- Runs on the same GPU that just trained the model, in seconds, with no robot and no reset labour.
- Catches the whole class of plumbing bugs: wrong embodiment tag, wrong normalisation statistics, wrong camera keys, dead checkpoint.
- Produces a plot per joint, so you can see which dimension is wrong instead of only that something is.
- Cheap enough to run on every saved checkpoint, which gives you a shape over training steps.
- Requires no success criterion, so it works on tasks where success is hard to define.
- The number does not predict task success, and the failure is documented in both robomimic and recent VLA fine-tuning work.
- Averaging across joints lets a broken gripper hide behind five well-tracked arm joints.
- Unnormalized MSE has no absolute scale, so it cannot be compared across datasets or projects.
- GR00T's implementation re-anchors to the recorded trajectory every --execution-horizon steps, so it never sees compounding drift.
- Defaults to one trajectory, which is a sample size of one unless you pass a list.

That latency column changes how closed-loop evaluation feels in practice. ACT at 20 ms per action step runs comfortably above 30 Hz, so a rollout looks like a normal robot motion. Pi0.5 at 485 ms per action step only works because the model emits a chunk and the controller plays it out. That is why real-time chunking exists, and why an evaluation that ignores the control rate is measuring the wrong thing. GR00T N1.7 sits at 152 ms, SmolVLA at 245 ms.
Running both, two ways
Everything above runs on your own machine, provided you have the card for it. GR00T N1.7 fine-tuning and serving want an 80 GB class GPU; ACT and SmolVLA fit on 24 GB. The friction is not the commands, it is the three environments (trainer, simulator, robot client) and the dataset version dance.
- 1Get the dataset into the version the loader wants
GR00T needs LeRobot v2.0 or v2.1. A v3.0 dataset crashes the loader, so convert down first.
bashcd scripts/lerobot_conversion uv venv && source .venv/bin/activate uv pip install -e . --verbose python convert_v3_to_v2.py --repo-id <DATASET_REPO_ID> - 2Open loop on a held-out episode
Pass trajectory ids you did not train on. Run it on more than one.
bashuv run python gr00t/eval/open_loop_eval.py \ --dataset-path ./my_so100_data \ --embodiment-tag NEW_EMBODIMENT \ --model-path /tmp/run/checkpoint-20000 \ --traj-ids 47 48 49 \ --execution-horizon 16 - 3Serve the shortlisted checkpoint
One process holds weights, one owns the hardware.
bashuv run python gr00t/eval/run_gr00t_server.py \ --embodiment-tag NEW_EMBODIMENT \ --model-path /tmp/run/checkpoint-20000 \ --device cuda:0 \ --host 0.0.0.0 --port 5555 - 4Rollout and count
On simulation, use rollout_policy.py. On a real arm, use LeRobot and a tally sheet. Fix N before you start, and do not stop early because the numbers look good.
bashlerobot-rollout \ --strategy.type=episodic \ --policy.path=outputs/train/act_so100/checkpoints/040000/pretrained_model \ --robot.type=so100_follower \ --robot.port=/dev/ttyACM1 \ --task="Put the cube in the bowl" \ --duration=600
If the server says ZMQError: Address already in use, something else holds 5555; pass --port. And note that --denoising-steps passed to the open-loop client is ignored when you are talking to a remote server. It is only applied when the client loads the checkpoint itself. Set it on run_gr00t_server.py instead, or you will compare two runs that used different sampling without knowing it.
The platform removes the GPU procurement and the serving setup, and it does not remove the counting. The training form picks a model, a dataset and hyperparameters; the backend rents a GPU on a spot market sized by required VRAM, runs the trainer, and writes checkpoints to object storage. For inference, /api/inference/pod auto-provisions a pod that serves the policy, and your local robot client talks to that endpoint. Pods carry an idle watchdog and destroy themselves, so a forgotten eval session does not keep billing.
- Dataset comes from the public directory, a Hugging Face repo id, or your own machine via the desktop client.
- Base checkpoints are the vendors' own:
nvidia/GR00T-N1.7-3B,nvidia/GR00T-N1.5-3B,lerobot/pi05_base. ACT has no base model at all, since it only exists after training on your task. - GR00T N1.7, GR00T N1.5 and Pi0.5 are cloud-only here. SmolVLA and ACT also run locally.
- The same operations are available from the CLI and to agents through the MCP server, which is the practical way to script an eval sweep.
There is no automatic success detector for your task. Nothing on this platform watches a rollout and decides whether the cube landed in the bowl. You get the arm, the pod and the recording; the tally sheet is yours. That is the honest position, and it is the same position every other tool in this space is in on real hardware.
If you do not own an arm yet, the live queue streams a physical SO-100 with no signup, and the try page lays out the three ways to start. For the training side of the loop, train your first policy and run your first policy are the short versions.
A loop that survives contact with a real week
Here is the sequence that wastes the least time, assuming one task, one arm, and a fine-tuning run that saved checkpoints along the way. It is built around the fact that open-loop evaluation is nearly free and closed-loop evaluation is not.
- 1Replay one recorded episode on the arm first
Before evaluating any model, replay a recorded episode. If the recorded actions do not reproduce the task, your calibration or your dataset is wrong and every evaluation after this is noise.
bashlerobot-replay \ --robot.type=so100_follower \ --robot.port=/dev/ttyACM1 \ --robot.id=my_follower \ --dataset.repo_id=${HF_USER}/my-task \ --dataset.episode=0 - 2Open loop on three held-out episodes, every checkpoint
Same --execution-horizon and same --denoising-steps for all of them, or the comparison is meaningless. Look at the per-joint plots, not only the scalar.
bashfor CKPT in 5000 10000 15000 20000; do uv run python gr00t/eval/open_loop_eval.py \ --dataset-path ./my_so100_data \ --embodiment-tag NEW_EMBODIMENT \ --model-path /tmp/run/checkpoint-$CKPT \ --traj-ids 47 48 49 \ --execution-horizon 16 \ --save-plot-path ./plots/ckpt-$CKPT.jpeg done - 3Throw away only the obviously broken ones
Use open loop to eliminate, not to rank. A checkpoint whose predictions are in the wrong numeric range or flat on the gripper dimension is dead. Two checkpoints within a few percent of each other are tied, whatever the decimal places say.
- 4Closed loop on the survivors, fixed N, fixed start states
Pick N before you start. Twenty rollouts is enough to answer does this work at all. Fifty is the smallest number that lets you rank two candidates. Vary the object start pose deliberately across the rollouts, otherwise you are measuring one initial condition.
- 5Write down N, the successes, and the failure mode
Not just 14/20. Note what the eight failures looked like: overshoot, early gripper close, freeze, wrong object. That distribution is what tells you whether to record more episodes or change the model, and it is the thing open-loop MSE can never give you.
On the A100 or H100 tier that GR00T N1.7, GR00T N1.5 and Pi0.5 need, a run is 3 to 6 hours at 1.20 to 2.00 USD per hour, roughly 4 to 12 USD. On the 24 GB tier that SmolVLA and ACT need, it is 2 to 5 hours at 0.30 to 0.60 USD per hour, roughly 1 to 3 USD. Open-loop evaluation of every checkpoint from one run costs a few minutes of that same GPU. Fifty closed-loop rollouts costs an hour of your own attention and cannot be parallelised on a single arm. Budget the human time, not the GPU time, and see pricing for the run side.

Where neither test helps, and what to do instead
Both tests share a blind spot: they assume the thing you are measuring is the policy. Often it is not. If inference runs across the public internet, a closed-loop rollout measures your network. The control loop on these models is 20 ms for ACT up to 485 ms for Pi0.5 per action step, and adding round trips on top of that turns a working policy into a hesitant one. Remote inference is viable for slow pick-and-place and not for fast reactive motion. For an evaluation you intend to trust, put the inference next to the servos.
The second blind spot is the dataset. If the recorded episodes contain a systematic defect, a wrist camera that shows a frozen frame, or an action stream sampled on a different raster than the images, then the open-loop error is measuring the defect and the closed-loop failures have a data cause that no checkpoint choice will fix. That check belongs before evaluation, not after it: see collecting high-quality VLA training data and, for the underlying dataset mechanics, episodes and the dataset docs.
GR00T's fine-tuning entry point, launch_finetune.py, exposes no seed. LeRobot's default seed is 1000. That means two GR00T runs with identical hyperparameters produce different weights, and therefore different evaluation numbers. If you are comparing a hyperparameter change, the difference you measure includes run-to-run variance you cannot remove. Treat small gaps between two GR00T runs the way you treat small gaps between two 10-rollout evaluations: as nothing. More on this in the training docs.
Finally, if you want context on how the field reports these numbers at all, the arena collects 85 VLA models and 332 benchmark results with every value linked to its paper or model card. Reading two or three of those source pages is the fastest way to see how differently people define success, and how rarely the rollout count is printed next to the percentage. Related reading on this blog: vision-language-action models for the model class, and the SO-100 complete guide for the hardware end of the loop.
Train it, then actually test it
Pick a model and an arm and get the exact guide for that combination, with the defaults the trainer really sends. The GPU is rented per run and the pod destroys itself when idle.
Open the training guidesCan I use open-loop MSE to pick the best checkpoint?▾
Only to eliminate, not to rank. The robomimic study found the best-validation-loss policy is 50 to 100 percent worse than the best-performing policy, and a 2026 VLA fine-tuning study found the checkpoint with the lowest total MSE lost the real-robot comparison. Use open-loop error to throw out checkpoints that are clearly broken, then run rollouts on the two or three that survive.
Why is GR00T's open-loop eval called open loop if it re-reads the recorded observation?▾
Because the environment never steps: the policy's action has no effect on what it sees next. It is open loop in the control sense. But it re-anchors to the recorded trajectory every --execution-horizon steps, default 16, so it is teacher-forced at chunk boundaries and cannot show error compounding across a whole episode. That is worth knowing before you read too much into a low MSE.
How many rollouts do I need before a success rate means anything?▾
For the binary question does this work at all, 20 rollouts is usually enough: an observed 14/20 has a 95 percent Wilson interval of about 48 to 86 percent, which is comfortably above chance for most tasks. For ranking two checkpoints, 50 per arm is the practical minimum, and even then you can only resolve gaps of roughly 20 points. Published LIBERO numbers use 200 to 500 rollouts per suite for this reason.
Do LIBERO or SimplerEnv scores tell me anything about my SO-100?▾
They tell you the architecture works and roughly how it ranks against alternatives. They do not transfer to your arm: LIBERO runs a simulated Franka Panda, SimplerEnv runs a simulated Google Robot or WidowX. Use them to choose a starting model, then evaluate on your own hardware. The comparison pages and the arena exist for exactly that first step.
What is the fastest check that a fresh checkpoint is not broken?▾
Run open-loop evaluation on one held-out trajectory and open the plot. Predictions in a completely wrong numeric range point at normalisation statistics. A flat line on the gripper dimension points at the gripper never being learned. A large error on every joint at once usually means the wrong embodiment tag or a camera key mismatch. All three are visible in under a minute, and none of them need a robot.
Does AY-Robots score my rollouts automatically?▾
No. The platform provisions the GPU, runs the trainer, serves the checkpoint from an inference pod and connects your robot client to it. Deciding whether a rollout succeeded is still a human watching the arm. There is no automatic success detector for an arbitrary real-world task on this platform, or on any of the open-source tooling for real hardware.
Sources
- NVIDIA Isaac-GR00T repository, README (open-loop and closed-loop evaluation sections)
- Isaac-GR00T: gr00t/eval/open_loop_eval.py, ArgsConfig defaults and MSE/MAE computation
- Isaac-GR00T: gr00t/eval/rollout_policy.py, RolloutConfig defaults and success accumulation
- Isaac-GR00T LIBERO example: benchmark results and client-server rollout commands
- LIBERO: Benchmarking Knowledge Transfer for Lifelong Robot Learning (Liu et al., 2023)
- LIBERO repository: task suites and evaluation script
- Evaluating Real-World Robot Manipulation Policies in Simulation (Li et al., 2024)
- SimplerEnv repository: visual matching, variant aggregation, MMRV and Pearson correlation
- What Matters in Learning from Offline Human Demonstrations for Robot Manipulation (Mandlekar et al., 2021)
- robomimic study page: validation loss is not a reliable stopping criterion
- Per-Group Error, Not Total MSE: Fine-Tuning Vision-Language-Action Models for 11-DoF Mobile Manipulation (Montagut Bofi et al., 2026)
- RoboArena: Distributed Real-World Evaluation of Generalist Robot Policies (Atreya et al., 2025)
- LeRobot docs: imitation learning on real robots, lerobot-rollout and lerobot-replay
- LeRobot: lerobot_eval.py, rollout loop and aggregated pc_success metric
- openpi LIBERO client: num_trials_per_task and per-suite max_steps defaults
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started