Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 48 additions & 16 deletions robot_assets/workflow/urdf_to_xacro.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,17 +210,23 @@ def _group_macro(robot: str, group: str, joints: list[dict], limits: dict) -> st


def _combined_macro(robot: str, groups: list[dict], backends: dict, imu: dict | None) -> str:
"""Return the single-block macro shared by the mock and MuJoCo backends."""
calls = "\n".join(f' <xacro:{robot}_{g["name"]}_joints/>' for g in groups)
# Only MuJoCo backs the IMU state interfaces; mock_components has no source for them.
sensor = (
f'\n <xacro:if value="${{{SIM_ARG}}}">\n'
f'{_sensor_element(imu["name"], " ")}\n </xacro:if>'
if imu else ""
)
return f""" <!-- Combined single block for the mock and MuJoCo backends. -->
{_macro_open(f"{robot}_ros2_control_combined", ["name", SIM_ARG])}
<ros2_control name="${{name}}" type="system">
"""Return the mock and MuJoCo macro: one block per bus, named as the real one."""
blocks = []
for group in groups:
# A gpio only under mock. The real backend exports these two through
# export_unlisted_state_interface_descriptions(), and MujocoSystem
# overrides the deprecated by-value export_state_interfaces(), which
# bypasses the base class's gpio handling -- declaring them there would
# promise interfaces that never appear.
safety = (
f'\n <xacro:unless value="${{{SIM_ARG}}}">\n'
f' <gpio name="{group["block_name"]}">\n'
f' <state_interface name="safety_level"/>\n'
f' <state_interface name="safety_flags"/>\n'
f' </gpio>\n'
f' </xacro:unless>'
)
blocks.append(f""" <ros2_control name="{group["block_name"]}" type="system">
<hardware>
<xacro:if value="${{{SIM_ARG}}}">
<plugin>{backends["sim"]}</plugin>
Expand All @@ -229,8 +235,35 @@ def _combined_macro(robot: str, groups: list[dict], backends: dict, imu: dict |
<plugin>{backends["mock"]}</plugin>
</xacro:unless>
</hardware>
{calls}{sensor}
</ros2_control>
<xacro:{robot}_{group["name"]}_joints/>{safety}
</ros2_control>""")
if imu and imu.get("real_block_name"):
# The real backend gives the IMU its own sensor component, so sim matches
# the name. Only MuJoCo backs the state interfaces; mock has no source.
blocks.append(
f' <xacro:if value="${{{SIM_ARG}}}">\n'
f' <ros2_control name="{imu["real_block_name"]}" type="sensor">\n'
f' <hardware>\n'
f' <plugin>{backends["sim"]}</plugin>\n'
f' </hardware>\n'
f'{_sensor_element(imu["name"], " ")}\n'
f' </ros2_control>\n'
f' </xacro:if>')
elif imu:
# A sim-only IMU has no real component to match, so it stays nested in the
# first bus block rather than inventing a component name of its own.
nested = (
f'\n <xacro:if value="${{{SIM_ARG}}}">\n'
f'{_sensor_element(imu["name"], " ")}\n'
f' </xacro:if>')
blocks[0] = blocks[0].replace("\n </ros2_control>", f"{nested}\n </ros2_control>")
body = "\n".join(blocks)
return f""" <!-- Mock and MuJoCo backends. One block per bus, carrying the same component
names as the real backend, so anything keyed by component name means the
same thing on every backend: a controller's safety_components, the
hardware_spawner, the manager's hardware_components_initial_state. -->
{_macro_open(f"{robot}_ros2_control_combined", [SIM_ARG])}
{body}
</xacro:macro>"""


Expand Down Expand Up @@ -274,9 +307,9 @@ def _top_macro(robot: str, args: list[str], real_params: list[str]) -> str:
return f""" <!-- Backend dispatch, following the Universal Robots description convention: one
boolean per non-real backend, real hardware as the fallback when both are false.
{SIM_ARG} wins over {MOCK_ARG}. -->
{_macro_open(f"{robot}_ros2_control", ["name", *args])}
{_macro_open(f"{robot}_ros2_control", args)}
<xacro:if value="${{{SIM_ARG} or {MOCK_ARG}}}">
<xacro:{robot}_ros2_control_combined name="${{name}}" {SIM_ARG}="${{{SIM_ARG}}}"/>
<xacro:{robot}_ros2_control_combined {SIM_ARG}="${{{SIM_ARG}}}"/>
</xacro:if>
<xacro:unless value="${{{SIM_ARG} or {MOCK_ARG}}}">
<xacro:{robot}_ros2_control_real
Expand Down Expand Up @@ -369,7 +402,6 @@ def build_assembly_xacro(robot: str, ros2_control: dict | None, package: str = D
<xacro:{robot}_description/>

<xacro:{robot}_ros2_control
name="{ros2_control["combined_block_name"]}"
{forwarded}/>
</robot>
"""
Expand Down
2 changes: 0 additions & 2 deletions robots/lite_bimanual/cad/ros2_control.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@

"base_link": {"name": "base_link", "child": "chest"},

"combined_block_name": "LiteHardware",

"groups": [
{"name": "left_arm", "block_name": "LiteLeftArm", "can_interface_arg": "can_interface_left"},
{"name": "right_arm", "block_name": "LiteRightArm", "can_interface_arg": "can_interface_right"}
Expand Down
35 changes: 30 additions & 5 deletions robots/lite_bimanual/xacro/lite_bimanual.ros2_control.xacro
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@
current_limit="14"/>
</xacro:macro>

<!-- Combined single block for the mock and MuJoCo backends. -->
<xacro:macro name="lite_bimanual_ros2_control_combined" params="name sim_mujoco">
<ros2_control name="${name}" type="system">
<!-- Mock and MuJoCo backends. One block per bus, carrying the same component
names as the real backend, so anything keyed by component name means the
same thing on every backend: a controller's safety_components, the
hardware_spawner, the manager's hardware_components_initial_state. -->
<xacro:macro name="lite_bimanual_ros2_control_combined" params="sim_mujoco">
<ros2_control name="LiteLeftArm" type="system">
<hardware>
<xacro:if value="${sim_mujoco}">
<plugin>mujoco_ros2_control/MujocoSystem</plugin>
Expand All @@ -87,7 +90,29 @@
</xacro:unless>
</hardware>
<xacro:lite_bimanual_left_arm_joints/>
<xacro:unless value="${sim_mujoco}">
<gpio name="LiteLeftArm">
<state_interface name="safety_level"/>
<state_interface name="safety_flags"/>
</gpio>
</xacro:unless>
</ros2_control>
<ros2_control name="LiteRightArm" type="system">
<hardware>
<xacro:if value="${sim_mujoco}">
<plugin>mujoco_ros2_control/MujocoSystem</plugin>
</xacro:if>
<xacro:unless value="${sim_mujoco}">
<plugin>mock_components/GenericSystem</plugin>
</xacro:unless>
</hardware>
<xacro:lite_bimanual_right_arm_joints/>
<xacro:unless value="${sim_mujoco}">
<gpio name="LiteRightArm">
<state_interface name="safety_level"/>
<state_interface name="safety_flags"/>
</gpio>
</xacro:unless>
</ros2_control>
</xacro:macro>

Expand Down Expand Up @@ -117,10 +142,10 @@
boolean per non-real backend, real hardware as the fallback when both are false.
sim_mujoco wins over use_mock_hardware. -->
<xacro:macro name="lite_bimanual_ros2_control"
params="name use_mock_hardware sim_mujoco can_interface_left can_interface_right
params="use_mock_hardware sim_mujoco can_interface_left can_interface_right
calibration_file">
<xacro:if value="${sim_mujoco or use_mock_hardware}">
<xacro:lite_bimanual_ros2_control_combined name="${name}" sim_mujoco="${sim_mujoco}"/>
<xacro:lite_bimanual_ros2_control_combined sim_mujoco="${sim_mujoco}"/>
</xacro:if>
<xacro:unless value="${sim_mujoco or use_mock_hardware}">
<xacro:lite_bimanual_ros2_control_real
Expand Down
1 change: 0 additions & 1 deletion robots/lite_bimanual/xacro/lite_bimanual.urdf.xacro
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
<xacro:lite_bimanual_description/>

<xacro:lite_bimanual_ros2_control
name="LiteHardware"
use_mock_hardware="$(arg use_mock_hardware)"
sim_mujoco="$(arg sim_mujoco)"
can_interface_left="$(arg can_interface_left)"
Expand Down
2 changes: 0 additions & 2 deletions robots/lite_biped/cad/ros2_control.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@

"base_link": {"name": "base_link", "child": "pelvis"},

"combined_block_name": "LiteBipedHardware",

"_comment_imu": "Sim/mock: the base_imu <sensor> lives in the combined block, backed by MujocoSystem's MJCF sensors. Real: a standalone <ros2_control type='sensor'> block (real_block_name) driven by real_plugin (Yahboom WIT USB IMU on imu_port), exposing the SAME interfaces so imu_sensor_broadcaster republishes /imu/data identically -- no separate driver node.",
"imu": {"name": "base_imu", "real_plugin": "humanoid_devices_witmotion/WitImuSensor", "real_block_name": "LiteBipedIMU"},

Expand Down
46 changes: 38 additions & 8 deletions robots/lite_biped/xacro/lite_biped.ros2_control.xacro
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,12 @@
current_limit="14"/>
</xacro:macro>

<!-- Combined single block for the mock and MuJoCo backends. -->
<xacro:macro name="lite_biped_ros2_control_combined" params="name sim_mujoco">
<ros2_control name="${name}" type="system">
<!-- Mock and MuJoCo backends. One block per bus, carrying the same component
names as the real backend, so anything keyed by component name means the
same thing on every backend: a controller's safety_components, the
hardware_spawner, the manager's hardware_components_initial_state. -->
<xacro:macro name="lite_biped_ros2_control_combined" params="sim_mujoco">
<ros2_control name="LiteBipedLeftLeg" type="system">
<hardware>
<xacro:if value="${sim_mujoco}">
<plugin>mujoco_ros2_control/MujocoSystem</plugin>
Expand All @@ -133,8 +136,35 @@
</xacro:unless>
</hardware>
<xacro:lite_biped_left_leg_joints/>
<xacro:unless value="${sim_mujoco}">
<gpio name="LiteBipedLeftLeg">
<state_interface name="safety_level"/>
<state_interface name="safety_flags"/>
</gpio>
</xacro:unless>
</ros2_control>
<ros2_control name="LiteBipedRightLeg" type="system">
<hardware>
<xacro:if value="${sim_mujoco}">
<plugin>mujoco_ros2_control/MujocoSystem</plugin>
</xacro:if>
<xacro:unless value="${sim_mujoco}">
<plugin>mock_components/GenericSystem</plugin>
</xacro:unless>
</hardware>
<xacro:lite_biped_right_leg_joints/>
<xacro:if value="${sim_mujoco}">
<xacro:unless value="${sim_mujoco}">
<gpio name="LiteBipedRightLeg">
<state_interface name="safety_level"/>
<state_interface name="safety_flags"/>
</gpio>
</xacro:unless>
</ros2_control>
<xacro:if value="${sim_mujoco}">
<ros2_control name="LiteBipedIMU" type="sensor">
<hardware>
<plugin>mujoco_ros2_control/MujocoSystem</plugin>
</hardware>
<sensor name="base_imu">
<state_interface name="orientation.x"/>
<state_interface name="orientation.y"/>
Expand All @@ -147,8 +177,8 @@
<state_interface name="linear_acceleration.y"/>
<state_interface name="linear_acceleration.z"/>
</sensor>
</xacro:if>
</ros2_control>
</ros2_control>
</xacro:if>
</xacro:macro>

<!-- Real hardware: one <ros2_control> block per CAN bus. The controller_manager
Expand Down Expand Up @@ -196,10 +226,10 @@
boolean per non-real backend, real hardware as the fallback when both are false.
sim_mujoco wins over use_mock_hardware. -->
<xacro:macro name="lite_biped_ros2_control"
params="name use_mock_hardware sim_mujoco can_interface_left can_interface_right
params="use_mock_hardware sim_mujoco can_interface_left can_interface_right
calibration_file imu_port use_linkage">
<xacro:if value="${sim_mujoco or use_mock_hardware}">
<xacro:lite_biped_ros2_control_combined name="${name}" sim_mujoco="${sim_mujoco}"/>
<xacro:lite_biped_ros2_control_combined sim_mujoco="${sim_mujoco}"/>
</xacro:if>
<xacro:unless value="${sim_mujoco or use_mock_hardware}">
<xacro:lite_biped_ros2_control_real
Expand Down
1 change: 0 additions & 1 deletion robots/lite_biped/xacro/lite_biped.urdf.xacro
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
<xacro:lite_biped_description/>

<xacro:lite_biped_ros2_control
name="LiteBipedHardware"
use_mock_hardware="$(arg use_mock_hardware)"
sim_mujoco="$(arg sim_mujoco)"
can_interface_left="$(arg can_interface_left)"
Expand Down
2 changes: 0 additions & 2 deletions robots/lite_biped_debug/cad/ros2_control.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@

"base_link": {"name": "base_link", "child": "pelvis"},

"combined_block_name": "LiteBipedHardware",

"_comment_imu": "Sim/mock: the base_imu <sensor> lives in the combined block, backed by MujocoSystem's MJCF sensors. Real: a standalone <ros2_control type='sensor'> block (real_block_name) driven by real_plugin (Yahboom WIT USB IMU on imu_port), exposing the SAME interfaces so imu_sensor_broadcaster republishes /imu/data identically -- no separate driver node.",
"imu": {"name": "base_imu", "real_plugin": "humanoid_devices_witmotion/WitImuSensor", "real_block_name": "LiteBipedIMU"},

Expand Down
46 changes: 38 additions & 8 deletions robots/lite_biped_debug/xacro/lite_biped_debug.ros2_control.xacro
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,12 @@
torque_limit="5.5" current_limit="14"/>
</xacro:macro>

<!-- Combined single block for the mock and MuJoCo backends. -->
<xacro:macro name="lite_biped_debug_ros2_control_combined" params="name sim_mujoco">
<ros2_control name="${name}" type="system">
<!-- Mock and MuJoCo backends. One block per bus, carrying the same component
names as the real backend, so anything keyed by component name means the
same thing on every backend: a controller's safety_components, the
hardware_spawner, the manager's hardware_components_initial_state. -->
<xacro:macro name="lite_biped_debug_ros2_control_combined" params="sim_mujoco">
<ros2_control name="LiteBipedLeftLeg" type="system">
<hardware>
<xacro:if value="${sim_mujoco}">
<plugin>mujoco_ros2_control/MujocoSystem</plugin>
Expand All @@ -133,8 +136,35 @@
</xacro:unless>
</hardware>
<xacro:lite_biped_debug_left_leg_joints/>
<xacro:unless value="${sim_mujoco}">
<gpio name="LiteBipedLeftLeg">
<state_interface name="safety_level"/>
<state_interface name="safety_flags"/>
</gpio>
</xacro:unless>
</ros2_control>
<ros2_control name="LiteBipedRightLeg" type="system">
<hardware>
<xacro:if value="${sim_mujoco}">
<plugin>mujoco_ros2_control/MujocoSystem</plugin>
</xacro:if>
<xacro:unless value="${sim_mujoco}">
<plugin>mock_components/GenericSystem</plugin>
</xacro:unless>
</hardware>
<xacro:lite_biped_debug_right_leg_joints/>
<xacro:if value="${sim_mujoco}">
<xacro:unless value="${sim_mujoco}">
<gpio name="LiteBipedRightLeg">
<state_interface name="safety_level"/>
<state_interface name="safety_flags"/>
</gpio>
</xacro:unless>
</ros2_control>
<xacro:if value="${sim_mujoco}">
<ros2_control name="LiteBipedIMU" type="sensor">
<hardware>
<plugin>mujoco_ros2_control/MujocoSystem</plugin>
</hardware>
<sensor name="base_imu">
<state_interface name="orientation.x"/>
<state_interface name="orientation.y"/>
Expand All @@ -147,8 +177,8 @@
<state_interface name="linear_acceleration.y"/>
<state_interface name="linear_acceleration.z"/>
</sensor>
</xacro:if>
</ros2_control>
</ros2_control>
</xacro:if>
</xacro:macro>

<!-- Real hardware: one <ros2_control> block per CAN bus. The controller_manager
Expand Down Expand Up @@ -196,10 +226,10 @@
boolean per non-real backend, real hardware as the fallback when both are false.
sim_mujoco wins over use_mock_hardware. -->
<xacro:macro name="lite_biped_debug_ros2_control"
params="name use_mock_hardware sim_mujoco can_interface_left can_interface_right
params="use_mock_hardware sim_mujoco can_interface_left can_interface_right
calibration_file imu_port use_linkage">
<xacro:if value="${sim_mujoco or use_mock_hardware}">
<xacro:lite_biped_debug_ros2_control_combined name="${name}" sim_mujoco="${sim_mujoco}"/>
<xacro:lite_biped_debug_ros2_control_combined sim_mujoco="${sim_mujoco}"/>
</xacro:if>
<xacro:unless value="${sim_mujoco or use_mock_hardware}">
<xacro:lite_biped_debug_ros2_control_real
Expand Down
1 change: 0 additions & 1 deletion robots/lite_biped_debug/xacro/lite_biped_debug.urdf.xacro
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
<xacro:lite_biped_debug_description/>

<xacro:lite_biped_debug_ros2_control
name="LiteBipedHardware"
use_mock_hardware="$(arg use_mock_hardware)"
sim_mujoco="$(arg sim_mujoco)"
can_interface_left="$(arg can_interface_left)"
Expand Down
2 changes: 0 additions & 2 deletions robots/lite_dummy/cad/ros2_control.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@

"base_link": {"name": "base_link", "child": "chest"},

"combined_block_name": "LiteHardware",

"_comment_neck": "The neck actuators are not on the bus. Its kinematic links live in the description xacro for robot_state_publisher, but it gets no group and no joints here until the hardware is wired (CAN ids 31..33 by convention).",

"groups": [
Expand Down
Loading
Loading