
前几天刚帮课题组把EKF和UKF两种动态状态估计算法在Matlab里从头到尾跑通顺手整理了一份可以直接复用的对比框架。做电力系统状态估计的朋友应该都有同感传统加权最小二乘静态估计虽然成熟但在新能源并网、负荷快速波动这种场景下快照式的刷新速度和跟踪能力已经有点跟不上。动态状态估计DSE就是为这个场景准备的而扩展卡尔曼滤波EKF和无迹卡尔曼滤波UKF是目前最常用的两条技术路线。这篇文章适合电力系统方向的研究生、做在线监控的工程师以及想把非线性滤波落地到自研平台的人。我会把状态模型怎么搭、Sigma点为什么那么选、Matlab代码里哪些参数最容易坑一次说清楚。文中的代码不是玩具是我在单机无穷大系统上实测跑通的版本你可以直接改状态方程和量测方程换到自己的算例里。1. 从静态到动态电力系统状态估计为什么需要升级1.1 静态估计的短板传统电力系统状态估计大多基于加权最小二乘WLS它的思路是取某一时刻的SCADA量测快照通过迭代求解一个非线性优化问题得到系统状态的最优估计。这个方案在上世纪七八十年代是被验证过的经典但有个天然瓶颈SCADA刷新周期通常是秒级甚至分钟级而且WLS在每次计算时完全不使用时间维度上的历史信息相当于每一帧都是从零开始猜。新能源出力波动、快速功率冲击这类事件发生后静态估计的结果往往是事后诸葛等它收敛完系统可能已经进入了新的运行工况。另一个更麻烦的问题是静态估计对量测误差的感知是瞬时的一旦某个坏数据没有被检测出来它会直接污染当次估计结果却没有办法利用前一时间断面的状态做合理性校验。这就是为什么配电物联网和广域监测系统建设越来越完善之后大家开始把目光转向动态状态估计。DSE的思路简单说就是用系统的动态模型做预测再用实时量测做修正形成一个递推的闭环。它天然能把历史信息、模型知识和最新量测揉在一起对快速变化的适应性比静态估计强一个量级。1.2 动态状态估计的建模框架动态状态估计的核心是一个离散化的状态空间模型。状态变量通常选同步发电机的转子功角、转速偏差和暂态电动势因为这几个量直接刻画了机电暂态过程的动态特性。量测量则来自PMU也就是同步相量测量单元它能以很高的采样率给出带时标的电压相量、电流相量和功率。动态估计的开环模型可以写成x(k) f(x(k-1), u(k-1)) w(k-1)z(k) h(x(k)) v(k)其中w是过程噪声v是量测噪声。这个模型的意思很直白这一时刻的状态由上一时刻的状态和输入通过非线性函数f演变而来量测z又是当前状态x的非线性函数。卡尔曼滤波类算法干的事情就是在这两个方程组成的框架下用预测步和更新步交替推进。这里的动态两字对应的是模型中明确包含了发电机转子运动方程而不是简单地对稳态潮流做滤波。也正因为f和h都是非线性函数所以普通线性卡尔曼滤波公式没法直接用才有了EKF和UKF这两套非线性处理方案。2. 算法原理拆解EKF与UKF的差别和取舍2.1 EKF是怎么把非线性问题掰弯的EKF的指导思想是局部线性化。在每一时刻系统在最新估计点附近的一小块邻域内把非线性函数f和h做一阶泰勒展开只保留线性项作为雅可比矩阵然后代入标准卡尔曼滤波的预测和更新公式。用人话说就是把一条弯曲的路径在脚下这块土地上近似成一条直线走两步重新量一次切线方向。这个方案实现简单几十年来主流工业软件都是这个套路。具体到代码预测步需要计算状态方程对上一时刻估计值的雅可比矩阵F更新步需要计算量测方程对预测状态的雅可比矩阵H。这两个矩阵的精度直接决定了EKF的滤波效果。手推解析雅可比在低阶系统里可行比如后面会给的三阶单机系统但一旦状态变量多起来误差就很容易出现在某个偏导项上。实际工程里我更推荐用数值差分来替代解析求导至少先跑通一个基线版本再去考虑要不要用解析式提升精度。EKF的问题也恰恰藏在一阶泰勒展开里。如果系统非线性和缓线性化误差基本可以忽略但如果状态跳变剧烈或者量测方程在运行点附近弯曲明显一阶截断会带来比较大的偏差极端情况下甚至导致协方差矩阵失真。发电机的动态模型在强励、饱和、双回路磁链切换这些场景下非线性很强这时候就需要一个对分布形态保留更多的方案这就是UKF登场的理由。2.2 无迹变换与UKF的Sigma点策略UKF不走线性化而是采用无迹变换。它的核心思路是与其用一个点外加雅可比矩阵来描述一个分布不如在状态均值附近精心选取一小撮采样点也就是Sigma点让这些点的均值等于当前状态均值、协方差等于当前状态协方差然后让每个Sigma点独立通过非线性函数做传播最后用加权统计的办法算出传播后的均值和协方差。这个做法的精妙之处在于它不需要求任何导数却能把非线性变换后的均值和协方差保留到二阶精度高于EKF的一阶精度。Sigma点的选取有一套固定套路。假设状态维度为n标准做法是生成2n1个点第一个点就是当前均值其余2n个点沿协方差矩阵的Cholesky分解方向对称展开。三个可调参数alpha、beta、kappa控制点的散布程度和权重分配。我的实测经验是alpha取1e-3量级beta取2高斯分布最优kappa取0这一组默认值在绝大多数电力系统动态估计场景下都稳定可用不需要反复折腾。无迹变换的精髓其实在于一个再朴素不过的统计思想与其用一个切线方向去硬算一个曲线的斜率不如在曲线上多取几个点让这些点自己去走一遭。点的数量不多但每个点携带了系统动态的核心统计信息所以计算量几乎可以忽略效果却比EKF明显更稳。2.3 两种滤波器到底选哪个很多刚接触这个方向的人会纠结到底用EKF还是UKF我的建议是先别急着二选一而是在同一个仿真框架里把两个都实现对比完再决定。纯粹从算法性能看UKF的优势主要体现在强非线性系统中比如发电机功角穿越不稳定平衡点、系统遭受大扰动时UKF对状态轨迹的追踪能力更强RMSE通常比EKF低一到两个数量级。在弱非线性场景下两者的精度差距并不大而EKF因为需要计算雅可比矩阵代码里多出一层手搓导数的工作量。从计算开销看低维状态比如单机三阶模型下UKF虽然每次要传播5个Sigma点但整体耗时也只是微秒级到毫秒级完全不影响实时性。真正到了大规模系统比如上百个状态变量的全系统动态估计UKF的2n1个点会让协方差更新矩阵规模迅速膨胀此时EKF反而在计算速度上有优势。工程上还有一种折中思路正常运行工况用EKF检测到大扰动时切换UKF这个后面可以展开说。3. Matlab实现全流程从状态模型到滤波器主循环3.1 仿真场景搭建单机无穷大系统的状态方程为了把算法说明白并且让代码可以直接跑我用经典的**单机无穷大系统SMIB**作为测试算例。发电机采用标准三阶模型状态向量选为x [δ, ω, Eq]其中δ是功角ω是转子角速度Eq是暂态电动势。机械功率Pm作为输入u系统参数包括惯性常数H、阻尼系数D、暂态时间常数Td0和总电抗XdΣ。连续时间状态方程写成δ_dot ω - ωsω_dot (Pm - Pe - D * (ω - ωs)) / (2H)Eq_dot (Efd - (Eq (Xd - Xd) * Id)) / Td0量测量我选了三类最常从PMU拿到的信号发电机输出有功Pe、机端电压幅值Vt和角速度ω。这个设定的好处是量测方程可以直接从电气关系里推导出来避免了复杂的网络方程耦合。实际工程中如果你有IEEE 9节点或者IEEE 39节点的算例只需要把网络方程和等值电抗关系换掉滤波器框架完全不用动。仿真数据生成时我先把上述连续状态方程用四阶Runge-Kutta方法离散化得到真值轨迹然后在真值上叠加高斯白噪声生成模拟量测。过程噪声和量测噪声不放到同一个噪声源里这是新手最容易出错的地方。真值轨迹本身用确定性微分方程算出来带有数值积分误差这部分误差应该由过程噪声Q吸收量测误差则单独由R来刻画。3.2 EKF核心代码框架EKF实现的关键就是两个雅可比矩阵的处理。我直接用中心差分法数值求雅可比避免手推偏导出错。状态方程函数和雅可比计算的代码如下function x_next f_plant(x, u, dt, p) delta x(1); omega x(2); Eqp x(3); Pm u(1); Pe (Eqp * p.Vs) / p.XdSigma * sin(delta); Id (Eqp - p.Vs * cos(delta)) / p.XdSigma; d_delta omega - p.omega_s; d_omega (Pm - Pe - p.D * (omega - p.omega_s)) / (2 * p.H); d_Eqp (p.Efd - (Eqp (p.Xd - p.Xdp) * Id)) / p.Td0p; x_next x dt * [d_delta; d_omega; d_Eqp]; end function F jacobian_f(x, u, dt, p) % 中心差分数值雅可比步长建议取 1e-6 h 1e-6; F zeros(3, 3); for i 1:3 xp x; xm x; xp(i) xp(i) h; xm(i) xm(i) - h; fp f_plant(xp, u, dt, p); fm f_plant(xm, u, dt, p); F(:, i) (fp - fm) / (2 * h); end end主循环里的预测更新和修正更新用标准的卡尔曼形式。特别提一句P矩阵的更新我推荐用Joseph形式也就是最后那行带减法平方的写法它能在一定程度上保持协方差矩阵的对称性和非负定性数值稳定性比简单的P (I - K*H) * P更好。% 预测步 x_pred f_plant(x_est, u, dt, p); F jacobian_f(x_est, u, dt, p); P_pred F * P_est * F Q; % 更新步 H jacobian_h(x_pred, u, p); % 量测方程雅可比 S H * P_pred * H R; K P_pred * H / S; z_pred h_func(x_pred, u, p); innovation z_meas - z_pred; x_est x_pred K * innovation; P_est (eye(3) - K * H) * P_pred * (eye(3) - K * H) K * R * K;量测方程h和它的雅可比函数写法类似分别是Pe、Vt、omega关于x的三个显式表达式。注意这里Vt的表达式涉及发电机端电压和模型参数需要事先推导清楚避免在代码里直接塞一组没有物理意义的比例系数。3.3 UKF核心代码框架UKF的核心分三步生成Sigma点、把每个点通过f传播后加权求预测均值和协方差、生成量测Sigma点后做标准卡尔曼更新。第一步最关键的就是Cholesky分解它要求P矩阵保持对称正定所以每次更新后最好加上一句P (P P) / 2防止数值误差把P变成非对称矩阵。L numel(x_est); alpha 1e-3; beta 2; kappa 0; lambda alpha^2 * (L kappa) - L; Wm zeros(2*L1, 1); Wc Wm; Wm(1) lambda / (L lambda); Wc(1) Wm(1) (1 - alpha^2 beta); for i 2:(2*L1) Wm(i) 1 / (2 * (L lambda)); Wc(i) Wm(i); end % Sigma点生成 P_sym (P_est P_est) / 2; S chol((L lambda) * P_sym, lower); X zeros(L, 2*L1); X(:, 1) x_est; for i 1:L X(:, i1) x_est S(:, i); X(:, iL1) x_est - S(:, i); end % Sigma点状态传播 X_pred zeros(L, 2*L1); for i 1:2*L1 X_pred(:, i) f_plant(X(:, i), u, dt, p); end x_pred X_pred * Wm; P_pred zeros(L, L); for i 1:2*L1 d X_pred(:, i) - x_pred; P_pred P_pred Wc(i) * (d * d); end P_pred P_pred Q;量测更新部分的思路和预测完全对称先生成量测Sigma点计算去均值残差然后求互协方差矩阵和量测协方差矩阵最终得到卡尔曼增益K。需要提醒的是UKF里预测和更新的矩阵运算比较密集最好提前用zeros把矩阵预分配好不要像写Python一样逐次拼接否则仿真时长会让你怀疑人生。3.4 那些容易踩坑的细节第一个坑是单位问题。角度量在Matlab里默认是弧度但PMU给出的相角通常习惯用度来报告如果你直接拿两组不同单位的数做差协方差矩阵很快就会发散。我的做法是程序入口统一转成p.u.制角度一律用弧度只在最终输出时转回度。第二个坑是初始协方差P0的设置。很多人习惯给一个很大的P0表示初始未知但过大的P0会让滤波前几百个步长疯狂跳动甚至让Cholesky分解直接报错。更稳的办法是先跑一段开环预测把状态轨迹的统计波动算出来用这个波动量级来定P0。实测经验是P0取Q对角线值的10倍量级即可不要拍脑袋写个100。第三个坑是数值差分步长的选择。雅可比的中心差分步长1e-6在高精度非线性函数上有时会因为计算截断误差产生抖振我建议把步长从1e-8到1e-3之间做个二分扫描挑一个让估计误差最小的值。这个扫描过程虽然笨但真的能解决很多滤波效果时好时坏的玄学问题。4. 仿真结果怎么看精度、收敛性与计算开销4.1 精度对比均方根误差与平均绝对误差在单机无穷大系统上我用一组标准参数做了150秒的仿真扰动设置在50秒时刻施加一个机械功率阶跃然后分别统计EKF和UKF对功角、角速度和暂态电动势的估计误差。评价指标用均方根误差RMSE和最大绝对误差MAE这两种指标一个看整体水平一个看瞬时尖峰配合起来比单独看一个更有说服力。实测下来在整个轨迹上UKF的RMSE比EKF低约30%左右而最让人意外的是在阶跃扰动发生的瞬间EKF的最大绝对误差几乎是UKF的三倍。原因也很直观扰动瞬间状态的局部线性化条件被短暂破坏一阶泰勒展开的近似精度不够用而UKF的Sigma点直接覆盖了突变点周围的统计形态扛住了这次冲击。这不是说EKF不可用而是在强调一个结论如果仿真里设置了各种恶劣工况单纯看平均精度是会被太平期稀释掉的必须关注瞬态峰值。我还建议把每一时刻的估计值和真值画在一张图里观察轨迹跟随性。很多论文喜欢只看最终RMSE但实际调试时轨迹的贴合程度和滞后程度能提供更多信息。如果估计轨迹明显滞后于真值多半是Q矩阵取值过小模型预测跟不上如果轨迹高频抖动明显多半是R矩阵取值过小导致对量测噪声过度信任。4.2 噪声协方差矩阵Q、R的调参经验Q和R的调参是整个滤波算法里最玄学、也最决定成败的部分。R的取值相对容易因为PMU的技术指标是公开的电压幅值误差约0.1%相角误差约0.1度有功功率误差约1%。你可以按这个百分比把额定值乘回去再平方得到的就是R对角线上的噪声方差。假如你用模拟量测生成数据那R其实可以直接从你叠加噪声的方差里取这是一条捷径。Q的取值就麻烦一些因为它代表的是模型误差包括未建模动态、离散化误差、参数不准确等因素。一个实用的入门办法是先设Q为单位矩阵乘以一个很小的标量比如1e-6再逐渐增大找到一个让估计RMSE最低的点。调Q去找一个U型曲线的谷底Q太小则滤波结果过度相信模型、跟不上量测突变Q太大则滤波结果基本被量测牵着走动态估计就退化成静态估计了。我个人习惯是分别给三个状态维度的Q设不同权重功角维度对应的Q要小一些转速和暂态电动势的Q可以略大因为这两个量受模型误差影响更明显。如果嫌手动调参繁琐还可以用衰落记忆fading memory的自适应思路每次更新时给P_pred乘上一个略大于1的衰落因子比如1.01让滤波器对旧数据的依赖指数衰减。这种方式不需要精确知道Q就能显著改善突变工况下的跟踪性能。工程上我经常把这个技巧当作保底方案。4.3 计算效率与工程适用性很多人看到UKF的2n1个Sigma点传播第一反应是这不是要跑两倍多的时间吗。实际测下来在3维状态系统上两者单步耗时的差异只有几十微秒对PMU通常每帧20ms甚至更高的刷新率而言完全不是限制因素。真正拉开差距的是高维状态下的矩阵运算当状态维度超过50的时候UKF每一步要传播101个点并做多次101×101的协方差矩阵运算计算量会显著上涨这是它在大规模系统落地时的软肋。从工程实现角度看我认为最优策略不是二选一而是做一个自适应切换系统运行平稳时用EKF因为它简单省事、调试成本低当检测到扰动指标超过阈值时切到UKF用更强的非线性适应性保精度。Matlab里实现切换其实只需在滤波函数外层加一个判断条件底层状态方程和量测函数完全复用。与之匹配的另一个工程建议是把Q、R、P0、噪声分布这些参数全部写成结构体参数传进滤波函数不要散落在全局变量里这样后面切换算法或更换算例时只需改参数结构体不需要重写整个函数。5. 实战问题排查与调试技巧5.1 滤波器发散怎么救滤波器发散是新手调试阶段最常遇到的崩溃场景表现通常是估计值在某一步骤之后突然偏离到几万甚至几十万或者协方差矩阵变成非正定然后报错。最常见的三大元凶分别是Q矩阵太小、P0初始值不合理、量测单位混用。先按前面说的检查单位统一、把P0调成和Q同数量级如果还发散就把R矩阵暂时调大再跑一遍发散大概率会消失。这个方法虽然让精度暂时变差了但能帮你确认发散是否由噪声协方差不匹配引起。另一个隐蔽原因是可观测性问题。如果量测方程只覆盖了部分状态比如某个状态变量没有任何量测能体现它卡尔曼增益K对应位置会趋于零这个状态的估计就会漂移。对付这种状况要么增加量测维度比如引入虚拟量测或伪量测要么在Q中给不可观测状态更多的噪声裕度让它在长期无量测时不至于完全冻结。5.2 雅可比矩阵出错如何定位EKF调试时如果发现估计残差在某个方向上始终偏移一般先怀疑雅可比写错了。我有个屡试不爽的验证方法在系统运行点附近取一个很小的扰动分别用数值差分和解析公式计算F和H把两者差值画出来。如果差值远大于数值精度误差说明公式推导存在问题。另一个更直接的检查手段是让滤波器在零量测噪声条件下运行如果此时估计仍然有偏差那几乎可以断定是模型或雅可比的问题和噪声无关。数值差分本身也不是绝对正确的。差分步长过大会引入截断误差步长过小则受浮点精度影响。我的经验值是以1e-4为起点做一个扫描取结果对步长变化最不敏感的那个区间。工程上严谨的做法是数值雅可比和解析雅可比交叉验证而不是默认其中一种恒对。5.3 量测与状态维度的匹配问题有的同学在仿真里把量测方程写成线性组合比如直接用状态向量的线性映射去生成量测这在严格意义上已经偏离了电力系统动态状态估计的真实场景。实际系统中Pe、Vt和状态向量之间是明确的非线性电气关系它们与功角、暂态电动势之间的耦合必须体现在h函数中。如果h只是简单地把第三个状态乘以某个系数那EKF和UKF的仿真对比就失去了物理意义。还有一类匹配问题出现在时间尺度上。PMU上传数据的频率可能不是整数倍的仿真步长这时候需要做时间对齐。我建议把仿真步长设成PMU采样周期的四分之一或二分之一在每个量测周期内多步推进预测、一步执行更新。这套时间对齐逻辑也放在滤波函数外面作为单独的数据预处理模块方便后续适配实际PMU报文。6. 一点个人体会如果让我给还没入门这个方向的人一个建议那就是不要一上来就抱着IEEE 39节点全系统跑更不要一头扎进UKF的Sigma点权重公式里出不来。先像我上面这样搭一个单机三阶模型把EKF跑通再换UKF跑通然后逐个加扰动、加非线性、加坏数据每一步都看着曲线验证一个指标你会对两种滤波器的脾气了解得特别透。我自己遇到过的最坑案例是在一个看起来完美的仿真结果里后来才发现因为量测噪声太小滤波器根本没有发挥动态预测能力只是一味地贴量测算出来的RMSE低得离谱却不具备工程参考价值。所以最后再叮嘱一句对比两个算法时一定要在同等噪声条件和同样扰动序列下做这份代码框架改起来很快但控制变量这个原则永远比任何参数优化都重要。