ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange |
1 | initial version |
The answer is based on the turtlebot robot, but of course it can be used with any robot(s).
For multiple robots the key point is to manage the namespaces and tf_prefixes. Although usually we will set them to the same value for each robot in this example I will you different for purpose.
In setting up the navigation stack(s) each robot must operate in it's own namespace, but they do have to share the map (and usually robot_description). So the effect we want to achieve is:
/map
/robot1/robot_state_publisher /robot2/robot_state_publisher
/robot1/robot_pose_ekf /robot2/robot_pose_ekf
/robot1/amcl /robot2/amcl
/robot1/move_base /robot2/move_base
First of all we have to launch the simulation, in our case gazebo. To make things more readable I split launch files in few files.
Launch file for one robot. We name it one_robot.launch:
<launch>
<arg name="robot_name"/>
<arg name="init_pose"/>
<node name="spawn_minibot_model" pkg="gazebo" type="spawn_model"
args="$(arg init_pose) -urdf -param /robot_description -model $(arg robot_name)"
respawn="false" output="screen" />
<node pkg="robot_state_publisher" type="state_publisher"
name="robot_state_publisher" output="screen"/>
<!-- The odometry estimator, throttling, fake laser etc. -->
<!-- All the stuff as from example -->
</launch>
Two things to mention for above launch file:
Having the robot declaration we can now play with the namespaces. Your main launch file for multiple robots should look like (robots.launch):
<launch>
<!-- No namespace here as we will share this description.
Access with slash at the beginning -->
<param name="robot_description"
command="$(find xacro)/xacro.py $(find turtlebot_description)/urdf/turtlebot.urdf.xacro" />
<!-- BEGIN ROBOT 1-->
<group ns="robot1">
<param name="tf_prefix" value="robot1_tf" />
<include file="$(find your_pkg)/launch/one_robot.launch" >
<arg name="init_pose" value="-x 1 -y 1 -z 0" />
<arg name="robot_name" value="Robot1" />
</include>
</group>
<!-- BEGIN ROBOT 2-->
<group ns="robot2">
<param name="tf_prefix" value="robot2_tf" />
<include file="$(find your_pkg)/launch/one_robot.launch" >
<arg name="init_pose" value="-x -1 -y 1 -z 0" />
<arg name="robot_name" value="Robot2" />
</include>
</group>
</launch>
Last thing of course is to include it in your simulation launch file:
<launch>
<param name="/use_sim_time" value="true" />
<!-- start world -->
<node name="gazebo" pkg="gazebo" type="gazebo"
args="$(find your_pkg)/worlds/your.world" respawn="false" output="screen" />
<!-- start gui -->
<node name="gazebo_gui" pkg="gazebo" type="gui" respawn="false" output="screen"/>
<!-- include our robots -->
<include file="$(find your_pkg)/launch/robots.launch"/>
</launch>
For setting up the navigation stack we follow the tutorial RobotSetup, but we have to add the namespace tricks for it.
We use the same approach of splitting files. First the move_base.launch for one robot, as from tutorial:
<launch>
<!--- Run AMCL -->
<include file="$(find amcl)/examples/amcl_diff.launch" />
<!-- Define your move_base node -->
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">
<rosparam file="$(find your_pkg)/costmap_common_params.yaml" command="load" ns="global_costmap"/>
<rosparam file="$(find your_pkg)/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam file="$(find your_pkg)/local_costmap_params.yaml" command="load" />
<rosparam file="$(find your_pkg)/global_costmap_params.yaml" command="load" />
<rosparam file="$(find your_pkg)/base_local_planner_params.yaml" command="load" />
<remap from="map" to="/map" />
</node>
</launch>
The important note is to notice this remapping at the end. This file will be wrapped with namespace so we have to remove namespace from all appearances of map (there is only one map).
Now we can launch our multiple navigation file:
<launch>
<param name="/use_sim_time" value="true"/>
<!-- Run the map server -->
<node name="map_server" pkg="map_server" type="map_server" args="$(find your_pkg)/map/map.yaml" >
<param name="frame_id" value="/map" />
</node>
<group ns="robot1">
<param name="tf_prefix" value="robot1_tf" />
<param name="amcl/initial_pose_x" value="1" />
<param name="amcl/initial_pose_y" value="1" />
<include file="$(find your_pkg)/launch/move_base.launch" />
</group>
<group ns="robot2">
<param name="tf_prefix" value="robot2_tf" />
<param name="amcl/initial_pose_x" value="-1" />
<param name="amcl/initial_pose_y" value="1" />
<include file="$(find your_pkg)/launch/move_base.launch" />
</group>
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find your_pkg)/config/multi.vcg"
output="screen" />
</launch>
Two points to mention here.
Last thing to do is to correct some plugins for gazebo.
Now it's time to play around. We can send your messages to /robot1/move_base_simple/goal or via actionlib to /robot2/move_base/goal.
In rviz we can add several Robot models, but we have to remember to put a correct tf_prefix for each robot_description:
Robot Description: robot_description
TF Prefix: robot1_tf
In rviz configuration file we have to modify the topic for actions connected to tool buttons (your local vcg file):
Tool\ 2D\ Nav\ GoalTopic=/robot1/move_base_simple/goal
Tool\ 2D\ Pose\ EstimateTopic=/robot1/initialpose
If we want to control all robots from rviz we need to add more tool buttons as described here
Have fun.
2 | No.2 Revision |
The answer is based on the turtlebot robot, but of course it can be used with any robot(s).
For multiple robots the key point is to manage the namespaces and tf_prefixes. Although usually we will set them to the same value for each robot in this example I will you different for purpose.
In setting up the navigation stack(s) each robot must operate in it's own namespace, but they do have to share the map (and usually robot_description). So the effect we want to achieve is:
/map
/robot1/robot_state_publisher /robot2/robot_state_publisher
/robot1/robot_pose_ekf /robot2/robot_pose_ekf
/robot1/amcl /robot2/amcl
/robot1/move_base /robot2/move_base
First of all we have to launch the simulation, in our case gazebo. To make things more readable I split launch files in few files.
Launch file for one robot. We name it one_robot.launch:
<launch>
<arg name="robot_name"/>
<arg name="init_pose"/>
<node name="spawn_minibot_model" pkg="gazebo" type="spawn_model"
args="$(arg init_pose) -urdf -param /robot_description -model $(arg robot_name)"
respawn="false" output="screen" />
<node pkg="robot_state_publisher" type="state_publisher"
name="robot_state_publisher" output="screen"/>
<!-- The odometry estimator, throttling, fake laser etc. go here -->
<!-- All the stuff as from example usual robot launch file -->
</launch>
Two things to mention for above launch file:
Having the robot declaration we can now play with the namespaces. Your main launch file for multiple robots should look like (robots.launch):
<launch>
<!-- No namespace here as we will share this description.
Access with slash at the beginning -->
<param name="robot_description"
command="$(find xacro)/xacro.py $(find turtlebot_description)/urdf/turtlebot.urdf.xacro" />
<!-- BEGIN ROBOT 1-->
<group ns="robot1">
<param name="tf_prefix" value="robot1_tf" />
<include file="$(find your_pkg)/launch/one_robot.launch" >
<arg name="init_pose" value="-x 1 -y 1 -z 0" />
<arg name="robot_name" value="Robot1" />
</include>
</group>
<!-- BEGIN ROBOT 2-->
<group ns="robot2">
<param name="tf_prefix" value="robot2_tf" />
<include file="$(find your_pkg)/launch/one_robot.launch" >
<arg name="init_pose" value="-x -1 -y 1 -z 0" />
<arg name="robot_name" value="Robot2" />
</include>
</group>
</launch>
Last thing of course is to include it in your simulation launch file:
<launch>
<param name="/use_sim_time" value="true" />
<!-- start world -->
<node name="gazebo" pkg="gazebo" type="gazebo"
args="$(find your_pkg)/worlds/your.world" respawn="false" output="screen" />
<!-- start gui -->
<node name="gazebo_gui" pkg="gazebo" type="gui" respawn="false" output="screen"/>
<!-- include our robots -->
<include file="$(find your_pkg)/launch/robots.launch"/>
</launch>
For setting up the navigation stack we follow the tutorial RobotSetup, but we have to add the namespace tricks for it.
We use the same approach of splitting files. First the move_base.launch for one robot, as from tutorial:
<launch>
<!--- Run AMCL -->
<include file="$(find amcl)/examples/amcl_diff.launch" />
<!-- Define your move_base node -->
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">
<rosparam file="$(find your_pkg)/costmap_common_params.yaml" command="load" ns="global_costmap"/>
<rosparam file="$(find your_pkg)/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam file="$(find your_pkg)/local_costmap_params.yaml" command="load" />
<rosparam file="$(find your_pkg)/global_costmap_params.yaml" command="load" />
<rosparam file="$(find your_pkg)/base_local_planner_params.yaml" command="load" />
<remap from="map" to="/map" />
</node>
</launch>
The important note is to notice this remapping at the end. This file will be wrapped with namespace so we have to remove namespace from all appearances of map (there is only one map).
Now we can launch our multiple navigation file:
<launch>
<param name="/use_sim_time" value="true"/>
<!-- Run the map server -->
<node name="map_server" pkg="map_server" type="map_server" args="$(find your_pkg)/map/map.yaml" >
<param name="frame_id" value="/map" />
</node>
<group ns="robot1">
<param name="tf_prefix" value="robot1_tf" />
<param name="amcl/initial_pose_x" value="1" />
<param name="amcl/initial_pose_y" value="1" />
<include file="$(find your_pkg)/launch/move_base.launch" />
</group>
<group ns="robot2">
<param name="tf_prefix" value="robot2_tf" />
<param name="amcl/initial_pose_x" value="-1" />
<param name="amcl/initial_pose_y" value="1" />
<include file="$(find your_pkg)/launch/move_base.launch" />
</group>
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find your_pkg)/config/multi.vcg"
output="screen" />
</launch>
Two points to mention here.
Last thing to do is to correct some plugins for gazebo.
Now it's time to play around. We can send your messages to /robot1/move_base_simple/goal or via actionlib to /robot2/move_base/goal.
In rviz we can add several Robot models, but we have to remember to put a correct tf_prefix for each robot_description:
Robot Description: robot_description
TF Prefix: robot1_tf
In rviz configuration file we have to modify the topic for actions connected to tool buttons (your local vcg file):
Tool\ 2D\ Nav\ GoalTopic=/robot1/move_base_simple/goal
Tool\ 2D\ Pose\ EstimateTopic=/robot1/initialpose
If we want to control all robots from rviz we need to add more tool buttons as described here
Have fun.