ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

自抗扰控制ADRC的MATLAB仿真实现与参数整定实战指南

自抗扰控制ADRC的MATLAB仿真实现与参数整定实战指南 简介面向自动控制、航空航天、电力系统等领域研究人员的ADRC-MATLAB仿真压缩包以便在MATLAB环境中快速上手自抗扰控制算法完成控制器设计与系统扰动响应验证。资源共5个文件以m脚本为主包含主程序、ADRC控制器函数及系统状态转移与输出函数另有一个asv辅助文件压缩包整体仅2KB代码结构精简便于逐模块研读。当前已有320人浏览学习。通过对照具实现读者可理解扩展状态观测器ESO的构建逻辑弄清fst、fal等关键函数在系统建模和扰动估计中的用途并能在仿真结果基础上分析不同扰动力下的响应曲线通过调整观测器增益和时间常数优化稳态精度与动态性能为后续在实际工程中部署ADRC打下扎实基础。 拿到这个adrc_matlab仿真.rar的时候我第一反应是这多半是某个朋友刚接触自抗扰控制ADRC从网上下了一堆资料但被里面五花八门的m文件和simulink模型搞得有点晕。说实话ADRC这个算法名字这几年在控制圈里出镜率非常高从电机调速到无人机姿态控制再到温度系统、电网逆变器几乎哪儿都有它的影子但真正能把它在MATLAB里跑通、跑顺、跑出效果的人并没有想象中那么多。这个压缩包对我来说其实是个很典型的“配齐了工具但没配齐思路”的案例——仿真文件都有缺的是把这些文件串起来的逻辑。我花了一个周末把里面常见的内容整理了一遍顺着“为什么用ADRC→到底怎么搭模型→参数怎么调→遇到问题怎么查”这条路重新走了一遍今天把这套东西完整分享出来。如果你正在做ADRC的MATLAB仿真不管是课程作业、毕业设计还是工程项目预研这篇文章能帮你省下大量摸索时间。1. 先说结论ADRC为什么能火起来1.1 从PID的痛点说起在聊ADRC之前得先搞清楚它解决了什么。PID控制器统治工业现场几十年结构简单、参数直观、鲁棒性好但有一件事它始终处理得不够好——耦合和扰动。当一个系统存在强耦合、大惯性、参数时变或者外部扰动很剧烈的时候PID往往会陷入“调好超调就大、压了超调响应就慢”的尴尬局面。根源在于PID的误差信号是“输出目标之差”它没有真正估计出系统里正在发生的扰动只是被动地等误差出来了再去纠正。我举个生活化的例子你开车下长坡PID的做法是看到车速快了才踩刹车看到车速慢了才松刹车永远在追着速度跑。而ADRC的做法是先预估一下坡度大概有多大主动提前补偿一个刹车力剩下的偏差再用反馈去修正。这就是“自抗扰”三个字的含义——把系统的内部动态和外部扰动统一当成“总扰动”实时估计出来再去抵消它。1.2 ADRC到底做了什么改动ADRC的完整结构包含三部分跟踪微分器TD、扩张状态观测器ESO、非线性状态误差反馈NLSEF。TD负责给输入信号安排一个平滑的过渡过程解决“快速性”和“超调”这对矛盾ESO是整个算法的灵魂它把一个高阶系统里的未建模动态和外部扰动扩张成一个新的状态量并且实时观测出来NLSEF则用非线性组合的方式把控制量算出来其中最核心的就是那个“扰动补偿项”。很多初学者面对这三块内容会懵觉得公式多、概念抽象。其实你在MATLAB里跑起来就会发现它本质上就是一个“状态观测器反馈控制”的框架ESO在里面的角色很像一个“内部仪表”实时告诉你系统当前受到了多大的额外推力或阻力。理解了这个前提后续所有的参数调试才能找到方向这就是为什么我强烈建议先读懂框架再动手改仿真。2. 仿真文件包拿到手先搞清楚里面装的是什么2.1 rar里通常都有哪些东西解压之后最常见的文件类型无非这么几类xxx.m脚本——这是纯代码实现的ADRC适合学习算法细节xxx.slx模型——这是Simulink图形化搭建的适合做系统级仿真或半实物验证还有一类是数据文件比如.mat里面存着调试好的参数或者仿真结果方便你直接对比。文件类型内容特征适合的人群.m 脚本离散化TD/ESO代码、被控对象模型想彻底搞懂算法细节的人.slx 模型Simulink模块封装、Scope观测输出做系统集成、搭复杂模型的人.mat 数据调好的参数、仿真结果需要快速验证效果的人我个人的建议是如果你是初学者先别急着打开slx拖模块先找一个结构清晰的m文件看一遍。因为Simulink里一个封装好的ADRC子模块你要是不知道内部连线反而不容易看出ESO的反馈路径到底是怎么走的而m文件里的每一步矩阵运算都是明明白白的。2.2 仿真环境和版本问题ADRC仿真对MATLAB版本没有硬性要求R2016a到R2023b都行只要不是太老的版本都能正常跑。真正影响你复现成功率的是脚本里调用到的工具箱。有些仿真里封装了sim函数、bodeplot、linmod这些需要Control System Toolbox和Simulink基础模块库纯算法部分用到的只是ode45、plot这些最通用的函数所以即使你的MATLAB装得比较精简跑通ADRC主程序也没问题。我遇到过一个比较头疼的情况有次用的脚本里写的是sim(adrc_sys)这种旧式语法在老版本上能跑换到新版会报警告提示用sim(adrc_sys.slx)或者Simulink.SimulationInput对象。如果你打开rar里的代码发现运行报错先看一眼报错是不是这种版本兼容问题别一上来就怀疑算法本身。3. 核心细节ADRC在MATLAB里的实现路径3.1 三种实现方式按需选择MATLAB里落地ADRC基本有三条路第一条是纯m脚本实现把TD、ESO、控制律写成离散迭代的形式配上被控对象模型一起算第二条是用Simulink搭模型ADRC部分用MATLAB Function模块或S-Function封装被控对象用传递函数或状态空间模块表示第三条是用S-Function写一个完整的ADRC控制器再接入Simulink环境适合参数需要在线调整的场景。纯m脚本的方式我最推荐给新手因为代码的每一行都对应一个公式比如离散化的ESO写出来就是这样e z1 - y; z1 z1 h*(z2 - beta01*e); z2 z2 h*(z3 - beta02*fal(e,0.5,0.01) b0*u); z3 z3 h*(-beta03*fal(e,0.25,0.01));这几行代码看起来简单但它体现了ESO的全部精髓先算观测误差e再用误差修正状态预测值z1、z2最后修正扩张状态z3z3就是观测出来的“总扰动”。你仔细盯着这段代码看十分钟会比看十页论文都管用。3.2 带宽法参数整定最实用的落地策略ADRC参数多如果不讲方法直接乱试很容易调几天都调不出发散。这里我强烈推荐带宽法这是高志强教授提出的一套简化整定思路核心就是“手动指定控制器带宽wc然后让观测器带宽wo取3~5倍wc”。举个例子二阶系统的ADRC控制器里把反馈控制律写成u0 kp*(v1-z1) kd*(v2-z2)带宽法直接给出kp wc^2; kd 2*wc; beta01 3*wo; beta02 3*wo^2; beta03 wo^3; b0 1; % 由对象模型估计这套公式把六个参数压缩成两个wc和wo工程上非常好用。你先把wc设成和系统自然频率差不多的数值然后把wo拉到3~5倍一般就能得到一个稳定可用的起点。整个过程我后面实测的时候会再次验证这个逻辑。3.3 fal函数到底有什么用ADRC里那个长得有点怪的非线性函数fal(e,α,δ)是整个算法的一个特别之处。它的作用是“小误差大增益、大误差小增益”这种非线性特性让ESO在误差较小时还能保持高增益来精确观测误差大时又不会让修正量爆炸。function f fal(e, alpha, delta) if abs(e) delta f abs(e)^alpha * sign(e); else f e / delta^(1-alpha); end end很多人在改参数时习惯把α设成1那fal就退化成线性函数了这时候ESO就变成了线性扩张状态观测器LESO。工程上为了调试方便很多人直接用LESO因为线性参数整定规律更成熟而保留非线性fal的形式往往是为了手动微调性能。这个选择没有绝对对错你先跑通线性版本再决定要不要加非线性。4. 实操过程从零搭一个二阶对象ADRC闭环4.1 被控对象与被控目标我这次实操用一个很经典也不失代表性的二阶对象G(s) 1/(s^2 2s 1)也就是自然频率1、阻尼比1的临界阻尼系统。这个对象本身不算难控但我要在这个基础上叠加一个外部扰动d(t) 0.5*sin(2t)并让系统输出跟踪一个阶跃信号r(t) 1。这样的设定同时检验了ADRC的跟踪能力和抗扰能力。4.2 完整代码结构和运行过程先写主程序把对象、ADRC控制器、离散化逻辑都放在一个脚本里方便整体调试%% 主脚本二阶对象ADRC仿真 clear; clc; close all; % 仿真参数 h 0.001; % 采样周期 T 10; % 仿真时长 N T / h; % 仿真步数 % 对象参数G(s) 1/(s^2 2*s 1) A1 0; A2 1; % 状态初始化 y 0; dy 0; % ADRC带宽参数 wc 8; % 控制器带宽 wo 5 * wc; % 观测器带宽 b0 1; % 控制增益估计值对象模型里b1这里用1 % ESO参数 beta01 3*wo; beta02 3*wo^2; beta03 wo^3; kp wc^2; kd 2*wc; % TD参数 r 10; v1 0; v2 0; % 数据存储 t_log zeros(1,N); y_log zeros(1,N); u_log zeros(1,N); z3_log zeros(1,N); d_log zeros(1,N); % 状态变量 z1 0; z2 0; z3 0; u 0; for k 1:N t (k-1)*h; % 参考输入 ref 1; % 外部扰动 d 0.5 * sin(2*t); % 跟踪微分器安排过渡过程 % v1跟踪refv2是v1的导数 e_td v1 - ref; v1 v1 h * (-r * fal(e_td, 0.5, 0.01)); v2 -r * fal(e_td, 0.5, 0.01); % 被控对象二阶系统 扰动 % x1 y, x2 dy ddy -2*dy - 1*y b0*u d; dy dy h * ddy; y y h * dy; % 扩张状态观测器 e_eso z1 - y; z1 z1 h * (z2 - beta01 * e_eso); z2 z2 h * (z3 - beta02 * fal(e_eso, 0.5, 0.01) b0*u); z3 z3 h * (-beta03 * fal(e_eso, 0.25, 0.01)); % 控制律PD 扰动补偿 u0 kp*(v1 - z1) kd*(0 - z2); u u0 - z3 / b0; % 记录数据 t_log(k) t; y_log(k) y; u_log(k) u; z3_log(k) z3; d_log(k) d; end % 绘图 figure(1); subplot(2,1,1); plot(t_log, y_log, b-, LineWidth, 1.5); hold on; plot(t_log, ones(1,N), r--, LineWidth, 1); ylim([0 1.5]); xlabel(时间/s); ylabel(输出); legend(y(t), 参考值); title(ADRC阶跃响应); subplot(2,1,2); plot(t_log, u_log, g-, LineWidth, 1.5); xlabel(时间/s); ylabel(控制量); title(控制量输出);这个脚本跑完你会看到输出在很短时间内平滑上升到1附近并且稳态时基本无超调、无静差。而在2~10秒内外部正弦扰动一直在加但输出曲线几乎观察不到波动这就是ESO实时补偿扰动带来的效果。4.3 关键环节为什么b0要尽量估准脚本里有一个参数b0它是对象“控制增益”的估计值。在二阶系统里b0 1是准确的因为这个对象模型里直接带有系数1。但实际工程里我们拿到的模型多半不精确比如实际对象是G(s)2.5/(s^23s1)你却用b01去设计控制器这时候ESO在补偿的时候就会把“控制增益误差”也当成扰动的一部分去估计和补偿所以系统可能还能工作但观测压力会增加。我实测过b0比真值大或者小两三倍的时候ADRC依然能稳住系统只是响应速度会变慢这恰恰体现了ADRC的鲁棒性。但你要是把b0和真值差出十倍以上ESO就很难快速收敛系统容易震荡甚至发散。所以工程上“b0估个大概准”就行别指望模型精确到小数点这是ADRC的容错性带来的好处。4.4 过渡过程TD的作用观察把代码里r从10改成1再跑一次你能明显看到输出的上升变得平缓但跟踪速度会变慢把r改成100输出会非常快地逼近阶跃值但相应地控制量也会在初期冲得更高。这就是TD在起作用——它给参考输入造了一个可调节斜率的“过渡路径”让你能直观地权衡“快”和“稳”。我在做电机系统仿真时特别喜欢用这个特性机械系统往往不希望加速度冲击太大你把r调小一点启动电流自然就平缓了相当于在控制器之外多了一个“软启动”功能。这个思路很多教科书没有明说但实际工程非常实用。5. 常见问题与排查技巧实录5.1 仿真发散或者NaN问题这是最让人头疼的。我见过很多人在群里求助“为什么我的ADRC仿真直接爆了”截图里曲线飞到天文数字。原因集中在几个地方采样周期h设得太大ESO离散化的数值稳定性被破坏wo设得太高观测器增益过大对噪声极端放大还有就是符号错误反馈路径上z3的补偿方向写反了。排查建议第一步把h调到0.0001试试如果问题解决说明就是采样时间的问题再逐步放大找到临界值第二步把wo降下来用3*wc的保守配置跑一遍第三步如果还炸检查u u0 - z3/b0这一行看看是不是符号反了写成z3/b0。这三个排查动作能覆盖九成以上的发散场景。5.2 跟踪慢或滞后明显如果你看到输出最终能到1但上升过程磨磨蹭蹭半天才跟上参考值多半是wc设小了。控制器带宽wc决定了闭环响应的快慢你可以先按对象的自然频率的2~3倍起步逐步往上加。同时检查一下u0是不是只用了比例项没用微分项如果kd0很多ADRC实现仍然能工作但动态表现会明显变肉。另一个容易被忽略的原因是ESO收敛速度不够——z1还没追上y控制器就已经在按错误的反馈计算了。这种时候优先增大wo并且确认beta02按wo^2量级设置不要手动乱改系数。5.3 输出存在明显震荡或抖振震荡的来源通常不是参数太大而是非线性函数fal里的δ取值太小。δ是线性区间的宽度如果取0.0001这种很小的值在误差跨过零点时增益会非常陡峭容易激起高频抖振。建议把δ设在0.01 ~ 0.05之间控制效果会更平滑。还有一种情况是Simulink模型里存在代数环。假如你搭建的模型把u直接用于计算u本身没有经过任何延迟模块仿真就会频繁报“Algebraic loop”或者卡死。这时候可以在反馈路径上插入一个Memory模块或者把采样时间设成离散来打破代数环。5.4 常见问题速查表现象可能原因解决方案曲线直接飞到NaNh太大、wo太高、补偿符号反了h降到0.0001、wo降到3wc、检查u0正负跟踪慢、上升迟缓wc太小、kd为0增大wc到2~3倍自然频率、确认kd2wc稳态有静差ESO没有完全补偿总扰动增大wo或在ESO中确认z3更新正常输出高频抖振δ太小、控制量大δ调到0.01~0.05给控制量加饱和限制Simulink有代数环反馈路径无延迟模块插入Memory模块或改用离散采样时间低版本代码报错sim语法版本兼容改成sim(model.slx)或Simulink.SimulationInput6. 我的一些补充建议和工程体会如果这次仿真你跑通了我强烈建议你再做两件事第一把wc和wo单独写成一个调参界面或者脚本循环批量跑几组参数画出对比曲线你会对“带宽决定快慢”这句话的理解完全不一样第二把被控对象换成自己课题里的实际模型哪怕是非线性系统比如永磁同步电机的dq轴方程ADRC的ESO照样能工作只是b0要重新估计z3对应的扰动模型更复杂而已。有人问过我ADRC和它的那些“后代”——比如线性自抗扰LADRC、级联扩展状态观测器CESO——之间到底选哪个好。我的建议很简单先从线性ADRC入门把ESO的原理理解透再回头去读非线性形式你会发现底层逻辑完全一样只是换了一套“增益调度”的策略。MATLAB仿真是验证这套逻辑最便宜的手段比搭实物平台快得多。还有一个很多人忽视的细节ADRC的采样时间h要实际落地时和硬件匹配。仿真里你设0.001很随意但如果MCU的中断周期是1ms那没问题如果你仿真里跑的是0.0001而实际控制周期只能到0.01那参数就得重新整定。建议在仿真阶段就把采样周期定得和硬件一致省得后面移植时又要推倒重调。最后说一下adrc_matlab仿真.rar这类压缩包的使用习惯。rar解压本身很简单无论是WinRAR还是7-Zip选“解压到当前文件夹”就行。如果遇到rar文件损坏或者提示密码多半是网上下载的包不完整或者被二次打包不用在这个问题上耗太多时间直接找原始来源重新下载。最关键的还是解压之后不要拿着文件就跑先按这篇文章的思路理清结构、看懂代码、跑通基准参数再去做自己的改动。ADRC这个方向入门门槛看着高实际上真正卡人的往往不是公式本身而是“不知道从哪一步开始调试”。希望这篇基于实操的经验能帮你把第一脚踹开后面的路就会顺很多了。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进