Examples › BAM rig › Sim-to-real · rack motor twins
Sim-to-real · rack motor twins
The rack's eight motor twins against the real motors on one page: twin error against the measurement floor per motor, the model ladders (which modelling rung buys how much), the twin's error run by run, the improvement rounds, and every model-card term. Click a motor anywhere and every panel shows only it.
The .base file and its data
Every tab above is one entry of this file; the views read these rows. ⋯ in the toolbar copies the current view as YAML.
# Sim-to-real: the rack's eight motor twins against the real motors. Per motor: how far the twin is from the
# measurement floor, which model rung buys how much, the twin's error run by run, the improvement rounds, and every
# model-card term. Real hardware runs, Oct 23-25 2026 (source/build.py, read-only from /archive/datasets/bam_rig).
# Grammar: docs/peps/0004-dashboards.md.
data: motors.json
facets:
- part
- board
properties:
motor: { displayName: Motor }
twin: { displayName: Twin RMS }
floor: { displayName: Floor }
twin_over_floor: { displayName: Twin ÷ floor }
model: { displayName: Model }
family: { displayName: Family }
error: { displayName: Error }
full_twin: { displayName: Full twin }
identified_twin: { displayName: Identified twin }
order: { displayName: Run (in order) }
label: { displayName: Term }
status: { displayName: Status }
datasets:
motors: motors.json
models:
data: models.json
facets: [family]
runs:
data: runs.json
facets: [kind, speed]
terms:
data: terms.json
label: Model-card terms
facets: [status, in_twin]
rounds: rounds.json
links:
- { from: models.motor, to: motors.motor }
- { from: runs.motor, to: motors.motor }
- { from: terms.motor, to: motors.motor }
- { from: rounds.motor, to: motors.motor }
layout:
panels:
- text: |
## Eight motor twins against the real rack
Each card is the full twin's RMS error against real runs (mrad), with its delta to the **measurement floor**:
a twin can't beat the floor. Only CAN 14 has a measured repeat floor; the other seven are a sine
cycle-to-cycle proxy (drawn as a stand-in, ◌). Click a motor anywhere to see only it. The model ladders use two
rulers: the 10-23 budget runs (reference, diagnostic, full) and the 10-24 held-out dataset (friction M0-M3), so
they sit in two charts. Full report: `~/bam_ws/reports/media/rack-motor-models-2026-10-23/report.html`.
w: 12
- { view: Twin vs floor, w: 12, h: 3 }
- { view: Model ladder (10-23 runs), w: 7, h: 5 }
- { view: Friction ladder (10-24 held-out), w: 5, h: 5 }
- { view: Twin error by run, w: 7, h: 4 }
- { view: Improvement rounds, w: 5, h: 4 }
- { view: Model cards, w: 7, h: 6, toolbar: full }
- { view: Motors, w: 5, h: 6 }
views:
- type: kpi
name: Twin vs floor
select: motor
groupBy: { property: motor }
groupOrder: [CAN 14, CAN 15, CAN 1, CAN 4, CAN 11, CAN 12, CAN 13, CAN 21]
kpi:
value: { property: twin }
compare: { against: floor, better: lower }
- type: chart
name: Model ladder (10-23 runs)
from: models
select: motor
filters: 'family != "friction"'
chart:
mark: { type: line, point: true }
encoding:
x: { field: model, sort: { field: rung }, title: null, axis: { labelAngle: -30 } }
y: { field: error, scale: { type: log }, title: RMS error vs real (mrad) }
color: { field: motor, title: Motor }
hover: { title: model, fields: [motor, family, error, ruler] }
- type: chart
name: Friction ladder (10-24 held-out)
from: models
select: motor
filters: 'family == "friction"'
chart:
mark: { type: line, point: true }
encoding:
x: { field: model, sort: { field: rung }, title: null, axis: { labelAngle: -30 } }
y: { field: error, title: Held-out median (mrad) }
color: { field: motor, title: Motor }
detail: { field: base_twin }
hover: { title: model, fields: [motor, base_twin, error, ruler] }
- type: chart
name: Twin error by run
from: runs
select: motor
chart:
mark: { type: line, point: true }
encoding:
x: { field: order, title: Real run, in acquisition order }
y: { field: full_twin, scale: { type: log }, title: Full twin RMS (mrad) }
color: { field: motor, title: Motor }
hover: { title: run, fields: [motor, session_label, kind, speed, full_twin, identified_twin] }
- type: chart
name: Improvement rounds
from: rounds
select: motor
filters: 'metric.contains("held-out median")'
chart:
mark: { type: line, point: true }
encoding:
x: { field: round, title: Round, axis: { tickMinStep: 1 } }
y: { field: median, title: Held-out median (mrad) }
color: { field: motor, title: Motor }
hover: { title: metric, fields: [motor, round, median, baseline_median, twin, wrote] }
- type: table
name: Model cards
from: terms
groupBy: { property: motor }
order: [label, value, unit, status, method, datasheet, agreement, in_twin, ci95]
fold: closed
- type: table
name: Motors
order: [motor, part, board, twin, floor, floor_kind, twin_over_floor, repeat_floor, gates_pass, gates_partial, gates_fail, gates_not_run, terms_measured, terms_total]
// motors.json: 8 rows, 11 KB; the first 3
{
"id": "sim-to-real-motors",
"properties": {
"board": {
"note": "moteus board model.",
"type": "text",
"width": 110
},
"can": {
"note": "CAN id on the rack; joins every file here.",
"type": "number",
"width": 60
},
"datasheet_peak_torque_nm": {
"note": "Peak output torque from the vendor datasheet; the card has it for the GIM4305 only.",
"type": "number",
"width": 130
},
"floor": {
"note": "The measurement floor the budget compares the twin to: the RMS two identical runs disagree by. Measured for CAN 14 only; the other seven use a sine cycle-to-cycle proxy (drawn stand-in), because those stations have no repeated identical tape.",
"type": "number",
"width": 110
},
"floor_kind": {
"note": "How `floor` was obtained (repeats or proxy).",
"type": "text",
"width": 220
},
"gates_fail": {
"note": "Acceptance gates that failed.",
"type": "number",
"width": 80
},
"gates_not_run": {
"note": "Acceptance gates not run yet (pass + fail + partial + not run = 11).",
"type": "number",
"width": 80
},
"gates_partial": {
"note": "Acceptance gates passed in part.",
"type": "number",
"width": 80
},
"gates_pass": {
"note": "Acceptance gates (T1-T11, rack_2026-10-24 acceptance.json) that passed.",
"type": "number",
"width": 80
},
"motor": {
"type": "text",
"width": 80
},
"part": {
"display": "badge",
"note": "Motor part from the model card (JMC parts are inferred from Kv/R, CAN 15 is unidentified).",
"type": "text",
"width": 190
},
"ratio": {
"note": "Gear ratio from the datasheet (1 = direct drive).",
"type": "number",
"width": 70
},
"repeat_floor": {
"note": "Floor from 5 identical floor-tape repeats on the pick-and-place tape (rack_2026-10-24 floors.json), every motor: a different tape and day than `floor`, so not comparable to `twin`.",
"type": "number",
"width": 120
},
"terms_measured": {
"note": "Card rows with status measured (the others are board settings, datasheet, refused or pending).",
"type": "number",
"width": 90
},
"terms_total": {
"note": "Rows in the model card.",
"type": "number",
"width": 80
},
"twin": {
"note": "Full twin: median over all real runs of the RMS of (real - twin) joint angle, from the 10-23 budget. All the budget's runs are listed as held out. n = runs.",
"type": "number",
"width": 110
},
"twin_over_floor": {
"note": "twin / floor: 1× would be indistinguishable from the rack repeating itself; see floor_kind for how trustworthy the floor is.",
"type": "number",
"width": 120
}
},
"rows": [
{
"board": "moteus-c1",
"can": 1,
"datasheet_peak_torque_nm": {
"basis": "datasheet",
"source": "model_cards/can1.json:datasheet.peak_torque_out_nm",
"unit": "N·m",
"value": 3.82
},
"floor": {
"basis": "stand-in",
"note": "PROXY: sine cycle-to-cycle repeat (no repeated identical runs on this station)",
"source": "budget_can1.json:floor_rms_mrad",
"unit": "mrad",
"value": 0.4471
},
"floor_kind": "proxy: sine cycle-to-cycle (no repeated tape)",
"gates_fail": 2,
"gates_not_run": 4,
"gates_partial": 4,
"gates_pass": 1,
"id": "can1",
"motor": "CAN 1",
"part": "steadywin-gim4305-10",
"ratio": {
"basis": "datasheet",
"source": "model_cards/can1.json:datasheet.ratio",
"unit": ":1",
"value": 10
},
"repeat_floor": {
"basis": "measured",
"n": 5,
"note": "median pairwise RMS of 5 identical floor-tape repeats (pickplace)",
"source": "floors.json:1.floor_mrad",
"unit": "mrad",
"value": 0.8968
},
"terms_measured": 9,
"terms_total": 25,
"twin": {
"basis": "measured",
"n": 5,
"source": "budget_can1.json:full_rms_mrad",
"unit": "mrad",
"value": 2.5866
},
"twin_over_floor": {
"basis": "derived",
"source": "budget_can1.json: full_rms_mrad / floor_rms_mrad",
"unit": "×",
"value": 5.786
}
},
{
"board": "moteus-r4.11",
"can": 4,
"datasheet_peak_torque_nm": {
"value": null,
"why": "the card's datasheet has no peak torque for this part"
},
"floor": {
"basis": "stand-in",
"note": "PROXY: sine cycle-to-cycle repeat (no repeated identical runs on this station)",
"source": "budget_can4.json:floor_rms_mrad",
"unit": "mrad",
"value": 5.2841
},
"floor_kind": "proxy: sine cycle-to-cycle (no repeated tape)",
"gates_fail": 1,
"gates_not_run": 5,
"gates_partial": 2,
"gates_pass": 3,
"id": "can4",
"motor": "CAN 4",
"part": "jmc-60asm400",
"ratio": {
"basis": "datasheet",
"source": "model_cards/can4.json:datasheet.ratio",
"unit": ":1",
"value": 1
},
"repeat_floor": {
"basis": "measured",
"n": 5,
"note": "median pairwise RMS of 5 identical floor-tape repeats (pickplace)",
"source": "floors.json:4.floor_mrad",
"unit": "mrad",
"value": 3.2221
},
"terms_measured": 5,
"terms_total": 24,
"twin": {
"basis": "measured",
"n": 10,
"source": "budget_can4.json:full_rms_mrad",
"unit": "mrad",
"value": 24.4206
},
"twin_over_floor": {
"basis": "derived",
"source": "budget_can4.json: full_rms_mrad / floor_rms_mrad",
"unit": "×",
"value": 4.622
}
},
{
"board": "moteus-r4.11",
"can": 11,
"datasheet_peak_torque_nm": {
"value": null,
"why": "the card's datasheet has no peak torque for this part"
},
"floor": {
"basis": "stand-in",
"note": "PROXY: sine cycle-to-cycle repeat (no repeated identical runs on this station)",
"source": "budget_can11.json:floor_rms_mrad",
"unit": "mrad",
"value": 1.1323
},
"floor_kind": "proxy: sine cycle-to-cycle (no repeated tape)",
"gates_fail": 3,
"gates_not_run": 5,
"gates_partial": 2,
"gates_pass": 1,
"id": "can11",
"motor": "CAN 11",
"part": "jmc-60asm200",
"ratio": {
"basis": "datasheet",
"source": "model_cards/can11.json:datasheet.ratio",
"unit": ":1",
"value": 10
},
"repeat_floor": {
"basis": "measured",
"n": 5,
"note": "median pairwise RMS of 5 identical floor-tape repeats (pickplace)",
"source": "floors.json:11.floor_mrad",
"unit": "mrad",
"value": 2.0328
},
"terms_measured": 3,
"terms_total": 24,
"twin": {
"basis": "measured",
"n": 5,
"source": "budget_can11.json:full_rms_mrad",
"unit": "mrad",
"value": 13.2106
},
"twin_over_floor": {
"basis": "derived",
"source": "budget_can11.json: full_rms_mrad / floor_rms_mrad",
"unit": "×",
"value": 11.668
}
}
],
"source": "/archive/datasets/bam_rig/rack_2026-10-23/analysis/budget_can*.json + model_cards/can*.json; rack_2026-10-24/analysis/{acceptance,floors,friction_study}.json; rack_2026-10-25/analysis/twin_round*.json (bam_rack_twin 0.1.0, bam_ws sim-to-real-motor-models-f1ee43 @ 00974f82); read_on 2026-10-26. Real hardware recordings of the rack, Oct 23-25 2026.",
"title": "Rack motor twins vs real hardware (8 motors)",
"titleProperty": "motor"
}README.md
# Sim-to-real · rack motor twins
The rack's eight motor twins against the real motors on one page: twin error against the measurement floor per motor,
the model ladders (which modelling rung buys how much), the twin's error run by run, the improvement rounds, and
every model-card term. Click a motor anywhere and every panel shows only it.
- **App:** BAM rig
- **Replaces:** the rack motor models report's tables and its per-motor JSON
(bam_ws `reports/media/rack-motor-models-2026-10-23/report.html`, built by `bam_rack_twin/docs/`)
and the model cards' markdown tables (`model_cards/*.md`), for browsing; the report keeps the figures and video.
- **Shows:** five datasets linked by `motor` to one `motors` dataset; KPI cards with the delta to a property of the
same row (`compare.against: floor`); two charts kept apart because they use different rulers; a run-by-run line
chart; rounds filtered by a view `filters:` expression; a model-card table grouped by motor with the full toolbar;
per-dataset rails (part and board; model family; run kind and speed; term status).
- **Data:** written by `source/build.py`, read-only from `/archive/datasets/bam_rig/rack_2026-10-23/analysis/`
(`budget_can*.json`, `model_cards/*.json`), `rack_2026-10-24/analysis/` (`acceptance.json`, `floors.json`,
`friction_study.json`) and `rack_2026-10-25/analysis/twin_round{4..7,9,10}.json`. **Real** hardware runs. Only CAN
14's floor is a measured repeat floor; the other seven are a sine cycle-to-cycle proxy, drawn as a stand-in. Every
budget run is held out, so there is no train/test split to show.
The original UI it replaces
the rack motor models report's tables and its per-motor JSON (bam_ws reports/media/rack-motor-models-2026-10-23/report.html, built by bam_rack_twin/docs/) and the model cards' markdown tables (model_cards/*.md), for browsing; the report keeps the figures and video.
What it shows
five datasets linked by motor to one motors dataset; KPI cards with the delta to a property of the same row (compare.against: floor); two charts kept apart because they use different rulers; a run-by-run line chart; rounds filtered by a view filters: expression; a model-card table grouped by motor with the full toolbar; per-dataset rails (part and board; model family; run kind and speed; term status).
Data
written by source/build.py, read-only from /archive/datasets/bam_rig/rack_2026-10-23/analysis/ (budget_can*.json, model_cards/*.json), rack_2026-10-24/analysis/ (acceptance.json, floors.json, friction_study.json) and rack_2026-10-25/analysis/twin_round{4..7,9,10}.json. Real hardware runs. Only CAN 14's floor is a measured repeat floor; the other seven are a sine cycle-to-cycle proxy, drawn as a stand-in. Every budget run is held out, so there is no train/test split to show.