ros_controllers icon indicating copy to clipboard operation
ros_controllers copied to clipboard

Pulling out hardware_interface_adapter from joint_trajectory_controller into separate package?

Open skohlbr opened this issue 10 years ago • 5 comments

We are looking into implementing some controllers of our own. For that, the approach taken in the joint_trajectory_controller package of providing a hardware interface adapter seems to be a good idea, as it makes adapting controllers to different interfaces relatively painless. It seems somewhat ugly to depend on joint_trajectory_controller just to get the hardware interface header, however. I´m thus throwing the idea out there to get it out of the joint trajectory controller into a separate package. Thoughts?

skohlbr avatar Sep 04 '14 20:09 skohlbr

Actually only now noticed that this would also require the used 'Segment' class to also be pulled out, so not a straightforward as I initially thought.

skohlbr avatar Sep 04 '14 20:09 skohlbr

It has been initially there brecause no other part of the project needed it. Another reason why it's there is that the input to the adapter is a pos, vel, acc tuple, and not all controllers generate commands of that type. The gripper_action_controller is an interesting example, as it implements a different kind of hardware interface adapter.

Actually only now noticed that this would also require the used 'Segment' class to also be pulled out, so not a straightforward as I initially thought.

It does actually not depend on the Segment class (the header is not included). It is instead templated on a State type. The requirements on this type are implicitly defined by how it's used by the implementation (until C++ has the notion of concepts). So far those requirements are roughly the existence of public position and velocity members (maybe acceleration in the future, but not currently), which implement size() and operator[]().

If ros_control allowed to chain controllers, or more generally functional blocks, these adapters would in fact be converting between hardware_interfaces. In this particular case, the basic joint_trajectory_controller would output to a pos, vel, acc hardware interface (cf. https://github.com/ros-controls/ros_control/pull/148), which would then be used as-is, or connected to conversion blocks downstream. This is food for ros_control 2.x.

adolfo-rt avatar Sep 04 '14 21:09 adolfo-rt

As you're considering what to do with the adapter, take a look at what I've done to change joint_trajectory_control: https://github.com/gt-ros-pkg/ros_controllers/compare/ros-controls:indigo-devel...jogging-ctrl?diff=split#diff-13

The first thing to notice is that the template parameters have changed. Instead of templating on the HardwareInterface, I template on the HardwareInterfaceAdapter class. This allows one to write multiple adapters for the same interface--not possible with the current implementation. Furthermore, the Controller base class is templated. This is crucial for getting the controller to work with my multi_controller framework: https://github.com/gt-ros-pkg/ros_control/blob/hydro-devel-multi-iface/controller_interface/include/controller_interface/multi_controller.h#L53-L94

Finally, we break the init method into parameter initialization (JointTrajectoryControllerBase::initInternal) and joint initialization, removing the later. The controller no longer has access to the full JointHandles, but rather, the only thing it really needs access to, the vector of JointStateHandles: https://github.com/gt-ros-pkg/ros_controllers/compare/ros-controls:indigo-devel...jogging-ctrl?diff=split#diff-b6b38387e5596ed3fbad49aac3692863R177 To me, since JointTrajectoryController should never be directly selecting the commands, this is an appropriate separation of concerns.

At this point, JointTrajectoryControllerBase is an abstract class which pretty much directly implements the ControllerBase interface. Then, for a particular Controller, the JointTrajectoryController* is implemented: https://github.com/gt-ros-pkg/ros_controllers/compare/ros-controls:indigo-devel...jogging-ctrl?diff=split#diff-b6b38387e5596ed3fbad49aac3692863R272

The actual init method is very light: https://github.com/gt-ros-pkg/ros_controllers/compare/ros-controls:indigo-devel...jogging-ctrl?diff=split#diff-9ec3f32075624b226ce54cd89fd6a606R690 My reasoning for this is that you need this method for every Controller base class and for every controller which uses HardwareInterfaceAdapters. For example, my JoggingCommandController uses it here: https://github.com/gt-ros-pkg/ros_controllers/compare/ros-controls:indigo-devel...jogging-ctrl?diff=split#diff-3d5dc348e2ca88127edac886aa3fdfbaR396

All of the joint initialization is shifted to the method HardwareInterfaceAdapter::initJoints https://github.com/gt-ros-pkg/ros_controllers/compare/ros-controls:indigo-devel...jogging-ctrl?diff=split#diff-8e04a031faaba0bdd4b11ea27d62f510R120 https://github.com/gt-ros-pkg/ros_controllers/compare/ros-controls:indigo-devel...jogging-ctrl?diff=split#diff-8e04a031faaba0bdd4b11ea27d62f510R207

For the multi-controller initialization, additional logic is needed to distinguish which joints correspond to which adapters. Furthermore, the combined State has to be remapped to individual states during the command update: https://github.com/gt-ros-pkg/ros_controllers/compare/ros-controls:indigo-devel...jogging-ctrl?diff=split#diff-8e04a031faaba0bdd4b11ea27d62f510R304 This is only for the multi-controller implementations though.

It makes sense to me that the joint command handles live in the HardwareInterfaceAdapter, instead of the controller, and that the initJoints method initializes the list of JointStateHandles. It should reduce code duplication. Also, HardwareInterfaceAdapterBase is the interface that a *ControllerBase implements.

Here is an example of how you would build two controllers, a JointTrajectoryController and a JoggingCommandController: https://github.com/gt-ros-pkg/excel-util/blob/hydro/excel_controllers/src/excel_controllers.cpp They both use a HardwareInterfaceAdapter2 which has two types of adapters based on two types of joint interfaces.

kphawkins avatar Sep 16 '14 13:09 kphawkins

Hi @kphawkins !

I would like to code my own JointTrajectoryController. Could you confirm that what you did here 2 years ago is still necessary to create one's own JointTrajectoryController? I don't really like the idea of modifying the sources of joint_trajectory_controller. I'd rather like to get a copy were I can change the namespace and the computation of the commands inside the HardwareInterfaceAdapter::update

Thank you for the help! Jimmy

JimmyDaSilva avatar Nov 24 '16 14:11 JimmyDaSilva

In case someone also struggles with this, I finally found out a proper way to do it. I had to :

  • create a new hardware_interface::EffortJointInterface under a new type hardware_interface::CartOptEffortJointInterface to add to my RobotHW
  • then define
typedef joint_trajectory_controller::JointTrajectoryController<trajectory_interface::QuinticSplineSegment<double>,
                                                                 hardware_interface::CartOptEffortJointInterface>
  • finally implement the controller following joint_trajectory_controller/hardware_interface_adapter.h

See my controller here for an example: https://github.com/kuka-isir/cart_opt_ctrl/commit/2414cef4730f6f127b5f51d5932011b9354079ed

JimmyDaSilva avatar Nov 28 '16 14:11 JimmyDaSilva