-
Notifications
You must be signed in to change notification settings - Fork 22
Home
This project aims to provide reliable real-time capable drivers for all KUKA robots. Currently KUKA robots are available with 3 different operating systems with real-time control API-s:
- KSS supporting industrial robots, with Robot Sensor Interface (RSI)
- Sunrise supporting cobots (LBR iiwa-s), with Fast Robot Interface (FRI)
- iiQKA supporting cobots (LBR iisy-s), with ExternalAPI.Control (EAC)
It is also the goal of this project to provide the same API for all three OS-s, hiding the underlying startup procedure and communication technology, thus enabling changing seamlessly to a different KUKA OS.
Additionally the aim was to write high quality, maintainable code with standardized interfaces, that conforms with the standards defined by the ROS-Industrial project.
Two different interfaces should be defined for all drivers supporting real-time control:
- real-time interface, defining how to handle the cyclic dataflow
- non-real-time interface, defining the startup procedure with optional configuration
The choice for the real-time interface was straightforward, as a standardized control framework exists for ROS2, called ros2_control
, also supported by ROS-Industrial. The drivers are built using this framework, therefore it is recommended the read through its documentation, as this documentation builds on the knowledge of the framework.
All 3 of the KUKA real-time interfaces handle the timing on the controller side, so external control is always synchronized with the internal control cycle. This means, that calling the read
method of the controller_manager
cannot return immediately, but has to wait until the controller sends an update, which is triggered by the internal clock. Because of this blocking read, the default ros2_control_node
cannot be used, as there it is expected that the controller_manager
handles the timing according to the configured control frequency. Therefore a custom control node was implemented that uses the controller_manager
and all other tools of ros2_control
, but leaves the time management to the robot controller.
This change does not influence the API of the ros2_control
framework, the real-time dataflow can be accessed by any controller.
The startup procedure for any system in ROS can be defined using a launch file, that can start multiple processes. By default, starting the control node with a hardware interface and controllers configured immediately starts external control. This behaviour has a few drawbacks:
- The user cannot configure some parameters during runtime, that cannot be changed during external control
- The user cannot easily synchronize the start of external control with other components of the system
- It can cause unexpected behaviour, which can be potentially dangerous to the hardware or surroundings.
- In torque control mode, the robot can start to move, if the torque sensors are not perfectly calibrated (and as that is hard to achieve, this can happen in most cases). This could be mitigated with a simple torque controller that tries to hold the position, but there is no guarantee that loading and activating the controller was successful, external control would start even if it failed. This could result in a scenario, where the robot starts to move unexpectedly.
The last issue should be certainly prevented from happening, therefore it was decided to extend the default startup procedure with a lifecycle interface, that synchronizes all components of the driver. The hardware interfaces and controllers already have a lifecycle interface, but by default they are loaded and activated at startup. This configuration was modified to only load the hardware interfaces and controllers, configuration and activation is handled by a custom a lifecycle node, called robot_manager
. The 3 states of the robot_manager
node have the following meaning:
-
unconfigured
: all necessary components are started, but no connection is needed to the robot -
configured
: The driver has valid parameters configured, external control can be initiated. It is possible to change most parameters (with the exception of IP addresses and robot model) in this state without having to clean up therobot_manager
node. Connection to the robot might be needed. (All of the parameters have default values in the driver, which are set on the robot controller during configuration.) A few configuration controllers might be active, that handle the runtime parameters of the hardware interface. -
active
: external control is running with cyclic real-time communication, controllers are active
To achieve these synchronized states, the state transitions of the system do the following steps (implemented by the launch file and the robot_manager
node):
- startup: all components of the system are started:
control_node
,robot_manager
node,robot_state_publisher
(optionallyrviz
) and all of the necessary controllers are loaded and configured -
configure
: activate configuration controllers, configure hardware interface -
activate
: activate real-time controllers, activate hardware interface -
deactivate
: deactivate hardware interface, deactivate real-time controllers -
cleanup
: clean up hardware interface, deactivate configuration controllers
Note: the lifecycle interface of the controllers are a little bit different, as they do not have a cleanup
transition. To have a consequent unconfigured
state, the configuration of the controllers are not handled in the configure
transition, but at startup.
Including the controller state handling in the system state makes the implementation more complex, as controllers must be deactivated and activated at control mode changes, but it has two advantages:
- minor performance increase: unused controllers are not active and therefore do not consume resources
- unexpected behaviour is not possible: external control will not start on the robot, unless all necessary controllers are successfully activated, while control mode changes (on the robot) are only possible after the controllers for the new control mode are activated.
The consequence of the lifecycle interface is, that 3 commands are necessary to start external control for all robots:
- start the appropriate launch file for your robot with your robot model as parameter (details can be found here)
ros2 lifecycle set robot_manager configure
ros2 lifecycle set robot_manager activate
The control mode specifications are also part of the common API. They are defined as an enum in the kuka_drivers_core
package, and have the following meaning:
- joint position control: the driver streams cyclic position updates for every joint.
- Needed command interface(s):
position
- Needed command interface(s):
- joint impedance control: the driver streams cyclic position updates for every joint and additionally stiffness [Nm/rad] and normalized damping [-] attributes, which define how the joint reacts to external effects (around the setpoint position). The effect of gravity is compensated internally. (FRI does not allow changing the impedance attributes in runtime, therefore the initial damping and stiffness values are valid for the whole motion.)
- Needed command interface(s):
position
,stiffness
,damping
- Needed command interface(s):
- joint velocity control: the driver streams cyclic velocity updates for every joint.
- Needed command interface(s):
velocity
- Needed command interface(s):
- joint torque control: the driver streams cyclic torque updates for every joint, which define the torque overlay to be superimposed over gravity compensation. (An input of 0 means, that the joint should remain in gravity compensation and should not move.)
- Needed command interface(s):
effort
- Needed command interface(s):
- cartesian position control: the driver streams cyclic pose updates for every degree of freedom. The orientation representation is the KUKA ABC convention. It is the responsibility of the user to stream poses, for which a valid IK solution exists.
- Needed command interface(s):
cart_position
- Needed command interface(s):
- cartesian impedance control: the driver streams cyclic pose updates for every degree of freedom. Additional stiffness [N/m or Nm/rad] and normalized damping [-] attributes define the behaviour of each degree of freedom to external forces. The nullspace stiffness and damping values define the behaviour of the redundant degree(s) of freedom.
- Needed command interface(s):
cart_position
,cart_stiffness
,cart_damping
, (nullspace_stiffness
,nullspace_damping
)
- Needed command interface(s):
- cartesian velocity control: the driver streams cyclic cartesian velocity (twist) updates for every degree of freedom. It is the responsibility of the user to stream velocities, for which a valid IK solution exists.
- Needed command interface(s):
cart_velocity
- Needed command interface(s):
- wrench control: the driver streams cyclic wrench updates, which define the forces and torques, that the robot end effector should exert on the environment. The effect of gravity is internally compensated. (If the environment does not have a counterforce, the robot will start to move)
- Needed command interface(s):
wrench
- Needed command interface(s):
The following table shows the supported features and control modes of each driver. (✓
means supported, ✗
means not supported by the KUKA interface, empty means supported by the KUKA interface, but not yet supported by the driver)
OS | Joint position control | Joint impedance control | Joint velocity control | Joint torque control | Cartesian position control | Cartesian impedance control | Cartesian velocity control | Wrench control | I/O control |
---|---|---|---|---|---|---|---|---|---|
KSS | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ||
Sunrise | ✓ | ✓ | ✗ | ✓ | ✗ | ||||
iiQKA | ✓ | ✓ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
The repository contains a few other packages aside from the 3 drivers:
-
kuka_driver_interfaces
: this package contains the custom message definition necessary for KUKA robots. -
kuka_drivers_core
: this package contains core functionalities used by more drivers, including thecontrol_node
, base classes for nodes with improved parameter handling, enum and constant definitions and a class for managing the controller activation and deactivation at control mode changes. Details about these features can be found in the package documentation -
kuka_rsi_simulator
: this package contains a simple simulator of RSI, that implements a UDP server accepting the same xml format as RSI and returning the commanded values as the current state, without any checks. -
iiqka_moveit_example
: this package contains basic examples of using moveit with the driver, more information in the next section. Additionally it contains a launch file that commands 4 goal positions near the home position cyclically (the points and parameters can be modified in this configuration file). This can be used to test moving any robot with the driver, and is the recommended way instead of therqt_joint_trajectory_controller
, which commands very jerky trajectories due to batching.
The ros2_control
framework supports Moveit out-of-the-box, as the joint_trajectory_controller
can interpolate the trajectories planned by it. Setting up Moveit is a little more complex, therefore an example package (iiqka_moveit_example
) is provided to help developers.
The package contains a launch file that starts the iiQKA driver, rviz
, and the move_group
server with the required configuration. The robot_manager
lifecycle node should be configured and activated after startup.
After activation, the Motion Planning plugin can be added (Add
-> moveit_ros_visualisation
-> MotionPlanning
) to plan trajectories from the rviz
GUI. (Planning group
in the Planning
tab should be changed to manipulator
.)
The package also contains examples of sending planning requests from C++ code, in which case the rviz
plugin is not necessary. The MoveitExample
class implements a wrapper around the MoveGroupInterface
, the example executables in the package use this class to interact with Moveit
. Four examples are provided:
-
moveit_basic_planners_example
: the example uses thePILZ
motion planner to plan aPTP
and aLIN
trajectory. It also demonstrates that planning with collision avoidance is not possible with thePILZ
planner by adding a collision box that invalidates the planned trajectory. -
moveit_collision_avoidance_example
: the example adds a collision box to the scene and demonstrates, that path planning with collision avoidance is possible using theOMPL
planning pipeline. -
moveit_constrained_planning_example
: this example demonstrates constrained planning capabilities, as the planner can find a valid path around the obstacle with the end effector orientation remaining constant (with small tolerance). -
moveit_depalletizing_example
: this example shows a depalletizing example: a 3x3x3 pallet pattern is added to the scene, the robot can successfully finish the depalletizing process by attaching each pallet to the end effector and moving it to a dropoff position with collision avoidance.
Note: the first three examples should be executed consequently (without restarting the launch file) to ensure that the collision objects are indeed in the way of the trivial path. The 4. example should be executed independently, so that the collision box added in the other examples are not there (launch file should be restarted after the other examples).
Note: The examples need user interaction in rviz
, the Next
button (RvizVisualToolsGui
tab) should be pressed each time the logs indicate it.
Planning with Moveit
, or simply moving the robot in impedance mode is quite tricky, as often there is a mismatch between the actual and commanded positions.
Both Moveit
and the joint_trajectory_controller
start planning/execution from the actually measured values, creating a jump in the commands (from the previous command to the measured state), which can trigger a joint limit violation. To solve this issue, the planning request must be modified to start from the commanded position instead of the measured one.
For example in the moveit_basic_planners_example
the following lines must be added before each planning request:
moveit_msgs::msg::RobotState start_state;
std::vector<double> commanded_pos; // TODO: fill this vector with the actually commanded positions
start_state.joint_state.name = example_node->moveGroupInterface()->getJointNames();
start_state.joint_state.position = commanded_pos;
example_node->moveGroupInterface()->setStartState(start_state);
Additionally the trajectory_execution.allowed_start_tolerfance
parameter in moveit_controllers.yaml
(found in the moveit support packages) should be increased based on the actual displacement between commanded and measured joint values.
If you would like to only move the robot by sending a goal to the joint_trajectory_controller
for interpolation (e.g. with the example trajectory publisher), the following line should be added to the controller configuration file:
open_loop_control: true
As the robot controllers manage the timing of the drivers, it does not make sense to add more robots to the same control loop, as it would only lead to timeouts on the controller side. Therefore to start the drivers of multiple robots, all components has to be duplicated. This can be achieved by using the namespace
argument of the launch files (which is available for all drivers) to avoid name collisions. This will add a namespace to all nodes and controllers of the driver and will also modify the prefix
argument of the robot description macro to namespace_
. To adapt to the new namespace and prefix, the configurations files of the driver must also be modified to reflect the new node and joint names. An example of this can found in the iiqka_moveit_example
package, that starts two robots with test1
and test2
namespaces and modified configuration files.
For more detailed information about the drivers, visit the dedicated wiki pages for KSS, Sunrise or iiQKA robots.