Dual SO-ARM100 manipulators in MuJoCo open a drawer, pass a fork from one arm to the other, and lay a place setting — driven by a language-conditioned policy compiled to OpenVINO and measured on an Intel Core Ultra 7.
The cutlery drawer sits on the left; the fork belongs on the right of the mat. Each arm reaches a band roughly 0.11–0.30 m from its own base, and neither band covers both. Neither arm can finish alone — the fork has to be passed across the middle of the table.
It is verified rather than assumed: the receiving arm must genuinely have the fork between its fingers, measured against the gripper's own capture geometry, or the transfer is refused and the hand-off counts as failed.
| Capability | Arm | Seeds 0–9 | Rate |
|---|---|---|---|
| Open the cutlery drawer | left arm | 10/10 | |
| Place the plate on the mat | right arm | 8/10 | |
| Pass the fork between arms | both arms | 6/10 |
completenot complete
The policy is a 1.01 M-parameter action-chunk transformer exported to OpenVINO IR and swept across the CPU, the Arc iGPU and the AI Boost NPU. Fastest configuration per device:
The CPU wins by roughly 23× over the NPU — and that is the finding. A model this small on three 96×96 images cannot amortise dispatch overhead on an accelerator, so fixed per-inference cost dominates. Offloading to an NPU pays for sustained large models; this policy is deliberately neither. Measuring all three is what makes “ship it on the CPU” an answer rather than an assumption.
| Device | Precision | p50 ms | p99 ms | vs PyTorch | control Hz |
|---|---|---|---|---|---|
| PyTorch-CPU | FP32 | 4.76 | 119.95 | 1.00× | 3,358 |
| CPU | FP32 | 1.23 | 1.82 | 3.86× | 12,981 |
| NPU | FP32 | 20.06 | 22.48 | 0.24× | 798 |
| CPU | FP16 | 1.15 | 1.43 | 4.14× | 13,904 |
| GPU | FP16 | 2.67 | 7.64 | 1.79× | 6,001 |
| NPU | FP16 | 20.20 | 22.63 | 0.24× | 792 |
| CPU | INT8 | 0.89 | 1.08 | 5.38× | 18,083 |
One inference yields a 16-step action chunk, so the sustainable control rate is sixteen times the inference rate — about 18,083 Hz against a 30 Hz requirement.
Deviation from the PyTorch reference on held-out demonstration frames, as a share of the output's own standard deviation — the scale-free reading.
INT8 is both smaller and more faithful than naive FP16. The difference is calibration: post-training quantisation saw real recorded frames and joint states and placed its ranges accordingly, while FP16 rounds every weight blindly — and a two-layer pre-norm transformer at d_model 128 has little headroom for that. FP16 is not automatically the safe default.
Hierarchical, because a single end-to-end model driving a ten-step sequence — trained without a GPU and without teleoperation data — does not work. Each tier does the thing it is good at.
The operator's sentence plus the overhead camera into a JSON plan, schema-validated and repaired, with a deterministic planner behind it whenever the model's output fails validation.
SmolVLM-500M · INT8Arm assignment, the hand-off state machine, shared-workspace sequencing, and a replan against the current scene whenever a sub-task fails.
per sub-taskThree camera views and 24-D proprioception with the active sub-task as a language token, emitting a 16-step × 12-DoF action chunk.
ACT-style · 0.89 msThe same episode compressed to twenty seconds — drawer, hand-off, plate, mug.