Skip to content

Commit 4336a53

Browse files
committed
dual arm tutorial documentation
1 parent 104e645 commit 4336a53

18 files changed

Lines changed: 511 additions & 5 deletions

File tree

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
:github_url: https://github.com/UniversalRobots/Universal_Robots_ROS2_Tutorials/blob/main/my_dual_robot_cell/doc/assemble_urdf.rst
2+
3+
Assembling the URDF
4+
===================
5+
6+
The `ur_description <https://github.com/UniversalRobots/Universal_Robots_ROS2_Description>`_ package provides `macro files <https://github.com/UniversalRobots/Universal_Robots_ROS2_Description/blob/rolling/urdf/ur_macro.xacro>`_ to generate an instance of a Universal Robots arm.
7+
We'll use this to create a custom workcell with two robots in it, both a UR3e (called Alice) and a UR5e (called Bob). In this section we will only go into
8+
detail about the URDF / xacro files, not the complete package structure. Please see the
9+
`description package source code
10+
<https://github.com/UniversalRobots/Universal_Robots_ROS2_Tutorials/blob/main/my_robot_cell/my_robot_cell_description>`_ for all other files assembling the description package.
11+
12+
Workcell description
13+
--------------------
14+
15+
.. literalinclude:: ../my_dual_robot_cell_description/urdf/my_dual_robot_cell.urdf.xacro
16+
:language: xml
17+
:linenos:
18+
:caption: my_robot_cell_description/urdf/my_robot_cell.urdf.xacro
19+
20+
Let's break it down:
21+
22+
First, we'll have to **include** the macro to generate our custom workcell:
23+
24+
.. literalinclude:: ../my_dual_robot_cell_description/urdf/my_dual_robot_cell.urdf.xacro
25+
:language: xml
26+
:start-at: <xacro:include filename="$(find my_dual_robot_cell_description)/urdf/my_dual_robot_cell_macro.xacro"/>
27+
:end-at: <xacro:include filename="$(find my_dual_robot_cell_description)/urdf/my_dual_robot_cell_macro.xacro"/>
28+
:caption: my_dual_robot_cell_description/urdf/my_dual_robot_cell.urdf.xacro
29+
30+
This line only loads the macro for generating the robot workcell.
31+
32+
We will need to provide some parameters to our workcell in order to parametrize the arms. Therefore,
33+
we need to declare certain arguments that must be passed to the macro.
34+
35+
.. literalinclude:: ../my_dual_robot_cell_description/urdf/my_dual_robot_cell.urdf.xacro
36+
:language: xml
37+
:start-at: <xacro:arg name="alice_ur_type" default="ur3e"/>
38+
:end-at: <xacro:arg name="bob_visual_parameters_file" default="$(find ur_description)/config/$(arg bob_ur_type)/visual_parameters.yaml"/>
39+
:caption: my_dual_robot_cell_description/urdf/my_dual_robot_cell.urdf.xacro
40+
41+
The workspace macro contains all items within the workcell including the robot arm. If you are not
42+
experienced in writing URDFs, you may want to refer to this `tutorial
43+
<https://docs.ros.org/en/rolling/Tutorials/Intermediate/URDF/URDF-Main.html>`_. The macro's content
44+
is generated using
45+
46+
.. literalinclude:: ../my_dual_robot_cell_description/urdf/my_dual_robot_cell.urdf.xacro
47+
:language: xml
48+
:start-at: <link name="world"/>
49+
:end-at: </xacro:my_dual_robot_cell>
50+
:caption: my_dual_robot_cell_description/urdf/my_dual_robot_cell.urdf.xacro
51+
52+
Here, a ``world`` link is created, and the robot workcell is created relative to the ``world`` link.
53+
54+
Workcell macro
55+
--------------
56+
57+
The workcell macro is defined in the following manner:
58+
59+
.. literalinclude:: ../my_dual_robot_cell_description/urdf/my_dual_robot_cell_macro.xacro
60+
:language: xml
61+
:linenos:
62+
:caption: my_dual_robot_cell_description/urdf/my_dual_robot_cell_macro.xacro
63+
64+
This macro provides an example of what a custom workcell could resemble. Your workspace will likely
65+
vary from this one. Please feel free to modify this portion of the URDF to match your own setup. In
66+
this instance, our workspace comprises a table in front of a wall, featuring a monitor, and the
67+
two robot arms mounted on top.
68+
69+
Ensure that your custom workcell includes the parent link, which must be passed to the **ur_robot**
70+
macro. In this example, we chose to create two links, one named **alice_robot_mount** and one named **bob_robot_mount**.
71+
72+
.. literalinclude:: ../my_dual_robot_cell_description/urdf/my_dual_robot_cell_macro.xacro
73+
:language: xml
74+
:start-at: <link name="alice_robot_mount"/>
75+
:end-before: <!--Creates the 1st Robot-->
76+
:linenos:
77+
:caption: my_dual_robot_cell_description/urdf/my_dual_robot_cell_macro.xacro
78+
79+
After that we are finally able to actually **create the robot arms** by calling the macro.
80+
81+
.. literalinclude:: ../my_dual_robot_cell_description/urdf/my_dual_robot_cell_macro.xacro
82+
:language: xml
83+
:start-at: <!--Creates the 1st Robot-->
84+
:end-before: </xacro:macro>
85+
:linenos:
86+
:caption: my_dual_robot_cell_description/urdf/my_dual_robot_cell_macro.xacro
87+
88+
Note that the **origin** argument is transmitted in a different manner than the other arguments.
89+
90+
Before we can test our code, it's essential to build and source our Colcon workspace:
91+
92+
.. code-block:: bash
93+
94+
#cd to your colcon workspace root
95+
cd ~/colcon_ws
96+
97+
#source and build your workspace
98+
colcon build
99+
source install/setup.bash
100+
101+
102+
We can view our custom workspace by running:
103+
104+
.. code-block:: bash
105+
106+
#launch rviz
107+
ros2 launch my_dual_robot_cell_description view_robot.launch.py
108+
109+
Use the sliders of the ``joint_state_puplisher_gui`` to move the virtual robots around.
110+
It should look something like this:
111+
112+
.. image:: rviz.png
113+
:alt: RViz window showing the custom workspace
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
:github_url: https://github.com/UniversalRobots/Universal_Robots_ROS2_Tutorials/blob/main/my_dual_robot_cell/doc/build_moveit_config.rst
2+
3+
4+
========================
5+
Build the MoveIt! config
6+
========================
7+
8+
At this point, you should be able to run the ``ur_robot_driver`` for your custom UR setup.
9+
Now, we are only one last step away from actually planning and executing trajectories.
10+
11+
To utilize MoveIt! 2 for this purpose, which will handle trajectory planning for us, we need to set up a MoveIt! configuration package.
12+
To create such a MoveIt! `configuration package <https://github.com/UniversalRobots/Universal_Robots_ROS2_Tutorials/tree/main/my_dual_robot_cell/my_dual_robot_cell_moveit_config>`_, MoveIt! provides a very useful Setup Assistant.
13+
14+
Setup Assistant
15+
---------------
16+
17+
We can start the MoveIt! Setup Assistant by running:
18+
19+
.. code-block:: bash
20+
21+
ros2 launch moveit_setup_assistant setup_assistant.launch.py
22+
23+
Please note that MoveIt! itself provides a detailed `tutorial <https://moveit.picknik.ai/main/doc/examples/setup_assistant/setup_assistant_tutorial.html?highlight=setup%20assistant>`_ on how to use the Setup Assistant, so you may want to go through that.
24+
Although the Setup Assistant is very straightforward, there are some tips and tricks we want to discuss in the following sections.
25+
26+
The first task the MoveIt! Setup Assistant asks us to do is to load the URDF with the optional xacro arguments. If you would like to change the ``ur_type`` for example, you could also specify that here. For this demonstration we'll go with our description's default values.
27+
28+
.. image:: load_urdf.png
29+
:alt: Load a URDF
30+
31+
Next, make sure that the generated **self-collisions** are detected correctly. In our example the robot
32+
is positioned on the table in such a way, that it collides with the monitor on the table when all
33+
joints are in the 0 position. Hence, the collision is allowed as "Collision by default". We remove
34+
that tick, since we don't want that collision to be ignored.
35+
36+
.. image:: self_collisions.png
37+
:alt: Adjust self-collisions - Remove the tick for the collision between monitor and ur20_upper_arm_link.
38+
39+
40+
We skip adding virtual joints for now and continue with our **planning group(s)**.
41+
We add a planning group called **ur_arm**. A reasonable and error-resistant approach is to define
42+
it as a kinematic chain in the following manner:
43+
44+
.. image:: planning_group_kinematics.png
45+
:alt: Kinematic setup for our planning group
46+
47+
.. image:: planning_group_chain.png
48+
:alt: Kinematic Chain
49+
50+
Your planning groups should look something like this:
51+
52+
.. image:: planning_groups.png
53+
:alt: Planning Groups
54+
55+
We'll skip setting up ros2_control related points, since we've already configured that in our
56+
control package.
57+
58+
In the **MoveIt Controllers** step we setup our desired controller to match the name
59+
"scaled_joint_trajectory_controller":
60+
61+
.. image:: moveit_controllers.png
62+
:alt: MoveIt! coontrollers configuration
63+
64+
We skip **perception** as we don't have any cameras setup in our scenario.
65+
66+
In the next step we modify the **launch files** being generated by the assistant. We only generate
67+
"RViz Launch and Config", "MoveGroup Launch" and "Setup Assistant Launch". The other launch files
68+
are for starting a demo using mock hardware, which would basically be a duplication of what we
69+
already did in our control package. Since we also do not use the Warehouse feature for now, we also
70+
skip that file.
71+
72+
.. image:: launch_files.png
73+
:alt: Select the launch files to be generated
74+
75+
After the **Author information** we select which configuration files to generate. Again, we strip
76+
down all the files needed for mock hardware startup.
77+
78+
.. image:: config_files.png
79+
:alt: Select the config files to be generated
80+
81+
With all the information entered, you can **generate** the package and close the setup assistant.
82+
83+
Manual adaptions
84+
----------------
85+
86+
Before we can actually use our package we have to adapt the joint limits. Since MoveIt! requires
87+
joint acceleration limits to be specified but the description doesn't contain those, we need to
88+
specify these inside the generated ``config/joint_limits.yaml`` file.
89+
90+
They are not part of the arm's description since there are no physical acceleration limits, as
91+
those are highly dependent on the robot's current pose and TCP force (e.g. induced by the weight
92+
carried at the TCP). Setting the acceleration limits to ``5.0`` for all joints should be a
93+
conservative value. Higher values might lead to unwanted slowdowns during execution or even
94+
protective stops, while lower values will result in slower motions due to slow ramp-up and
95+
ramp-down parts of the trajectory.
96+
97+
.. literalinclude:: ../my_dual_robot_cell_moveit_config/config/joint_limits.yaml
98+
:language: yaml
99+
:linenos:
100+
:caption: my_dual_robot_cell_moveit_config/config/joint_limits.yaml
101+
102+
Please note that you could also change the default velocity and acceleration scaling in this file.
103+
Also, if you want to specify any limits (position or velocity) that differ from your description, you
104+
can set them here. Remember that these will only be used for planning trajectories, not necessarily
105+
for execution. If you send trajectories from another source than MoveIt!, those limits will not
106+
apply!
107+
108+
109+
Usage
110+
-----
111+
112+
Before we can test our code, it's essential to build and source our Colcon workspace:
113+
114+
.. code-block:: bash
115+
116+
#cd to your colcon workspace root
117+
cd ~/colcon_ws
118+
119+
#source and build your workspace
120+
colcon build
121+
source install/setup.bash
122+
123+
124+
Now you are ready to use MoveIt! with two actual robot arms, MoveIt! itself also provides you with the opportunity to start a robot with mock hardware.
125+
126+
To startup the complete system, you'll have to start three launch files in three individual
127+
terminals.
128+
129+
First, we need to start a robot, simulated or real. If you start a real robot, make sure that the
130+
*external_control* program is active on the robot.
131+
132+
.. code-block:: bash
133+
134+
# You can switch to real hardware if you prefer
135+
ros2 launch my_dual_robot_cell_control start_robots.launch.py bob_use_mock_hardware:=true alice_use_mock_hardware:=true
136+
137+
138+
139+
Second, we can start the move_group node by running the launch file the setup assistant created for us:
140+
141+
.. code-block:: bash
142+
143+
ros2 launch my_dual_robot_cell_moveit_config move_group.launch.py
144+
145+
If everything went well you should see the output: **"You can start planning now!"**.
146+
147+
To interact with the MoveIt! setup, you can start RViz with the correct setup file:
148+
149+
.. code-block:: bash
150+
151+
ros2 launch my_dual_robot_cell_moveit_config moveit_rviz.launch.py
152+
153+
From that setup you can start developing your application involving a custom move_group interface
154+
or similar. Please refer to the MoveIt! documentation for further reading.

my_dual_robot_cell/doc/conf.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
project = "ur_dual_arm_tutorial"
2+
copyright = "2025, Universal Robots A/S"
3+
author = "Jacob Larsen"
4+
5+
# The short X.Y version
6+
version = ""
7+
# The full version, including alpha/beta/rc tags
8+
release = ""
9+
10+
# -- General configuration ---------------------------------------------------
11+
12+
# If your documentation needs a minimal Sphinx version, state it here.
13+
#
14+
# needs_sphinx = '1.0'
15+
16+
# Add any Sphinx extension module names here, as strings. They can be
17+
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
18+
# ones.
19+
extensions = []
20+
21+
# Add any paths that contain templates here, relative to this directory.
22+
templates_path = ["_templates"]
23+
24+
# The suffix(es) of source filenames.
25+
# You can specify multiple suffix as a list of string:
26+
#
27+
source_suffix = [".rst"]
28+
29+
# The master toctree document.
30+
master_doc = "index"
31+
32+
numfig = True
33+
34+
# The language for content autogenerated by Sphinx. Refer to documentation
35+
# for a list of supported languages.
36+
#
37+
# This is also used if you do content translation via gettext catalogs.
38+
# Usually you set "language" from the command line for these cases.
39+
language = "en"
40+
41+
# List of patterns, relative to source directory, that match files and
42+
# directories to ignore when looking for source files.
43+
# This pattern also affects html_static_path and html_extra_path.
44+
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "migration/*.rst"]
45+
46+
# The name of the Pygments (syntax highlighting) style to use.
47+
pygments_style = None
48+
49+
50+
# -- Options for HTML output -------------------------------------------------
51+
52+
# The theme to use for HTML and HTML Help pages. See the documentation for
53+
# a list of builtin themes.
54+
#
55+
html_theme = "alabaster"
56+
57+
# Theme options are theme-specific and customize the look and feel of a theme
58+
# further. For a list of options available for each theme, see the
59+
# documentation.
60+
#
61+
# html_theme_options = {}
62+
63+
# Add any paths that contain custom static files (such as style sheets) here,
64+
# relative to this directory. They are copied after the builtin static files,
65+
# so a file named "default.css" will overwrite the builtin "default.css".
66+
# html_static_path = ["_static"]
67+
68+
# Custom sidebar templates, must be a dictionary that maps document names
69+
# to template names.
70+
#
71+
# The default sidebars (for documents that don't match any pattern) are
72+
# defined by theme itself. Builtin themes are using these templates by
73+
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
74+
# 'searchbox.html']``.
75+
#
76+
# html_sidebars = {}
77+
78+
79+
# -- Options for HTMLHelp output ---------------------------------------------
80+
81+
# Output file base name for HTML help builder.
82+
htmlhelp_basename = "ur_dual_arm_tutorial_doc"
171 KB
Loading

my_dual_robot_cell/doc/index.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
:github_url: https://github.com/UniversalRobots/Universal_Robots_ROS2_Tutorials/blob/main/my_dual_robot_cell/doc/index.rst
2+
3+
.. _dual_arm_tutorial:
4+
5+
================
6+
Dual robot arm cell
7+
================
8+
9+
Example about integrating two UR arms into a custom workspace. We will build a custom workcell
10+
description containing two robot arms, start the driver and create a MoveIt config to enable trajectory planning with MoveIt.
11+
12+
Please see the `package source code
13+
<https://github.com/UniversalRobots/Universal_Robots_ROS2_Tutorials/tree/main/my_robot_cell>`_ for
14+
all files relevant for this example.
15+
16+
.. toctree::
17+
:maxdepth: 4
18+
:caption: Contents:
19+
20+
assemble_urdf
21+
start_ur_driver
22+
build_moveit_config

0 commit comments

Comments
 (0)