Summary:
When launching a VRX simulation with a standard WAM-V configuration that has multiple
camera sensors, ros2 node list emits a warning that multiple nodes in the graph share the
exact same fully-qualified name /wamv/optical_frame_publisher. Each instance is spawned
once per camera/RGBD sensor, but none are given a unique name= override in the launch
code, so they all collide on the default executable name.
The nodes themselves work correctly (topic remappings are unique per sensor, so image
data flows properly), but the graph warning is noisy and makes introspection tools (ros2
node list, ros2 node info, parameter queries) ambiguous.
Steps to reproduce:
- Fresh Ubuntu 24.04 install with ROS 2 Jazzy and Gazebo Harmonic
- Clone VRX (jazzy branch) into a ROS 2 workspace:
mkdir -p ~/vrx_ws/src && cd ~/vrx_ws/src
git clone -b jazzy https://github.com/osrf/vrx.git
cd ~/vrx_ws && colcon build --merge-install
source install/setup.bash
- Launch the default VRX competition world with a WAM-V:
ros2 launch vrx_gz competition.launch.py world:=sydney_regatta
- In a second terminal, list the nodes using the command:
ros2 node list
Expected behavior:
Each optical_frame_publisher instance should have a unique fully-qualified name so that
ros2 node list shows distinct entries and no duplicate-name warning is emitted.
Actual behavior:
$ ros2 node list
WARNING: Be aware that there are nodes in the graph that share an exact name, which can
have unintended side effects.
/buran_controller_node
/oko_perception_node
/ros_gz_bridge
/rosapi
/rosbridge_websocket
/rviz2
/sputnik_planner_node
/transform_listener_impl_62b1257ae1c0
/wamv/frame_publisher
/wamv/optical_frame_publisher
/wamv/optical_frame_publisher
/wamv/optical_frame_publisher
/wamv/robot_state_publisher
/wamv/ros_gz_bridge
/waypoint_visualizer_node
/web_video_server
Note the three identical /wamv/optical_frame_publisher entries and the warning banner at
the top.
(The extra nodes buran_controller_node, sputnik_planner_node, etc. come from my own
downstream boat navigation stack — they are not part of VRX and not relevant to this
issue. The warning reproduces on an unmodified VRX launch as well.)
Root cause:
In vrx_gz/src/vrx_gz/model.py around lines 119–144, the payload iteration spawns
optical_frame_publisher nodes without passing a name= argument:
for sensor_name, value in self.payload.items():
...
if sensor_type == sdf.Sensortype.CAMERA:
ros_sensor_prefix = f'sensors/cameras/{sensor_name}'
nodes.append(Node(
package='vrx_ros',
executable='optical_frame_publisher',
arguments=['1'],
remappings=[('input/image', f'{ros_sensor_prefix}/image_raw'),
('output/image', f'{ros_sensor_prefix}/optical/image_raw'),
('input/camera_info', f'{ros_sensor_prefix}/camera_info'),
('output/camera_info',
f'{ros_sensor_prefix}/optical/camera_info')]))
elif sensor_type == sdf.Sensortype.RGBD_CAMERA:
ros_sensor_prefix = f'sensors/cameras/{sensor_name}'
nodes.append(Node( # RGB stream
package='vrx_ros',
executable='optical_frame_publisher',
...))
nodes.append(Node( # depth stream
package='vrx_ros',
executable='optical_frame_publisher',
...))
Without an explicit name=, each Node falls back to the default node name hardcoded in the
optical_frame_publisher C++ executable (vrx_ros/src/optical_frame_publisher.cc), so
every instance registers under the same name optical_frame_publisher. The remappings make
topic I/O unique, but ROS graph identity remains collided.
Impact:
- Functional: None observed — image topics are correctly remapped per sensor and the
perception pipeline (LiDAR + cameras) works end-to-end.
- Usability:
- ros2 node list emits a misleading warning that makes it harder to audit the graph for
actual problems.
- ros2 node info /wamv/optical_frame_publisher returns information for an unpredictable
one of the three instances.
- Parameter queries (ros2 param get /wamv/optical_frame_publisher ...) are
non-deterministic which node answers.
- Downstream tooling (my own health-check script, for example) cannot reliably
distinguish the three nodes.
Reproduces on:
Both the jazzy branch (v3.1.2, HEAD 7609d1b) and the main branch — verified by
inspecting vrx_gz/src/vrx_gz/model.py on both branches; the three Node() declarations are
byte-identical and none pass a name= argument.
Suggested fix:
Pass a unique name= to each Node() call, derived from the existing sensor_name loop
variable (which is already guaranteed unique per sensor since it's used to build topic
prefixes):
if sensor_type == sdf.Sensortype.CAMERA:
ros_sensor_prefix = f'sensors/cameras/{sensor_name}'
nodes.append(Node(
package='vrx_ros',
executable='optical_frame_publisher',
name=f'optical_frame_publisher_{sensor_name}',
arguments=['1'],
remappings=[...]))
elif sensor_type == sdf.Sensortype.RGBD_CAMERA:
ros_sensor_prefix = f'sensors/cameras/{sensor_name}'
nodes.append(Node( # RGB stream
package='vrx_ros',
executable='optical_frame_publisher',
name=f'optical_frame_publisher_{sensor_name}',
arguments=['1'],
remappings=[...]))
nodes.append(Node( # depth stream
package='vrx_ros',
executable='optical_frame_publisher',
name=f'optical_frame_publisher_{sensor_name}_depth',
arguments=['1'],
remappings=[...]))
This preserves every existing topic remapping and only affects the node's display name in
the ROS graph. After the fix, ros2 node list would show e.g.:
/wamv/optical_frame_publisher_front_left_camera
/wamv/optical_frame_publisher_front_right_camera
/wamv/optical_frame_publisher_middle_camera
…and the duplicate-name warning would disappear.
System setup:
- OS: Ubuntu 24.04.4 LTS (Noble Numbat)
- Kernel: Linux 6.17.0-20-generic
- ROS 2 distro: Jazzy
- Gazebo: Harmonic (Gazebo Sim 8.11.0, gz-harmonic 1.0.0)
- VRX version: v3.1.2 (branch
jazzy, HEAD 7609d1b)
- Platform: Dell Precision 7560
Summary:
When launching a VRX simulation with a standard WAM-V configuration that has multiple
camera sensors, ros2 node list emits a warning that multiple nodes in the graph share the
exact same fully-qualified name /wamv/optical_frame_publisher. Each instance is spawned
once per camera/RGBD sensor, but none are given a unique name= override in the launch
code, so they all collide on the default executable name.
The nodes themselves work correctly (topic remappings are unique per sensor, so image
data flows properly), but the graph warning is noisy and makes introspection tools (ros2
node list, ros2 node info, parameter queries) ambiguous.
Steps to reproduce:
mkdir -p ~/vrx_ws/src && cd ~/vrx_ws/src
git clone -b jazzy https://github.com/osrf/vrx.git
cd ~/vrx_ws && colcon build --merge-install
source install/setup.bash
ros2 launch vrx_gz competition.launch.py world:=sydney_regatta
ros2 node list
Expected behavior:
Each optical_frame_publisher instance should have a unique fully-qualified name so that
ros2 node list shows distinct entries and no duplicate-name warning is emitted.
Actual behavior:
$ ros2 node list
WARNING: Be aware that there are nodes in the graph that share an exact name, which can
have unintended side effects.
/buran_controller_node
/oko_perception_node
/ros_gz_bridge
/rosapi
/rosbridge_websocket
/rviz2
/sputnik_planner_node
/transform_listener_impl_62b1257ae1c0
/wamv/frame_publisher
/wamv/optical_frame_publisher
/wamv/optical_frame_publisher
/wamv/optical_frame_publisher
/wamv/robot_state_publisher
/wamv/ros_gz_bridge
/waypoint_visualizer_node
/web_video_server
Note the three identical /wamv/optical_frame_publisher entries and the warning banner at
the top.
(The extra nodes buran_controller_node, sputnik_planner_node, etc. come from my own
downstream boat navigation stack — they are not part of VRX and not relevant to this
issue. The warning reproduces on an unmodified VRX launch as well.)
Root cause:
In vrx_gz/src/vrx_gz/model.py around lines 119–144, the payload iteration spawns
optical_frame_publisher nodes without passing a name= argument:
for sensor_name, value in self.payload.items():
...
if sensor_type == sdf.Sensortype.CAMERA:
ros_sensor_prefix = f'sensors/cameras/{sensor_name}'
nodes.append(Node(
package='vrx_ros',
executable='optical_frame_publisher',
arguments=['1'],
remappings=[('input/image', f'{ros_sensor_prefix}/image_raw'),
('output/image', f'{ros_sensor_prefix}/optical/image_raw'),
('input/camera_info', f'{ros_sensor_prefix}/camera_info'),
('output/camera_info',
f'{ros_sensor_prefix}/optical/camera_info')]))
elif sensor_type == sdf.Sensortype.RGBD_CAMERA:
ros_sensor_prefix = f'sensors/cameras/{sensor_name}'
nodes.append(Node( # RGB stream
package='vrx_ros',
executable='optical_frame_publisher',
...))
nodes.append(Node( # depth stream
package='vrx_ros',
executable='optical_frame_publisher',
...))
Without an explicit name=, each Node falls back to the default node name hardcoded in the
optical_frame_publisher C++ executable (vrx_ros/src/optical_frame_publisher.cc), so
every instance registers under the same name optical_frame_publisher. The remappings make
topic I/O unique, but ROS graph identity remains collided.
Impact:
perception pipeline (LiDAR + cameras) works end-to-end.
actual problems.
one of the three instances.
non-deterministic which node answers.
distinguish the three nodes.
Reproduces on:
Both the jazzy branch (v3.1.2, HEAD 7609d1b) and the main branch — verified by
inspecting vrx_gz/src/vrx_gz/model.py on both branches; the three Node() declarations are
byte-identical and none pass a name= argument.
Suggested fix:
Pass a unique name= to each Node() call, derived from the existing sensor_name loop
variable (which is already guaranteed unique per sensor since it's used to build topic
prefixes):
if sensor_type == sdf.Sensortype.CAMERA:
ros_sensor_prefix = f'sensors/cameras/{sensor_name}'
nodes.append(Node(
package='vrx_ros',
executable='optical_frame_publisher',
name=f'optical_frame_publisher_{sensor_name}',
arguments=['1'],
remappings=[...]))
elif sensor_type == sdf.Sensortype.RGBD_CAMERA:
ros_sensor_prefix = f'sensors/cameras/{sensor_name}'
nodes.append(Node( # RGB stream
package='vrx_ros',
executable='optical_frame_publisher',
name=f'optical_frame_publisher_{sensor_name}',
arguments=['1'],
remappings=[...]))
nodes.append(Node( # depth stream
package='vrx_ros',
executable='optical_frame_publisher',
name=f'optical_frame_publisher_{sensor_name}_depth',
arguments=['1'],
remappings=[...]))
This preserves every existing topic remapping and only affects the node's display name in
the ROS graph. After the fix, ros2 node list would show e.g.:
/wamv/optical_frame_publisher_front_left_camera
/wamv/optical_frame_publisher_front_right_camera
/wamv/optical_frame_publisher_middle_camera
…and the duplicate-name warning would disappear.
System setup:
jazzy, HEAD 7609d1b)