ROS2-012-Communication Mechanisms, Wrapping Up: Duplicate Topic Names
Introduction
We talked about duplicate node names earlier, so are there duplicate topic names too?
Yes, dude, yes there are☝🤓
Just like duplicate node names, in ROS2 topic names can clash too. Between different nodes, communication all depends on topics, and when topic names clash the system doesn't throw an exception, but nodes tied together by the same topic name may not belong to the same communication logic, which scrambles the communication and can even throw errors. In that case you may need to give the same topic name different values.
Unlike node names, though, some scenarios need you to avoid duplicate topic names, while other scenarios need you to change different topic names to the same one so that some feature works. So duplicate topic names are allowed in certain cases.
For example, two nodes belong to the same communication logic, but because the topic names differ between them, communication fails. In that case you need to change the two nodes' topic names from different to the same.
So how do we change a topic name in ROS2?
The strategies
As with duplicate node names, the same two common strategies apply:
- Namespace, i.e. putting a prefix on the topic's name; the prefix can be several levels deep, usually in the format of /xxx/yyy/zzz
- Name remapping, giving the topic an alias;
Note
When you set a topic name through a namespace, you need to make sure the topic type is not a global topic, otherwise it can't be changed. For details on topic types see the description below
How to do it
As with duplicate node names, there are generally the following common approaches:
1. Setting the node name with ros2 run
①. Set the namespace
The syntax is:
ros2 run <package name> <node name> --ros-args --remap __ns:=<namespace>For example:
ros2 run turtlesim turtlesim_node --ros-args --remap __ns:=/t1Before running that command in a terminal, checking the topic information with ros2 topic list shows information containing the following result:
/turtle1/cmd_velAnd after running that command in a terminal, checking the topic information with ros2 topic list shows information containing the following result:
/t1/turtle1/cmd_velIsn't this step the same as when we changed duplicate node names?
Indeed🤣 because a node changed this way has the namespace prefix added to its topics already — that is, every non-global topic under the node gets the namespace prefix — so for duplicate topic names, the fix here is no different from duplicate node names. As for what a global topic is, see the description below
②. Set name remapping
The syntax is:
ros2 run <package name> <node name> --ros-args --remap <original topic name>:=<remapped topic name>For example:
ros2 run turtlesim turtlesim_node --ros-args --remap /turtle1/cmd_vel:=/cmd_velBefore running that command in a terminal, checking the topic information with ros2 topic list shows information containing the following result:
/turtle1/cmd_velAfter running that command in a terminal, checking the node information with ros2 topic list shows information containing the following result:
/cmd_velWhat is this usually good for?
There's a package often used to control robots, teleop_twist_keyboard, which uses the topic name /cmd_vel when it runs. Take ROS2's most-used Hello World stand-in package — the little turtle package turtlesim: when you want to drive the little turtle in the window with a keyboard or other input device, the topic it uses for control is /turtle1/cmd_vel. If you want to control it with teleop_twist_keyboard, you have to change the two topics to one shared name before the data can flow and the little turtle can be driven. At that point you can use the approach above to change either side's topic name, for example changing /turtle1/cmd_vel to /cmd_vel.
2. Changing the topic name with a launch file
launch files can be written in Python, XML or YAML, and all three can set the namespace and the name remapping.
Before you use it...
When you use a launch file, remember to add the following at the install spot in CMakeLists.txt
install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})Of course, the launch files below cover the configuration for C++ nodes; if you want the configuration for Python nodes, see the launch file notes further on
①. Writing the launch file in Python to set the namespace and name remapping (recommended)
When you write a launch file in Python, you can create the node object to be launched with the Python class launch_ros.actions.Node, and that object's constructor takes two arguments, remappings and namespace, to set the topic's name and namespace. An example:
from launch import LaunchDesciption
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDesciption([
# set the namespace
Node(package="turtlesim",
executable="turtlesim_node",
namespace="t1"),
# set the name remapping
Node(package="turtlesim",
executable="turtlesim_node",
remappings=[("/turtle1/cmd_vel", "/cmd_vel")])
])Note
The function name above must be generate_launch_description, otherwise ROS2 won't recognise and run the function.
②. Writing the launch file in XML to set the namespace and name remapping
When you write a launch file in XML, you can set the topic's namespace through the namespace argument that node provides, and change the topic name through the remap child tag inside the node tag. In the remap child tag, the from argument is the topic name being changed and the to argument is the topic name after the change. An example:
<launch>
<!-- set the namespace -->
<node pkg="turtlesim" exec="turtlesim_node" namespace="t1"/>
<!-- set the name remapping -->
<node pkg="turtlesim" exec="turtlesim_node">
<remap from="/turtle1/cmd_vel" to="/cmd_vel" />
</node>
</launch>③. Writing the launch file in YAML to set the namespace and name remapping
When you write a launch file in YAML, you can likewise set the topic's namespace through the namespace argument that node provides, and change the topic name through the remap property under the node properties. In remap, the from argument is the topic name being changed and the to argument is the topic name after the change. An example:
launch:
# set the namespace
- node:
pkg: "turtlesim"
exec: "turtlesim_node"
namespace: "t1"
# set the name remapping
- node:
pkg: "turtlesim"
exec: "turtlesim_node"
remap: "ts1"
-
from: "/turtle1/cmd_vel"
to: "/cmd_vel"3. Changing the topic name in code
When we write a topic, we are in fact doing the relevant setup inside the matching constructors that rclcpp and rclpy provide, and topics built from different types of topic name differ somewhat depending on where they hang. In other words, setting a topic name in code is pretty flexible too. So for topic-name changes in code, here we have to lay them out by topic category.
Assumption
Assume that when creating the C++ or Python packages and nodes, the namespace used is ABC and the node name is XYZ. The node creation here follows the previous chapter, the part on rclcpp's API and the part on rclpy's API
Topic types and how they relate to topic names
How a topic's name is set has some connection to the node's namespace and the node's name. Topic names can be roughly put into three types:
Global topics
Property: a global topic refers to the ROS system; it sits on the same level as the node's namespace, hanging under the root directory
Format: a name defined starting with
/; it has nothing to do with thenamespaceor thenode name.Creating a publisher topic inside a custom node:
rclcpp example:
public: MyNode():Node("XYZ", "ABC"){ publisher_ = this->create_publisher<std_msgs::msgs::String>("/topic/chatter", 10); } ... private: rclcpp::Publisher<std_msgs::msgs::String>::SharedPtr publisher_;rclpy example:
class MyNode(Node): def __init__(self): super().__init__("XYZ", namespace="ABC") self.publisher_ = self.create_publisher(String, "/topic/chatter", 10);In this case the topic name used by that publisher is "/topic/chatter"; being a global topic, the name has nothing to do with the namespace
ABCor the node nameXYZ.Relative topics
Property: the topic refers to the node's namespace, sitting on the same level as the node name, hanging under the namespace
Format: a name defined not starting with
/; it sets its topic name by reference to thenamespace, and has nothing to do with thenode name.Creating a publisher topic inside a custom node:
rclcpp example:
public: MyNode():Node("XYZ", "ABC"){ publisher_ = this->create_publisher<std_msgs::msgs::String>("topic/chatter", 10); } ... private: rclcpp::Publisher<std_msgs::msgs::String>::SharedPtr publisher_;rclpy example:
class MyNode(Node): def __init__(self): super().__init__("XYZ", namespace="ABC") self.publisher_ = self.create_publisher(String, "topic/chatter", 10);Caution
Both of these use "topic/chatter" rather than the "/topic/chatter" used earlier — mind the difference!
In this case the topic name used by that publisher is "/ABC/topic/chatter"; being a relative topic, the name is tied to the namespace
ABCand has nothing to do with the node nameXYZ.Private topics
Property: the topic refers to the node name, is a child of the node name, hanging under the node name
Format: a name defined not starting with
/; it sets its topic name by reference to both thenamespaceand thenode name.Creating a publisher topic inside a custom node:
rclcpp example:
public: MyNode():Node("XYZ", "ABC"){ publisher_ = this->create_publisher<std_msgs::msgs::String>("~/topic/chatter", 10); } ... private: rclcpp::Publisher<std_msgs::msgs::String>::SharedPtr publisher_;rclpy example:
class MyNode(Node): def __init__(self): super().__init__("XYZ", namespace="ABC") self.publisher_ = self.create_publisher(String, "~/topic/chatter", 10);Caution
Both of these use "~/topic/chatter" rather than the "/topic/chatter" used earlier — mind the difference!
In this case the topic name used by that publisher is "/ABC/XYZ/topic/chatter"; being a private topic, the name is tied to both the namespace
ABCand the node nameXYZ.
In short, apart from the path descriptions, the rules for setting topic names are basically the same in rclcpp and rclpy, and the rules above apply just as well to the ros2 run command and to launch files.
Important
This article was translated by Hermes Agent; the original text can be found at here. The translation is for reference only. Translation time: 2026-11-05
