
How many rollouts a robot benchmark really needs, how to randomise starting conditions reproducibly, what to freeze between policies, and how to report a success rate someone can check.
A benchmark is not a number you produce at the end of a training run. It is a procedure you write down before you run anything, that another person could follow and get roughly the same answer. Most people skip the writing-down part, run twelve rollouts, get eight successes, and tell everyone the policy is at 67 percent. That number is close to meaningless, and this article is about why, and what to do instead.
The good news is that an honest benchmark for one task on one SO-100 is mostly bookkeeping: a fixed list of starting conditions, a written success rule, a frozen configuration, and the habit of reporting raw counts instead of a percentage. The expensive part is trial count, so that is where this starts.
What you need to know
- •Ten trials is not a measurement. At an observed 70 percent success rate the 95 percent Wilson interval on 10 trials runs from 39.7 to 89.2 percent. On 50 trials it is 56.2 to 80.9 percent.
- •Comparing two policies costs far more than measuring one. Resolving a real 60 vs 80 percent gap at 80 percent power takes about 81 trials per policy; 70 vs 80 percent takes about 292.
- •Published protocols are smaller than people assume: ACT ran 25 real-world trials per task, pi0.5 ran 10 evaluations per task, RoboChallenge runs 10 rollouts per task. All of them say so in the paper.
- •Pair the trials. RoboArena requires both policies to run back to back from closely matched initial conditions, and on the McNemar numbers below that cuts the rollout budget by roughly a third.
- •Randomise the starting conditions, but from a written list, with a seed. Randomisation you cannot reproduce is noise you cannot audit.
- •Report counts, not percentages: 18/25, not 72 percent. On 25 trials the metric can only land on multiples of 4 percentage points.
- •Freeze the inference configuration alongside the checkpoint. Image preprocessing, chunk size, control rate and where inference physically runs all change the result.
Why a leaderboard number cannot answer your question
The AY-Robots arena lists 85 vision-language-action models with 332 benchmark results, and every value links back to the paper or model card it came from. That is useful for orientation. It cannot tell you what a fine-tuned checkpoint will do on your table, with your lighting, your gripper and your task. Different embodiment, different objects, different success rule, different operator.
The SIMPLER paper makes the same point from the other direction. Ranking six open-source policy checkpoints on Google Robot tasks, validation mean squared error reached an average Pearson correlation of only 0.308 with real-world performance, while their visual-matching simulated evaluation reached 0.924. The loss curve you watch during training is a poor predictor of the ranking you care about, which is why you have to run the robot.

It also helps to see how small real published protocols are. People assume the numbers in VLA papers rest on thousands of rollouts. In the real-robot sections they usually do not.
| Protocol | Trials | Setting | Reported as |
|---|---|---|---|
| ACT / ALOHA (Zhao et al., 2023) | 25 per task, one seed | Real robot, 6 tasks | Success rate per task and per sub-stage |
| ACT / ALOHA, simulated tasks | 50 per seed, 3 seeds | MuJoCo | Mean over seeds |
| OpenVLA on LIBERO | 50 rollouts per task, 10 tasks, 3 seeds | Simulation, 500 rollouts per seed per suite | LIBERO-Spatial 84.7 +/- 0.9 percent, standard error over the 3 seeds |
| pi0.5 (Physical Intelligence, 2025) | 10 evaluations per task per policy | Real homes and mock homes, 12 locations | Percent of rubric points, partial credit |
| SIMPLER, pick coke can | 75 (3 orientations x 25 grid positions) | Real and simulated, paired | Success rate plus MMRV and Pearson r against real |
| RoboChallenge Table30 | 10 rollouts per task, 30 tasks | Real robots, online submission | Success rate plus a 0 to 100 progress score |
| RoboArena | more than 600 pairwise episodes, 7 policies | DROID arms at 7 institutions | Task-aware ranking from double-blind pairwise preferences |
Two things stand out. First, the per-condition sample sizes are small, often 10 to 25. Second, almost none of these report a confidence interval on a single policy's success rate. PhAIL, a 2026 preprint that benchmarks four public VLAs on a Franka FR3, opens by describing the field in exactly those terms: real-world VLA evaluation still rests on binary success rate at a fixed timeout with 25 or fewer rollouts per condition, almost always without confidence intervals or paired statistical comparison. That is the paper's own framing of the status quo rather than a counted survey, but it matches the table above. Copy the field's habits and you inherit the field's uncertainty.
How many trials you actually need
Success on a rollout is a coin flip with an unknown bias. That makes the statistics simple and unforgiving. The table below is a 95 percent Wilson score interval for an observed 70 percent success rate at different trial counts. Wilson rather than the textbook Wald interval because Brown, Cai and DasGupta showed in Statistical Science in 2001 that the Wald interval's coverage is erratic and recommended Wilson or Jeffreys for small samples; the NIST/SEMATECH e-Handbook gives the same formula in section 7.2.4.1.
| Trials | Successes | Observed | 95% Wilson interval | Interval width |
|---|---|---|---|---|
| 10 | 7 | 70.0% | 39.7% to 89.2% | 49.5 pp |
| 20 | 14 | 70.0% | 48.1% to 85.5% | 37.3 pp |
| 25 | 18 | 72.0% | 52.4% to 85.7% | 33.3 pp |
| 30 | 21 | 70.0% | 52.1% to 83.3% | 31.2 pp |
| 50 | 35 | 70.0% | 56.2% to 80.9% | 24.6 pp |
| 100 | 70 | 70.0% | 60.4% to 78.1% | 17.7 pp |
| 200 | 140 | 70.0% | 63.3% to 75.9% | 12.6 pp |
| 500 | 350 | 70.0% | 65.8% to 73.9% | 8.0 pp |
# pip install statsmodels
from statsmodels.stats.proportion import proportion_confint
for n in (10, 20, 25, 30, 50, 100, 200, 500):
k = round(0.70 * n)
lo, hi = proportion_confint(k, n, alpha=0.05, method="wilson")
print(f"{k}/{n} [{lo*100:5.1f}, {hi*100:5.1f}] width {100*(hi-lo):4.1f} pp")
10 successes out of 10 gives a 95 percent Wilson interval of 72.2 to 100 percent. 25 out of 25 gives 86.7 to 100 percent. A flawless short demo is compatible with a policy that fails one attempt in four. This is the single most common way a demo video misleads the person who recorded it.
Comparing two policies costs much more than measuring one
Measuring one policy roughly is cheap. Deciding which of two checkpoints is better is not, because you are now stacking two noisy estimates. The numbers below are for a standard two-proportion test, two-sided, alpha 0.05, 80 percent power, independent trials for each policy.
| True gap you want to detect | Trials per policy | Total rollouts |
|---|---|---|
| 50% vs 70% | 93 | 186 |
| 60% vs 80% | 81 | 162 |
| 70% vs 90% | 60 | 120 |
| 80% vs 90% | 195 | 390 |
| 70% vs 80% | 292 | 584 |
| 50% vs 60% | 388 | 776 |
Read the last two rows again. A ten-point improvement, the size most fine-tuning changes actually produce, needs hundreds of rollouts per policy with independent trials. If your budget is 20 rollouts per checkpoint, you cannot resolve ten points, and your notes should say so.
The mirror-image question is more useful in practice: how often will a short evaluation rank the worse policy higher? Suppose the true rates are 60 and 70 percent, and you run n trials of each.
| Trials per policy | Worse policy scores higher | Exact tie | Fails to show the right order |
|---|---|---|---|
| 10 | 23.9% | 16.6% | 40.5% |
| 20 | 20.2% | 10.6% | 30.7% |
| 25 | 18.5% | 8.9% | 27.5% |
| 50 | 12.3% | 4.8% | 17.1% |
| 100 | 5.9% | 2.0% | 7.9% |
On 25 trials one rollout is 4 percentage points; on 10 trials it is 10 points. Every real-world success rate in Table II of the ACT paper is a multiple of 4, ACT's own row running 100, 96, 92, 84, 72, 64, 40 and 20, because they ran one seed and 25 trials. Reporting 18/25 instead of 72 percent makes that resolution visible for free.
Define success before you run anything
Half the disagreements about a benchmark are definitional, not statistical. Write the success rule down first, in a form an unfamiliar person could apply from the video alone, and never edit it after seeing results.
- Terminal state: the cube is inside the bin footprint and the gripper has released it. Not 'roughly in the bin'.
- Tolerance: fully inside the printed rectangle counts, touching the rim does not.
- Time limit: 60 seconds of wall clock, or a fixed number of control steps. Say which, because the two diverge when inference latency changes.
- Retries: does a second grasp attempt after a slip still count as success? Decide before, not after.
- Abort conditions: what stops the rollout early and how that outcome is scored. Hitting a joint limit is a failure, not a discarded trial.
- Who scores it: one named person, or a video reviewed later by someone blind to which policy ran.
Binary success throws away most of what you saw
A binary outcome on 25 trials is a low-bandwidth measurement, and every serious recent protocol adds partial credit. RoboChallenge splits each task into stages worth 10 progress points per rollout, runs 10 rollouts per task so a task totals 100 points, deducts 0.5 progress points for each retry, and terminates the rollout once the number of successive failed retries exceeds four. pi0.5 scores against a rubric that roughly corresponds to the percentage of steps completed, so placing half the dishes in the sink scores around 50. RoboArena asks evaluators for a continuous progress score from 0 to 100 alongside the binary preference.
| Metric | What it captures | Cost | When to use it |
|---|---|---|---|
| Binary success | Did the task complete | Free | Always. It is the number people compare. |
| Stage progress (0 to 10) | How far it got before failing | Define stages once | Long-horizon tasks where most failures are late |
| Retry count | Fumbling that still succeeds | One tally per rollout | Tasks where a clumsy success is not a good success |
| Time to success | Speed, and whether it hesitates | A stopwatch or a log timestamp | Comparing a local run against a remote pod |
| Failure category | Where the policy breaks | A short tag per rollout | Choosing what to fix next |
The failure category is the field you will thank yourself for. Tags like 'missed grasp', 'grasped then dropped', 'wrong object', 'froze' or 'collided' turn 25 rollouts into a debugging plan, and the failure-mode pages split along roughly those lines: a policy that freezes mid-motion and a gripper that does not close are different problems even when both show up as a zero in the success column.

Randomisation you can reproduce
There is a real tension here. If every trial starts from an identical cube position you are measuring one point in the task distribution, and the number will not survive a slightly different Tuesday. If every trial starts wherever you happened to drop the cube, the evaluation is not repeatable and, worse, it is manipulable. The RoboChallenge team measured this: they compared experienced testers who had collected the training data, ignorant testers who only read the task description, and adaptive testers who were the model authors and placed objects strategically based on how previous runs had gone. Their summary of real-machine testing is blunt: with the same props, task and model, the measured success rate can change even from 0 to 100 percent.
The fix is a written condition grid. Enumerate the factors you want to vary, take the product, and sample from that list with a fixed seed. SIMPLER does this explicitly: pick coke can is 3 orientations times 25 marked grid positions for 75 trials; move object near object is 5 object triplets times 2 triangle patterns times 6 role assignments for 60 trials; the drawer tasks are 9 robot base positions times 3 drawers times 2 directions for 54 trials. Nothing is left to the operator's judgement in the moment.
- 1Write the condition grid
Pick two or three factors that matter for your task and enumerate their levels. Tape a grid onto the work surface and label the cells, so a position is a name and not a guess.
pythonimport itertools, random positions = ["A1", "A2", "B1", "B2", "C1", "C2"] # taped cells on the mat orientations = ["flat", "upright"] distractor = [False, True] conditions = list(itertools.product(positions, orientations, distractor)) # 24 cells trials = conditions * 2 # 48 trials random.Random(1000).shuffle(trials) # fixed seed print("trial,position,orientation,distractor") for i, (pos, ori, dis) in enumerate(trials, start=1): print(f"{i:03d},{pos},{ori},{int(dis)}") - 2Print the sheet and follow it
Print the 48 lines and work down the list. The order is randomised so that anything drifting across the session, servo temperature, daylight through a window, your own fatigue, is spread across conditions instead of piling into the last ten trials.
bashpython make_trials.py > trials_2026-08-24.csv head -4 trials_2026-08-24.csv # trial,position,orientation,distractor # 001,B2,flat,1 # 002,A1,upright,0 # 003,C1,flat,0 - 3Reproduce the scene, do not eyeball it
RoboChallenge superimposes a reference image on the live camera stream and asks the tester to move objects until the images match. You can do the same with a still frame and any camera viewer: it is the cheapest reproducibility tool in this whole article.
bash# capture the reference frame for one condition, once ffmpeg -f avfoundation -framerate 30 -i "0" -frames:v 1 ref_B2_flat.png # later, overlay the reference at 50 percent on the live view while you reset the scene ffplay -f avfoundation -framerate 30 -i "0" \ -vf "movie=ref_B2_flat.png[r];[in][r]blend=all_mode=average" - 4Log the trial before you run it
Write down the condition and the checkpoint identifier before the rollout starts, not after you have seen the outcome. This is the difference between a record and a memory.
json{"trial": 17, "policy": "act_so100_ckpt100000", "grid_seed": 1000, "position": "B2", "orientation": "flat", "distractor": true, "operator": "pk", "started_at": "2026-08-24T14:05:11Z", "outcome": "fail", "stage_reached": 2, "stages_total": 4, "retries": 1, "time_to_success_s": null, "timeout_s": 60, "failure_tag": "grasped_then_dropped", "notes": "caught the rim, slipped"}
Publish the seed with the results. LeRobot uses 1000 as its default seed in EvalPipelineConfig, and OpenVLA's LIBERO evaluation script defaults to --seed 7. A seed that lives only in your shell history is not a protocol, and re-running the same trial order later is how you separate a real regression from a bad afternoon.
What to hold fixed
Everything not in the condition grid must be pinned, written down and re-checked between policies. None of the items below show up in the success column when they go wrong; they just quietly make the comparison meaningless. Two of them, the action chunking settings and the inference latency you actually run at, change the controller itself rather than the weights.
| Hold fixed | Why it moves the number | How you pin it |
|---|---|---|
| Checkpoint identity | Two checkpoints from one run behave differently | Full path plus step count in every trial record |
| Normalisation statistics | Mismatched stats produce plausible but wrong actions | Ship the stats file with the checkpoint, never regenerate at eval time |
| Image preprocessing | OpenVLA needs --center_crop True at eval because it trained on a random 90 percent-area crop of every sample | Record the exact preprocessing flags next to the checkpoint |
| Action chunk settings | ACT defaults to chunkSize 100 and nActionSteps 100 here; changing either changes the controller | Freeze both, and report them |
| Control rate | The same policy at a different loop rate is a different controller | Log the achieved rate, not the intended one |
| Where inference runs | 20 ms locally versus a public-internet round trip is a different system | State local or remote pod in the report |
| Camera pose and lens | A bumped wrist camera invalidates every trial after it | Photograph the rig at the start and end of the session |
| Lighting | Daylight drifts across a session | Blinds down, one lamp, fixed exposure and white balance |
| Instruction string | The language token stream is an input | Copy-paste it, never retype it |
| Battery and supply | STS3215 servos on the SO-100 run at 7.4 V and torque changes as supply sags | One bench supply, checked before the session |
AY-Robots can auto-provision a cloud GPU pod that serves your policy to the robot client. That is a fine way to run a policy without a local GPU, but the control loop is already 20 to 485 ms per action step depending on the model, and public-internet round trips land on top of that. A benchmark run against a remote pod and a deployment run locally are two different systems. Measure the one you intend to ship, and if you must measure both, treat them as two rows in the table rather than one number. This is a real limit: remote inference is viable for slow pick and place and not viable for fast reactive motion.
One thing you cannot hold fixed is worth naming. The GR00T N1.7 fine-tuning entry point, the tyro CLI in Isaac-GR00T's launch_finetune.py, exposes no seed argument, so GR00T runs are not bit-for-bit reproducible and trainer variation becomes part of the noise in your comparison. LeRobot does expose a seed and defaults it to 1000, so ACT, SmolVLA and Pi0.5 runs can at least be repeated. If you are comparing two GR00T checkpoints trained on the same data, some of the difference you measure is the trainer, not your change.
Pair the trials and blind the operator
The cheapest way to buy statistical power is to stop running two independent batches. RoboArena is explicit about it: the evaluator arranges a scene, is handed two anonymous policy endpoints, runs A and B back to back, and is required to closely match the initial conditions within that pairwise comparison, though conditions may change freely between comparisons. pi0.5 does the same by holding the set of items constant for both policies within a comparison. Pairing removes scene-to-scene variance, and a paired test then only looks at the trials where the two policies disagreed.
| Design | To detect a 20-point gap at 80% power | Total rollouts |
|---|---|---|
| Independent batches, 60% vs 80% | 81 per policy | 162 |
| Paired, policies disagree on 24% of trials | 45 paired trials | 90 |
| Paired, policies disagree on 30% of trials | 57 paired trials | 114 |
| Paired, policies disagree on 40% of trials | 77 paired trials | 154 |
| Paired, policies disagree on 50% of trials | 96 paired trials | 192 |
The paired numbers come from the McNemar sample-size formula and depend on how often the two policies differ on the same scene. Two similar checkpoints agree often, so pairing wins big. Either way you avoid the failure mode where policy A was evaluated in the morning and policy B after the sun moved.
- Removes scene, lighting and operator drift from the comparison, because both policies see the same scene within seconds of each other.
- Needs meaningfully fewer rollouts for the same power when the policies are similar, which is the usual case when you are iterating.
- Makes blinding easy: the operator resets the scene, a script picks which endpoint answers, and the operator does not know which one ran.
- Gives you a per-scene record, so you can watch the two videos side by side for the trials where they disagreed.
- Does not give a clean absolute success rate for either policy, because the paired scenes are not a random sample of anything.
- Requires swapping checkpoints between every pair, which on cloud inference means either two live endpoints or a slow reload.
- Only compares the policies you paired. Adding a third checkpoint later means re-running, not appending.
- Scene reproduction has to be genuinely good, or you have paid the cost of pairing without getting the variance reduction.
RoboChallenge's comparative protocol has the tester prepare the initial state, then a randomly selected model is called, and the tester oversees the run without knowing which model is running. They adopted it because they watched model authors place objects strategically based on previous results. You are the model author. A ten-line wrapper that shuffles which of two endpoints answers is enough, and it costs you nothing.
Two ways to run this
Everything above works with a printed sheet, a text file and open-source tooling. If your task also exists as a simulated gym environment, LeRobot ships a runner that does the bookkeeping. Read on the main branch at version 0.6.2 on 2026-08-24, lerobot-eval defaults to n_episodes 50, auto-tunes batch_size from the CPU count, seeds at 1000 through EvalPipelineConfig, and writes an aggregated pc_success plus a per-episode record carrying each episode's seed. Those are upstream defaults; the trainer on this platform pins lerobot 0.5.1 for Pi0.5, so check the version you actually run before quoting a default.
# the evaluation extra alone is not enough: you also need the policy extra
# (e.g. lerobot[pi]) and, for a simulated env, the env extra (e.g. lerobot[pusht])
pip install 'lerobot[evaluation,pusht]'
lerobot-eval \
--policy.path=outputs/train/my_run/checkpoints/100000/pretrained_model \
--env.type=pusht \
--eval.n_episodes=200 \
--eval.batch_size=50 \
--seed=1000 \
--policy.device=cuda
This is the honest gap in the open-source stack. lerobot-eval and OpenVLA's run_libero_eval.py both assume a simulator that can reset itself. On a physical arm the reset is a human hand, and no tool automates that. Budget the wall-clock time: 100 paired rollouts at 60 seconds each plus resets is most of a working day.
# the simulated reference protocol, for comparison
git clone https://github.com/openvla/openvla.git
python experiments/robot/libero/run_libero_eval.py \
--model_family openvla \
--pretrained_checkpoint openvla/openvla-7b-finetuned-libero-spatial \
--task_suite_name libero_spatial \
--center_crop True
# defaults: num_trials_per_task=50, seed=7, num_steps_wait=10
# 10 tasks x 50 rollouts = 500 trials per suite, averaged over 3 seeds
- Write the success rule and the condition grid in a file, and commit it before the first rollout.
- Generate the randomised trial sheet with a fixed seed and print it.
- Freeze the checkpoint, the normalisation statistics and every preprocessing flag.
- Run paired A/B rollouts with the operator blind to which endpoint answered.
- Record counts, stage progress, retries and a failure tag per trial.
- Report k/n with a Wilson interval, and publish the seed and the sheet.
The platform removes the parts around the benchmark, not the benchmark itself. Training writes checkpoints to object storage, so a checkpoint you evaluated is still there next month under the same identifier. Inference can auto-provision a GPU pod that serves the policy to the local robot client, with an idle watchdog that destroys the pod so nothing bills quietly. The CLI and the MCP server expose the same operations to a terminal and to an agent, which is how you script the checkpoint swap between paired trials.
| Benchmark step | What the platform does | What you still do |
|---|---|---|
| Get comparable checkpoints | Trains five policy families on the same dataset with recorded hyperparameters | Decide which pair is worth 100 rollouts |
| Reproduce a checkpoint | Checkpoints persist in object storage under a run identifier | Record which one ran in each trial |
| Serve the policy | Auto-provisioned GPU pod with an idle watchdog, or local for SmolVLA and ACT | Decide local or remote, and report which |
| Vary the conditions | Nothing | Build and follow the condition grid yourself |
| Score the rollout | Nothing | Watch it, apply your written rule, tag the failure |
| Compute the statistics | Nothing | Wilson interval in five lines of Python |
Cost and setup, not measurement. A run on the RTX 4090 tier for SmolVLA or ACT is 2 to 5 hours at 0.30 to 0.60 USD per hour, roughly 1 to 3 USD. The A100 or H100 tier for GR00T N1.7, GR00T N1.5 and Pi0.5 is 3 to 6 hours at 1.20 to 2.00 USD per hour, roughly 4 to 12 USD. That means training three seeds of the same configuration to measure trainer variance is a real option rather than a budget conversation. See pricing for the current tiers.
Be clear about the limit: there is no evaluation scheduler, no automatic success detector and no statistics panel here. If you want a benchmark, you build it with the sheet, the stopwatch and the five lines of Python above. The platform's contribution is that the checkpoints, the datasets in the dataset directory and the training configuration stay identifiable while you do it.
Reporting a result someone else can check
A benchmark result is a claim, and a claim needs enough attached information for a reader to decide whether to believe it. If you cannot fill in a row, say so rather than letting the reader assume.
| Field | Example | Why it belongs |
|---|---|---|
| Raw counts | 18/25 and 21/25 | Percentages hide the trial count and the granularity |
| Interval | 72.0% (95% Wilson 52.4 to 85.7) | Says what the number cannot rule out |
| Arm and serial | SO-100, unit 2, calibrated 2026-08-20 | Two arms of the same model are not the same arm |
| Checkpoint | act_so100 run 4a1f, step 100000 | Identifies the exact weights |
| Training seed | 1000, or 'GR00T, no seed exposed' | Names the reproducibility you have and the kind you do not |
| Condition grid and seed | 6 positions x 2 orientations x 2 distractors, shuffle seed 1000 | Lets someone regenerate your trial order |
| Success rule | Cube fully inside the rectangle, gripper open, within 60 s | Ends the definitional argument before it starts |
| Inference location | Local, 20 ms per action step | A remote pod measures a different system |
| Operator and blinding | One operator, blind to endpoint | Says whether the number could have been nudged |
| Session and date | 2026-08-24, 14:00 to 17:30, blinds down | Drift within and between sessions is real |
Two conventions from outside robotics are worth stealing. Agarwal and colleagues, writing about deep reinforcement learning in 2021, argued that with few runs the interquartile mean with stratified bootstrap confidence intervals beats a bare mean, and that performance profiles beat single numbers. And OpenVLA reports 84.7 +/- 0.9 percent on LIBERO-Spatial, where the interval is a standard error across three training seeds of 500 rollouts each rather than a spread over rollouts. Those are two different uncertainties: rollout noise says how well you measured this checkpoint, seed noise says how much of a difference is the trainer.

A worked protocol for one SO-100 pick and place
Putting it together for a concrete case: you have fine-tuned ACT on a LeRobot dataset of roughly 60 episodes recorded through teleoperation, and you want to know whether a second checkpoint trained on 120 episodes is actually better.
- 1Decide what gap is worth detecting
Be honest before you start. If you only care about improvements of 20 points or more, 45 to 60 paired trials will do. If you want to resolve 10 points, the paired budget is over 200 trials and you should probably spend that time collecting more data instead.
texttarget gap 20 points (60% -> 80%) design paired A/B, matched scene expected disagreement ~30% of trials budget 57 paired trials = 114 rollouts at 60 s + 30 s reset about 3 hours of bench time - 2Pin the configuration
Write both checkpoints, both sets of normalisation statistics, and the chunk settings into one file. ACT on this platform defaults to chunkSize 100 and nActionSteps 100; if the two checkpoints do not share those values you are comparing two controllers, not two datasets.
yamlpolicy_a: name: act_so100_60ep checkpoint: run-4a1f/step-100000 chunk_size: 100 n_action_steps: 100 seed: 1000 policy_b: name: act_so100_120ep checkpoint: run-9c02/step-100000 chunk_size: 100 n_action_steps: 100 seed: 1000 inference: local # 20 ms per action step for ACT instruction: "pick up the red cube and place it in the bin" - 3Run the sheet
For each line: reset the scene against the reference image, start the wrapper that randomly picks A or B, run, score, then run the other policy from the same reset. Two rollouts per line, one line per scene.
bashwhile read -r line; do echo "scene: $line" read -rp "scene matched to reference? [enter]" _ ./run_pair.sh --config bench.yaml --trial "$line" >> results.jsonl done < trials_2026-08-24.csv - 4Count the disagreements, not the totals
The paired comparison lives entirely in the trials where one policy succeeded and the other failed. Everything else cancels.
pythonimport json from statsmodels.stats.contingency_tables import mcnemar rows = [json.loads(l) for l in open("results.jsonl")] b = sum(1 for r in rows if r["a"] and not r["b"]) # A wins the scene c = sum(1 for r in rows if r["b"] and not r["a"]) # B wins the scene n11 = sum(1 for r in rows if r["a"] and r["b"]) n00 = sum(1 for r in rows if not r["a"] and not r["b"]) print(f"both {n11} neither {n00} A only {b} B only {c}") print(mcnemar([[n11, b], [c, n00]], exact=True)) - 5Write the result down in full
One paragraph with counts, interval, configuration and seed. If the paired test does not reach significance, say that, and say what gap your sample size could have detected. A null result you can trust is more useful than a positive result you cannot.
textACT/SO-100 pick-and-place, 2026-08-24, unit 2, local inference. 57 paired scenes, grid seed 1000, blind endpoint selection, one operator. A (60 episodes): 34/57 = 59.6% (95% Wilson 46.7 to 71.3) B (120 episodes): 44/57 = 77.2% (95% Wilson 64.8 to 86.2) Discordant: A only 4, B only 14. McNemar exact p = 0.031. Powered to detect a 20-point gap; a 10-point gap would not have shown.
One caution about which weights you pick. Running the benchmark on eight checkpoints at 25 trials each and reporting the best is the same mistake as tuning on the test set: you have selected for luck. Screen on a small pass, benchmark the survivor properly, and say in the report that you did it that way.
Where this stops being worth it
Honest benchmarking has a point of diminishing returns, and knowing where it is saves you weeks. If your policy is at 20 percent, you do not need statistics, you need better data or a different setup; the data collection guide is a better use of an afternoon than 200 rollouts. If your policy works in exactly one lighting condition, that is a known failure pattern with a known set of causes, described on the page about policies that only work in one setup, and a bigger trial count will only measure it more precisely.
Rigorous evaluation pays off at the margin: when two options look similar and you have to choose, when you are about to spend real money scaling a dataset, or when someone outside your team will act on the number. Work on making it cheaper is active. SureSim combines large-scale simulation with a small number of real trials using prediction-powered inference and reports saving over 20 to 25 percent of hardware evaluation effort for similar bounds; RoboArena found its crowd-sourced ranking converged within roughly 100 pairwise comparisons. Neither removes the need to run the robot.
Not trained the policy yet? Start with train your first policy and ACT on the SO-100. If it trains but does nothing useful, see loss falls, policy does nothing, and for background the VLA overview and the complete SO-100 guide.
How many trials is the minimum for a benchmark I can quote?▾
For a single policy, 25 to 50 trials gives a Wilson interval roughly 25 to 33 percentage points wide at a mid-range success rate, which is enough to say whether the policy works reliably at all. For comparing two policies, 25 trials each fails to show the correct order about 27 percent of the time when the true gap is 10 points, so use a paired design and budget 50 or more paired scenes. Below 20 trials you are reporting an anecdote and should label it as one.
Should I report a percentage or a fraction?▾
Both, with the fraction first. 18/25 (72 percent) gives the reader the trial count, the granularity and the estimate in one string. A percentage alone hides the fact that on 25 trials the metric can only move in steps of 4 points, which is why every real-world number in the ACT paper's tables is a multiple of 4.
Do I need a simulator to benchmark properly?▾
No, but a simulator changes the economics. OpenVLA's published LIBERO protocol runs 500 rollouts per suite, 10 tasks at 50 rollouts each, across three seeds, because rollouts are cheap there. On a physical SO-100 each rollout costs a minute plus a human reset, so you buy power through pairing and a good condition grid instead of volume. SIMPLER exists because simulated evaluation that correlates with real evaluation is worth a lot: their visual-matching pipeline reached a Pearson r of 0.924 against real performance where validation MSE managed 0.308.
Can I compare my success rate to a number from a paper or from the arena?▾
Almost never directly. Different embodiment, different objects, different success rule, different timeout, different operator. Use published numbers to decide which model family to try, and your own benchmark to decide which of your checkpoints to ship. The arena links every value to its source, so you can check what the protocol was, which is usually enough to see why a direct comparison would be wrong.
How do I benchmark when training is not reproducible?▾
Make the irreproducibility part of the measurement. GR00T's fine-tuning CLI exposes no seed, so train the same configuration two or three times and see how far apart the results land. A run is roughly 1 to 3 USD on the 24 GB tier and 4 to 12 USD on the A100 or H100 tier, so seed variance is affordable to measure. If your policy-to-policy difference is smaller than your seed-to-seed difference, you have not found an improvement.
Is it cheating to throw away a trial where something went wrong?▾
It is if you decide after seeing the outcome. Write the exclusion rules before you start: a servo dropping out, an object falling off the table during the reset, an operator error before the policy takes over. Log every excluded trial with its reason and report how many you excluded. If more than about one trial in twenty is being excluded, the rig is the problem, not the policy.
Train the checkpoints worth benchmarking
Pick a model and an arm, and the guide gives you the exact GPU tier, dataset format and hyperparameters the trainer sends. A run is 1 to 3 USD on the 24 GB tier and 4 to 12 USD on the A100 or H100 tier, which makes training several seeds of the same configuration a real option.
Open the training matrixSources
- Learning Fine-Grained Bimanual Manipulation with Low-Cost Hardware (ACT / ALOHA), Zhao et al., 2023
- OpenVLA: An Open-Source Vision-Language-Action Model, Kim et al., 2024
- openvla/openvla: LIBERO evaluation instructions, run_libero_eval.py defaults and the center_crop note
- LIBERO: Benchmarking Knowledge Transfer for Lifelong Robot Learning, Liu et al., 2023
- Evaluating Real-World Robot Manipulation Policies in Simulation (SIMPLER), Li et al., 2024
- simpler-env/SimplerEnv repository
- RoboArena: Distributed Real-World Evaluation of Generalist Robot Policies, Atreya et al., 2025
- RoboChallenge: Large-scale Real-robot Evaluation of Embodied Policies, Yakefu et al., 2025
- Reliable and Scalable Robot Policy Evaluation with Imperfect Simulators (SureSim), Badithela et al., 2025
- pi0.5: a Vision-Language-Action Model with Open-World Generalization, Physical Intelligence, 2025
- PhAIL: A Real-Robot VLA Benchmark and Distributional Methodology, Arkhangelskiy, 2026 (preprint, Franka FR3)
- Deep Reinforcement Learning at the Edge of the Statistical Precipice, Agarwal et al., 2021
- Interval Estimation for a Binomial Proportion, Brown, Cai and DasGupta, Statistical Science 16(2), 2001, 101-133
- NIST/SEMATECH e-Handbook of Statistical Methods, section 7.2.4.1 Confidence intervals for a proportion
- huggingface/lerobot: lerobot-eval, EvalConfig and EvalPipelineConfig defaults (v0.6.2)
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started