简介:本资源是面向控制工程领域研究人员与自动化专业高年级本科生/研究生的Matlab自抗扰控制(ADRC)专用工具箱,旨在解决含建模误差、外部扰动及强非线性的复杂系统鲁棒控制难题。工具箱完整实现ADRC核心算法,包含扩展状态观测器(ESO)设计、李雅普诺夫稳定控制器配置、多场景仿真验证及工程化部署支持,适用于电机驱动、DC-DC变换器、TCLab温控、双容水箱等典型物理系统建模与控制实践。压缩包共18个文件,涵盖6个.m函数脚本(算法核心)、6个.slx Simulink模型(含DC-DC、TCLab、直流电机等5类典型应用)、2个.ini配置文件、1个.mltbx官方工具箱安装包、1个.png示意图及配套license协议,整体仅740KB,轻量易集成。已有952人学习下载,提供开箱即用的实例工程、模块化库文件(adrc_toolbox_library.slx)与实时控制支持函数(如msfun_realtime_pacer.m),显著降低ADRC理论落地门槛。 做控制的这些年,我越来越觉得“自抗扰控制”是一门被严重低估的技术。论文里它被写得玄乎,什么“总扰动”“扩张状态”,读着读着就劝退;工程里真用起来,又发现每次都要从头撸一遍微分器、观测器、参数整定的代码,光调试就耗掉好几个通宵。我最早接触ADRC是在一个伺服系统项目上,电机负载突变导致PID调好的参数瞬间失效,后来换上ADRC,扰动抑制效果确实立竿见影,但当时没有现成工具,只能靠手写的S函数硬扛,代码又乱又难复用。后来我把这套东西整理成了一个Matlab自抗扰控制工具箱,从TD、ESO到控制律全部封装成独立模块,配合Simulink仿真和参数整定脚本,基本能满足从课堂作业到工程验证的大部分需求。这篇文章就把我这个工具箱的完整思路、核心实现、实际踩坑和调试经验全部梳理出来,给正在学ADRC或者打算在项目里用它的人一个可以直接参考的落地路径。
1. 工具箱的整体设计思路:为什么要把ADRC封装成模块
1.1 自抗扰控制到底解决什么问题
要理解工具箱的设计逻辑,先得说清楚ADRC本身的思路。传统PID最怕两件事:一是模型参数变化,二是外部扰动不可测。ADRC的核心想法特别朴素——既然扰动很难直接测,那就把它当作一个额外的状态,用观测器把“总扰动”估计出来,然后在控制量里把它抵消掉。这里“总扰动”不是一个严谨的物理量,而是指所有偏离理想模型的因素叠加:负载变化、摩擦力、未建模动态,甚至参数摄动,全装进这个“总扰动”的口袋里。
ADRC的标准结构分成三块:跟踪微分器(TD)负责安排过渡过程,把输入信号的突变“平滑化”;扩张状态观测器(ESO)负责实时估计系统的状态和总扰动;然后控制器用估计出来的状态做反馈,同时把估计的扰动前馈补偿掉。理解这个结构以后,你会发现ADRC本质上是一种“估计-补偿”的控制范式,PID只是它的一个特例。
但问题在于,结构简单不代表实现简单。ADRC里面的ESO是非线性的,参数又多,每个模块的离散化方式也影响仿真结果。如果每次都从零写,光一个ESO的调参就够让人头疼。所以工具箱的核心价值不是“创造算法”,而是把ADRC的常用模块做成干净、稳定、可复用的工具,让使用者专注于被控对象本身。
1.2 工具箱的结构与模块划分
在设计工具箱之初,我给自己定了几个原则:模块独立、接口统一、参数可批处理。模块之间不互相依赖,你只用TD也行,只用ESO也行,甚至可以拿ESO单独当扰动观测器用。接口统一的意思是,每个模块的函数签名遵循同样的风格,输入输出约定明确,方便二次开发和替换。
工具箱当前包含六大类功能:
- 连续域模块:td_cont、eso_cont、nlsef_cont,用于理论研究与连续系统仿真
- 离散域模块:td_disc、eso_disc、nlsef_disc,用于数字控制器实现与采样系统仿真
- 系统对象封装:ADRC2Order类,用一个对象搞定二阶系统整条控制链路
- 参数整定脚本:带宽法参数计算、基于粒子群(PSO)的自动寻优
- Simulink支持:提供S-Function封装模块,可直接拖入Simulink模型
- 辅助工具:阶跃响应对比、扰动估计误差分析、控制量输出监视
这个结构的最大好处是灵活。比如你只想验证ESO的观测性能,可以直接调用eso_disc,输入系统的控制量和测量输出,返回状态估计值和扰动估计值,不需要搭整套控制回路。很多刚开始用ADRC的人其实也会遇到这种需求:先确定观测器能不能看准,再谈控制器。
1.3 为什么选择“半仿真半工程”的形态
我见过不少工具箱,要么纯公式推演,代码看不懂;要么纯工程化,装了以后像黑盒子,出了问题根本没法调试。我这个工具箱刻意做成“半仿真半工程”的形态:每一行代码都是公开的、可读的,模块注释里直接写对应公式和离散化方法;但同时又提供面向工程的高层封装,命令行几行代码就能快速跑通一个仿真实例。
打个比方,这就像给了你全套菜谱,但也给你一盘做好的菜。你先看菜长什么样、尝一口,再翻菜谱研究怎么做出来的。对初学者来说,最怕的是拿到一盘菜但不知道里面放了什么,更怕的是只有菜谱但做出来完全不是那么回事。工具箱的目标就是让这两种状态之间无缝切换。
2. 核心算法模块:每一个关键环节的实现与解析
2.1 TD跟踪微分器:为什么需要“过渡过程”
很多刚开始学ADRC的人不理解:PID不是直接对误差信号做运算就行了吗?为什么还要搞一个TD把输入先处理一遍?这里面的逻辑其实很实际。当设定值突然从0变到1时,如果控制器增益比较大,初始误差瞬间拉满,控制量也会瞬间冲到饱和值,轻则超调,重则激发机械共振。TD的作用就是用一个跟踪速度尽可能快、同时不产生超调的动态过程,把阶跃跳变成斜坡甚至更平滑的曲线。
TD的典型离散形式是:
x1(k+1) = x1(k) + h * x2(k) x2(k+1) = x2(k) + h * fhan(x1(k) - v(k), x2(k), r, h0)这里的fhan是“最速控制综合函数”,参数r决定了跟踪速度,r越大跟踪越快,但太大也会引入噪声放大和相位滞后;h0是滤波因子,对噪声有抑制作用。使用工具箱时,我算过一组典型参数:采样周期1毫秒的系统,r取50到200之间比较稳妥,h0取1到2倍的采样周期。
在工具箱里,我实现了连续和离散两类TD。连续版本主要用于理论验证,离散版本的fhan函数是重点,因为它直接决定了数字控制器的实际表现。如果你的系统对超调极其敏感,比如温控或精密运动平台,TD的过渡过程能明显降低初始冲击,这个效果在调试时会非常有感。
2.2 ESO扩张状态观测器:工具箱的心脏
ESO是ADRC里最核心的模块,它做的事情可以这样理解:系统只有输出y可以测量,但控制律需要知道速度和扰动,ESO就利用输入u和输出y,把系统的内部状态和总扰动全部“猜”出来。这个“猜”的过程就是观测器方程在起作用。
以二阶系统为例,ESO在连续域的标准形式是:
dot(z1) = z2 + beta1 * (y - z1) dot(z2) = z3 + beta2 * (y - z1) + b0 * u dot(z3) = beta3 * (y - z1)z1跟踪系统输出,z2跟踪输出的导数(速度),z3跟踪总扰动。这里的b0是你对被控对象控制增益的粗略估计,不需要精确,但数量级要对。beta1、beta2、beta3是观测器增益,工程上最常用的方法是带宽法:把三个增益统一表示为观测器带宽w0的函数,beta1 = 3w0,beta2 = 3w0^2,beta3 = w0^3,这样就把三个参数化为一个参数,调参难度直线下降。
带宽法在工具箱里是写进参数整定脚本的。你可以只给一个期望带宽w0,脚本自动算出三组beta系数。我在很多对象上试过,典型的一阶惯性加纯滞后系统,w0取5到20倍控制带宽就能获得不错的观测效果;w0太大了也不是好事,系统噪声会被放大,控制量毛刺增加。
工具箱里的ESO也提供了“线性ESO”和“非线性ESO”两种选择。非线性ESO用fal(e, alpha, delta)函数代替线性项,理论上估计精度更高,但参数更多、对离散化步长更敏感。我的建议是,工程上优先用线性ESO,配合带宽法整定,稳且够用;如果是研究性质的项目,可以进一步对比非线性ESO的增益。
2.3 控制律设计:从NLSEF到线性组合
ESO把状态和扰动估计出来了,接下来就是怎么用这些估计值构造控制量。我在工具箱里实现了两种控制组合方式。
第一种是非线性组合(NLSEF),形式是u0 = kp * fal(e1, alpha1, delta) + kd * fal(e2, alpha2, delta),e1是跟踪误差,e2是误差的导数。非线性函数在误差大时增益大、误差小时增益小,理论上能兼顾快速性和平稳性,但缺点很明显:参数调整没有清晰的规律,alpha、delta、kp、kd之间的耦合很复杂。
第二种是线性组合(LSEF),也就是u0 = kp * e1 + kd * e2。线性组合配合带宽法之后,kp和kd可以表示为控制器带宽wc的函数:kp = wc^2,kd = 2*wc。这样整个ADRC就只剩两个带宽参数:观测器带宽w0和控制器带宽wc。这种LADRC线性自抗扰控制的方案,在工程上极其好用,也是我在工具箱里默认推荐的做法。
工具箱在最终控制量计算时,会加入扰动补偿项:u = (u0 - z3) / b0。z3是ESO估计的总扰动,减去它就把扰动的影响在前面抵消掉。这个补偿是ADRC区别于普通状态反馈控制器的关键,也是它在强扰动环境下表现优秀的根本原因。
2.4 参数选择的数学逻辑
很多人卡在“参数不知道怎么选”这一步。用带宽法以后,参数选择其实就变成了两个带宽的选择。我在工具箱里封装了一个函数,只要输入期望的w0和wc,就自动生成整套ADRC参数。
那么w0和wc怎么选?我的经验是:wc的期望值可以先从系统的开环截止频率出发,再适当增大;w0一般取wc的3到10倍,因为观测器必须比控制器更快地“看见”扰动和状态,才能及时补偿。但w0也不可以无限大,受制于采样频率和噪声水平,通常w0取到采样频率的1/20到1/10就是上限了。
举个例子,一个采样周期为1毫秒(即1kHz)的系统,如果期望控制器带宽wc=20 rad/s,那么w0先取80到150试一试。仿真发现观测器估计有明显滞后,再把w0往上调,具体数值取决于系统的噪声底。这个用工具箱函数“adrc_tune_bandwidth”一行代码就能算出来,不需要手工逐个去试。
3. 实操过程:安装、仿真与逐步调试
3.1 环境准备与工具箱安装
使用这个工具箱需要的基本环境是MATLAB R2018b以上版本(太老的版本是2016a也可以,但是S-Function模块的兼容性会稍差一些),Simulink和Control System Toolbox是标配。没有Control System Toolbox也能跑大部分功能,只有个别用于线性化分析的辅助函数会报错。
安装方式很简单,把整个工具箱目录放到一个固定的文件夹下面,然后在MATLAB里执行:
addpath(genpath('你的工具箱路径')); savepath;这样MATLAB重启后依然保留路径。建议不要放在当前工作目录,因为一旦切换目录,函数找不到了。我一开始就吃过这个亏,把工具箱放在Downloads下面,后来重装了系统,整包丢了,所有调试好的例子全没了。后来学乖了,专门建了一个D:\tools\matlab-adrc-toolbox固定路径,并且定期做版本备份。
安装好以后,在命令行输入help adrc,能看到整个工具箱的函数列表。建议先把自带的示例脚本跑一遍,确认安装没问题,再开始自己的项目。
3.2 快速上手:被控对象与控制器
假设你要控制一个典型的二阶对象:G(s) = 25 / (s^2 + 5s + 25),外加一个外部扰动d(t) = 2*sin(3t)。用工具箱跑一个完整闭环仿真的代码如下:
% 定义被控对象参数 sys_plant = tf(25, [1 5 25]); b0 = 25; % 控制增益的估计值(这里取真值) % ADRC参数(带宽法) wc = 10; % 控制器带宽 w0 = 60; % 观测器带宽 param = adrc_tune_bandwidth(wc, w0, b0, 0.001); % ts=1ms % 创建ADRC控制器对象 adrc_ctrl = ADRC2Order(param); % 仿真时间与输入 t = 0:0.001:5; r = ones(size(t)); % 阶跃设定值 d = 2 * sin(3 * t); % 外部扰动 % 闭环仿真(工具箱自带仿真函数) [y, u, z, est_d] = adrc_simulate(adrc_ctrl, sys_plant, r, d, t); % 画图 figure; subplot(2,1,1); plot(t, r, 'k--', t, y, 'b-', 'LineWidth', 1.5); legend('设定值', '实际输出'); ylabel('输出'); subplot(2,1,2); plot(t, d, 'r--', t, est_d, 'b-', 'LineWidth', 1.5); legend('真实扰动', 'ESO估计扰动'); xlabel('时间/s'); ylabel('扰动估计');运行这个脚本,第一眼你会看到输出曲线几乎没有超调地跟踪到设定值,然后ESO在经历初始过渡后能很好地估计出2*sin(3t)这个扰动。这个示例代码块是工具箱里自带的,我第一次跑通的时候还挺有成就感的,因为这就意味着ESO确实把扰动“看”到了。
比较建议先跑这个基础示例,再替换成自己的被控对象。因为如果不确定工具箱本身有没有问题,后面遇到奇怪现象就分不清楚是代码问题、参数问题还是对象问题,排查起来特别浪费精力。
3.3 在Simulink中使用工具箱
脚本仿真方便理论验证,但工程上很多模型都在Simulink里搭。工具箱提供了一个S-Function封装模块,叫adrc_sfun,在Simulink里通过“Level-2 MATLAB S-Function”调用。
在Simulink中使用步骤:
- 在模型编辑器中放一个“S-Function”模块(User-Defined Functions库)
- 双击模块,在S-Function name中填
adrc_sfun - 在S-Function parameters中填写参数向量
[wc, w0, b0, ts] - 把设定值信号、被控对象测量输出分别接到模块的输入端口
- 模块输出端口1是控制量u,输出端口2是扰动估计值z3
我踩过的坑是:S-Function模块的采样时间设置要和被控对象模型的仿真步长一致。如果被控对象用变步长求解器(默认ode45),而ADRC模块是固定步长1ms,两者之间会有时序混乱。后来我的做法是整体用固定步长求解器,步长设置为1ms或更小,整个模型一致性高,问题少很多。
Simulink里面跑完仿真以后,要仔细检查控制量有没有频繁饱和。如果控制量长时间顶在饱和限幅上,说明力不够大,这时候调再多的ADRC参数也没用,是需要从执行机构层面解决的问题。
3.4 参数批量寻优:当手动调参不够高效时
手动调参在单点工况下通常够用,但如果你想分析不同w0、wc组合下的性能甚至自动找最优参数,手动一个个试就太慢了。工具箱里集成了一组基于粒子群(PSO)的参数寻优函数。
基本思路是定义目标函数——比如ITAE指标,越小代表追踪性能和扰动抑制性能的综合表现越好——然后让PSO自动搜索wc和w0的最优组合。使用方式如下:
% 定义被控对象 sys_plant = tf(25, [1 5 25]); % PSO参数搜索,设定搜索范围 lb = [1, 10]; % wc下限、w0下限 ub = [50, 300]; % wc上限、w0上限 options = optimoptions('particleswarm', 'SwarmSize', 30, 'MaxIterations', 50); % 调用工具箱封装的目标函数 [best_param, best_cost] = adrc_pso_tune(sys_plant, lb, ub, 0.001, options);PSO跑出来的参数不是一个“绝对最优解”,而是一个在给定评价标准下不错的解。搜索结束以后,建议把最优参数带回到Simulink模型里做一轮人工验证,看看控制量变化是否平缓、有没有明显抖动。机器优化的目标函数往往只抓“量化指标”,但工程上你还得关心控制量饱满度、执行机构寿命这些定性因素。
4. 常见问题与排查技巧实录
4.1 仿真发散:最常见也最让人头疼
仿真发散的典型表现是输出一路飙到无穷大或者NaN。通常原因有三个:b0设置错误、观测器增益过大、仿真步长过大。
b0的问题最隐蔽。如果被控对象的真实控制增益是25,但你b0写成了250,相当于控制器觉得“只需要很小的控制量就能产生想要的加速度”,于是实际控制量给大了十倍,很容易震荡发散。反过来b0太小,控制量给出去了没效果,观测器输出一直跟不上,整个环路也越来越差。处理办法是:先用开环仿真的数据粗略估算对象的增益,再在这个值上下浮动两倍的范围里尝试。
观测器增益过大(w0取值太大)的表现是:状态估计值出现高频噪声,然后控制器把这些噪声放大,最终系统发散。我遇到过w0取200的情况,仿真结果直接变成一条粗带子,全是高频振动。这种情况把w0降到80左右立刻稳定。
仿真步长的问题也好解决——步长改小。固定步长从1ms改到0.1ms,很多发散问题自动消失。但注意,工程实现时的步长由控制器硬件决定,如果硬件只能支持1ms,那就得接受步长限制,不能靠仿真步长小来掩盖问题。
我建议的排查顺序是:先检查b0的数量级,再用带宽法公式重算一遍参数(确认没有手误),最后再改仿真步长。不要一上来就去调非线性函数的参数,那样只会让问题更乱。
4.2 扰动估计滞后:ESO“看得到但反应慢”
在实际工程中,ESO对快速变化扰动的估计通常存在滞后。原因不难理解:观测器带宽w0限制了它的响应速度,如果扰动变化频率高于w0,估计值自然跟不上。
有两类处理思路。第一类是提高w0,但代价是噪声放大;第二类是引入扰动模型,如果扰动是周期性的(比如机械系统的动不平衡、负载的周期性波动),可以在ESO的基础上增加一个谐振项,让观测器“知道”扰动在某个频率附近周期性变化,估计精度会显著提高。
工具箱里提供了一个扩展版本eso_resonant,参数里多一个谐振频率w_r。遇到过这样一个实例:一台旋转机械的负载扰动主要是2倍频分量,测试时把这个分量可以精确到2倍频,ESO加谐振项以后,扰动估计误差从原来的8%降到了1.5%左右。这个方法其实很实用,但很多人不知道ADRC还能这样扩展。
4.3 系统输出有抖振:控制量在高频振荡
抖振是数字控制器里常见的现象。当你看到控制量出现小幅高频振荡时,第一步应该怀疑噪声放大,也就是w0太高了;第二步检查传感器测量信号的质量,如果测量输出本身就带噪声,ESO会把噪声当作某种“猛烈的扰动”去补偿。
有一个比较实用的稳定化技巧:在ESO的输入端做轻度滤波,对测量信号y做一个一阶低通,截止频率设置在5倍控制带宽以上。这样不会明显影响ADRC的快速性,但能有效抑制高频噪声。工具箱里有一个filter_measurement辅助函数,专门用于设计这个前级滤波器。
另外,控制量本身的限幅和变化率限制也很重要。如果执行机构有响应速度限制,但控制器输出变化太快,实际物理系统是跟不上的,就会出现“控制器在输出高频分量、执行器在低通滤除、控制效果打折”的无奈局面。这时候要在控制律输出后加一个变化率限制模块,让控制量不会“瞬间跳变”。
4.4 参数整定中的几个常见惯性思维
我发现不少使用者容易掉进几个惯性思维的坑。第一个坑是照搬论文参数。论文里的w0和wc是基于特定的对象和采样时间,你的对象不一样,参数就必须重新整定。第二个坑是认为参数越多越好。其实在带宽法框架下,两个带宽就足够了,再加一堆非线性参数反而会让调试无从下手。第三个坑是忽略采样时间的影响。同一个控制器,在1kHz采样下表现正常,换到500Hz采样可能就震荡了,因为离散化带来的相位滞后变了,需要重新调w0和wc。
我在工具箱文档里专门加了一页“参数调整指南”,用表格对比了各种异常现象对应的调参方向,这比我给你一堆零零散散的建议要实用得多。比如:
| 现象 | 原因分析 | 调整方向 |
|---|---|---|
| 阶跃响应超调大 | wc过大 | 减小wc |
| 响应太慢 | wc过小 | 增大wc |
| 扰动抑制弱 | w0不够 | 增大w0 |
| 输出高频抖动 | w0过高或测量噪声大 | 降低w0或增加测量滤波 |
| 控制量饱和频繁 | b0偏大 | 减小b0 |
| 稳态误差明显 | ESO扰动补偿不足 | 检查b0或增加ESO增益 |
这张表我反复测试过,大部分调试卡住的情况都能在里面找到方向。
4.5 工具箱使用中的工程习惯建议
最后分享几个工具箱使用过程中养成的工程习惯,这些看起来都是小事,但能帮你少掉很多头发。
第一,每次仿真前先确认b0的值和方向正负号是否合理。符号错了,整个控制系统就反了,输出会像开飞机直接俯冲。第二,养成看“控制量曲线”的习惯,不要只盯着输出曲线。很多时候输出看着还行,但控制量早就饱和甚至畸变了,这种状态在工程上是不能接受的。第三,不要一上来就接真实硬件,先在Simulink里做一轮数字仿真,把参数大概范围定下来,再转到硬件上做微调。这样调试效率会高很多。
工具箱的存在不是为了“一键解决所有ADRC问题”,而是为了把重复的、容易出错的模块搭好、测好、注释好,让你把精力集中在真正需要思考的地方:怎么建模你的被控对象、怎么定义扰动的特点、怎么评价控制效果。这些才是做好控制的真正核心。
从我个人经验来看,ADRC工具箱最大的价值不是“自动调好所有参数”,而是大大缩短了从“想用ADRC”到“真正跑起来看效果”的距离。以前可能要花两三天才能搭好的仿真环境,现在十几分钟就能出第一版结果。希望这篇文章能帮你少走一些弯路,把时间花在更有价值的控制方案设计上。
本文还有配套的精品资源,点击获取