Matlab实现电力系统动态状态估计:EKF与UKF实战指南
发布时间:2026/9/30 12:13:20来源:尧图网络
最近在做电力系统动态状态估计相关的仿真项目把扩展卡尔曼滤波EKF和无迹卡尔曼滤波UKF在Matlab里的实现从头到尾完整捋了一遍。这套东西我在单机无穷大系统、多机系统上都跑过也和实验室几个师弟交流过踩坑经历发现很多人并不是卡在算法原理上而是卡在“代码怎么组织、参数怎么设、跑出来怎么判断好坏”这些实战细节上。所以这篇文章就把整套实现方案、核心代码、调参过程和排错经验都写清楚想用Matlab做电力系统动态状态估计的同学可以直接参考。这篇内容适合正在做电力系统动态状态估计论文的研究生、想用PMU量测数据做在线状态估计的工程师也适合第一次接触非线性卡尔曼滤波、想在Matlab里把EKF和UKF跑通并对比性能的初学者。你不需要一开始就弄懂所有数学推导跟着代码把流程跑起来再回头看原理理解成本会低很多。1. 动态状态估计要解决什么问题为什么选EKF和UKF1.1 从静态估计到动态估计多了一个时间维传统电力系统状态估计业内叫静态状态估计本质上是一个加权最小二乘问题。它把某个时间断面上所有SCADA量测拿过来以节点电压幅值和相角为状态量离线迭代求解一个最优断面。好处是理论成熟、实现简单坏处也很明显它把每一个时间断面当成孤立的上一时刻怎么变化、系统的动态方程是什么样的统统不管。带来的实际问题是量测冗余度不够时断面估计可能完全失真坏数据检测需要额外花功夫而且SCADA量测刷新率低秒级甚至分钟级根本追不上系统动态。真正需要在线监视发电机功角、转速等动态状态的时候静态估计就力不从心了。我曾经在一个含风电接入的算例里对比过故障后半个周波内静态估计给出的功角可能还停在故障前的位置这对广域保护决策来说基本没用。动态状态估计就不一样它引入了系统的状态转移方程把状态估计变成一个递推问题。上一时刻的状态通过发电机运动方程、电磁暂态方程预测到当前时刻再用当前量测去修正。这样做的收益是量测瞬时缺失时预测值仍然能给出一段可用的状态轨迹量测有坏数据时滤波器天然有平滑作用更重要的是它能给出状态的真实动态变化过程这直接服务于动态安全评估、广域控制、参数辨识、以及新能源并网后的惯量监测。动态状态估计的状态量一般取发电机的功角、角速度偏差、暂态电动势等物理量。这些量没法直接用表计测量但可以通过PMU测得的功角、电压幅值等间接推算出来。1.2 非线性滤波选型EKF、UKF之外的取舍状态方程确定以后下一个问题是用什么滤波器来递推。线性卡尔曼滤波在电力系统里几乎没有直接用武之地因为发电机动态方程是强非线性的比如电磁功率Pe与功角δ之间的关系是正弦函数摆动方程里这个非线性躲都躲不掉。所以必须在非线性滤波框架里选型。EKF的思路最直白在每一个时刻把非线性函数做一阶泰勒展开只保留线性项然后套用标准卡尔曼滤波的预测-更新流程。它需要显式计算雅可比矩阵实现简单、计算量小是很多人入门非线性滤波的第一站。但它的短板也出在线性化上系统非线性越强、仿真步长越大一阶近似误差越大遇到强扰动或者状态突变滤波结果可能出现明显滞后甚至发散。UKF走的是另一条路线不线性化函数本身而是用一个精心设计的Sigma点集合去“采样”状态分布让每个点通过非线性函数再从传回来的点集里重新统计均值和协方差。它在数学上能捕捉到三阶矩的精度不需要求雅可比矩阵对强非线性系统的适应能力明显好于EKF。为什么不直接上粒子滤波粒子滤波处理非线性和非高斯问题的能力最强但代价是需要大量粒子计算量比UKF高两三个数量级对于需要在线实时递推的动态状态估计来说工程上很难接受。我在实际对比中也验证了这一点EKF调得好时稳态跟踪没什么大问题但故障暂态期间UKF明显收敛更快、误差更小而粒子滤波在同样场景下虽然精度最高仿真一个10秒算例要跑几分钟在线应用根本不现实。所以如果你的研究目标不是专门做粒子滤波算法改进EKF和UKF是最优的折中选择。2. 算法核心拆解EKF和UKF的数学逻辑与适用边界2.1 EKF非线性函数的“线性化近似”EKF处理的系统可以写成x(k1) f(x(k), u(k)) w(k) z(k) h(x(k)) v(k)其中w为过程噪声v为量测噪声分别假设为高斯白噪声协方差矩阵为Q和R。预测步做两件事。第一是用状态方程直接积分得到先验状态估计第二是把状态方程在上一时刻估计值处做一阶泰勒展开计算雅可比矩阵F再通过F把上一时刻的协方差P变换到预测协方差。公式是x_pred f(x_hat(k)) P_pred F * P * F Q更新步也要算量测方程的雅可比矩阵H然后计算卡尔曼增益KK P_pred * H * (H * P_pred * H R)^(-1) x_hat(k1) x_pred K * (z(k1) - h(x_pred)) P(k1) (I - K*H) * P_pred这里的关键问题有两个。第一个是雅可比矩阵F和H必须每一步重新算因为工作点一直在变。第二个是雅可比矩阵的推导很容易出错状态方程里只要有一项偏导写错滤波器轻则精度下降重则直接发散。我去实验室看过不少同学的代码EKF结果诡异的基本都是这个原因。一个非常实用的替代方案是先用数值差分替代解析雅可比把主流程跑通确认滤波逻辑没有问题再花时间推导解析式来加速。这个思路我强烈建议新手采用。数值雅可比本质上就是用中心差分近似偏导数对状态量每个分量给一个小扰动计算函数输出的变化率。只要扰动步长取值合适精度完全够用。2.2 UKFSigma点采样下的统计逼近UKF的核心是无迹变换Unscented Transformation。它不直接对非线性函数做近似而是对状态的分布做近似。假设状态向量是n维的均值为x协方差为P那么生成2n1个Sigma点X^(0) x X^(i) x (sqrt((nlambda)*P))_i, i 1,...,n X^(in) x - (sqrt((nlambda)*P))_i, i 1,...,n其中sqrt((nlambda)*P)表示对协方差矩阵做Cholesky分解或者特征值分解后取矩阵平方根的第i列。lambda是缩放参数由alpha、beta、kappa三个参数决定lambda alpha^2 * (n kappa) - nalpha控制Sigma点围绕均值的散布程度通常取1e-3到1之间的值beta用来融进状态分布的先验信息高斯分布下取2最优kappa是次级缩放参数一般取0或者3-n。权重按下式计算Wm^(0) lambda / (n lambda) Wc^(0) lambda / (n lambda) (1 - alpha^2 beta) Wm^(i) Wc^(i) 1 / (2*(n lambda)), i 1,...,2n得到Sigma点以后每个点都通过状态方程传递到下一时刻再按权重重新合成预测均值和预测协方差。量测更新也做同样的操作所有Sigma点通过量测方程得到量测预测的均值和协方差再算状态与量测的交叉协方差最后计算增益并更新状态。整个过程不需要求任何雅可比矩阵。我用一个类比来理解EKF和UKF的区别EKF就像你拿到一张弯曲的地图非要在当前所在位置用一把直尺把地图抹平然后用直线去量距离UKF则是派出一小队侦察兵往不同方向走每个人回来后汇报海拔和坐标再把这些信息加权汇总。显而易见地形越复杂侦察兵方案越可靠。UKF的数值实现有一个绕不开的坑每一步生成Sigma点都需要对协方差矩阵做平方根分解而协方差矩阵在递推过程中可能因为舍入误差失去正定性导致chol函数报错。我的处理办法是给协方差矩阵加一个很小的对角修正项比如1e-8乘以单位阵保证分解能正常进行。这个细节很多教程不会写但跑起来迟早会遇到。2.3 两者对比选型逻辑和工程判断对比维度EKFUKF非线性处理方式一阶泰勒展开Sigma点统计采样是否需要求雅可比需要不需要计算量小中等约为EKF的2到3倍理论精度一阶三阶高斯分布下强非线性场景表现可能滞后、发散更稳、更准实现复杂度低但雅可比推导费时中等注意矩阵分解问题应用场景弱非线性、实时性要求高强非线性、精度优先实际工程里怎么选我的建议是如果你的状态方程非线性不严重比如主要靠量测方程修正那EKF足够没必要为一个模型精度问题引入更多计算量但如果你的算例里扰动大、状态变化剧烈或者你根本推不出解析雅可比矩阵直接上UKF省心且效果好。做研究写论文的时候两个都实现并做对比是最稳妥的审稿人也喜欢看到这种比较。3. Matlab实现从系统建模到主循环代码3.1 三阶发电机模型与量测方程设计为了让代码既能说明问题又不过度复杂我选了单机无穷大系统SMIB配三阶发电机模型。状态向量取三个量x(1) delta 功角rad x(2) dw 角速度偏差标幺值实际工程中也可用rad/s x(3) Eq 暂态电动势标幺值状态方程写成d(delta)/dt wb * dw d(dw)/dt (Pm - Pe - D*dw) / M d(Eq)/dt (Efd - Eq - (xd - xdp)*Id) / Tdo其中wb 2pif0是同步角速度基准M 2H是惯性时间常数D是阻尼系数Pm是机械功率Pe是电磁功率Efd是励磁电动势Tdo是励磁绕组时间常数。电磁功率和电流按下式计算Id (Eq - Vinf*cos(delta)) / xSum Iq Vinf*sin(delta) / xSum Pe Eq*Vinf*sin(delta) / xSum这里的xSum是暂态电抗与线路电抗之和。量测方程取PMU能提供的两个量功角delta和机端电压幅值Vt其中Vt由d轴和q轴分量合成Vd xq * Iq Vq Eq - xdp * Id Vt sqrt(Vd^2 Vq^2)仿真参数我建议这样取参数数值说明f050 Hz系统频率H4 s惯性常数D1.5阻尼系数xd1.86同步电抗xdp0.35暂态电抗xq0.35简化模型取与xdp一致xL0.55线路电抗Tdo6.5 s励磁绕组时间常数Pm0.8 pu机械功率Efd1.2 pu励磁电动势Vinf1.0 pu无穷大母线电压初始运行点通过稳态条件反推先给定Eq0再根据Pm与Pe相等的条件求出delta0。仿真步长取0.01s对应100Hz的PMU量测频率这个步长下Euler积分已经有点吃力建议后续换成RK4。3.2 数据生成与初始化先有真值再有量测动态状态估计的仿真逻辑和真实系统是反过来的在仿真环境里我们先用高精度数值积分比如ode45跑出一条“真实轨迹”再往真实轨迹上叠加高斯噪声模拟PMU量测最后拿含噪声的量测去喂给EKF和UKF并把它们的估计结果与真实轨迹做对比。这个流程很多人都知道但实际操作中有一个细节常被忽略千万不要把无损轨迹直接当量测输入滤波器那样你会得到一个低得离谱的估计误差而且完全检验不出算法的抗噪声能力。量测噪声的生成也有一点讲究。角度量测噪声要换算成弧度PMU的角度误差通常在0.01度到0.1度之间对应弧度范围大约是1.7e-4到1.7e-3电压幅值误差一般在0.001到0.01 pu。我建议初始设置取R diag([1e-4, 1e-5])角度方差的单位是rad^2电压方差的单位是pu^2这样噪声量级比较符合实际。P0的初始化也容易踩坑。有些同学把P0设得特别小觉得初始状态很准结果前几步卡尔曼增益过小估计值半天追不上真实轨迹。合理做法是给P0一个适中偏大的值比如对角元素取1e-4到1e-3量级让滤波器在前几步有足够的修正能力。3.3 EKF主循环代码实现与雅可比处理先写一个通用的数值雅可比函数这样EKF主循环就不需要手推复杂的偏导公式了function J numJacobian(f, x, varargin) % 中心差分数值雅可比矩阵 % f: 函数句柄x: 状态向量varargin: 其他参数 n numel(x); fx f(x, varargin{:}); m numel(fx); J zeros(m, n); h 1e-6; % 扰动步长 for j 1:n xp x; xm x; xp(j) xp(j) h; xm(j) xm(j) - h; J(:, j) (f(xp, varargin{:}) - f(xm, varargin{:})) / (2*h); end end状态方程和量测方程写成独立函数便于复用function dx sysFun(x, u) % 三阶发电机模型状态方程 % x [delta; dw; Eq] del x(1); dw x(2); Eq x(3); Id (Eq - Vinf*cos(del)) / xSum; Iq Vinf*sin(del) / xSum; Pe Eq*Vinf*sin(del) / xSum; dx zeros(3,1); dx(1) wb * dw; dx(2) (Pm - Pe - D*dw) / M; dx(3) (Efd - Eq - (xd - xdp)*Id) / Tdo; end function z mesFun(x) % 量测方程功角和机端电压幅值 del x(1); Eq x(3); Id (Eq - Vinf*cos(del)) / xSum; Iq Vinf*sin(del) / xSum; Vd xq * Iq; Vq Eq - xdp * Id; Vt sqrt(Vd^2 Vq^2); z [del; Vt]; end注意状态方程里我用的是全局变量或者外部参数实际写代码时最好把参数定义在工作区或者用结构体传入避免硬编码。EKF主循环代码n 3; m 2; x_ekf zeros(n, N); P P0; x_ekf(:,1) x0; for k 1:N-1 % 状态预测用Euler积分先跑通后续可换RK4 x_pred x_ekf(:,k) dt * sysFun(x_ekf(:,k)); % 预测协方差 F numJacobian(sysFun, x_ekf(:,k)); P_pred F * P * F Q; % 量测预测与雅可比 z_pred mesFun(x_pred); H numJacobian(mesFun, x_pred); % 卡尔曼增益 S H * P_pred * H R; K P_pred * H / S; % 更新 x_ekf(:,k1) x_pred K * (z_meas(:,k1) - z_pred); P (eye(n) - K*H) * P_pred; end这里有几个细节值得说明。第一Euler积分在0.01s步长下勉强能用但如果你发现结果有轻微振荡不要急着怀疑滤波器先把状态预测换成四阶Runge-Kutta很多怪问题会自己消失。第二雅可比矩阵用的是数值差分效率不是最高但对于三状态系统完全够用上了多机系统再考虑解析式。第三量测更新用的z_meas(:,k1)是当前时刻的量测不要错用成上一时刻。3.4 UKF主循环代码实现与Sigma点生成UKF的实现比EKF多一个Sigma点生成和权重计算的步骤% 参数设置 alpha 0.01; beta 2; kappa 0; lambda alpha^2*(n kappa) - n; % 权重 Wm zeros(2*n1, 1); Wc zeros(2*n1, 1); Wm(1) lambda / (n lambda); Wc(1) lambda / (n lambda) (1 - alpha^2 beta); for i 2:2*n1 Wm(i) 1 / (2*(n lambda)); Wc(i) 1 / (2*(n lambda)); end x_ukf zeros(n, N); x_ukf(:,1) x0; P P0; for k 1:N-1 % 1. 生成Sigma点 % 协方差矩阵正定修正 P_chol P 1e-12 * eye(n); S chol(P_chol, lower); chi zeros(n, 2*n1); chi(:,1) x_ukf(:,k); for i 1:n chi(:,i1) x_ukf(:,k) sqrt(n lambda) * S(:,i); chi(:,in1) x_ukf(:,k) - sqrt(n lambda) * S(:,i); end % 2. Sigma点通过状态方程传播 chi_pred zeros(n, 2*n1); for i 1:2*n1 chi_pred(:,i) chi(:,i) dt * sysFun(chi(:,i)); end % 3. 加权合成预测均值和协方差 x_pred zeros(n,1); for i 1:2*n1 x_pred x_pred Wm(i) * chi_pred(:,i); end P_pred Q; for i 1:2*n1 d chi_pred(:,i) - x_pred; P_pred P_pred Wc(i) * (d * d); end % 4. 量测更新所有Sigma点通过量测方程 Z_pred zeros(m, 2*n1); for i 1:2*n1 Z_pred(:,i) mesFun(chi_pred(:,i)); end z_mean zeros(m,1); for i 1:2*n1 z_mean z_mean Wm(i) * Z_pred(:,i); end Pzz R; % 量测协方差 Pxz zeros(n,m); % 交叉协方差 for i 1:2*n1 dz Z_pred(:,i) - z_mean; dx chi_pred(:,i) - x_pred; Pzz Pzz Wc(i) * (dz * dz); Pxz Pxz Wc(i) * (dx * dz); end % 5. 增益与状态更新 K Pxz / Pzz; x_ukf(:,k1) x_pred K * (z_meas(:,k1) - z_mean); P P_pred - K * Pzz * K; endUKF比EKF多出来的计算量主要就在这两层循环上但换来的是不需要任何求导操作而且对非线性函数的逼近精度高得多。如果你发现chol(P_chol)仍然报错说明协方差矩阵的状态很差这时候把修正项从1e-12调到1e-6基本能解决问题。3.5 Q、R、P0的整定方法滤波器的参数整定本质上是给“模型预测”和“量测修正”分配信任度。Q大意味着模型不准、要更相信量测R大意味着量测噪声大、要更相信模型。这个比例关系直接决定了滤波器的动态响应特性。Q的初始值可以按状态量在正常运行时的动态范围来估计。比如功角的变化幅度可能在0.01到0.1 rad量级那么对应的过程噪声方差可以取(0.01^2)到(0.1^2)之间的值也就是1e-4到1e-2。角速度偏差和暂态电动势也类似。我常用的做法是Q diag([1e-4, 1e-4, 1e-4]);R的初始值按PMU量测精度取R diag([1e-4, 1e-5]);P0取一个相对适中的值比如P0 diag([1e-3, 1e-3, 1e-3]);调参的总体原则是先固定R把Q从1e-6逐渐扫描到1e-2观察估计误差的RMSE随Q的变化再反过来固定Q扫描R。不要指望一次成功多跑几组参数你会看到明显的规律。后面第4节我会详细讲怎么从曲线形态反推参数问题。4. 仿真结果分析与调参实战4.1 扰动场景设置与结果观察为了对比EKF和UKF在动态过程中的表现我设置了一个典型的扰动算例在t 1s时刻机械功率Pm从0.8 pu阶跃增加到0.84 pu持续时间0.2s后恢复。这个扰动的物理含义相当于发电机输入功率的短时波动幅度不大但足够让功角出现明显摆动。仿真时长取5s步长0.01s总共500个量测点。真实轨迹用ode45生成量测加噪声以后再分别输入EKF和UKF。跑完以后把功角、角速度偏差、暂态电动势三个状态的估计曲线和真实值画在同一张图上比较直观。从我的实测结果看稳态阶段EKF和UKF都能跟上真实轨迹误差差距不大但扰动发生的瞬间EKF的功角估计会出现一个相对明显的尖峰误差且回到平稳状态的时间更长。UKF的估计曲线始终贴着真实值尤其在功角变化率最大的那几个时刻优势非常明显。这个现象和理论完全吻合扰动越强系统非线性越突出EKF的一阶线性化误差就被放大得越厉害。4.2 评价指标RMSE与最大误差单看曲线容易主观还得用数值指标说话。我一般用均方根误差RMSE和最大绝对误差MAE两个指标rmse sqrt(mean((x_est - x_true).^2, 2)); max_err max(abs(x_est - x_true), [], 2);RMSE反映整体跟踪精度MAE反映最坏情况下的偏离程度。由于量测噪声是随机生成的单次仿真的结果有随机性我建议做20次蒙特卡洛仿真每次重新生成噪声然后统计RMSE的均值和标准差。这样得出来的结论才有说服力审稿人也不会挑毛病。我跑20次的平均结果显示UKF的功角RMSE比EKF小约30%到50%最大误差小得更多在角速度偏差上UKF的优势更大因为角速度是由功角差分演变来的状态方程非线性更敏感。如果你跑出来两者几乎一样可以先检查是不是扰动设置太小或者Q、R参数处于特殊比例上。4.3 调参方向判断从曲线形态反推问题调参的时候曲线会给你很多信息。我是这样判断的如果滤波曲线明显滞后于真实轨迹在阶跃扰动后要好几拍才跟上去通常是Q偏小或者R偏大滤波器太信任模型、不敢采信量测。解决办法是把Q稍微调大或者把R调小。如果滤波曲线抖得很厉害噪声完全没被平滑掉甚至比量测噪声还剧烈那就是R偏小或者Q偏大滤波器把量测噪声当成了真实状态变化。解决办法是把R调大让滤波器更信任模型。如果估计值和真实值之间有一个稳定的偏置扰动前后都存在那不是滤波器的问题多半是系统模型参数有偏差或者量测方程写错了。这种系统性偏差靠调Q、R是救不回来的。还有一个我很推荐的检查手段打印“预测残差”也就是量测值与量测预测值之差。如果滤波器调得合适残差序列应该围绕0波动且其协方差统计量接近R矩阵。如果残差明显有偏或者方差远远大于R说明模型和量测之间有一方出了问题。我实际经验中还有一个性价比很高的做法先做一个参数敏感性扫描把Q的每个对角元素从1e-6到1e-2按对数间隔取5到6个值跑完整的蒙特卡洛仿真画出RMSE热力图。这个过程能让整个系统的行为规律一目了然比手动盲调高效得多。5. 常见问题与排错速查5.1 高频故障与解决方案表跑这套代码的时候我遇到过的、以及帮别人排查过的问题集中在下面几类现象可能原因解决办法UKF运行时报chol矩阵必须为正定协方差矩阵失去正定性给P加1e-8到1e-6的对角修正项再分解EKF估计结果发散数值出现NaN雅可比矩阵写错或数值差分步长不合理先用数值雅可比跑通把差分步长设到1e-6到1e-4估计曲线严重滞后真实轨迹Q偏小或R偏大增大Q或减小R重新扫描估计曲线剧烈抖动R偏小或Q偏大增大R减小Q滤波结果在扰动点出现巨大尖峰EKF线性化误差放大换UKF或减小仿真步长稳态下两个滤波器差距很小非线性弱时两者本来就接近这是正常现象不能说明UKF没用量测缺失时不知道怎么办代码没有处理丢帧逻辑跳过量测更新步直接把预测值作为估计值状态初值给得和真实值差很远初始化不当增大P0让滤波器前几步有更大修正增益其中量测缺失这个场景值得多说一句。动态状态估计的优势之一就是能在量测短时缺失时维持输出实现方法非常简单把更新步的卡尔曼增益强制设为0或者直接跳过更新部分让状态和协方差用预测值。恢复量测后滤波器会自动通过增益恢复正常更新不需要额外逻辑。这个能力在PMU通信丢帧的情况下非常实用。5.2 几条提高效率的实操建议第一代码结构上把状态方程、量测方程、数值雅可比、EKF主循环、UKF主循环都封装成独立函数这样换系统模型的时候只需要改sysFun和mesFun滤波器核心代码一行都不用动。我在从单机模型换到三机九节点系统的时候只改了模型函数和参数主循环完全复用省了大量时间。第二调试阶段先用小步长、短时长、无扰动场景跑通再逐步加扰动、加噪声。如果你一开始就直接上强扰动高噪声出了问题都不知道该怀疑滤波器还是怀疑代码。第三运行蒙特卡洛仿真的时候把噪声种子设为可配置的。你可以固定一个种子复现实验也可以随机化种子做统计评估。Matlab里用rng(seed)控制即可。第四如果打算把动态状态估计作为论文研究点建议在单机模型跑通以后尽快过渡到IEEE标准测试系统比如三机九节点或者IEEE 39节点系统。算法部分不需要改但状态方程要从单机扩展到多机量测矩阵维度会变大需要考虑稀疏存储和更高效的雅可比计算。多机系统的状态量之间通过网络方程耦合量测方程的维度也会显著增加不过这已经超出本文范围感兴趣的可以沿着这个方向继续深入。还有一个细节绘制结果图的时候把量测噪声点也画出来用浅灰色散点表示可以直观看出滤波器的平滑效果。很多同学只画估计值和真值忽略了量测噪声结果图看起来过于“完美”反而不真实。最后再分享一个技巧调完参数以后把最终使用的Q、R、P0、alpha、beta、kappa这几个关键参数和对应的RMSE指标一起保存下来。这个习惯看似简单实际帮了我大忙——同一个系统换扰动场景或者换量测配置时我不需要从头盲调直接在已有的参数网格上插值就行。做工程和做研究能保存中间过程的人永远比从头再来的人快。
网站建设高端定制企业官网