做电力系统动态状态估计有几年了,最早啃的就是EKF(扩展卡尔曼滤波)和UKF(无迹卡尔曼滤波)这两套非线性滤波方法。很多同学拿到PMU(同步相量量测装置)数据之后,第一反应就是上卡尔曼滤波,结果真正写Matlab代码的时候才发现,坑比想象中多得多:雅可比矩阵求错一个符号整个滤波就飞了,协方差矩阵莫名其妙非正定,UKF的Sigma点参数调了半天也不知道合不合理,好不容易跑通了估计曲线又滞后……这篇文章就把我在实际调试EKF和UKF做电力系统动态状态估计时积累的经验完整整理出来,包括两种算法的原理差异、Matlab代码骨架、参数整定方法、以及一步步排查发散问题的思路。
内容面向正在做电力系统暂态仿真、动态状态估计、或者研究同步相量量测应用的同学,也适合已经具备一定Matlab基础但想快速把这两种滤波器跑通、用在自己的论文或项目里的读者。文中的代码框架不依赖特定的电力系统工具箱,直接copy下来改成自己的模型就能用。
1. 电力系统动态状态估计:为什么EKF和UKF是主流选择
1.1 动态状态估计到底解决什么问题
电力系统状态估计,本质上是从带有噪声污染的量测数据里,还原系统真实的运行状态。传统的静态状态估计,比如加权最小二乘法(WLS),处理的是稳态工况下的量测冗余问题,它的隐含假设是系统在量测周期内基本不变化。这个假设在过去的调度自动化体系下没什么问题,因为SCADA系统提供的数据刷新率只有秒级甚至分钟级,系统的确可以被当作稳态来处理。
但PMU的大规模部署改变了这个局面。PMU能提供毫秒级同步相量数据,动态过程第一次可以被实时感知。这时候静态状态估计就显得不够用了——在故障扰动后的暂态过程中,发电机的功角、角速度都在快速变化,系统的实际运行点每时每刻都在移动,一个“稳态快照”根本无法描述这种时变状态。
动态状态估计就是为此而生的。它利用系统的动态模型,比如发电机转子运动方程,把时间维度上的物理约束加进来,通过递推滤波的方式跟踪状态的实时变化轨迹。和静态估计相比,动态估计有两个明显优势:一是能给出状态在暂态过程中的连续轨迹,而不是孤立的快照;二是模型约束天然提供了一定程度的滤波作用,对量测噪声和坏数据有更强的容忍能力。
1.2 为什么偏偏是EKF和UKF
卡尔曼滤波家族里,标准卡尔曼滤波(KF)只适用于线性系统,而电力系统的动态模型是一个强非线性系统。状态方程里的功角、角速度关系,量测方程里的电压、功率关系,都带有三角函数和非线性耦合。直接用KF是行不通的,需要做非线性扩展。
EKF的思路最直接——把非线性函数在每次滤波的工作点附近做一阶泰勒展开,舍去高阶项,然后用标准KF的递推框架继续处理。这个方案实现简单,计算效率高,在非线性不强的时候精度足够好,所以长期以来是工程应用的主力。
UKF走的是另一条路线。它不对非线性函数做任何近似,而是通过无迹变换选取一组Sigma点,让这些点经过非线性函数传播,再在传播结果上重构均值和协方差。相当于用采样来逼近真实的概率分布,避开了线性化误差。这个思路在数学上比一阶线性化更精细,因为截断到二阶矩的精度,所以在强非线性场景下表现更稳定。
还有一个现实原因:EKF和UKF已经是电力系统动态状态估计领域的事实标准,几乎所有相关论文、开源代码都在用这两种算法做基准对比。后续不管你想尝试粒子滤波、平方根滤波还是鲁棒滤波,都要先和EKF/UKF比性能,所以这两个算法是绕不开的基础工具。
1.3 两种算法在工程场景中的适用边界
拿我自己的项目经验来说,怎么判断该用哪个?看两件事:系统非线性的强度和状态维度。
如果工作点变化平缓、非线性程度不强,比如正常负荷波动下的慢动态过程,EKF完全够用,计算量又小,代码也简单。但如果研究的是故障后的暂态过程,功角可能摆到几十度甚至上百度的范围,量测方程里的sin、cos函数偏离线性近似已经很远,这时候EKF的一阶截断误差就会被放大,UKF的优势就体现出来了。
状态维度也得考虑。UKF要传播2n+1个Sigma点(n是状态维度),状态越多计算量增长越明显。电力系统发电机动态状态估计的状态量通常就三到六个,UKF的计算量完全可控;但如果把模型扩展到全系统多机动态,状态维度几十上百,UKF的计算负担就会成为问题,这时候要么用EKF,要么用缩减Sigma点的变体算法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. EKF在电力系统状态估计中的原理与Matlab实现
2.1 状态方程与量测方程的建立
先把模型建起来。电力系统动态状态估计中,最基础的状态方程来自发电机的转子运动方程,也就是常说的摇摆方程。以单机无穷大系统为例,状态量通常选发电机的功角δ和角速度偏差Δω,离散化的状态方程可以写成:
matlab复制% 状态方程离散递推,dt为采样间隔,omega_s为同步角速度
function x_next = system_dynamics(x, dt, param)
delta = x(1);
omeg
