ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

ADRC自抗扰控制SIMULINK仿真模型搭建与参数整定避坑指南

ADRC自抗扰控制SIMULINK仿真模型搭建与参数整定避坑指南

简介:这份资源面向控制算法学习者和自抗扰控制(ADRC)入门研究者,提供一套可直接运行的SIMULINK仿真模型,重点针对二阶惯性环节进行控制器设计与验证。包内包含1个m脚本、1个mdl模型文件和1份PDF参考文献,压缩包约136KB,共3个文件。其中m文件负责参数初始化与调优,mdl模型搭建了完整的仿真回路,并内置线性扩张状态观测器(LESO)对系统状态进行实时估计,参数已预先整定完毕,还额外加入噪声信号用于测试观测器的抗扰能力与鲁棒性。PDF文档可作为理论补充,帮助理解从PID到自抗扰控制的演进思路。使用时需保证m文件与模型文件路径一致,在MATLAB 2014a环境下即可打开运行。目前已有3163人学习下载,适合希望快速上手ADRC仿真、观察LESO跟踪效果并在此基础上修改参数做对比实验的读者,能省去从零搭建模型的时间,直接聚焦算法调试与现象分析。

1. ADRC的SIMULINK仿真模型:为什么你搭的模型一跑就发散

如果你在控制领域待过一阵,大概率听过自抗扰控制(ADRC)这个名字。它最吸引人的地方在于:不需要被控对象的精确数学模型,把内外部扰动统一当成一个“总扰动”去估计并补偿掉。听起来很美好,但很多人第一次在 SIMULINK 里搭 ADRC 仿真模型时,会遇到一个非常反直觉的现象——参数明明按论文里的值设了,一运行输出就发散,或者跟踪曲线抖得像心电图。

问题往往不在 ADRC 本身,而在于仿真模型的搭建方式。ADRC 的核心是扩张状态观测器(ESO),ESO 里有一个带宽参数 ωo,这个参数和仿真步长之间有一个隐性的匹配关系。步长太大,ESO 的离散化误差会直接让观测器失稳;步长太小,仿真时间又长得让人抓狂。再加上 TD(跟踪微分器)和 NLSEF(非线性状态误差反馈)的离散实现方式不同,同一个控制结构在不同搭建方式下表现可能天差地别。

这篇笔记面向的是准备在 SIMULINK 里搭一套可复现、可调参、能跑通的 ADRC 仿真模型的工程师。不管你是刚接触 ADRC 想快速验证,还是已经搭过一版但效果不理想想排查问题,下面从模块拆解、参数整定到避坑排查,按实际搭建顺序讲清楚。

2. 把 ADRC 拆成 SIMULINK 能落地的四个模块

2.1 为什么不能直接照搬连续域公式

ADRC 的原始文献大多在连续域给出公式,比如经典的非线性 ADRC:

TD: v1' = v2 v2' = -r * fal(v1 - v, 0.5, h) ESO: e = z1 - y z1' = z2 - beta1 * e z2' = z3 - beta2 * fal(e, 0.5, h) + b0 * u z3' = -beta3 * fal(e, 0.25, h) NLSEF: u0 = beta1_nl * fal(e1, a1, delta) + beta2_nl * fal(e2, a2, delta) u = u0 - z3 / b0

如果你直接把v1' = v2这样的微分方程用 Integrator 模块搭出来,在连续求解器下确实能跑。但一旦换成离散求解器,或者你想把控制器部署到实际数字控制器上,就必须做离散化。离散化方式选不对,fal 函数在零点附近的非线性特性会被破坏,ESO 的收敛性直接崩掉。

常见做法是:TD 和 ESO 都用前向欧拉离散,NLSEF 保持代数运算不变。前向欧拉虽然精度一般,但胜在实现简单、和实际代码的对应关系清晰。如果你追求更高精度,可以用双线性变换,但 fal 函数里的幂次项在双线性变换下会引入额外的计算量,仿真阶段不一定划算。

2.2 四个核心模块的 SIMULINK 搭建步骤

下面按信号流向,从 TD 到被控对象,逐个说明搭建方式。假设被控对象是一个二阶系统,比如y'' = -a*y' - b*y + c*u + d(t),其中 d(t) 是外部扰动。

TD 模块(跟踪微分器)

TD 的作用是安排过渡过程,给出跟踪信号 v1 及其微分 v2。在 SIMULINK 里用一个 MATLAB Function 模块实现最方便:

function [v1, v2] = TD(v, v1_prev, v2_prev, r, h) % v: 参考输入 % v1_prev, v2_prev: 上一时刻状态 % r: 跟踪速度因子 % h: 仿真步长 fh = fal(v1_prev - v, 0.5, h); v1 = v1_prev + h * v2_prev; v2 = v2_prev + h * (-r * fh); end function y = fal(e, alpha, delta) if abs(e) <= delta y = e / (delta^(1-alpha)); else y = (abs(e)^alpha) * sign(e); end end

这段代码里,r决定 TD 的跟踪速度,r越大跟踪越快但噪声放大越明显。h必须和 SIMULINK 的固定步长一致,否则 TD 输出的微分信号会带一个系统性偏差。fal函数里的delta是线性区宽度,一般取h的 5 到 10 倍,太小会导致零点附近抖振,太大则退化成线性环节。

ESO 模块(扩张状态观测器)

ESO 是 ADRC 的心脏,负责估计状态和总扰动。同样用 MATLAB Function 实现:

function [z1, z2, z3] = ESO(y, u, z1_prev, z2_prev, z3_prev, beta1, beta2, beta3, b0, h) % y: 系统输出 % u: 控制量 % beta1, beta2, beta3: 观测器增益 % b0: 控制增益估计值 e = z1_prev - y; z1 = z1_prev + h * (z2_prev - beta1 * e); z2 = z2_prev + h * (z3_prev - beta2 * fal(e, 0.5, h) + b0 * u); z3 = z3_prev + h * (-beta3 * fal(e, 0.25, h)); end

三个增益的整定有明确的带宽配置法:把 ESO 的极点配置在-ωo处,则beta1 = 3*ωo,beta2 = 3*ωo^2,beta3 = ωo^3。这是线性 ESO 的配置方式,非线性 fal 函数下这个关系近似成立。ωo一般取控制器带宽ωc的 3 到 5 倍,但要注意ωo * h不能太大,经验上ωo * h < 0.1比较安全。

NLSEF 模块(非线性状态误差反馈)

NLSEF 根据 TD 给出的 v1、v2 和 ESO 给出的 z1、z2 计算控制量:

function u0 = NLSEF(v1, v2, z1, z2, kp, kd, alpha1, alpha2, delta) e1 = v1 - z1; e2 = v2 - z2; u0 = kp * fal(e1, alpha1, delta) + kd * fal(e2, alpha2, delta); end

kp和kd按kp = ωc^2、kd = 2*ωc配置,ωc是期望的闭环带宽。alpha1常取 0.75,alpha2常取 1.25,这是经验值,实际可以根据跟踪误差的收敛速度微调。

扰动补偿与对象

最终控制量u = u0 - z3 / b0。这里b0的取值很关键:它不需要精确等于对象的真实控制增益,但偏差太大会导致 ESO 的 z3 估计范围超出线性区,补偿效果变差。一般取真实增益的 0.5 到 2 倍之间都能接受。

被控对象用 Transfer Fcn 或 State-Space 模块搭建,扰动可以用 Band-Limited White Noise 或正弦叠加。

2.3 离散求解器与步长的匹配关系

SIMULINK 里必须选固定步长离散求解器,不要用变步长。变步长求解器会在 fal 函数的非线性区反复试探,导致仿真速度极慢甚至卡死。

步长h的选择有一个经验公式:h < 1 / (10 * ωo)。比如ωo = 100 rad/s,则h < 0.001 s。如果ωo = 500,h要小于 0.0002 s。这个约束来自 ESO 离散化后的稳定性条件,超过这个界限,z3 会振荡发散。

仿真总时长一般取闭环响应时间的 5 到 10 倍。比如期望调节时间 1 秒,仿真 5 到 10 秒足够看到稳态。

3. 参数整定:从 ωc 和 ωo 出发的三步法

3.1 先定控制器带宽 ωc,再定观测器带宽 ωo

ADRC 的参数整定可以归结为两个带宽的配置。ωc决定闭环响应速度,ωo决定扰动估计速度。一般ωo = 3*ωc到5*ωc。如果对象有较大延迟,ωc要适当降低,否则 NLSEF 的输出会剧烈振荡。

具体操作:先设ωc = 10 rad/s,ωo = 50 rad/s,b0取对象真实增益的 1 倍。跑一次仿真,看阶跃响应的上升时间和超调。如果上升太慢,增大ωc;如果超调太大,减小ωc或增大ωo。

3.2 b0 的粗调与精调

b0的粗调方法是:给对象一个已知的阶跃输入,测量稳态输出变化量,b0 ≈ Δy / Δu。如果对象有积分环节,b0取积分增益。

精调时观察 ESO 的 z3 输出。如果 z3 在稳态时明显偏离零,说明b0和真实增益不匹配。b0偏大,z3 会偏负;b0偏小,z3 会偏正。调整b0使 z3 在稳态时接近零。

3.3 用表格对比不同参数下的响应

下面这张表是某二阶对象在相同 TD 和 NLSEF 参数下,只改ωo和h的响应对比。对象为G(s) = 1 / (s^2 + 2s + 1),ωc = 20,b0 = 1。

组别ωo (rad/s)步长 h (s)上升时间 (s)超调量稳态误差是否发散
A600.0010.184.2%0.001否
B1000.0010.158.7%0.001否
C1000.005———是
D2000.00050.1215.3%0.002否
E2000.002———是

从表里能看出两个规律:第一,ωo增大能加快扰动估计,但超调也会增大;第二,ωo和h必须匹配,C 组和 E 组都是因为ωo * h超过了 0.1 而发散。A 组和 B 组的ωo * h分别是 0.06 和 0.1,都在安全边界内。

提示:如果你不确定ωo * h是否安全,先设ωo = 50,h = 0.001,跑通后再逐步增大ωo,每次增大后观察 z3 是否振荡。

4. 避坑排查:ADRC 仿真模型最常见的五个翻车点

4.1 现象:一运行就报代数环错误

原因:ESO 的 MATLAB Function 里用了当前时刻的u计算z2,而u又依赖z3,形成代数环。

解决:在 ESO 输出到 NLSEF 的路径上加一个 Unit Delay 模块,或者把 ESO 里的u换成上一时刻的u_prev。前者更通用,后者需要多存一个状态变量。我一般用 Unit Delay,步长就是h,对控制性能影响可以忽略。

4.2 现象:跟踪曲线在稳态时高频抖振

原因:fal 函数的delta设得太小,ESO 在零点附近频繁切换线性区和非线性区。

解决:把delta增大到5*h到10*h。如果抖振还在,检查 TD 的r是否过大,r过大会让 v2 信号噪声放大,进而通过 NLSEF 传到控制量上。

4.3 现象:z3 估计的扰动和实际扰动方向相反

原因:b0的符号搞反了。ADRC 的补偿量是-z3/b0,如果b0符号错了,补偿方向就反了。

解决:确认对象的控制增益符号。如果对象是y'' = ... + b*u,b为正,则b0取正。如果对象是y'' = ... - b*u,b0取负。不确定的话,先给一个正的小阶跃,看输出是增大还是减小。

4.4 现象:仿真跑得很慢,步长已经很小了

原因:用了变步长求解器,或者 MATLAB Function 里用了persistent变量导致每次调用都重新初始化。

解决:求解器换成 Fixed-step Discrete,步长按1/(10*ωo)设。MATLAB Function 里不要用persistent,状态用输入输出端口传递。如果还是慢,把 fal 函数里的abs(e)^alpha换成查表法,仿真阶段没必要用精确幂运算。

4.5 现象:换一组参数后 ESO 的 z1 跟踪不上 y

原因:ωo太小,ESO 的带宽低于对象输出 y 的变化频率。

解决:增大ωo,同时按比例减小h保持ωo * h < 0.1。如果对象输出本身有高频噪声,先加一个低通滤波器再送入 ESO,否则 ESO 会把噪声当成扰动去补偿,导致控制量抖振。

5. 进阶技巧:用 MATLAB 脚本批量扫参和自动验证

5.1 把 SIMULINK 模型参数化

手动改参数、点运行、看波形,效率太低。我一般会把所有可调参数写到 MATLAB 脚本里,用set_param批量赋值,再用sim命令跑仿真,最后用To Workspace模块把数据导出来做自动分析。

% 批量扫参脚本 wc_list = [10, 20, 30]; wo_ratio = [3, 4, 5]; h = 0.001; results = []; for wc = wc_list for ratio = wo_ratio wo = wc * ratio; if wo * h > 0.1 continue; % 跳过不安全组合 end % 设置 SIMULINK 模型参数 set_param('adrc_model/ESO', 'beta1', num2str(3*wo)); set_param('adrc_model/ESO', 'beta2', num2str(3*wo^2)); set_param('adrc_model/ESO', 'beta3', num2str(wo^3)); set_param('adrc_model/NLSEF', 'kp', num2str(wc^2)); set_param('adrc_model/NLSEF', 'kd', num2str(2*wc)); % 运行仿真 simOut = sim('adrc_model', 'StopTime', '5'); y = simOut.yout{1}.Values.Data; % 计算性能指标 overshoot = (max(y) - 1) * 100; rise_time = find(y >= 0.9, 1) * h; results = [results; wc, wo, overshoot, rise_time]; end end % 找超调小于 10% 且上升时间最短的组合 idx = results(:,3) < 10; best = results(idx, :); [~, min_idx] = min(best(:,4)); disp(['最佳参数: wc=', num2str(best(min_idx,1)), ... ', wo=', num2str(best(min_idx,2))]);

这段脚本的逻辑是:遍历ωc和ωo/ωc的组合,跳过ωo*h > 0.1的不安全组合,跑仿真后提取超调和上升时间,最后筛选出超调小于 10% 且上升时间最短的参数。set_param里的路径adrc_model/ESO需要替换成你实际模型里的模块路径。

5.2 用频域分析验证 ESO 的观测带宽

时域扫参只能看最终响应,如果想确认 ESO 本身是否工作正常,可以做一个小信号频域测试:给对象输出叠加一个已知频率的正弦扰动,看 z3 能否跟踪上这个扰动。如果 z3 的幅值在某个频率后明显衰减,说明 ESO 的观测带宽不够,需要增大ωo。

具体操作:在扰动通道加一个 Sine Wave 模块,频率从 1 rad/s 扫到ωo,记录 z3 的幅值和相位。如果 z3 在ωo/3处幅值衰减到 70% 以下,说明ωo偏小。

5.3 一个容易被忽略的细节:TD 的 h 和仿真步长必须一致

TD 里的h参数如果和 SIMULINK 的固定步长不一致,v2 信号会带一个比例误差。这个误差在低速跟踪时不明显,但在快速跟踪时会让 NLSEF 的微分项补偿过量,导致超调。我一般会在脚本里把h设成和set_param('adrc_model', 'FixedStep', '0.001')完全一样的值,避免手动改参数时漏掉。

5.4 从仿真到代码生成的注意事项

如果你打算把 ADRC 从 SIMULINK 仿真推到实际控制器,MATLAB Function 里的 fal 函数需要改成定点或单精度浮点实现。abs(e)^alpha在定点下会溢出,常见做法是用查表加线性插值替代。另外,ESO 的三个状态变量在代码里要显式声明为全局或静态变量,不能像仿真里那样靠端口传递。

我自己的习惯是:仿真阶段用双精度浮点,参数整定完成后,把 fal 函数替换成定点查表版本,再跑一次仿真对比。如果定点版本的性能下降超过 20%,说明查表精度不够,需要加密表格。

希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

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