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.

BAM rigkpicharttablea dashboard: 7 views of 5 linked datasets

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.