SO-101 follower arm
The Robot Studio / Hugging Face. A certified SimCloud arm twin: the report below is the evidence, check by check, from its latest validation.
certifiedv1.0.0
- twin
so-101- version
- 1.0.0
- firmware
- feetech-sts3215 12V 1:345, lerobot P16 I0 D32, zero at mid-range
- certified
- 04 Oct 2026
- kind
- arm
- toolchain
simtwin 0.1.1- re-checked
- 14 Nov 2026, error
checks passed
21 / 22
1 with a warning
engines passed
2 / 2
mujoco, pybullet
worst tracking error
1.1°
gripper, 0.020 rad RMSE
control latency
134 ms
fitted from lerobot/svla_so101_pickplace@f641879e2217
Checks
Each check the validation ran, in the order it ran them, with what it found. Run by simtwin 0.1.1 on 2026-10-04.
| Check | Status | What it found |
|---|---|---|
twin-schema | pass | valid simtwin.twin/1 for so-101@1.0.0, 6 joints |
files-present | pass | 5 named and 52 referenced files present inside the twin |
mjcf-loads | pass | compiles in MuJoCo 3.14.0: 8 bodies, 6 joints, 6 actuators, 17 meshes |
urdf-loads | pass | 10 links, 9 joints, root 'base_link' |
joint-names-agree | pass | 6 moving joints, same names, types and tree order: shoulder_pan, shoulder_lift, elbow_flex, wrist_flex, wrist_roll, gripper |
joint-axes-agree | pass | 6 joints: axes within 0.001 deg and anchors within 0.002 mm over 13 configurations |
joint-limits-agree | pass | 6 joints agree within 3.7e-06 (tolerance 0.001 rad) |
twin-limits-agree | pass | twin.json limits match URDF and MJCF within 3.7e-06 |
units-consistent | pass | hwUnits, policyIO and 1 cameras are consistent |
no-massless-bodies | pass | every moving body has mass (16 links and rigid groups checked) |
inertia-valid | pass | 18 inertias positive definite and within the triangle inequality |
mass-agree | pass | 7 rigid groups agree; total 0.6456 kg (URDF) vs 0.6454 kg (MJCF) |
actuator-ranges | pass | 6 actuators command exactly their joint's range |
actuator-params-agree | pass | actuator parameters of 6 joints agree between twin.json and the models |
self-collision-home | warn | home is collision-free; collision geometry interpenetrates at rest: lower_arm and shoulder by 4.8 mm, shoulder and wrist by 4.0 mm (a pose the real arm reaches without contact: the collision shapes are larger than the parts there) |
mesh-valid | pass | 17 meshes, 334358 triangles, all readable with plausible size and outward faces; 1 notes: assets/motor_holder_so101_base_v1.stl: not closed (0 boundary and 1 non-manifold edges): engines use its convex hull for contact, which is fine, but inertia from this geometry would be wrong |
crosscheck | pass | mujoco: replayed; pybullet: within 0.276 deg and 0.38 mm of MuJoCo over 4 s. |
dataset-units | pass | lerobot/svla_so101_pickplace@f641879e2217: units range_0_100, range_m100_100 confirmed by the values; wrist_roll.pos: the recording arm's calibrated span is 359.9 deg, the twin's range 320.0 deg |
sysid | pass | declared parameters are at the fit on lerobot/svla_so101_pickplace@f641879e2217 episodes 0-4 (within 2 % of its error); parameters the data does not decide (kept as declared): effort, timeconst; search: 3480 evaluations, 100 % of its budget, 6 of 6 joints verified rather than searched (their declared values were already a local optimum) |
fidelity | pass | lerobot/svla_so101_pickplace@f641879e2217 episodes 5-14 (held out), open loop: RMS shoulder_pan 0.31, shoulder_lift 0.65, elbow_flex 0.42, wrist_flex 0.29, wrist_roll 0.24, gripper 1.14 deg; latency 134 ms twin vs 125 ms arm |
fidelity:armnet/armnetbench_v01_lerobot_so101 | pass | armnet/armnetbench_v01_lerobot_so101@1db69c82b589 episodes 0-14: RMS shoulder_pan 0.25, shoulder_lift 1.20, elbow_flex 1.24, wrist_flex 0.37, wrist_roll 0.18, gripper 2.19 deg |
catalogue | pass | so-101-twin@1.0.0: 8 operators (backlash, coarse, compliance, drift, lag, mass, sluggish, weak-grip) with magnitudes from this twin's fit, 13 hazards; valid under mutational's rules |
Fidelity per joint
Commanded against measured joint positions over 10 episodes of lerobot/svla_so101_pickplace@f641879e2217: tracking error as RMSE, and overshoot of each step. Control latency 134 ms.
| Joint | Tracking RMSE (rad) | (degrees) | Overshoot |
|---|---|---|---|
shoulder_pan | 0.005 | 0.31 | 0.0% |
shoulder_lift | 0.011 | 0.65 | 0.0% |
elbow_flex | 0.007 | 0.42 | 0.0% |
wrist_flex | 0.005 | 0.29 | 0.0% |
wrist_roll | 0.004 | 0.24 | 0.0% |
gripper | 0.020 | 1.14 | 0.3% |
Engines
The same trajectories run in each engine the twin exports to, and compared.
| Engine | Version | Status | Measured |
|---|---|---|---|
| mujoco | 3.14.0 | pass | maxTrackingRad 0.803 |
| pybullet | 202010061 | pass | maxJointDivergenceRad 0.00481, rmsJointDivergenceRad 0.000901, maxTipDivergenceM 0.000385 |
Policy compatibility
Whether a policy's inputs and outputs - names, order, units - are the twin's.
| Policy | Status | Detail |
|---|---|---|
lerobot/smolvla_base | fail | smolvla: camera keys ['observation.images.camera1', 'observation.images.camera2', 'observation.images.camera3'] are not twin cameras (twin maps ['observation.images.wrist']): the scene must supply them, or rename them to a twin camera; the checkpoint does not say which dataset it learnt from: joint names unverified; so100: training values lie outside the twin's joint range (shoulder_lift 15..225 deg vs [-105, 105]; elbow_flex 10..209 deg vs [-97, 97]; wrist_flex -17..131 deg vs [-104, 104]): another calibration convention (an SO-100 'old calibration', zero with the arm stretched out, reads ~120 deg on shoulder_lift and elbow_flex); so100-blue: training values lie outside the twin's joint range (shoulder_lift 47..205 deg vs [-105, 105]; elbow_flex 89..162 deg vs [-97, 97]; wrist_flex 17..111 deg vs [-104, 104]; wrist_roll -183..-30 deg vs [-157, 163]): another calibration convention (an SO-100 'old calibration', zero with the arm stretched out, reads ~120 deg on shoulder_lift and elbow_flex); so100-red: training values lie outside the twin's joint range (shoulder_lift 39..210 deg vs [-105, 105]; elbow_flex 86..161 deg vs [-97, 97]; wrist_flex 24..110 deg vs [-104, 104]; wrist_roll -171..-37 deg vs [-157, 163]): another calibration convention (an SO-100 'old calibration', zero with the arm stretched out, reads ~120 deg on shoulder_lift and elbow_flex) |
samarth-shukla6/act-svla-so101-pickplace | warn | act: camera keys ['observation.images.up', 'observation.images.side'] are not twin cameras (twin maps ['observation.images.wrist']): the scene must supply them, or rename them to a twin camera; statistics: trained on range_m100_100 values, the twin's policyIO speaks deg: configure the follower with use_degrees=false |
Downloads
Version 1.0.0, as it was certified. The bundle holds twin.json and every file it names.
bundle.zip · twin.json and every file it names, in one zipCanonical spec
twin.json · simtwin.twin/1: joints, actuators, cameras, policy I/OURDF
model.urdf · for ROS, PyBullet and IsaacMJCF
model.xml · for MuJoCoUSD
model.usda · for Isaac Sim and OmniverseFault catalogue
catalogue.json · mutational's catalogue for this robotReport
report.json · simtwin.report/1, every check behind the verdict
Versions
Every published version. A newer certified version supersedes the one before it; the older reports stay readable.
Badge
For a README or a datasheet: it shows the twin's status and the engines it passed in, and links to the twin's page in the catalogue, where its report is.
Use it in an evaluation
A task names the twin by id and version; SimCloud fetches this bundle for it. Run a policy for N episodes, under perturbations, and read the success rate with its interval.