news 2026/10/5 1:37:47

ADRC调参实战:Simulink搭建步骤与完整参数表全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ADRC调参实战:Simulink搭建步骤与完整参数表全解析

开头

前阵子在技术群里看到有人问:“PID 调了这么多年,好歹有套成熟口诀,ADRC 有没有类似的参数表?能不能别一上来就讲公式?”我当时回了一句:能,而且早该有人整理了。自抗扰控制(ADRC)的火爆不是没道理,它把“模型不确定性”和“外部扰动”统一扔给扩张状态观测器去扛,比 PID 多了一双看透扰动的眼睛。但对绝大多数刚接触它的人来说,最劝退的不是韩京清教授那篇经典论文的数学推导,而是 Simulink 里那堆非线性函数、观测器增益、带宽参数凑到一起之后,根本不知道先动哪一个。

这篇文章就是来拆掉这层墙的。我会完全站在“调参实战”的角度,手把手带你在 MATLAB/Simulink 里把 ADRC 三大件——跟踪微分器(TD)、扩张状态观测器(ESO)、非线性状态误差反馈(NLSEF)一个一个搭出来,每一处模块怎么接、参数初始值怎么定、调参顺序怎么走,全给你捋清楚,最后直接给一份能抄作业的完整参数表。适合刚接触 ADRC 的学生、刚用 ADRC 做项目验证的工程师,以及那些“PID 已经很熟但想迈出下一步”的同行。


1. ADRC 无非三件事:安排过渡、做估计、做反馈

很多人一打开 ADRC 的资料,就被“自抗扰”“扰动补偿”“扩张状态”这些词震住了,其实拆开看就三个功能模块,每个模块解决一个问题。

1.1 三个模块各自干什么活

先说 TD(跟踪微分器)。它的任务很朴素:给你那个阶跃信号“安排过渡”。PID 最让人头疼的地方是给定值突变时,误差瞬间拉满,控制器会猛输出,系统跟着超调。TD 能把一个阶跃信号处理成一条平滑的过渡曲线,同时还能提取出它的“速度”,也就是微分信号。换句话说,TD 相当于给信号加了个“缓启动”加“滤波器”,让你不再需要对给定值做一堆前置处理。

然后是 ESO(扩张状态观测器)。这是 ADRC 的灵魂。它不只能观察到系统的输出和输出的一阶导,还能额外“扩张”出一个状态:把系统内部建模误差、外部扰动、未建模动态全部打包成一个“总扰动”实时估出来。你不需要精确知道干扰是摩擦力、负载突变还是模型参数漂移,ESO 会自动告诉你“等效的总扰动大概是多少”,然后你再拿这个估计值去补偿掉它。

最后是 NLSEF(非线性状态误差反馈)。它把 TD 给出的光滑给定和微分,与 ESO 给出的输出估计和速度估计相减,得到误差,再做一个“非线性组合”。这个环节替代了 PID 的线性 P、D 组合,用非线性函数让“小误差增益大、大误差增益小”,效果比固定增益更灵活,相当于低速时紧抓、高速时放松,很多工况下能显著减小超调。

1.2 数学模型先写下来:从公式到 Simulink 的映射关系

要搭 Simulink,第一步是把数学结构落到方程。以一个常见的二阶系统为例,ADRC 的核心方程如下:

TD 离散形式:

x1(k+1) = x1(k) + h * x2(k) x2(k+1) = x2(k) + h * fhan(x1 - v, x2, r, h0)

这里的fhan是最速控制综合函数,里面带两个可调参数:r决定跟踪速度快慢,h0是滤波因子,能抑制微分噪声。h是仿真步长。

ESO 离散形式:

e = z1 - y z1(k+1) = z1(k) + h * (z2(k) - β01 * fal(e, α1, δ)) z2(k+1) = z2(k) + h * (z3(k) - β02 * fal(e, α1, δ) + b0 * u) z3(k+1) = z3(k) - h * β03 * fal(e, α2, δ)

z1是输出估计,z2是速度估计,z3是总扰动估计。fal是韩京清提出的非线性函数,β01、β02、β03是观测器增益,b0是控制增益估计(也就是被控对象输入到加速度之间的增益,你能给个大概值就行)。

NLSEF 控制量生成:

e1 = x1 - z1 e2 = x2 - z2 u0 = kp * fal(e1, α1, δ) + kd * fal(e2, α2, δ) u = u0 - z3 / b0

这里的kp、kd相当于非线性 PD 的增益,u0是“不补偿扰动时的基础控制量”,最后一项- z3 / b0就是扰动补偿。看清楚这个结构之后,调参逻辑就出来了:先让 TD 把给定信号平滑好,再让 ESO 把扰动估计准,最后用 NLSEF 把误差拉回来。三块各管各的,调参不是一锅粥,而是一个递进过程。


2. Simulink 里搭 TD:三种做法里我最推荐这种方式

TD 是整个结构里最容易搭的,但越简单越有人搭错。我见过很多人直接拿一个传递函数r/(s+r)去“等效”跟踪微分器,结果发现跟踪没问题,微分信号全是噪声。这里我给出三种做法,并讲讲各自的优劣。

2.1 用 MATLAB Function 写 fhan,最省事

我的第一选择是:在 Simulink 里拖一个MATLAB Function模块,把fhan函数用代码写进去,再用两个Integrator模块搭离散积分。MATLAB Function的好处是代码可读性高,调参的时候一目了然,不用在模块图里穿梭。

下面是一份可以直接用的fhan代码:

function fh = fhan(x1, x2, r, h0) % 韩京清最速控制综合函数 d = r * h0^2; a0 = h0 * x2; y = x1 + a0; a1 = sqrt(d * (d + 8 * abs(y))); a2 = a0 + sign(y) * (a1 - d) / 2; a = (abs(y) > d) * a2 + (abs(y) <= d) * (a0 + y); fh = -r * (abs(a) > d) * sign(a) - r * (abs(a) <= d) * a / d; end

这段代码在很多项目里都会被复用,建议存成一个.m文件放到工作目录里,Simulink 的MATLAB Function模块直接调用。代码里用了sign()函数,注意 Simulink 里sign(0)的处理是返回 0,在原版公式里这一步没问题。

2.2 在模型里搭出 TD 的完整结构

有了fhan,搭 TD 就顺手了。按下面步骤来:

  1. 拖入两个Integrator模块,一个输出x1(跟踪信号),一个输出x2(跟踪信号的微分),注意给两个积分器设置合适的初始值,一般设成被控对象初始输出值,这样跟踪起点和实际输出一致,不会产生启动冲击。
  2. 将外部给定信号v与x1相减,送入MATLAB Function模块的第一个输入。
  3. x2直接送入MATLAB Function的第二个输入。
  4. fhan的输出乘以仿真步长h,作为第二个积分器的输入增量。
  5. 第二个积分器输出x2再乘h加到x1的积分器上,完成离散积分。

简化写法是直接在一个MATLAB Function里把两步离散积分都写完,输入只需要v和上一次的x1、x2,输出x1_next和x2_next,然后再用Unit Delay模块形成状态迭代。这种方式更适合做代码生成的同学,谁用谁知道。

2.3 r、h0 到底怎么定

r越大,跟踪越快,但过头了,过渡过程的“急加速”会让执行器压力变大,还会放大噪声。h0是滤波因子,比仿真步长h稍大一些,能有效滤掉微分信号中的高频噪声。我的经验是:

  • 先设r = 50起步,观察跟踪曲线,再逐步增加;
  • h0一般取h的 2~10 倍,比如仿真步长h = 0.001时,h0从0.005开始试;
  • 如果对象响应本身比较慢,r别超过对象可达速度的 2 倍,否则会看到输出“追不上”的现象。

自己的体会是,TD 参数不会特别敏感,它的设计自由度比 ESO 高得多,所以千万别在这儿卡太久。


3. ESO 搭起来不难,难的是把 β 参数从经验值变成稳定值

ESO 是整个 ADRC 中最值钱的部分。没有它,TD 加 NLSEF 就是个非线性 PD;有了它,系统才有了“看见扰动并补偿掉”的能力。但 ESO 也是最容易让人弃坑的部分——β 一不对,观测器输出就发散或者抖得像筛子。

3.1 ESO 的 Simulink 实现方式

ESO 的状态方程有三个,我直接在MATLAB Function模块里写离散递推,避免用连续积分器带来的代数环问题。代码结构如下:

function [z1_next, z2_next, z3_next] = eso_update(z1, z2, z3, y, u, h, beta01, beta02, beta03, b0, alpha1, alpha2, delta) % fal 函数 fe = fal(z1 - y, alpha1, delta); fe1 = fal(z1 - y, alpha2, delta); z1_next = z1 + h * (z2 - beta01 * fe); z2_next = z2 + h * (z3 - beta02 * fe + b0 * u); z3_next = z3 - h * beta03 * fe1; end function f = fal(x, alpha, delta) % 非线性函数 fal f = (abs(x) > delta) * abs(x)^alpha * sign(x) + (abs(x) <= delta) * x / delta^(1 - alpha); end

这里把z1、z2、z3作为状态,用Unit Delay模块把上一次的估计值送回来。y接被控对象的测量输出,u接你计算出的控制量。注意一点:b0不是真实增益,是你对被控对象增益的估计,ESO把“实际增益与 b0 的差异”也一并算进总扰动里,所以b0不需要精确匹配,但不能差一个数量级。

3.2 带宽法:一秒估算 β01、β02、β03

很多论文里给了 β 的公式,但对新手来说不友好。这里上一个工程上最好用的方法——带宽法。ESO 的观测器增益可以按下面规律设定:

  • 设定观测器带宽ωo,一般取控制器预期带宽ωc的 3~10 倍;
  • β01 = 3 * ωo
  • β02 = 3 * ωo^2
  • β03 = ωo^3

比如你预估系统闭环带宽在 20 rad/s 左右,那ωo取 100 rad/s,则 β01 = 300,β02 = 30000,β03 = 1000000。

这个公式我一用就是好几年,几乎没有失手。但实际仿真时,ωo不是越大越好。带宽大了,ESO 响应快,扰动估计也准,但会把测量噪声放大,导致控制量高频率抖动,甚至把仿真步长逼得非常小才能稳定。我的经验是:先用带宽法算出初值,然后往小调,直到 z1 能紧贴 y 且 z2 不抖为止。

3.3 为什么 z3 总是不收敛

很多人遇到的现象是:前两个状态 z1、z2 跟踪得很好,但 z3(扩张状态)抖得离谱或者收敛不到真实扰动值。这个问题的原因有几种,按概率排序:

  1. b0 给错了。b0 偏小,ESO 会把“真实增益和 b0 的差值”当作扰动的一部分,z3 会明显偏离真实扰动;b0 偏大,补偿过头,系统会震荡。
  2. 非线性函数的 δ 取得太小。δ 太小,fal在零点附近增益奇高,微小的输出噪声就会被放大,z3 跟着抖。把 δ 取在0.01 ~ 0.1之间,问题会缓解很多。
  3. 仿真步长太大。ESO 本身是一个高增益观测器,步长大了很容易数值发散,尤其是 β03 是 ωo 的三次方,在 ωo 超过 200 后对步长非常敏感。建议先试试把步长缩小 10 倍,如果 z3 变稳,说明就是这个原因。

我在实际项目里最喜欢的做法是:先把 ESO 单独拎出来做开环测试。断开 TD 和 NLSEF,或者干脆给固定控制量 u,观察 z1、z2 是否能跟上实际输出(比如一个正弦激励),z3 是否稳定在一个合理范围。这个过程能帮你把观测器的内部问题先解决掉,再去做闭环联调。这一步千万别跳。


4. NLSEF 和控制量生成:把非线性写进 MATLAB Function

NLSEF 结构比 TD 和 ESO 简单,但它决定了系统的“反馈性格”。有的人只改了 kp、kd 就感觉 ADRC 不过如此,其实是非线性函数的几个参数还没吃透。

4.1 控制律结构上面升一级的写法

先看最基本的控制量生成:

function u = nLsef(e1, e2, z3, b0, kp, kd, alpha1, alpha2, delta) u0 = kp * fal(e1, alpha1, delta) + kd * fal(e2, alpha2, delta); u = u0 - z3 / b0; end

fal函数和 ESO 里那个是同一个,可以直接复用。e1 = x1 - z1,其中x1是 TD 生成的跟踪信号,z1是 ESO 估计的系统输出;e2 = x2 - z2,是跟踪速度和输出速度估计的误差。

控制量的物理含义很清晰:先把 PD 型控制量算出来,再从里面减去z3 / b0,把 ESO 看到的扰动直接抵消掉。这个结构直接决定了 ADRC 为什么对模型不精确不敏感——因为大多数模型误差都被 z3 兜住了。

4.2 kp、kd、α1、α2、δ 的整定逻辑

NLSEF 参数里,kp、kd最像 PID,但它俩不是直接对应比例和微分增益,而是配合非线性函数工作的。

  • kp决定主回路刚度。太小系统软绵绵,误差收敛慢;太大则会出现“哒哒哒”的抖振。
  • kd是阻尼,用来压住超调。调大 kd 能迅速稳定过渡过程,但太大相当于给系统加了很强的“刹车”,响应会变慢。
  • α1一般取0.5到0.75,α2取1.25到1.5。α1小于 1 意味着误差小时增益大,误差大时增益被压住;α2大于 1 则让微分误差在小误差区较小,避免微小的抖动引起输出乱跳。
  • δ是线性区间宽度。它设定了“误差小到什么程度就按线性处理”,一般取0.01 ~ 0.1。取太大会失去非线性优势,取太小则容易抖动。

一个实用的整定出口是:先把 α1、α2、δ 固定为常见值(比如0.75、1.25、0.01),然后只动 kp、kd,等系统响应形态满意了,再微调非线性参数。不要上来就七个参数一起调,那等于给自己挖坑。


5. 一次完整调参实录:从被控对象到全套参数表

理论讲完了,下面来一段完整的实战。我会选一个典型二阶对象,走一遍完整的调参流程,最终给出一份可直接复用的参数表。

5.1 被控对象选型与仿真环境搭建

我选的被控对象是一个带延迟的直流电机转速模型,等效传递函数写为:

G(s) = 400 / (s^2 + 50s)

这个对象有惯性和阻尼,比较接近工程常见的转速环。仿真用定步长h = 0.001,求解器选ode4(四阶龙格库塔),整个 Simulink 模型大概结构是:TD 接收阶跃给定,输出x1、x2;ESO 接收对象输出y和控制量u,输出z1、z2、z3;NLSEF 把误差组合成u,并叠加扰动补偿。

5.2 调参顺序:先 TD,再 ESO,最后 NLSEF

我调参从来不是上来就全接上,而是分三步走:

第一步,把对象输出反馈直接接到一个 PID 控制器上,或者干脆开环,先弄清楚对象本身的响应快慢和增益量级。算出稳态增益大概是400/50 = 8,时间常数约0.04s。这说明对象响应相当快,适合把 TD 的r定在 100 到 200 之间。

第二步,接上 TD,单独观察x1是否平滑无超调地跟踪阶跃,x2是否光滑。如果有高频毛刺,加大h0。

第三步,断开 TD 到 NLSEF 的误差通路,把 ESO 的z1接到跟踪信号x1上,给 ESO 一个电机的实测输出和真实控制量,跑一段匀速工况,观察z3是否收敛到一个常数。这一步能确认 ESO 没有发散、z3 的估计值符合物理直觉。

第四步,把环路全部闭合,用带宽法给出 β 初值,再逐项微调。

5.3 完整参数表:照着填就能跑

下面这份参数表是以h = 0.001、定步长为前提的,如果你用的是变步长,请把 TD 和 ESO 的离散积分改为连续积分器,或者保持定步长仿真,否则参数意义会漂移。

TD 参数表

参数推荐值说明
r120跟踪快慢,对象响应时间约 0.04s 时设为 100~200 均可
h00.005滤波因子,为仿真步长的 5 倍
x1 初值0与对象输出初值一致
x2 初值0微分初值一般设 0

ESO 参数表

参数推荐值说明
ωo150观测器带宽,先设 3~10 倍控制器带宽
β014503 * ωo
β02675003 * ωo^2
β033375000ωo^3
b04对象增益估计值,真实增益约 8,这里故意取一半
α1(ESO)0.5非线性函数指数
α2(ESO)0.25总扰动状态的非线性指数
δ(ESO)0.01线性区间宽度

窄和 ESO 里的 α 取值可以不同,别被我上面的参数表固定住思维。

NLSEF 参数表

参数推荐值说明
kp300主反馈增益,从 50 开始加
kd40阻尼增益,先给小值再加
α1(NLSEF)0.75误差非线性指数
α2(NLSEF)1.25误差微分非线性指数
δ(NLSEF)0.01线性区间宽度

合起来之后,阶跃给定从 0 跳到 1,系统的超调量大约在 3% 以内,调节时间约 0.1s。这个表现已经比同环境下调好的 PID 更干净,更重要的是:我把对象增益从 8 变成 12 或者从 8 变成 6,ADRC 依然能维持相近的动态性能,这就是 z3 补偿在起作用。

如果仿真中出现了控制量饱和问题,记得在u输出端加一个Saturation模块,并且把饱和后的实际控制量再送进 ESO 和对象,否则 ESO 估计的总扰动会出现偏差,因为 ESO 内部用的是“理想控制量”而对象真实接收的却是限幅后的值。


6. 我在 ADRC 仿真里踩过的五个坑,你大概率也会踩

这部分不写不知道,写下来全是泪。下面五个坑我都在项目里真实遇到过,每一个都花了不少时间去排查,现在整理出来,希望能帮你绕开。

6.1 用了 Derivative 模块提取信号,全盘皆输

有人为了省事,直接在 Simulink 里用Derivative模块对测量输出求导得到速度信号,再喂给 ESO 或直接用。这是大忌。实际测量信号一定有噪声,Derivative会把噪声放大到不可用的程度。正确的做法是:把速度交给 ESO 去估计,也就是用z2而不是对 y 求导。ESO 的设计初衷就是让你不碰导数。

6.2 定步长和变步长混用,参数跟着漂

我在同一套模型上先用变步长调好了参数,换成定步长后,系统直接发散。原因是 TD、ESO 的离散迭代方程里带步长h,如果求解器实际步长和代码里写的h不一致,整个模型等于在算另一套系统。建议从一开始就定步长仿真,把所有离散模块的h设成同一个值,别让 Simulink 自动变步长。

6.3 饱和和限幅的位置放错

控制量输出常常要限幅,但限幅之后不能只把限幅前的值送给执行器,而要把限幅后的值同时反馈给被控对象和 ESO。很多人只在执行器那边加了个Saturation,ESO 看到的还是限幅前的u,结果 z3 疯狂往上飘,因为它把“执行器没执行的那部分”也当成了扰动。这个细节最容易让人怀疑 ESO 是不是坏了。

6.4 α 参数乱改,非线性变成非稳定

fal函数里的 α 不是随手填的。α 小于 1 的部分会让误差很小时增益很大,如果 α 太小,比如 0.1,系统容易产生高频自激。α 大于 1 的部分又会让大误差时增益过大,导致发散。建议先使用上面参数表里的推荐值,等整个系统跑顺了,再小幅调整,每次不超过 0.1。

6.5 b0 的估算偏差太大,扰动补偿形同虚设

b0是唯一一个需要你对被控对象“有点感觉”的参数。你不需要精确知道它,但方向不能错。比如对象稳态增益是 8,你把 b0 设成 800,ESO 会把“实际对象远没有你想象的那么灵敏”当成巨大的负扰动,系统输出会瘫软无力;反过来 b0 设成 0.01,ESO 会认为对象灵敏度太低,拼命补偿,结果就是震荡外加控制量饱和。我一般用“对象稳态增益 / 对象时间常数”粗略估算 b0 的初值。

刚接触 ADRC 的同行,往往会花很多时间研究非线性函数的细节,实际上工程调参最重要的还是那个铁三角:TD 的 r、ESO 的带宽(对应 β)、NLSEF 的 kp/kd。把这几个抓住了,剩下的无非是在这个骨架上润色。调参过程中记录每一次参数组合下的阶跃响应曲线,多攒几组,你会慢慢建立起直觉——哪些地方该加带宽,哪些地方该加阻尼,一眼就能看出来。我如今回头再看当初被 ADRC 公式劝退的日子,最大的感触是:它没那么神,但也确实没那么难。搭好模型,按顺序调参,你会很快收获一条比 PID 更沉着的阶跃曲线。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 1:35:56

Simulink生成F28335 DSP代码全流程:从环境配置到烧录运行

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 1:34:38

法律人AI工具深度横评:Kimi Work与WorkBuddy实战对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 1:33:25

200张图的道路交通锥YOLO数据集实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 1:33:10

工业级MRAM与AVR单片机SPI驱动实战:MR25H40CDF数据记录方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 1:32:29

STM32从入门到实战:选型、开发环境与调试避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 1:32:03

STM32L031C6驱动MR25H40CDF MRAM:工业数据采集存储方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华