Skip to content

#10 Feature/launch files xml - #11

Merged
mcamurri merged 8 commits into
ros2-jazzy-develfrom
feature/launch_files_xml
Jul 28, 2026
Merged

#10 Feature/launch files xml#11
mcamurri merged 8 commits into
ros2-jazzy-develfrom
feature/launch_files_xml

Conversation

@AlexBarbi

@AlexBarbi AlexBarbi commented Jul 8, 2026

Copy link
Copy Markdown

Summary

Ports the legacy ROS 1-style .launch files in legged_controllers, legged_unitree_description and legged_unitree_hw to native ROS 2 XML launch format (*_launch.xml). Also confirmed that both the ocs2_legged_robot_ros demo and the legged_controllers controller loading work end-to-end with these new launch files — see attached videos.

Demos

ocs2_legged_robot_ros demo

LeggedRobot_example.mp4

Shows the keyboard-driven gait command (legged_robot_gait_command) working against ocs2_legged_robot_ros, confirming the demo still works after the launch file port.

legged_controllers demo

LeggedControl.mp4

Shows Gazebo (gz) starting from empty_world_launch.xml and the controllers loading successfully from load_controller_launch.xml.

Changes

legged_unitree_description: empty_world.launchempty_world_launch.xml

Beyond the mechanical ROS 1 → ROS 2 syntax translation ($(find ...)$(find-pkg-share ...), docdescription, paramlet, typeexec, etc.), three things changed because Gazebo Classic + gazebo_ros don't exist in this ROS 2 setup:

  • Added a robot_state_publisher node. In ROS 1, gazebo_ros/spawn_model implicitly took care of publishing the robot's TF tree. In ROS 2 that's no longer implicit, so robot_state_publisher is now launched explicitly with legged_robot_description as its robot_description param.
  • Swapped gazebo_ros for ros_gz_sim. empty_world.launch's gazebo_ros/empty_world.launch include and spawn_model node are replaced with ros_gz_sim's gz_sim.launch.py (loads empty_world.world) and the ros_gz_sim create node (spawns the robot from the robot_description topic).
  • Added a ros_gz_bridge parameter_bridge node for /clock. Gazebo (gz) publishes sim time on its own message type; without bridging /clock (gz.msgs.Clockrosgraph_msgs/msg/Clock), use_sim_time never gets a clock and ROS 2 nodes stall on sim time.

legged_controllers: load_controller.launchload_controller_launch.xml

  • Controller spawning moved from ros_control to ros2_control, which changed both the spawner mechanics and the controller names:
    • One controller_manager/spawner call loading ["controllers/joint_state_controller", "controllers/legged_controller", "controllers/imu_sensor_controller"] → three separate controller_manager/spawner nodes, one per controller.
    • Names updated to match ros2_control's broadcaster convention:
      • controllers/joint_state_controllerjoint_state_broadcaster
      • controllers/imu_sensor_controllerimu_sensor_broadcaster
      • controllers/legged_controllerlegged_controller
  • controllers.yaml updated to match: dropped the standalone joint_state_broadcaster and legged_controller parameter blocks and params that don't apply to ros2_control broadcasters (map_interface_to_joint_state, use_local_topics, use_urdf_to_filter, publish_rate), keeping only what imu_sensor_broadcaster needs (sensor_name, frame_id).
  • Also ported joy_teleop.launch.pyjoy_teleop_launch.xml for consistency with the rest of the package.

legged_unitree_hw

Same .launch.py*_launch.xml conversion for a1, aliengo, go1 and the top-level legged_unitree_hw.launch.py. Not tested on real hardware — README updated to flag this.

Known limitation: joypad gait switching doesn't work

The joy:=true path (legged_robot_gait_joy_command) is not functionalocs2_legged_robot_ros doesn't ship a ROS 2 package/executable for the joypad-based gait command, so there's nothing for that node to run. The default keyboard-based gait command (legged_robot_gait_command, joy:=false) is unaffected and is what's used in the ocs2_legged_robot_ros demo above.

Testing

  • ros2 launch legged_unitree_description empty_world_launch.xml — Gazebo (gz) starts, robot spawns, TF is published, /clock is bridged correctly.
  • ros2 launch legged_controllers load_controller_launch.xml — all three controllers load successfully (video attached).
  • ocs2_legged_robot_ros keyboard gait demo — confirmed working (video attached).
  • Joypad gait switching (joy:=true) — known not to work, see above.
  • legged_unitree_hw launch files — not tested on physical hardware.

Tested on WSL2 -> Ubuntu-24.04

@AlexBarbi
AlexBarbi requested a review from mcamurri July 8, 2026 07:08
@AlexBarbi AlexBarbi self-assigned this Jul 8, 2026
@AlexBarbi AlexBarbi changed the title 10# Feature/launch files xml #10 Feature/launch files xml Jul 8, 2026
@mcamurri

mcamurri commented Jul 8, 2026

Copy link
Copy Markdown

The joypad gait switching was implemented by me here:
idra-lab/ocs2@dad78a6

if you cherry pick the same mods on the ROS2 branch of our ocs2 fork, it should work.

You can test it with a virtual joypad like this one: https://discourse.openrobotics.org/t/ros-2-rqt-virtual-joystick-smooth-robot-control-without-hardware/50582

When that's done and tested, I think we can merge and leave the testing on the hardware for a later PR

@mcamurri mcamurri left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally good we need a little adjustments, check my previous comment also.

controller_manager:
ros__parameters:
update_rate: 100 # Hz
update_rate: 100

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leave the comment so people know it's hertz

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with the joy node is that was not ported to ros2. So should i open an other issue to port it?
image
image

@mcamurri mcamurri Jul 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, on ocs2

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated joy.yaml to make it compatible with ros2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are making new files, I would enforce the two spaces rule for indentation, thanks.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two spaces for indent.


<let name="legged_robot_description" value="$(command 'xacro $(find-pkg-share legged_unitree_description)/urdf/robot.xacro robot_type:=$(var robot_type)')"/>

<node pkg="legged_common" exec="generate_urdf.sh" output="screen" name="generate_urdf"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of the generate_urdf.sh script? It looks like it's doing the same as the command in line 4.

Check what does generate_urdf.sh is actually doing, was done in the original code and why, and whether it is still necessary.

My best guess is that it needs the urdf to load into the robot state publisher, but also needs a physical file to load it into pinocchio at runtime. But there's the risk the two descriptions are not exactly identical.

Ideally, either the content of the file should be dumped into the parameter, or viceversa.

In the future we will get rid of this anyway and make specific launch files for specific robots using the official URDF definition.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<let name="legged_robot_description" value="$(command 'xacro ...')"/>
runs xacro once and holds the result in memory, for
robot_state_publisher's robot_description parameter.

generate_urdf.sh runs the same xacro file/args again and writes the
result to a file, /tmp/legged_control/<robot_type>.urdf, because
LeggedInterface → Pinocchio (legged_interface/src/LeggedInterface.cpp,
createPinocchioInterface) needs an actual filesystem path. It can't take
the URDF as an in-memory string. That file is read later via the urdfFile
param in legged_controllers/launch/load_controller_launch.xml:.

So they're not redundant. They serve two different consumers that want the
URDF in two different shapes (string vs. path). This pattern already existed
in the ROS1 launch files and predates the ROS2 port.

The two descriptions were not unified and that risk is real but
untouched for now.
Both invocations use identical inputs today, so the
output text matches, but they're still two independent xacro runs with no
cross-check: if someone adds an argument to one and not the other, the TF
description and the dynamics-model URDF would silently diverge.

What was actually broken, and what got fixed: in
legged_unitree_hw_launch.xml specifically, line 4's legged_robot_description
was computed but never consumed. No node in the real-hardware launch chain
used it, so the file was necessary but the in-memory string was pure dead
computation. Added a robot_state_publisher node consuming it, mirroring
empty_world_launch.xml. Now both forms of the URDF are actually used again.

@mcamurri
mcamurri merged commit 31e6d89 into ros2-jazzy-devel Jul 28, 2026
@mcamurri
mcamurri deleted the feature/launch_files_xml branch July 28, 2026 11:43
AlexBarbi added a commit that referenced this pull request Jul 30, 2026
Co-authored-by: AlexBarbi <alejandro.enrique.barbi@gmai.com>
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.

2 participants