Multiple Robot Arms¶
Introduction¶
In MoveIt, we can plan motions for multiple robot arms, but there are pre-required steps to prepare robot models and configure ROS controllers. This tutorial provides ROS beginners with the steps to model multiple arms, configure controllers, and plan motions using MoveIt.
Watch this Quick YouTube video of the multiple arms being controlled in Gazebo with the MoveIt Rviz plugin.
Getting Started¶
If you haven’t already done so, make sure you’ve completed the steps in Getting Started.
The steps of setting multiple arms environments to use MoveIt motion planning are as follows:
- Build the Xacro/URDF model of the multiple arms.
- Prepare the MoveIt config package using MoveIt setup Assistant.
- Write the ROS controllers configuration and launch files for the multiple arms.
- Integrate the simulation in Gazebo with MoveIt motion planning.
- Plan arm motions with the MoveIt Move Group Interface.
This tutorial explains every step to help set up your multiple robot arms.
Step 1: Build the Xacro/URDF model of the multiple arms¶
The Panda robot arm is used in the following explanation, but the same applies to preparing other types of robot arms.
To start building your multiple arms model, create a new panda_multiple_arms
package as follows:
cd ~/ws_moveit/src
catkin create pkg panda_multiple_arms
cd panda_multiple_arms
mkdir robot_description && cd robot_description
touch panda_multiple_arms.xacro
To prepare your multiple robot arms xacro file (model), you need to have the single arm’s xacro file. In the following part, we will build a multiple arms panda robot description file consisting of two identical arms.
Our multiple arms model has right_arm
and left_arm
robots. Each arm is equipped with a gripper. Here is a link to the panda_multiple_arms.xacro file. Please copy its XML code to your panda_multiple_arms.xacro
file.
Notes:
Two arguments
right_arm
andleft_arm
are defined as prefixes to differentiate the arms and hands names. Be careful not to use any of the following characters in the prefixes:-
,[
,]
,(
,)
,/
.The arms and hands models are loaded from the
franka_description
package, which is installed as a dependency of thepanda_moveit_config
package. Ensure thefranka_description
package is installed in your ROS environment.We usually need to have a careful look at the robot’s xacro macro to understand parameters to use. Here is an example from the
franka_arm.xacro
file in thefranka_description
package. It has the following macro for the robot model:<xacro:macro name="franka_arm" params="arm_id:='panda' description_pkg:='franka_description' connected_to:='' xyz:='0 0 0' rpy:='0 0 0' gazebo:=false safety_distance:=0 joint_limits" >
We can search those parameters in the macro to understand the function of each. The arm_id
sets a prefix to the arm name to enable reusing the same model. This is essential for our purpose of modeling multiple robots. The connected_to
parameter allows the robot base to be attached to a given link. In our multiple arms model, each robot is connected to a box-shaped base. The gazebo
parameter determines whether to load the gazebo simulation required information (e.g links inertias and joints transmissions) or not.
After knowing the xacro macro for the arm, and understanding the input parameters, we can use it as follows to load the arms.
<xacro:franka_arm arm_id="$(arg arm_id_1)" connected_to="base" xyz="0 -0.5 1" gazebo="true" safety_distance="0.03" joint_limits="${xacro.load_yaml('$(find franka_description)/robots/panda/joint_limits.yaml')}"/>
<xacro:franka_arm arm_id="$(arg arm_id_2)" connected_to="base" xyz="0 0.5 1" gazebo="true" safety_distance="0.03" joint_limits="${xacro.load_yaml('$(find franka_description)/robots/panda/joint_limits.yaml')}"/>
The same applies to loading the robot grippers/hands models. Follow the tutorials of URDF and xacro to learn more about modeling robots with those formats.
At this point, it is recommended to check our xacro model is working as expected. This can be done in three simple steps; convert your xacro model to URDF, check the connections between links and joints are correct, and visualize the model. Run the following commands to build your panda_multiple_arms
package and check the xacro model has no problems.
cd ~/ws_moveit
catkin build
source devel/setup.bash
roscd panda_multiple_arms/robot_description
rosrun xacro xacro panda_multiple_arms.xacro -o panda_multiple_arms.urdf
check_urdf panda_multiple_arms.urdf
The check_urdf
command shows the links tree and indicates if there are any errors:
robot name is: panda_multiple_arms
---------- Successfully Parsed XML ---------------
root Link: world has 1 child(ren)
child(1): base
child(1): left_arm_link0
child(1): left_arm_link1
child(1): left_arm_link2
child(1): left_arm_link3
child(1): left_arm_link4
child(1): left_arm_link5
child(1): left_arm_link6
child(1): left_arm_link7
child(1): left_arm_link8
child(1): left_arm_hand
child(1): left_arm_leftfinger
child(2): left_arm_rightfinger
child(3): left_arm_hand_sc
child(4): left_arm_hand_tcp
child(2): left_arm_link7_sc
child(2): left_arm_link6_sc
child(2): left_arm_link5_sc
child(2): left_arm_link4_sc
child(2): left_arm_link3_sc
child(2): left_arm_link2_sc
child(2): left_arm_link1_sc
child(2): left_arm_link0_sc
child(2): right_arm_link0
child(1): right_arm_link1
child(1): right_arm_link2
child(1): right_arm_link3
child(1): right_arm_link4
child(1): right_arm_link5
child(1): right_arm_link6
child(1): right_arm_link7
child(1): right_arm_link8
child(1): right_arm_hand
child(1): right_arm_leftfinger
child(2): right_arm_rightfinger
child(3): right_arm_hand_sc
child(4): right_arm_hand_tcp
child(2): right_arm_link7_sc
child(2): right_arm_link6_sc
child(2): right_arm_link5_sc
child(2): right_arm_link4_sc
child(2): right_arm_link3_sc
child(2): right_arm_link2_sc
child(2): right_arm_link1_sc
child(2): right_arm_link0_sc
To visually check your multiple robot arm model, ensure that the urdf_tutorial
ROS package is installed and run the command:
roslaunch urdf_tutorial display.launch model:=panda_multiple_arms.urdf
Once the Rviz GUI starts, set the fixed frame on the upper left corner to world
.
If the previous steps are correctly done, the multiple arms model should show up as follows.
This concludes the first step of building the model and verifying it.
Step 2: Prepare the MoveIt config package using MoveIt Setup Assistant¶
MoveIt Setup Assistant is used to configure our multiple robot arms for using the MoveIt pipeline.
Start the MoveIt Setup Assistant:
roslaunch moveit_setup_assistant setup_assistant.launch
Follow the MoveIt Setup Assistant tutorial to configure the arms. Note that we will be making a separate move group for each arm and hand. The groups are called right_arm
, left_arm
, right_hand
, and left_hand
.
Here are two more steps which are not required but often useful:
- Define a practical ready pose for both arms with joint values {0.0, -0.785, 0.0, -2.356, 0.0, 1.571, 0.785}.
The arms should look as follows at the ready pose.
- Define
open
andclose
poses for theright_hand
andleft_hand
move groups. Theopen
pose with joint1 value set to 0.035, and theclose
has the joint1 set to 0.0. Notice that the hand’sjoint2
mimics the value ofjoint1
. Therefore, there is no need to includejoint2
in the hand move_group poses. The defined poses for the arms and hands should look as follows. Feel free to add other poses of interest for the arms/hands.
Name the MoveIt config package panda_multiple_arms_moveit_config
and generate the files using the Setup Assistant in the ~/ws_moveit/src
directory.
Step 3: Write the ROS controllers configuration and launch files for the multiple arms¶
This step creates ros_control
configuration files and roslaunch
files to start them. We need two controller types. The first is a joint state controller, which publishes the state of all joints. The second is joint trajectory controller, which executes joint-space trajectories on a group of robot joints.
In the following configuration files, the controllers names are joint_state_controller
, right_arm_trajectory_controller
, and left_arm_trajectory_controller
. Under each trajectory controller, we need to specify its hardware interface type, joint groups, and constraints. For more about ROS controllers, refer to their documentation. Let’s create the controllers configuration and their launch file in systematic steps and with descriptive names.
- The joint state controller:
Create the
joint_state_controller.yaml
configuration file inside thepanda_multiple_arms/config
directory as follows:cd ~/ws_moveit cd src/panda_multiple_arms mkdir config && cd config touch joint_state_controller.yaml
Open the
joint_state_controller.yaml
and copy the following controller configuration to itjoint_state_controller: type: joint_state_controller/JointStateController publish_rate: 50
- The joint trajectory controller:
Create the
trajectory_controller.yaml
configuration file in the same directorypanda_multiple_arms/config
cd ~/ws_moveit cd src/panda_multiple_arms/config touch trajectory_controller.yaml
Open the
trajectory_controller.yaml
and copy the following controller configuration to itright_arm_trajectory_controller: type: "position_controllers/JointTrajectoryController" joints: - right_arm_joint1 - right_arm_joint2 - right_arm_joint3 - right_arm_joint4 - right_arm_joint5 - right_arm_joint6 - right_arm_joint7 constraints: goal_time: 0.6 stopped_velocity_tolerance: 0.05 right_arm_joint1: {trajectory: 0.1, goal: 0.1} right_arm_joint2: {trajectory: 0.1, goal: 0.1} right_arm_joint3: {trajectory: 0.1, goal: 0.1} right_arm_joint4: {trajectory: 0.1, goal: 0.1} right_arm_joint5: {trajectory: 0.1, goal: 0.1} right_arm_joint6: {trajectory: 0.1, goal: 0.1} right_arm_joint7: {trajectory: 0.1, goal: 0.1} stop_trajectory_duration: 0.5 state_publish_rate: 25 action_monitor_rate: 10 left_arm_trajectory_controller: type: "position_controllers/JointTrajectoryController" joints: - left_arm_joint1 - left_arm_joint2 - left_arm_joint3 - left_arm_joint4 - left_arm_joint5 - left_arm_joint6 - left_arm_joint7 constraints: goal_time: 0.6 stopped_velocity_tolerance: 0.05 left_arm_joint1: {trajectory: 0.1, goal: 0.1} left_arm_joint2: {trajectory: 0.1, goal: 0.1} left_arm_joint3: {trajectory: 0.1, goal: 0.1} left_arm_joint4: {trajectory: 0.1, goal: 0.1} left_arm_joint5: {trajectory: 0.1, goal: 0.1} left_arm_joint6: {trajectory: 0.1, goal: 0.1} left_arm_joint7: {trajectory: 0.1, goal: 0.1} stop_trajectory_duration: 0.5 state_publish_rate: 25 action_monitor_rate: 10 #notice that the grippers joint2 mimics joint1 #this is why it is not listed under the hand controllers right_hand_controller: type: "effort_controllers/JointTrajectoryController" joints: - right_arm_finger_joint1 gains: right_arm_finger_joint1: {p: 50.0, d: 1.0, i: 0.01, i_clamp: 1.0} left_hand_controller: type: "effort_controllers/JointTrajectoryController" joints: - left_arm_finger_joint1 gains: left_arm_finger_joint1: {p: 50.0, d: 1.0, i: 0.01, i_clamp: 1.0}
Create a
control_utils.launch
file inside thepanda_multiple_arms/launch
directory to start the robot state publisher, and the controllers. Copy the following XML code to yourcontrol_utils.launch
file:<?xml version="1.0"?> <launch> <!-- Robot state publisher --> <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher"> <param name="publish_frequency" type="double" value="50.0" /> <param name="tf_prefix" type="string" value="" /> </node> <!-- Joint state controller --> <rosparam file="$(find panda_multiple_arms)/config/joint_state_controller.yaml" command="load" /> <node name="joint_state_controller_spawner" pkg="controller_manager" type="spawner" args="joint_state_controller" respawn="false" output="screen" /> <!-- Joint trajectory controller --> <rosparam file="$(find panda_multiple_arms)/config/trajectory_controller.yaml" command="load" /> <node name="arms_trajectory_controller_spawner" pkg="controller_manager" type="spawner" respawn="false" output="screen" args="right_arm_trajectory_controller left_arm_trajectory_controller right_hand_controller left_hand_controller" /> </launch>
The joint state controller publishes the robot joint values on the /joint_states
topic, and the robot state publisher uses them to calculate forward kinematics and publish the poses/transforms of the robot links. The joint trajectory controller enables executing joint-space trajectories on a group of joints.
The remaining part of this step explains how to modify the auto-generated controller files in the moveit config package to execute MoveIt-generated trajectories on Gazebo-simulated robots. Also in a systematic way, we need to modify two files, ros_controllers.yaml
, and simple_moveit_controllers.yaml
- The ros_controllers.yaml
The
ros_controllers.yaml
file is auto-generated in thepanda_multiple_arms_moveit_config/config
. This file is for the ROS control configuration, which means its content should be the same as the content of bothjoint_state_controller.yaml
andtrajectory_controller.yaml
. Its contents should be as followsjoint_state_controller: type: joint_state_controller/JointStateController publish_rate: 50 right_arm_trajectory_controller: type: "position_controllers/JointTrajectoryController" joints: - right_arm_joint1 - right_arm_joint2 - right_arm_joint3 - right_arm_joint4 - right_arm_joint5 - right_arm_joint6 - right_arm_joint7 constraints: goal_time: 0.6 stopped_velocity_tolerance: 0.05 right_arm_joint1: {trajectory: 0.1, goal: 0.1} right_arm_joint2: {trajectory: 0.1, goal: 0.1} right_arm_joint3: {trajectory: 0.1, goal: 0.1} right_arm_joint4: {trajectory: 0.1, goal: 0.1} right_arm_joint5: {trajectory: 0.1, goal: 0.1} right_arm_joint6: {trajectory: 0.1, goal: 0.1} right_arm_joint7: {trajectory: 0.1, goal: 0.1} stop_trajectory_duration: 0.5 state_publish_rate: 25 action_monitor_rate: 10 left_arm_trajectory_controller: type: "position_controllers/JointTrajectoryController" joints: - left_arm_joint1 - left_arm_joint2 - left_arm_joint3 - left_arm_joint4 - left_arm_joint5 - left_arm_joint6 - left_arm_joint7 constraints: goal_time: 0.6 stopped_velocity_tolerance: 0.05 left_arm_joint1: {trajectory: 0.1, goal: 0.1} left_arm_joint2: {trajectory: 0.1, goal: 0.1} left_arm_joint3: {trajectory: 0.1, goal: 0.1} left_arm_joint4: {trajectory: 0.1, goal: 0.1} left_arm_joint5: {trajectory: 0.1, goal: 0.1} left_arm_joint6: {trajectory: 0.1, goal: 0.1} left_arm_joint7: {trajectory: 0.1, goal: 0.1} stop_trajectory_duration: 0.5 state_publish_rate: 25 action_monitor_rate: 10 #notice that the grippers joint2 mimics joint1 #this is why it is not listed under the hand controllers right_hand_controller: type: "effort_controllers/JointTrajectoryController" joints: - right_arm_finger_joint1 gains: right_arm_finger_joint1: {p: 50.0, d: 1.0, i: 0.01, i_clamp: 1.0} left_hand_controller: type: "effort_controllers/JointTrajectoryController" joints: - left_arm_finger_joint1 gains: left_arm_finger_joint1: {p: 50.0, d: 1.0, i: 0.01, i_clamp: 1.0}
- The simple_moveit_controllers.yaml
This file is also auto-generated in
panda_multiple_arms_moveit_config/config
. MoveIt requires a trajectory controller which has a FollowJointTrajectoryAction interface. After motion planning, the FollowJointTrajectoryAction interface sends the generated trajectory to the robot ROS controller (written above). This file configures the controllers to be used by MoveIt controller manager to execute planned trajectories. The controllers names should match the ROS controllers in the previousros_controllers.yaml
. Copy the following to yoursimple_moveit_controllers.yaml
file.controller_list: - name: right_arm_trajectory_controller action_ns: follow_joint_trajectory type: FollowJointTrajectory default: True joints: - right_arm_joint1 - right_arm_joint2 - right_arm_joint3 - right_arm_joint4 - right_arm_joint5 - right_arm_joint6 - right_arm_joint7 - name: left_arm_trajectory_controller action_ns: follow_joint_trajectory type: FollowJointTrajectory default: True joints: - left_arm_joint1 - left_arm_joint2 - left_arm_joint3 - left_arm_joint4 - left_arm_joint5 - left_arm_joint6 - left_arm_joint7 #notice that the gripper's joint2 mimics joint1 #this is why it is not listed under the hand controllers - name: right_hand_controller action_ns: follow_joint_trajectory type: FollowJointTrajectory default: true joints: - right_arm_finger_joint1 - name: left_hand_controller action_ns: follow_joint_trajectory type: FollowJointTrajectory default: true joints: - left_arm_finger_joint1
The last step is to make the auto-generated
ros_controllers.launch
spawn the ROS controllers configured in theros_controller.yaml
file. This launch file is inside thepanda_multiple_arms_moveit_config/launch
directory. Edit the file and add the controller names as arguments in the controller spawner node as shown below.<?xml version="1.0"?> <launch> <!-- Load joint controller configurations from YAML file to parameter server --> <rosparam file="$(find panda_multiple_arms_moveit_config)/config/ros_controllers.yaml" command="load"/> <!-- Load the controllers --> <node name="controller_spawner" pkg="controller_manager" type="spawner" respawn="false" output="screen" args=" right_arm_trajectory_controller left_arm_trajectory_controller right_hand_controller left_hand_controller"/> </launch>
Step 4: Integrate the simulation in Gazebo with MoveIt motion planning¶
At the integration step, we need to ensure that ROS packages for ROS control, Gazebo ROS control, MoveIt ROS control interface, and MoveIt planners are installed and sourced on our ROS environment. Install the required ROS packages as follows, and source your ROS environment:
sudo apt install ros-noetic-ros-control ros-noetic-ros-controllers ros-noetic-joint-state-controller ros-noetic-effort-controllers ros-noetic-position-controllers ros-noetic-velocity-controllers ros-noetic-gazebo-ros ros-noetic-gazebo-ros-control ros-noetic-moveit-ros-control-interface ros-noetic-moveit-simple-controller-manager ros-noetic-moveit-fake-controller-manager ros-noetic-moveit-planners
For the integration to work, we need to prepare a launch file to start three components. Those components are the simulated robot in Gazebo, ROS controllers, and MoveIt motion plannig executable. We have already prepared the control_utils.launch
file to load the ROS controllers, and the required MoveIt motion planning file move_group.launch
is auto-generated. Then, our tasks here are to start the simulated robot in a Gazebo world, and prepare a launch file that starts the mentioned three components.
- Starting the simulated robot in an empty Gazebo world
To spawn the panda multiple arms model in Gazebo, we need to prepare a launch file in the panda_multiple_arms/launch
directory. Let’s call it panda_multiple_arms_empty_world.launch
. Here are the steps to prepar this file.
cd ~/ws_moveit
cd src/panda_multiple_arms/launch
touch panda_multiple_arms_empty_world.launch
The panda_multiple_arms_empty_world.launch
file launches an empty world file, loads the robot description, and spawns the robot in the empty world. Copy the following XML code to this file.
<?xml version="1.0"?>
<launch>
<!-- Launch empty Gazebo world -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="use_sim_time" value="true" />
<arg name="gui" value="true" />
<arg name="paused" value="false" />
<arg name="debug" value="false" />
</include>
<!-- Find my robot Description-->
<param name="robot_description" command="$(find xacro)/xacro '$(find panda_multiple_arms)/robot_description/panda_multiple_arms.xacro'" />
<!-- Spawn The robot over the robot_description param-->
<node name="urdf_spawner" pkg="gazebo_ros" type="spawn_model" respawn="false" output="screen" args="-urdf -param robot_description -model panda_multiple_arms" />
</launch>
Preparing a
bringup_moveit.launch
file to start the three integration components. Create the file in thepanda_multiple_arms/launch
directory.cd ~/ws_moveit cd src/panda_multiple_arms/launch touch bringup_moveit.launch
Copy the following XML code to the bringup_moveit.launch
file.
<?xml version="1.0"?>
<launch>
<!-- Run the main MoveIt executable with trajectory execution -->
<include file="$(find panda_multiple_arms_moveit_config)/launch/move_group.launch">
<arg name="allow_trajectory_execution" value="true" />
<arg name="moveit_controller_manager" value="ros_control" />
<arg name="fake_execution_type" value="interpolate" />
<arg name="info" value="true" />
<arg name="debug" value="false" />
<arg name="pipeline" value="ompl" />
<arg name="load_robot_description" value="true" />
</include>
<!-- Start the simulated robot in an empty Gazebo world -->
<include file="$(find panda_multiple_arms)/launch/panda_multiple_arms_empty_world.launch" />
<!-- Start the controllers and robot state publisher-->
<include file="$(find panda_multiple_arms)/launch/control_utils.launch"/>
<!-- Start moveit_rviz with the motion planning plugin -->
<include file="$(find panda_multiple_arms_moveit_config)/launch/moveit_rviz.launch">
<arg name="rviz_config" value="$(find panda_multiple_arms_moveit_config)/launch/moveit.rviz" />
</include>
</launch>
To start the MoveIt-Gazebo integration, build and source your ROS workspace, and run the bringup_moveit.launch
file.
cd ~/ws_moveit
catkin build
source devel/setup.bash
roslaunch panda_multiple_arms bringup_moveit.launch
If all steps are done, this should bring up all the required components for the integration. Then, we can plan motions for the arms and hands using MoveIt’s rviz plugin and execute those motions on the simulated robots in Gazebo as shown in this video.
Step 5: Plan arms motions with MoveIt Move Group Interface.¶
When writing code for multiple move groups, motion planning works the same way it did in previous tutorials. There is an example here for multiple move groups and a minimal CMakeLists.txt file with the dependencies to use Moveit Move Group Interface and describe the arms poses. This short YouTube video shows the resulting arms and hands motions. This example uses a separate move group for every arm, but we can make a new move group that contains both the right_arm
and left_arm
groups to plan and execute motions for them simultaneously.
Refer to MoveIt tutorials to learn more about the Move Group Interface.
Open Source Feedback
See something that needs improvement? Please open a pull request on this GitHub page