|
| 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 | +[](https://opensource.org/licenses/BSD-3-Clause) |
| 7 | + |
| 8 | +# Demo Video with motion_primitives_forward_controller |
| 9 | +[](https://youtu.be/SKz6LFvJmhQ) |
| 10 | + |
| 11 | +# Architecture |
| 12 | +**with motion_primitives_forward_controller** |
| 13 | + |
| 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 | + |
| 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 |
0 commit comments