简介NESO.rar压缩包内含一套基于Simulink的非线性扩展状态观测器ESO仿真模块面向自动控制、无人系统、机器人及伺服驱动领域的研究者用于解决系统不可测状态的实时估计问题。压缩包共3个文件均为MATLAB脚本整体仅1KB分别为NESO.m、Plant.m与tuning.mPlant.m定义被控对象的非线性动态模型NESO.m实现扩张状态观测器核心算法tuning.m提供参数整定与调试流程三者在Simulink中可组合替换方便复现和二次开发。已有337人学习下载。借助这套仿真模型读者能够掌握ESO的建模思路与调参规律观察不同非线性强度下观测器的估计精度与收敛速度并可将模型迁移至电机控制、无人机姿态估计、电力电子等实际工程场景是理解主动抗扰控制与状态观测技术的高效工具。1. NESO在Simulink里到底在解决什么如果你在 MATLAB 的搜索框里敲过ESO或NESO大概率是从自抗扰控制ADRC那边摸过来的。拿到的模型往往是一个打包好的.rar里面躺着simulink的.slx或.mdl文件打开一看里面除了被控对象最显眼的是一坨连着积分器的反馈结构注释写着 ESO 或者 Nonlinear ESO。这个结构就是整个 ADRC 里最值钱的部分——扩张状态观测器。它做的事情很朴素把系统内部未建模动态和外部扰动捏成一个总扰动当成一个新的状态变量去在线估计。NESO 和 ESO 的区别在反馈误差的处理方式上ESO 常用线性增益NESO 用非线性函数fal()在小误差区域放大增益、大误差区域压低增益从而在扰动估计精度和噪声抑制之间拿到更好的平衡。这篇文章面向的是做电机控制、飞行器控制、电力电子或者一般非线性系统仿真的工程师和研究生。读完你能在 Simulink 里自己搭一个 NESO知道参数怎么设、仿真发散了怎么查、估计效果怎么验证。2. ESO 与 NESO 的数学基础从线性扩张到非线性修正2.1 ESO 的核心思想把不确定性扩张成状态对一个二阶非线性被控对象可以写成这样的形式[ \ddot{x} f(x, \dot{x}, w, t) b u ]其中x是系统状态u是控制输入b是控制增益f(·)包含了内部动力学、未建模动态和外部扰动w的总和。ESO 的思路是不去建模f的具体表达式而是把它当作第三个状态变量[ x_3 f(x, \dot{x}, w, t) ]于是原系统被扩张成一个三阶系统x1 x2 x2 x3 b·u x3 h这里h f假定有界。对应地设计三阶观测器e z1 - y z1 z2 - β1·fal(e, α1, δ) z2 z3 - β2·fal(e, α2, δ) b0·u z3 -β3·fal(e, α3, δ)其中z1跟踪系统输出yz2跟踪速度状态x2z3跟踪扩张状态x3也就是总扰动的实时估计值。b0是控制增益b的估计值。这个结构里没有用到被控对象的精确模型这正是 ESO 的工程价值所在。2.2 线性 ESO 的带宽参数化如果直接把fal(e, α, δ)换成线性函数e就得到线性扩张状态观测器LESOe z1 - y z1 z2 - β1·e z2 z3 - β2·e b0·u z3 -β3·eLESO 的参数可以用带宽法统一配置减少整定维度。令观测器带宽为ω0则参数表达式典型值ω0 30β13·ω090β23·ω0²2700β3ω0³27000这个配置来自观测器特征多项式(s ω0)³的展开系数。ω0越大观测器收敛越快但噪声放大也越明显。线性增益在误差大和误差小时用的是同一个比例系数导致一个矛盾误差大时希望增益高、收敛快误差小时希望增益低、抗噪好。线性结构无法同时满足这两点。2.3 NESO 的非线性机制fal函数的行为拆解NESO 把线性增益替换成韩京清提出的非线性函数fal(e, α, δ)定义如下fal(e, α, δ) e / δ^(1-α) 当 |e| ≤ δ fal(e, α, δ) |e|^α · sign(e) 当 |e| δ其中α是幂次取值范围一般在 0 到 1 之间δ是线性段的区间宽度。这个函数的行为分两段看当|e| δ时,fal相当于一个压缩的符号函数。α越小增益随误差增大而下降得越快大误差时的输出幅值被压得越低好处是初始误差很大时不会产生过大的控制冲击。当|e| ≤ δ时fal退化为线性函数增益是1/δ^(1-α)。δ越小这个线性段增益越大对微小误差的响应越灵敏。换句话说误差小的时候观测器有高增益误差大的时候增益自动降下来。α和δ配合让观测器在小误差时有高增益、大误差时有低增益兼顾了收敛速度和噪声抑制。sign(e)是符号函数|e|^α的幂次变换是为了让增益随误差平滑变化。2.4 NESO 改在哪从β·e到β·fal(e)把 LESO 的误差项e换成fal(e, α, δ)就得到 NESO。这个改动带来一个直接效果当估计误差很小时fal的增益远大于线性增益能更敏锐地捕捉到扰动起始时刻的微小变化当估计误差很大时fal的输出被压缩观测器状态不会剧烈跳变抗初始冲击能力更强。在实际的 Simulink 仿真里改动非常小只是把乘法器前面的误差信号换成一个fal函数模块。但这个模块怎么实现有不同的做法下一章展开。3. 在 Simulink 中搭建 NESO 模型最小实现与模块选择3.1 最小被控对象模型二阶不确定系统先搭一个被控对象。这里用最常见的二阶非线性系统x1 x2 x2 -25·x2 2·sin(x1) w(t) 5·u y x1其中w(t)是外部扰动-25·x2是阻尼项2·sin(x1)是非线性项。控制增益b 5取估计值b0 4.5故意偏差 10%模拟模型不精确的情况。Simulink 模型用积分器、增益、三角函数模块搭起来注意两个积分器的初始值都设为零。新建一个模型拖入两个Integrator模块、一个Gain模块增益设为 25注意符号、一个Trigonometric Function模块选sin、一个Add模块把各项加起来。w(t)用一个Signal Builder或Step模块注入。控制输入u先接常数 0只观察观测器效果。3.2 用 MATLAB Function 实现 NESO 的核心方程NESO 本身不适合用零散模块搭容易乱。我一般直接在 Simulink 里放一个MATLAB Function模块把三阶观测器写成离散递推形式。双击模块输入以下代码function [z1, z2, z3] neso_observer(y, u, dt, param) % NESO 离散化递推实现 % 输入: % y - 系统输出 (x1 的测量值) % u - 控制输入 % dt - 固定步长 % param - 参数向量 [b0, beta1, beta2, beta3, alpha1, alpha2, alpha3, delta] % 输出: % z1, z2, z3 - 扩张状态观测器三阶状态 persistent z1k z2k z3k if isempty(z1k) z1k 0; % 观测器初值 z2k 0; z3k 0; end b0 param(1); beta param(2:4); alpha param(5:7); delta param(8); % 误差 e z1k - y; % fal 函数计算 fal1 fal(e, alpha(1), delta); fal2 fal(e, alpha(2), delta); fal3 fal(e, alpha(3), delta); % 一阶欧拉离散 z1_next z1k dt * (z2k - beta(1) * fal1); z2_next z2k dt * (z3k - beta(2) * fal2 b0 * u); z3_next z3k dt * (-beta(3) * fal3); % 更新 z1k z1_next; z2k z2_next; z3k z3_next; z1 z1k; z2 z2k; z3 z3k; end function f fal(e, alpha, delta) if abs(e) delta f e / (delta^(1 - alpha)); else f (abs(e)^alpha) * sign(e); end end逻辑说明用persistent变量保存三个状态避免使用全局变量。dt必须是 Simulink 模型里设置的固定步长比如 0.001 秒。param是 8 维数组前 4 个是b0和观测器增益后 4 个是 NESO 特有的非线性参数。每次调用先计算误差e再用欧拉法递推三个状态。这个实现方式有一个前提——必须用定步长求解器因为递推式里用到固定的dt。如果你用的是变步长求解器dt传进去的只是一个常量实际步长会变结果就是仿真发散或精度下降。参数说明b0是控制增益的估计值欠估计会导致z3稳态偏差beta1影响z1的收敛速度beta2影响z2beta3影响z3扰动估计的收敛速度。三者数量级通常相差很大后面整定章节细说。3.3 用基本模块搭建的等价方案如果不习惯用 MATLAB Function也可以用 Simulink 基本模块搭出同样的结构。做法是三个Integrator模块串联分别输出z1、z2、z3。第一个积分器输入是z2 - beta1 * fal(e)第二个是z3 - beta2 * fal(e) b0*u第三个是-beta3 * fal(e)。fal函数用Fcn模块写输入设为u(1)即误差e表达式写if(abs(u(1)) delta, u(1)/(delta^(1-alpha)), (abs(u(1))^alpha)*sign(u(1)))。注意Fcn模块里不能直接用abs之外的复杂函数sign要用sign()MATLAB 的Fcn支持这个语法。用基本模块的好处是每个信号都拉线可见适合教学演示缺点是模型图会比较大连线容易错。建议2.2 节提到的参数beta1 3*omega0、beta2 3*omega0^2、beta3 omega0^3在模型里用Gain模块的乘系数直接填表达式Simulink 支持在增益框里写计算式。3.4 求解器与步长设置NESO 的仿真必须注意求解器配置。打开Configuration Parameters → Solver求解器类型选定步长Fixed-step不要用变步长。求解器选ode4四阶龙格库塔或ode3两个都够用。ode4精度更高ode3速度更快。步长设0.001秒即 1 kHz。步长再大如 0.01 秒会导致观测器递推误差增大特别是z3的估计值会明显滞后步长再小如 0.0001 秒仿真时间会拉长 10 倍一般没必要。如果模型里有MATLAB Function模块定步长情况下 Simulink 会在每个步长调用一次该函数dt传参和实际步长一致。提示步长和观测器带宽ω0之间有关系。经验上要求dt * ω0 0.1否则离散化误差会积累最终表现为高频震荡或发散。4. NESO 的六个关键参数与整定步骤4.1 参数速查表NESO 在实际调试中需要关注的参数一共有六个再加一个b0总共七个。下表给出每个参数的作用和典型范围参数含义典型范围对行为的主导影响ω0观测器带宽5 ~ 100收敛速度与噪声放大b0控制增益估计0.5b ~ 1.5b扰动估计的稳态偏差α1fal 第一个幂次0.5 ~ 1.0z1跟踪误差的压缩程度α2fal 第二个幂次0.5 ~ 1.0z2对速度估计的灵敏度α3fal 第三个幂次0.1 ~ 0.5z3扰动估计的收敛速度δfal 线性区间宽度0.005 ~ 0.1稳态误差与噪声抑制的权衡h采样步长固定0.0001 ~ 0.01离散化精度不算可调参数但影响上限上面的范围是针对一般二阶机械系统、仿真步长 1 kHz 的经验值。电机控制里ω0常取 20~50飞行器姿态控制里取 10~30具体要看传感器噪声水平。噪声大就调低ω0和调大δ。α的取值逻辑α1和α2可以取同一个值一般 0.5 左右α3要小一些比如 0.25这样可以加快对扰动变化率的跟踪但代价是容易引入高频分量。4.2 从带宽法出发的整定顺序NESO 虽然有七个参数但不要一上来就全部手动调。常见的做法是先用线性 ESO 把ω0和b0调好再引入非线性项。具体顺序先把fal换成线性即令fal(e) e。此时α和δ参数无效只留β1 3ω0、β2 3ω0²、β3 ω0³。从ω0 20开始观察z1对测量信号y的跟踪误差。如果跟踪慢逐步增大ω0到 40、60如果z1出现高频毛刺或震荡说明ω0太高或步长不够小回调一档。调b0。给被控对象加一个已知的阶跃扰动比如w 1看z3的稳态值。如果z3稳态值大于 1说明b0偏小扰动被放大如果小于 1说明b0偏大。调整b0使z3稳态值接近真实扰动幅值。切换为 NESO。把误差通路换成fal函数α1 α2 0.5α3 0.25δ 0.01。保持β不变观察z3的响应曲线。精调δ。如果z3在稳态时有明显噪声逐步增大δ到 0.02、0.05观察噪声是否被压下去。如果z3对扰动的响应出现明显滞后逐步减小δ。δ每改动一次至少跑完一个完整仿真周期再看不要看瞬间波形。微调α3。如果z3对突变扰动的超调量太大把α3从 0.25 降到 0.1如果扰动变化快但z3跟不上把α3提高到 0.4。这套顺序的核心思想是用带宽法锁定稳定性区间再用非线性项改善性能避免上来就跌入多维参数搜索。4.3 仿真发散排查模型刚性、代数环、初始值NESO 仿真发散是最常见的问题特别是在ω0调大之后。发散来源一般有三个第一个是模型刚性。观测器带宽ω0大意味着闭环响应快系统动力学出现时间尺度差异。Simulink 的变步长求解器会发现步长被压得极小但误差仍然超限最终报错或输出无穷大。排查方法是打开求解器的Max Step Size手动设置为dt的 10 倍看是否改善。如果仍然发散很可能是ω0 * dt 0.1把ω0调低或减小步长。第二个是代数环。当MATLAB Function模块的输出直接反馈回同一个模块的输入时Simulink 会提示 Algebraic Loop。NESO 的方程是显式递推理论上不会产生代数环但如果把z3直接接回了被控对象模型的某个输入而这个输入又决定y就会形成环。排查方法是看诊断窗口有没有 Algebraic Loop 警告。解决方法是把反馈信号用Unit Delay模块延迟一个步长或者把控制律放在同一个 MATLAB Function 里统一计算。第三个是初始值不匹配。观测器的初始状态z1(0)、z2(0)和被控对象的真实初始状态偏差过大时初始误差会经过β3放大导致z3在仿真起始瞬间出现巨大的尖峰尖峰又反馈到控制量造成发散。处理方式有两种一是把观测器初值设成与对象状态一致二是在z3输出后加一个Saturation模块限幅范围设为真实扰动可能最大值的 1.5 倍。提示如果你拿到手的模型一开始就跑不动先检查是不是所有Gain模块的增益都填了数值而不是变量名。MATLAB Function里引用的变量如果不在param数组中也会报未定义错误。5. NESO 仿真验证技巧三种检验方法与实用建议5.1 零初值阶跃扰动估计验证搭好模型后第一个验证实验用阶跃扰动。给被控对象注入一个幅值 1.0 的Step信号作为w(t)控制输入u保持为 0。运行仿真后把z3和真实扰动w画在同一张图上。正确的行为是z3在大约5/ω0秒内追上w的幅值稳态误差不超过 5%。如果z3稳态值低于 1检查b0是否偏大如果高于 1检查b0是否偏小。z1应该紧密贴合y误差量级在1/(ω0²)左右这是带宽法给出的理论精度。5.2 正弦扰动跟踪对比阶跃扰动验证的是稳态精度正弦扰动验证动态跟踪能力。把w(t)换成幅值 0.5、频率 5 Hz 的正弦波。在 NESO 输入端加一组正弦波发生器Simulink 里用Sine Wave模块幅值 0.5频率 2π×5 rad/s。观察z3对正弦扰动的跟踪幅值和相位差。NESO 相对 LESO 的优势这里会体现出来——同样ω0下z3的相位滞后更小、幅值衰减更少。如果z3的波形明显畸变把α3往小调比如 0.1畸变通常会缓解。5.3 NESO 输出接入控制器的闭环验证z3补偿方向测试NESO 估计出总扰动z3后最终要进入控制律做前馈补偿。常用的 ADRC 控制律是u (u0 - z3) / b0其中u0是反馈控制器比如 PD的输出。这里有一个特别容易出错的点补偿符号。如果符号反了z3会把扰动推得更大而不是抵消。测试方法给系统加一个正向阶跃扰动观察被控对象输出。若补偿方向正确输出的偏移量会明显减小若方向错误输出会朝扰动方向加速跑偏。排查方向错了就在控制律里把减法改成加法看波形是否恢复正常。在把 NESO 模型用于生成嵌入式代码前还要做两个检查一是确认MATLAB Function里的所有运算都是定点和离散兼容的persistent变量不能被代码生成器优化掉二是检查dt是否与实际中断周期一致这直接决定z3在硬件上的估计精度。Simulink 的Code Generation选项里有静态代码检查工具跑一次能发现不少隐藏的类型不匹配问题。本文还有配套的精品资源点击获取