控制器链式连接 / 串级控制(Controller Chaining / Cascade Control)¶
本文档提出了对《控制器链式连接设计文档》中描述的串行控制器链式连接(serial controller chaining)的一种最小可行实现(MVI)。串级控制(Cascade Control)是控制器链式连接的一种特定形式。
文档范围与背景知识¶
本方案仅关注控制器的串行链式连接,并尽可能复用现有机制。重点在于控制器的输入与输出,以及 Controller Manager 对这些接口的管理。
本文引入“控制器组”(controller groups)这一概念仅用于表述清晰,其唯一含义是:组内的控制器可以以任意顺序更新。
这并不意味着未来不会实现设计文档中描述的完整“控制器组”机制。作者认为,在当前阶段引入完整控制器组会带来不必要的复杂性,尽管从长远来看,它们可能提供更清晰的结构和接口。
动机、目的与用途¶
为阐明本文意图,我们聚焦于《controllers_chaining》设计文档中的 示例 2(Example 2):
示例 2¶
在此例中,我们希望将以下控制器进行链式连接:
- position_tracking 控制器
- diff_drive_controller(差速驱动控制器)
- 两个 PID 控制器
- robot_localization(机器人定位)控制器
设想一个更灵活的使用场景:我们不仅希望将这些控制器作为一组运行,还希望动态地添加前置步骤。具体流程如下:
-
机器人启动时:
仅激活两个 PID 控制器,以验证电机速度控制是否正常。此时,PID 控制器的输入可通过外部话题(如/pid_reference)控制。同时,这些控制器也提供虚拟接口,可用于后续链式连接。 -
激活
diff_drive_controller:
它连接到 PID 控制器的虚拟输入接口。此时,PID 控制器检测到自己处于“链式模式”(chained mode),自动禁用其外部话题订阅者。随后,验证差速机器人的运动学是否正常。 -
diff_drive_controller激活后:
它暴露odom状态接口(state interfaces),供以下控制器使用: odom_publisher:订阅odom状态接口并发布里程计消息-
sensor_fusion(传感器融合):同时使用diff_drive_controller的odom接口和 IMU 的状态接口 -
sensor_fusion激活后:
它暴露自己的odom状态接口,供robot_localization控制器使用。 -
robot_localization激活后:
它暴露actual_pose状态接口,供position_tracking控制器使用。 -
最后激活
position_tracking: - 它连接到
diff_drive_controller的命令接口(使其禁用外部话题) - 同时使用
robot_localization提供的actual_pose状态接口
关键规则:若链中任一控制器被停用,则其所有前置控制器也必须被停用。
注意事项¶
- 参考接口(reference interfaces)只有在被其他控制器使用时,才进入“链式模式”;否则,控制器从话题订阅者获取参考值。
- 状态接口(state interfaces)被其他控制器使用时,不会触发链式模式。
- 术语说明:
- 前置控制器(preceding controller):其输出连接到另一控制器的输入(参考接口)
- 后置控制器(following controller):其输入由另一控制器提供
例如:
diff_drive_controller是前置控制器,PID 控制器是后置控制器。
实现方案¶
控制器基类:ChainableController¶
ChainableController 继承自 ControllerInterface,并新增两个纯虚方法:
virtual std::vector<hardware_interface::CommandInterface> export_reference_interfaces() = 0;
virtual std::vector<hardware_interface::StateInterface> export_state_interfaces() = 0;
export_reference_interfaces():导出供其他控制器作为命令输入的虚拟命令接口(即本控制器的“参考输入”来源)。export_state_interfaces():导出供其他控制器读取的虚拟状态接口。
简化假设:当前假设控制器的所有参考接口要么全被使用,要么全不被使用。状态接口可被多个控制器同时、任意组合使用。若需排他性,应拆分为多个控制器。
此外,ChainableController 提供:
void set_chained_mode(bool activate); // 设置链式模式标志
virtual void on_set_chained_mode(bool activate) = 0; // 子类实现具体行为
示例¶
- PID 控制器:
- 导出虚拟接口:
pid_reference - 链式模式下:停用
/pid_reference话题订阅者 diff_drive_controller:- 导出虚拟接口:
v_x,v_y,w_z - 链式模式下:停用
/cmd_vel和/cmd_vel_unstamped订阅者,但发布者照常运行
术语定义(Nomenclature)¶
在 ros2_control 中,有两种核心接口:
| 接口类型 | 访问权限 | 典型用途 |
|---|---|---|
CommandInterface |
读写 | 向硬件(或链式控制器)发送命令 |
StateInterface |
只读 | 从硬件(或链式控制器)读取状态 |
控制器接口配置方法¶
| 方法 | 作用 |
|---|---|
command_interface_configuration()(ControllerInterface) |
声明本控制器所需的命令接口(来自硬件或其他控制器的参考接口) |
state_interface_configuration()(ControllerInterface) |
声明本控制器所需的状态接口(来自硬件或其他控制器的状态接口) |
export_reference_interfaces()(ChainableController) |
声明本控制器对外提供的参考接口(供其他控制器作为命令输入) |
export_state_interfaces()(ChainableController) |
声明本控制器对外提供的状态接口(供其他控制器读取) |
Controller Manager 使用这些信息向
ResourceManager申请(claim)所需接口。
内部资源管理(Inner Resource Management)¶
- 当链式控制器被配置(configured)后,Controller Manager 调用其
export_reference_interfaces()和export_state_interfaces()方法。 - Controller Manager 接管这些导出接口的所有权(类似于
ResourceManager管理硬件接口)。 - 接口的“已申请”(claimed)状态由 Controller Manager 维护(使用与
ResourceManager相同的数据结构)。
链式控制器的激活与停用¶
链式控制器的启停必须遵循严格顺序:
- 激活顺序:必须先激活所有后置控制器,才能激活前置控制器。
- 停用顺序:必须先停用所有前置控制器,才能停用后置控制器。
可类比为“链条”:不能从中间断开或插入链节。
实用方法¶
- 使用
switch_controllers服务时,在activate_controllers/deactivate_controllers列表中一次性传入整个链。 - 使用
spawner脚本时,添加--activate-as-group参数,可一次性激活整条链。
调试输出状态说明¶
| 控制器状态 | 参考接口状态 | 说明 |
|---|---|---|
| 未激活(inactive) | 不可用、未申请 | 接口未导出 |
| 激活但未链式连接 | 可用、未申请 | 从话题订阅者获取参考值 |
| 激活且链式连接 | 可用、已申请 | 从链式前置控制器获取参考值 |
结语(Closing Remarks)¶
或许无需引入新的
ChainableController类型。
也可以直接在ControllerInterface中添加export_reference_interfaces()和export_state_interfaces()方法,并默认返回interface_configuration_type::NONE。
这种方式可避免新增基类,保持接口简洁,同时通过默认实现兼容非链式控制器。