Hardware interfaces are used by ROS control in conjunction with one of the available ROS controllers to send (hardware_interface::RobotHW::write) commands to the hardware and receive (hardware_interface::RobotHW::read) states from the robot's resources (joints, sensors, actuators).
A list of available hardware interfaces (provided via the HardwareResourceManager) as of this writing are:
The following example explains how to set up a new robot to work with the controller_manager. When you make your robot support one or more of the standard interfaces, you will be able to take advantage of a large library of controllers (see ros_controllers) that work on the standard interfaces mentioned above.
As the image above suggests, a custom robot hardware interface is not limited to be composed of only one single interface. The robot can provide as many interfaces as required. It could for example provide both the hardware_interface::PositionJointInterface and the hardware_interface::VelocityJointInterface, and many more.
Assuming a robot with two joints: A & B where both joints are position controlled. Such a robot is defined with a class derived from hardware_interface::RobotHW that should provide the standard PositionJointInterface and the JointStateInterface, so it can re-use all controllers that are already written to work with the PositionJointInterface and the JointStateInterface. The code would look like this:
This code represents a custom robot and is required to control it. The functions above are designed to give the controller manager (and the controllers inside the controller manager) access to the joint state of custom robot, and to command it. When the controller manager runs, the controllers will read from the pos, vel and eff variables of the custom robot hardware interface, and the controller will write the desired command into the cmd variable. It's mandatory to make sure the pos, vel and eff variables always have the latest joint state available, and to make sure that whatever is written into the cmd variable gets executed by the robot. This can be done by implementing hardware_interface::RobotHW::read() and a hardware_interface::RobotHW::write() methods. A node's
main() function can be implemented like this:
To implement software transmissions, see transmission_interface for examples.
The controller manager keeps track of which resources are in use by each of the controllers. A resource can be something like 'right_elbow_joint', 'base', 'left_arm', 'wrist_joints'. Pretty much anything a specific robot can consist of. Put simply, resources are specified in the robot's hardware interface (derived from hardware_interface::RobotHW). And a robot is represented by a bunch of hardware interfaces, where one is a set of similar resources. For example, the hardware_interface::PositionJointInterface groups the position controlled joints as resources. It is also possible to implement a custom hardware interface, and define custom resources. When controllers are getting initialized, they request a number of resources from the hardware interface; these requests get recorded by the controller manager. So the controller manager knows exactly which controller has requested which resources.
The RobotHW class has a simple default implementation for resource management: it simply prevents two controllers that are using the same resource to be running at the same time. Note that two controllers that are using the same resource can be loaded at the same time, but can't be running at the same time. If this simple resource management scheme fits to a robot, nothing else needs to be done, the controller manager will automatically apply this scheme. If a robot needs a different scheme, it is possible to create a custom one, by implementing one single function:
The input to the hardware_interface::RobotHW::checkForConflict method is a list of controller info objects. Each of these objects matches to one single controller, and contains all the info about that controller. This info includes the controller name, controller type, hardware interface type, and the list of resources that are claimed by the controller. Based on all this info, it is possible to come up with a custom scheme to decide if the given list of controllers is allowed to be running at the same time.
The standard interfaces are helpful to avoid writing a whole new set of controllers for a robot, and to take advantage of the libraries of existing controllers. But in case a robot has some features that are not supported by the standard interfaces it is possible to leverage the standard interfaces (and re-use the standard controllers) for the features of a robot that are standard. And at the same time expose robot-specific features in a robot-specific interface. The image shown above shows a robot with both standard and robot-specific interfaces.
The code for such a robot looks like this:
Anotehr way is to register the MyRobot class itself:
With this, the custom interfaces could be nothing more than adding any number of function calls to a custom robot class, and registering the robot class itself. These robot-specific functions will only be available to controllers that are specifically designed for the custom robot, but at the same time, the robot will still work with standard controllers using the standard interfaces of the robot.