ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

ADRC与PID等效:传递函数推导及MATLAB仿真实践

2026/9/9 15:19:50 拓冰建站 浏览量
ADRC与PID等效:传递函数推导及MATLAB仿真实践 ADRC这玩意儿玩起来确实挺有意思尤其是当你想跟别人解释“ADRC到底是不是PID”的时候。我当年被问到这个问题第一反应是肯定不是啊ADRC有观测器PID哪来的观测器结果真到了要写文档、要对标调参、要跟懂频域的工程师对齐思路的时候才发现不把传递函数推一遍很多话根本说不清楚。这篇就把ADRC的内核拆开重点走一遍“从传递函数推导到PID等效”的完整链路顺便给一段能直接跑的MATLAB热身代码。适合刚接触ADRC、想从原理上吃透它或者已经在用但总感觉调参全靠试的朋友。1. PID的抱怨与ADRC的逆袭逻辑1.1 PID的三个“看不惯”PID用了这么多年大家心里都有数它的问题不在于结构简单而在于三个根子上的毛病。第一个毛病是误差驱动的滞后性。PID是在误差出现之后才动作的误差小的时候控制量就小误差大才猛打。对一个慢变对象还好但对象一旦有纯滞后或者大惯性PID就会陷入“打一下、等一会儿、看错了再打”的循环系统表现为振荡或者响应迟钝。第二个毛病是微分项对噪声的放大。D项理论上能预测误差走向但实际现场里的测量噪声高频分量很多一求导就把噪声高频分量放大了所以工业PID里往往要加滤波加完滤波D的效果又打折搞得很多人干脆把D关掉只用PI。第三个毛病是积分项的“秋后算账”。为了消除稳态误差必须加积分但积分一强抗饱和处理不当系统超调就压不住而且积分是对过去误差的累加它不认识对象模型遇到扰动变化只能慢慢“磨”。这三个毛病合在一起本质上是同一个问题PID对“对象是什么样”知道得太少它只能根据输入输出误差来猜。猜得慢就滞后猜得猛就振荡。1.2 ADRC的思路把“不知道的”变成“看得见的”ADRC的逆袭思路特别朴素你不是不知道对象模型吗那我干脆把“不知道的那部分”当成一个额外的状态量设计一个观测器把它估计出来然后在控制量里把它减掉。这句话值得重复一遍ADRC不是不依赖模型而是把模型误差、外部扰动、未建模动态这些东西全打包成一个“总扰动”然后实时估计、实时补偿。补偿完之后剩下的对象在控制器眼里就是一个干干净净的积分串联型。打个比方。PID像一个看不到路况的司机只能靠踩油门后的车速反应来调整。ADRC则像是车上装了传感器把坡度、风力、载重全测出来然后在踩油门的指令里把这些阻力直接抵消掉。司机剩下的工作就很简单——控制一个“没有阻力”的虚拟车。把总扰动变成状态量这就是“扩张状态”的意思。后面我会详细拆。2. 扩张状态观测器ESO到底在估计什么2.1 状态是怎么“扩张”出来的拿最常见的二阶对象来推。假设被控对象可以写成y f(y, y, w, t) b0 * u这里面y是输出u是控制量。f是“总扰动”它包括了对象内部动态、外部扰动、参数变化、未建模部分反正所有你不知道、不想建模、建模不准的东西全塞进f。这个式子其实没什么神奇的它就是牛顿第二定律的抽象版加速度等于“所有别的力”加“控制产生的力”。既然是二阶系统自然状态是x1 yx2 y。那第三个数呢把f本身也当成一个状态令x3 f。这就是“扩张”的含义——在原系统阶次基础上多引入一个状态变量。于是状态方程变成x1 x2 x2 x3 b0 * u x3 h其中h是f的导数。虽然我们不知道h具体是多少但在观测器设计里它被当成一个未知但有限的“扰动变化率”。2.2 观测器的结构与增益设计有了扩张状态方程ESO的观测器方程就能直接写出来z1 z2 β1 * (y - z1) z2 z3 β2 * (y - z1) b0 * u z3 β3 * (y - z1)其中z1估计yz2估计yz3估计总扰动f。观测器增益β1、β2、β3怎么取最常用的方法是带宽参数化让观测器特征方程落在(s ωo)^3上。展开(s ωo)^3 s^3 3ωo s^2 3ωo^2 s ωo^3于是得到β1 3ωo β2 3ωo^2 β3 ωo^3这个参数化方法极大简化了整定过程。你不需要去试三个独立的β只要定一个观测器带宽ωo即可。ωo越大ESO追踪越快但噪声放大也越严重。这个就是我们后面调参时的主要矛盾。2.3 ESO的传递函数表达ESO在时域里很直观但如果你想从频域角度理解它或者做扫频、做等效分析就必须把它拉到s域里来。把观测器方程做拉普拉斯变换整理之后可以得到z1、z2、z3对y和u的传递函数。这里的关键是引入共同分母D(s) s^3 β1 s^2 β2 s β3那么z1 [(β1 s^2 β2 s β3) / D(s)] * y [b0 s / D(s)] * u z2 [(β2 s^2 β3 s) / D(s)] * y [b0 s(s β1) / D(s)] * u z3 [β3 s^2 / D(s)] * y - [b0 β3 / D(s)] * u这几个式子看着长但信息量很大。z1对y的传递函数在低频段趋近于1说明z1能无差跟踪输出z2对y的传递函数在低频段趋近于s说明z2本质上就是对输出的微分估计z3表达式里的第一项说明它从输出里提取低频累积信息第二项则是把控制量带来的影响减掉保证它估计的是“外部总扰动”而不是控制量本身。知道这些传递函数形式后面推导PID等效就能直接用了。3. 从传递函数到PID等效推导过程与边界3.1 先把控制律写成频率域形式线性ADRC的控制律通常写成u0 kp * (r - z1) - kd * z2 u (u0 - z3) / b0其中r是参考输入kp和kd是控制器增益。对于二阶对象可以把闭环期望极点配置成kp ωc^2 kd 2ωcωc是控制器带宽。控制量u先由u0除以b0再减去总扰动估计z3这样做的目的就是“对抗”对象中的b0 u项实现动态补偿。把这个控制律往s域写可以整理成从误差和输出到控制量的传递函数。整个过程稍微繁琐一点但每一步都是线性代数。把ESO的z1、z2、z3表达式全部代入控制律u的表达式经过整理从输出y到控制量u的闭环反馈补偿器可以写成一个统一的传递函数C(s) [a2 s^2 a1 s a0] / [b0 s (s^2 c1 s c0)]其中各系数为a2 kp * β1 kd * β2 β3 a1 kp * β2 kd * β3 a0 kp * β3 c1 β1 kd c0 β2 kp kd * β1注意分母里有一个s因子。这个s因子单独提出来说明整个补偿器天然含有积分作用。这就是ADRC能消除稳态误差的频域根源不是靠额外加的积分项而是ESO结构带来的结果。3.2 低频近似下的等效PID参数把分母后面那个二阶多项式看成一个“滤波因子”。在低频段也就是s趋近于0的时候s^2项相对c0可以忽略那么补偿器近似为C(s) ≈ [a2 s^2 a1 s a0] / [b0 * c0 * s]这就是一个标准的PID结构分子除以s之后变成Kp_eq Ki_eq / s Kd_eq * s对应地等效PID参数为Kp_eq a1 / (b0 * c0) Ki_eq a0 / (b0 * c0) Kd_eq a2 / (b0 * c0)把a0、a1、a2、c0的具体表达式代回去就会看到每个等效参数都是ωc、ωo、b0的组合。也就是说ADRC的“PID味道”是可以直接用公式算出来的。但必须强调这个等效是有条件的就是我前面说的“低频段近似”。具体来说分母中那个二阶多项式s^2 c1 s c0的零点所对应的转折频率必须在闭环带宽之上。如果ESO带宽不够高这个二阶因子的动态就不该被忽略此时ADRC的行为会明显偏离等效PID。3.3 边界条件什么时候等效会失真我实际推导、仿真下来的几点感受第一b0严重不准确时等效PID增益会大幅跑偏但ADRC的自抗扰能力恰恰体现在这里——即使b0和真实对象增益差很多ESO还能把“b0误差造成的等效扰动”也一并估计和补偿掉。所以b0不准确时ADRC的实际表现依然能保持稳定只是等效PID参数已经不能反映真实闭环特性了。第二测量噪声大的时候如果把ωo调得很高z3会把噪声当成高频扰动去补偿控制量会表现出剧烈的抖振。这时候从频域看就是C(s)中的高频增益被放大等效PID里的“微分项”主导了输出。第三控制器带宽ωc和观测器带宽ωo的比值如果太近闭环极点会与观测器极点相互干扰等效PID模型会失效。经验上ωo至少要比ωc大3到5倍我一般取5到10倍后面会有专门说明。所以推导出来“ADRC低频等价于PID”不是要证明ADRC故弄玄虚或者多此一举。它的价值在于你用PID调了很多年积攒了一身直觉那这些直觉在ADRC上依然能部分复用而当系统行为异常时你知道该往哪个环节去找原因而不是盲调。4. MATLAB热身一个最小可跑的LADRC仿真4.1 仿真对象与参数设计说了这么多理论还是得跑代码。下面这段MATLAB是一个最小可跑的线性ADRC仿真包含完整的对象模型、ESO、控制器三个部分。我故意把“建模用b0”和“真实对象增益”设得不一致这样ESO才有东西可估计。假设真实对象为y -3y 2y 0.5sin(3t) 0.8u d这个对象里有内部动态-3y 2y有非线性项0.5*sin(3t)还有外部负载扰动d。但我建模时用的b01.2比真实增益0.8大了50%。这种情况下PID基本必须重新整定ADRC却能靠ESO一路兜住。控制目标是让y跟踪阶跃参考r1并在第3秒加一个幅度1的阶跃负载扰动看系统能不能扛住。4.2 完整代码%% LADRC最小仿真ESO估计总扰动 动态补偿 clear; clc; close all; % 仿真参数 dt 0.001; % 采样步长 T 6; % 仿真时长 t 0:dt:T; % 时间向量 n length(t); % 对象初始状态 y 0; dy 0; % ESO初始状态 z1 0; z2 0; z3 0; % LADRC参数 wc 5; % 控制器带宽 wo 30; % 观测器带宽 kp wc^2; % PD控制器比例增益 kd 2*wc; % PD控制器微分增益 b0 1.2; % 建模使用的控制增益真实对象是0.8 % 观测器增益带宽参数化 beta1 3*wo; beta2 3*wo^2; beta3 wo^3; % 存储结果 yout zeros(1, n); uout zeros(1, n); z3out zeros(1, n); z1out zeros(1, n); % 参考输入 r 1; % 主循环 for k 1:n % 外部负载扰动第3秒后加入阶跃扰动 d 0; if t(k) 3 d 1.0; end % 对象内部动态对控制器而言未知 f_nl -3*dy 2*y 0.5*sin(3*t(k)); % --- ADRC控制律 --- u0 kp*(r - z1) - kd*z2; u (u0 - z3)/b0; % --- 真实对象更新欧拉法 --- ddy f_nl 0.8*u d; dy dy ddy*dt; y y dy*dt; % --- ESO更新 --- e y - z1; dz1 z2 beta1*e; dz2 z3 beta2*e b0*u; dz3 beta3*e; z1 z1 dz1*dt; z2 z2 dz2*dt; z3 z3 dz3*dt; % 保存数据 yout(k) y; uout(k) u; z3out(k) z3; z1out(k) z1; end % 绘图 figure(Color, w, Position, [100 100 900 600]); subplot(3,1,1); plot(t, yout, b-, LineWidth, 1.5); hold on; plot(t, r*ones(size(t)), k--, LineWidth, 1); ylabel(y); title(ADRC跟踪与抗扰); legend(y, r, Location, best); grid on; xlim([0 T]); subplot(3,1,2); plot(t, uout, r-, LineWidth, 1.2); ylabel(u); title(控制量); grid on; xlim([0 T]); subplot(3,1,3); plot(t, z3out, g-, LineWidth, 1.5); hold on; % 画实际总扰动的参考曲线仅用于对照 f_actual -3*dy 2*y 0.5*sin(3*t) d*ones(size(t)); plot(t, f_actual, k--, LineWidth, 1); legend(z3估计, 实际总扰动, Location, best); ylabel(总扰动); title(ESO估计效果); grid on; xlim([0 T]);4.3 结果怎么看跑完这段代码你会看到三个图第一个图输出y能快速跟踪阶跃参考超调不大大约1秒内进入稳态。第3秒加入负载扰动之后y出现一个小凹陷但很快被拉回来恢复时间大概0.5秒以内。这就是ESO把扰动估计出来后直接补偿掉的效果。第二个图控制量u在扰动加入瞬间会有一个明显的跳变这个跳变就是补偿动作。它不像PID那样靠误差累积一点点“磨”而是几乎同步把扰动电压顶回去。第三个图最关键看z3估计值能不能追上实际总扰动。如果你把观察窗口放大会看到z3在扰动阶跃后大概0.1秒左右追上新值。这就是ESO带宽wo30的效果。如果把wo降到10z3跟踪会变慢抗扰能力明显下降但噪声放大的风险会小很多。你还可以试着改wc和wo观察一个现象wc决定响应速度wo决定ESO估计速度两者协调才能有好的动态和抗扰表现。b0则决定了控制量尺度b0给大了控制量偏大给小了控制量偏保守但ESO都会尽量把它“圆回来”。5. 扫频法实战用开环传递函数标定截止频率5.1 为什么需要扫频搞控制的人到了现场最关心的往往是几个硬指标幅值裕度、相位裕度、截止频率。这些指标用传递函数一看就知道系统稳不稳、快不快。但ADRC的参数是带宽是b0是kp、kd不是穿越频率。那怎么在频域层面评估一个ADRC闭环系统最直接的办法就是扫频。所谓扫频法就是给被控对象或者闭环系统依次注入不同频率的正弦信号记录输入输出的幅值比和相位差然后画出伯德图从伯德图里读出截止频率和裕度。这个方法的好处是不需要知道对象模型的解析表达式只要系统能加正弦激励、能测响应就能得到频率特性。对现场调试来说特别实用。5.2 扫频法的实现要点在MATLAB里做扫频核心步骤是生成一组频率点对每个频率点仿真足够长时间直至达到稳态然后计算输出与输入在同一频率上的幅值比和相位差。相位差的计算有个小技巧不要直接用xcorr或者fft去翻最稳妥的方式是用正交解调。对每个频率ω把输出y乘以cos(ωt)和sin(ωt)然后在一个整周期或多周期上求平均就可以得到实部和虚部进而算出幅值和相位。下面给一个简洁的扫频法参考代码片段可以直接在这个LADRC仿真里继续使用。%% 扫频法求开环响应参考代码片段 freqs logspace(-1, 2, 40); % 0.1~100 rad/s对数均匀分布 mag zeros(size(freqs)); phase zeros(size(freqs)); for idx 1:length(freqs) w freqs(idx); Ts 2*pi/w * 20; % 跑20个周期确保进入稳态 t_sim 0:dt:Ts; % 注入正弦记录输出可复用上面的LADRC循环结构 % 这里仅示意实际需要把上一段主循环改成“正弦输入记录输出” % y_ss 为每个频率下的稳态输出信号 % 正交解调 cos_part trapz(t_sim, y_ss .* cos(w*t_sim)); sin_part trapz(t_sim, y_ss .* sin(w*t_sim)); mag(idx) 2*sqrt(cos_part^2 sin_part^2) / Ts; phase(idx) atan2(sin_part, cos_part) * 180/pi; end % 画伯德图 figure; subplot(2,1,1); semilogx(freqs, 20*log10(mag), b-o); grid on; ylabel(幅值 (dB)); title(扫频法伯德图); subplot(2,1,2); semilogx(freqs, phase, r-o); grid on; xlabel(频率 (rad/s)); ylabel(相位 (deg));注意扫频法有几个坑第一必须等系统进入稳态再取数据。如果从t0就开始算瞬态分量会污染幅值和相位测量。所以我通常每个频率点会丢掉前5到10个周期。第二频率点要覆盖关心的频段不要全集中在高频或低频。建议在截止频率附近多取几个点这样估出来的截止频率更准。第三如果系统存在强非线性扫出来的“频率特性”会随注入幅值变化。此时需要固定注入幅值并在多个幅值下扫描对比否则结果不可比。用扫频法验证ADRC我一般看两个量一是闭环的截止频率是否接近wc二是开环幅值穿越处相位是否离开-180度足够远。如果相位裕度偏小优先降低wc或者适当提高wo让ESO更“稳准狠”。5.3 截止频率与ADRC参数的经验关系从我扫过的几个系统来看LADRC闭环的截止频率大致在ωc附近但不会刚好等于ωc。因为ESO的动态等效于在回路里串了一个二阶滤波器会略微压低穿越频率。实际测出来截止频率大概在0.6到0.9倍ωc之间。wo越低、ESO动态越重这个折扣越大。所以如果你想设计一个截止频率为10 rad/s的闭环建议把wc设在12到15左右再通过扫频结果微调。这个思路和用PID时先定一个穿越频率再反推参数的思路是一模一样的这就是等效PID概念在工程上的真正用处。6. 调参顺序与数字实现中的几个坑6.1 调参顺序先b0再wo最后wcADRC参数少但少不代表可以乱来。我实践下来最稳的顺序是先定b0再升wo最后动wc。b0代表你对对象控制增益的“粗估”。第一版先给一个量级差不多的值就行不需要精确。b0的量级怎么估算可以做一个阶跃实验给对象加一个已知幅值的u看稳态加速度变化量两者相除就是b0的近似值。定好b0后逐步升高wo同时观察控制量毛刺。wo升到某个值控制量开始出现明显高频抖动说明它高到噪声敏感区了退回一点即可。最后再升wc直到跟踪速度满足要求。如果升wc过程中出现振荡不要急着调回wc先看wo是不是低了ESO跟不上导致相位损失。6.2 数字实现的离散化问题很多人把连续域里的ADRC方程直接搬到单片机或者控制器里结果发现跟仿真完全不一样。问题出在离散化。最简单的欧拉法在采样周期比较小的时候够用但采样周期大比如5毫秒以上的PLC扫描周期欧拉法会引入明显相位滞后。我一般推荐用双线性变换或者直接用离散状态空间来实现ESO。离散ESO可以写成z1[k1] z1[k] T*(z2[k] β1*(y[k]-z1[k])) z2[k1] z2[k] T*(z3[k] β2*(y[k]-z1[k]) b0u[k]) z3[k1] z3[k] Tβ3*(y[k]-z1[k])这个形式跟连续版一一对应工程上可以直接用。但注意T不能太大经验上ωo*T的乘积最好小于0.3否则离散化误差会明显削弱ESO的观测性能。比如wo30时T要小于0.01秒也就是采样频率至少100赫兹。6.3 ESO初值、抗饱和与几个实战心得ESO的初值一般设0就行因为观测器本身会收敛。但要注意如果对象初始状态离0很远ESO需要一段时间才能追上这段时间里的控制量会偏大。实际中最好让控制器在参考输入变化前先运行几个周期让ESO收敛到当前工作点。抗饱和这块经常被忽略。ADRC的控制量u里含有z3/b0这个补偿分量如果执行器饱和了u被限幅但ESO内部仍然按照未饱和的u去计算z3就会越积越偏等退出饱和时表现得很“冲”。解决思路有两个一是给ESO也做限幅让它知道实际执行器的输出边界二是检测到饱和时冻结z3的更新。工业上常用后者简单有效。最后分享一个我踩过的坑。有一回我在仿真里把b0设成了精确值wo也调得很高控制效果漂亮得不行。结果拿到现场测量噪声一上来控制量抖得电机都在响。后来把wo从50降到15噪声下去了抗扰性能虽然差了一截但系统整体能稳定跑。ADRC调参就是这样永远要在“跟踪快”和“噪声稳”之间找平衡而找平衡的抓手就是wo和wc不是kp、kd那些表面参数。说到底ADRC的数学门槛不高难得是把里面每个量的物理意义和工程后果都对上号。传递函数到PID等效的推导就是把“时域里靠观测器补偿”这个高级操作翻译回“频域里它到底给回路增加了什么”翻译通了你才算真正看懂了它。