Skip to content

Commit 4893313

Browse files
authored
Implement motion_primitive interface in hardware interface (#1341)
With this it is possible to use motion_primitives to command the robot's motion.
1 parent 6344060 commit 4893313

14 files changed

+1146
-14
lines changed

ur_calibration/include/ur_calibration/calibration.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@
4444
#ifndef UR_CALIBRATION__CALIBRATION_HPP_
4545
#define UR_CALIBRATION__CALIBRATION_HPP_
4646

47-
#include <cassert>
4847
#include <Eigen/Dense>
48+
#include <cassert>
4949
#include <fstream>
5050
#include <string>
5151
#include <vector>

ur_robot_driver/CMakeLists.txt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ find_package(tf2_geometry_msgs REQUIRED)
3838
find_package(ur_client_library REQUIRED)
3939
find_package(ur_dashboard_msgs REQUIRED)
4040
find_package(ur_msgs REQUIRED)
41+
find_package(motion_primitives_controllers REQUIRED)
42+
find_package(control_msgs REQUIRED)
4143

4244
include_directories(include)
4345

@@ -55,12 +57,14 @@ target_link_libraries(ur_robot_driver_plugin PUBLIC
5557
${tf2_geometry_msgs_TARGETS}
5658
${ur_dashboard_msgs_TARGETS}
5759
${ur_msgs_TARGETS}
60+
${control_msgs_TARGETS}
5861
controller_manager::controller_manager
5962
hardware_interface::hardware_interface
6063
pluginlib::pluginlib
6164
rclcpp::rclcpp
6265
rclcpp_lifecycle::rclcpp_lifecycle
6366
ur_client_library::urcl
67+
motion_primitives_controllers::motion_primitives_controllers
6468
)
6569
target_include_directories(
6670
ur_robot_driver_plugin
@@ -190,10 +194,6 @@ install(DIRECTORY resources
190194
)
191195

192196

193-
install(DIRECTORY include/
194-
DESTINATION include
195-
)
196-
197197
ament_export_dependencies(
198198
hardware_interface
199199
pluginlib
@@ -202,10 +202,6 @@ ament_export_dependencies(
202202
controller_manager
203203
controller_manager_msgs
204204
geometry_msgs
205-
hardware_interface
206-
pluginlib
207-
rclcpp
208-
rclcpp_lifecycle
209205
std_msgs
210206
std_srvs
211207
tf2_geometry_msgs
@@ -225,6 +221,7 @@ install(PROGRAMS
225221
scripts/start_ursim.sh
226222
examples/examples.py
227223
examples/force_mode.py
224+
examples/send_dummy_motion_primitives_ur10e.py
228225
examples/move_until_example.py
229226
DESTINATION lib/${PROJECT_NAME}
230227
)
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
hardware_interface in motion primitives mode
2+
==========================================
3+
4+
Hardware interface for executing motion primitives on a UR robot using the ROS 2 control framework. It allows the controller to execute linear (LINEAR_CARTESIAN/ LIN/ MOVEL), circular (CIRCULAR_CARTESIAN/ CIRC/ MOVEC), and joint-based (LINEAR_JOINT/ PTP/ MOVEJ) motion commands asynchronously and supports motion sequences for smooth trajectory execution.
5+
6+
[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
7+
8+
# Demo Video with motion_primitives_forward_controller
9+
[![Play Video](doc/motion_primitive_ur_driver/moprim_forward_controller_ur_demo_thumbnail.png)](https://youtu.be/SKz6LFvJmhQ)
10+
11+
# Architecture
12+
**with motion_primitives_forward_controller**
13+
![Architecture Overview](doc/motion_primitive_ur_driver/ros2_control_motion_primitives_ur.drawio.png)
14+
15+
# Command and State Interfaces
16+
17+
In motion primitives mode, the following state and command interfaces are used to enable communication between the controller and the hardware interface.
18+
19+
## Command Interfaces
20+
21+
These interfaces are used to send motion primitive data to the hardware interface:
22+
23+
- `motion_type`: Type of motion primitive (LINEAR_JOINT, LINEAR_CARTESIAN, CIRCULAR_CARTESIAN)
24+
- `q1``q6`: Target joint positions for joint-based motion
25+
- `pos_x`, `pos_y`, `pos_z`: Target Cartesian position
26+
- `pos_qx`, `pos_qy`, `pos_qz`, `pos_qw`: Orientation quaternion of the target pose
27+
- `pos_via_x`, `pos_via_y`, `pos_via_z`: Intermediate via-point position for circular motion
28+
- `pos_via_qx`, `pos_via_qy`, `pos_via_qz`, `pos_via_qw`: Orientation quaternion of via-point
29+
- `blend_radius`: Blending radius for smooth transitions
30+
- `velocity`: Desired motion velocity
31+
- `acceleration`: Desired motion acceleration
32+
- `move_time`: Optional duration for time-based execution (For LINEAR_JOINT and LINEAR_CARTESIAN. If move_time > 0, velocity and acceleration are ignored)
33+
34+
## State Interfaces
35+
36+
These interfaces are used to communicate the internal status of the hardware interface back to the controller.
37+
38+
- `execution_status`: Indicates the current execution state of the primitive. Possible values are:
39+
- `IDLE`: No motion in progress
40+
- `EXECUTING`: Currently executing a primitive
41+
- `SUCCESS`: Last command finished successfully
42+
- `ERROR`: An error occurred during execution
43+
- `STOPPING`: The hardware interface has received the `STOP_MOTION` command, but the robot has not yet come to a stop.
44+
- `STOPPED`: The robot was stopped using the `STOP_MOTION` command and must be reset with the `RESET_STOP` command before executing new commands.
45+
- `ready_for_new_primitive`: Boolean flag indicating whether the interface is ready to receive a new motion primitive
46+
47+
In addition to these, the driver also provides all standard state interfaces from the original UR hardware interface (e.g., joint positions, velocities). These are used by components like the `joint_state_broadcaster` and allow tools like RViz to visualize the current robot state.
48+
49+
50+
# Supported Motion Primitives
51+
52+
- Support for basic motion primitives:
53+
- `LINEAR_JOINT`
54+
- `LINEAR_CARTESIAN`
55+
- `CIRCULAR_CARTESIAN`
56+
- Additional helper types:
57+
- `STOP_MOTION`: Immediately stops the current robot motion and clears all pending primitives in the controller's queue.
58+
- `RESET_STOP`: After `RESET_STOP`, new commands can get handled.
59+
- `MOTION_SEQUENCE_START` / `MOTION_SEQUENCE_END`: Define a motion sequence block. All primitives between these two markers will be executed as a single, continuous sequence. This allows seamless transitions (blending) between primitives.
60+
61+
![MotionPrimitiveExecutionWithHelperTypes](doc/motion_primitive_ur_driver/MotionPrimitiveExecutionWithHelperTypes_UR.drawio.png)
62+
63+
# Overview
64+
65+
In contrast to the standard UR hardware interface, this driver does not compute or execute trajectories on the ROS 2 side. Instead, it passes high-level motion primitives directly to the robot controller, which then computes and executes the trajectory internally.
66+
67+
This approach offers two key advantages:
68+
69+
- **Reduced real-time requirements** on the ROS 2 side, since trajectory planning and execution are offloaded to the robot.
70+
- **Improved motion quality**, as the robot controller has better knowledge of the robot's kinematics and dynamics, leading to more optimized and accurate motion execution.
71+
72+
73+
## write() Method
74+
75+
In motion primitives mode, the `write()` method checks whether a new motion primitive command has been received from the controller via the command interfaces. If a new command is present:
76+
77+
1. If the command is `STOP_MOTION`, a flag is set which leads to interrupting the current motion inside the `asyncStopMotionThread()`. If the command is `RESET_STOP`, the flag is reset, and new motion primitives can be received and executed.
78+
2. For other commands, they are passed to the `asyncCommandThread()` and executed asynchronously. Individual primitives are executed directly via the Instruction Executor.
79+
If a `MOTION_SEQUENCE_START` command is received, all subsequent primitives are added to a motion sequence. Once `MOTION_SEQUENCE_END` is received, the entire sequence is executed via the Instruction Executor.
80+
81+
Threading is required since calls to the Instruction Executor are blocking. Offloading these to separate threads ensures the control loop remains responsive during motion execution. The stopping functionality is also threaded to allow interrupting a primitive even during execution or in a motion sequence.
82+
83+
## read() Method
84+
85+
The `read()` method:
86+
87+
- Publishes the `execution_status` over a state interface with possible values: `IDLE`, `EXECUTING`, `SUCCESS`, `ERROR`, `STOPPED`.
88+
- Publishes `ready_for_new_primitive` over a state interface to signal whether the interface is ready to receive a new primitive.
89+
- Handles additional state interfaces from the UR driver, such as joint states, enabling RViz to visualize the current robot pose.
90+
91+
92+
# Example usage notes with UR10e
93+
## (optional) URSim
94+
Start UR-Sim according to the [Universal Robots ROS 2 Driver Documentation](https://docs.universal-robots.com/Universal_Robots_ROS2_Documentation/doc/ur_client_library/doc/setup/ursim_docker.html) or the [Documentation for universalrobots/ursim_e-series docker container](https://hub.docker.com/r/universalrobots/ursim_e-series)
95+
```
96+
ros2 run ur_client_library start_ursim.sh -m ur10e
97+
```
98+
Remote control needs to be enabled:
99+
https://robodk.com/doc/en/Robots-Universal-Robots-How-enable-Remote-Control-URe.html
100+
101+
## With motion_primitives_forward_controller
102+
**With URSim:**
103+
```
104+
ros2 launch ur_robot_driver ur_control.launch.py ur_type:=ur10e robot_ip:=192.168.56.101 launch_rviz:=true headless_mode:=true initial_joint_controller:=motion_primitive_forward_controller
105+
```
106+
**With H-KA UR10e:**
107+
```
108+
ros2 launch ur_robot_driver ur_control.launch.py ur_type:=ur10e robot_ip:=192.168.1.102 launch_rviz:=true headless_mode:=true initial_joint_controller:=motion_primitive_forward_controller
109+
```
110+
**(optional) switching control mode**
111+
```
112+
ros2 control switch_controllers --activate motion_primitive_forward_controller --deactivate scaled_joint_trajectory_controller
113+
```
114+
**Send motion primitives from python script**
115+
> [!WARNING]
116+
> Ensure that the robot in your configuration is able to execute these motion primitives without any risk of collision.
117+
```
118+
ros2 run ur_robot_driver send_dummy_motion_primitives_ur10e.py
119+
```
120+
During the execution of the motion primitives, the movement can be stopped by pressing the Enter key in the terminal.
121+
122+
# TODOs/ Improvements
123+
- if trajectory is finished while `instruction_executer->cancelMotion()` is called --> returns with execution_status ERROR --> no new command can be sent to hw-interface --> need to call `instruction_executer->cancelMotion()` a second time

ur_robot_driver/config/ur_controllers.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ controller_manager:
5353
# true.
5454
enforce_command_limits: false
5555

56+
motion_primitive_forward_controller:
57+
type: motion_primitives_controllers/MotionPrimitivesForwardController
58+
5659
speed_scaling_state_broadcaster:
5760
ros__parameters:
5861
state_publish_rate: 100.0
@@ -206,3 +209,8 @@ tcp_pose_broadcaster:
206209
tool_contact_controller:
207210
ros__parameters:
208211
tf_prefix: "$(var tf_prefix)"
212+
213+
214+
motion_primitive_forward_controller:
215+
ros__parameters:
216+
tf_prefix: "$(var tf_prefix)"
95 KB
Loading
896 KB
Loading
365 KB
Loading

0 commit comments

Comments
 (0)