资讯详情 二阶ADRC自抗扰控制:从原理、Simulink仿真到参数整定与部署
📅 2026/10/22 15:13:18
简介这是一份基于自抗扰控制ADRC理论的二阶系统仿真资源面向自动化、控制工程等专业的学生与工程师聚焦ADRC控制器设计、扩展状态观测器ESO搭建及系统频域特性分析。压缩包内共5个文件包含3个MATLAB脚本.m和2个Simulink模型.mdl与.slx分别实现二阶对象建模、ESO参数整定、阶跃响应仿真以及Bode图绘制覆盖从算法理解到仿真验证的完整链路。目前已有809人学习与下载。通过运行源码可直观比较加入ADRC前后系统响应的差异观察ESO对总扰动的实时估计效果并借助Bode图评估幅值裕度与相位裕度进而掌握控制器参数调整的关键方法。资源包大小仅41KB、轻量易用结构紧凑下载后即可在MATLAB环境中运行既适合课堂演示也便于科研与工程中快速复用与二次开发。1. ADRC不是玄学二阶自抗扰控制为什么能扛住模型不准做运动控制的人大多有过这种体验对象明明是个二阶系统手里拿着标称参数整定 PID跑得好好的结果换一台设备、惯量变了一点整套参数就废了又得从头调。自抗扰控制ADRC换了个思路它不跟模型较劲而是把模型失配、外部负载、未建模动态全归成总扰动用扩张状态观测器实时估出来再在控制律里直接补偿掉。二阶 ADRC 是这条路里最常用的起手版本结构不复杂Simulink 里半天能搭起来但真正把它调出效果绕不开带宽整定和几个隐蔽的坑。这篇笔记按原理 → 搭建 → 整定 → 避坑 → 上板的顺序把二阶 ADRC 从框图画到能跑出曲线再给出部署前必须处理的细节。2. 看懂ADRC结构图二阶自抗扰控制的三个模块与一份初始化脚本2.1 结构图拆开看TD、ESO、控制律各管一段二阶 ADRC 的结构图没有 PID 反馈回路那么绕核心是三段信号流参考信号先进跟踪微分器TD安排过渡过程对象输出送进扩张状态观测器ESO估计状态和总扰动最后控制律把两者的输出做差、补偿后生成控制量。对不熟悉的人来说第一次看 ADRC 图容易被三个模块吓到其实每个模块只干一件事。被控对象假设是二阶的y f b0*u。其中 f 代表总扰动包括外部负载、摩擦力、模型参数偏差b0 是控制输入增益的估计值不要求精确量级对就行。TD 的作用是把阶跃参考 r 变成一条平滑的过渡轨迹 v1、v2避免启动瞬间误差太大把控制量顶到饱和。ESO 是核心它用对象的输入 u 和输出 y把 y、y 以及总扰动 f 三个量一起估出来分别是 z1、z2、z3。控制律做两件事对 z1、z2 做 PD 组合再把 z3 的估计值除 b0 后前馈补偿掉。信号关系可以用下面这张表说明白搭 Simulink 时照着这张表连线就行信号来源去向含义v1TD控制律比较点过渡后的参考位置v2TD控制律可选过渡后的参考速度y被控对象输出ESO 输入含噪声的测量值u控制律输出对象与 ESO 输入控制量z1ESO控制律y 的估计值z2ESO控制律y 的估计值z3ESO控制律总扰动 f 的估计值控制律的表达式写成 u (kp*(v1-z1) - kd*z2 - z3) / b0。注意这里用 z3 直接除 b0 再减就是把扰动补偿折算成控制量的一部分。kp 和 kd 后面会按带宽法给出计算公式不需要像调 PID 那样独立试凑三个增益。2.2 ESO为什么能看见总扰动ESO 是这个结构的灵魂。把总扰动 f 当作第三个状态 x3对象的增广状态方程写成x1 x2x2 x3 b0*ux3 h其中 h 是扰动变化率有界ESO 用输出 y 的估计误差来校正三个状态估计z1 z2 β01*(y - z1)z2 z3 β02*(y - z1) b0*uz3 β03*(y - z1)只要 β01、β02、β03 取得合适z1 会收敛到 yz2 收敛到 yz3 收敛到总扰动 f。而且有个很关键的性质就算 b0 和真实增益 b 有偏差偏差项 (b - b0)*u 也会被 z3 一并吸收进总扰动里。这就是 ADRC 敢说不依赖精确模型的原因——模型不准的部分最终都会跑到 z3 里被观测器逮住并补偿掉。三个 β 系数不是随便试的工程上直接按观测器带宽 wo 配置β01 3woβ02 3wo²β03 wo³。这个配置把观测器误差方程的特征多项式配成了 (s wo)³wo 越大观测器跟踪越快但对测量噪声也越敏感。wo 和控制器带宽 wc 的经验关系是 wo 3~10*wc初始化时先按这个范围来。注意b0 的符号一旦填反闭环必炸调试时先查它。2.3 初始化脚本把参数一次注入Simulink我一般不在 Simulink 模块里手填参数抄错一个数排查半天。更稳的做法是写一个初始化脚本在仿真开始前把全部参数算好放到工作区模型里的参数全部用变量名引用。下面这份脚本是二阶 ADRC 的起点TD 参数先留好最小系统可以暂时不用 TD。% ADRC 二阶系统参数初始化 % 对象模型y f b0*uf 为总扰动 clear; clc; % ---- 被控对象 ---- b0 100; % 控制输入增益估计量级正确即可不需精确 % 真实对象若写成 b120偏差会被 z3 吸收这里故意留偏差 % ---- 控制器带宽 ---- wc 20; % 控制器带宽 rad/s决定闭环响应快慢 kp wc^2; % 比例增益 kd 2*wc; % 微分增益 % ---- 扩张状态观测器带宽 ---- wo 80; % ESO 带宽经验值取 3~10 倍 wc beta1 3*wo; % z1 通道校正增益 beta2 3*wo^2; % z2 通道校正增益 beta3 wo^3; % 扰动通道校正增益 % ---- TD 参数可选接入过渡过程时用---- r_td 10; % 过渡过程速度因子越大过渡越快 h_td 0.01; % TD 内部步长固定步长仿真时用参数说明wc 决定闭环能多快地跟随参考取 20 rad/s 时上升时间大约 0.1~0.2 秒量级适合大多数伺服对象试跑wo 取 wc 的 4 倍既能跟上扰动又不会把噪声放大得太难看。b0 的估计方法是给对象一个开环阶跃测量 y 的稳态值除以控制量幅值后面第 4 章会细说怎么标定。这份脚本存成 adrc_init.m每次打开模型后先运行它再点仿真参数改起来只动一个文件。3. 在Simulink里把ADRC仿真搭起来最小系统到S-Function封装3.1 最小系统四个模块连出主回路搭模型之前先说我的习惯第一版永远只搭对象 ESO 控制律三个部分不加 TD、不加扰动、不加噪声先确认闭环能在最简单条件下稳定。这样一旦发散嫌疑范围小得多。Simulink 模型 adrc_demo.slx 里被控对象用两个 Integrator 串联实现ESO 用 S-Function控制律用一个 Fcn 模块就能写干净。模块主要设置说明Integrator1初值 0输出 x2即速度Integrator2初值 0输出 x1即位置 ySum13 输入符号 - -合成控制律的三个误差项Gain11/b0对 u0 做增益折算Fcn见下方表达式实现 u0 kp*(r-z1) - kd*z2 - z3S-Functionadrc_eso输入 [y;u]输出 [z1;z2;z3]Step幅值 1起点 0参考输入控制律的 Fcn 表达式写成 kp*(u(1)-u(2)) - kd*u(3) - u(4)其中 u(1) 是参考 ru(2) 是 z1u(3) 是 z2u(4) 是 z3。这个表达式输出 u0后面接一个 Gain 模块增益 1/b0就得到了最终控制量 uu 一路反馈回对象另一路送给 ESO 作为第二个输入。注意 Fcn 模块的输入向量顺序要和连线顺序一致否则满屏报错。最小系统里我先不加 TD直接把 Step 的阶跃参考接到 Fcn 的 u(1)。这一步跑通了再考虑参考过渡的问题。对象部分的两个 Integrator 串联后x2 b0*u 是名义模型真实对象如果想要 b120就在 x2 的加法点加一个偏差项或者直接把 Gain 的增益改成真实 b让 b0 和 b 不同正好可以观察 ESO 的补偿能力。3.2 扰动注入让ESO有东西可估对象模型里 y f b0*u所以扰动 f 加在第二个 Integrator 的输入加法点上。推荐两种扰动用两个信号源叠加一个 Step 模拟外部负载突变比如电机突然带上负载一个正弦波模拟未建模动态比如机械谐振或者周期性阻力波动。我的做法是Step 幅值 1在 t3s 时跳变正弦波幅值 0.1频率 10 rad/s从 0 时刻就叠加进去。这样 z3 的收敛过程可以分成两段观察——正弦段看它能不能持续跟踪周期性扰动阶跃段看它对突变扰动的收敛速度。测量噪声也不能省用 Band-Limited White Noise 模块噪声功率先给 1e-6加在 y 的测量通路上。加了噪声再回来看 z3 曲线你会发现 wo 不能无限大噪声会通过 β03 通道直接泄漏进扰动估计里。这里有个常见误用很多人把扰动直接加到对象输入端 u 上这不对。加在 u 上等于改变控制增益加在 x2 上才是真正的负载扰动。3.3 S-Function封装ESO代码与参数填写ESO 在 Simulink 里可以用 Fcn 模块搭信号多了以后连线绕得头疼我直接用 M 文件 S-Function 封装。下面这份是 Level-1 写法新版 MATLAB 会提示推荐 Level-2但 Level-1 简洁直观不影响运行。function [sys,x0,str,ts] adrc_eso(t,x,u,flag,b0,beta1,beta2,beta3) % 二阶 ADRC 扩张状态观测器 (Level-1 M-file S-Function) % 输入 u(1)y, u(2)控制量 u输出 [z1; z2; z3] switch flag case 0 sizes simsizes; sizes.NumContStates 3; % 三个连续状态 sizes.NumDiscStates 0; sizes.NumOutputs 3; sizes.NumInputs 2; sizes.DirFeedthrough 1; % 微分方程用到 u(2)必须设为 1 sizes.NumSampleTimes 1; sys simsizes(sizes); x0 [0; 0; 0]; % 与对象初始状态保持一致 str []; ts [0 0]; % 连续模块离散化时改成 [h 0] case 1 e u(1) - x(1); sys(1) x(2) beta1 * e; sys(2) x(3) beta2 * e b0 * u(2); sys(3) beta3 * e; case 3 sys x; case {2,4,9} sys []; otherwise error([Unhandled flag ,num2str(flag)]); end参数说明S-Function 模块的 Parameters 框按顺序填 b0,beta1,beta2,beta3对应代码里的四个附加参数。DirFeedthrough 必须设 1因为 z2 的表达式里直接用到了当前拍的控制量 u(2)如果设成 0Simulink 会报代数环或者直接忽略输入。ts[0 0] 表示连续模块第 6 章做离散化时要把这里改成 [h 0] 并换成离散状态更新方程。x0 三个初值要和对象的 Integrator 初值一致否则仿真的前几拍会出现一段收敛过程看起来像超调其实是初值不一致造成的假象。连好线、运行初始化脚本、点仿真如果曲线没有发散最小系统就算通了。下一步才接入第 3.2 节的扰动和噪声。4. 二阶ADRC参数整定路线带宽法三步走与一张调参对照表4.1 第一步先把ESO的眼睛睁开单独校wo整定顺序不能反先让 ESO 把状态和扰动看清楚再调控制器才有意义。我习惯先把控制量固定成常数比如 u 0.5给对象加阶跃扰动用 Scope 同时看真实扰动 f 和 z3然后从 wo30 开始逐步加到 60、120观察 z3 的收敛速度和波形抖动。wo 太小的现象很明显阶跃扰动加上去以后z3 慢吞吞地爬要好几百毫秒才到位而且正弦扰动段的 z3 幅值明显偏小、相位滞后。wo 合适的表现是阶跃后 30~50ms 内 z3 跟上正弦段幅值基本吻合。wo 过大的表现也直观z3 曲线变毛噪声被放大严重时整个闭环发尖。这一步还有个附带收获对比 f 和 z3如果 z3 总是差一个固定偏差检查 b0 的量级偏差大说明 b0 估得偏了ESO 会连带把这一项当扰动吸收动态过程变慢。wo 最终取多少以能跟上扰动且噪声可接受为准不必照抄别人的数值。4.2 第二步wc决定响应快慢b0决定补偿力度ESO 校好后把控制量切回闭环开始调 wc。起始值按 wo/4 取比如 wo80 时 wc20。此时如果阶跃响应太慢、上升时间过长加大 wc如果超调大、控制量抖减小 wc。kp、kd 不要单独乱动直接从 kpwc²、kd2wc 算出这是线性 ADRC 收敛性分析的常用配置少两个自由度反而好调。b0 的影响更隐蔽。b0 偏小扰动补偿量 (z3/b0) 被放大闭环对扰动响应过猛容易震荡b0 偏大补偿力度不足扰动过来会先掉一截再由 ESO 慢慢拉回稳态没问题但动态偏差大。b0 的标定我推荐开环阶跃实验给 u 一个固定幅值等 y 稳定后b0_est y / u。拿不到 y 时就用速度稳态斜率反推。工程上保证 b0 估计值落在真实值的 0.5 倍到 2 倍之间就够了剩下的偏差 z3 会兜底。现象调整手段调整方向阶跃上升慢加大 wcwc 每次增加 30%~50%超调大减小 wc或检查 b0 是否偏小先改 b0 再看 wcz3 跟踪滞后加大 wowo 不超过 wc 的 10 倍z3 噪声大减小 wo同时检查测量噪声功率启动瞬间控制量饱和参考信号加过渡加 TD 或一阶惯性滤波4.3 第三步验收脚本超调量和扰动恢复时间一起算参数整定不是看起来稳了就完事。我会写一个验收脚本固定跑一遍阶跃加扰动的场景把超调量、扰动恢复时间打印出来。模型的 To Workspace 模块记下 y、u、z3、f 四个信号变量名按下面代码里的来。% 二阶 ADRC 仿真验收 out sim(adrc_demo, StopTime, 6); t out.tout; y out.y(:,1); % 被控输出按你的 To Workspace 变量名调整 u out.u(:,1); % 控制量 z3 out.z3(:,1); % ESO 扰动估计 f out.f(:,1); % 真实总扰动用于对比 % 指标 1阶跃超调量 yss y(end); [ym, km] max(y); overshoot (ym - yss) / yss * 100; fprintf(阶跃超调量: %.2f%%\n, overshoot); % 指标 2扰动恢复时间t3s 加入负载阶跃 idx_d find(t 3, 1); band 0.02 * abs(yss); idx_ok find(abs(y(idx_d:end) - yss) band, 1); if ~isempty(idx_ok) t_settle t(idx_d idx_ok - 1) - 3; fprintf(扰动恢复时间: %.3f s\n, t_settle); else fprintf(扰动恢复时间: 超过 3s需要增大 wo 或检查 b0\n); end % 指标 3z3 与真实扰动的稳态误差 err_z3 mean(abs(z3(end-500:end) - f(end-500:end))); fprintf(ESO 扰动估计稳态误差: %.4f\n, err_z3); % 画图 subplot(3,1,1); plot(t, y); title(输出 y); subplot(3,1,2); plot(t, u); title(控制量 u); subplot(3,1,3); plot(t, [z3 f]); title(z3 vs f); legend(z3,f);代码逻辑说明脚本先跑仿真再算三个量化指标。超调量用最大值和稳态值的差计算扰动恢复时间是扰动发生后输出第一次进入 2% 误差带并保持住的时间ESO 估计误差只取末尾 500 个点做平均避开过渡段。这三个指标定了整定参数就有客观标准而不是对着波形肉眼调参。5. 二阶ADRC仿真避坑指南五个高频翻车现场的现象、原因与排查下面五个坑是二阶 ADRC 仿真里出现频率最高的。一半来自我第一次接触 ADRC 时的翻车记录一半是同组同学反复踩过的每条按现象 → 原因 → 解决拆开讲。坑位 1仿真一开始就发散现象双击 Scope 看到输出瞬间冲到 1e5 甚至直接变 NaN仿真在某一步卡死报错。 原因最常见是求解器最大步长过大。阶跃突变瞬间ESO 的误差 e 被放大β03 通道的 wo³ 项对步长极敏感变步长求解器一步跨过去就数值爆炸。其次是 b0 符号填反等于把正反馈接进了闭环必然发散。 解决先固定求解器为 ode45最大步长设 1e-3把暴力数值问题排除再检查 b0 符号最后从较小的 wo比如 20起步慢慢加。坑位 2z3 总追不上扰动现象t3s 加阶跃负载后z3 爬得很慢稳态总是差一截正弦扰动段 z3 幅值明显偏小。 原因wo 太低观测器带宽低于扰动变化的速率或者 b0 估计偏差过大偏差项 (b-b0)*u 超出了 z3 的动态补偿范围。 解决wo 翻倍地加每次看 z3 的收敛时间把 b0 校准到真实值的 0.5~2 倍以内。wo 加到跟踪上了但波形变毛说明已经接近噪声极限此时反向微调。坑位 3启动瞬间控制量巨大尖峰现象阶跃参考给进去u(0) 打出几百执行机构明显饱和输出却冲过冲。 原因控制律里用的是 r 和 z1 的瞬态误差阶跃瞬间误差最大如果 b0 又估小了u(u0-z3)/b0 会把 u0 再放大好几倍。 解决参考信号后面加一阶惯性滤波 1/(T*s1)T 取 0.5~1s先让 r 变成斜坡同时把 b0 标定准别让它靠 z3 硬扛。这种做法比直接上 TD 更简单效果接近。坑位 4离散化后性能对不上现象连续域仿真曲线很漂亮换成固定步长离散模块或者上板后超调变大、控制量抖动严重的直接发散。 原因只离散了对象ESO 还按连续方式处理或者采样周期 h 和 wc 不匹配数字控制器的相位损失太大。连续域里 wo 取 100 很轻松离散后同样的 β03 在采样间隔内可能已经让递推不稳。 解决ESO 和控制律一起离散用前向欧拉递推h 满足 h*wc 在 0.05~0.1 量级离散后重新微调 wo通常要往下压一半左右。坑位 5把 ADRC 当黑匣子调越调越乱现象同时动 kp、kd、β01、β02、β03 五个参数调了一下午性能还不如 PID开始怀疑 ADRC 本身。 原因这五个参数不是独立的。β 三个值由 wo 一个变量决定kp、kd 由 wc 决定真正需要手动定的只有 wo、wc、b0 三个自由度。独立乱调等于破坏带宽法的整定结构系统必然表现出奇怪的耦合现象。 解决回到第 4 章的带宽法把 β 和 kp、kd 全部写成公式只动 wo、wc、b0。调参遇到瓶颈时先怀疑 b0 量级再去动 wc最后才碰 wo。6. 上板前夜把二阶ADRC从Simulink搬到Arduino前必须做的三件事6.1 离散ESO前向欧拉递推与控制周期匹配仿真跑通只是第一步真机部署时对象变成了数字控制器ESO 必须离散化。最常见做法是前向欧拉把连续微分方程直接改成递推式控制周期为 hz1[k1] z1[k] h*(z2[k] β01*e[k])z2[k1] z2[k] h*(z3[k] β02e[k] b0u[k])z3[k1] z3[k] h*(β03*e[k])采样周期不要拍脑袋。我的习惯是先定 wc再要求 h*wc 落在 0.05~0.1 区间。wc20 rad/s 时h 应该在 2.5ms~5ms实际取 2ms 留足裕量。这个约束的物理含义是每个闭环周期内至少采样 10~20 次数字控制的相位损失才不明显。// 二阶 ADRC 离散 ESO 与控制律前向欧拉 // 控制周期 h0.002s全局参数见 adrc_init.m 计算 static float z1 0.0f, z2 0.0f, z3 0.0f; void adrc_init(void) { z1 0.0f; z2 0.0f; z3 0.0f; } void adrc_step(float ref, float y, float *u_out) { float e y - z1; static float u 0.0f; // 上一拍控制量 // 扩张状态观测器先估计后输出 z1 h * (z2 beta1 * e); z2 h * (z3 beta2 * e b0 * u); z3 h * (beta3 * e); // 线性控制律 扰动补偿 float u0 kp * (ref - z1) - kd * z2; u (u0 - z3) / b0; // 控制量限幅 if (u u_max) u u_max; if (u u_min) u u_min; *u_out u; }代码逻辑说明注意 z2 更新用的是上一拍控制量 u而不是当前拍刚算出来的值这对应先估计、后控制的一拍延迟。这个延迟在连续域仿真里不存在上板后如果 wc 高会表现为轻微相位滞后要消除可以把当前拍 u 代回 ESO 再算一次但多数对象没必要ht对 wc 留够裕量即可。z3 长时间运行可能漂移可以在递推里给 z3 乘一个略小于 1 的系数比如 0.999做泄漏防止数值累积。6.2 限幅与现场验证习惯限幅不只加在控制量上z3 也应该限幅。扰动估计值如果跑出物理上不可能的范围说明 ESO 已经失稳此时直接强制输出限幅治标不治本。我会在 z3 递推后加一句若 z3 超出预设的扰动上限就把它拉回上限值同时把 z1、z2 也检查一遍避免观测器内部状态失控。现场验证的顺序也值得养成习惯先在板子上跑一个 10% 幅值的小阶跃确认输出平稳、u 没有预料外的抖动再手动加一个已知大小的负载扰动看 z3 能不能在几百毫秒内跟踪到预设值附近。这个已知扰动测试是最快的 ESO 健康检查——如果 z3 和标定值差得远先别查控制律回去校 b0。我第一次把连续 ESO 直接搬到板子上时控制周期随便取了 5mswo 还按仿真里的 80 用结果超调比仿真大了两三倍u 一直在抖。后来按 h*wc 约束把控制周期压到 2ms、wo 降到 40波形才和仿真基本对得上。那次之后我养成一个习惯任何离散化改动先把采样周期和带宽的关系写在代码注释里防止过几个月自己都忘了当初为什么取这个数。希望这篇笔记能让你少走一趟同样的弯路。本文还有配套的精品资源点击获取