1. 自抗扰控制(ADRC)是什么?能解决什么问题?
第一次接触自抗扰控制(ADRC)是在2015年,当时我在一个钢铁厂的热轧生产线改造项目中遇到了大麻烦——传统的PID控制器在应对轧辊间隙的时变负载时,调节效果总是不理想。车间主任天天追着问:"这系统怎么老是抖?能不能调稳点?"直到我尝试了ADRC方案,问题才迎刃而解。
ADRC本质上是一种不依赖精确数学模型的控制方法,特别适合工业自动化中常见的三类难题:
- 时变负载:像传送带载重突然变化的情况
- 非线性系统:比如机械臂关节的摩擦死区
- 复合扰动:同时存在内部参数漂移和外部干扰的场景
它的核心思想很巧妙:把系统所有不确定因素(包括模型误差和外部干扰)打包成一个"总扰动",然后实时观测并补偿。这就好比开车时突然遇到侧风,老司机不会死握方向盘,而是感知风力后顺势微调。ADRC中的扩张状态观测器(ESO)就是这套"感知-补偿"机制的数学实现。
2. ADRC的三大核心算法解析
2.1 跟踪微分器(TD):给控制指令"踩刹车"
在PLC编程中直接给阶跃信号会让电机剧烈抖动,就像突然把油门踩到底。TD的作用就是生成平滑过渡信号,我常用离散形式实现:
# Python伪代码示例(实际PLC用ST语言) def TD(v0, h, r): v1 = 0 # 跟踪速度 v2 = 0 # 跟踪加速度 while True: fh = fhan(v1-v0, v2, r, h) v1 += h * v2 v2 += h * fh yield v1 def fhan(x1, x2, r, h): d = r * h**2 a0 = h * x2 y = x1 + a0 a1 = sqrt(d*(d+8*abs(y))) a2 = a0 + sign(y)*(a1-d)/2 sy = (sign(y+d)-sign(y-d))/2 return -r*(a2/d - sign(a2))*sy - r*sign(a2)关键参数经验:
- 速度因子r:相当于"刹车力度",一般取5-20,太大易超调
- 滤波因子h:建议设为控制周期的1.5-2倍
2.2 扩张状态观测器(ESO):系统的"CT扫描仪"
ESO是ADRC最精髓的部分,它能同时观测系统状态和总扰动。在嵌入式C代码中我这样实现二阶ESO:
// 基于STM32的ESO实现片段 void ESO_Update(float y, float u, float h) { float e = z1 - y; // 观测误差 z1 += h * (z2 - beta01 * e); z2 += h * (z3 + b0*u - beta02*fal(e,0.5,delta)); z3 += h * (-beta03*fal(e,0.25,delta)); } float fal(float e, float alpha, float delta) { if(fabs(e) > delta) return pow(fabs(e), alpha) * sign(e); else return e / pow(delta, 1-alpha); }调试技巧:
- beta系列参数:按"带宽法"设置,比如beta01=3w, beta02=3w², beta03=w³
- 非线性函数fal:delta取0.01-0.1,增强抗噪能力
2.3 非线性误差反馈(NLSEF):智能调节器
传统PID的线性组合在非线性系统会失灵,NLSEF通过非线性函数实现:
- 小误差时高增益——快速调节
- 大误差时低增益——避免超调
在伺服电机控制中,我的参数对照表如下:
| 误差范围 | 增益策略 | 实际效果 |
|---|---|---|
| <5% | 指数型增强(α=1.2) | 消除静差 |
| 5%-15% | 线性区(kp=2.5) | 平稳调节 |
| >15% | 饱和限制 | 防止执行器过冲 |
3. 工业场景实战案例
3.1 注塑机压力控制
某注塑机在保压阶段要求压力波动<0.5MPa,但熔胶粘度变化导致PID参数难以整定。采用ADRC后:
- TD设计:r=15,h=0.01s,平滑设定值跳变
- ESO配置:带宽w=50rad/s,b0=0.8
- NLSEF参数:α=0.7, δ=0.05
实测结果对比:
| 指标 | PID控制 | ADRC控制 |
|---|---|---|
| 超调量 | 12% | 3% |
| 调节时间(s) | 1.2 | 0.6 |
| 抗原料扰动 | 需重新整定 | 自适应 |
3.2 机器人关节摩擦补偿
六轴机械臂的关节静态摩擦会导致0.5°的定位误差。通过ESO观测摩擦扰动:
// 欧姆龙PLC ST语言实现 ESO_Joint( IN := ActualPosition, U := TorqueCmd, H := 0.002, Beta1 := 300, Beta2 := 30000, Beta3 := 1000000, OUT => EstimatedFriction );补偿后重复定位精度从±0.3mm提升到±0.1mm,特别适合精密装配场景。
4. 实现中的常见坑与解决方案
4.1 采样周期选择不当
曾有个项目因把h设为0.1s导致系统震荡,后来总结出经验公式:
h ≤ 1/(10×系统带宽)
比如带宽50Hz的系统,h至少≤2ms。在Codesys平台要注意任务周期配置:
<!-- 错误的周期设置 --> <Task Name="ADRC_Task" Interval="100000"/> <!-- 100ms --> <!-- 正确的周期设置 --> <Task Name="ADRC_Task" Interval="2000"/> <!-- 2ms -->4.2 ESO初始值问题
观测器启动时若z1-z3初始值为0,会导致初期补偿异常。我的处理方法是:
- 上电后先静置100ms
- 用前10个采样值的均值初始化z1
- z2/z3保持为0
4.3 执行器饱和处理
在液压系统调试中发现,当ESO输出超过油缸压力极限时会产生积分饱和。改进方案:
// 抗饱和ESO修改 if(fabs(z3) > Z3_MAX){ z3 = sign(z3) * Z3_MAX; beta03 *= 0.7; // 临时降低观测增益 }5. 不同平台的优化技巧
5.1 嵌入式端(STM32)
- 定点数优化:将ESO的β参数放大1000倍用整数运算
- 查表法:预先计算fal函数值存入Flash
- DMA加速:用DMA搬运ADC采样数据
5.2 PLC平台(西门子SCL)
// 优化后的TD实现 FUNCTION_BLOCK FB_ADRC_TD VAR_INPUT v0 : REAL; h : REAL := 0.001; r : REAL := 10.0; END_VAR VAR_OUTPUT v1 : REAL; v2 : REAL; END_VAR VAR d,a0,a1,a2 : REAL; END_VAR d := r * h * h; a0 := h * v2; v1 := v1 + a0; a1 := SQRT(d*(d + 8*ABS(v1 - v0))); a2 := a0 + SIGN(v1 - v0)*(a1 - d)/2; v2 := v2 + h * (-r*(a2/d - SIGN(a2))*(SIGN(v1 - v0 + d) - SIGN(v1 - v0 - d))/2 - r*SIGN(a2));5.3 工控机(C#实现)
// 多线程安全版本 class ADRC_ThreadSafe { private object _lock = new object(); public void Update(double y, double u) { lock(_lock) { double e = z1 - y; z1 += h * (z2 - beta01 * e); z2 += h * (z3 + b0*u - beta02*Fal(e,0.5,delta)); z3 += h * (-beta03*Fal(e,0.25,delta)); } } }