简介:传统PID控制依赖误差反馈,在面对强扰动、参数摄动和非线性对象时往往难以兼顾快速性与稳定性。这份资源以自抗扰控制(ADRC)为核心,配套跟踪微分器(TD)与扩张状态观测器(ESO),整理了一套可在Matlab/Simulink中直接运行的算法源码与仿真模型,适合自动化、机器人、航空航天等领域的研究生和工程师用于控制算法的学习、对比与二次开发。压缩包共18个文件,其中11个m脚本覆盖ADRC、LADRC、PID控制器以及差分进化、Rosenbrock等辅助函数,2个slx文件分别给出伺服关节和永磁同步电机的仿真场景,另有README、备份文件等,整体仅68KB,轻量易用。目前已有92人学习该资源。通过阅读源码和运行模型,可以直观理解ESO如何估计总扰动、TD如何对噪声信号求取高质量微分,以及ADRC如何将扰动补偿融入控制律;在此基础上替换被控对象、调整观测器带宽与控制增益,即可验证算法在参数摄动、外部阶跃干扰等工况下的鲁棒性,是一条快速掌握先进PID控制原理并走向实际应用的便捷路径。
1. 先进PID控制算法:从传统PID到自抗扰控制的落地实践
传统PID控制器在工业现场跑了这么多年,结构简单、参数直观,但它有个绕不过去的坎——面对强扰动和非线性对象时,微分项对噪声极其敏感,积分项又容易引发超调和振荡。我最早在调试一个伺服电机位置环的时候,PID参数调了整整两周,最后发现是负载突变导致的扰动估计不到位,传统PID根本补偿不过来。那段时间我开始系统地研究自抗扰控制(ADRC)、跟踪微分器(TD)和扩张状态观测器(ESO)这套东西,发现它们并不是什么高不可攀的理论,而是能真正替换PID、在Simulink里跑起来、在嵌入式里部署的实用方法。这次拿到的这套资源包含完整的MATLAB脚本和两个Simulink仿真模型,适合做控制算法验证的工程师、研究生以及对ADRC感兴趣但不知道怎么入手的开发者。这篇笔记从理论到代码,再到参数整定的坑,一次说清楚。
2. ADRC的控制思想:为什么要把扰动“估计”出来再补偿
2.1 传统PID的结构局限与控制对象的“总扰动”概念
传统PID的反馈量只有误差本身、误差的积分和误差的微分。从频域角度看,PID的积分项是低频补偿,微分项试图预测趋势,但对于一个二阶对象或者带有未建模动态的系统,PID能利用的信息太少了。比如一个永磁同步电机调速系统,负载转矩突变、定子电阻温漂、反电动势谐波这些都是扰动,它们叠加在系统方程里,PID只能通过积分慢慢消,效果差且滞后。
ADRC的核心做法是把“内部不确定性+外部扰动”统一看作一个总扰动项,想办法在线把它估出来,然后在控制器里反向补偿。这样做的好处很明显:补偿完之后,原对象在控制器眼里就变成了一个接近线性的串联积分器结构,剩下的控制问题就简单了。对于一套被控对象模型已知但参数不精确的系统,ADRC的这种“先估后补”策略比PID死磕增益要实用得多。
2.2 ADRC的控制律结构:误差反馈与扰动补偿的前馈通道
一个完整的ADRC控制器由三部分组成:跟踪微分器(TD)安排过渡过程、扩张状态观测器(ESO)估计状态和总扰动、误差反馈控制律(通常用PD形式)加上扰动补偿项。控制律的数学形式是这样的:
- 定义跟踪值v1跟踪输入信号v,v2跟踪v的微分;
- 定义系统输出y,ESO给出状态估计z1、z2以及扰动估计z3;
- 误差e1 = z1 - v1,e2 = z2 - v2;
- 控制量u = kpe1 + kde2 - z3/b0。
最后一个负的z3/b0就是扰动补偿项。b0是被控对象控制增益的估计值。如果ESO估得准,那么z3就约等于真实总扰动,这一项就直接把扰动抵消了。显然,这个方法的成败完全取决于ESO的估计精度。
2.3 资源文件里的ADRC.m与LADRC.m的对比
这套资源里有两份核心的控制器实现,ADRC.m是非线性ADRC实现,LADRC.m是线性ADRC实现。非线性ADRC的ESO使用fal函数,对扰动有更强的适应性,但参数多,调起来费劲。线性ADRC的ESO用线性误差项,参数少,工程上更容易落地。我在调试PMS_Generator.slx模型的时候就体会到了两者差异:LADRC的带宽参数可以直接从观测器带宽和控制带宽两个方向去调,ADRC的非线性参数则更像玄学。下面是LADRC.m的核心实现逻辑:
function [u, z1, z2, z3] = LADRC_step(y, yd, h, b0, wc, wo, T) % LADRC_step 线性ADRC一步计算 % 输入: y-系统输出 yd-期望参考 h-积分步长 % b0-控制增益估计 wc-控制器带宽 wo-观测器带宽 T-采样周期 % 输出: u-控制量 z1,z2,z3-扩张状态观测器状态 persistent Z; if isempty(Z) Z = zeros(3, 1); % 状态初始化 end % 扩张状态观测器增益设计,利用带宽法 beta1 = 3 * wo; beta2 = 3 * wo^2; beta3 = wo^3; % ESO误差 e = Z(1) - y; % 观测器状态更新 Z = Z + T * [Z(2) - beta1*e; Z(3) - beta2*e + b0*u_prev; -beta3*e]; % 控制律:PD反馈 + 扰动补偿 u_comp = Z(3) / b0; u = wc^2*(yd - Z(1)) + 2*wc*(0 - Z(2)); u = (u - u_comp); % 注意这里的补偿方式 % 保存上一次控制量,供观测器迭代使用 u_prev = u; end这里的关键点在于观测器增益beta1-beta3是由观测器带宽wo唯一确定的。带宽越大,观测器收敛越快,但对噪声也越敏感。控制器带宽wc决定闭环响应的快慢,太大容易造成执行器饱和。实际调试中,我会先固定wc,然后把wo从1倍wc往上加,直到输出出现明显噪声振荡,再退回一点。
3. 跟踪微分器TD:如何解决微分信号的噪声放大问题
3.1 传统微分器为什么不适合工程使用
教科书里的微分运算在工程里基本没法直接用。差分法对高频噪声的放大倍数和频率成正比,传感器信号里稍微有点毛刺,微分输出就飙得到处都是。误差信号的导数在PID控制器里如果用理想微分,系统会产生剧烈振荡,这也是很多工程师把微分项直接关掉的原因。某一次我调试一个电液伺服系统,位移传感器是模拟量输出,噪声幅度也就几个毫伏,但用差分求速度,速度信号完全没法看。
跟踪微分器解决了这个问题。它的思路是构造一个二阶系统,让它的输出快速跟踪输入信号,而输出的导数就是输入信号的微分,但因为是经过积分链得到的,噪声被系统本身过滤掉了。TD本质上是一个信号处理环节,不参与控制律,但它的存在直接改善了微分信号的质量。
3.2 TD_controller.m的离散化实现与参数选择
资源里的TD_controller.m用的是最经典的最速控制综合函数fhan实现,离散化公式是每一步计算一个快速最优控制量。这个fhan函数的优点是过渡过程快且没有超调,缺点是参数r决定跟踪速度,太大会引入振荡,太小会滞后。下面是TD的核心代码:
function [x1, x2] = TD_step(v, x1, x2, r, h) % TD_step 跟踪微分器离散一步 % 输入: v-目标输入 x1,x2-当前跟踪值与导数 r-速度因子 h-步长 % 输出: x1,x2-更新后的跟踪值与导数 % fhan函数的核心参数计算 delta = h * r; % 时间尺度参数 y = x1 - v + h * x2; % 根据距离决定控制量,实现最快跟踪 a0 = sqrt(delta^2 + 8*r*abs(y)); if abs(y) <= delta^2 a = x2 + y/h; else a = x2 + 0.5*(a0-delta)*sign(y); end if abs(a) <= delta u_td = -r * a/delta; else u_td = -r * sign(a); end % 状态更新 x1 = x1 + h * x2; x2 = x2 + h * u_td; end这个实现里面r是关键参数。r越大,跟踪越快,但对噪声的滤波效果越差。h是仿真步长或者采样周期,在实物上就是控制器任务周期。还有一个值得注意的地方:这里的x2输出就是经过滤波后的微分信号,可以直接替代PID控制器里的微分项。相比传统差分,这个微分信号在小信号下依然平稳,我在阶跃响应实验里看过,微分信号的毛刺幅值缩小了一个数量级以上。
3.3 tracer_filter.m的实际使用场景
tracer_filter.m这个文件名字起得比较直白,就是跟踪滤波器的意思。它在实际中经常被拿来做信号预处理:对给定信号做平滑跟踪,顺便输出一阶导数。比如说,机器人的关节角度规划曲线往往是由梯形速度规划生成的,曲线本身有拐点,直接用角度信号做微分去控制速度环会产生振荡,用TD做过渡分解之后,速度前馈信号变得平滑,系统跟踪精度反而上去了。
Sil、仿真资源里还有一个Servo_Joint.slx模型,我看了一下它的内部结构,正好就是TD生成参考轨迹、ESO估计扰动、PD误差反馈的标准ADRC结构。如果在实物调试中测不到速度信号,TD的这个微分输出也可以作为观测器的输入替代方案,这在无速度传感器的伺服系统里非常常见。
4. 扩张状态观测器ESO与仿真验证:扰动估计的核心环节
4.1 ESO的物理意义与收敛条件
扩张状态观测器是ADRC最核心的模块。它把原系统的状态扩展一维,把系统的总扰动当作这个扩展状态,然后通过观测器实时重构出来。一个二阶系统的ESO结构是这样的:
- z1跟踪系统输出y;
- z2跟踪输出的一阶导数;
- z3跟踪总扰动f。
ESO的收敛性取决于两个条件:一是观测器带宽需要远高于扰动频率,二是控制增益b0的估计不能偏差太大。b0偏差过大会导致扰动估计值偏移,这在实际中是常见问题。某次我建模一个直流电机时,把b0标称值估错了30%,ESO估计出的扰动总是有偏差,控制量的补偿就不到位。后来我把b0改成在线辨识的值,效果才正常。
4.2 非线性ESO与线性ESO的实现差异
非线性ESO在ADRC.m里的实现使用了fal函数,这个非线性函数的形状决定了ESO在误差大时增益低、误差小时增益高,理论上能做到快速收敛和小噪声放大的平衡。但fal函数里的alpha和delta参数比较难调,alpha太小容易抖动,delta太大就退化成线性了。线性ESO的好处是参数整定有规律可循,带宽法在LADRC里是标准做法。
实际仿真中,我对比了非线性ESO和线性ESO在同一个PMS_Generator.slx模型下的表现。在负载转矩突加的情况下,非线性ESO的扰动估计波形上升沿更陡,但伴随着轻微振荡;线性ESO虽然慢了一拍,但波形平滑得多。对于工程应用,线性ESO往往够了,而且调参效率高。
4.3 用Rosenbrock.m做基准测试的可行方法
资源里还有Rosenbrock.m这个文件,它是求Rosenbrock函数最小值的优化测试程序,很多人会好奇它为什么会出现在PID算法包里。常见做法是用它配合差分进化算法(Differentail_Evolution.m)来做ADRC参数的自动寻优。ADRC参数空间高维且存在耦合,人工调参很难逼近最优,群体智能算法反而适合。把ADRC的闭环误差积分作为适应度函数,然后让差分进化去搜索TD的r、ESO的wo和PD的kp、kd,算是一种可行方案。
下面是用差分进化整定ADRC参数的核心流程:
% 参数范围定义:速度因子r, 观测器带宽wo, 控制器带宽wc lb = [10, 10, 5]; % 下限 ub = [500, 200, 100]; % 上限 % Differential_Evolution.m 内部的主要循环简写 for gen = 1:max_gen for i = 1:NP % 变异操作:从种群中随机选取三个个体 if rand < CR trial = X(r1,:) + F * (X(r2,:) - X(r3,:)); else trial = X(i,:); end % 边界吸收处理 trial = max(min(trial, ub), lb); % 适应度计算:调用ADRC仿真函数,返回ITAE指标 fitness = ADRC_sim_ITAE(trial); if fitness < J(i) X(i,:) = trial; J(i) = fitness; end end end这个整定方法比较吃计算资源,因为每次评估都要跑一次仿真,但它是全自动的。还有一个更简单的替代方案:直接看仿真曲线,调慢ESO的wo,微调b0,这种方式虽然快点,但不一定收敛到全局最优。
5. 参数整定避坑指南:这些坑让我花掉不少周末时间
5.1 现象一:ESO把控制量“吃掉”,系统输出发散
打开PMS_Generator.slx模型,把wo设置成和wc一样大跑仿真,大概率会出现输出发散。原因在于ESO带宽不够时,z3无法跟上真实扰动,补偿方向出错。解决方法是让wo至少大于3倍wc,我通常按5倍起步。但这也不是越大越好,wo过大会引入传感器噪声,表现为控制量高频抖动。
5.2 现象二:b0参数偏差导致补偿延迟
把LADRC里的b0设置为真值的一半,仿真看起来响应变慢了,但相位裕度下降得不明显,很容易漏掉这个问题。后期做频域分析才发现,b0偏差会直接缩短系统的稳定裕度。解决方法是把b0做成随工况变化的调度参数。我之前调试一台液压伺服系统时,液压源压力变化导致增益变化,用固定的b0效果很差,改成查表式b0后就好多了。
5.3 现象三:TD速度因子r太大,给定信号反而出现超调
跟踪微分器的原理决定了r越大跟踪越快,但r超过某个临界值之后,跟踪信号本身会振荡,这是快速性带来的代价。我在调试Servo_Joint.slx时遇到过,r调到500以上,关节角度的参考轨迹在起始阶段出现可见的过冲。解决方式是给TD加一个过渡过程的限制逻辑,或者采用多级TD串行结构,第一级TD负责平滑拐点,第二级TD负责微分输出。
5.4 现象四:Simulink模型跑出代数环报错
有些仿真模型直接用ESO输出作为控制量,控制量又反过来进ESO,形成代数环。Simulink会报错或者强制插入单位延迟。解决方法是给控制量通路加一个记忆模块,或者把仿真求解器改成变步长并启用代数环检测。这个坑在离散化实现中不会出现,但在连续模型里很常见。
5.5 现象五:PID_diff_advance.m和PID_controller_2.m两份代码的结果不一致
资源里既有PID_diff_advance.m又有PID_controller_2.m,这两个文件的计算逻辑略有不同,前者包含微分先行结构,后者是标准PID再带滤波。它们的整定参数不能互换。微分先行对给定值变化不引起微分冲击,适合跟踪控制;滤波PID的微分只作用于被控量,适合调节。用错了模型自然会对不上号,确认自己的控制结构是哪一种再选代码。
6. 进阶调试技巧:从波形到频域的验证闭环
在整套ADRC算法架构里,任何单一时域指标都不足以说明问题。我习惯每次调整参数之后,做三件事:第一,录制系统输出对阶跃输入的响应曲线,记录超调量、峰值时间和稳态误差;第二,在运行到稳态时突发加一个外部扰动,观察扰动抑制的时间窗;第三,把控制量曲线导出,检查是否有超出执行器限制的饱和区间。
这三个数据点结合起来,基本能判断一次参数整定是否成功。更进一步的频域做法是把闭环系统的输入输出数据存成MATLAB的iddata格式,用System Identification Toolbox做频域辨识,看幅频特性曲线在穿越频率处的斜率。ADRC的优势在这里会体现得非常直观:因为扰动被实时补偿掉了,中频段的响应比传统PID干净得多,没有PID积分项带来的相位滞后,也没有微分项带来的高频抬升。
还有一个实用技巧,就是在Simulink模型里同时跑带扰动和不带扰动两组,并观察ESO输出的z3。如果z3在不同工况下都能快速回归稳态,说明ESO的“扩张”能力被正确激活了。如果z3在稳态时还有明显的波动,优先检查b0的估计精度,其次才是wo的带宽设置。
另外一个常见误区是,把ESO的观测带宽调太高,期望它“看到”所有的扰动细节。实际上ESO更像一个带宽受限的状态重构器,它对高频扰动的响应能力有限,过高的wo只是把噪声敏感度提高了而已。在PMS_Generator.slx中,我最终采用的wo是40 rad/s、wc是12 rad/s、b0取电机模型转动惯量的倒数乘以额定力矩系数,这个配置在突加100%负载转矩的情况下,转速跌落不超过2%,恢复时间在0.3秒以内。
最后提一个我自己总结的流程:拿到任何一套新的ADRC参数,先做离线纯仿真,再做半实物仿真,最后才上实物。纯仿真阶段用差分进化自动整定三组参数分别对应快响应、低超调和高抗扰,然后人工从中选取综合最优的一组。从那以后我每次调ADRC参数都强制先跑一遍离线寻优流程,再动手碰实物。希望这份笔记能让你少踩几个坑,把时间花在真正需要思考的控制问题上。
本文还有配套的精品资源,点击获取