You're reading the documentation for a version of ROS 2 that has reached its EOL (end-of-life), and is no longer officially supported. If you want up-to-date information, please have a look at Jazzy.

Differences to ros_control (ROS 1)

Hardware Structures - classes

The ros_control framework uses the RobotHW class as a rigid structure to handle any hardware. This makes it impossible to extend the existing robot with additional hardware, like sensors, actuators, and tools, without coding. The CombinedRobotHardware corrects this drawback. Still, this solution is not optimal, especially when combining robots with external sensors.

The ros2_control framework defines three types of hardware Actuator, Sensor and System. Using a combination (composition) of those basic components, any physical robotic cell (robot and its surrounding) can be described. This also means that multi-robot, robot-sensor, robot-gripper combinations are supported out of the box. Section Hardware Components describes this in detail.

Hardware Interfaces

The ros_control framework allows only three types of interfaces (joints), i.e., position, velocity, and effort. The RobotHW class makes it very hard to use any other data to control the robot.

The ros2_control approach does not enforce a fixed set of interface types, but they are defined as strings in hardware’s description. To ensure compatibility of standard controllers, standard interfaces are defined as constants in hardware_interface package.

Controller’s Access to Hardware

In ros_control, the controllers had direct access to the RobotHW class requesting access to its interfaces (joints). The hardware itself then took care of registered interfaces and resource conflicts.

In ros2_control, ResourceManager takes care of the state of available interfaces in the framework and enables controllers to access the hardware. Also, the controllers do not have direct access to hardware anymore, but they register their interfaces to the ControllerManager.

Migration Guide to ros2_control

RobotHardware to Components

  1. The implementation of RobotHW is not used anymore. This should be migrated to SystemInterface class or, for more granularity, SensorInterface and ActuatorInterface. See the above description of “Hardware Components” to choose a suitable strategy.

  2. Decide which component type is suitable for your case. Maybe it makes sense to separate RobotHW into multiple components.

  3. Implement ActuatorInterface, SensorInterface or SystemInterface classes as follows:

    1. In the constructor, initialize all variables needed for communication with your hardware or define the default one.

    2. In the configure function, read all the parameters your hardware needs from the parsed URDF snippet (i.e., from the HardwareInfo structure). Here you can cross-check if all joints and interfaces in URDF have allowed values or a combination of values.

    3. Define interfaces to and from your hardware using export_*_interfaces functions. The names are <joint>/<interface> (e.g., joint_a2/position). This can be extracted from the HardwareInfo structure or be hard-coded if sensible.

    4. Implement start() and stop() methods for your hardware. This usually includes changing the hardware state to receive commands or set it into a safe state before interrupting the command stream. It can also include starting and stopping communication.

    5. Implement read() and write() methods to exchange commands with the hardware. This method is equivalent to those from RobotHW-class in ROS 1.

    6. Do not forget the PLUGINLIB_EXPORT_CLASS macro at the end of the .cpp file.

  4. Create .xml library description for the pluginlib, for example see RRBotSystemPositionOnlyHardware.

Controller Migration

An excellent example of a migrated controller is the JointTrajectoryController. The real-time critical methods are marked as such.

  1. Implement ControllerInterface class as follows:

    1. If there are any member variables, initialized those in the constructor.

    2. In the init() method, first call ControllerInterface::init() to initialize the lifecycle of the controller. Following this, declare all parameters defining their default values.

    3. Implement the state_interface_configuration() and command_interface_configuration() methods.

    4. Design the update() function for the controller. (real-time)

    5. Add the required lifecycle management methods (others are optional):
      • on_configure() - reads parameters and configures controller.

      • on_activate() - called when controller is activated (started) (real-time)

      • on_deactivate() - called when controller is deactivated (stopped) (real-time)

    6. Finally, do not forget to add the PLUGINLIB_EXPORT_CLASS macro at the end of the .cpp file.

  2. Create .xml library description for the pluginlib, for example see joint_trajectory_plugin.xml.