4、龙伯格观测器仿真:基于MATLAB/Simulink的仿真实现

好,咱们前面把龙伯格观测器的理论讲透了。说实话,理论再漂亮,不上手跑一遍仿真,心里总是不踏实。我个人习惯是,先搭个简单的模型,看看状态估计到底能不能跟上实际值。这一章,我就带你手把手把仿真搭起来。

4.1 仿真环境与模型准备

我用的是MATLAB R2020b,Simulink版本差别不大。你想想看,咱们要仿真的是一个电机系统,核心就是那三个状态:电流、转速、位置。嗯,这里要注意,仿真模型里一定要把实际系统和观测器分开建,不然你分不清哪个是真实值,哪个是估计值。

我建议你新建一个模型,结构大概这样:

  • 实际电机模型:用状态空间方程搭,输入是电压,输出是电流和转速
  • 龙伯格观测器:复制一份电机模型,但输入是电压+电流反馈
  • 增益矩阵L:这个就是咱们上一章算出来的极点配置结果

我在项目中遇到过一个问题:有人直接把实际模型和观测器用同一个模块,结果仿真跑出来两条线完全重合。这其实不对,因为观测器的意义就在于有误差时能收敛。所以一定要给实际模型加一点初始偏差。

4.2 核心参数设置

咱们用一个永磁同步电机的简化模型来做演示。参数如下:

参数 符号 数值 单位
定子电阻 Rs 0.5 Ω
d轴电感 Ld 0.008 H
q轴电感 Lq 0.008 H
永磁磁链 ψf 0.175 Wb
转动惯量 J 0.001 kg·m²
阻尼系数 B 0.0001 N·m·s

观测器的增益矩阵L,我按极点配置法算出来是这样的:

% 极点配置代码
A = [ -R/Ld,  w*Lq/Ld,  0;
      -w*Ld/Lq, -R/Lq,  -psi_f/Lq;
       0,      0,       0 ];

C = [1 0 0; 0 1 0];  % 测量电流

% 期望极点:比系统极点快3-5倍
poles = [-200, -250, -300];
L = place(A', C', poles)';

为什么选这三个极点?说白了,观测器的收敛速度要比系统本身的动态快。我曾经试过把极点设得太快,结果仿真直接发散——因为增益太大,把测量噪声也放大了。这个坑我踩过,你一定要注意。

4.3 Simulink模型搭建步骤

咱们一步步来。打开Simulink,新建一个空白模型。

  1. 拖入State-Space模块:作为实际电机模型。矩阵A、B、C、D填好,初始状态设成[0; 0; 0]。
  2. 再拖一个State-Space模块:作为观测器。矩阵A、B、C、D完全一样,但初始状态设成[1; 0.5; 0.1]——故意给个偏差。
  3. 加入增益矩阵L:用Gain模块,把L矩阵填进去。注意维度,L是3×2的矩阵。
  4. 反馈回路:实际模型的电流输出减去观测器的电流输出,乘以L,再加到观测器的状态导数上。

嗯,这里有个细节。观测器的输入有两路:一路是控制电压u,另一路是电流误差反馈。你需要在观测器的输入端加一个Sum模块,把这两路加起来。

关键连接关系:

观测器状态导数 = A·x̂ + B·u + L·(y - ŷ)

其中 y 是实际电流,ŷ 是观测器输出的估计电流。

4.4 仿真结果分析

跑一下仿真,时间设0.5秒。你会看到什么?

  • 电流估计:大概0.02秒内,估计电流就追上实际电流了。因为电流动态快,极点又设得高。
  • 转速估计:慢一点,大概0.1秒左右收敛。毕竟转速是机械量,惯性大。
  • 位置估计:这个最慢,因为位置是转速的积分,误差累积需要时间消除。

为什么会这样?你想想看,观测器的收敛速度取决于极点位置。电流对应的极点我设了-200,转速对应的极点-250,位置对应的-300。按理说位置应该最快,但实际仿真中位置误差的初始值最大,所以看起来收敛慢。

我的调试技巧:

如果你发现观测器收敛太慢,别急着调高增益。先检查一下初始误差是不是太大了。我曾经有个项目,初始位置误差设了90度,结果观测器花了0.5秒才追上。后来我把初始误差降到30度,0.1秒就收敛了。

4.5 常见问题与避坑指南

仿真过程中,你可能会遇到这些问题:

⚠️ 仿真发散

我曾经把极点设到-1000,结果仿真直接炸了。原因是增益太大,数值积分不稳定。解决办法:检查采样时间,或者用隐式求解器(比如ode15s)。

⚠️ 估计值震荡

如果估计值在真实值附近来回震荡,说明极点虚部太大。我建议你把极点都设在实轴上,或者阻尼比大于0.7。

⚠️ 稳态误差不为零

这个最常见。如果模型参数不准(比如电阻实际是0.6Ω,你用了0.5Ω),观测器会有稳态误差。解决办法:要么加积分项(变成龙伯格-积分观测器),要么在线辨识参数。

4.6 代码实现参考

如果你不想用Simulink,也可以用m脚本跑。我个人更喜欢脚本,因为调试方便。下面是一个完整的仿真代码:

% 龙伯格观测器仿真 - m脚本版本
clear; clc;

% 电机参数
Rs = 0.5; Ld = 0.008; Lq = 0.008;
psi_f = 0.175; J = 0.001; B = 0.0001;
w = 100; % 电角速度

% 状态空间矩阵
A = [-Rs/Ld, w*Lq/Ld, 0;
     -w*Ld/Lq, -Rs/Lq, -psi_f/Lq;
     0, 0, 0];
B = [1/Ld, 0; 0, 1/Lq; 0, 0];
C = [1 0 0; 0 1 0];

% 观测器增益
poles = [-200, -250, -300];
L = place(A', C', poles)';

% 仿真参数
dt = 1e-4; T = 0.5;
N = T/dt;
t = linspace(0, T, N);

% 初始化
x_real = [0; 0; 0];  % [id; iq; omega]
x_obs = [1; 0.5; 0.1]; % 故意给偏差
u = [10; 10]; % 电压输入

% 存储数据
x_real_log = zeros(3, N);
x_obs_log = zeros(3, N);

% 仿真循环
for k = 1:N
    % 实际系统
    y_real = C * x_real;
    dx_real = A * x_real + B * u;
    x_real = x_real + dx_real * dt;
    
    % 观测器
    y_obs = C * x_obs;
    dx_obs = A * x_obs + B * u + L * (y_real - y_obs);
    x_obs = x_obs + dx_obs * dt;
    
    % 记录
    x_real_log(:, k) = x_real;
    x_obs_log(:, k) = x_obs;
end

% 画图
figure;
subplot(3,1,1);
plot(t, x_real_log(1,:), 'b', t, x_obs_log(1,:), 'r--');
legend('实际id', '估计id');
subplot(3,1,2);
plot(t, x_real_log(2,:), 'b', t, x_obs_log(2,:), 'r--');
legend('实际iq', '估计iq');
subplot(3,1,3);
plot(t, x_real_log(3,:), 'b', t, x_obs_log(3,:), 'r--');
legend('实际转速', '估计转速');

跑完这个代码,你会看到三条曲线,估计值从初始偏差开始,逐渐收敛到实际值。嗯,这就是龙伯格观测器在干活了。

4.7 小结

这一章咱们把仿真跑通了。说白了,龙伯格观测器的仿真就三步:搭模型、设增益、看收敛。你在实际项目中,可能会遇到参数不准、噪声干扰等问题,但核心原理不变。

下一章,咱们会讨论观测器在实际嵌入式系统里怎么实现——毕竟仿真跑得再好,代码写不对也是白搭。

本章要点回顾:

  • 实际模型和观测器要分开建,初始状态故意给偏差
  • 极点位置决定收敛速度,一般比系统极点快3-5倍
  • 增益太大容易发散,太小收敛慢,需要权衡
  • 参数不准会导致稳态误差,需要加积分或在线辨识