Controller Chaining / Cascade Control
This document proposes a minimal-viable-implementation of serial controller chaining as described in Chaining Controllers design document. Cascade control is a specific type of controller chaining.
Scope of the Document and Background Knowledge
This approach focuses only on serial chaining of controllers and tries to reuse existing mechanisms for it. It focuses on inputs and outputs of a controller and their management in the controller manager. The concept of controller groups will be introduced only for clarity reasons, and its only meaning is that controllers in that group can be updated in arbitrary order. This doesn’t mean that the controller groups as described in the controller chaining document will not be introduced and used in the future. Nevertheless, the author is convinced that this would add only unnecessary complexity at this stage, although in the long term they could provide clearer structure and interfaces.
Motivation, Purpose and Use
To describe the intent of this document, lets focus on the simple yet sufficient example Example 2 from ‘controllers_chaining’ design docs:
In this example, we want to chain ‘position_tracking’ controller with ‘diff_drive_controller’ and two PID controllers. Let’s now imagine a use-case where we don’t only want to run all those controllers as a group, but also flexibly add preceding steps. This means the following:
When a robot is started, we want to check if motor velocity control is working properly and therefore only PID controllers are activated. At this stage we can control the input of PID controller also externally using topics. However, these controllers also provide virtual interfaces, so we can chain them.
Then “diff_drive_controller” is activated and attaches itself to the virtual input interfaces of PID controllers. PID controllers also get informed that they are working in chained mode and therefore disable their external interface through subscriber. Now we check if kinematics of differential robot is running properly.
After that, “position_tracking” can be activated and attached to “diff_drive_controller” that disables its external interfaces.
If any of the controllers is deactivated, also all preceding controllers are deactivated.
A Controller Base-Class: ChainableController
ControllerInterface class with
virtual InterfaceConfiguration input_interface_configuration() const = 0 method.
This method should implement for each controller that can be preceded by another controller exporting all the input interfaces.
For simplicity reasons, it is assumed for now that controller’s all input interfaces are used.
Therefore, do not try to implement any exclusive combinations of input interfaces, but rather write multiple controllers if you need exclusivity.
ChainableController base class implements
void set_chained_mode(bool activate) that sets an internal flag that a controller is used by another controller (in chained mode) and calls
virtual void on_set_chained_mode(bool activate) = 0 that implements controller’s specific actions when chained modes is activated or deactivated, e.g., deactivating subscribers.
As an example, PID controllers export one virtual interface
pid_reference and stop their subscriber
<controller_name>/pid_reference when used in chained mode. ‘diff_drive_controller’ controller exports list of virtual interfaces
<controller_name>/w_z, and stops subscribers from topics
<controller_name>/cmd_vel_unstamped. Its publishers can continue running.
Inner Resource Management
After configuring a chainable controller, controller manager calls
input_interface_configuration method and takes ownership over controller’s input interfaces.
This is the same process as done by
ResourceManager and hardware interfaces.
Controller manager maintains “claimed” status of interface in a vector (the same as done in
Activation and Deactivation Chained Controllers
Chained controllers must be activated and deactivated together or in the proper order. This means you must first activate all following controllers to have the preceding one activated. For the deactivation there is the inverse rule - all preceding controllers have to be deactivated before the following controller is deactivated. One can also think of it as an actual chain, you can not add a chain link or break the chain in the middle.
unavailable if the reference interface does not provide much information about anything at the moment. So don’t get confused by it. The reason we have it are internal implementation reasons irelevant for the usage.
Maybe addition of the new controller’s type
ChainableControlleris not necessary. It would also be feasible to add an implementation of
ControllerInterfaceclass with default result