Giving a TurtleBot3 a Namespace for Multi-Robot Experiments

As I was researching, I noticed that ROBOTIS doesn’t provided a guide on how to run multiple TurtleBot3 robots together. It is especially dangerous if you run them in the same network because they all run on the same topic names and node names, which can interfere with their individual operation. So to help run multiple TurtleBots on the same network, you need to give each robot a unique namespace. The following guide will show you how to do this for the TurtleBot3.

For this guide, we will be using tb3_0 as the namespace we wish to use for our TurtleBot3 Burger robot. This helps us number our robots easier when running multiple robot experiments. This guide also assumes you have followed the procedure located here for installing and setting up your TurtleBot3 with ros2!

Step 1: Create a New ros2 Package

Start by changing into your src directory of your workspace that also contains the turtlebot3 and utils packages provided by ROBOTIS.

~$ cd ~/turtlebot3_ws/src
~$ ros2 pkg create my_tb3_launcher

Now, create two empty directories in the new package:

~$ cd ~/turtlebot3_ws/src/my_tb3_launcher
~$ mkdir launch
~$ mkdir param

Change into the launch directory and create a new bringup launch file.

~$ cd launch
~$ touch my_tb3_bringup.launch.py

Step 2: Copy and Modify Contents from the TB3 Bringup Package into Your Package

In the turtlebot3/turtlebot3_bringup ros2 package, copy the contents of robot.launch.py into the my_tb3_bringup.launch.py with the following changes marked as # comments in the following code:

import os

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
from launch.substitutions import ThisLaunchFileDir
from launch_ros.actions import Node


def generate_launch_description():
    TURTLEBOT3_MODEL = os.environ['TURTLEBOT3_MODEL']

    usb_port = LaunchConfiguration('usb_port', default='/dev/ttyACM0')

    tb3_param_dir = LaunchConfiguration(
        'tb3_param_dir',
        default=os.path.join(
            get_package_share_directory('my_tb3_launcher'),  # <--- CHANGE THIS!
            'param',
            TURTLEBOT3_MODEL + '.yaml'))

    use_sim_time = LaunchConfiguration('use_sim_time', default='false')

    return LaunchDescription([
        DeclareLaunchArgument(
            'use_sim_time',
            default_value=use_sim_time,
            description='Use simulation (Gazebo) clock if true'),

        DeclareLaunchArgument(
            'usb_port',
            default_value=usb_port,
            description='Connected USB port with OpenCR'),

        DeclareLaunchArgument(
            'tb3_param_dir',
            default_value=tb3_param_dir,
            description='Full path to turtlebot3 parameter file to load'),

        IncludeLaunchDescription(
            PythonLaunchDescriptionSource(
                [ThisLaunchFileDir(), '/turtlebot3_state_publisher.launch.py']),
            launch_arguments={'use_sim_time': use_sim_time}.items(),
        ),

        IncludeLaunchDescription(
            PythonLaunchDescriptionSource([ThisLaunchFileDir(), '/hlds_laser.launch.py']),  <--- CHANGE THIS
            launch_arguments={'port': '/dev/ttyUSB0', 'frame_id': 'base_scan'}.items(),
        ),

        Node(
            package='turtlebot3_node',
            node_executable='turtlebot3_ros',
            node_namespace='tb3_0',  # <------------------- ADD THIS!
            parameters=[tb3_param_dir],
            arguments=['-i', usb_port],
            output='screen'),
    ])

Next, copy the file turtlebot3_state_publisher.launch.py from the turtlebot3_bringup/launch directory into your package’s launch directory. Make sure it has the same name! Once complete, make the following changes as marked by the following comments:

import os

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node


def generate_launch_description():
    TURTLEBOT3_MODEL = os.environ['TURTLEBOT3_MODEL']

    use_sim_time = LaunchConfiguration('use_sim_time', default='false')
    urdf_file_name = 'turtlebot3_' + TURTLEBOT3_MODEL + '.urdf'

    print("urdf_file_name : {}".format(urdf_file_name))

    urdf = os.path.join(
        get_package_share_directory('turtlebot3_description'),
        'urdf',
        urdf_file_name)

    return LaunchDescription([
        DeclareLaunchArgument(
            'use_sim_time',
            default_value='false',
            description='Use simulation (Gazebo) clock if true'),

        Node(
            package='robot_state_publisher',
            node_executable='robot_state_publisher',
            node_name='robot_state_publisher',
            node_namespace='tb3_0',  # <------------------- ADD THIS!
            output='screen',
            parameters=[{'use_sim_time': use_sim_time}],
            arguments=[urdf]),
    ])

Finally, copy the file hlds_laser.launch.py from the hls_lfcd_lds_driver package located in the launch directory into your package’s launch directory. Again, make sure it has the same name!. Modify the launch file with the following changes marked by the comments below:

import os

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.actions import LogInfo
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node


def generate_launch_description():
    port = LaunchConfiguration('port', default='/dev/ttyUSB0')

    frame_id = LaunchConfiguration('frame_id', default='laser')

    return LaunchDescription([

        DeclareLaunchArgument(
            'port',
            default_value=port,
            description='Specifying usb port to connected lidar'),

        DeclareLaunchArgument(
            'frame_id',
            default_value=frame_id,
            description='Specifying frame_id of lidar. Default frame_id is \'laser\''),

        Node(
            package='hls_lfcd_lds_driver',
            node_executable='hlds_laser_publisher',
            node_name='hlds_laser_publisher',
            node_namespace='tb3_0',  # <------------------- ADD THIS!
            parameters=[{'port': port, 'frame_id': frame_id}],
            output='screen'),
    ])

Step 3: Modify the Parameter YAML File

Now copy the burger.yaml file located in the param directory of the turtlebot3_bringup package, and make the following modification at the top!

tb3_0:
  turtlebot3_node:
    ros__parameters:

      opencr:
        id: 200
        baud_rate: 1000000
        protocol_version: 2.0

      wheels:
        separation: 0.160
        radius: 0.033

      motors:
        profile_acceleration_constant: 214.577

        # [rev/min2]
        # ref) http://emanual.robotis.com/docs/en/dxl/x/xl430-w250/#profile-acceleration
        profile_acceleration: 0.0

      sensors:
        bumper_1: false
        bumper_2: false

        illumination: false

        ir: false

        sonar: false

tb3_0:
  diff_drive_controller:
    ros__parameters:

      odometry:
        publish_tf: true
        use_imu: true
        frame_id: "odom"
        child_frame_id: "base_footprint"

As you can see, the top most parameter used to be the node name (turtlebot3_node and diff_drive_controller). For the node namespace that you added to work, you will need to add the node namespace (tb3_0) one level above the node name!

In Step 2, we already changed the launch file to point to this yaml file instead of the one located in the turtlebot3_bringup package.

Step 4: Modify the CMakeLists File

For this section, we will just be adding a small code snipet to our CMakeLists.txt that will install the launch and param contents of our my_tb3_launcher package.

...
install(DIRECTORY
  launch
  param
  DESTINATION share/${PROJECT_NAME}/
)
...

Add this snippet right before the if(BUILD_TESTING) section of the CMakeLists.txt file.

Step 5: Compile and Run!

Finally, compile the code on your TurtleBot3:

~$ cd ~/turtlebot3_ws
~$ colcon build --symlink-install --parallel-workers 1
~$ . install/setup.bash

Now, run your launch file to make sure it works!

~$ export TURTLEBOT3_MODEL=burger
~$ ros2 launch my_tb3_launcher my_tb3_bringup.launch.py

You should get the following topics when you run ros2 topic list in another bash session:

/tb3_0/battery_state
/tb3_0/cmd_vel
/tb3_0/imu
/tb3_0/joint_states
/tb3_0/magnetic_field
/tb3_0/odom
/tb3_0/parameter_events
/tb3_0/robot_description
/tb3_0/rosout
/tb3_0/scan
/tb3_0/sensor_state
/tb3_0/tf
/tb3_0/tf_static

You can repeat these procedures with other TurtleBot3 robots with different namespaces to have multiple robots working in your network. Hope this helps and happy programming everyone!

9 Likes

@zmk5, this is really cool I’m going to try it out myself. :slightly_smiling_face:

1 Like

Great! Let me know if it works well with you!

@zmk5 @miker256

Navigation2 team has been developing multi-robot functionality. It takes care of the name spacing, launching, spawning, and rviz control, etc.

There are currently 4 PRs on the stack, which you might want to take a look:
[multirobot - Part1] Spawn multiple robots into Gazebo#1146
[multirobot - Part2] Add namespacing to nav stack #1147
[multirobot - Part3] Add multi-robot launching #1148
[multirobot - Part4] Multiple robot control via RVIZ #1149

Cheers!

1 Like

@zmk5 Nice tutorial! Thanks for your contribution :slight_smile:

2 Likes

@Because Cool, I did not know about these! Thanks for the heads up!

@routiful Thanks! Glad you liked it!

@zmk5 and @routiful, can we get these tutorials added to the ROBOTIS E-manual and linked back to the ROS2 tutorials here:
https://index.ros.org/doc/ros2/Tutorials/

I think this is useful, but posting it to Discourse is not the best place for people to find it in the future.

2 Likes

I’m more than happy to let ROBOTIS use the guide if I am credited. It is really up to them at this point! I also don’t know many places to post these other than my blog or the discourse channel. Do you have any suggestions?

1 Like

You can submit Tutorials to the ros2 Tutorials also, via pull request.

3 Likes

Cool, I’ll try that!

1 Like

@mkhansen, @zmk5,

Awesome! I am very grateful to @zmk5. It was also helpful when @zmk5 had previously posted about using TurtleBot3 with Ubuntu Server IoT. This time, I think it will be very useful information for many users. I have a plan to add this information in the official manual of TurtleBot3. Thank you very much. :slight_smile:

2 Likes

Hi I have been trying to get run multiple turtlebots in Gazebo simulation. I followed your tutorial very closely but I’m still getting errors in step 5 when launching the my_tb3_launcher. I think the error is has something to do with the YAML file that we make

I just wanted to make sure that this tutorial is Gazebo simulations?

Got some problems with the new LDS-02, but now it works:
=>

hey! I have been getting the same error. Did you find any fix for this ?

It would be very helpful for me if anyone could tell me what is the issue here.
(cc: @zmk5 )

What is the exact error that you are getting?

I’m having trouble assigning a namespace to certain topics and I was wondering if you could point me in the right direction to fix my issues. I followed your guide. When I run ros2 topic list most of my topics are correctly namespaced but not all
ros2 topic list
/mic/battery_state
/mic/cmd_vel
/mic/imu
/mic/joint_states
/mic/magnetic_field
/mic/odom
/mic/robot_description
/mic/sensor_state
/parameter_events
/rosout
/scan
/tf
/tf_static