Skip to content

Name the sim and mock components as the real ones - #18

Merged
T-K-233 merged 1 commit into
mainfrom
feat/backend-component-name-parity
Aug 25, 2026
Merged

Name the sim and mock components as the real ones#18
T-K-233 merged 1 commit into
mainfrom
feat/backend-component-name-parity

Conversation

@T-K-233

@T-K-233 T-K-233 commented Aug 25, 2026

Copy link
Copy Markdown
Member

The mock and MuJoCo backends collapsed every bus into a single component, so a robot was LiteHardware in simulation and LiteLeftArm + LiteRightArm on hardware.

Anything keyed by component name therefore meant a different thing per backend — a controller's safety_components, the hardware_spawner, the manager's hardware_components_initial_state. The concrete cost: SafetyMonitorController names the real components, so it could not load in simulation at all — the one place the fault-handover path can be exercised without a robot.

Approved as item 3 of the structural audit.

What changed

                        before                    after
default / mock    LiteHardware              LiteLeftArm LiteRightArm
use_mock:=false   LiteLeftArm LiteRightArm  LiteLeftArm LiteRightArm
sim_mujoco:=true  LiteHardware              LiteLeftArm LiteRightArm

The combined macro emits one block per bus using the real backend's own block_name. Nothing in the vendored mujoco_ros2_control needs changing — it already iterates every <ros2_control> block and imports each.

Mock also gains a <gpio> per component carrying safety_level and safety_flags. The real drivers export those from export_unlisted_state_interface_descriptions(), which mock_components has no equivalent for. The base class parses gpio state interfaces straight from the description (hardware_component_interface.cpp:163) and GenericSystem overrides only the command side, so declaring them is sufficient.

MuJoCo is deliberately excluded. Our MujocoSystem overrides the deprecated by-value export_state_interfaces(), which bypasses the base class's gpio handling — a gpio there would promise interfaces that never appear. Full MuJoCo parity needs a fork patch and stays out of scope.

combined_block_name is deleted from every cad/ros2_control.json: with the real names in use, nothing reads it.

An edge case the tests caught

An IMU with a real component now gets a matching sensor block in sim. A sim-only IMU has no name to match and stays nested — test_imu_without_real_plugin_emits_no_sensor_component pins exactly that, and caught my first attempt, which invented a component for it.

Verification

1618 tests pass. Two new ones lock the parity in: component names equal across sim and real, mock a subset since it cannot back an IMU; and every mock system block carrying both safety interfaces.

Both were negative-controlled against the committed artifacts, which is what they actually read — sabotaging the generator alone leaves them green, so a generator-only control would have been worthless. Renaming a mock component gives sim ['LiteHardware', 'LiteRightArm'] != real ['LiteLeftArm', 'LiteRightArm']; deleting a gpio gives mock component LiteLeftArm declares no safety gpio.

Also caught in review: my first generated comment contained --, which is illegal inside an XML comment. The repo's own well-formedness test found it.

Not verified end to end: I did not get a clean live run of safety_monitor activating against the mock gpios in this session — my test harness kept losing the controller_manager. The mechanism is confirmed by expansion (the gpios are present with the right names) and by reading the upstream source, and an earlier structural audit did observe it activate. Worth a single ros2 launch … lite_mujoco.launch.py to confirm before relying on it in CI.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EotyWG9YmYvg5xE2v3Zkpt

…safety gpio

The mock and MuJoCo backends collapsed every bus into one component, so a robot
was LiteHardware in simulation and LiteLeftArm + LiteRightArm on hardware.
Anything keyed by component name therefore meant a different thing per backend:
a controller's safety_components, the hardware_spawner, the manager's
hardware_components_initial_state. The practical cost was that
SafetyMonitorController, which names the real components, could not load in
simulation at all -- the one place the fault-handover path can be exercised
without a robot.

The combined macro now emits one block per bus with the real backend's own
block_name. Nothing in the vendored mujoco_ros2_control needs changing: it
already iterates every <ros2_control> block and imports each one.

Mock also gains a <gpio> per component carrying safety_level and safety_flags.
The real drivers export those from
export_unlisted_state_interface_descriptions(), which mock_components has no
equivalent for; the base class parses gpio state interfaces straight from the
description (hardware_component_interface.cpp:163) and GenericSystem does not
override that path, so declaring them is enough. MuJoCo is deliberately left
out: our MujocoSystem overrides the deprecated by-value
export_state_interfaces(), which bypasses the base class's gpio handling, so a
gpio there would promise interfaces that never appear.

An IMU with a real component gets a matching sensor block in sim. A sim-only one
has no name to match and stays nested, which is what
test_imu_without_real_plugin_emits_no_sensor_component pins.

combined_block_name is deleted from every cad/ros2_control.json: with the real
names in use it is read by nothing.

Two tests lock this in -- component names equal across sim and real, mock a
subset since it cannot back an IMU, and every mock system block carrying both
safety interfaces. Both were negative-controlled against the committed
artifacts, since that is what they read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EotyWG9YmYvg5xE2v3Zkpt
@T-K-233
T-K-233 merged commit 12a488a into main Aug 25, 2026
2 checks passed
@T-K-233
T-K-233 deleted the feat/backend-component-name-parity branch August 25, 2026 22:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant