ros_controllers
ros_controllers copied to clipboard
Pulling out hardware_interface_adapter from joint_trajectory_controller into separate package?
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?
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 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_interface
s. 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.
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 JointStateHandle
s. 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.
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
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 typehardware_interface::CartOptEffortJointInterface
to add to myRobotHW
- 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