
最近不少同学问我同一个问题“网上都说LADRC比PID强我是不是该从PID转过去学它”如果你也想在Simulink里把LADRC和PID放在同一张图上看看它们的跟踪响应和抗扰动能力到底差多少那这篇就是给你准备的。我会从模型搭建开始一步步讲清楚LADRC的原理、参数整定和仿真对比过程不跳步不藏着掖着适合刚接触自抗扰控制的读者也适合想把PID调参经验再往前推一步的工程师。我自己的体会是只看公式很难把LADRC用起来但如果能在Simulink里亲手搭一遍一边调参一边观察扩张状态观测器的输出很多直觉性的东西会一下子建立起来。这篇文章的内容就是我当时踩坑、看文献、反复仿真才总结出来的一套入门路线希望能帮你少走弯路。1. 为什么要把LADRC和PID放在一起比1.1 PID的本质是“事后纠错”LADRC是“事前估计”PID控制器用的是误差的比例、积分、微分。比例项对当前误差做出反应积分项对历史误差累积做出反应微分项大致预测误差变化趋势。这里面的问题是PID并不知道外部扰动到底是什么它只能等系统输出偏离了设定值通过误差变化来“倒推”出一个合适的控制量。积分项尤其典型——它一直在慢慢攒“历史欠账”遇到持续负载变化时要先偏差再慢慢把输出顶上去响应总是慢半拍。你在实际系统里应该也见过这种情况温度炉子加热时打开炉门冷空气灌进来温度出现了偏差PID脑子里没有“门开了”这个信息只能根据偏差一点点调整占空比。如果扰动大、变化快积分项还没来得及补偿温度已经掉了一大截。这就是PID“被动纠错”的局限。LADRC的思路则完全不同。它把外部扰动、未建模动态、参数摄动统统看成一个“总扰动”再用扩张状态观测器把这个总扰动实时估出来然后在前向通道里反向补偿掉。注意这个“扩张状态”的理解普通状态观测器只能估计系统的原始状态变量而扩张状态观测器额外增加了一个状态专门用来代表总扰动。这个状态不是直接测量的而是通过输入输出数据“推算”出来的属于一种在线主动估计。所以你可以把LADRC的补偿过程理解成系统里有个看不见的“捣乱小孩”PID只能等他搞了破坏、看到结果再去补救而LADRC派了一个“监控摄像头”一直盯着他小孩刚伸手就把他的力气给抵消掉了。这个比喻并不夸张仿真里你能清清楚楚看到加入同样的负载扰动LADRC的输出偏差峰值往往只有PID的几分之一。1.2 为什么入门阶段最推荐用Simulink做对比入门阶段最大的问题不是“不知道LADRC公式”而是“不知道这些状态变量在系统里到底怎么跑”。Simulink把传递函数、积分器、增益、示波器都摆在桌面上你可以把每一步信号拉出来看设定值、反馈值、ESO的三个状态、控制量、扰动信号全部可视化。比如你可以把扩张状态观测器估计出的总扰动信号z3和实际注入的扰动信号放到同一个Scope里一眼就能看出估计准不准、相位是否滞后。这在其他环境里要额外写很多绘图代码而在Simulink里就是拖一个模块的事。另外Simulink模型可以很方便地转成C代码或者通过外部模式External Mode直接跑在嵌入式设备上这正好接上了你后面做实物验证的路子。很多初学者学完仿真后不知道怎么落地其实从Simulink模型到Embedded Coder生成C代码之后再烧到控制板里是一条非常成熟的路径。2. 仿真环境搭建被控对象和控制器实现2.1 选一个能体现差异的被控对象对比实验最重要的第一步是选一个“足够典型”的被控对象。如果选一个纯一阶惯性对象PID和LADRC差距不会太大体现不出LADRC的价值如果选一个高阶或者纯延迟对象又对参数整定要求太高新手容易直接劝退。我的建议是选一个二阶欠阻尼对象比如G(s) 1 / (s^2 0.5s 1)这个对象的阻尼比约为0.25开环增益为1阶跃响应自带超调本身就有一定“脾气”很适合用来考察控制器的跟踪和抗扰能力。试着在MATLAB里建一下num 1; den [1 0.5 1]; G_obj tf(num, den);如果你手头有更贴近实际工程的对象比如直流电机速度环模型也可以换成带惯量和阻尼的一阶系统但为了第一次上手先把上面这个二阶对象做熟后面再换复杂对象。2.2 PID部分的搭建细节在Simulink里新建一个空白模型从Continuous库拖一个Transfer Fcn或State-Space模块填入上面这个对象。控制器我建议直接用Continuous库里的PID Controller模块不要自己用增益模块徒手搭因为官方PID模块自带积分抗饱和Anti-windup和输出限幅选项能少踩很多坑。双击PID模块把P、I、D参数填好。这里要注意为了公平对比PID的输出限幅也要设成和LADRC一样比如都限制在[-10, 10]范围内。如果你只给PID加限幅而不给LADRC加结果会失真。仿真中控制器输出如果长时间顶在限幅值上会发生积分饱和导致系统来回震荡这也是很多新手说“PID越调越乱”的原因之一。关于PID参数初值我建议先用手动试凑法大致走一遍先设I0、D0只调P观察响应曲线接着加一点I消除稳态误差最后如果超调太大再加一点D。拿上面这个对象来说一组可以接受的初值是Kp5、Ki2、Kd0.5。注意这组参数不是唯一的后面你完全可以按自己的需求重新整定。还有一个细节微分项对测量噪声很敏感。Simulink的PID Controller模块里有一个滤波器系数N默认可能是100意思是对纯微分项近似加了一个一阶低通滤波。如果你发现高频噪声导致控制量毛刺很大就把N降到1030之间试一下。这个经验在实物调试时特别有用因为真实传感器的噪声远比仿真里的白噪声大。2.3 LADRC控制器的搭建与原理对应LADRC的核心就是扩张状态观测器加一个线性状态反馈。先写一下连续时间下的离散表达方便你在Simulink里照着搭。对于一个相对阶为2的对象LESO的方程可以写成z1_dot z2 l1 * (y - z1)z2_dot z3 l2 * (y - z1) b0 * uz3_dot l3 * (y - z1)其中z1估计对象输出z2估计输出的导数z3估计总扰动。控制律则采用u (u0 - z3) / b0u0 kp * (r - z1) - kd * z2这里r是设定值b0是控制增益的估计值可以理解为“对象输入到输出二阶导数的增益”。你可以把b0想象成控制量的“杠杆比”同样的控制电压电机加速度多大、温度上升速率多快都体现在b0里。为了减少待调参数LADRC通常采用“带宽法”整定。把观测器的三个极点都放在同一个频率wo上控制器两个极点都放在wc上于是l1 3 * wol2 3 * wo^2l3 wo^3kp wc^2kd 2 * wc简单来说你只需要调两个数控制器带宽wc和观测器带宽wo。对新手来说这是LADRC最友好的地方。wo一般取wc的310倍意思是观测器要比控制器“看得快”这样估计值才能跟上实际状态变化。在Simulink里搭建时我建议直接用Integrator、Gain、Add、Sum等基础模块按方程接线而不是一上来就写S-Function。这样做的好处是每一路信号都能单独拉出来看便于理解。你可以在MATLAB工作区预先定义好参数然后在模型里直接填变量名b0 1; wc 6; wo 30; l1 3 * wo; l2 3 * wo^2; l3 wo^3; kp wc^2; kd 2 * wc;模块搭好后把系统中y、r、u、z1、z2、z3这些信号都送到To Workspace里后面批量仿真和绘图就全靠它们了。顺便提醒一下别把z1和反馈信号y直接当作一回事早期我犯过这个错误导致控制器本质上是开环控制。正确的接法是输出反馈y只送给ESO做校正而控制器中的反馈完全用z1、z2这两个估计值。2.4 给控制器包一层子系统模型搭通之后建议把LADRC控制器封装成一个子系统Subsystem对外只留r、y、u三个端口。这样做的好处很多一方面模型看着清爽另一方面后面换对象、换场景时不需要改动内部结构只需要改子系统内部参数或者b0、wc这些变量。封装时右键点击选中相关模块选择Create Subsystem from Selection然后给子系统加一个Mask把wc、wo、b0做成可以修改的“旋钮”。我特别推荐这么做因为对比实验需要反复改参数每次双击子系统就能改比在MATLAB命令行里改变量再重新运行还要方便。3. 对比实验设计从参数整定到批量仿真3.1 设计两个典型测试场景一个公平的对比实验不能只比“阶跃响应谁更快”因为不同控制器本来就有不同的调教风格。建议至少跑两个场景第一个场景是设定值跟踪在t1s时设定值从0阶跃到1比较超调量、调节时间、稳态误差。这个主要考察控制器的跟踪能力和动态品质。第二个场景是输入端扰动抑制在t10s时在对象输入端叠加一个幅值为0.5的阶跃扰动信号相当于给系统突然加上一个不小的负载变化比较输出的跌落深度和恢复时间。这个才真正体现出LADRC和PID在抗扰能力上的差异。在Simulink里可以放两个Step模块一个接在设定值通道另一个接在对象控制量之后、被控对象之前注意一定要把扰动加在对象输入端不要加在输出端。加在输出端属于测量噪声加在输入端才是负载扰动两者对控制器的考验完全不同。我习惯的接法是设定值Step输出进入控制器控制器的输出先经过Sum模块和扰动Step相加再进被控对象。这样扰动信号从控制通道注入更接近真实系统里负载变化对执行器带动的影响。3.2 PID参数的整定顺序很多教程会直接给你一组PID参数然后跑出来对比说LADRC更好。这种对比很不公平因为PID的参数可能根本没调到位。我建议你用临界比例法找一个过得去的PID作为对比基准别拿一组随手填的参数糊弄自己。临界比例法的操作步骤如下先把PID模块里的I、D设为0P设为很小的值比如0.1然后逐步增大P观察系统输出直到输出出现等幅震荡记下此时的临界增益Kc和振荡周期Tc。之后再根据经验公式Ziegler-Nichols算出P、I、D初值再手动微调。拿前面那个二阶对象临界振荡点对应的Kc大约在2附近。通过Z-N公式可以得到一组有超调但不至于发散的PID参数。如果你想看更平滑的响应可以在此基础上降低P增加一点D让超调控制在10%以内。总之花10分钟给PID调到“不丢人”的状态再和LADRC对比结论才站得住脚。3.3 LADRC参数的整定路线LADRC的整定有自己的次序我建议按照“先定wc再定wo最后调b0”的顺序来。先把b0按你对对象模型的估计设为一个合理值上面这个对象可以直接取b01。然后从wc1开始看跟踪响应。如果响应太慢就把wc逐步提高到3、5、8。wc越大系统响应越快但控制量也越大而且对噪声越敏感。我建议wc取值范围先限制在120之间反复试。确定wc之后再调wo。wo通常取wc的310倍典型值可以先取5*wc。如果发现z3波形与实际扰动对不上比如相位明显滞后或者估值偏差很大就把wo提高如果控制量上有很多高频毛刺说明wo太高、把噪声也放大了需要降下来。很多人把wo理解成“越大越好”这是一个误区。wo本质上是观测器的带宽带宽越高估计速度越快但同时通噪声也越多。实际系统里传感器一般都有零点几毫伏的白噪声wo调得太大之后z3会疯狂抖动控制量也会跟着抖。这也是为什么后来有人提出降噪LESO、加入滤波器的改进方案。3.4 用脚本批量跑仿真手动在Simulink里改参数跑一次当然够用但对比实验如果只跑一组就显得单薄。我习惯写一个简单的MATLAB脚本用sim命令批量修改wc、wo、b0自动跑完并把曲线画在一起。% 定义需要扫描的参数组 wc_list [2, 5, 10]; wo_ratio 5; b0_list [0.5, 1, 2]; figure; hold on; for i 1:length(wc_list) % 更新工作区变量 wc wc_list(i); wo wo_ratio * wc; b0 1; % 重新配置LESO参数 l1 3 * wo; l2 3 * wo^2; l3 wo^3; kp wc^2; kd 2 * wc; % 运行Simulink模型注意模型名和路径 simOut sim(compare_ladrc_pid.slx); % 取数据假设你用了To Workspace模块变量名为simout t simOut.simout.time; y simOut.simout.signals(1).values; u simOut.simout.signals(2).values; plot(t, y, LineWidth, 1.5); end legend(wc2, wc5, wc10); xlabel(时间/s); ylabel(输出); grid on;跑脚本前记得把模型里的Scope不要接收所有信号因为To Workspace需要显式输出信号。通常我放置三个To Workspace模块一个存输出y一个存控制量u一个存ESO的三路状态z1、z2、z3数据结构统一用Array或Timeseries。批量扫描时同一模型反复调用sim函数MATLAB工作区的变量更新后模型会自动引用新参数这一点比GUI操作方便很多。4. 仿真结果解读响应、抗扰与控制量4.1 典型波形和指标对比跑完一组有代表性的参数后你会看到比较明显的差异。以wc6、wo30、b01的LADRC和一组调好的PID为例我得到的一组典型结果是比较项PIDLADRC设定值超调约18%约4%2%调节时间约4.5s约2.8s10s扰动导致的输出跌落约0.32约0.12扰动后恢复时间约5.2s约1.5s控制量峰值约1.5约2.1这个表格只是示意具体数值会随你的参数和对象不同而变但规律是稳定的在带宽足够的情况下LADRC的跟踪更快、超调更小抗扰能力明显更强。代价是控制量更“猛”执行器压力更大。如果你的执行器有小马拉大车的顾虑需要在控制器带宽上做些取舍。为什么会有这样的差异关键在于PID面对扰动时系统输出已经偏出去一段才开始纠偏而LADRC的ESO能提前把扰动“看出来”并且在扰动还没有真正造成大偏差时就把控制量补偿上去。这个提前量不是预测算法那种基于未来轨迹的预测而是基于输入输出数据的状态估计它把扰动的动态包含在一个扩张状态中所以理论上可以做到对阶跃扰动无静差地抵消。4.2 观察ESO估计准不准是调参的关键调试LADRC最有用的一招是把z3和实际扰动同时画出来。在模型中你可以在扰动Step之后再接一条线经过To Workspace送给工作区然后和z3一起绘制。如果z3曲线远远落后于真实扰动说明wo太低观测器反应跟不上。如果z3的波形在扰动加入前就有很大毛刺说明wo太高或者测量噪声太大。如果z3的稳态值比扰动实际幅值小很可能b0设得太小控制器补偿量不足如果z3明显发散到离谱的值还需要检查模型是否接成了代数环。我在初学阶段有一回把z3接到了错误的位置导致ESO估计的是输出误差而不是输出本身结果z3完全不合理。后来我干脆把z1、y、z3三个信号放在同一个Scope里每次改参数后先看这三个量再决定下一步怎么调。这个习惯帮我少走了很多弯路。4.3 PID调得好是否就能战胜LADRC你可能会想“既然可以仔细调PID是不是也能调到和LADRC差不多”在某些限定条件下确实可以。比如对象模型非常精确、参数不变化、外部扰动以小阶跃为主、工作点固定PID经过精心整定完全可以达到不错的动态性能。这也是为什么很多老系统用PID用了二十年也没换。但真实系统最怕的是“工作点漂移”和“参数摄动”。同一个PID参数在负载轻的时候调好了负载变重之后可能就震荡在环境温度高的时候调好了冬天一启动又超调。LADRC把未建模动态和参数变化合并进总扰动由观测器在线估计所以对工作点变化的适应能力更强。你可以在仿真里做一个简单的实验t020s用G(s)1/(s^20.5s1)t20s以后把被控对象切换成G(s)2/(s^20.3s1)即增益和阻尼都变了。这种情况下固定参数的PID通常会明显变差而LADRC只要wo足够仍能维持较好的响应。当然LADRC也不是万能药。遇到大延迟对象、非最小相位对象或者执行器饱和严重的情况基础LADRC直接上会出问题需要改进结构或者引入预测信息。做对比实验时别急着断言“LADRC全面碾压PID”要结合具体场景下结论这既是工程素养也是写技术报告时的基本要求。5. 常见问题与排查技巧实录5.1 仿真直接发散或出现NaN、Inf这是新手最容易碰到的问题。我遇到过的常见原因有这么几类一是求解器步长太大。Simulink默认可能会选较大的可变步长对LADRC这种含高带宽观测器的系统来说容易造成数值不稳定。解决办法是把最大步长设小一点比如0.01s或者直接换用ode15s这类适合刚性系统的求解器。二是b0严重失配。b0如果比实际值小太多相当于控制器增益被放大系统容易震荡b0比实际值大太多控制作用又过于温和甚至补偿不足。建议先从b01这个数量级试起再上下各试一档。三是ESO初值设置不合理。一般取z1、z2、z3的初始值为0但如果你把被控对象输出初值设成了非0ESO初始会有很大的偏差导致第一个周期控制量巨大。先让所有初值统一或者让z1初值等于实际输出初值。四是控制器输出没有限幅。LADRC的补偿项z3/b0在扰动突变瞬间可能非常大如果输出没有饱和模块给到对象的控制量会是一个夸张的数进而引发数值爆炸。在控制器输出端加一个Saturation模块限幅范围根据实际执行器能力设定是最基础的保护措施。5.2 出现代数环报警代数环在Simulink里表现为一个闭环里没有积分器或者Memory模块信号在同一个步长内来回自洽求解有时候能解出来但效率低还容易有数值问题。LADRC的ESO内部有积分器正常不会出现代数环但如果你的控制器是纯增益模块搭出来的比如用MATLAB Function直接实现了LESO方程而内部没有离散状态就可能触发代数环。解决代数环最简单的办法是在反馈通道里放一个Memory模块相当于引入一个步长的延迟。要注意这个延迟会降低观测器的相位裕度所以千万别在所有通道上都乱加Memory。更推荐的做法是把ESO离散化用离散积分器搭状态方程这样既能避免代数环也和后面要生成C代码的路径一致。5.3 Scope曲线看不出东西或自动空白多半是因为工作区变量没定义好。Simulink模型里的增益模块引用了变量l1、l2、l3如果你只清空了工作区再运行模型就会报错或者参数变成空值。解决方法是把参数初始化写在一个脚本里并挂到模型的PreLoadFcn回调上。具体操作模型属性 回调 PreLoadFcn填入“init_ladrc_params”。这样每次打开模型参数都会自动加载不会出现手动运行脚本后忘掉的尴尬。还有一次我把To Workspace模块的保存格式设成了Array结果simOut.signals结构访问不到数据。如果你确实遇到了类似问题建议把保存格式统一设为Timeseries访问方式更直观画图也简单。5.4 离散化与C代码生成的衔接仿真完成后如果要做嵌入式实现需要把连续LADRC离散化。最简单的离散化方法就是欧拉法直接把导数换成差分z1[k1] z1[k] Ts * (z2[k] l1 * (y[k] - z1[k]))z2[k1] z2[k] Ts * (z3[k] l2 * (y[k] - z1[k]) b0 * u[k])z3[k1] z3[k] Ts * (l3 * (y[k] - z1[k]))采样周期Ts的选择很重要一般建议至少满足Ts 1 / (20 * wo)如果wo30 rad/sTs要小于1.7ms实际取1ms比较稳妥。如果你用的是Simulink的离散模块还可以直接通过Embedded Coder生成C代码前提是把离散求解器设置好并把模型参数设置为定常。这里提一个和PID相关的注意点如果你后来想用增量式PID思想实现离散LADRC同样要注意输出限幅和反计算抗饱和因为LADRC的控制律本身不内置抗饱和结构饱和时需要对z3或控制量限幅做额外处理。5.5 常见问题速查表现象可能原因解决办法仿真直接发散或NaN最大步长过大、b0严重失配、初值不合理降低最大步长、检查b0数量级、初始化z1初值控制量高频乱抖wo过高、测量噪声大、D项没有滤波降低wo、增加测量滤波、调整PID微分系数抗扰补偿不足b0偏大、wo偏低减小b0、提高wo持续振荡或极限环控制器输出连续顶限幅增加输出限幅、加入抗饱和处理SCOPE无曲线工作区变量丢失、To Workspace格式错误配置PreLoadFcn改用Timeseries保存格式代数环报警MATLAB Function内部无状态延迟改用离散积分器或在反馈中加Memory模块6. 从仿真到嵌入式部署一个过来人的扩展建议6.1 别急着上LADRC先做对象辨识和b0估计很多初学者仿真里跑得很顺一到实物就崩最常见的原因就是b0估计得太随便。b0不是一个可以随便拍脑袋的数它代表着“控制量到输出二阶导”的增益。如果你的对象是直流电机速度环可以根据电机转动惯量和力矩常数估算如果是温控系统需要结合热容热阻特性做辨识。我的建议是先对被控对象做阶跃响应实验用系统辨识工具箱拟合出二阶模型再从模型的最高频段增益反推b0。表达式大概是如果辨识出的模型是K/(s^2a1*sa2)那么高频段增益近似等于K所以b0可以取K。这里的K不是开环增益对对于传递函数分母s^2项系数为1的模型b0就是分子的静态增益。仿真验证这一步必须要做它决定了你的ESO在实物上能不能稳定。6.2 和级联PID结合而不是直接取代在真实的电机控制、无人机姿态控制、伺服系统中你不需要把原来的PID全盘换成LADRC。更务实的做法是内环保留传统的PI电流环因为它带宽高、要求快调好了很稳外环位置或速度环改用LADRC这样可以获得更强的抗负载扰动能力又不至于让整个系统因为大换血而失控。这种“外环LADRC、内环PI”的结构在Simulink里搭建并不复杂只需要把内环当成一个等效被控对象再在外面包一层LADRC即可。你还可以顺便对比一下“内外环都用PID”和“内环PI外环LADRC”两种结构的差异会很有意思。我实测下来外环LADRC对导轨摩擦变化、负载突变的抑制效果要明显好于单纯PID启动阶段也不容易产生过大的超调。6.3 把ESO的状态放到监控界面上是价值最大的一步如果你有上位机或者串口调试工具建议在代码里把z1、z2、z3这三个状态实时发出来用曲线工具画出来看。z3的动态直接反映了系统承受的总扰动当你给系统加上负载、改变工况时z3会跟着变化你能立刻“看到”扰动的大小和方向。这种感知是PID控制器给不了你的。我个人在实际操作中的体会是学LADRC不要一上来就追求参数最优化先把模型跑通、把ESO三路状态看清再动手调参会顺畅很多。最后再分享一个小技巧批量仿真时不要让参数一次性跨度过大比如wo从30直接跳到100很多模型会直接发散你先从30跳到45再跳到60这样能明显减少数值突变的概率。希望这份Simulink入门指南能帮你把LADRC从“听说的控制方法”变成“自己能用的控制工具箱”。