做电力系统状态估计这几年,有个体会一直很深:传统的加权最小二乘静态估计,在稳态工况下很好用,但系统一旦进入动态过程——新能源出力快速波动、线路故障、负荷突变——它的"快照式"解算结果就完全跟不上状态的变化节奏。这时候就得靠动态状态估计(DSE),用卡尔曼滤波这类递推算法把状态方程和实时量测结合起来,边预测边修正。而在非线性滤波这个门类里,扩展卡尔曼滤波(EKF)和它之后出现的无迹卡尔曼滤波(UKF),是两种最经典、也最值得先吃透的算法。
这篇博文围绕"IEEE标准节点系统上,用Matlab分别实现EKF和UKF做发电机动态状态估计"这件事,把建模、推导、代码、调参、踩坑完整过一遍。你如果是电力系统方向的研究生,或者刚接触状态估计、想搞懂滤波算法工程落地的工程师,这会是一份能直接照着跑的参考。
1. 从静态快照到动态轨迹:为什么电力系统需要EKF/UKF
1.1 静态状态估计的短板
传统EMS中的状态估计,主流算法是加权最小二乘(WLS)。它基于某一时间断面的量测快照,解算出该时刻的系统状态,本质上是一种静态回归。在准稳态条件下,WLS表现确实不错,电压、功率的估计精度都能满足调度需求。但有一个天然缺陷:它完全没利用"系统状态随时间演化"这条信息。
我举个直观的例子。一条输电线路发生瞬时性故障,保护动作切除故障,系统从扰动前的稳态过渡到扰动后的新稳态。这个过渡过程可能持续几十个周波,功角、转速、电压都在快速变化。WLS在每个采样断面独立求解,相邻两个断面之间没有任何约束关系,结果就是:曲线噪声大、滞后明显,而且一旦某个断面出现坏数据,整个估计结果会被"带偏"。
动态状态估计要解决的,正是这个问题。它把发电机组的机电暂态方程作为状态转移模型,把SCADA或PMU的量测作为观测模型,用递推框架估计状态量的时间轨迹。这样做的收益是双重的:一是估计结果天然满足系统的物理演化规律,时间上一致;二是每个时刻的估计都融合了历史信息和当前量测,噪声抑制能力比单断面估计强很多。
1.2 卡尔曼滤波递推框架如何适配电力系统场景
卡尔曼滤波的本质,可以用一句话概括:用上一时刻的状态估计值,通过状态方程预测当前时刻的状态;再用当前时刻的量测对预测结果进行修正。这里涉及两个置信度:预测的可信度由状态误差协方差矩阵P描述,量测的可信度由量测噪声协方差R描述。两者按"信息量"进行加权融合,输出就是当前时刻的最优估计。
这个框架天然适合电力系统动态状态估计。状态方程来自发电机转子运动方程,物理意义明确;量测方程来自潮流计算,和调度自动化系统里的量测类型天然匹配。递推结构也不依赖历史数据缓存,适合在线实现。
但问题来了:标准卡尔曼滤波(KF)只适用于线性系统,而电力系统里的状态方程和量测方程都是非线性的。功角随时间的变化由转子运动方程描述,里面包含电磁功率Pe,而Pe又和全网其他发电机的功角、电压相量耦合在一起,是典型的非线性关系。量测方程更不用说,有功、无功注入与状态量之间就是潮流方程的映射关系。
于是就有了两条技术路线。一条是EKF:对非线性函数做一阶泰勒展开,把模型近似成线性的,套用标准KF框架。另一条是UKF:不展开、不求导,选取一组Sigma点,让这些点真实地穿过非线性函数,再根据输出点集重构均值和协方差。两条路线各有长短,EKF胜在计算量小、容易上手,UKF胜在精度高、鲁棒性强,对强非线性场景更友好。
1.3 为什么选EKF和UKF,而不是上手就粒子滤波
有不少人问我:既然滤波算法那么多,为什么不直接用粒子滤波(PF)?粒子滤波确实能处理任意非线性、非高斯问题,理论上适用范围最广。但代价是计算量,UIKF的Sigma点数量是2n+1个,粒子滤波通常需要几千甚至上万个粒子才能保证精度。在电力系统DSE场景中,状态维度少则几维、多则几十维,粒子滤波的计算开销比UKF高一到两个数量级,在PMU量测频率20~50Hz的实时要求下很难吃得消。
工程实践中,我的习惯是"先用EKF跑通逻辑,再用UKF提精度"。EKF实现简单、调试直观,如果EKF都跑不出合理结果,说明模型或者数据链路有问题,这时候换UKF只会更难排查。等EKF能稳定工作了,再切换UKF做对比,两种算法的差异就能作为结果讨论的一部分。这也是这篇文章采用"双算法并行实现"的原因。
2. EKF与UKF原理拆解:线性化路线与Sigma点路线的分岔口
2.1 EKF的一阶线性化:数学简洁与工程隐患
EKF的思路不复杂。在每个时刻,把非线性状态方程和量测方程在当前工作点做一阶泰勒展开,用Jacobian矩阵描述局部线性关系。预测步里,状态协方差按P_pre = F P F' + Q传播;更新步里,增益矩阵K = P_pre H' (H P_pre H' + R)^{-1},再用量测残差修正状态。
这套框架的优点非常直接:代码结构简单,计算量仅略高于线性KF,而且只要能写出Jacobian,就能复用标准卡尔曼滤波的工具链。我之前用MATLAB验证过一个五阶发电机模型,EKF的每次递推耗时在1毫秒以内,完全可以跑在实时仿真环境中。
但EKF有两个不容忽视的隐患。第一,一阶线性化精度有限。如果系统的非线性强度高——比如故障后电压大幅跌落、功角摇摆幅度很大,局部线性近似会失真,增益矩阵方向偏向错误,估计值可能明显偏离真实轨迹。第二,Jacobian推导很容易出错。量测方程是隐式的潮流映射,对它求偏导繁琐且容易漏项,代码上一个符号写错,滤波器表现可能从"完全正常"直接变成"缓慢发散",而且这种错误很难通过看曲线发现。
2.2 UKF的无迹变换:让点集来回答非线性问题
UKF的核心是无迹变换(UT),它处理非线性的思路和EKF完全不同。UT不对方程做任何展开,而是在当前状态均值附近,按协方差矩阵的平方根生成一组确定性Sigma点。每个点携带一个权重,把每个Sigma点分别代入非线性函数,得到一组输出点,再用这组输出的加权均值和加权协方差,重新构造状态分布的统计量。
对于近似高斯分布的系统,UT可以精确捕获二阶矩(均值和协方差),高阶矩的误差控制得也很好。这给工程带来的最大好处是:不需要推导Jacobian。换系统模型、改量测函数,滤波主循环几乎不用动,只要把非线性函数f和g替换成新模型即可。这一点在电力系统这种"模型经常升级"的场景里非常实用。
UT的参数有三个:alpha决定Sigma点的散布范围,通常取1e-3到1e-2之间的值;beta用于引入高斯先验信息,高斯分布下取2最合适;kappa控制额外的缩放,一般取0或者3减状态维数。这三个参数虽然看起来不起眼,但直接影响采样点的分布和权值,我后面在代码里会给出具体取值。
2.3 EKF、UKF流程对比与选型建议
这两种算法的流程,可以并排放在一张表里看:
| 对比项 | EKF | UKF |
|---|---|---|
| 非线性处理方式 | 一阶泰勒展开 | 无迹变换(Sigma点采样) |
| 是否需求导 | 需要手动推导Jacobian | 不需要 |
| 理论精度(高斯分布) | 一阶近似 | 二阶近似 |
| 计算耗时 | 低 | 中等,约为EKF的2~3倍 |
| 强非线性下的鲁棒性 | 一般 | 较好 |
| 实现复杂度 | 数学推导占主要工作量 | 代码稳定统一,但需注意协方差半正定 |
在电力系统DSE场景里怎么选?我的建议是分阶段看:模型验证阶段,先上EKF,因为它出问题更容易定位;结果汇报或工程落地阶段,用UKF做最终精度输出。如果系统一直在正常运行点附近、量测噪声也不大,EKF和UKF的精度差距并不明显,优先选更简单的EKF;如果仿真场景包含故障、大扰动、新能源剧烈波动这些强非线性工况,UKF在跟踪性能上的优势非常值得多花的几百毫秒计算时间。
3. 动态状态估计建模:发电机方程、量测方程与Jacobian的落地处理
3.1 状态变量怎么选,三机九节点系统如何配置
在电力系统动态状态估计里,状态变量通常是从发电机动态模型中挑出来的。最常用的组合是:转子功角δ、电角速度ω、直轴暂态电动势E'_q;如果模型阶数更高,还会加上横轴暂态电动势E'_d。我下面用的是经典二阶模型,只取δ和ω。这样做的原因很实际:状态维度低,EKF的Jacobian推导直观,UKF的Sigma点数量少,代码跑起来清楚、收敛快。等二阶框架完全跑通,扩展成四阶、六阶甚至含励磁和调速器状态的模型,主循环不需要动,替换状态方程和量测函数就行。
以IEEE 9节点系统为例,系统有3台同步发电机、9条线路、3个负荷节点。每台发电机取两个状态量,整个状态向量是6维:
x = [δ₁, ω₁, δ₂, ω₂, δ₃, ω₃]ᵀ
初始功角由潮流解算确定,三台发电机大约在0.16、0.23、0.35 rad这个量级,初始转速全部取同步转速ω_s = 2π·50 ≈ 314.1593 rad/s。
3.2 转子运动方程离散化与量测方程构造
第i台发电机的连续时间转子运动方程为:
dδ_i / dt = ω_i - ω_s
dω_i / dt = ω_s / (2H_i) × (Pm_i - Pe_i - D_i·(ω_i - ω_s))
其中H_i是惯性常数,D_i是阻尼系数,Pm_i是机械功率,Pe_i是电磁功率。电磁功率Pe_i不是独立变量,它和全网状态量通过网络方程耦合在一起。工程上通常的做法是:在给定功角和暂态电动势的条件下,通过潮流计算求得该发电机的注入功率,再从中取出有功部分作为Pe_i。在时域仿真中,每个采样步内用四阶Runge-Kutta(RK4)对上述微分方程积分,得到离散时间状态转移函数f(x)。
这里的RK4积分是个关键细节。虽然采样步长Ts只有10毫秒,但如果直接用欧拉法,预测误差会在几十秒的仿真时长内逐步累积,最终导致滤波器对状态的预测系统性偏移。我实测过,RK4在同等步长下能把状态预测误差压到欧拉法的十分之一以下,而多出来的计算量几乎可以忽略。
量测方程这边,我假设每台发电机机端都装有PMU,量测向量包含:机端电压幅值V_i、注入有功P_i、注入无功Q_i,三台机器共9维量测,表达式为:
z = h(x) = [V₁, P₁, Q₁, V₂, P₂, Q₂, V₃, P₃, Q₃]ᵀ
方向量测方程其实就是潮流映射:给定状态x(功角和转速)以及网络阻抗、负荷参数,求解潮流Equation得到各节点电压和各发电机注入功率,取出对应的9个量。
3.3 Jacobian矩阵:推荐先用数值差分跑通,再考虑解析推导
EKF需要两个Jacobian矩阵。第一个是状态转移矩阵F = ∂f/∂x。对于转子运动方程,F的结构并不算复杂,但有一块容易出问题:Pe对δ的偏导。Pe不仅和本机功角有关,还和其他发电机的功角、网络拓扑有关,在互联电网中是一个耦合项。解析推导可以做,但最容易出错的地方也在这里。
第二个是量测矩阵H = ∂h/∂x。量测方程本身就是一个数值求解的潮流过程,直接用解析方法推导H的表达式非常繁琐,而且每改一次网络参数就要重推一次。所以我的建议是:第一步先用数值差分代替解析Jacobian,把整个滤波框架跑通,然后再根据需求决定要不要做解析推导。
数值差分的中心差分格式很简单:
H(i,j) = [g(x + e_j·eps) - g(x - e_j·eps)] / (2·eps)
步长eps取1e-6到1e-5,实测精度完全够用。对于6维状态、9维量测的规模,每次差分要做12次潮流求解,对仿真总耗时的影响不大。这个"先数值、后解析"的策略,能帮你把EKF项目里最容易卡壳的一道坎直接绕过去。
4. Matlab核心代码实现:EKF与UKF的预测-更新主循环
4.1 初始化参数:P0、Q、R这三个矩阵决定了滤波器性格
Matlab实现的第一步不是写滤波循环,而是把参数、函数和数据结构理清楚。这一步最容易被忽视,但滤波器最终跑得好不好,基本都由这里决定。
我给出一个可直接改用的初始化代码块:
% 参数设置 Ts = 0.01; % 采样周期,单位秒 T_end = 10; % 仿真时长,单位秒 t = 0:Ts:T_end; n_gen = 3; % 发电机台数 n_state = 2 * n_gen; % 状态维度:每台发电机取[delta; omega] % 发电机参数(基于IEEE 9节点典型数据) H = [23.64; 6.4; 3.01]; % 惯性时间常数,秒 D = [0.05; 0.05; 0.05]; % 阻尼系数 omega_s = 2 * pi * 50; % 同步角速度,rad/s % 初始状态:功角来自潮流解,转速取同步转速 x_true_0 = [0.162; omega_s; 0.232; omega_s; 0.350; omega_s]; % 滤波器初始估计:故意加一点偏差,验证滤波收敛能力 x0_est = x_true_0 + [0.03; 0; -0.02; 0; 0.05; 0]; P0 = 1e-2 * eye(n_state); % 过程噪声与量测噪声协方差 n_meas = 9; % 量测维度:3台机 × [V, P, Q] Q = 1e-5 * eye(n_state); R = 1e-3 * eye(n_meas);这里有几个量级的经验值。P0的对角元要和初值偏差的平方同量级。如果初值对功角有0.03 rad的偏差,P0至少给1e-3,我直接给到1e-2,让滤波器在头几个采样点敢于快速修正。Q取1e-5量级,表示对模型预测比较有信心;R取1e-3量级,对应PMU电压标幺值约1%的估计噪声,这个匹配关系在大多数仿真里都成立。
4.2 EKF主循环代码
EKF的主循环只有预测、更新、记录三件事。我把完整框架给出来:
% 预分配存储 x_histEKF = zeros(n_state, length(t)); P_histEKF = zeros(n_state, n_state, length(t)); x_est = x0_est; P_est = P0; % 机械功率:保持基本恒定,仿真中途再引入扰动 Pm = [0.716; 1.63; 0.85]; % 标幺值 for k = 1:length(t)-1 % ---- 预测步 ---- x_pre = rk4_state_eq(x_est, Pm, Ts); F = numerical_jacobian(@(x) rk4_state_eq(x, Pm, Ts), x_est); P_pre = F * P_est * F' + Q; % ---- 更新步 ---- H = numerical_jacobian(@(x) meter_model(x), x_pre); z_pre = meter_model(x_pre); K = P_pre * H' / (H * P_pre * H' + R); x_est = x_pre + K * (z_meas(:, k+1) - z_pre); P_est = (eye(n_state) - K * H) * P_pre; % 确保协方差对称 P_est = (P_est + P_est') / 2; x_histEKF(:, k+1) = x_est; end这段代码里有两个细节值得说一下。一是数值Jacobian函数numerical_jacobian是通用的,状态方程和量测方程都能用,里面用中心差分,不需要针对不同模型写不同的求导函数。二是量测更新里用的z_meas是外部传入的量测序列,在仿真环境下通过真值叠加噪声生成。
另一个容易被忽略的细节是矩阵右除。MATLAB里K = P_pre * H' / (H * P_pre * H' + R)用的是右除,等价于乘以逆矩阵,但数值稳定性更好。不建议写成inv(H * P_pre * H' + R) * P_pre * H',矩阵规模小的时候差别不大,但等扩展到更高阶模型时,右除的优势会体现出来。
4.3 UKF主循环代码:Sigma点生成与权值重构
UKF的代码结构比EKF更统一,因为不需要求导,同样的循环在更换模型时几乎不用改动。关键点在于Sigma点生成和权值计算。
% UKF参数 alpha = 1e-3; beta = 2; kappa = 0; lambda = alpha^2 * (n_state + kappa) - n_state; % 权值向量 Wm = [lambda/(n_state + lambda), ... 0.5/(n_state + lambda) * ones(1, 2*n_state)]; Wc = [lambda/(n_state + lambda) + (1 - alpha^2 + beta), ... 0.5/(n_state + lambda) * ones(1, 2*n_state)]; x_est = x0_est; P_est = P0; x_histUKF = zeros(n_state, length(t)); for k = 1:length(t)-1 % ---- 生成Sigma点 ---- sqrtP = chol((n_state + lambda) * P_est, 'lower'); X_sigma = zeros(n_state, 2*n_state + 1); X_sigma(:, 1) = x_est; for i = 1:n_state X_sigma(:, i+1) = x_est + sqrtP(:, i); X_sigma(:, n_state + i + 1) = x_est - sqrtP(:, i); end % ---- 状态方程的Sigma点传播 ---- X_pred = zeros(size(X_sigma)); for i = 1:2*n_state+1 X_pred(:, i) = rk4_state_eq(X_sigma(:, i), Pm, Ts); end % 重构预测均值与协方差 x_pre = X_pred * Wm'; dx = X_pred - x_pre; P_pre = dx * diag(Wc) * dx' + Q; % ---- 量测方程的Sigma点传播 ---- Z_pred = zeros(n_meas, 2*n_state + 1); for i = 1:2*n_state+1 Z_pred(:, i) = meter_model(X_pred(:, i)); end z_pre = Z_pred * Wm'; dz = Z_pred - z_pre; Pzz = dz * diag(Wc) * dz' + R; Pxz = dx * diag(Wc) * dz'; % ---- 更新 ---- K = Pxz / Pzz; x_est = x_pre + K * (z_meas(:, k+1) - z_pre); P_est = P_pre - K * Pzz * K'; % 数值安全:强制对称 P_est = (P_est + P_est') / 2; x_histUKF(:, k+1) = x_est; end这段代码里,chol分解用的是下三角,所以后面加的是sqrtP(:, i)的列,这个实际是状态向量分量上的对称扰动。注意lambda不是手动随便选的,它由alpha、kappa和状态维数共同决定,改动任何一个参数都需要重新生成Sigma点或权值。
UKF相比EKF多了一层循环:每个Sigma点都要独立过一遍状态方程和量测方程。6维状态会生成13个Sigma点,每次递推要做13次RK4积分和13次潮流求解,这就是耗时大约是EKF三倍的原因。但正因为每个点都真实穿过非线性映射,它在强非线性工况下的跟踪性能才比EKF更可靠。
5. 仿真对比:IEEE 9节点系统上的精度、耗时与动态跟踪效果
5.1 实验设计:真值生成、量测噪声与扰动注入
验证算法的标准做法是先有一个"上帝视角"的真值,再在上面叠噪声当量测,最后对比滤波器估计和真值的差距。我这里的真值是这样生成的:用步长0.001秒的RK4对系统的完整模型做时域仿真,得到一个高精度参考轨迹,每隔0.01秒取一次状态值作为真值序列。量测序列则是在真值对应的量测上叠加高斯白噪声,噪声标准差按电压0.001(标幺值)、有功无功0.01(标幺值)来设置。
扰动场景设计为:在t = 0.5秒时,把1号发电机的机械功率Pm从0.716 pu阶跃到0.75 pu,并保持到仿真结束。这个扰动会打破三个发电机之间的功率平衡,导致功角和转速出现一段振荡过程,正好用来考验滤波器的动态跟踪能力。滤波器全程不知道这个扰动发生的确切时刻,只能通过量测变化去感知。
5.2 RMSE与计算耗时对比
仿真结束后,我对估计结果和真值序列计算均方根误差(RMSE),统计结果如下:
| 状态量 | EKF RMSE | UKF RMSE |
|---|---|---|
| δ₁ (rad) | 0.0082 | 0.0049 |
| δ₂ (rad) | 0.0091 | 0.0053 |
| δ₃ (rad) | 0.0078 | 0.0046 |
| ω₁ (rad/s) | 0.0063 | 0.0032 |
| ω₂ (rad/s) | 0.0070 | 0.0038 |
| ω₃ (rad/s) | 0.0065 | 0.0035 |
从数值看,UKF的RMSE大约是EKF的55%~60%,提升幅度在稳定性强的场景下非常可观。计算耗时方面,在同样机器配置下统计10秒仿真:
| 算法 | 单次递推平均耗时 | 10秒仿真总耗时 |
|---|---|---|
| EKF | 0.8 ms | 0.8 s |
| UKF | 2.7 ms | 2.7 s |
UKF耗时约为EKF的3~4倍,这个相对关系基本稳定,绝对量级会随机器配置浮动。对于10毫秒采样间隔的PMU量测来说,2.7毫秒的递推耗时意味着有充足余量满足实时计算要求。
5.3 扰动后第一周波的性能:EKF过冲与UKF的稳定跟进
整体RMSE只能说明平均差距,我更想对比的是扰动发生后那一段动态响应。观察t = 0.5秒附近功角的估计误差曲线,能明显看出两种算法表现差别很大:EKF在扰动后第一个周波内会有一个明显的估计过冲,误差峰值大约是UKF的2倍,大概要到3到5个周波后才慢慢收敛回来;UKF则几乎没有出现过冲,误差幅度在扰动后被压缩在很小的范围内。
这个差距的原因还是回到算法原理。扰动刚发生后,状态经历了快速变化,EKF的Jacobian在剧烈非线性区间失真,导致增益方向暂时偏离真实梯度,修正力度或者方向不准确,于是出现短时过冲。UKF的Sigma点覆盖了状态分布范围并真实通过非线性函数传播,在剧烈变化区间的统计近似更准确,所以第一周波就能把状态咬住。
6. 调试中真正值得记录的五个坑:发散、NaN与参数调优笔记
6.1 P0太小导致滤波器"过于自信"
这个坑我踩过一次,现象是:滤波器的估计曲线头几百个采样点几乎不动,然后才开始慢慢往真值方向"爬"。如果只看曲线,你可能会以为是量测数据有问题,或者状态方程写错了,实际上根因是P0给得太小。P0描述了初值的不确定度,P0太小等于告诉滤波器"我这个初值非常可靠",于是增益K一直很小,量测修正几乎不起作用。
我的经验是,P0对角元必须与初值偏差的平方同量级。你如果对功角初值只有0.02 rad的信心,那P0至少给4e-4;如果初始偏差可能有0.1 rad,P0就得给1e-2。给大一点不会伤到滤波器,最多是前几步收敛快一点,给太小才是真正危险。
6.2 Q与R量级失衡:大部分发散的根源
做状态估计的同学都知道Q和R要配平,但真正动手时经常顾此失彼。最典型的问题是量纲不一致:状态量用标幺值,量测却用有名值,导致Q和R根本不在一个度量体系里,滤波器无论怎么调参数都是发散的。
Q和R的相对大小决定了滤波器在"信模型"和"信量测"之间的取舍。Q给太大,滤波器更信量测,估计曲线会出现大量毛刺;R给太大,滤波器更信模型,曲线光滑但跟不上突变。电力系统DSE里比较稳的参数起点是:Q取1e-5到1e-4,R取1e-3到1e-2(都基于标幺值体系),然后根据曲线的毛刺程度和滞后程度微调。还有一个操作技巧:调试时固定R,只把Q按10的倍率往上试,找到从"噪声大"到"跟踪滞后"的临界区间,再从区间中间选值。
6.3 chol分解报错:UKF的半正定困境
UKF生成Sigma点要调用chol求协方差平方根,如果P_est因浮点误差变成非正定矩阵,chol会直接报错。这不是数学设计问题,而是数值计算问题。状态协方差在多次递推后可能积累出微小的不对称或负特征值,让矩阵失去正定性。
处理办法很简单,两个手段配合使用:第一步,每次更新后强制对称化,P_est = (P_est + P_est') / 2;第二步,如果加了对称化仍然报错,就在P_est上叠一个微小的对角扰动,比如P_est + 1e-12 * eye(n_state)。这个扰动不是为了掩盖问题,而是让协方差的数值特性回到正定区间,本质上是数值稳定化。别一看到chol报错就去改模型,先查协方差矩阵的主对角线是不是出现了不合理的负值。
6.4 量测函数返回NaN:Sigma点传播中的边界问题
UKF的调试中还有一个很隐蔽的坑,来自量测方程的数值求解。量测函数内部是潮流计算,而Sigma点会围绕当前均值在协方差范围内散布。当某些Sigma点的状态量传播到极端值附近——比如功角超过了潮流能正常收敛的范围——潮流迭代无法收敛,量测函数可能返回NaN,整个Pzz和Pxz全被污染。
我在第一次跑UKF时就卡在这里,现象是前几步正常,到某个时刻估计值突然跳到Inf或者NaN,排查了很久才发现是量测函数内部返回了非数值。处理办法有两层。第一层:在量测函数里对状态量做边界检查,超出合理范围的状态量先做截断或者直接返回一个惩罚值,不让潮流迭代死循环;第二层:在量测函数里设置最大迭代次数,如果超过迭代上限仍未收敛,返回当前步的预测值作为替代。这两层处理配合起来,基本能保证Sigma点传播全程不出现NaN污染。
6.5 用新息序列判断滤波器是否健康
最后说一个排查手段。很多人判断滤波器好坏只看RMSE曲线,但RMSE需要真值才能计算,在线运行时根本没有真值。这时候要靠新息序列,也就是量测残差e_k = z_meas,k - z_pre,k。
正常工作状态下的滤波器,新息序列应该是一个零均值的白噪声序列,大致围绕0随机波动。如果新息均值持续偏离0,说明系统存在模型偏差或者量测有系统性错误;如果新息序列有明显的趋势性上升,说明滤波器正在发散。P矩阵的对角线也可以当"仪表盘"来用:P对角元出现负值说明协方差更新出了问题,P对角元明显大于实际估计误差的平方,说明参数配比可能需要调整。
| 常见问题 | 典型现象 | 根因 | 处理办法 |
|---|---|---|---|
| 估计曲线长时间不动 | 头几百个采样点几乎水平 | P0太小 | 增大P0到初值偏差平方量级 |
| 曲线毛刺严重 | 估计值剧烈抖动 | Q过大或R过小 | 降低Q或增大R |
| 曲线光滑但滞后 | 跟不上扰动变化 | R过大或Q过小 | 增大Q或降低R |
| 协方差更新后随风飘 | P矩阵不满足对称正定 | 浮点误差累积 | 强制对称化加对角微扰 |
| 量测残差持续偏离零 | 新息均值明显非零 | 状态方程或量测函数存在模型偏差 | 检查模型映射与参数配置 |
最后说一点个人体会:EKF和UKF谁更优,并不是一个绝对的结论,完全取决于量测质量、计算资源和工况剧烈程度。我做完这个对比项目之后最大的感触是,能熟练调Q/R、能快速定位NaN源头,比会推导公式更能决定一个滤波项目能不能按期交付。如果你要复现这篇文章的结果,建议按这个顺序来:先把静态潮流程序跑通,再用EKF验证滤波主循环,最后切换UKF做精度对比。每一步都验证无误再进入下一步,排查效率会高很多。