Name the sim and mock components as the real ones - #18
Merged
Conversation
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The mock and MuJoCo backends collapsed every bus into a single component, so a robot was
LiteHardwarein simulation andLiteLeftArm+LiteRightArmon hardware.Anything keyed by component name therefore meant a different thing per backend — a controller's
safety_components, thehardware_spawner, the manager'shardware_components_initial_state. The concrete cost:SafetyMonitorControllernames 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
The combined macro emits one block per bus using the real backend's own
block_name. Nothing in the vendoredmujoco_ros2_controlneeds changing — it already iterates every<ros2_control>block and imports each.Mock also gains a
<gpio>per component carryingsafety_levelandsafety_flags. The real drivers export those fromexport_unlisted_state_interface_descriptions(), whichmock_componentshas no equivalent for. The base class parses gpio state interfaces straight from the description (hardware_component_interface.cpp:163) andGenericSystemoverrides only the command side, so declaring them is sufficient.MuJoCo is deliberately excluded. Our
MujocoSystemoverrides the deprecated by-valueexport_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_nameis deleted from everycad/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_componentpins 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 givesmock 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_monitoractivating 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 singleros2 launch … lite_mujoco.launch.pyto confirm before relying on it in CI.🤖 Generated with Claude Code
https://claude.ai/code/session_01EotyWG9YmYvg5xE2v3Zkpt