ADRC自抗扰控制MATLAB源码拆解:TD、ESO与参数整定
发布时间:2026/9/16 0:08:14
分类:文化教育
浏览:1234

简介面向自动控制领域的学习者与工程师这套自抗扰控制器ADRCMatlab源码聚焦NLSEF TD、ADRC PID与ESO扩展状态观测器等关键模块旨在解决传统PID控制中响应快速性与超调难以平衡、抗扰动能力不足等问题。源码以TD跟踪微分器、ESO扰动观测器和非线性状态误差反馈控制律为主线通过.m脚本实现核心算法并借助.mdl模型搭建Simulink仿真验证环境读者可直观看到状态估计、扰动补偿与PID控制融合的完整过程也可调整ESO参数和控制器增益观察不同控制效果。资源仅6KB共4个文件包含3个.m函数脚本与1个.mdl仿真模型结构紧凑清晰便于逐模块阅读和调试。目前已有375人学习使用适用于电力系统、机械传动、航空航天等复杂非线性控制对象的参数整定与算法研究是理解ADRC原理并快速上手的实用代码包。1. 从PID到自抗扰这套ADRC源码为什么值得拆开看传统PID控制有三个长期困扰现场工程师的问题微分信号在物理上很难直接测得通常用差分近似但会放大噪声积分项面对初始误差大的阶跃响应时容易产生超调控制器参数往往只能在响应速度和稳定性之间妥协。韩京清提出的ADRC核心构成正是把这三个问题拆开处理用跟踪微分器TD安排过渡过程用扩张状态观测器ESO实时估计并补偿总扰动再用非线性状态误差反馈NLSEF做误差组合整个思路从源头上绕开了“既要快又要稳”的取舍。这套代码包里的han_td.m、han_eso.m、han_ctrl.m和ex_han2.mdl把三个核心环节和Simulink闭环仿真完整串了起来配套的m文件可以直接在MATLAB里单步调试Simulink模型则方便做阶跃响应和抗扰动实验。对于做运动控制、电力电子、伺服系统的人拆这套代码比直接读论文更容易理解ADRC的离散实现细节对于刚接触自抗扰的工程师它也是一个能快速改造成自己被控对象的最小框架。我后面会按TD、ESO、NLSEF的顺序往下拆再落到参数整定上。2. TD、ESO与NLSEFADRC三大核心构成与离散递推2.1 跟踪微分器TD安排过渡过程和提取微分信号对于带噪声的测量信号直接做微分会放大高频噪声这是PID里的D项在实际系统中经常被关掉的原因。TD的做法是让一个二阶系统去跟踪输入信号x1跟踪vx2作为x1的导数从而间接获得较为平滑的微分信号。同时对于阶跃输入TD还能安排一个过渡过程避免初始误差过大导致控制量饱和这个特性尤其适合伺服系统的快速定位。离散化后的核心递推很简单实际仿真中我通常把它封装成函数每次按固定步长调用一次。计算的核心就三行第一步计算加速度第二步更新位置第三步更新速度顺序不能乱。下面这段代码取自han_td.m的主循环只是去掉了函数包装便于看清数据流% 离散TD核心递推步长h快速因子r f fhan(x1 - v, x2, r, h); % 最速控制综合函数 x1 x1 h * x2; % 位置更新 x2 x2 h * f; % 速度更新逻辑说明fhan表示韩京清提出的最速控制综合函数它根据当前误差x1-v和速度x2在“快速到达原点”的约束下输出一个加速度控制量f。r越大跟踪越快但也会放大噪声h是采样步长直接决定离散化精度。注意这里的x1和x2需要同时更新不能先更新x1再算f否则就不是同一个时刻的状态。实际使用中我会把fhan单独写成子函数便于在TD和非线性组合里复用。2.2 扩张状态观测器ESO把总扰动扩张成状态传统观测器只能估计可建模的状态ESO的特别之处在于把系统未建模动态和外部扰动f(x1,x2,w,t)合并成一个新的状态x3称为“扩张状态”。以二阶系统为例原系统可以写成两个状态加上扩张状态后变成三阶而ESO的任务就是在线估计z1、z2、z3其中z3跟随着总扰动。这样控制器可以在输出端把z3补偿掉把被控对象近似补偿成串联积分器。ESO的离散迭代式在代码里一般写成单步更新函数每次仿真步进调用一次。需要注意更新顺序是先算误差再依次更新z1、z2、z3第三步里的控制量u必须用上一时刻的值避免离散化时产生代数环这也是很多初学ADRC的人最容易忽略的细节。下面的代码是从han_eso.m里抽出的核心循环% 扩张状态观测器单步迭代 e z1 - y; % 输出估计误差 z1 z1 h*(z2 - beta01*e); z2 z2 h*(z3 - beta02*fal(e, 0.5, h) b0*u); z3 z3 h*(-beta03*fal(e, 0.25, h));逻辑说明beta01、beta02、beta03是观测器增益决定估计收敛速度b0是被控对象控制增益的粗略估计它把控制量u折算进状态方程中。fal函数是非线性增益函数e较大时按幂次衰减e较小时进入线性区间用来平衡收敛速度和噪声抑制。注意z3的方向如果系统微分方程里扰动项为正那么z3的迭代就是h*(-beta03*fal(...))。在写代码时这一步的正负号必须和后续扰动补偿u(u0-z3)/b0配套否则会变成正反馈。2.3 NLSEFfal函数的非线性增益与误差组合非线性状态误差反馈做的事情是把TD给出的期望状态v1、v2和ESO估计的z1、z2做差再通过非线性函数组合成虚拟控制量u0。经典方案有两种一种直接调用fhan(e1, c*e2, r, h)另一种用两个fal函数分别处理位置误差和速度误差。fal函数在matlab里的实现很简短但边界条件必须写对否则会在线性区和非线性区之间出现跳变影响控制品质。下面这个就是fal函数的常见实现它把误差e映射成经过整形的误差增益alpha和delta决定了整形曲线的形状后面参数整定会反复用到function f fal(e, alpha, delta) % 非线性增益函数 if abs(e) delta f abs(e)^alpha * sign(e); % 幂次区 else f e / delta^(1-alpha); % 线性区 end end逻辑说明参数alpha在0到1之间alpha越小小误差区的等效增益越高系统收敛越快但也更容易激发高频抖动。delta是线性区宽度一般取采样步长h的整数倍比如h到10hdelta过小会让fal在小误差附近出现抖振delta过大会使非线性特性退化。实际工程中我常用alpha0.5和deltah来起步。把线性PID和ADRC放在一起对比能看出每个环节的设计思路差异很大。这里的对比不是否定PID的价值而是说明ADRC把PID里隐含的微分近似、积分累积、固定增益三个局限分别替换成了显式结构具体差异见表1对比维度线性PIDADRCTDESONLSEF微分信号来源近似差分噪声大TD通过积分链获得平滑误差增益线性的P、I、D固定增益fal非线性增益小误差高增益未知扰动处理靠积分慢慢消除ESO实时估计前馈补偿初始大误差易超调、积分饱和TD安排过渡过程抑制超调这个表说明ADRC并不是完全抛弃PID而是把PID中的积分项职责转移给了ESO把微分项职责转移给了TD然后把剩余的比例关系用非线性函数重新组合。这也是为什么很多改造成本不高的场景里把PID环换成ADRC后阶跃响应会同时得到更小超调和更快上升时间。3. han_td.m、han_eso.m、han_ctrl.m三个文件如何协同3.1 han_td.m从fhan到完整的求导实现我拆这套源码包时首先看的是han_td.m。它核心是做了两层封装外层是TD的离散递推内层是fhan函数。为了保证fhan能被han_ctrl复用通常把它写成子函数放在同一个文件末尾。一个典型的实现如下function [x1, x2] han_td(x1, x2, v, h, r) % 跟踪微分器入口 % 输入: x1,x2上一时刻状态; v期望信号; h步长; r快速因子 % 输出: 当前时刻的跟踪值x1和微分值x2 f fhan(x1 - v, x2, r, h); % 由最速控制综合函数计算加速度 x1 x1 h * x2; x2 x2 h * f; end function f fhan(e1, e2, r, h) % 最速控制综合函数韩京清形式 % e1: 位置误差; e2: 速度误差; r: 快速因子; h: 步长 d r * h^2; % 线性区边界 d0 h * d; % 边界对应的位置量 y e1 h * e2; % 等效位置误差 a0 sqrt(d^2 8 * r * abs(y)); % 快速达原点的临界参数 if abs(y) d0 a e2 (a0 - d) * 0.5 * sign(y); else a e2 y / h; end if abs(a) d f -r * sign(a); else f -r * a / d; end end逻辑说明fhan的输出f在形式上像是一个受限的加速度控制量它保证状态在最短时间内回到原点而不产生超调。dr*h^2是速度从0加速到最大允许加速度r所需的位移用来区分线性区和非线性区。a0的推导基于最速路径的相轨迹当|y|较大时就按饱和输出-r或r当|y|较小时就给出连续过渡的线性控制。调用时需要注意r越大过渡过程越快但也会让x2的峰值变大h必须与仿真步长一致否则TD的离散化特性会漂移。在ex_han2.mdl中han_td的输出v1和v2会直接接到han_ctrl的e1和e2上所以这个函数的输出顺序不要随意调换。3.2 han_eso.m扩张状态观测器的迭代实现han_eso.m是整套代码里最关键的部分它决定了扰动估计的精度。常见实现是把它做成一个函数每次调用走一遍离散递推输入输出包括上一拍的状态量和当前拍的y、u。下面这段写法在源码包里很常见注意函数的返回值需要是更新后的z1、z2、z3否则下一次调用会沿用旧值function [z1, z2, z3] han_eso(y, u, z1, z2, z3, h, beta01, beta02, beta03, b0) % 扩张状态观测器单步迭代 % y: 系统输出; u: 控制量; z1,z2,z3: 状态估计与扰动估计 e z1 - y; fe1 fal(e, 0.5, h); fe2 fal(e, 0.25, h); z1 z1 h * (z2 - beta01 * e); z2 z2 h * (z3 - beta02 * fe1 b0 * u); z3 z3 h * (-beta03 * fe2); end function f fal(e, alpha, delta) if abs(e) delta f abs(e)^alpha * sign(e); else f e / delta^(1-alpha); end end逻辑说明这里有两个关键点。第一z1的更新用的是线性误差ez2和z3的更新用的是非线性fal输出这是因为位置估计的误差本身已经足够平滑不需要再整形而速度和扰动项更容易受噪声干扰需要fal把大误差的增益压下来。第二b0*u这一项必须放在z2里而不是z1或z3因为它对应的是系统方程中的输入通道。如果b0估计得偏小z3会漂移去补偿输入偏差导致观测到的“扰动”里混入了模型误差后续补偿时看起来反应慢半拍。beta01、beta02、beta03的取值一般按带宽法来后面第5章再展开。3.3 han_ctrl.m控制量的合成与扰动补偿han_ctrl.m把TD的期望状态和ESO的估计状态组合起来生成最终控制量u。常见写法有两种一种是fhan组合一种是fal组合源码包里的han_ctrl.m一般走的是fal组合路线。参数kp、kd是权重系数需要和alpha配合使用function u han_ctrl(v1, v2, z1, z2, z3, b0, alpha1, alpha2, delta, kp, kd) % 非线性状态误差反馈控制律 e1 v1 - z1; % 位置误差 e2 v2 - z2; % 速度误差 u0 kp * fal(e1, alpha1, delta) kd * fal(e2, alpha2, delta); u (u0 - z3) / b0; % 扰动补偿 end逻辑说明u0是虚拟控制量它等效地作用于一个已经被补偿成纯积分器串联型的系统对象z3/b0把估计出的总扰动折算成控制量修正从而在物理上抵消掉扰动。kp和kd在这里已经不是PID里的比例微分增益而是非线性组合的权重kp越大位置回中越硬kd越大对速度误差的阻尼越强。alpha1通常取0.75~1alpha2取1~1.5delta取h到10h。如果han_ctrl.m里没有fal函数定义说明fal被放在了公共路径的fal.m中记得保证MATLAB搜索路径里有这个文件。需要注意u0和z3的符号必须以系统模型为准如果被控对象是负增益则b0应取负值否则整个闭环会形成正反馈。对于ex_han2.mdl里常见的二阶积分对象b0直接取对象增益即可。为了便于对照我把三个文件的输入输出整理成表2文件名输入输出关键参数han_td.mv, x1, x2, h, rx1, x2r, hhan_eso.my, u, z1, z2, z3, h, beta01~03, b0z1, z2, z3beta01~03, b0han_ctrl.mv1, v2, z1, z2, z3, b0, alpha1, alpha2, delta, kp, kdualpha1, alpha2, delta, kp, kd表2里的数据流正好对应闭环han_td产生期望轨迹han_eso产生状态估计han_ctrl把两者合并输出u。只要这三个文件的接口保持一致改成别的被控对象时只需要修改b0和被控对象模型控制律部分基本不用动。4. ex_han2.mdlSimulink闭环结构与参数整定4.1 模型的模块划分与信号走向ex_han2.mdl是一个基于Simulink搭建的ADRC闭环仿真模型打开后可以看到它把ADRC算法封装成了几个子系统HanTD、HanESO、HanCTRL外加一个被控对象和信号源。信号走向可以概括成三条回路第一条是期望信号v经过HanTD得到v1和v2第二条是对象输出y回到HanESO的输入端同时控制量u也引入HanESO得到z1、z2、z3第三条是v1、v2与z1、z2、z3在HanCTRL里做差并合成uu一方面驱动被控对象一方面反馈给HanESO。这个结构里最容易被忽略的是HanESO子系统它同时吃y和u两个信号u的口在原理图中一般放在下面仿真时如果连线断掉扰动补偿就会失效但系统可能仍然稳定导致问题很难发现。我一般会在模型里把y、u、z3三路信号接到Scope上一眼就能看出观测器有没有跟上。4.2 ADRC参数整定表与初始化脚本在ex_han2.mdl里运行仿真前需要先在工作区里准备好模型参数。模型一般通过变量名从基本工作区读取所以我习惯于把参数集中写在一个初始化脚本里避免在Simulink模块里填死数字。下面是一份可以直接套用的初始化示例% 初始化ADRC模型参数 h 0.001; % 采样周期/仿真步长 r 10; % 跟踪微分器快速因子 b0 1; % 被控对象增益的估计值 beta01 100; % ESO误差增益1 beta02 3000; % ESO误差增益2 beta03 30000; % ESO误差增益3 alpha1 0.75; % 位置误差非线性系数 alpha2 1.25; % 速度误差非线性系数 delta h; % fal线性区宽度 kp 30; % 非线性比例权重 kd 10; % 非线性微分权重逻辑说明h在这里既当仿真步长也当TD和ESO的离散步长三者必须保持一致否则ESO的相位会滞后表现为z3大幅振荡。r与系统带宽有关r取得太大TD过渡过程变快但x2的噪声会变明显b0是唯一需要先粗略识别的对象参数若对象是一阶惯性环节加积分b0取系统增益即可。beta01、beta02、beta03的初值可以按带宽法估具体关系见第5章但在仿真里可以先按数量级拉开保证观测器收敛速度远快于控制器。常见的参数调整顺序是先整定ESO让z3在扰动注入后能快速跟踪再整定TD和控制律。4.3 阶跃响应与抗扰动验证模型搭建完成后除了直接点击Run还可以通过sim命令批量跑仿真便于在参数整定过程中自动对比不同组参数的响应。下面这段脚本可以快速跑完ex_han2.mdl并绘制阶跃响应注意它依赖当前工作区已经存在4.2节里的变量% 运行ex_han2.mdl并绘制阶跃响应 options simset(SrcWorkspace, current); % 使用当前工作区变量 simOut sim(ex_han2.mdl, [0 5], options); t simOut.tout; y simOut.yout; plot(t, y, LineWidth, 1.5); grid on; xlabel(时间 (s)); ylabel(输出 y);逻辑说明simset(SrcWorkspace, current)可以确保Simulink模型从当前脚本的工作区读取参数而不是从基本工作区读取这样在循环整定时不会互相污染。simOut.tout和simOut.yout分别保存时间和输出信号如果你在模型中用了Signal Logging还可以直接从simOut.logsout里取z1、z2、z3。要测试抗扰性可以在被控对象输入端或输出端加一个Step扰动源扰动幅值设为阶跃幅值的10%~30%观察y的跌落范围和恢复时间。这里特别建议把ESO的z3输出也拉出来对比理想情况下z3应该能在几个仿真步长内跟上实际扰动如果z3明显滞后优先调大beta03。相比传统的级联PID控制ADRC的处理方式是把扰动直接估计并补偿掉而不是等误差累积到一定程度再由积分项慢慢消除所以恢复时间通常短一个数量级。5. ADRC参数整定的进阶技巧带宽法与b0辨识经验ESO的三个增益beta01、beta02、beta03以及控制器增益如果能按带宽法做一次换算会比盲调快很多。对于连续形式的ESO一般把观测器极点配置在-wo处得到beta013wo、beta023wo^2、beta03wo^3对于离散仿真把连续值直接代进去在步长较小时误差不大但步长大于1ms时我会先乘上一个采样周期h的修正因子或者直接改成离散极点配置。wo通常取控制器带宽wc的3到5倍这样既能保证观测器收敛远快于闭环又不会把噪声放大到影响控制量。控制器部分如果用的是fhan组合带宽和r之间的粗略关系是r约等于wc^2如果用的是fal组合kp可以按wc^2量级起步kd按2*wc量级起步。b0的辨识是所有参数的起点最土的办法是给对象一个阶跃输入测稳态加速度与输入幅值的比值这个比值就是b0。如果对象传递函数是K/((T s 1) s)b0取K即可但实际工程中会有延迟和高频未建模动态b0偏大时ESO的z2会把偏差归因于控制增益太强导致补偿过冲b0偏小时z3会持续反向漂移。我一般的做法是先把b0设为理论值的70%仿真一次观察z3是否平缓若z3里有明显的周期性波动说明b0偏大往下调若z3单调爬升说明b0偏小往上调。当模型允许时还可以在输出端叠加白噪声后再调ESO观察z1的跟随误差和z3的波动幅度找到一个既能让z3紧跟扰动又不会高频抖动的折中。把这一套参数固化到ex_han2.mdl的工作区里再跑一次阶跃确认超调小于5%、调节时间满足指标后再把对象换成带延迟的模型验证鲁棒性即可。本文还有配套的精品资源点击获取