电力系统动态状态估计:EKF与UKF对比及Matlab仿真实战
2026/9/7 20:02:49 网站建设 项目流程

做电力系统动态状态估计仿真的时候,很多人都会在EKF和UKF之间犹豫。不是这俩算法有多玄乎,而是现实中的电力系统模型高度非线性,动态过程又夹杂着各种随机扰动——滤波器选不好,仿真图上那条估计曲线就敢给你偏到天边去。这篇文章我从项目角度把整套思路捋一遍,从问题建模到EKF和UKF的核心原理,再到Matlab代码实现、参数整定和实战避坑,希望对正在做相关课题的同学有一定参考价值。

先说清楚这套代码解决什么问题。传统电力系统状态估计大多用加权最小二乘,本质是静态估计,拿到的只是某一时间断面上系统状态的“快照”。可系统实际运行是动态的,尤其发生扰动之后,功角、转速这些状态在几十毫秒到几秒内快速变化,静态估计根本追不上。动态状态估计关心的是状态随时间演化的轨迹,EKF和UKF都属于递推贝叶斯滤波框架,核心差别在于对非线性的处理方式不同:EKF靠一阶泰勒展开硬线性化,UKF则用一组确定性采样点穿过非线性函数去逼近真实分布。搞懂这两种思路,你就知道自己该在什么场景下选谁了。

1. 项目整体设计与方案选型思路

1.1 为什么电力系统需要动态状态估计

很多初学者有个误区,觉得状态估计就是“用一堆量测量去算系统的电压幅值和相角”。这句话对了一半——它是电力系统状态估计的经典功能,但这只是静态估计的范畴。

动态状态估计的核心差异在于增加了一个“时间维度”。我拿到一组量测数据时,如果只做加权最小二乘,相当于只利用当前时刻的量测信息去拟合一个状态断面,前后时刻之间没有任何关联。但电力系统的状态量,比如发电机转子功角和转速,它们的变化本身是受转子运动方程约束的。也就是说,上一时刻的状态、系统参数和输入功率,完全可以推导出下一时刻状态的大致范围。动态状态估计就是把这个“时间演化规律”也利用起来,让估计结果不仅贴合量测,还尊重系统本身的物理动态。

这套公式的价值在电网发生扰动、系统进入动态过程的时候会特别明显。稳态截面下静态估计效果还可以,可一旦频率波动、功角摇摆,量测噪声和非线性程度都会加剧,静态估计的精度会打折扣。动态状态估计能输出一条连续的、平滑的状态轨迹,对后续的稳定评估和实时安全分析来说,参考价值要高得多。

1.2 EKF和UKF方案选型背后的取舍逻辑

既然要做动态状态估计,滤波器方案就绕不开。我一开始也纠结过,是直接用经典的EKF还是上UKF,后来干脆两个都写了一遍做对比。

EKF的思路是“线性化+标准卡尔曼滤波”,核心是把非线性状态方程和量测方程用一阶泰勒展开来近似。它的优势是计算量小,代码实现简单,在系统运行点偏离不大的情况下精度尚可。缺点是强非线性环境下的一阶截断近似会有明显偏差,而且雅可比矩阵的求解在复杂系统里面特别繁琐,手动推导极易出错。

UKF走的是另一条路。它不直接对非线性函数做线性化近似,而是通过无迹变换生成一组Sigma点,让这些点穿过非线性函数后再加权求均值和协方差。这种做法避免了雅可比矩阵的计算,对非线性程度高的系统反而更稳,精度上至少达到二阶。

在电力系统动态状态估计这个场景里,我个人的判断是:如果只是做机电暂态过程里的功角、转速跟踪这类中等非线性问题,两者都能用;但如果你后续要把模型扩展到更复杂的动态过程,或者面对量测异常、坏数据较多的场景,UKF的表现会更可靠。当然代价也有——UKF需要额外生成和传播多个Sigma点,计算量上要比EKF高一截。

下面这张表是我在项目里实际跑数据之后整理的对比结果,直接摆出来给大家看更直观:

对比维度EKFUKF
核心思想一阶泰勒线性化无迹变换确定性采样
是否需要雅可比矩阵需要(推导麻烦)不需要
非线性适应能力弱到中等中等到强
计算量较低较高,但通常可控
初值敏感性较敏感,易发散相对稳健
实现难度简单,易上手中等,需理解Sigma点机制
对重尾噪声适应性较弱相对更好

1.3 仿真系统与问题建模

搭建仿真系统的时候,我没有一上来就用IEEE 39节点那种大家伙,而是先用一个单机无穷大系统把算法跑通,验证滤波器逻辑没问题,再考虑扩展。这个思路值得参考——状态估计算法调试过程中最容易出的问题不是算法本身,而是模型和滤波器交互时的数值问题,小系统定位问题要容易得多。

状态量选取的是发电机功角δ和转速偏差Δω(即ω-ωs),对应的动态模型用的是经典二阶转子运动方程:

  • dδ/dt = Δω
  • dΔω/dt = (Pm - Pe - D·Δω) / M

其中Pm是机械功率,Pe是电磁功率(这里我们处理成状态的非线性函数),D是阻尼系数,M是惯性时间常数对应的机械启动时间。这个模型虽然简化得比较厉害,但用来验证EKF和UKF的滤波效果完全够用,而且物理含义清晰。

量测方程构建时,我采用了一个包含非线性关系的函数h(x),把母线电压幅值、有功功率等量测量与功角、转速之间的关系映射出来,模拟量测设备会给出的观测值。也就是说,仿真中的量测是从“真实状态+噪声”生成的,这让我们能精准评估滤波器的估计偏差到底有多大。

2. 核心算法原理:EKF和UKF到底在做什么

2.1 EKF:把非线性“掰直”再估计

EKF的思路,说白了就是“既然非线性不好处理,那就把它线性化”。

具体做法是在每个采样时刻,将状态方程和量测方程围绕当前的状态估计值做一阶泰勒展开,取线性项,然后套用标准卡尔曼滤波的五条公式做预测和校正。在电力系统动态状态估计中,状态方程是转子运动方程,量测方程则包含电压幅值、功率等非线性映射关系,它们对状态变量的导数(雅可比矩阵)就成了EKF的关键,也是最大的麻烦点。

EKF的常规递推流程是:

  1. 利用状态方程做一步预测,得到先验状态估计,并通过线性化后的状态转移矩阵计算预测协方差。
  2. 计算预测状态对应的量测预测值。
  3. 通过量测雅可比矩阵计算卡尔曼增益。
  4. 用实际量测与量测预测的差(新息)校正先验状态,得到后验估计。
  5. 更新后验协方差矩阵。

上面的第3步是EKF的灵魂,也是很多人出错的地方。雅可比矩阵只要有一项算错,卡尔曼增益就跟着错,滤波结果直接飞掉。我在项目里对非线性较强的量测环节做了对比实验,结果很明显:EKF在不稳定工况下有时会出现估计轨迹与真值偏离的现象,主要原因就是一阶近似把函数曲率信息全丢了。

2.2 UKF:让Sigma点替我们去丈量非线性

UKF的想法从根上就不一样:我不去近似非线性函数本身,而是去近似状态的分布。既然一个高斯分布经过非线性变换后很难直接解析求解,那我就在当前分布里“抽取”几个代表点,让它们各自穿越非线性函数,再把穿越后的结果重新拼成一个高斯分布。

这些代表点就是Sigma点。对于n维状态,UKF要生成2n+1个Sigma点,每个点带两个权重:一个用于求均值,一个用于求协方差。Sigma点的选取方式是围绕当前状态均值对称展开,展开幅度由尺度参数λ决定,也就是由α、β、κ三个参数控制。

生成Sigma点之后,把它们分别代入非线性状态方程和量测方程传播一遍,再按权重合并出预测均值和协方差。整个过程完全规避了雅可比矩阵,也不需要泰勒展开,非线性函数在Sigma点间的行为都被采样到了。从原理上,这相当于至少保留了非线性变换的二阶精度,遇上强非线性场景自然更有底气。

最高效的理解方式,是把它看成“用一群侦察兵去摸地形”,每个Sigma点就是一个侦察兵,穿越非线性函数后回传信息,最终由滤波器综合这些信息来更新对状态和不确定度的认识。电力系统的动态过程恰好挺适合这种策略,因为功角、转速的关系曲线本身就存在明显的弯曲,线性近似误差挺明显,用点采样反而能把弯曲信息带回来。

2.3 其实EKF和UKF的共同框架:预测-校正

如果你把EKF和UKF的代码并排放在一起,会发现它们的骨架是惊人相似的,这才是理解卡尔曼滤波的钥匙。

两个滤波器都遵循同一个递推框架:时间更新(预测)和量测更新(校正)。在预测阶段,利用系统状态方程推进状态,并更新协方差以反映不确定度的增长。在校正阶段,利用量测信息对预测结果做修正:量测与预测差异越大且量测噪声越小,校正的力度就越强。

EKF和UKF的真正分歧只在于“如何计算预测均值和协方差”以及“如何计算卡尔曼增益”这两件事的具体实现方式。前者用雅可比矩阵做传递,后者用Sigma点做传递;前者量测预测是直接代入线性化后的量测函数,后者是对Sigma点的量测结果加权求和。

理解了这一层,做实验时会轻松很多。我调试算法时经常先把主框架搭好,滤波器模块做成函数接口,EKF和UKF两个版本换来换去,只动内部的计算方式,外面完全不动。这种设计让我能快速对比两者差异,定位问题也更方便。

3. Matlab代码实现与仿真推演

3.1 代码整体架构与文件组织

Matlab实现这个项目,我不建议把所有代码堆在一个脚本里。虽然单纯为了跑通可以这么干,但后续调参、改模型、换滤波器会让人抓狂,维护性太差。

我推荐的代码组织方式是分模块管理,至少分成这几个文件:

  • 主脚本:负责初始化参数、加载数据、循环调用滤波器、绘图展示结果。
  • 状态方程函数:定义系统的动态模型,输入当前状态和控制量,输出下一时刻状态。
  • 量测方程函数:定义状态变量到量测量的映射关系。
  • EKF滤波函数:封装EKF完整递推逻辑。
  • UKF滤波函数:封装UKF完整递推逻辑。
  • 仿真数据生成脚本:基于给定的“真实状态轨迹”去生成量测序列。

这样分层的好处是,算法与模型解耦——你想换一套电力系统模型,只需修改状态方程和量测方程的接口函数,滤波器的核心逻辑完全不用动。我后来扩展系统规模时,这层设计省了不少事。

下面给一个主脚本的框架示例,演示整个仿真流程:

%% 初始化 clear; clc; close all; dt = 0.01; % 采样时间 10ms T = 10; % 仿真时长 10s t = 0:dt:T; N = length(t); %% 真实轨迹生成 x_true = zeros(2, N); x_true(:, 1) = [0.5; 0]; % 初始功角 0.5rad,转速偏差 0 for k = 1:N-1 x_true(:, k+1) = state_func(x_true(:, k), dt); end %% 生成带噪声的量测 R = diag([0.01, 0.01]); % 量测噪声协方差 z = zeros(2, N); for k = 1:N z(:, k) = meas_func(x_true(:, k)) + sqrt(R) * randn(2, 1); end %% 调用EKF x_ekf = ekf_filter(z, dt, x_true(:, 1), R); %% 调用UKF x_ukf = ukf_filter(z, dt, x_true(:, 1), R); %% 绘图对比 figure; subplot(2,1,1); plot(t, x_true(1,:), 'k-', 'LineWidth', 1.5); hold on; plot(t, x_ekf(1,:), 'r--', 'LineWidth', 1.2); plot(t, x_ukf(1,:), 'b-.', 'LineWidth', 1.2); legend('真值', 'EKF', 'UKF'); ylabel('功角 (rad)'); grid on; subplot(2,1,2); plot(t, x_true(2,:), 'k-', 'LineWidth', 1.5); hold on; plot(t, x_ekf(2,:), 'r--', 'LineWidth', 1.2); plot(t, x_ukf(2,:), 'b-.', 'LineWidth', 1.2); legend('真值', 'EKF', 'UKF'); ylabel('转速偏差 (rad/s)'); xlabel('时间 (s)'); grid on;

这个框架足够简洁,把整个流程串起来了,后续所有细节都围绕这五个模块展开。

3.2 关键参数设置与调试心得

参数设置是滤波器的“手感”所在,也是大家最容易踩坑的地方。很多同学跑出来的曲线发散,多半不是算法写错了,而是参数不合理。我按经验整理出几个核心参数,说明它们各自的作用和取值策略。

过程噪声协方差Q是最关键、也最抽象的参数。它表示你对“系统模型信任度”的量化:Q越大,表示模型误差越大,滤波器就越倾向相信量测;Q越小,滤波器就越相信状态方程而怀疑量测。太极端都会出问题——Q过小会让滤波失去跟踪能力,曲线跟不上真值;Q过大则会让输出剧烈抖动,噪声被当成了信号。调试时我的习惯是从一个偏小的值开始,比如1e-4量级,然后逐渐增大,观察估计轨迹在“平滑”和“跟踪”之间找到一个均衡点。

量测噪声协方差R相对好设置一些,因为它有物理含义——跟传感器的测量误差标准差挂钩。比如你用的PMU测量功角的误差标准差大概在0.01弧度,那对应方差就是1e-4。如果R设置得比实际噪声小,滤波器会对量测过度信任,输出毛刺多;设置得比实际大,则响应迟钝。

初始协方差P0反映的是你对初始状态猜测的不确定程度。一般来说设置在合理范围内即可,不必过度精确,因为滤波器会通过若干步递推自行收敛。但注意别设成零矩阵,那会让滤波器一开始就拒绝修正。

UT变换的三个参数,α通常取一个小于1的正数比如1e-3到1之间的值,β在高斯噪声下取2,κ一般取0或者3-n。α的作用是控制Sigma点离均值的远近,太大会丢失局部细节,太小又容易数值不稳定。

3.3 核心代码模块逐段拆解

状态方程函数是最基础的模块。单机无穷大系统的离散化处理,我采用前向欧拉法,采样时间取足够小时精度完全够用:

function x_next = state_func(x, dt) % 状态量: x(1)为功角delta, x(2)为转速偏差domega M = 10; % 惯性时间常数相关参数 D = 1; % 阻尼系数 Pm = 0.8; % 机械功率 Pe = sin(x(1)); % 电磁功率,简化为功角的正弦函数 omega_s = 1; % 同步转速标幺值 x_next = zeros(2,1); x_next(1) = x(1) + dt * (x(2)); x_next(2) = x(2) + dt * (Pm - Pe - D*x(2)) / M; end

注意这个模型已经做了相当大的简化,Pe取为sin(delta)的形式,但它恰恰提供了足够的非线性强度,用于对比EKF和UKF很合适。

量测方程函数如下,这里我设计成测量值和功角存在非线性关系,同时把转速偏差引入到第二个量测量中,模拟比较理想的量测配置:

function z = meas_func(x) z = zeros(2,1); z(1) = cos(x(1)) + 0.1*x(2); % 电压相关量测,非线性 z(2) = sin(x(1)) + 0.05*x(2); % 功率相关量测,非线性 end

实际项目中,这两个量测方程要替换成基于电网拓扑的潮流计算函数,但接口形式是一样的。

EKF滤波函数的核心是雅可比矩阵的计算和更新逻辑。Matlab里可以用符号计算求导,但仿真循环里每次都用符号工具效率太低。我采用的方式是在状态方程和量测方程旁边额外定义两个雅可比函数,直接给出解析表达式。这种方式虽然前期推导费点时间,但跑起来又快又稳。

EKF的更新逻辑核心代码如下:

function x_est = ekf_step(f_func, h_func, F_func, H_func, x, P, Q, R, z, dt) % 预测 x_pred = f_func(x, dt); F = F_func(x); P_pred = F * P * F' + Q; % 更新 z_pred = h_func(x_pred); H = H_func(x_pred); K = P_pred * H' / (H * P_pred * H' + R); x_est = x_pred + K * (z - z_pred); P = (eye(size(P)) - K * H) * P_pred; end

这里需要注意的一点是,Matlab里求卡尔曼增益尽量不要写成inv(H * P_pred * H' + R) * H * P_pred,数值稳定性不够好。推荐用右除运算符/,它内部会走更稳定的求解路径,在协方差矩阵病态时也能维持一定精度。

UKF滤波函数核心是Sigma点生成。我强调一下权重计算的细节,这块极容易写错:

function [X, Wm, Wc] = sigma_points(x, P, alpha, beta, kappa) n = length(x); lambda = alpha^2 * (n + kappa) - n; % 计算协方差矩阵平方根 sqrtP = chol((n + lambda) * P, 'lower'); X = zeros(n, 2*n+1); X(:, 1) = x; for i = 1:n X(:, i+1) = x + sqrtP(:, i); X(:, i+n+1) = x - sqrtP(:, i); end Wm = zeros(1, 2*n+1); Wc = zeros(1, 2*n+1); Wm(1) = lambda / (n + lambda); Wc(1) = lambda / (n + lambda) + (1 - alpha^2 + beta); for i = 2:2*n+1 Wm(i) = 1 / (2*(n + lambda)); Wc(i) = 1 / (2*(n + lambda)); end end

这里最容易出问题的就是chol分解。如果P矩阵不是严格正定,chol会直接报错。现实中由于数值计算误差,P矩阵有时会失去正定性,这时候需要做一点数值保护,比如先对P做一个对称化处理,加上一个极小的单位阵,或者改用svd分解来求平方根。我后面会在常见问题里专门展开聊这个问题。

3.4 仿真结果如何看才算通过

滤波器跑出来的图,不是画出来就完事了,你得会判断结果到底对不对。

第一看总体趋势。估计线应该和真值线基本重合,即便有偏差也是围绕真值小幅波动,而不是长期偏离某一侧。长期偏离往往是模型有偏或Q设置过小导致的系统性偏差。

第二看初始阶段。滤波开始的前几步,估计值通常会有一个从初值向真值收敛的过程,这个过程有波动很正常,但如果超过一两秒都不收敛,那就要检查P0和Q是不是设置得太离谱了。

第三看稳态噪声水平。当系统进入平稳阶段后,估计曲线的毛刺大小反映了滤波器的噪声抑制能力。EKF的稳态轨迹如果是平滑稳定的,说明雅可比矩阵计算正确;UKF的稳态精度通常略好一些,但差距不会过于悬殊,如果误差差异特别巨大,反而要怀疑是不是某一边的参数设置不合理。

第四看具体数值指标。我习惯用均方根误差(RMSE)来量化对比两种算法的估计效果,分别在功角和转速两个状态量上计算。

代码很简单:

rmse_ekf_delta = sqrt(mean((x_ekf(1,:) - x_true(1,:)).^2)); rmse_ukf_delta = sqrt(mean((x_ukf(1,:) - x_true(1,:)).^2)); rmse_ekf_omega = sqrt(mean((x_ekf(2,:) - x_true(2,:)).^2)); rmse_ukf_omega = sqrt(mean((x_ukf(2,:) - x_true(2,:)).^2));

我实测下来,在弱非线性场景下EKF和UKF的RMSE相差通常在10%到20%以内。如果设置更强的非线性条件,比如把电磁功率改成更复杂的函数形式,UKF的优势会拉大到30%以上。这个趋势本身就能说明算法选型的重要性。

4. 工程实战中那些绕不开的坑

4.1 滤波发散的六大原因与对策

滤波发散是动态状态估计里最头疼的现象。代码写完了,逻辑照着公式来的,参数也调过,结果曲线还是飞了。根据我自己的调试经验,发散原因基本跳不出下面这几个:

原因一:雅可比矩阵推导错误。EKF里雅可比矩阵是手推的,符号错了、正负号反了、维度对不上,滤波立马发散。解决办法是把雅可比矩阵用数值差分法做交叉验证,能对上再正式使用。

原因二:P矩阵失去正定性。这通常由量测更新时P = (I - KH)P_pred的数值误差累积造成。别小看这个误差,长时间运行之后矩阵可能变得不对称甚至非正定,UKF里的chol分解首当其冲会报错。解决办法是每次P更新后做对称化处理,或者用Joseph形式的协方差更新公式替代标准形式。

原因三:Q矩阵设置过小。系统动态过程中如果存在模型未描述的因素,Q又给得特别保守,滤波器会严重依赖模型预测,量测的纠偏作用被削弱,一旦真值偏离预期轨迹,估计就追不上了。

原因四:量测出现粗差或者数据缺失。实际工程中PMU偶尔会有坏数据和通信中断。EKF和UKF对量测异常没有天然免疫能力,一个巨大异常值就能把估计结果暴力拉偏。应对办法是加入新息检验机制,当新息过大时适当降低卡尔曼增益权重,这一条在工程落地上非常重要。

原因五:采样时间过大。离散化用的欧拉法如果步长太大,模型本身就已失真,滤波器再聪明也是基于错误模型的估计,发散是必然的。我调试时候的经验法则是采样频率至少要达到系统主要动态频率的20倍以上。

原因六:初值严重偏离真值。虽然卡尔曼滤波器理论上有收敛能力,但初值偏太多时,非线性系统下的滤波器可能无法正确收敛,甚至收敛到错误的状态。工程上通常用一段静态估计结果来辅助初始化动态滤波。

4.2 协方差矩阵调优的“手感”从哪来

协方差矩阵调优可能是整个项目里最依赖经验的部分了。很多人问,Q和R到底应该怎么设,有没有标准答案。很遗憾,没有。不同系统模型、不同量测配置、不同扰动水平下,最优参数差距很大。

但有一些实践规律可以参考。

第一,Q和R的比例决定了滤波器的动态响应特性。如果系统模型准确度较高,Q可以相对R取小一些,滤波器会稳定平滑;如果模型简化程度较高,Q则要适当放大,给滤波器更多“不信任模型”的空间。我习惯先固定R的物理数值,再单独调Q,这样少一个变量,定位问题更方便。

第二,状态量的单位差异会影响Q的物理意义。功角单位是弧度,转速偏差是标幺值,数量级差异明显,如果给它们分配相同的Q值就很不合理。正确做法是按每个状态量的实际动态幅度来估计过程噪声方差。这一点做好,滤波精度能立刻提升一截。

第三,调试过程中要留回调记录。我每次实验前会把Q、R、P0的参数组合记录在文件名里,比如Q1e4_R1e2.mat,这样回头对比实验时一目了然,不会迷失在几十次仿真的结果里。

4.3 EKF和UKF在实际工程中的选型建议

最后聊一下选型。做了这么多对比实验,我得出的结论是:选型没有绝对的最优,只有相对的适合。

如果你关注的是实时性,计算资源受限,EKF依然是工程首选。虽然它精度上限不高,但只要系统工作点相对稳定或者非线性不强,它有足够的竞争力。

如果你关注的是估计精度和鲁棒性,UKF是更稳妥的选择。它省去了雅可比矩阵的推导工作,对非线性的适应能力更强,而且在初始误差较大的情况下收敛表现也相对更好。代价只是多了一些Sigma点的计算量——在Matlab仿真环境下,这点开销完全不构成压力。

还有一个折中思路值得尝试:在系统动态平缓时用EKF,动态剧烈时切换UKF。不过这个自适应切换机制本身实现起来比较复杂,要看算法的应用场景是否需要这么高端的策略。

从我个人经验来看,如果项目周期比较紧、追求最短时间出稳定结果,直接选UKF往往更省心。EKF的精度和稳定性太依赖于模型线性化质量,一旦遇到强非线性场景,调参的痛苦远大于省下的那点计算时间。

做这套代码实验的过程中,我最大的体会是:EKF和UKF的差异,不是谁碾压谁的问题,而是它们对同一个问题的两个不同切入角度。EKF提供了一条简洁直观的路径,适合入门和理解卡尔曼滤波框架;UKF则用更“高级”的采样手段,保证了在更复杂场景下的可靠性。建议读到这里的同学,把两种滤波器的代码都自己写一遍,然后故意调大非线性强度去压测它们的差距。这个实验做下来,你对状态估计的理解深度会远超只抄代码的效果。最后分享一个调试小技巧——真值轨迹一定要保存下来对比,否则你很难判断滤波误差是来自算法本身的缺陷,还是仅仅因为参数没调好。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询