From ab1b9f93237618fdc9dac35910ea739d3cee73fc Mon Sep 17 00:00:00 2001 From: Fastroof Date: Fri, 6 Nov 2026 00:54:01 +0200 Subject: [PATCH] feat(bringup): add robot_ns to sim_robot.launch.py For a simulation shared by several robots (the arena of the KPI Rover hub): - robot_ns names the Gazebo model, prefixes the Gazebo topics of the lidar and the IMU and is the namespace of the controller manager. The controller manager topics (robot_description, joint_states, cmd_vel, odom) move under /, so what runs the shared simulation bridges them; /scan, /imu_broadcaster/imu, /clock and the frames stay the same. - load_controllers:=false leaves loading the controllers to what runs the shared simulation. - The controller config matches the controller manager in any namespace (/**/). --- README.md | 5 +++++ .../kpi_rover_bringup/config/gz_bridge.yaml | 8 +++---- .../config/kpi_rover_controllers.yaml | 8 ++++--- .../launch/sim_robot.launch.py | 22 +++++++++++++++---- .../launch/description.launch.py | 8 ++++++- .../kpi_rover_description/urdf/imu.xacro | 2 +- .../kpi_rover_description/urdf/lidar.xacro | 2 +- .../urdf/robot.urdf.xacro | 2 ++ .../urdf/ros2_control.xacro | 1 + 9 files changed, 44 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 87ba8f4..e4f7b02 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,11 @@ its previews. `sim.launch.py` is `world.launch.py` (Gazebo with the world) and `sim_robot.launch.py` (the simulated robot with the same localization, SLAM and navigation as the real one) together, with RViz and joystick teleop; tools such as the GUI start them one by one. +In a simulation shared by several robots each one is started with `sim_robot.launch.py robot_ns:=`: the name +is given to the model, its Gazebo topics and its controller manager. The topics of the controller manager move under +`/` too (`robot_description`, `joint_states`, `diff_drive_controller/cmd_vel`, `diff_drive_controller/odom`), so +`robot_ns` works only with what runs the shared simulation and bridges them (the arena of the KPI Rover hub); +with `load_controllers:=false` it also loads the controllers. Keyboard control (second zellij pane): ```bash diff --git a/src/platform/kpi_rover_bringup/config/gz_bridge.yaml b/src/platform/kpi_rover_bringup/config/gz_bridge.yaml index 558e3b2..e4049b8 100644 --- a/src/platform/kpi_rover_bringup/config/gz_bridge.yaml +++ b/src/platform/kpi_rover_bringup/config/gz_bridge.yaml @@ -1,16 +1,16 @@ -- ros_topic_name: "clock" - gz_topic_name: "clock" +- ros_topic_name: "/clock" + gz_topic_name: "/clock" ros_type_name: "rosgraph_msgs/msg/Clock" gz_type_name: "gz.msgs.Clock" direction: GZ_TO_ROS -- ros_topic_name: "scan" +- ros_topic_name: "/scan" gz_topic_name: "scan" ros_type_name: "sensor_msgs/msg/LaserScan" gz_type_name: "gz.msgs.LaserScan" direction: GZ_TO_ROS -- ros_topic_name: "imu_broadcaster/imu" +- ros_topic_name: "/imu_broadcaster/imu" gz_topic_name: "imu_broadcaster/imu" ros_type_name: "sensor_msgs/msg/Imu" gz_type_name: "gz.msgs.IMU" diff --git a/src/platform/kpi_rover_bringup/config/kpi_rover_controllers.yaml b/src/platform/kpi_rover_bringup/config/kpi_rover_controllers.yaml index 1ed5018..7e591d5 100644 --- a/src/platform/kpi_rover_bringup/config/kpi_rover_controllers.yaml +++ b/src/platform/kpi_rover_bringup/config/kpi_rover_controllers.yaml @@ -1,4 +1,4 @@ -controller_manager: +/**/controller_manager: ros__parameters: # TODO Return back normal rate. It was decrised for debugging update_rate: 30 @@ -12,7 +12,7 @@ controller_manager: imu_broadcaster: type: imu_sensor_broadcaster/IMUSensorBroadcaster -diff_drive_controller: +/**/diff_drive_controller: ros__parameters: left_wheel_names: ["front_left_wheel_joint", "rear_left_wheel_joint"] right_wheel_names: ["front_right_wheel_joint", "rear_right_wheel_joint"] @@ -28,12 +28,14 @@ diff_drive_controller: publish_wheel_tf: true publish_cmd: true enable_odom_tf: false + # The frames stay odom, base_footprint in a namespace too (robot_ns of sim_robot.launch.py) + tf_frame_prefix_enable: false position_feedback: true linear: x: {min_velocity: -1.0, max_velocity: 1.0} -imu_broadcaster: +/**/imu_broadcaster: ros__parameters: sensor_name: "imu" frame_id: "imu_link" diff --git a/src/platform/kpi_rover_bringup/launch/sim_robot.launch.py b/src/platform/kpi_rover_bringup/launch/sim_robot.launch.py index 03c484c..1b2e5b1 100644 --- a/src/platform/kpi_rover_bringup/launch/sim_robot.launch.py +++ b/src/platform/kpi_rover_bringup/launch/sim_robot.launch.py @@ -3,7 +3,7 @@ from launch.conditions import IfCondition from launch.event_handlers import OnProcessExit from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, PathJoinSubstitution +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution, PythonExpression from launch_ros.actions import Node from launch_ros.substitutions import FindPackageShare @@ -17,6 +17,7 @@ def generate_launch_description(): y_pose = LaunchConfiguration('y_pose') z_pose = LaunchConfiguration('z_pose') yaw_pose = LaunchConfiguration('yaw_pose') + robot_ns = LaunchConfiguration('robot_ns') declared_arguments = [ DeclareLaunchArgument( @@ -32,6 +33,14 @@ def generate_launch_description(): DeclareLaunchArgument( 'nav', default_value='true', description='Start Nav2 (handles the RViz 2D Goal Pose)'), + DeclareLaunchArgument( + 'robot_ns', default_value='', + description='Name of the robot in a simulation shared by several robots: its model, ' + 'Gazebo topics and controller manager. The controller manager topics move under it ' + 'and need a bridge from what runs the shared simulation'), + DeclareLaunchArgument( + 'load_controllers', default_value='true', + description='Load the controllers; false when something else loads them'), ] # robot_state_publisher with the sim_mode URDF (gz_ros2_control plugin) @@ -42,6 +51,7 @@ def generate_launch_description(): 'use_sim_time': 'true', 'sim_mode': 'true', 'wheel_radius': wheel_radius, + 'robot_ns': robot_ns, }.items(), ) @@ -49,7 +59,7 @@ def generate_launch_description(): package='ros_gz_sim', executable='create', arguments=[ - '-name', 'kpi_rover', + '-name', PythonExpression(["'", robot_ns, "' or 'kpi_rover'"]), '-topic', 'robot_description', '-x', x_pose, '-y', y_pose, @@ -59,11 +69,14 @@ def generate_launch_description(): output='screen', ) + # Relative Gazebo topics of the config get the namespace (/scan), the ROS topics don't gz_bridge = Node( package='ros_gz_bridge', executable='parameter_bridge', + namespace=robot_ns, parameters=[{ 'config_file': PathJoinSubstitution([pkg_bringup, 'config', 'gz_bridge.yaml']), + 'expand_gz_topic_names': True, }], output='screen', ) @@ -71,7 +84,7 @@ def generate_launch_description(): joint_state_broadcaster = Node( package='controller_manager', executable='spawner', - arguments=['joint_state_broadcaster', '--switch-timeout', '60'], + arguments=['joint_state_broadcaster', '-c', [robot_ns, '/controller_manager'], '--switch-timeout', '60'], output='screen', ) @@ -80,6 +93,7 @@ def generate_launch_description(): executable='spawner', arguments=[ 'diff_drive_controller', + '-c', [robot_ns, '/controller_manager'], '--switch-timeout', '60', '--controller-ros-args', '-r /diff_drive_controller/odom:=/odom', ], @@ -90,7 +104,7 @@ def generate_launch_description(): spawn_controllers = RegisterEventHandler(OnProcessExit( target_action=spawn_robot, on_exit=[joint_state_broadcaster, diff_drive_controller], - )) + ), condition=IfCondition(LaunchConfiguration('load_controllers'))) # Turns unstamped /cmd_vel (Nav2, teleop_twist_keyboard) into the stamped input of the controller twist_stamper = Node( diff --git a/src/platform/kpi_rover_description/launch/description.launch.py b/src/platform/kpi_rover_description/launch/description.launch.py index 7f2c0f1..fde3e2c 100644 --- a/src/platform/kpi_rover_description/launch/description.launch.py +++ b/src/platform/kpi_rover_description/launch/description.launch.py @@ -16,6 +16,7 @@ def generate_launch_description(): serial_device = LaunchConfiguration('serial_device') baud_rate = LaunchConfiguration('baud_rate') wheel_radius = LaunchConfiguration('wheel_radius') + robot_ns = LaunchConfiguration('robot_ns') pkg_share = FindPackageShare('kpi_rover_description') @@ -28,7 +29,8 @@ def generate_launch_description(): ' sim_mode:=', sim_mode, ' serial_device:=', serial_device, ' baud_rate:=', baud_rate, - ' wheel_radius:=', wheel_radius + ' wheel_radius:=', wheel_radius, + ' robot_ns:=', robot_ns ]) # Launch! @@ -53,6 +55,10 @@ def generate_launch_description(): 'wheel_radius', default_value='0.034', description='Wheel radius in meters'), + DeclareLaunchArgument( + 'robot_ns', + default_value='', + description='Name of the robot in a simulation shared by several robots (Gazebo topics, controller manager)'), Node( package='robot_state_publisher', diff --git a/src/platform/kpi_rover_description/urdf/imu.xacro b/src/platform/kpi_rover_description/urdf/imu.xacro index d2330a4..33b70e1 100644 --- a/src/platform/kpi_rover_description/urdf/imu.xacro +++ b/src/platform/kpi_rover_description/urdf/imu.xacro @@ -12,7 +12,7 @@ 1 false - /imu_broadcaster/imu + $(arg robot_ns)/imu_broadcaster/imu imu_link diff --git a/src/platform/kpi_rover_description/urdf/lidar.xacro b/src/platform/kpi_rover_description/urdf/lidar.xacro index c1c4be7..c7e5a38 100644 --- a/src/platform/kpi_rover_description/urdf/lidar.xacro +++ b/src/platform/kpi_rover_description/urdf/lidar.xacro @@ -36,7 +36,7 @@ 30 - scan + $(arg robot_ns)/scan laser_frame diff --git a/src/platform/kpi_rover_description/urdf/robot.urdf.xacro b/src/platform/kpi_rover_description/urdf/robot.urdf.xacro index 1b3242a..c333926 100644 --- a/src/platform/kpi_rover_description/urdf/robot.urdf.xacro +++ b/src/platform/kpi_rover_description/urdf/robot.urdf.xacro @@ -6,6 +6,8 @@ + + diff --git a/src/platform/kpi_rover_description/urdf/ros2_control.xacro b/src/platform/kpi_rover_description/urdf/ros2_control.xacro index 6c36bc4..e184d22 100644 --- a/src/platform/kpi_rover_description/urdf/ros2_control.xacro +++ b/src/platform/kpi_rover_description/urdf/ros2_control.xacro @@ -56,6 +56,7 @@ $(find kpi_rover_bringup)/config/kpi_rover_controllers.yaml + $(arg robot_ns)