Namespaces and Remapping#

If you went through the above link on launch files, you might have come across the terms namespaces and remapping. Understanding namespaces and remapping is very crucial to working with large ROS software stacks.

Consider you have two Duckiebots - donald and daisy. You want them to communicate with each other so that you use one rosmaster for both the robots. You have two copies of the same node running on each of them which grabs images from the camera and publishes them on a topic called /image. Do you see a problem here? Would it not be better if they were called /donald/image and /daisy/image? Here donald and daisy are ROS namespaces.

What if you were dealing with a robot which has two cameras? The names /daisy/camera_left/image and /daisy/camera_right/image are definitely the way to go. You should also be able to do this without writing a new Python file for the second camera.

Let’s see how we can do this. First of all, we need to make sure that all the topics used by your Duckiebot are within its namespace.

Edit the ./packages/my_package/launch/multiple_nodes.launch to look like this:

<launch>

  <group ns="$(arg veh)">  

    <node pkg="my_package" type="my_publisher_node.py" name="my_publisher_node" output="screen"/>
    <node pkg="my_package" type="my_subscriber_node.py" name="my_subscriber_node"  output="screen"/>

  </group>

</launch>

Then edit the roslaunch command in ./launch.sh as follows:

roslaunch my_package multiple_nodes.launch veh:=$VEHICLE_NAME

Build and run the image. Once again run rqt_graph like above. What changed?

As a next step, we need to ensure that we can launch multiple instances of the same node with different names, and publishing topics corresponding to those names. For example, running two camera nodes with names camera_left and camera_right respectively, publishing topics /my_robot/camera_left/image and /my_robot/camera_right/image.

Notice how the node tag in the launch file has a name attribute. You can have multiple node tags with different names for the same python node file. The name provided here will override the name you give inside the python file for the node.

Edit the ./packages/my_package/launch/multiple_nodes.launch file to have two publishers and two subscribers as below:

<launch>

  <group ns="$(arg veh)">  

    <node pkg="my_package" type="my_publisher_node.py" name="my_publisher_node_1" output="screen"/>
    <node pkg="my_package" type="my_publisher_node.py" name="my_publisher_node_2" output="screen"/>
    <node pkg="my_package" type="my_subscriber_node.py" name="my_subscriber_node_1"  output="screen"/>
    <node pkg="my_package" type="my_subscriber_node.py" name="my_subscriber_node_2"  output="screen"/>

   </group>

</launch>

Check rqt_graph. All communications are happening on one topic. You still cannot differentiate between topics being published by multiple nodes. Turns out doing that is very simple. Open the file ./packages/my_package/src/my_publisher_node.py and edit the declaration of the publisher from

...
        self.pub = rospy.Publisher('chatter', String, queue_size=10)
...

to

...
        self.pub = rospy.Publisher('~chatter', String, queue_size=10)
...

All we did was add a tilde(~) sign in the beginning of the topic. Names that start with a ~ in ROS are private names. They convert the node’s name into a namespace. Note that since the nodes are already being launched inside the namespace of the robot, the node’s namespace would be nested inside it. Read more about private namespaces here

Do this for the subscriber node as well. Run the experiment and observe rqt_graph again. This time, switch the graph type from Nodes only to Nodes/Topics (all) and uncheck Hide: Dead sinks and Hide: Leaf topics. Play with these two “Hide” options to see what they mean.

All looks very well organized, except that no nodes are speaking to any other node. This is where the magic of remapping begins.

Edit the ./packages/my_package/launch/multiple_nodes.launch file to contain the following:

<launch>

  <group ns="$(arg veh)">  

    <node pkg="my_package" type="my_publisher_node.py" name="my_publisher_node_1" output="screen"/>
    <node pkg="my_package" type="my_publisher_node.py" name="my_publisher_node_2" output="screen"/>

    <node pkg="my_package" type="my_subscriber_node.py" name="my_subscriber_node_1"  output="screen">
        <remap from="~/chatter" to="/$(arg veh)/my_publisher_node_1/chatter"/>
    </node>

    <node pkg="my_package" type="my_subscriber_node.py" name="my_subscriber_node_2"  output="screen">
        <remap from="~/chatter" to="/$(arg veh)/my_publisher_node_2/chatter"/>
    </node>

   </group>

</launch>

Check rqt_graph. Does it make sense?

Now, replace

<node pkg="my_package" type="my_subscriber_node.py" name="my_subscriber_node_1"  output="screen">
    <remap from="~/chatter" to="/$(arg veh)/my_publisher_node_1/chatter"/>
</node>

with

<node pkg="my_package" type="my_subscriber_node.py" name="my_subscriber_node_1"  output="screen">
    <remap from="~/chatter" to="my_publisher_node_1/chatter"/>
</node>

Does it still work? Why?

How about if you replace it with this:

<node pkg="my_package" type="my_subscriber_node.py" name="my_subscriber_node_1"  output="screen">
    <remap from="~/chatter" to="/my_publisher_node_1/chatter"/>
</node>

How about this?

<remap from="my_subscriber_node_1/chatter" to="my_publisher_node_1/chatter"/>
<node pkg="my_package" type="my_subscriber_node.py" name="my_subscriber_node_1"  output="screen"/>

Or this?

<remap from="~my_subscriber_node_1/chatter" to="~my_publisher_node_1/chatter"/>
<node pkg="my_package" type="my_subscriber_node.py" name="my_subscriber_node_1"  output="screen"/>

Can you explain why some of them worked, while some did not?