lockwire 0.2.4__tar.gz → 0.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lockwire-0.2.4 → lockwire-0.2.5}/PKG-INFO +1 -1
- {lockwire-0.2.4 → lockwire-0.2.5}/pyproject.toml +1 -1
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/__init__.py +1 -1
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/checks.py +22 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/cli.py +10 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/edit.py +4 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/arm/contract.yaml +6 -1
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/drone/contract.yaml +28 -1
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/humanoid/contract.yaml +15 -5
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/quadruped/contract.yaml +3 -1
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/rover/contract.yaml +3 -1
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/schema.py +25 -2
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/urdf.py +2 -1
- {lockwire-0.2.4 → lockwire-0.2.5}/README.md +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/__main__.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/_filelock.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/advice.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/api.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/api_payload.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/bench.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/blame.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/catalog.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/catalog_seed.yaml +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/cli_line.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/confine.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/constraints.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/copy.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/describe.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/emit_ts.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/arm/LICENSE +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/arm/NOTICE +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/arm/plan.yaml +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/arm/scene.glb +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/arm/starter.json +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/drone/plan.yaml +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/drone/scene.glb +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/drone/starter.json +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/humanoid/NOTICE +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/humanoid/plan.yaml +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/humanoid/scene.glb +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/humanoid/starter.json +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/quadruped/NOTICE +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/quadruped/plan.yaml +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/quadruped/scene.glb +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/quadruped/starter.json +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/rover/NOTICE +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/rover/plan.yaml +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/rover/scene.glb +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/rover/starter.json +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/examples/servo-bench.yaml +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/export_mhs.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/factdiff.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/forces.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/gitref.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/hw.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/mcp_server.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/mechanism.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/mesh_dae.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/mesh_simple.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/page.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/parts_list.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/plan.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/plan_payload.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/plugins.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/report.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/report_html.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/report_json.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/review_draft.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/scan.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/scene.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/scene_import.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/scene_urdf.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/scene_view.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/sdf.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/serve.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/start_operation.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/statics.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/units.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/vcs.py +0 -0
- {lockwire-0.2.4 → lockwire-0.2.5}/src/lockwire/whatif.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lockwire
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: The physical-assumption contract for robots: checks that a robot's declared parts (power, weight, bus, torque) add up
|
|
5
5
|
Keywords: robotics,urdf,ros,preflight,lockfile,ci,hardware,drone
|
|
6
6
|
Author: Shivansh
|
|
@@ -2104,6 +2104,28 @@ def check_budgets(c: Contract, *, scores: _ScoreBook | None = None) -> list[Find
|
|
|
2104
2104
|
f"({headroom_kg / limit_kg * 100:.0f}%)"
|
|
2105
2105
|
),
|
|
2106
2106
|
)
|
|
2107
|
+
if b.nominal is not None and total_kg > 0.0:
|
|
2108
|
+
nominal_kg = b.nominal.base()
|
|
2109
|
+
gap_pct = (total_kg - nominal_kg) / nominal_kg * 100
|
|
2110
|
+
# a partial sum (unknown masses) can only prove "too heavy", never "too light"
|
|
2111
|
+
if abs(gap_pct) > (b.tolerance_pct or 0.0) and (not unknown_mass or gap_pct > 0):
|
|
2112
|
+
out.append(
|
|
2113
|
+
Finding(
|
|
2114
|
+
severity="violation",
|
|
2115
|
+
quantity=f"budgets/{b.id}/nominal",
|
|
2116
|
+
observed=format_magnitude(total_kg, "kg"),
|
|
2117
|
+
expected=f"{format_magnitude(nominal_kg, 'kg')} +/- {b.tolerance_pct or 0:g}%",
|
|
2118
|
+
observed_quantity=Quantity(value=total_kg, unit="kg"),
|
|
2119
|
+
expected_quantity=Quantity(value=nominal_kg, unit="kg"),
|
|
2120
|
+
delta=f"{gap_pct:+.0f}% ({total_kg - nominal_kg:+.2f} kg)",
|
|
2121
|
+
dependent=", ".join(contributors),
|
|
2122
|
+
owner=b.owner,
|
|
2123
|
+
source=b.provenance.source if b.provenance is not None else None,
|
|
2124
|
+
witness=b.provenance.witness if b.provenance is not None else None,
|
|
2125
|
+
locked_at=b.provenance.locked_at if b.provenance is not None else None,
|
|
2126
|
+
action="the summed link masses disagree with the declared nominal mass; fix the model or the declared value",
|
|
2127
|
+
)
|
|
2128
|
+
)
|
|
2107
2129
|
|
|
2108
2130
|
elif b.kind == "endurance":
|
|
2109
2131
|
_, _, battery_w, battery_witnesses, _, battery_unknown = _power_rollup(c, b.draw_mode)
|
|
@@ -1584,6 +1584,14 @@ def add_cmd(
|
|
|
1584
1584
|
sources: str | None = typer.Option(
|
|
1585
1585
|
None, "--sources", help="budget (mass_total only): devices|links|links+devices."
|
|
1586
1586
|
),
|
|
1587
|
+
nominal: str | None = typer.Option(
|
|
1588
|
+
None,
|
|
1589
|
+
"--nominal",
|
|
1590
|
+
help="budget (mass_total): datasheet mass the sum must match, e.g. '1.25 kg'.",
|
|
1591
|
+
),
|
|
1592
|
+
tolerance_pct: float | None = typer.Option(
|
|
1593
|
+
None, "--tolerance-pct", help="budget: percent the sum may differ from --nominal."
|
|
1594
|
+
),
|
|
1587
1595
|
nominal_voltage: str | None = typer.Option(
|
|
1588
1596
|
None, "--nominal-voltage", help="battery: voltage, e.g. '25.9 V'."
|
|
1589
1597
|
),
|
|
@@ -1649,6 +1657,8 @@ def add_cmd(
|
|
|
1649
1657
|
"limit": limit,
|
|
1650
1658
|
"draw_mode": draw_mode,
|
|
1651
1659
|
"sources": sources,
|
|
1660
|
+
"nominal": nominal,
|
|
1661
|
+
"tolerance_pct": tolerance_pct,
|
|
1652
1662
|
"rail": rail,
|
|
1653
1663
|
"nominal_voltage": nominal_voltage,
|
|
1654
1664
|
"chemistry": chemistry,
|
|
@@ -1655,6 +1655,10 @@ def _build_budget(fields: dict[str, Any], doc: Any) -> CommentedMap:
|
|
|
1655
1655
|
entry["draw_mode"] = fields["draw_mode"]
|
|
1656
1656
|
if fields.get("sources"):
|
|
1657
1657
|
entry["sources"] = fields["sources"]
|
|
1658
|
+
if fields.get("nominal"):
|
|
1659
|
+
entry["nominal"] = parse_value(fields["nominal"], field_name="nominal")
|
|
1660
|
+
if fields.get("tolerance_pct"):
|
|
1661
|
+
entry["tolerance_pct"] = float(fields["tolerance_pct"])
|
|
1658
1662
|
entry["owner"] = fields["owner"]
|
|
1659
1663
|
return entry
|
|
1660
1664
|
|
|
@@ -137,10 +137,15 @@ device_models:
|
|
|
137
137
|
witness: w0
|
|
138
138
|
source: "https://www.waveshare.com/st3215-servo.htm; github.com/TheRobotStudio/SO-ARM100 README.md (Bill of Materials, follower-arm note)"
|
|
139
139
|
locked_at: 2026-10-15
|
|
140
|
-
note: "STS3215 12 V version: operating voltage 6-12.6 V, 30 kg.cm at 12 V, no-load current 200 mA, locked-rotor current 2.7 A (Waveshare retailer listing, not manufacturer). The README says the 12 V version has 30 kg.cm stall torque; the 7.4 V version is 16.5 kg.cm at 6 V."
|
|
140
|
+
note: "STS3215 12 V version: operating voltage 6-12.6 V, 30 kg.cm at 12 V, no-load current 200 mA, locked-rotor current 2.7 A (Waveshare retailer listing, not manufacturer). The README says the 12 V version has 30 kg.cm stall torque; the 7.4 V version is 16.5 kg.cm at 6 V. temperature_c hard limit 70 C: Feetech STS memory table (https://aifitlab-wiki.super.site/feetech-servo-motor-docs/feetech-sts-servo-memory-table-analysis, read 2026-10-22, a third-party transcription of the Feetech table): row Maximum Temperature Limit, address 13 (0x0D), initial value 70, range 0 ~ 100, unit C; row Present Temperature, address 63 (0x3F), read-only, unit C, Internal temperature of the servo. The 70 C is a user-writable default, not an absolute rating; a translated vendor datasheet is said to state torque output turns off above 70 C (second-hand, could not read the PDF). No lower temperature bound is published, so the floor is absolute zero."
|
|
141
141
|
input_voltage_range:
|
|
142
142
|
min: {value: 6, unit: V}
|
|
143
143
|
max: {value: 12.6, unit: V}
|
|
144
|
+
hard_limits:
|
|
145
|
+
# Feetech STS memory table: Maximum Temperature Limit, address 13 (0x0D), initial value 70, range 0-100, unit C;
|
|
146
|
+
# Present Temperature, address 63 (0x3F), read-only, unit C. The lower bound is NOT published: absolute zero is
|
|
147
|
+
# used only as a non-binding floor so the bound is one-sided.
|
|
148
|
+
temperature_c: {lo: -273.15, hi: 70, unit: degC, why: "Feetech STS servos switch torque off above their Maximum Temperature Limit, default 70 C"}
|
|
144
149
|
- id: mdl.controller.waveshare-bus-servo-adapter-a
|
|
145
150
|
family: fam.controller.servo-adapter
|
|
146
151
|
provenance:
|
|
@@ -64,7 +64,14 @@ device_models:
|
|
|
64
64
|
witness: w0
|
|
65
65
|
source: docs.holybro.com/autopilot/pixhawk-6c/technical-specification
|
|
66
66
|
locked_at: 2026-07-25
|
|
67
|
-
note: "Board current draw is unknown; aluminium/plastic case masses are variants, so no installed mass is selected."
|
|
67
|
+
note: "Board current draw is unknown; aluminium/plastic case masses are variants, so no installed mass is selected. Sensor limits (hard_limits): the board's primary IMU is the ICM-42688-P (see mdl.imu.icm-42688-p), so the limits below are its selected full-scale ranges from the TDK InvenSense datasheet DS-000347 section 3.1/3.2 (read 2026-10-22): accelerometer +/-16 g (ACCEL_FS_SEL=0) = +/-156.9064 m/s^2, gyroscope +/-2000 deg/s (GYRO_FS_SEL=0) = +/-34.9066 rad/s. These are the ranges the flight stack configures only if it selects the widest scale; a narrower selection would clip earlier."
|
|
68
|
+
hard_limits:
|
|
69
|
+
accel_x: {lo: -156.9064, hi: 156.9064, unit: "m/s^2"}
|
|
70
|
+
accel_y: {lo: -156.9064, hi: 156.9064, unit: "m/s^2"}
|
|
71
|
+
accel_z: {lo: -156.9064, hi: 156.9064, unit: "m/s^2"}
|
|
72
|
+
gyro_x: {lo: -34.9066, hi: 34.9066, unit: "rad/s"}
|
|
73
|
+
gyro_y: {lo: -34.9066, hi: 34.9066, unit: "rad/s"}
|
|
74
|
+
gyro_z: {lo: -34.9066, hi: 34.9066, unit: "rad/s"}
|
|
68
75
|
- id: mdl.companion.jetson-orin-nano
|
|
69
76
|
family: fam.companion.sbc
|
|
70
77
|
provenance:
|
|
@@ -284,6 +291,26 @@ devices:
|
|
|
284
291
|
physical:
|
|
285
292
|
mass: {value: 59.3, unit: g}
|
|
286
293
|
mount_frame: x500_frame
|
|
294
|
+
envelopes:
|
|
295
|
+
- context: bench-idle
|
|
296
|
+
signals:
|
|
297
|
+
accel_x:
|
|
298
|
+
band: {lo: -0.196133, hi: 0.196133, unit: m/s^2}
|
|
299
|
+
accel_y:
|
|
300
|
+
band: {lo: -0.196133, hi: 0.196133, unit: m/s^2}
|
|
301
|
+
accel_z:
|
|
302
|
+
band: {lo: -10.002783, hi: -9.610517, unit: m/s^2}
|
|
303
|
+
gyro_x:
|
|
304
|
+
band: {lo: -0.0087266, hi: 0.0087266, unit: rad/s}
|
|
305
|
+
gyro_y:
|
|
306
|
+
band: {lo: -0.0087266, hi: 0.0087266, unit: rad/s}
|
|
307
|
+
gyro_z:
|
|
308
|
+
band: {lo: -0.0087266, hi: 0.0087266, unit: rad/s}
|
|
309
|
+
provenance:
|
|
310
|
+
witness: human
|
|
311
|
+
source: "TDK InvenSense ICM-42688-P Datasheet DS-000347, section 3.1 and 3.2 (read from the datasheet PDF, Revision 1.2 mirror, 2026-10-22); product page https://invensense.tdk.com/products/motion-tracking/6-axis/icm-42688-p/ for the current revision"
|
|
312
|
+
locked_at: 2026-10-22
|
|
313
|
+
note: "The Pixhawk 6C carries the ICM-42688-P as its primary IMU (see mdl.imu.icm-42688-p), so the IMU bands are declared on the flight controller device, which is what the recording reports. Vehicle at rest, level, on the bench (+Z reads -g in the recording's sign convention). Gyro band is the datasheet row 'Initial ZRO Tolerance, Board-level, 25C: +/-0.5 deg/s' = +/-0.0087266 rad/s (0.5 x pi/180). Accel x/y band is the row 'ZERO-G OUTPUT Initial Tolerance, Board-level, all axes: +/-20 mg' = +/-0.196133 m/s^2 (20e-3 x 9.80665); the z band is -9.80665 +/- 0.196133 for the same tolerance. The datasheet figures are at 25 C and board level; ZRO drifts +/-0.005 deg/s per C and zero-g +/-0.15 mg per C (same tables), which this band does not widen for. Assumes a level mount: a tilted airframe reads gravity on x/y and would trip it. Newer datasheet revisions were not readable (TDK site blocks automated fetch); the +/-20 figure matches the TDK product page listing 'Accel ZGO'."
|
|
287
314
|
owner: avionics-integration
|
|
288
315
|
provenance:
|
|
289
316
|
witness: w0
|
|
@@ -1103,50 +1103,60 @@ device_models:
|
|
|
1103
1103
|
witness: w0
|
|
1104
1104
|
source: "https://emanual.robotis.com/docs/en/dxl/x/xc330-t288/"
|
|
1105
1105
|
locked_at: 2026-10-15
|
|
1106
|
-
note: "Robotis e-Manual, XC330-T288, fetched 2026-10-15: Stall Torque 1.00 N.m at 12.0 V, 0.88 A; Input Voltage 6.5 ~ 12.0 V (recommended 11.1 V). Stall draw at 12 V = 12 V x 0.88 A = 10.56 W."
|
|
1106
|
+
note: "Robotis e-Manual, XC330-T288, fetched 2026-10-15: Stall Torque 1.00 N.m at 12.0 V, 0.88 A; Input Voltage 6.5 ~ 12.0 V (recommended 11.1 V). Stall draw at 12 V = 12 V x 0.88 A = 10.56 W. temperature_c hard limit -5 to 70 C: Robotis e-Manual (read 2026-10-22) Specifications table, 'Operating Temperature -5 ~ +70 [°C]' (https://emanual.robotis.com/docs/en/dxl/x/xc330-t288/); control table row 'Present Temperature' (address 146, read-only, unit C). The Temperature Limit register (row '31 1 Temperature Limit RW 70 0 ~ 100 1 [℃]') is a user-writable shutdown default; the operating range is used as the limit."
|
|
1107
1107
|
input_voltage_range:
|
|
1108
1108
|
min: {value: 6.5, unit: V}
|
|
1109
1109
|
max: {value: 12.0, unit: V}
|
|
1110
|
+
hard_limits:
|
|
1111
|
+
temperature_c: {lo: -5, hi: 70, unit: degC, why: "Outside the servo's published operating temperature range"}
|
|
1110
1112
|
- id: mdl.servo.2xc430-w250
|
|
1111
1113
|
family: fam.servo.dynamixel
|
|
1112
1114
|
provenance:
|
|
1113
1115
|
witness: w0
|
|
1114
1116
|
source: "https://emanual.robotis.com/docs/en/dxl/x/2xc430-w250/"
|
|
1115
1117
|
locked_at: 2026-10-15
|
|
1116
|
-
note: "Robotis e-Manual, 2XC430-W250, fetched 2026-10-15: Stall Torque 1.8 N.m at 12.0 V, 1.4 A; Input Voltage 6.5 ~ 14.8 V (recommended 12.0 V); the page lists one stall figure for the whole two-axis module. Stall draw at 12 V = 12 V x 1.4 A = 16.8 W."
|
|
1118
|
+
note: "Robotis e-Manual, 2XC430-W250, fetched 2026-10-15: Stall Torque 1.8 N.m at 12.0 V, 1.4 A; Input Voltage 6.5 ~ 14.8 V (recommended 12.0 V); the page lists one stall figure for the whole two-axis module. Stall draw at 12 V = 12 V x 1.4 A = 16.8 W. temperature_c hard limit -5 to 72 C: Robotis e-Manual (read 2026-10-22) Specifications table, 'Operating Temperature -5 ~ +72 [°C]' (https://emanual.robotis.com/docs/en/dxl/x/2xc430-w250/); control table row 'Present Temperature' (address 146, read-only, unit C). The Temperature Limit register (row '31 | 1 | Temperature Limit | RW | 80 | 0 ~ 100 | 1 [°C]') is a user-writable shutdown default; the operating range is used as the limit."
|
|
1117
1119
|
input_voltage_range:
|
|
1118
1120
|
min: {value: 6.5, unit: V}
|
|
1119
1121
|
max: {value: 14.8, unit: V}
|
|
1122
|
+
hard_limits:
|
|
1123
|
+
temperature_c: {lo: -5, hi: 72, unit: degC, why: "Outside the servo's published operating temperature range"}
|
|
1120
1124
|
- id: mdl.servo.xc430-t240bb
|
|
1121
1125
|
family: fam.servo.dynamixel
|
|
1122
1126
|
provenance:
|
|
1123
1127
|
witness: w0
|
|
1124
1128
|
source: "https://emanual.robotis.com/docs/en/dxl/x/xc430-t240bb/"
|
|
1125
1129
|
locked_at: 2026-10-15
|
|
1126
|
-
note: "Robotis e-Manual, XC430-T240BB, fetched 2026-10-15: Stall Torque 1.9 N.m at 12.0 V, 1.4 A; Input Voltage 6.5 ~ 14.8 V (recommended 12.0 V). Stall draw at 12 V = 12 V x 1.4 A = 16.8 W."
|
|
1130
|
+
note: "Robotis e-Manual, XC430-T240BB, fetched 2026-10-15: Stall Torque 1.9 N.m at 12.0 V, 1.4 A; Input Voltage 6.5 ~ 14.8 V (recommended 12.0 V). Stall draw at 12 V = 12 V x 1.4 A = 16.8 W. temperature_c hard limit -5 to 80 C: Robotis e-Manual (read 2026-10-22) Specifications table, 'Operating Temperature -5 ~ +80 [°C]' (https://emanual.robotis.com/docs/en/dxl/x/xc430-t240bb/); control table row 'Present Temperature' (address 146, read-only, unit C). The Temperature Limit register (row '31 | 1 | Temperature Limit | RW | 80 | 0 ~ 100 | 1 [°C]') is a user-writable shutdown default; the operating range is used as the limit."
|
|
1127
1131
|
input_voltage_range:
|
|
1128
1132
|
min: {value: 6.5, unit: V}
|
|
1129
1133
|
max: {value: 14.8, unit: V}
|
|
1134
|
+
hard_limits:
|
|
1135
|
+
temperature_c: {lo: -5, hi: 80, unit: degC, why: "Outside the servo's published operating temperature range"}
|
|
1130
1136
|
- id: mdl.servo.xm430-w210
|
|
1131
1137
|
family: fam.servo.dynamixel
|
|
1132
1138
|
provenance:
|
|
1133
1139
|
witness: w0
|
|
1134
1140
|
source: "https://emanual.robotis.com/docs/en/dxl/x/xm430-w210/"
|
|
1135
1141
|
locked_at: 2026-10-15
|
|
1136
|
-
note: "Robotis e-Manual, XM430-W210, fetched 2026-10-15: Stall Torque 3.0 N.m at 12.0 V, 2.3 A; Input Voltage 10.0 ~ 14.8 V (recommended 12.0 V). Stall draw at 12 V = 12 V x 2.3 A = 27.6 W."
|
|
1142
|
+
note: "Robotis e-Manual, XM430-W210, fetched 2026-10-15: Stall Torque 3.0 N.m at 12.0 V, 2.3 A; Input Voltage 10.0 ~ 14.8 V (recommended 12.0 V). Stall draw at 12 V = 12 V x 2.3 A = 27.6 W. temperature_c hard limit -5 to 80 C: Robotis e-Manual (read 2026-10-22) Specifications table, 'Operating Temperature -5 ~ +80 [°C]' (https://emanual.robotis.com/docs/en/dxl/x/xm430-w210/); control table row 'Present Temperature' (address 146, read-only, unit C). The Temperature Limit register (row '31 | 1 | Temperature Limit | RW | 80 | 0 ~ 100 | 1 [°C]') is a user-writable shutdown default; the operating range is used as the limit."
|
|
1137
1143
|
input_voltage_range:
|
|
1138
1144
|
min: {value: 10.0, unit: V}
|
|
1139
1145
|
max: {value: 14.8, unit: V}
|
|
1146
|
+
hard_limits:
|
|
1147
|
+
temperature_c: {lo: -5, hi: 80, unit: degC, why: "Outside the servo's published operating temperature range"}
|
|
1140
1148
|
- id: mdl.servo.xm430-w350
|
|
1141
1149
|
family: fam.servo.dynamixel
|
|
1142
1150
|
provenance:
|
|
1143
1151
|
witness: w0
|
|
1144
1152
|
source: "https://emanual.robotis.com/docs/en/dxl/x/xm430-w350/"
|
|
1145
1153
|
locked_at: 2026-10-15
|
|
1146
|
-
note: "Robotis e-Manual, XM430-W350, fetched 2026-10-15: Stall Torque 4.1 N.m at 12.0 V, 2.3 A; Input Voltage 10.0 ~ 14.8 V (recommended 12.0 V). Stall draw at 12 V = 12 V x 2.3 A = 27.6 W."
|
|
1154
|
+
note: "Robotis e-Manual, XM430-W350, fetched 2026-10-15: Stall Torque 4.1 N.m at 12.0 V, 2.3 A; Input Voltage 10.0 ~ 14.8 V (recommended 12.0 V). Stall draw at 12 V = 12 V x 2.3 A = 27.6 W. temperature_c hard limit -5 to 80 C: Robotis e-Manual (read 2026-10-22) Specifications table, 'Operating Temperature -5 ~ +80 [°C]' (https://emanual.robotis.com/docs/en/dxl/x/xm430-w350/); control table row 'Present Temperature' (address 146, read-only, unit C). The Temperature Limit register (row '31 1 Temperature Limit RW 80 0 ~ 100 -') is a user-writable shutdown default; the operating range is used as the limit."
|
|
1147
1155
|
input_voltage_range:
|
|
1148
1156
|
min: {value: 10.0, unit: V}
|
|
1149
1157
|
max: {value: 14.8, unit: V}
|
|
1158
|
+
hard_limits:
|
|
1159
|
+
temperature_c: {lo: -5, hi: 80, unit: degC, why: "Outside the servo's published operating temperature range"}
|
|
1150
1160
|
- id: mdl.regulator.pololu-d36v50f12
|
|
1151
1161
|
family: fam.regulator
|
|
1152
1162
|
provenance:
|
|
@@ -233,10 +233,12 @@ device_models:
|
|
|
233
233
|
witness: w0
|
|
234
234
|
source: "github.com/open-dynamic-robot-initiative/open_robot_actuator_hardware mechanics/actuator_module_v1/README.md; electronics/micro_driver_electronics/README.md; arxiv.org/abs/1910.00093"
|
|
235
235
|
locked_at: 2026-10-15
|
|
236
|
-
note: "T-Motor Antigravity 4004 300KV, dual-stage timing belt 3:1 x 3:1 = 9:1 (actuator README; paper II-A). Paper: k_i = 0.025 N m/A, tau_joint = 0.225 i, tau_max = 2.7 N m at 12 A. Micro-driver board input 5-32 V, 'we operate our robots at 24 V' (micro-driver README), used as the input range."
|
|
236
|
+
note: "T-Motor Antigravity 4004 300KV, dual-stage timing belt 3:1 x 3:1 = 9:1 (actuator README; paper II-A). Paper: k_i = 0.025 N m/A, tau_joint = 0.225 i, tau_max = 2.7 N m at 12 A. Micro-driver board input 5-32 V, 'we operate our robots at 24 V' (micro-driver README), used as the input range. current_a hard limit +/-12 A: the paper (https://arxiv.org/pdf/1910.00093, read 2026-10-22) states 'The actuator can output tau_max = 2.7 N m joint torque at 12 A.'; the 12 A is a rated peak, and the paper gives no lower bound beyond its symmetric reading for reverse torque. The Solo12's IMU and per-motor temperature are NOT declared: the motor driver publishes no temperature figure, and the IMU bias figures could not be sourced to a page that names the Solo12's part."
|
|
237
237
|
input_voltage_range:
|
|
238
238
|
min: {value: 5, unit: V}
|
|
239
239
|
max: {value: 32, unit: V}
|
|
240
|
+
hard_limits:
|
|
241
|
+
current_a: {lo: -12, hi: 12, unit: A, why: "The ODRI paper rates the actuator at 12 A (2.7 N m joint torque); above it the module is outside its published envelope"}
|
|
240
242
|
devices:
|
|
241
243
|
- id: dev.motor.front-left-hip-abduction
|
|
242
244
|
model: mdl.actuator.odri-4004-9to1
|
|
@@ -135,7 +135,9 @@ device_models:
|
|
|
135
135
|
witness: w0
|
|
136
136
|
source: "emanual.robotis.com/docs/en/dxl/x/xl430-w250/"
|
|
137
137
|
locked_at: 2026-10-15
|
|
138
|
-
note: "Input 6.5-12.0 V (recommended 11.1 V); stall torque 1.4 N.m at 11.1 V, 1.3 A; standby current 52 mA; mass 57.2 g."
|
|
138
|
+
note: "Input 6.5-12.0 V (recommended 11.1 V); stall torque 1.4 N.m at 11.1 V, 1.3 A; standby current 52 mA; mass 57.2 g. temperature_c hard limit -5 to 72 C: Robotis e-Manual (read 2026-10-22) Specifications table, 'Operating Temperature -5 ~ +72 [°C]' (https://emanual.robotis.com/docs/en/dxl/x/xl430-w250/); control table row 'Present Temperature' (address 146, read-only, unit C). The Temperature Limit register (row '31 1 Temperature Limit RW 72 0 ~ 100 1 [°C]') is a user-writable shutdown default; the operating range is used as the limit."
|
|
139
|
+
hard_limits:
|
|
140
|
+
temperature_c: {lo: -5, hi: 72, unit: degC, why: "Outside the servo's published operating temperature range"}
|
|
139
141
|
- id: mdl.lidar.lds-02
|
|
140
142
|
family: fam.lidar
|
|
141
143
|
provenance:
|
|
@@ -519,8 +519,6 @@ class JointLimits(Base):
|
|
|
519
519
|
|
|
520
520
|
@model_validator(mode="after")
|
|
521
521
|
def _ordered(self) -> "JointLimits":
|
|
522
|
-
if self.lower is not None and self.upper is not None and self.lower >= self.upper:
|
|
523
|
-
raise ValueError(f"joint limit lower {self.lower} >= upper {self.upper}")
|
|
524
522
|
if self.velocity is not None and self.velocity < 0:
|
|
525
523
|
raise ValueError(f"joint velocity limit must be >= 0, got {self.velocity}")
|
|
526
524
|
for limit in ("lower", "upper", "effort", "velocity"):
|
|
@@ -540,6 +538,21 @@ class Joint(Base):
|
|
|
540
538
|
axis: tuple[float, float, float] | None = None
|
|
541
539
|
limits: JointLimits | None = None
|
|
542
540
|
|
|
541
|
+
@model_validator(mode="after")
|
|
542
|
+
def _ordered(self) -> "Joint":
|
|
543
|
+
# Only a moving joint has a range: URDF ignores lower/upper on fixed and continuous
|
|
544
|
+
# joints, and real files carry lower="0" upper="0" there (Kinova's gen3_lite.urdf).
|
|
545
|
+
lim = self.limits
|
|
546
|
+
if (
|
|
547
|
+
self.type in ("revolute", "prismatic")
|
|
548
|
+
and lim
|
|
549
|
+
and lim.lower is not None
|
|
550
|
+
and lim.upper is not None
|
|
551
|
+
and lim.lower >= lim.upper
|
|
552
|
+
):
|
|
553
|
+
raise ValueError(f"joint {self.name!r} limit lower {lim.lower} >= upper {lim.upper}")
|
|
554
|
+
return self
|
|
555
|
+
|
|
543
556
|
|
|
544
557
|
def _model_structure_hash(links: list[Link], joints: list[Joint]) -> str:
|
|
545
558
|
"""Canonical v0.2 hash; axis and limits are load-bearing joint structure."""
|
|
@@ -1485,6 +1498,8 @@ class Budget(Base):
|
|
|
1485
1498
|
sources: Literal["devices", "links", "links+devices"] = "devices" # mass_total: what to
|
|
1486
1499
|
# sum — explicit so double-counting a device that also appears in the URDF is a declared
|
|
1487
1500
|
# choice, not an accident
|
|
1501
|
+
nominal: Quantity | None = None # mass_total only: the datasheet mass the sum must match
|
|
1502
|
+
tolerance_pct: float | None = None # with nominal; absent means exact
|
|
1488
1503
|
owner: str
|
|
1489
1504
|
provenance: Provenance | None = None
|
|
1490
1505
|
|
|
@@ -1505,6 +1520,14 @@ class Budget(Base):
|
|
|
1505
1520
|
"mass_total": "mass",
|
|
1506
1521
|
}[self.kind]
|
|
1507
1522
|
_quantity_dimension(expected, f"{self.kind} budget limit")(self.limit)
|
|
1523
|
+
if self.nominal is not None:
|
|
1524
|
+
if self.kind != "mass_total":
|
|
1525
|
+
raise ValueError("nominal is for mass_total budgets only")
|
|
1526
|
+
_quantity_dimension("mass", "budget nominal")(self.nominal)
|
|
1527
|
+
if self.nominal.base() <= 0:
|
|
1528
|
+
raise ValueError("budget nominal must be positive")
|
|
1529
|
+
if self.tolerance_pct is not None and (self.nominal is None or self.tolerance_pct < 0):
|
|
1530
|
+
raise ValueError("tolerance_pct needs nominal and must not be negative")
|
|
1508
1531
|
return self
|
|
1509
1532
|
|
|
1510
1533
|
|
|
@@ -259,7 +259,8 @@ def _parse_joint(el: ET.Element) -> Joint:
|
|
|
259
259
|
)
|
|
260
260
|
limit_el = el.find("limit")
|
|
261
261
|
limits = None
|
|
262
|
-
|
|
262
|
+
# A mimic joint follows its parent and has no limits of its own (OnRobot's RG6 ships 0/0/effort 0).
|
|
263
|
+
if limit_el is not None and el.find("mimic") is None:
|
|
263
264
|
lower_raw, upper_raw = limit_el.get("lower"), limit_el.get("upper")
|
|
264
265
|
effort_raw, velocity_raw = limit_el.get("effort"), limit_el.get("velocity")
|
|
265
266
|
limits = JointLimits(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|