迭代学习控制 03:怎样接入通用关节控制器

一个 controller 要适配不同机器人,适合共用的是控制接口、数据记录与学习流程;真正被学到的补偿曲线仍与具体机器人、轨迹、负载和速度有关。本篇从仓库现有的关节控制接口出发,说明 ILC 应放在哪里、每一轮怎样开始和结束,以及怎样判断学到的结果能否继续使用。

专题目录 · 01 原理与资料 · 02 交互实验

1. 先明确当前做到哪一层

本专题已提供可复现的教学数值模型、离线生成的数据和逐轮交互读图。下面的 controller 组合方式是根据现有源码提出的接入设计,尚未加入 ROS2 实时控制链,也未进行真机验证。教学模型中的补偿是位置参考修正,单位为弧度。

当前源码位于 docker/ros2-humble-dev/control_ws/src/ros2_controller/include/ros2_controller/。其中 control_interface.hpp 定义:

struct ControlTime { double now, dt; };

template<class State, class Command>
class IController {
public:
virtual ~IController() = default;
virtual void reset(const State& state, ControlTime time) = 0;
virtual const Command& update(const State& state, ControlTime time) = 0;
};

controller.hpp 进一步把关节边界的数据定义为:

struct JointCommand {
std::vector<double> position, velocity, effort, kp, kd;
};
using IJointController = IController<JointState, JointCommand>;

控制器输出目标关节位置、速度、前馈力矩和 PD 增益。最终的 compute_joint_effort() 先限制目标位置,再计算:

τreq=τff+Kp(qcmd−q)+Kd(q˙cmd−q˙),τapplied=clip⁡(τreq).\tau_{\mathrm{req}} =\tau_{\mathrm{ff}} +K_p(q_{\mathrm{cmd}}-q) +K_d(\dot q_{\mathrm{cmd}}-\dot q), \qquad \tau_{\mathrm{applied}}=\operatorname{clip}(\tau_{\mathrm{req}}).

这里 effort 是转动关节的力矩前馈,单位为 N·m;position 是 rad。接口让不同算法共享关节层边界,但算法输出的物理意义仍必须一致。原有四旋翼控制笔记中的 RotorCommand 则表示旋翼推力,单位为 N,不能直接装进关节力矩向量。

2. 选择一个明确的补偿入口

本轮反馈控制与跨轮学习补偿配合,上一轮误差经过学习器生成下一轮前馈

补偿入口 改动什么 学习对象和适用条件
位置参考修正 q_cmd = q_nominal + f_q 输入到输出的模型包含原有位置伺服;本专题的教学实验采用此方式
力矩前馈修正 effort = tau_nominal + f_tau 需要力矩通道和相应动力学;f_tau 的单位是 N·m
任务空间参考修正 修改 TCP 参考后转换到关节参考 需要任务空间误差测量、运动学转换与可达性处理

“把误差加回控制输入”不是一个完整的实现说明。例如末端位置误差是毫米,关节参考是弧度,力矩是牛米;中间的学习算子负责单位和动态映射。直接把同一组浮点数加进三个入口,会产生完全不同的系统。

用位置修正时,要同时考虑速度参考的一致性。若接口要求位置、速度构成同一条可微轨迹,应从平滑后的完整修正曲线计算导数:

qcmd(t)=qd(t)+fq,k(t),q˙cmd(t)=q˙d(t)+f˙q,k(t).q_{\mathrm{cmd}}(t)=q_d(t)+f_{q,k}(t),\qquad \dot q_{\mathrm{cmd}}(t)=\dot q_d(t)+\dot f_{q,k}(t).

不能一边改位置,一边默认速度字段总能沿用旧值。另一种设计可以有意保持速度前馈不变,但识别的闭环输入输出模型必须与这种实现一致。

一个未来适配层的职责可以写成下面的伪代码。它用于讲清数据流,不是已实现、可编译的 C++ 类:

每个控制周期:
nominal = base_controller.update(state, time)
command = copy(nominal) # 不修改基础控制器内部保存的 const 返回值
correction = frozen_trial.sample(time.now - trial_start)
command.position += correction.position
command.velocity += correction.velocity
经过既有的关节限位和力矩限制后执行
记录:原目标、修正量、实测状态、请求命令、实际命令、时间戳

完整一轮结束后:
校验日志完整性与起始条件
对齐参考和测量
离线计算候选补偿并做约束检查
下一轮开始前,发布完整且固定的新版本

这与综述中“反馈控制加学习前馈”的并联结构相呼应。学习器要使用从其补偿入口到误差测量输出的有效闭环模型;只拿裸电机模型代替已经包含 PD、滤波和延迟的系统,会使学习方向或相位失配。Bristow 等,2006 综述

3. 单独管理每一轮的生命周期

本轮的实时控制与轮间学习有不同的任务。建议让 TrialManager 管理流程,基础 controller 保持原有 reset/update 接口。

状态 本阶段做什么 进入下一阶段的条件
准备 载入轨迹、控制参数、已有补偿,核对实验标识 通道、单位、版本与长度一致
回到起点 基础反馈控制把机器人带回指定初态并等待稳定 位置、速度误差满足针对该平台设定的容差
执行与记录 冻结补偿版本,完成有限时间轨迹 日志完整、无中断且没有触发停止条件
评估与更新 计算误差和候选学习曲线 候选通过幅值、变化率和性能检查
冻结验证 固定补偿,多次独立执行以评价结果 形成准确度、重复性与异常情况报告

一次途中停止的轨迹,不能不加区分地当成完整的一轮加入学习。请求命令与实际执行命令也应分开记录:如果驱动端已经饱和,输入不再是线性模型假设的那条曲线。此时需要拒绝该轮更新、缩小任务范围,或显式采用带约束的学习方法。

这里的“回到起点”包含速度与柔性状态。关节角回到了同一个值,末端仍可能在摆动。因此柔性实验中应有足够停稳时间;对无法测量的内部状态,也要通过等待和重复检查控制其影响。初态能够变化的 AILC 方法有额外设计条件,不能把它理解为任意初态都无关紧要。Shu 等,2024

仓库现有 ControlLatch 的停止状态由执行宿主管理。设计学习器时,算法 reset() 应只清理算法状态,不应隐含解除执行宿主的停止状态。参数更新、清空学习数据、重新允许运动,是三个独立操作。

4. 一份补偿曲线应该附带什么信息

设想昨天学好的圆轨迹,今天把工件换重一倍。数组长度与关节数都没有改变,旧补偿却可能已经不合适。因此除了保存浮点数组,还应保存能够识别任务条件的元数据:

机器人/模型版本、关节名称与顺序、关节单位
补偿入口:position_reference 或 effort_feedforward
轨迹内容标识、执行时长、采样时刻、插值方式
工具、负载、基础控制参数与限幅配置
学习算法及参数、生成版本、使用的测量通道
初始状态要求、验证指标、最后一次通过验证的记录

使用文件内容摘要识别轨迹,比只存文件名可靠:名为 circle.csv 的文件可能已经被修改。速度也应显式记录。同一条几何圆在 2 秒和 8 秒内完成,速度、加速度和动态滞后都不同,按原采样索引回放补偿未必适用。

时间对齐同样关键。记录中至少要能恢复“目标生成时间、命令执行时间、测量采集时间”的关系,估计固定延迟并检测抖动。将晚到的测量误认为当前采样点,会把正确的补偿学到错误的位置。对齐后再重采样到学习网格,不能仅依赖两份数组恰好长度相等。

泛化能力需要验证:共用的是学习组件的接口和算法实现,不是把一台机械臂的补偿文件原样用于另一台机器人。四足周期步态还涉及接触模式与相位;无人机重复运动涉及姿态、推力和不同的输出量,都需要各自的任务与执行器适配。

5. 怎样证明学到了需要的精度

先选清楚测量输出。只使用编码器时,可评价关节跟踪,但不能凭此证明物理 TCP 的绝对位置准确。末端挠曲、工具安装偏差和几何参数误差,可能无法从电机侧编码器直接观察到。FANUC LVC 的公开资料使用加速度计观察振动;2024 年 M20iA 研究使用外部 C-Track 780 测量末端位姿,两者的测量目标不同。

一个可执行的评价顺序是:

  1. **固定实验条件。**记录机器人、轨迹、速度、负载和测量方法,先多次执行未学习的基线。
  2. **观察学习过程。**每轮分别记录 RMS、最大误差、命令幅值和饱和比例。误差降低但命令剧增,需要解释其代价。
  3. **冻结补偿后重复运行。**将学习过程结束后的固定控制方案重复多次,计算平均误差及跨轮离散程度。学习中不断变化的轨迹不能直接当作固定方案的重复性样本。
  4. **分开报告停稳段。**柔性末端应同时记录运动段跟踪误差、停稳段残振和稳定时间;全程 RMS 可能掩盖末端残振。
  5. **改变一个条件。**例如只改变负载或初始状态,重新评价旧补偿,确定它的适用范围。

任务空间还应区分两种误差:同一时刻的参考与实测之差是时间参数化的跟踪误差;实测点到目标几何路径的最短距离是轮廓误差。机器人沿圆跑得慢半拍,可能路径形状仍然接近圆,但时间跟踪误差明显。因此本专题的画圆图必须结合关节或同时间末端误差一起读。

准确度、重复性、校准与测量坐标的背景可继续读 NIST 机器人校准与配准报告。不要把一次仿真的下降曲线换算成真机重复定位精度的保证。

6. 复现实验,再考虑接入

数值模型、数据生成与测试放在 docker/iterative-learning-lab/,静态网页读取 source/js/iterative-learning/experiments.json。在仓库根目录运行:

./bin/site.sh ilc-test
./bin/site.sh ilc-generate
./bin/site.sh build
./bin/site.sh serve 4096

如果本机没有可用的 Node 24 容器镜像,先执行 ./bin/site.sh ilc-build 构建专用环境。数值实验在 Docker 内执行,网页的配置菜单选择已经计算好的数据。切换一项配置会从该实验的第 0 轮重新查看,不会对本机机器人发送命令。

当前完成范围与复现细节以该目录的 README.md、TODO.md 为准。把教学模型换成真实 controller 前,需建立相应的输入输出模型和日志适配;完成受控仿真中的限幅、中断、延迟和负载变化验证后,才具备评估硬件接入的基础。

7. 三个设计练习

**练习一:**关节顺序从 [shoulder, elbow] 换成 [elbow, shoulder],数组长度仍为 2,可以直接读旧补偿吗?

**答案:**不可以。必须按关节名称重排所有相关数据,并验证单位和模型;单靠向量维度检查发现不了这个错误。

**练习二:**学习期间每轮最大误差都下降了,能否据此宣布重复定位精度提升?

**答案:**不能。先冻结学习结果,在相同条件下重复执行,测量固定方案的落点或轨迹离散程度。学习下降曲线回答的是不同控制方案逐轮改善的问题。

**练习三:**把学到的 0.02 rad 位置修正直接加到 effort 字段,为什么不正确?

**答案:**位置和力矩单位不同,输入到误差的动态映射也不同。若选择力矩前馈入口,需要重建该入口的学习模型,并以 N·m 为补偿单位。

8. 接着读

知识共享许可协议