
卡尔曼滤波家族在电力系统动态状态估计里一直是讨论热度比较高的方向。尤其是扩展卡尔曼滤波EKF和无迹卡尔曼滤波UKF这两兄弟一个靠线性化硬啃非线性一个靠Sigma点采样“曲线救国”很多做电力系统仿真和实时监控的朋友都在这两者之间反复权衡过。我最早接触这块是在做单机无穷大系统的功角估计时手里有一堆Matlab脚本但真正把EKF和UKF从公式变成能跑、能对比、能出图的代码还是踩了不少坑。这篇文章就把我完整跑通的实现过程、原理拆解和调参经验整理出来希望能帮到正在做电力系统动态状态估计课题或者毕业设计的同学。1. 为什么电力系统需要“动态”状态估计从静态断面到实时跟踪很多人刚接触状态估计时脑子里默认是电力系统里那个经典问题给定SCADA量测用加权最小二乘WLS去算节点电压幅值和相角。这就是所谓的静态状态估计。静态估计的隐含假设是系统运行在某个稳态断面量测一帧一帧地独立处理每一帧之间没有时间上的耦合关系。在早期电网结构相对简单、负荷平稳的场景下这个假设是够用的。但现在的系统早就不一样了。新能源大规模接入风机和光伏出力波动剧烈直流输电投运后功率传输走廊的动态行为更复杂系统的等效惯量在下降功角振荡、电压失稳这类动态过程出现的频率和烈度都在上升。在这种背景下“下一个时刻的状态”不再是独立随机变化的它严格由当前状态和系统动力学方程决定。这时候如果还用静态估计去逐帧处理本质上是在扔掉一个极其重要的信息——系统动态模型本身。动态状态估计的核心价值就是把这个“状态随时间演化”的规律纳入滤波框架让估计器不仅能看懂当前断面还能利用上一时刻的信息对下一时刻做预测再用量测修正预测从而实时跟踪系统的动态轨迹。具体到物理对象动态状态估计主要关注发电机的动态过程功角δ、角频率偏差Δω、q轴暂态电动势Eq这些量共同刻画了发电机在扰动后的摇摆行为。对这些状态做实时估计意义不只是“看着好看”而是为了给后续的紧急控制、切机切负荷、阻尼控制提供可靠的实时反馈信号。很多控制策略在实施时如果状态反馈信号本身是滞后或者含噪的控制效果会大打折扣。从信息融合的角度看动态状态估计把两类信息拧在了一起一是系统模型给出的“预测”二是PMU/SCADA量测给出的“修正”。预测精不精、修正强不强直接决定了估计器的性能。EKF和UKF就是两种实现这种预测-修正循环的滤波策略区别只在于“如何把非线性模型塞进卡尔曼框架”的手法不同。2. 动态状态估计的建模基础状态方程与量测方程到底怎么列在进入EKF和UKF算法细节之前先把模型立起来。我用的是最经典的单机无穷大系统SMIB这几乎是动态状态估计研究的“Hello World”。虽然简单但它的非线性特征、状态耦合、量测映射关系和多机系统在数学结构上完全一致跑通它再扩到多机就是体力活。2.1 状态向量的选取发电机经典三阶模型发电机动态模型可以取到不同阶数。工程上常用的是三阶模型状态向量取x [δ, Δω, Eq]三个状态的物理含义很直观δ是发电机的转子功角单位是radΔω是转子角速度相对于同步转速的偏差单位是rad/sEq是q轴暂态电动势单位是p.u.。这三者构成了一个完整的“机电暂态励磁动态”的最小集合。如果你在文献里看到把汽轮机调速器状态也塞进来的那就是更高阶模型原理完全一样只是状态维数多了滤波器的计算量会涨。2.2 连续时间状态方程的离散化处理发电机三阶模型的连续时间微分方程如下dδ/dt ωb * Δω dΔω/dt (Pm - Pe - D*Δω) / (2H) dEq/dt (Efd - Eq - (xd - xd) * Id) / Td0这里的参数里ωb是同步角速度基准值一般是2π50或者2π60Pm是机械功率Pe是电磁功率D是阻尼系数H是惯性时间常数Efd是励磁电压恒定励磁时为一个常量xd和xd分别是同步电抗和暂态电抗Td0是励磁绕组开路时间常数Id是d轴电流。量测方程建立在电气量上。常见的量测包括机端电压幅值Vt、有功功率Pe、无功功率Qe它们和状态的关系是Pe (Eq * V∞ / xdΣ) * sin(δ) Qe (Eq^2 / xdΣ) - (Eq * V∞ / xdΣ) * cos(δ) Vt的表达式稍微复杂一点和Eq、δ、系统等值电抗都有关。可以看到量测方程相对于状态是高度非线性的这正是EKF和UKF发挥价值的地方。如果模型是线性的直接用标准卡尔曼滤波就行也就不需要在这篇文章里讨论这三兄弟的区别了。在Matlab里搭建模型时我的做法是先把连续方程写成函数句柄再用欧拉法离散化。欧拉法在采样周期足够小比如0.01s以下的时候精度足够而且实现最简单。如果你的采样时间较大比如0.02s以上建议改成四阶龙格库塔RK4来离散状态转移否则预测误差会偏大滤波精度会受拖累。用RK4会多几次函数求值但动态估计的采样频率通常就是几十到上百赫兹这个计算开销完全可接受。3. EKF在电力系统动态估计里的实现线性化这一步做对了后面才有意义EKF的基本思想一句话就能说清楚既然卡尔曼滤波要求线性高斯系统那我就在每个时刻把非线性函数在估计点附近做一阶泰勒展开得到一个“瞬时线性化”的模型然后套标准卡尔曼流程。3.1 雅可比矩阵的两种求法解析推导和数值差分EKF在实现时最麻烦的就是算两个雅可比矩阵状态转移矩阵F和量测矩阵H。很多教材上只写“求雅可比”真上手时会发现这一行字能让人折腾一天。F矩阵是状态函数f对状态x的偏导H矩阵是量测函数h对状态x的偏导。对三阶发电机模型来说解析推导虽然繁琐但可行。比如对Pe求δ的偏导就是dPe/dδ (Eq*V∞/xdΣ)*cos(δ)这个写起来不算太难。但一旦状态维数涨到10维以上解析推导雅可比矩阵就变成了非常痛苦的差事而且容易出错。我个人的建议是在验证阶段用解析雅可比确认算法跑通之后再换成数值差分代码更简洁扩展性更好。数值差分在Matlab里实现很直接可以用中心差分for i 1:n px x0; px(i) x0(i) h; mx x0; mx(i) x0(i) - h; F(:,i) (f(px) - f(mx)) / (2*h); end这里的步长h需要谨慎选择过大会导致截断误差过小会引入舍入误差。我的经验值是取 sqrt(eps) * max(1, abs(x0(i)))效果比较稳定。3.2 EKF预测步状态与协方差的时间更新预测步的流程在数学上很干净用当前时刻的后验估计值x_hat(k)代入非线性状态转移函数得到下一时刻的先验估计误差协方差则通过雅可比矩阵线性传播x_pri(k1) f(x_est(k)) P_pri(k1) F * P_est(k) * F Q这里Q是过程噪声协方差矩阵。有人会问为什么协方差的预测要经过F矩阵左乘右乘打个不严谨但好理解的比方如果你对一个向量做线性变换那这个向量的“不确定范围”近似看作一个椭球也会被同一个变换拉伸、旋转。F矩阵就是那个变换的局部代言人所以协方差要两边同时乘F。3.3 EKF更新步卡尔曼增益与后验修正更新步骤完全是标准卡尔曼滤波的形式K P_pri * H * (H * P_pri * H R)^(-1) x_est(k1) x_pri K * (z - h(x_pri)) P_est(k1) (I - K * H) * P_pri在实际代码里计算增益时千万不要显式求逆。用Matlab的右除运算符会更稳定K (P_pri * H) / (H * P_pri * H R)。这个细节在P矩阵病态时能救你一命。更新步的直觉也值得说清楚Kalman增益K本质上是一个加权系数它权衡“模型的预测”和“量测的修正”谁更可信。如果R特别小量测很准K会偏向量测如果Q特别大模型不可信同样是K偏向量测。这个平衡关系在调参阶段的理解至关重要。3.4 EKF的实际局限强非线性场景下的“硬伤”用EKF在电力系统里做动态估计最容易翻车的场景是大扰动后的暂态过程。功角在故障清除后会发生大范围摆动此时线性化点的邻域很小一阶泰勒展开很快就撑不住了误差可能被急剧放大。我做过一个实验在三相短路故障场景下如果故障持续时间超过0.1sEKF的功角估计误差有时会冲到1度以上这在严苛的动态安全分析里是不可接受的。EKF计算量小、实现灵活但它把宝押在“局部线性化足够准”上这个假设在强非线性时非常脆弱。4. UKF实现细节用Sigma点“无迹变换”绕开雅可比矩阵UKF的出发点完全换了个思路我不在估计点附近去做线性化而是直接找一组精心挑选的采样点Sigma点把这组点分别通过非线性函数传播再根据传播后的点集重新统计均值与协方差。这个操作被称为无迹变换Unscented Transform, UT。4.1 为什么Sigma点比随机采样更优雅你可能第一时间想到蒙特卡洛扔几万个随机点传播过去均值协方差也能统计出来吧理论上可以但粒子滤波的教训告诉我们随机采样的计算量和估计精度之间的权衡很尴尬。UT的精致之处在于Sigma点是确定性选取的2n1个点就能抓住高斯分布的一阶矩和二阶矩信息传播后得到的均值和协方差可以精确到非线性函数的二阶泰勒展开项这一精度已经超过EKF的一阶线性化而计算量只多了几次函数求值。4.2 Sigma点生成与权重计算对n维状态我这里n3选取2n1 7个Sigma点。比例对称采样法的经典做法如下计算矩阵平方根S sqrt((nλ) * P)其中λ α^2*(nκ) - n。然后用chol()函数分解协方差矩阵。Sigma点这样生成第0个点就是当前状态均值第1到第n个点等于均值加上S的第i列第n1到第2n个点等于均值减去S的第i列。权重计算要分两套一套用于求均值一套用于求协方差。均值权重里第0个点是 λ/(nλ)协方差权重第0个点是 λ/(nλ) (1-α^2β)其余点的两组权重都是 1/(2(nλ))。这里的经验参数取值α控制Sigma点离均值的距离建议取1e-3到1之间系统越非线性取越小β对高斯分布取2最优κ一般取0保证半正定。很多文献会说这个步骤“简单”但真实写代码时有一个很隐形的坑如果P矩阵在迭代过程中因为数值误差变得不再正定chol()会直接报错。防这个问题的办法有两个一是给P加一个微小对角阵比如1e-12*I二是直接用sqrtm()代替chol()虽然慢一点但能兼容半正定矩阵。4.3 UKF的状态预测与量测更新UKF的预测步很直白把7个Sigma点逐一丢进状态转移函数得到一组传播后的点然后按照权重加权平均得到先验状态估计对每个点减去先验均值的加权外积求和再加上过程噪声Q就得到先验协方差。量测更新的关键选择是用“重组Sigma点”还是“复用预测Sigma点”严格的做法是拿先验状态重新生成Sigma点再丢进量测函数但更省计算量的做法是直接把预测步算出来的传播后状态点丢进量测函数。两种做法在理论上都有文献支持我实际测试的结果是差异极小为了代码简洁通常选择复用。量测均值、量测协方差和状态-量测互协方差算出来后计算UKF增益K P_xz / P_zz x_est x_pri K * (z - z_mean) P_est P_pri - K * P_zz * K这里P_xz是状态与量测的互协方差P_zz是量测协方差。整个流程里完全没有求导这就是UKF比EKF最大的工程优势——你把状态方程和量测方程变成复杂的查表函数、甚至Simulink仿真模型UKF也能照样滤波。4.4 UKF在不同场景下的稳定性表现我跑过两个典型场景对比平缓负荷波动和三相短路故障。在平缓场景里EKF和UKF的估计误差都在0.01度量级差异小到几乎看不出区别但在短路故障后的功角大幅振荡场景UKF的跟踪能力明显更稳功角最大估计误差比EKF小一个数量级。代价就是计算量每个时刻要执行至少7次状态函数求值和7次量测函数求值比EKF的几次雅可比计算两次函数求值要贵。但电力系统动态估计的采样频率通常是工频的1到2个周波一次也就是10ms到20ms一次这点计算量在现在的工控机和工作站上完全不是瓶颈。5. Matlab仿真实验从真值生成到RMSE对比的完整流程这一节直接给出一套可复现的实验流程。我测试用的系统参数如下H5sD2xd1.81xd0.3Td07.5s系统等值电抗xdΣ0.5包含变压器和线路无穷大母线电压V∞1.0。5.1 数据生成如何构造“真实”的动态轨迹动态状态估计的仿真基准通常是用真值加噪声来模拟量测。我的做法是先用ode45求解发电机三阶模型的连续方程得到一条高精度的状态轨迹作为“真实值”ground truth然后从这条轨迹里抽取离散时间样本叠加高斯白噪声生成量测序列。具体来说仿真时长设为20s扰动设置在t5s机械功率Pm从0.8 p.u.阶跃到1.0 p.u.。这个扰动会激起功角振荡正好用来检验两种滤波器对动态过程的跟踪能力。采样周期取0.01s每时刻给量测叠加1%的有功功率噪声和0.5%的电压幅值噪声噪声协方差R就按这条设定。滤波器的初始状态会略偏离真值这是动态估计的常态因为滤波启动时我们没有精确的初始状态比如初始功角偏了0.05 rad、初始Δω偏了0.01 p.u.这样能真实检验滤波器的收敛能力。5.2 核心代码结构与关键函数代码的整体结构采用脚本函数句柄的方式一个脚本负责生成真值、叠加噪声、调用滤波器、绘制结果两个滤波器各写成一个函数文件两个模型状态函数f、量测函数h写成匿名函数或子函数。下面是状态函数和量测函数的核心片段% 状态转移函数离散化后的欧拉形式 f_state (x, u, dt, sys) [ x(1) sys.wb * x(2) * dt; x(2) ((sys.Pm - Pe(x, sys) - sys.D*x(2)) / (2*sys.H)) * dt; x(3) ((sys.Efd - x(3) - (sys.xd - sys.xd1)*Id(x, sys)) / sys.Td0) * dt ];量测函数Pe和Vt的计算建议直接写成嵌套函数方便复用function pe Pe_calc(x, sys) pe (x(3) * sys.Vinf / sys.xds) * sin(x(1)); end滤波器函数里预测步和更新步严格分离这样后续想换滤波器框架时只需要改函数入口和Sigma点生成逻辑其他代码结构不用大改。5.3 评估指标与结果分析我用的评估指标是均方根误差RMSE和最大绝对误差MAE。RMSE看整体精度MAE看最坏情况下的跟踪能力。跑完一次仿真结果整理成类似下面这样的表算法功角RMSE (rad)角速度RMSE (rad/s)功角MAE (rad)单步平均耗时 (ms)EKF0.00830.00210.02140.3UKF0.00410.00120.00981.1从这个表里能看到几个很典型的结论第一UKF的RMSE大约是EKF的一半尤其在扰动后的暂态段体现最明显第二单步耗时UKF确实更贵但依然远低于采样周期实时性完全没问题第三两者的稳态误差差别不大差距全部发生在动态过程中。画图时我习惯把真值、EKF估计值、UKF估计值叠在一张图里再用一个子图单独画误差曲线。第一眼看上去三条线几乎重合缩小到误差子图才能体现两者差异。这也是为什么很多论文的图看起来“EKF也挺准”——大部分场景下确实如此。5.4 一个容易被忽略的问题量测顺序对结果的影响实际编程时量测的接入顺序会影响立刻可见的观测效果。一个很常见的错误是在滤波初段就把所有量测并行接入这会放大初始协方差确定不当的影响。我的做法是在初始阶段用较大的过程噪声Q让滤波器先自主收敛等协方差矩阵收敛到合理水平后通常几百个采样点之后再将精确量测权重调大。这一招在实际调试中非常管用能把滤波器从一个不太准的初值拉回来。6. 调参踩坑实录Q、R矩阵和其他看不见的细节滤波算法的效果好坏一半靠算法一半靠参数。EKF和UKF对Q和R的敏感程度远超一般人的想象。6.1 过程噪声Q矩阵不是越大越好也不是越小越好Q矩阵在物理上代表我们对状态方程的信任程度。Q取小了滤波器会过度信任模型量测的修正作用被削弱当模型本身有误差时容易发散Q取大了滤波器会跟着量测噪声乱跳估计轨迹毛刺多虽然“数据很新”但精度反而下降。我的经验是先用对角线定标对功角任务状态噪声量级按真值变化率的1%到5%来设。比如功角每步变化典型值0.1 rad那Q(1,1)就取(0.001~0.005)²。这个经验值配合现场数据缩放一般能拿到一个能用的起点。之后再针对具体场景做一次半自动调节把量测仿真往真值上怼观察到RMSE随Q变化的U形曲线取谷底对应的值。6.2 协方差矩阵的正定性维护UKF对P矩阵的正定性要求比EKF苛刻得多因为每次预测都要做Cholesky分解。我在一次仿真时发现当功角接近π边界时P矩阵的数值会突然半正定化chol()直接报错。解决办法有两条第一是给P加一个1e-12的Frobenius范数缩放对角阵属于“保险丝”的加法第二是改用sqrtm()函数求解平方根它更稳定代价是慢一点。工程上我建议两者都做正常运行时用chol()一旦抛异常就退化为sqrtm()并把对小特征值截断到1e-10的量级。6.3 滤波发散的监测与保护动态估计中滤波发散是最磨人、也最容易让结果完全没有可用性的问题。发散的典型症状是新息序列量测残差不再服从零均值白噪声分布而是出现系统性的偏差漂移。我的调试习惯是在滤波器里加一个健康度指标即每一步记录z - z_pred的Mahalanobis距离d (z - z_pred) / (P_zz) * (z - z_pred)。正常情况下d的统计均值应该接近量测维数如果连续几十步d都显著偏大说明滤波器快不行了。遇到这种情况常用的抢救措施是为Q加一个自适应调节因子当新息连续偏大时主动涨Q让滤波器重新“信任量测”拉回来。这个自适应策略在工程上很常见但要注意别让它频繁触发否则滤波器会变得过度活跃。6.4 单位问题p.u.系统和角度单位的统一电力系统动态仿真里角度量纲一会用弧度一会用度这个细节看着不起眼但能让你的结果差出百倍。我见过有人把δ的单位在量测方程里写成了度而状态方程里用的是弧度结果滤波器直接把量测当作完全错误的信息丢掉了。统一的做法是全程使用rad仅在最后出图时转换为度。另外转速偏差Δω的单位也要统一如果是p.u.需要乘上ωb才是rad/s。我的建议是状态空间里统一用rad/s量测和绘图时再转换。6.5 从单机系统向多机系统的扩展路径这套单机实验的代码跑通后向多机系统扩展的核心工作就是把状态方程和量测方程换成多机微分代数方程组其余滤波框架完全不用动。多机系统的状态维数会涨到10到20维此时EKF的雅可比矩阵解析推导会变得非常痛苦数值差分也会因为维数上升而明显变慢而UKF的Sigma点数量是2n1n15时只需要31次函数求值反而更有优势。我对那些想写多机动态估计的同学一般都会建议直接用UKF作为主力滤波器EKF用作对比基线就够了。如果要进一步提高估计质量可以考虑把UKF里的过程噪声Q做成随工况变化的自适应矩阵或者引入交互多模型IMM来应对拓扑切换。这些方向都是动态状态估计领域比较活跃的研究点从这篇基础实现往上扩展的路是通畅的。回到Matlab工程本身代码组织上我最后还想强调一点不要把滤波算法和仿真数据生成混在同一个文件里。把系统参数、真值生成器、滤波器、评估指标分别封装成独立函数或脚本后续换系统、换滤波器、换扰动场景时你只需要改参数或换函数不必重写全部代码。这也是我踩了两次“改一处牵全身”的坑之后总结出的最大教训。