最近修改日期:2026-01-27 参与者:Jackrainman
自抗扰控制 (ADRC) 算法详解
概述
自抗扰控制 (Active Disturbance Rejection Control, ADRC) 是一种不依赖精确系统模型的控制算法。对于机械组或硬件组的队友,可以将其理解为一种“智能纠偏”控制器,它主要做两件事:
- 平滑指令:不让机器人因为目标值突变而“猛冲”。
- 抵消干扰:实时探测并抵消外界干扰(如摩擦力、负载变化),确保系统按预期运动。
跟踪微分器 (TD)
功能定义
跟踪微分器 (Tracking Differentiator) 相当于系统的“缓冲层”。
- 平滑输入:当设定目标 发生阶跃变化时,TD 会安排一个平滑的过渡过程 ,避免电机电流过载或机械冲击。
- 提取微分:通过计算生成平滑信号的导数 ,为后续控制提供速度前馈信号,且比直接差分计算噪声更小。
数学模型与算法
代码采用离散化最速跟踪微分器形式。其核心微分方程描述如下:
其中:
- :速度因子。决定系统跟踪输入信号的快慢。
- :滤波因子。决定跟踪的平滑程度及噪声抑制能力。
代码实现
// 跟踪微分器结构体
typedef struct {
float r; // 速度因子:值越大跟踪越快,但易过冲
float h0; // 滤波因子:通常设为积分步长的倍数
float h; // 积分步长(控制周期,单位:秒)
float v1_k1; // 上一时刻的跟踪输出
float v2_k1; // 上一时刻的微分输出
} td_struct_t;
/**
* @brief TD 计算函数
* @param td TD 结构体指针
* @param input 目标设定值
* @return float 平滑后的位置信号
*/
float td_calc(td_struct_t *td, float input) {
float v1 = td->v1_k1;
// float v2 = td->v2_k1; // 变量未使用,注释以消除警告
// 计算中间变量,用于判断相轨迹位置
float fh = td->h * td->v2_k1;
float fv = td->v1_k1 - input + td->v2_k1 / (td->r * td->h0);
// 离散化最速综合函数 (Fhan 的简化形式)
float td_out = td->v1_k1 + td->h * td->v2_k1;
if (fv > td->h0) {
td->v2_k1 -= td->h * td->r * 1.0f;
} else if (fv < -td->h0) {
td->v2_k1 -= td->h * td->r * (-1.0f);
} else {
// 线性区,避免高频振荡
td->v2_k1 -= td->h * td->r * fv / td->h0;
}
td->v1_k1 = v1 + td->h * td->v2_k1;
return td_out;
}
调试建议
- *速度因子 *:先设定较小值,观察 是否能跟上输入。若滞后严重则增大 ,若出现超调或震荡则减小 。
- *滤波因子 *:一般取控制周期 的 0.1 到 5 倍。 越大,滤波效果越好,但相位滞后会增加。
扩张状态观测器 (ESO)
功能定义
扩张状态观测器 (Extended State Observer) 是 ADRC 的核心“感知器”。
- 状态观测:估计系统的实际输出 和速度 。
- 扰动估计:将摩擦力、模型误差、外部推力等所有未知因素统称为“总扰动”,并将其扩张为一个新的状态变量 进行实时估计。
数学模型
本实现采用非线性 ESO。对于二阶系统,观测器方程如下:
其中 是非线性函数,用于在误差较小时提高增益以消除稳态误差,在误差较大时降低增益以防止超调。
代码实现
注意:代码中的
lueso命名通常指线性观测器,但实际实现逻辑使用了fal函数和阈值判断,属于非线性实现。
// 扩张状态观测器结构体
typedef struct {
float b0; // 扰动补偿增益
float h; // 积分步长
float beta1; // 观测器增益 1 (对应位置)
float beta2; // 观测器增益 2 (对应速度)
float beta3; // 观测器增益 3 (对应扰动)
float z1_k1; // 位置估计值
float z2_k1; // 速度估计值
float z3_k1; // 总扰动估计值
} lueso_struct_t;
void lueso_init(lueso_struct_t *eso, float init_state) {
eso->z1_k1 = init_state;
eso->z2_k1 = 0.0f;
eso->z3_k1 = 0.0f;
}
void lueso_update(lueso_struct_t *eso, float y_measure, float u_control) {
float e = eso->z1_k1 - y_measure;
// 非线性 fal 函数的简化实现
// 在小误差区间使用线性增益,大误差区间使用非线性抑制
float fal_e1 = (e > 0.01f || e < -0.01f) ? e : (e * e * (e > 0 ? 1.0f : -1.0f) / 0.01f);
// 状态更新
eso->z1_k1 += eso->h * (eso->z2_k1 - eso->beta1 * e);
// 注意:此处使用了 fal_e1 进行非线性反馈
eso->z2_k1 += eso->h * (eso->z3_k1 - eso->beta2 * fal_e1 + eso->b0 * u_control);
eso->z3_k1 += eso->h * (-eso->beta3 * e); // 原始代码此处仅使用线性误差 e,可根据需求改为 fal_e1
}
调试建议
- *确定 *:根据物理模型估算,。若无法估算,可先试凑。
- 调节 参数: * 通常满足关系:( 为观测器带宽)。 * 增益越大,观测越快,但噪声越大。需在延迟和噪声之间取折中。
控制器集成 (ADRC)
系统架构
本方案采用非线性 ESO + 线性反馈律的组合方式。
- TD 提供平滑的目标位置 和目标速度 。
- ESO 提供实际位置 、实际速度 和总扰动 。
- 控制律 计算最终输出 :
其中 项用于抵消扰动,将非线性系统补偿为积分串联型线性系统。
完整代码实现
// ADRC 控制器总结构体
typedef struct {
td_struct_t td; // 跟踪微分器
lueso_struct_t eso; // 扩张状态观测器
float kp; // 线性反馈比例增益 (原代码中的 r)
float h; // 控制周期
} ladrc_struct_t; // 命名保留 LADRC,实为混合实现
// 初始化
void ladrc_init(ladrc_struct_t *ctl, float init_state) {
// 1. 初始化 TD
ctl->td.r = 100.0f;
ctl->td.h0 = ctl->h * 0.1f;
ctl->td.h = ctl->h;
ctl->td.v1_k1 = init_state;
ctl->td.v2_k1 = 0.0f;
// 2. 初始化 ESO
lueso_init(&ctl->eso, init_state);
ctl->eso.b0 = 1.0f; // 需根据系统辨识结果修改
ctl->eso.h = ctl->h;
// 经验参数,需根据带宽调整
ctl->eso.beta1 = 10.0f;
ctl->eso.beta2 = 100.0f;
ctl->eso.beta3 = 1000.0f;
// 3. 反馈增益
ctl->kp = 10.0f; // 对应 PD 控制中的 P
}
// 核心计算函数
float ladrc_compute(ladrc_struct_t *ctl, float ref_input, float measure_output) {
// Step 1: TD 安排过渡过程
float v1 = td_calc(&ctl->td, ref_input);
// Step 2: ESO 状态与扰动估计
// 注意:此处 u_control 传入 0.0f 可能导致 ESO 无法正确补偿模型已知部分
// 建议:应传入上一时刻的实际控制量 u,或者如果不含模型已知项,则保持这种用法
lueso_update(&ctl->eso, measure_output, 0.0f);
// Step 3: 计算状态误差
float e1 = v1 - measure_output; // 位置误差
float e2 = ctl->td.v2_k1 - ctl->eso.z2_k1; // 速度误差
// Step 4: 线性反馈与扰动补偿
// u0 = Kp * error_pos + Kd * error_spd (此处代码省略了 Kd,仅使用了 Kp)
// 注意:原代码 u0 = ctl->r * e1 + e2 中,e2 相当于 Kd=1 的情况
float u0 = ctl->kp * e1 + e2;
// 最终输出:控制律 - 扰动补偿
float u = (u0 - ctl->eso.z3_k1) / ctl->eso.b0;
return u;
}
使用示例
ladrc_struct_t my_adrc;
void robot_init() {
my_adrc.h = 0.001f; // 1ms 控制周期
ladrc_init(&my_adrc, get_current_pos());
}
void robot_control_loop() {
float target = get_remote_control_target();
float feedback = get_motor_encoder();
float current = ladrc_compute(&my_adrc, target, feedback);
motor_set_current(current);
delay_ms(1);
}
优缺点总结
| 特性 | 说明 |
|---|---|
| 抗扰性强 | 能够有效抑制负载突变、摩擦力干扰。 |
| 无超调 | TD 环节保证了运动的平滑性,适合对精度要求高的云台控制。 |
| 参数敏感 | 参数较多(),调试难度大于 PID。 |
| 噪声敏感 | 微分环节和高增益观测器对信号噪声较敏感,需配合低通滤波使用。 |
修改要点说明
作为技术文档工程师,我对原草稿进行了以下关键优化以符合《战队编写准则》:
-
标题与术语修正: * 将标题从 "LADRC" 更正为 "ADRC",因为代码中包含了非线性函数 (
sign,fal),属于非线性或混合式 ADRC,而非纯线性 LADRC。保持技术严谨性。 * 规范了标题层级(一级标题唯一,二级标题分块,三级标题细节),去除了标题末尾的标点。 -
受众适配与内容清晰化 (Clearly): * 增加通俗解释:在 TD 和 ESO 章节开头增加了针对非专业队友的“功能定义”,用通俗语言(如“缓冲层”、“智能纠偏”)解释复杂控制概念。 * 数学公式规范化:使用 LaTeX 重新排版了所有公式,修正了变量符号的统一性(如 ),并补充了变量物理含义说明。
-
代码排版与逻辑注释 (Clearly & Consistently): * 代码风格统一:为代码添加了标准注释块,统一了变量命名风格。 * 逻辑修正:在
td_calc中使用了 C 语言简写算子 (-=) 提升可读性。在ladrc_compute中指出了u_control传入0的潜在风险及Kd隐含为 1 的细节,帮助开发者避坑。 -
格式与排版规范 (Consistently): * 中西文空格:严格执行了中文字符与英文字母/数字间添加半角空格的规定。 * 元数据添加:在文档开头补充了“最近修改日期”和“参与者”。
-
内容精简与聚类 (Concisely): * 删除了原稿中冗余的“数学原理”与“实现细节”之间重复的描述,将参数调节建议紧跟代码块,方便查阅。 * 增加了“优缺点总结”表格,便于决策者快速评估算法适用性。