Start the ur_robot_driver
In the last chapter we created a custom scene description containing our robot. In order to make
that description usable to ros2_control
and hence the ur_robot_driver
, we need to add
control information. We will also add a custom launchfile to startup our demonstrator.
We will generate a new package called my_robot_cell_control for that purpose.
Create a description with ros2_control tag
The first step is to create a description containing the control instructions:
1<?xml version="1.0"?>
2<robot name="my_robot_cell" xmlns:xacro="http://ros.org/wiki/xacro">
3
4 <!--First, include the macro from our custom description-->
5 <xacro:include filename="$(find my_robot_cell_description)/urdf/my_robot_cell_macro.xacro"/>
6 <!--The driver already provides a control tag for the UR arm, we can directly include-->
7 <xacro:include filename="$(find ur_robot_driver)/urdf/ur.ros2_control.xacro"/>
8
9 <!--We need to specify the arguments used for parametrizing our description-->
10 <xacro:arg name="ur_type" default="ur20"/>
11 <xacro:arg name="joint_limit_parameters_file" default="$(find ur_description)/config/$(arg ur_type)/joint_limits.yaml"/>
12 <xacro:arg name="kinematics_parameters_file" default="$(find ur_description)/config/$(arg ur_type)/default_kinematics.yaml"/>
13 <xacro:arg name="physical_parameters_file" default="$(find ur_description)/config/$(arg ur_type)/physical_parameters.yaml"/>
14 <xacro:arg name="visual_parameters_file" default="$(find ur_description)/config/$(arg ur_type)/visual_parameters.yaml"/>
15
16 <xacro:arg name="robot_ip" default="0.0.0.0"/>
17 <xacro:arg name="headless_mode" default="false" />
18 <xacro:arg name="ur_script_filename" default="$(find ur_client_library)/resources/external_control.urscript"/>
19 <xacro:arg name="ur_output_recipe_filename" default="$(find ur_robot_driver)/resources/rtde_output_recipe.txt"/>
20 <xacro:arg name="ur_input_recipe_filename" default="$(find ur_robot_driver)/resources/rtde_input_recipe.txt"/>
21 <xacro:arg name="use_mock_hardware" default="false" />
22 <xacro:arg name="mock_sensor_commands" default="false" />
23
24 <link name="world" />
25
26 <!--Create the scene description including the robot-->
27 <xacro:my_robot_cell
28 parent="world"
29 ur_type="$(arg ur_type)"
30 joint_limits_parameters_file="$(arg joint_limit_parameters_file)"
31 kinematics_parameters_file="$(arg kinematics_parameters_file)"
32 physical_parameters_file="$(arg physical_parameters_file)"
33 visual_parameters_file="$(arg visual_parameters_file)"
34 >
35 <origin xyz="0 0 1" rpy="0 0 0" />
36 </xacro:my_robot_cell>
37
38 <!--Create the control tag for the UR robot-->
39 <xacro:ur_ros2_control
40 name="$(arg ur_type)"
41 tf_prefix="$(arg ur_type)_"
42 kinematics_parameters_file="$(arg kinematics_parameters_file)"
43 robot_ip="$(arg robot_ip)"
44 script_filename="$(arg ur_script_filename)"
45 output_recipe_filename="$(arg ur_output_recipe_filename)"
46 input_recipe_filename="$(arg ur_input_recipe_filename)"
47 use_mock_hardware="$(arg use_mock_hardware)"
48 mock_sensor_commands="$(arg mock_sensor_commands)"
49 headless_mode="$(arg headless_mode)"
50 />
51
52 <!--If you had more controlled devices in your description, e.g. a gripper, you can add another ros2_control tag here-->
53
54</robot>
This URDF is very similar to the one we have already assembled. We simply need to include the ros2_control macro,
<xacro:include filename="$(find ur_robot_driver)/urdf/ur.ros2_control.xacro"/>
define the necessary arguments that need to be passed to the macro,
<xacro:arg name="robot_ip" default="0.0.0.0"/>
<xacro:arg name="headless_mode" default="false" />
<xacro:arg name="ur_script_filename" default="$(find ur_client_library)/resources/external_control.urscript"/>
<xacro:arg name="ur_output_recipe_filename" default="$(find ur_robot_driver)/resources/rtde_output_recipe.txt"/>
<xacro:arg name="ur_input_recipe_filename" default="$(find ur_robot_driver)/resources/rtde_input_recipe.txt"/>
<xacro:arg name="use_mock_hardware" default="false" />
<xacro:arg name="mock_sensor_commands" default="false" />
and then call the macro by providing all the specified arguments.
<xacro:ur_ros2_control
name="$(arg ur_type)"
tf_prefix="$(arg ur_type)_"
kinematics_parameters_file="$(arg kinematics_parameters_file)"
robot_ip="$(arg robot_ip)"
script_filename="$(arg ur_script_filename)"
output_recipe_filename="$(arg ur_output_recipe_filename)"
input_recipe_filename="$(arg ur_input_recipe_filename)"
use_mock_hardware="$(arg use_mock_hardware)"
mock_sensor_commands="$(arg mock_sensor_commands)"
headless_mode="$(arg headless_mode)"
/>
Extract the calibration
One very important step is to extract the robot’s specific calibration and save it to our workcell’s startup package. For details, please refer to our documentation on extracting the calibration information. For now, we just copy the default one for the ur20.
cp $(ros2 pkg prefix ur_description)/share/ur_description/config/ur20/default_kinematics.yaml \
my_robot_cell_control/config/my_robot_calibration.yaml
Create robot_state_publisher launchfile
To use the custom controlled description, we need to generate a launchfile loading that description
(Since it contains less / potentially different) arguments than the “default” one. In that
launchfile we need to start a robot_state_publisher
(RSP) node that will get the description as a
parameter and redistribute it via the robot_description
topic:
1
2from launch_ros.actions import Node
3from launch_ros.substitutions import FindPackageShare
4
5from launch import LaunchDescription
6from launch.actions import DeclareLaunchArgument
7from launch.substitutions import (
8 Command,
9 FindExecutable,
10 LaunchConfiguration,
11 PathJoinSubstitution,
12)
13
14
15def generate_launch_description():
16 ur_type = LaunchConfiguration("ur_type")
17 robot_ip = LaunchConfiguration("robot_ip")
18
19 use_mock_hardware = LaunchConfiguration("use_mock_hardware")
20 mock_sensor_commands = LaunchConfiguration("mock_sensor_commands")
21
22 headless_mode = LaunchConfiguration("headless_mode")
23
24 kinematics_parameters_file = LaunchConfiguration("kinematics_parameters_file")
25
26 # Load description with necessary parameters
27 robot_description_content = Command(
28 [
29 PathJoinSubstitution([FindExecutable(name="xacro")]),
30 " ",
31 PathJoinSubstitution(
32 [
33 FindPackageShare("my_robot_cell_control"),
34 "urdf",
35 "my_robot_cell_controlled.urdf.xacro",
36 ]
37 ),
38 " ",
39 "robot_ip:=",
40 robot_ip,
41 " ",
42 "ur_type:=",
43 ur_type,
44 " ",
45 "kinematics_parameters_file:=",
46 kinematics_parameters_file,
47 " ",
48 "use_mock_hardware:=",
49 use_mock_hardware,
50 " ",
51 "mock_sensor_commands:=",
52 mock_sensor_commands,
53 " ",
54 "headless_mode:=",
55 headless_mode,
56 ]
57 )
58 robot_description = {"robot_description": robot_description_content}
59
60 declared_arguments = []
61 # UR specific arguments
62 declared_arguments.append(
63 DeclareLaunchArgument(
64 "ur_type",
65 description="Typo/series of used UR robot.",
66 choices=[
67 "ur3",
68 "ur3e",
69 "ur5",
70 "ur5e",
71 "ur10",
72 "ur10e",
73 "ur16e",
74 "ur20",
75 "ur30",
76 ],
77 default_value="ur20",
78 )
79 )
80 declared_arguments.append(
81 DeclareLaunchArgument(
82 "robot_ip", description="IP address by which the robot can be reached."
83 )
84 )
85 declared_arguments.append(
86 DeclareLaunchArgument(
87 "kinematics_parameters_file",
88 default_value=PathJoinSubstitution(
89 [
90 FindPackageShare("my_robot_cell_control"),
91 "config",
92 "my_robot_calibration.yaml",
93 ]
94 ),
95 description="The calibration configuration of the actual robot used.",
96 )
97 )
98 declared_arguments.append(
99 DeclareLaunchArgument(
100 "use_mock_hardware",
101 default_value="false",
102 description="Start robot with mock hardware mirroring command to its states.",
103 )
104 )
105 declared_arguments.append(
106 DeclareLaunchArgument(
107 "mock_sensor_commands",
108 default_value="false",
109 description="Enable mock command interfaces for sensors used for simple simulations. "
110 "Used only if 'use_mock_hardware' parameter is true.",
111 )
112 )
113 declared_arguments.append(
114 DeclareLaunchArgument(
115 "headless_mode",
116 default_value="false",
117 description="Enable headless mode for robot control",
118 )
119 )
120
121 return LaunchDescription(
122 declared_arguments
123 + [
124 Node(
125 package="robot_state_publisher",
126 executable="robot_state_publisher",
127 output="both",
128 parameters=[robot_description],
129 ),
130 ]
131 )
With this we could start our workcell using
ros2 launch ur_robot_driver ur_control.launch.py \
description_launchfile:=$(ros2 pkg prefix my_robot_cell_control)/share/my_robot_cell_control/launch/rsp.launch.py \
use_mock_hardware:=true \
robot_ip:=123 \
ur_type:=ur20 \
rviz_config_file:=$(ros2 pkg prefix my_robot_cell_description)/share/my_robot_cell_description/rviz/urdf.rviz \
tf_prefix:=ur20_
Create start_robot launchfile
Since the command above is obviously not very convenient to start our robot, we wrap that into another
launchfile that includes the ur_control.launch.py
launchfile with the correct description
launchfile and prefix:
1from launch_ros.actions import Node
2from launch_ros.parameter_descriptions import ParameterFile
3from launch_ros.substitutions import FindPackageShare
4
5from launch import LaunchDescription
6from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
7from launch.launch_description_sources import PythonLaunchDescriptionSource
8from launch.conditions import IfCondition
9from launch.substitutions import (
10 Command,
11 FindExecutable,
12 LaunchConfiguration,
13 PathJoinSubstitution,
14 TextSubstitution,
15)
16
17
18def generate_launch_description():
19 ur_type = LaunchConfiguration("ur_type")
20 robot_ip = LaunchConfiguration("robot_ip")
21 declared_arguments = []
22 declared_arguments.append(
23 DeclareLaunchArgument(
24 "ur_type",
25 description="Type/series of used UR robot.",
26 choices=[
27 "ur3",
28 "ur3e",
29 "ur5",
30 "ur5e",
31 "ur10",
32 "ur10e",
33 "ur16e",
34 "ur20",
35 "ur30",
36 ],
37 default_value="ur20",
38 )
39 )
40 declared_arguments.append(
41 DeclareLaunchArgument(
42 "robot_ip",
43 default_value="192.168.56.101", # put your robot's IP address here
44 description="IP address by which the robot can be reached.",
45 )
46 )
47 declared_arguments.append(
48 DeclareLaunchArgument("launch_rviz", default_value="true", description="Launch RViz?")
49 )
50
51 return LaunchDescription(
52 declared_arguments
53 + [
54 IncludeLaunchDescription(
55 PythonLaunchDescriptionSource(
56 [
57 PathJoinSubstitution(
58 [
59 FindPackageShare("ur_robot_driver"),
60 "launch",
61 "ur_control.launch.py",
62 ]
63 )
64 ]
65 ),
66 launch_arguments={
67 "ur_type": ur_type,
68 "robot_ip": robot_ip,
69 "tf_prefix": [LaunchConfiguration("ur_type"), "_"],
70 "rviz_config_file": PathJoinSubstitution(
71 [
72 FindPackageShare("my_robot_cell_description"),
73 "rviz",
74 "urdf.rviz",
75 ]
76 ),
77 "description_launchfile": PathJoinSubstitution(
78 [
79 FindPackageShare("my_robot_cell_control"),
80 "launch",
81 "rsp.launch.py",
82 ]
83 ),
84 }.items(),
85 ),
86 ]
87 )
With that we can start the robot using
ros2 launch my_robot_cell_control start_robot.launch.py use_mock_hardware:=true
Testing everything
Before we can test our code, it’s essential to build and source our Colcon workspace:
#cd to your colcon workspace root, e.g.
cd ~/colcon_ws
#source and build your workspace
colcon build
source install/setup.bash
We can start the system in a mocked simulation
#start the driver with mocked hardware
ros2 launch my_robot_cell_control start_robot.launch.py use_mock_hardware:=true
Or to use it with a real robot:
#start the driver with real hardware
ros2 launch my_robot_cell_control start_robot.launch.py