Skip to content

Commit e91e63f

Browse files
authored
Dual arm docs (#18)
* dual arm tutorial documentation
1 parent 104e645 commit e91e63f

30 files changed

+444
-851
lines changed
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_dual_robot_cell/my_dual_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:: figures/rviz.png
113+
:alt: RViz window showing the custom workspace

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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
23+
MoveIt! Configuration
24+
---------------------
25+
For inspiration on how to configure MoveIt! 2 using the setup assistant, please see the `single arm robot cell tutorial
26+
<https://docs.universal-robots.com/Universal_Robots_ROS_Documentation/doc/ur_tutorials/my_robot_cell/doc/build_moveit_config.html>`_
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
:github_url: https://github.com/UniversalRobots/Universal_Robots_ROS2_Tutorials/blob/main/my_dual_robot_cell/doc/start_ur_driver.rst
2+
3+
=========================
4+
Start the ur_robot_driver
5+
=========================
6+
7+
In the last chapter we created a custom scene description containing our two robots. In order to make
8+
that description usable to ``ros2_control`` and hence the ``ur_robot_driver``, we need to add
9+
control information. We will also add a custom launchfile to start up our demonstrator.
10+
11+
We will generate a new package called `my_dual_robot_cell_control <https://github.com/UniversalRobots/Universal_Robots_ROS2_Tutorials/tree/main/my_dual_robot_cell/my_dual_robot_cell_control>`_ for that purpose.
12+
13+
Create a description with ros2_control tag
14+
------------------------------------------
15+
16+
The first step is to create a description containing the control instructions:
17+
18+
.. literalinclude:: ../my_dual_robot_cell_control/urdf/my_dual_robot_cell_controlled.urdf.xacro
19+
:language: xml
20+
:linenos:
21+
:caption: my_dual_robot_cell_control/urdf/my_dual_robot_cell_controlled.urdf.xacro
22+
23+
This URDF is very similar to the one we have already assembled. We simply need to include the ros2_control macro,
24+
25+
.. literalinclude:: ../my_dual_robot_cell_control/urdf/my_dual_robot_cell_controlled.urdf.xacro
26+
:language: xml
27+
:start-at: <xacro:include filename="$(find ur_robot_driver)/urdf/ur.ros2_control.xacro" />
28+
:end-at: <xacro:include filename="$(find ur_robot_driver)/urdf/ur.ros2_control.xacro" />
29+
:caption: my_dual_robot_cell_control/urdf/my_dual_robot_cell_controlled.urdf.xacro
30+
31+
define the necessary arguments that need to be passed to the macro,
32+
33+
.. literalinclude:: ../my_dual_robot_cell_control/urdf/my_dual_robot_cell_controlled.urdf.xacro
34+
:language: xml
35+
:start-at: <xacro:arg name="alice_use_mock_hardware" default="false" />
36+
:end-at: <xacro:arg name="ur_input_recipe_filename" default="$(find ur_robot_driver)/resources/rtde_input_recipe.txt" />
37+
:caption: my_dual_robot_cell_control/urdf/my_dual_robot_cell_controlled.urdf.xacro
38+
39+
and then call the macro by providing all the specified arguments.
40+
41+
.. literalinclude:: ../my_dual_robot_cell_control/urdf/my_dual_robot_cell_controlled.urdf.xacro
42+
:language: xml
43+
:start-at: <xacro:ur_ros2_control
44+
:end-before: </robot>
45+
:caption: my_robot_cell_control/urdf/my_robot_cell_controlled.urdf.xacro
46+
47+
Extract the calibration
48+
-----------------------
49+
50+
One very important step is to extract each robot's specific calibration and save it to our
51+
workcell's startup package. For details, please refer to `our documentation on extracting the calibration information <https://docs.ros.org/en/ros2_packages/rolling/api/ur_robot_driver/installation/robot_setup.html#extract-calibration-information>`_.
52+
For now, we just copy the default ones for the UR3e and UR5e.
53+
54+
.. code-block::
55+
56+
cp $(ros2 pkg prefix ur_description)/share/ur_description/config/ur3e/default_kinematics.yaml \
57+
my_dual_robot_cell_control/config/alice_calibration.yaml
58+
59+
cp $(ros2 pkg prefix ur_description)/share/ur_description/config/ur5e/default_kinematics.yaml \
60+
my_dual_robot_cell_control/config/bob_calibration.yaml
61+
62+
63+
Create robot_state_publisher launchfile
64+
---------------------------------------
65+
66+
To use the custom controlled description, we need to generate a launchfile loading that description
67+
(Since it contains less / potentially different) arguments than the "default" one. In that
68+
launchfile we need to start a ``robot_state_publisher`` (RSP) node that will get the description as a
69+
parameter and redistribute it via the ``robot_description`` topic:
70+
71+
.. literalinclude:: ../my_dual_robot_cell_control/launch/rsp.launch.py
72+
:language: py
73+
:start-at: from launch import LaunchDescription
74+
:linenos:
75+
:caption: my_dual_robot_cell_control/launch/rsp.launch.py
76+
77+
Before we can start our workcell we need to create a launchfile that will launch the two robot descriptions correctly.
78+
79+
Create start_robot launchfile
80+
-----------------------------
81+
82+
Here we create a launch file which will launch the driver with the correct description and robot state publisher, as well as start all of the controllers necessary to use the two robots.
83+
84+
.. literalinclude:: ../my_dual_robot_cell_control/launch/start_robots.launch.py
85+
:language: py
86+
:linenos:
87+
:caption: my_dual_robot_cell_control/launch/start_robots.launch.py
88+
89+
With that we can start the robot using
90+
91+
.. code-block:: bash
92+
93+
ros2 launch my_dual_robot_cell_control start_robots.launch.py alice_use_mock_hardware:=true bob_use_mock_hardware:=true
94+
95+
Testing everything
96+
------------------
97+
98+
Before we can test our code, it's essential to build and source our Colcon workspace:
99+
100+
.. code-block:: bash
101+
102+
#cd to your colcon workspace root, e.g.
103+
cd ~/colcon_ws
104+
105+
#source and build your workspace
106+
colcon build
107+
source install/setup.bash
108+
109+
We can start the system in a mocked simulation
110+
111+
.. code-block:: bash
112+
113+
#start the driver with mocked hardware
114+
ros2 launch my_dual_robot_cell_control start_robots.launch.py alice_use_mock_hardware:=true bob_use_mock_hardware:=true
115+
116+
Or to use it with a real robot:
117+
118+
.. code-block:: bash
119+
120+
#start the driver with real hardware (or the docker simulator included in this example)
121+
ros2 launch my_dual_robot_cell_control start_robots.launch.py
122+
123+
.. note::
124+
We extracted the calibration information from the robot and saved it in the
125+
``my_robot_cell_control`` package. If you have a different robot, you need to extract the
126+
calibration information from that, and pass that to the launch file. Changing the ``ur_type``
127+
parameter only will lead to a wrong model, as it will still be using the example kinematics from
128+
a UR3e and UR5e. To use a UR10e and UR20, for example, you can do
129+
130+
.. code-block:: bash
131+
132+
ros2 launch my_dual_robot_cell_control start_robots.launch.py bob_ur_type:=ur10e bob_use_mock_hardware:=true \
133+
bob_kinematics_parameters_file:=$(ros2 pkg prefix ur_description)/share/ur_description/config/ur10e/default_kinematics.yaml \
134+
alice_ur_type:=ur20 alice_use_mock_hardware:=true \
135+
alice_kinematics_parameters_file:=$(ros2 pkg prefix ur_description)/share/ur_description/config/ur20/default_kinematics.yaml

0 commit comments

Comments
 (0)