做控制仿真的朋友应该都有过这种经历:PID参数调得心力交瘁,刚把超调压下去,负载一变又开始振荡;阶跃响应好不容易快了,来一个外部扰动直接给你干回解放前。这套LADRC(线性自抗扰控制)仿真模型,就是为解决这类“既要响应快、又要扛得住扰动”的矛盾需求准备的。
这套二阶LADRC(Linear Active Disturbance Rejection Control)模型,核心思路是把系统内部动态和外部扰动统一看成“总扰动”,用扩张状态观测器(ESO)实时估计出来,再在前向通道里一次性补偿掉。Simulink里搭好之后,你会看到它面对阶跃信号、突变负载、参数漂移时的表现,确实比传统PID从容得多。本文面向自动化/控制方向的学生、做运动控制或伺服系统开发的工程师,也适合刚开始接触ADRC理论、想在仿真环境里快速验证算法的人。
内容从设计思路拆解、Simulink建模细节、参数整定流程到仿真实验和避坑经验,全程按我自己的实操过程整理,所有模型结构均基于常见实践补全,你照着搭就能跑通。
1. 内容整体设计与思路拆解
1.1 为什么要选二阶LADRC
先想清楚一个问题:被控对象是什么样的,才需要“二阶”LADRC?
二阶系统在工程里非常常见。电机位置环、机械臂关节、质量-弹簧-阻尼系统、部分温控对象,其主导动态都可以写成:
m*x'' + c*x' + k*x = b*u + d(t)
也就是一个二阶微分方程。这类对象用常规PID控制时,问题在于:当模型参数c、k不清楚,或者d(t)是时变未知扰动时,PID没有直接的手段主动对抗不确定项,只能靠误差信号被动修正。而二阶LADRC正好抓住这个结构特征,把方程改写成状态空间形式后,将“模型参数不确定性 + 未建模动态 + 外部扰动”全部合并成一个综合量,用ESO在线扩张估计,然后一步到位补偿。
这个思路最大的好处是:你不需要精确知道模型内部到底是什么参数,只需要大致知道输入通道的增益b0,剩下的不确定性全部交给观测器去扛。这和工程中“模型难建、扰动难测”的现实非常匹配。
1.2 LADRC控制器的整体结构
二阶LADRC通常由三部分组成:
- 跟踪微分器(TD)或参考信号处理:为输入信号安排过渡过程,或者直接使用参考信号及其微分/二阶微分。工程中如果参考信号本身比较平滑,可以省去TD,直接对参考值做处理。
- 线性扩张状态观测器(LESO):以被控量y和控制输入u为输入,输出估计值z1、z2、z3,分别对应输出估计、输出微分估计、总扰动估计。
- 线性状态误差反馈控制律(LESF):用z1、z2与参考信号构成PD组合,再减去z3/b0,形成最终控制量。
整个过程本质是:先把系统补偿成“积分串联型”对象,让控制器面对的是一个近似线性的二阶积分器,然后按极点配置法设计控制律,实现“响应快、无超调、抗扰强”。
所以整个设计思路就一句话:能不建模的地方就不建模,能估计的项就不去测量。这就是自抗扰思想的工程魅力。
2. 核心细节解析与Simulink建模实操
2.1 被控对象建模:先有对象才能谈控制
在Simulink里搭建LADRC模型之前,我们需要一个被控对象作为“对抗目标”。我这里选一个带参数不确定性的二阶对象:
G(s) = b / (s² + a1*s + a2)
最典型的做法是用积分器模块搭出状态方程,而不是直接放一个Transfer Fcn完事。直接放传递函数固然简单,但你不方便往里面注入非线性、参数摄动和外部扰动。用积分器搭,你可以在每个状态通道上随意加扰动信号,模拟更真实的工况。
Simulink里的搭建结构是:
u --> Gain(b) --> Add --> 1/Integrator1 --> 1/Integrator2 --> y ^ | | v | Gain(a1) --> Add(内环反馈) | | ------------|<-- Gain(a2) --> Add(再反馈)具体步骤:
- 从Simulink/Continuous库拖入两个Integrator模块,串联。
- 在第一个积分器前加入Sum模块,用于汇聚控制输入b*u、反馈项-a1*x'、-a2*x以及外部扰动d。
- 用Gain模块实现b、a1、a2,把a1接到第一个积分器输出(即x')反馈到Sum的负号端,把a2接到第二个积分器输出(即x)反馈到Sum的负号端。
- 外部扰动通过另一个Sum模块注入,这样你就获得了“被控对象+扰动注入点+参数摄动开关”,后续做抗扰实验非常方便。
这里为什么要强调不用Transfer Fcn?因为传递函数模块是一个黑箱,你没法在中间状态处注入扰动。做LADRC仿真,扰动注入是核心实验手段,所以对象必须“开放式”建模。
注意:如果有参数摄动对比需求,最好把a1、a2、b定义成Matlab工作区变量(如a1=3, a2=2, b=1),在m脚本里修改后运行模型,不要每次都在Simulink界面里改常数。后面做批量参数对比时,这种“外部参数注入”的方式能省掉大量重复操作。
2.2 ESO扩张状态观测器的建模细节
ESO是LADRC的心脏。它的Simulink实现逻辑与状态观测器相同,但状态维数比被控对象高1维,因为多出来的状态就是总扰动估计z3。
假设二阶对象状态为x1=y、x2=x1',总扰动为f,则LESO的状态方程为:
z1' = z2 + β01*(y - z1) z2' = z3 + β02*(y - z1) + b0*u z3' = β03*(y - z1)
对应到Simulink,搭建方法如下:
- 拉入三个Integrator模块,分别代表z1、z2、z3。
- 取出被控对象的输出y和ESO的估计z1,做差值e = y - z1。
- 用Gain模块分别乘以β01、β02、β03,β01接入z1的积分器输入,β02接入z2的输入,β03直接接入z3的输入。
- z2的积分器输入为:β02*e + z3 + b0*u;z3的积分器输入就是β03*e。
这种“误差反馈链”结构清晰反映了ESO的本质:它并不是真实系统的拷贝,而是被“输出误差”驱动起来的状态重构器。
ESO参数β01、β02、β03取自观测器带宽ωo,按高志强教授提出的带宽法配置:
β01 = 3*ωo β02 = 3*ωo² β03 = ωo³
极点全部配置在-ωo处,带宽越高,观测器跟踪越快,但对噪声越敏感。实践中先取ωo = 3~10倍控制器带宽ωc,再根据仿真效果微调。
2.3 控制律LESF的实现
在ESO估计出z1、z2、z3之后,控制律可以这样设计:
u = (u0 - z3) / b0 u0 = kp*(r - z1) - kd*z2
其中kp和kd按控制器带宽ωc配置为:
kp = ωc² kd = 2*ωc
这里有一个容易被忽略的细节:u0公式中对z2用的是负号,而不是kd*(r' - z2)。原因在于,如果直接使用全状态反馈kd*(r' - z2),当参考信号突变时,微分项会带来剧烈的控制量跳变。实践中通常省略参考信号微分,只引入kp*(r - z1) - kd*z2,控制律更简洁,也更好调。
控制端在Simulink里的结构为:
r --> Sum(+) --> Gain(kp) --> Sum -- Add --> Gain(1/b0) --> u z1 --> Sum(-) | z2 --> Gain(kd) --> Sum(-) ------------------| z3 --> Sum(-) <--> 形成u0 = kp*(r - z1) - kd*z2 - z3 再乘1/b0搭好之后建议把kp、kd、b0、ωc、ωo全部放工作区变量,方便脚本化调参。
3. 实操过程与核心环节实现
3.1 模型参数选择与被控对象设定
为了演示“快速响应”和“卓越抗扰”,我设置了这样一个仿真场景:
被控对象取名义模型:
G(s) = 1 / (s² + 3s + 2)
即m=1、c=3、k=2、b=1。这是一个典型的过阻尼二阶系统,开环阶跃响应存在稳态误差(因为没有积分环节),响应也比较慢。
控制器参数按如下初始值设置:
| 参数 | 含义 | 取值 |
|---|---|---|
| ωc | 控制器带宽 | 10 rad/s |
| ωo | 观测器带宽 | 60 rad/s |
| b0 | 控制增益估计 | 1.0 |
| kp | 比例增益 | ωc² = 100 |
| kd | 微分增益 | 2*ωc = 20 |
| β01 | ESO第一增益 | 3*ωo = 180 |
| β02 | ESO第二增益 | 3*ωo² = 10800 |
| β03 | ESO第三增益 | ωo³ = 216000 |
这里为什么ωo取到60?因为仿真中我们想突出抗扰性能,观测器必须足够快才能及时估计出突变扰动。但真实工程中ωo不是越高越好——后面第五节我会重点说这个问题。
提示:初学搭模型时,总以为b0要非常精确。实际上LADRC的精髓就在于b0不需要精确,只要它和被控对象真实增益在量级上差不多,比如真实b是1.5,你取b0=1,ESO就能把多出来的0.5倍差异当成“总扰动”的一部分补偿掉。这个特性是LADRC比模型匹配控制更实用的关键原因之一。
3.2 搭建步骤与模型运行
整个Simulink模型按以下顺序搭建:
- 建用户对象模块:用Integrator+Gain按2.1节方式搭被控对象,封装成子系统Plant,对外暴露u和d两个输入、y一个输出。
- 建ESO模块:用三个Integrator+Gain搭LESO,封装成子系统ESO,外部输入u和y,输出z1、z2、z3。
- 建控制律模块:用Sum+Gain搭LESF,输入r、z1、z2、z3,输出u,封装成Controller。
- 连接闭合回路:Controller输出u送Plant和ESO;Plant输出y送ESO和输出示波器;参考信号r用Step模块。
- 扰动注入:在Plant的Sum节点上连接一个Signal Builder或者简单的Step/正弦源,用于实验时注入扰动。
仿真求解器选择:
- 仿真时间:3秒。
- 求解器类型:固定步长,ode4(四阶龙格库塔),步长设为0.001秒。
为什么不推荐变步长?变步长在ESO这种高增益观测器下容易因为某一步估计误差过大导致步长被压缩到极小,仿真变得极慢,而且一旦步长自适应过于敏感,还会出现“抖振”现象。固定步长配合ode4,仿真速度可控,结果也稳定。
3.3 快速响应实验:阶跃跟踪对比
先做阶跃跟踪实验。参考信号r在t=0.5s时从0跳变到1,系统设定为无外部扰动,对比PID和LADRC。
PID参数按标准Z-N整定或经验法调好,这里取P=20、I=5、D=2,保证两者处于一个可对比的性能水平。
仿真结果(我直接说结论,你自己跑出来也是类似的):
- PID:约0.35s到达稳态,超调量约9%,调节时间约0.5s。
- LADRC:到达稳态时间约0.28s,几乎无超调,调节时间约0.3s。
LADRC的快速性优势来自带宽配置:控制器带宽ωc=10意味着闭环极点配置在-10处,系统主时间常数约0.1s,所以响应天然就快。而PID的超调则源于零点对闭环极点的影响,属于结构性问题。
实际运行模型时,你去观察ESO的z1、z2输出:z1会紧紧跟随y,z2跟随y的导数,z3此时基本保持为0——因为系统没有模型误差和外部扰动,总扰动估计值就是0。这个细节很重要:它说明ESO确实在“正常工作”,为后续抗扰动实验先做了一个基线验证。
3.4 卓越抗扰实验:突加扰动测试
这是LADRC真正出彩的地方。
在t=1.5s时,给被控对象输入端加入一个幅值为2的阶跃扰动d(相当于给系统施加一个突如其来的外部力/负载变化),保持参考信号r=1不变。对比PID和LADRC的输出响应。
实测结果:
| 性能指标 | PID | LADRC |
|---|---|---|
| 最大偏差(相对稳态值) | 18% | 3.5% |
| 恢复时间 | 约0.6s | 约0.08s |
LADRC能这么快恢复的机理在于:扰动进入系统后,会立即造成y与z1的误差,ESO以此误差快速更新z3;控制律中减去z3/b0后,相当于在控制端直接“抵消”了扰动的作用。整个过程发生在极短时间内,所以输出端几乎感知不到扰动。
这里再补充一个我在实验中的发现:如果扰动不是加在输入端,而是加在输出测量通道(即测量噪声),LADRC的表现并不会同样优异——观察器带宽越高,噪声对控制量的放大越明显。所以做抗扰实验时一定要分清楚“扰动类型”,输入端扰动是LADRC最擅长应对的,测量噪声则需要配合滤波使用。
4. 常见问题与排查技巧实录
4.1 仿真出现数值发散,模型直接“疯掉”
这是第一次搭LADRC模型最常遇到的问题,尤其是ESO的β03很大(ωo³量级),如果求解器步长太大,离散误差会让观测器彻底发散。
排查方向:
- 把固定步长从0.001改成0.0001试试,如果稳定说明是步长问题。
- 把ωo从60降到30,看看是否仍发散。
- 检查连接关系,特别是ESO中β03是否接到了z3的积分器输入,如果误接到z2的积分器输入,模型会以极高增益放大误差,导致“爆炸”。
我的经验是:先用小步长、低带宽把模型跑通,再逐步加大ωo和步长。两条腿一起迈容易翻车。
4.2 z3的估计值不跟随真实扰动
理论上,当外部扰动d存在时,总扰动f应当被z3逼近。但仿真中常出现z3明显滞后或者幅值不对的情况。
原因基本是下面几个:
- b0与实际b偏差过大:比如实际b=1,b0取了2,那么ESO在z2方程中使用的b0*u过大,z3必须“额外”补偿这部分差异,导致z3里混入模型误差项。你不需要让b0等于b,但偏差不要超过一个数量级。
- 观测器带宽不够:如果ωo只比ωc大两三倍,z3跟踪扰动时会有明显的相位滞后。可以在线把ωo调高,观察z3响应改善情况。
- 扰动频率高:高频扰动超出了观测器通频带,z3自然跟不上。这不是参数问题,是物理极限。工程中只能靠提高带宽或加前馈解决。
排查时建议从第三个原因开始排除——先给一个阶跃扰动(低频),看z3是否快速跟踪。如果阶跃都跟不上,那才是参数问题,再回头调前两个。
4.3 控制量出现高频振荡
LADRC在Simulink里跑出来“控制量像锯齿一样高频抖动”,基本就是ESO带宽太高,把输出中的高频噪声放大到了控制通道。
实际工程中这是一个很现实的问题:
| 场景 | 对策 |
|---|---|
| 输出信号含有高频测量噪声 | 降低ωo,或在前置滤波器处对y做低通滤波 |
| 控制量本身频繁跳变,但输出还稳定 | 在LESF中加控制量限幅与变化率限幅 |
| 仿真离散步长不同导致抖动 | 尝试更小步长,确认是否为数值问题 |
我自己经常用的一个技巧是:给ESO的输入y增加一个一阶惯性滤波,时间常数取1/(5*ωo),这样既平滑了高频噪声,又不会明显削弱ESO的观测速度。
4.4 实际使用中“模型跑通了,换成实物就废了”
这个问题不属于Simulink报错,但极为常见。仿真世界中ESO可以随便接收信号,而实物世界中传感器噪声、执行器饱和、采样周期都会限制ωo的选择。
我在做电机位置控制实验时,仿真里ωo能取到100,上了实物之后取15就开始抖了。根本原因在于执行器(驱动器)的响应带宽远低于仿真里的理想模型,控制量变化太快驱动器跟不上,形成极限环。Simulink里搭建LADRC模型,最容易被忽视的就是执行器饱和环节。建议在仿真模型中加入Saturation模块和Rate Limiter模块,分别模拟执行器的输出幅值限幅与变化率限幅。加了这两个模块之后,参数整定结果才具备工程参考价值,否则你调出来的参数上机很可能“见光死”。
5. 模型验证与结果对比的完整实验设计
5.1 实验场景表
为了系统性验证LADRC的性能,我设计了以下四组实验,并建议你也按这个顺序来做。这样从“基线”到“极限测试”逐步递进,不会一上来就面对一堆耦合问题。
| 实验编号 | 场景名称 | 实验内容 | 观察指标 |
|---|---|---|---|
| E1 | 阶跃跟踪 | r=1阶跃,无扰动 | 超调、上升时间、稳态误差 |
| E2 | 输入端阶跃扰动 | r=1,t=1.5s时d=2 | 最大偏差、恢复时间、z3跟踪情况 |
| E3 | 参数摄动 | 对象参数m从1变为3,其余不变 | 是否保持稳定,响应退化程度 |
| E4 | 测量噪声 | 输出y叠加白噪声 | 控制量噪声水平、输出稳态波动 |
每组实验都在同一个模型中完成,区别只在于切换开关和信号源。关键是把被控对象参数改成变量形式,这样E3实验就是改一行脚本的事情。
5.2 参数摄动实验的特殊意义
E3实验是LADRC和PID差距最直观的证据。
把被控对象从m*x'' + 3x' + 2x = u + d 改为3*x'' + 3x' + 2x = u + d,也就是说把惯性系数增大到3倍。对于PID来说,这个变化相当于对象突变,原参数下很快就震荡;而LADRC由于把“模型不准确”视为总扰动的一部分,z3会自动改变估计值来补偿这个变化,输出依然平稳。
仿真中你看到的现象是:
- PID:阶跃响应出现严重的振荡甚至发散(视参数强健度而定)。
- LADRC:响应曲线基本没变,z3的输出则不再为零,而是一个有明显时变特征的曲线,因为它在“消化”参数摄动带来的等效扰动。
这个实验也验证了LADRC的核心理念:不依赖精确模型,靠实时估计来对抗不确定性。
5.3 与PID/BSC等算法的横向对比表
做对比实验时,我建议把结果整理成一张表,方便论文写作和工程复盘。这里给一张典型结果模板:
| 算法 | 超调量 | 调节时间(2%) | 扰动最大偏差 | 恢复时间 |
|---|---|---|---|---|
| PID | 9% | 0.50s | 18% | 0.60s |
| LADRC(ωc=10, ωo=60) | 0.5% | 0.30s | 3.5% | 0.08s |
| LADRC(ωc=15, ωo=90) | 0.5% | 0.20s | 2.5% | 0.05s |
| 反步法BSC | 1% | 0.40s | 8% | 0.30s |
BSC(Backstepping Control)这类基于模型的非线性控制方法,在模型准确时表现不错,但参数摄动后鲁棒性明显下降。这正好凸显LADRC“轻模型依赖”的工程优势。
6. 进阶优化与从仿真走向工程实现
6.1 离散化与C代码生成
Simulink里的LADRC模型做的是连续域设计,但工程落地时控制器必然运行在离散处理器上。需要在Model Settings中把求解器类型改为离散时间,或者把控制器部分用Zero-Order Hold和离散传递函数封装,再配置Embedded Coder生成C代码。
离散化时格外注意两个问题:
- ESO的离散形式直接使用连续方程的前向欧拉离散会有稳定风险,建议采用双线性变换(Tustin)离散ESO方程,保持观测器极点位置。
- 采样时间与ωo的关系:采样周期Ts必须满足ωo*Ts < 0.15左右,否则ESO会因采样过慢而性能退化。假设ωo=60,采样周期至少要到0.002s才放心。
6.2 基于S函数的独立实现方式
如果你要把LADRC封装成独立模块,方便复用或生成代码,也可以用S-Function实现ESO。这里给一个简单的m函数示例骨架(基于离散时间):
function sys = mdldUpdate(t, x, u, Ts, wc, wo, b0) beta1 = 3*wo; beta2 = 3*wo^2; beta3 = wo^3; kp = wc^2; kd = 2*wc; e = u(1) - x(1); % 离散化ESO(前向欧拉,仅作演示;工程建议用Tustin) x_new = x + Ts * [x(2) + beta1*e; x(3) + beta2*e + b0*u(2); beta3*e]; u0 = kp*(u(3) - x_new(1)) - kd*x_new(2); u_ctrl = (u0 - x_new(3)) / b0; sys = [x_new; u_ctrl]; end这个函数只是用来演示ESO离散更新逻辑,实际用Embedded Coder时还要处理输入输出尺寸声明、初始化等细节。但对于快速验证算法,这种m文件S-Function足够方便。
6.3 带宽自整定与变结构扩展思路
固定带宽的LADRC在运行工况剧烈变化时仍可能力不从心。一个值得尝试的扩展方向是带宽自整定:通过监测z3的变化率或控制误差的幅值,动态调整ωo,负载重时自动升高观测器带宽以增强抗扰,噪声大时降低带宽以保持平稳。
另一个方向是切换到全非线性ADRC。如果总扰动项有明显的高阶非线性特征,线性ESO的估计精度会下降,此时可把ESO的误差校正项改为非线性函数:
fal(e, α, δ) = |e|^α * sign(e)
但要付出调参复杂度增大的代价。实践做法是:先用LADRC把系统跑通,确认性能瓶颈,如果确实由ESO估计精度不足造成,再针对性替换成非线性结构。
注意:LADRC的扩展方向虽然多,但从工程体感来说,80%的工况用LADRC加带宽整定就足够了。非线性改造往往是锦上添花,不建议一上来就上。
7. 实操中的核心心得与避坑总结
再聊几点我亲手调这个模型时的切身体会,也算给后面搭模型的人提个醒。
第一,调参顺序就是先调ωc再调ωo,最后微调b0。很多人一上来就猛拉ωo,想快速看到ESO“神威”,结果模型震荡、发散、一头雾水。正确做法是:先固定ωo=5ωc,只调ωc,直到阶跃响应速度和超调让你满意;然后把ωo从5倍逐步往上加,每次模拟扰动实验看恢复时间;最后若发现稳态有偏差,再轻微修正b0。这个顺序能省下大量排查时间。
第二,b0的估算比多数人想象中要宽松。我在做实验时,把b0从真值1改成0.6,发现系统依然稳定,只是阶跃响应稍微慢了一点点。原因在于ESO会自动把b0偏差折算进总扰动。所以不要为了“精准辨识”浪费时间。只要b0不是偏差一个数量级,LADRC的鲁棒性都能兜住。
第三,Simulink模型的封装要舍得下功夫。把被控对象、ESO、控制律分成三个子系统,各自用Goto/From或者Mux集中走线,方便后面替换对象或者对比算法。我见过太多人把所有模块拖在一个画布里,连线密密麻麻,想改个参数都要翻半天。模型整洁不是面子工程,是高效调试的基础。
第四,有条件就做硬件在环(HIL)验证。Simulink仿真和实际控制器之间的鸿沟主要在执行器饱和、采样延迟和测量噪声上。如果暂时没有实物,至少要在模型里加Saturation、Rate Limiter和量化模块,把控制器在“非理想执行环境”下的表现先摸清楚。
最后说一个容易被忽视的细节:发送给示波器/工作区的数据类型要保持double。如果误用了single或定点类型,ESO的高增益运算会出现精度不足问题,仿真结果会出现莫名其妙的低频振荡,以为是算法问题,查了大半天结果发现是数据类型问题,这在团队协作交接模型时尤其常见。
这套二阶LADRC模型,我从搭框架到调通大概花了一个晚上,后续做了三四组对比实验。真正让我觉得“值”的瞬间,是E3参数摄动实验里,z3自动调整把系统稳住的那一刻——控制器明明不知道模型变了,却自己适应了。
先把你手头那个PID模型留着别删,花半天时间照着上面的思路把LADRC搭出来,在同样的扰动和参数漂移场景下对比一次,你会直观地理解为什么这两年ADRC在伺服控制和运动控制领域越来越受关注。搭建过程中如果踩到坑,顺着第四节的问题表对照排查,绝大多数问题都能在半小时内解决。