简介:这份资源是一套基于自抗扰控制(ADRC)理论编写的MATLAB程序,参考韩京清教授的自抗扰理论,面向自动化、车辆控制方向的学习者与工程人员,用于理解并复现汽车引擎控制中的抗扰与自适应策略。压缩包共1个文件,为m脚本类型,体积约563B,核心围绕扩展状态观测器(ESO)的实现,可在线估计系统状态与未知扰动,为控制器提供补偿依据。资源已有1932人学习下载,说明其在ADRC入门与工程对照中具有一定参考价值。读者可从中获得一套可运行的仿真验证案例,对照理解ESO设计、扰动估计与补偿的基本流程,并体会如何将非模型依赖的控制思路落到汽车引擎这类实际对象上,适合作为理论学习向工程实践过渡的参考脚本。
1. 从一条“仿真跑通了”的曲线说起
去年帮一个做底盘电控的朋友看模型,他丢过来一句“ADRC 仿真成功了”,我打开一看,跟踪曲线确实漂亮,超调几乎为零,但把扰动幅值调大一档,输出立刻抖成锯齿。问题不在算法,而在他把 TD(跟踪微分器)的滤波因子当成了万能旋钮。这件事让我意识到,很多标着“仿真成功”的自抗扰程序包,真正值钱的不是那条好看的曲线,而是里面那套能让你改参数、换对象、复现失败的结构。这份 adrc 自抗扰程序包就是围绕 MATLAB/Simulink 环境整理的一套可运行实现,覆盖 TD、ESO、NLSEF 三个核心环节,配套汽车纵向/横向控制的被控对象示例。它适合两类人:一类是刚接触自抗扰、想找个能跑起来的骨架照着改的;另一类是做过 PID、想看看 ESO 到底怎么把“未知扰动”估出来再补回去的。下面我按自己拆包的习惯,把结构、参数、坑和验证方法一层层摊开。
2. 拆开程序包:TD、ESO、NLSEF 三段式到底怎么落地
自抗扰的核心不是某一个公式,而是“安排过渡过程 + 估计并补偿总扰动”这条链路。程序包把这条链路拆成三个可独立调试的模块,这个拆法对复现非常友好,因为你可以先单独验证 TD 的微分质量,再单独看 ESO 的估计收敛,最后才闭环。很多人一上来就整闭环,曲线一抖就不知道是哪一段的问题,血泪经验是:分段验证永远比整体调参快。
2.1 跟踪微分器 TD:把阶跃变成“能跟得上的斜坡”
TD 的作用是给参考输入安排一个过渡过程,同时给出质量较好的微分信号。程序包里常见的是离散最速控制型 TD,形式如下:
% 离散 TD:输入 v(k),输出 x1(跟踪v)、x2(近似dv) function [x1, x2] = td_discrete(v, x1_prev, x2_prev, r, h, T) % r : 速度因子,越大跟踪越快,但微分噪声越大 % h : 滤波因子,通常取仿真步长 T 的 3~10 倍 % T : 采样周期 x1 = x1_prev + T * x2_prev; fh = fhan(x1_prev - v, x2_prev, r, h); % 最速控制综合函数 x2 = x2_prev + T * fh; end function f = 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; a = (a0 + y - a2) * sy + a2; sa = (sign(a + d) - sign(a - d)) / 2; f = -r * (a / d - sign(a)) * sa - r * sign(a); end这段代码里r和h是两个必须理解的参数。r决定过渡过程快慢,调大跟踪更紧,但x2的噪声会被放大;h是滤波因子,它和采样周期T不是一回事,常见错误是把h直接设成T,结果微分信号毛刺明显。我一般先把h设成5T左右,再根据x2的平滑程度微调。fhan是最速控制综合函数,里面的sign组合是为了在离散条件下避免高频抖振,这也是为什么 TD 比直接差分更适合做微分——直接差分对噪声毫无抵抗力。
2.2 扩张状态观测器 ESO:把“说不清的总扰动”当成一个状态来估
ESO 是整个自抗扰里最值得花时间的一块。它的思路是把系统里所有未建模动态、参数摄动、外部扰动合并成一个“总扰动”状态,然后用观测器把它估出来。程序包里给的是三阶线性 ESO(对二阶对象),离散形式如下:
% 三阶线性 ESO:估计 z1(输出)、z2(输出导数)、z3(总扰动) function [z1, z2, z3] = eso_discrete(y, u, z1_prev, z2_prev, z3_prev, b0, beta, T) % y : 实测输出 % u : 控制量 % b0 : 控制增益估计值,符号和量级都要对 % beta : [beta1 beta2 beta3] 观测器增益 e = z1_prev - y; z1 = z1_prev + T * (z2_prev - beta(1) * e); z2 = z2_prev + T * (z3_prev - beta(2) * e + b0 * u); z3 = z3_prev + T * (-beta(3) * e); endb0是最容易被低估的参数。它不是随便填的,理想情况下应接近对象真实控制增益;填小了补偿不足,填大了闭环容易振荡。beta三个增益一般按带宽整定,常见做法是把观测器带宽wo设成闭环带宽的 3~5 倍,然后取beta1=3wo、beta2=3wo^2、beta3=wo^3。这个配置不是玄学,它对应三重极点配置,能让 ESO 在噪声和收敛速度之间取得平衡。程序包里如果给了b0的标定脚本,别跳过,先跑一遍确认符号——符号错了,补偿会变成正反馈,这是最典型的翻车点。
2.3 NLSEF 与扰动补偿:控制量最后怎么拼出来
非线性状态误差反馈(NLSEF)负责把 TD 给出的过渡过程和 ESO 给出的状态估计做误差反馈,再减去扰动补偿项。简化后的控制律常见形式是:
% 二阶对象 ADRC 控制量合成 function u = adrc_control(v, y, x1, x2, z1, z2, z3, b0, kp, kd) e1 = x1 - z1; % 过渡过程与观测输出的误差 e2 = x2 - z2; % 微分误差 u0 = kp * e1 + kd * e2; % 线性反馈(也可换成 fal 非线性函数) u = (u0 - z3) / b0; % 减去总扰动估计并除以控制增益 end这里u = (u0 - z3)/b0是自抗扰的灵魂:z3估出来的总扰动被前馈抵消,剩下的u0只需要对付一个“近似积分器”对象。参数上kp、kd可以按二阶系统s^2 + kd*s + kp的极点配置来给,比如期望带宽wc,取kp=wc^2、kd=2*wc。程序包里如果同时提供了fal非线性版本,建议先用线性版跑通,再换非线性,否则两个非线性环节叠在一起,出问题很难定位。
3. 在 MATLAB 里把闭环跑起来:从脚本到 Simulink 的两条路
拿到程序包后,我一般不建议直接点开 Simulink 模型就运行,而是先用脚本把单步逻辑跑通,确认每个函数的输入输出维度、符号和量级都对,再进模型。原因是 Simulink 里报错信息往往指向信号维度,而不是算法逻辑,新手很容易被带偏。
3.1 纯脚本闭环:最小可复现例子
下面是一个二阶对象的脚本闭环骨架,对象用y'' = -a*y' + b*u + d近似,d是外部扰动:
T = 0.001; % 采样周期 N = 5000; % 仿真步数 b0 = 1.2; % 控制增益估计 wo = 60; % ESO 带宽 beta = [3*wo, 3*wo^2, wo^3]; wc = 15; % 闭环带宽 kp = wc^2; kd = 2*wc; x1 = 0; x2 = 0; % TD 状态 z1 = 0; z2 = 0; z3 = 0; % ESO 状态 y = 0; yd = 0; % 对象输出及其导数 ref = 1; % 阶跃参考 for k = 1:N [x1, x2] = td_discrete(ref, x1, x2, 200, 5*T, T); u = adrc_control(ref, y, x1, x2, z1, z2, z3, b0, kp, kd); % 对象积分(欧拉法,仅示意) d = 0.3 * sin(2*pi*0.5*k*T); % 外部扰动 ydd = -2*yd + b0*u + d; yd = yd + T * ydd; y = y + T * yd; [z1, z2, z3] = eso_discrete(y, u, z1, z2, z3, b0, beta, T); end这段脚本的价值在于它把“参考—TD—控制—对象—ESO”这条回路完整走了一遍,每一步都能单独打印。调试时我习惯把x1、z1、z3和y画在同一张图上:如果z1跟不住y,先查beta和b0;如果z3在扰动加入前就大幅偏离零,多半是b0符号或量级不对。注意对象积分用的是欧拉法,只适合验证逻辑,真要评估控制性能,步长要更小或换ode45。
3.2 Simulink 模型:模块对应关系与信号维度
程序包里的 Simulink 模型通常把 TD、ESO、控制律各封装成一个 MATLAB Function 模块。进模型后先做三件事:确认所有模块的采样时间一致(别一个0.001一个-1混用)、确认b0和beta是通过Simulink.Parameter还是直接写死、确认扰动源是Step还是Sine Wave并记录其幅值。信号维度上,ESO 输出是三路,接控制律时顺序别接反,z1对输出、z2对导数、z3对扰动,接反了曲线会“看起来能跑但补偿方向错”,这种错误比直接报错更难查。
| 模块 | 输入 | 输出 | 关键参数 |
|---|---|---|---|
| TD | 参考v | x1, x2 | r, h, T |
| ESO | y, u | z1, z2, z3 | b0, beta, T |
| 控制律 | x1,x2,z1,z2,z3 | u | kp, kd, b0 |
参数扫描时建议一次只动一个:先固定b0扫wo,看z3收敛速度;再固定wo扫wc,看跟踪超调。两个一起调,曲线变好你也不知道是谁的功劳,这是我在多个项目里反复验证过的低效做法。
4. 避坑与排查:那些“仿真成功”背后容易翻车的地方
4.1 现象:曲线一开始就发散,u迅速打到限幅
原因多半是b0符号与对象真实增益相反,扰动补偿变成正反馈。解决:先用开环给一个常值u,看y的变化方向,确认b0符号;再检查beta是否过大导致 ESO 高频抖振被放大。这个坑在汽车纵向控制里尤其常见,因为传动系增益在不同挡位下会变号。
4.2 现象:稳态无静差,但一加扰动就缓慢偏回来
原因是 ESO 带宽wo偏低,z3跟不上扰动变化。解决:把wo提到闭环带宽的 3~5 倍,同时观察z3是否出现高频毛刺;如果毛刺明显,说明采样周期或噪声水平不支持这么高的带宽,此时应降低wo并接受一定的恢复时间,而不是硬调。
4.3 现象:TD 的x2毛刺严重,控制量跟着抖
原因是滤波因子h取得太小,或r过大。解决:先把h提到5T~10T,再降r;如果对象本身噪声大,可以在 TD 前加一阶低通,但要注意低通会引入相位滞后,别和 ESO 的相位补偿打架。
4.4 现象:Simulink 能跑,生成代码后行为不一致
原因是模型里用了连续模块或变步长求解器,而生成代码按离散执行。解决:统一改成离散模块并显式指定采样时间,所有积分用离散累加替代,beta、b0用参数对象管理,避免生成时被优化掉。
4.5 现象:换一个被控对象后参数全废
原因是b0和带宽是按原对象整定的,自抗扰虽然“抗扰”,但不等于“免整定”。解决:保留 TD 和 ESO 结构,重新标定b0,再按新对象的惯量和延迟重设wc、wo。把整定过程写成脚本,换对象时重跑一遍,比手工试凑可靠得多。
5. 进阶验证:用“扰动估计误差”而不是跟踪曲线判断成败
很多人判断 ADRC 好不好,只看跟踪曲线超调小不小,这其实不够。真正能说明 ESO 在工作的指标是z3与真实总扰动的接近程度。做法是在仿真里把真实扰动d记录下来,和z3画在一起,看幅值和相位差。如果z3幅值明显偏小,说明wo不够或b0偏大;如果z3相位滞后严重,说明带宽受采样限制,此时跟踪曲线再漂亮,鲁棒性也存疑。
另一个我常用的验证是“参数摄动扫描”:把对象增益在标称值 ±30% 范围内扫一遍,记录超调量和调节时间,画成热力图。自抗扰的卖点是抗摄动,如果扫描下来某一侧明显恶化,说明b0标称值选偏了。下面是一个简化的扫描骨架:
gains = 0.7:0.1:1.3; % 对象增益摄动范围 os = zeros(size(gains)); for i = 1:numel(gains) % 用 gains(i) 缩放对象增益,跑一次闭环,记录超调 os(i) = run_once(gains(i)); % run_once 为封装好的仿真函数 end plot(gains, os); grid on;run_once里把对象增益乘上缩放系数,其余参数不动,这样能看出控制器对增益摄动的容忍边界。如果某侧超调突然抬升,优先回头查b0和wo,而不是去调kp、kd——反馈增益只能改动态,改不了扰动估计的质量。
最后说个习惯:从那以后我每次拿到标着“仿真成功”的自抗扰程序,都强制先做三件事——单独跑 TD 看微分、单独跑 ESO 看z3收敛、再做 ±30% 增益扫描。三步都过,才认为这套参数能拿去改对象。希望帮到你。
本文还有配套的精品资源,点击获取