状态估计这个东西,我看不少教材一上来就是G矩阵、H矩阵、协方差,公式推了三页纸,读者还是不知道它在电力系统里到底扮演什么角色。我自己刚接触的时候也很懵,直到动手把WLS和PMU这套流程完整跑通,才真正把“状态估计”这四个字从一个术语变成了一个工具箱。
这个项目做的事情用一句话概括:在Matlab里用加权最小二乘(WLS)配合相量测量单元(PMU)数据,去估计电力系统的电压幅值和相角,然后把估计结果和Newton-Raphson(NR)潮流计算得到的基准状态做对比。它最合适的定位是一个教学型、入门型算例,但里面的代码逻辑、量测建模思路、权重设置方法,放到工程实践里也完全能用。
这篇文章我会从算法原理开始,把NR潮流、WLS估计、PMU量测建模这三块讲透,然后给出Matlab实现的关键代码,最后分享我实际调试过程中踩过的坑。如果你想搞懂状态估计是怎么“从垃圾数据里淘出金子”的,或者你在做PMU相关的课题,这篇可以帮你把整条链路打通。
1. 项目要解决的核心问题与整体思路
1.1 状态估计在电力系统里的定位
电力系统调度中心每天要面对海量数据,但这些数据远没有想象中那么可靠。传统SCADA系统把各个厂站的遥测信息传上来,存在几个问题:一是量测不完整,不可能每个节点都装齐所有表计;二是误差普遍存在,电流互感器、电压互感器、变送器、A/D转换链路,每一个环节都在累积误差;三是可能有坏数据,比如通信干扰、遥测跳变、甚至长时间冻结;四是时标不统一,不同厂站的数据到达调度中心的时间不一样,拼出来的“断面”很可能根本不是一个瞬间。
状态估计就是用来解决这些问题的。它利用量测数据的冗余度——同一个物理量可以从多个量测中间接推导出来——通过统计估计的方法,求出一组与所有量测最“自洽”的系统状态。这里的系统状态就是所有节点的电压幅值V和相角θ。有了这套状态,才能进一步做安全分析、经济调度、自动发电控制这些EMS高级应用。
类比一下,就像你在一片陌生地方定位,如果只有一条路的提示,你很容易走偏;但如果手里同时有手机基站信号、车载里程计、路边路牌,把这些信息拿来做加权拟合,定位就会准得多。状态估计干的就是这个“多源信息融合拟合”的活。
1.2 PMU带来什么新玩法
PMU(相量测量单元)和传统RTU最大的区别,在于它基于GPS同步授时,能以很高的刷新率直接输出带绝对时标的电压相量和电流相量。这意味着两件事:
第一,相角可以直接测了。传统SCADA系统里,相角是无法直接量测的,只能靠功率量测间接推出来;而PMU直接把电压相角θ报出来,而且不同厂站之间测到的相角是全局一致的,因为参考时标是同一颗卫星。
第二,刷新率和精度都上了一个台阶。SCADA典型刷新周期是2到10秒,PMU可以做到每秒几十帧;相角测量误差通常在±0.01°量级,电压幅值误差也远优于传统变送器。
但PMU也有短板:成本高、通信带宽要求大、全网覆盖还需要时间。所以现实中的状态估计大多是“SCADA+PMU”混合量测模式,也就是既有传统功率量测,又有少量节点的高精度相量量测。这个项目里我就按这种混合模式来构造实验,重点观察PMU加了之后估计精度的变化。
1.3 整体技术路线:NR搭台、WLS唱戏、PMU助阵
整个项目的技术路线可以拆成四步,也对应四段代码模块:
第一步,用Newton-Raphson方法计算潮流,得到一组满足潮流方程的系统状态V和θ。在仿真环境里,这组状态就是“真值”,因为它完全满足网络方程,没有任何量测噪声污染。
第二步,以这组真值为基础,叠加高斯噪声,模拟生成两类量测:传统SCADA类的节点注入有功、无功功率量测,以及PMU类的电压幅值、相角量测。
第三步,把生成好的量测数据丢给WLS状态估计器,通过迭代求解得到状态估计值。
第四步,把估计值和NR潮流解放在一起对比,计算最大误差和均方根误差,评估不同PMU配置下的估计效果。
为什么拿NR潮流结果当基准?因为状态估计的“真值”在真实系统中是不可知的,你只能通过更高精度的仪器去逼近;但在数字仿真里,NR潮流解就是构造出来的“真实世界”,没有噪声、没有坏数据,完全满足网络方程。用它当基准来评价估计算法,是学术界和工业界都通用的做法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 算法背后的数学原理
2.1 Newton-Raphson潮流:把“真值”算出来
NR潮流本质上是在求解一组非线性代数方程。对于每个节点i,极坐标形式的功率平衡方程是:
节点注入有功:P_i = V_i Σ V_j (G_ij cosθ_ij + B_ij sinθ_ij)
节点注入无功:Q_i = V_i Σ V_j (G_ij sinθ_ij - B_ij cosθ_ij)
其中G_ij和B_ij是节点导纳矩阵Ybus的第(i,j)个元素的实部和虚部,θ_ij = θ_i - θ_j。把给定的负荷和发电功率代入,方程左侧P_i、Q_i是已知量,未知量就是各节点的V_i和θ_i。
NR方法的思想是:先给一组初值,算出方程不平衡量ΔP、ΔQ,利用雅可比矩阵J把不平衡量映射为状态修正量Δθ、ΔV,迭代修正直到不平衡量趋近于零。数学形式是:
[ΔP; ΔQ] = J · [Δθ; ΔV]
J是一个2N×2N的分块矩阵,四个分块分别是∂P/∂θ、∂P/∂V、∂Q/∂θ、∂Q/∂V。每次迭代解这个线性方程组得到修正量,更新状态后重新计算不平衡量,直到max(|ΔP|, |ΔQ|)小于阈值,比如1e-8。
为什么用NR?因为它收敛快,二次收敛特性在潮流计算里是经过了几十年工程验证的。更重要的是,NR潮流里需要构造雅可比矩阵,而这个雅可比矩阵和后面WLS状态估计里需要构造的量测雅可比H矩阵,在数学结构上是高度同源的。你在写代码的时候,很多函数可以直接复用或者改一改就能用。
2.2 WLS估计:目标函数与迭代推导
WLS(加权最小二乘)的核心思想很直接。设量测向量为z,状态向量为x,量测函数为h(x),那么量测方程写作:
z = h(x) + e
其中e是量测误差,假设服从均值为零、协方差为R的高斯分布。状态估计的目标是找到一组x,使得加权残差平方和最小:
J(x) = [z - h(x)]ᵀ · W · [z - h(x)]
其中权重矩阵W = R⁻¹。权重设置的直觉是:误差方差越小的量测越可信,权重越大。
对J(x)求梯度并令其为零,就得到一个非线性方程组。用高斯-牛顿法线性化,迭代公式是:
Δx = (Hᵀ W H)⁻¹ Hᵀ W [z - h(x)]
x ← x + Δx
这里的H是量测雅可比矩阵,即∂h(x)/∂x;G = Hᵀ W H称为信息矩阵(增益矩阵)。迭代进行到Δx的范数足够小为止。
从公式能看出,WLS本质上是把一个测量问题转化成了加权线性回归问题的迭代扩展。它和NR潮流很像,都是“给初值→算残差→解线性方程组→更新状态”的循环结构。区别在于:NR求解的是功率平衡方程,状态更新量由功率不平衡量驱动;WLS求解的是最优估计问题,状态更新量由加权量测残差驱动。
2.3 PMU量测建模与权重设计
PMU量测和传统SCADA量测在模型上有个显著区别:PMU能直接测电压和相角,量测函数几乎是线性的。比如在节点i,PMU给出的电压量测为:
V_i^meas = V_i + e_V
θ_i^meas = θ_i + e_θ
对应的量测雅可比行向量就是:对V_i那一列的导数是1,对θ_i那一列的导数是1,其余全是0。这意味着在迭代过程中,这些量测的残差计算极其简单,而且它们对G矩阵的贡献是“硬邦邦”的,不会因为状态估计值的变化而剧烈改变。
权重设计上,我们取W中对应某个量测的对角元素为1/σ²,σ是该量测的标准差。比如PMU相角测量的标准差如果是0.001 rad,那它的权重就是(1/0.001)²=1e6,远超一般的功率量测。这个高权重会把估计结果强力“拉”向PMU的观测值,这也是PMU能够显著提升估计精度的核心原因。
另外,PMU还可以测支路电流相量。电流和状态的关系是非线性的,因为支路电流取决于两端节点的电压和相角。做项目时我先用电压和相角量测,电流量测可以作为扩展,加到H矩阵的对应行里,属于锦上添花的部分。
3. Matlab代码实现全流程
3.1 测试系统与数据准备
我用的算例是IEEE 14节点系统,这是状态估计和潮流计算领域最常用的标准测试系统之一。它有14个节点、20条支路、5台发电机,数据可以从Matpower的case14.m里直接读,也可以手工整理成一个矩阵。
我在代码里把系统数据组织成两个矩阵:
- bus:节点编号、类型(1为平衡节点、2为PV节点、3为PQ节点)、有功负荷、无功负荷、发电机有功、发电机无功、电压初值、相角初值
- branch:支路首端节点、末端节点、电阻r、电抗x、对地电纳b、变比tap
形成节点导纳矩阵Ybus的代码比较固定,核心逻辑是遍历每条支路,把串联导纳和对地导纳叠加到对应位置。这里给出一个生成Ybus的关键函数:
matlab复制function Ybus = makeYbus(nb, branch)
Y = zeros(nb, nb);
for k = 1:size(branch, 1)
f = branch(k, 1); t = branch(k, 2);
r = branch(k, 3); x = branch(k, 4); b = branch(k, 5); tap = branch(k, 6);
z = r + 1j*x;
y = 1/z;
Y(f, f) = Y(f, f) + y + 1j*b/2;
Y(t, t) = Y(t, t) + y + 1j*b/2;
Y(f, t) = Y(f, t) - y;
Y(t, f) = Y(t, f) - y;
end
Ybus = sparse(Y);
end
注意这里把对地导纳平均分到支路两端,这是IEEE数据格式的惯例。如果是带变压器变比的支路,还要按变比折算,这个细节我后面避坑部分再说。
3.2 NR潮流求解的代码实现
NR潮流的实现我拆成三个函数:一个算不平衡量,一个算雅可比矩阵,一个跑迭代主循环。这里贴出主循环的核心代码:
matlab复制function [V, theta, iter] = nr_powerflow(Ybus, Sbus, V0, ref, pv, pq, tol, max_iter)
nb = length(V0);
V = V0(:); theta = zeros(nb, 1);
iter = 0;
while iter < max_iter
% 计算注入功率
Vc = V .* exp(1j * theta);
I = Ybus * Vc;
S_calc = Vc .* conj(I);
P_calc = real(S_calc); Q_calc = imag(S_calc);
% 功率不平衡量
dP = real(Sbus) - P_calc;
dQ = imag(Sbus) - Q_calc;
% PV节点不需求无功平衡,平衡节点不需求有功无功平衡
dPQ = [dP(pq); dQ(pq)];
% 雅可比矩阵(示意,实际应构造完整J矩阵)
J = makeJac(Ybus, V, theta, pv, pq);
% 解修正方程
dx = J \ dPQ;
% 更新状态
% ... 按dx更新theta和V ...
iter = iter + 1;
if max(abs(dPQ)) < tol
break;
end
end
end
这段代码是骨架,实际运行还需要把雅可比矩阵的构造补完整。需要注意几个点:平衡节点的θ是0,不参与迭代;PV节点的无功不参与不平衡量计算,但电压幅值固定;迭代收敛判据我取max(|dP|, |dQ|) < 1e-8,这个精度对后续叠加噪声生成量测来说已经足够。
最后跑出来的V和theta就是整个仿真系统的“真实状态”。我习惯把它们存一份,后面所有误差对比都拿它们当基准。
3.3 量测数据生成:用噪声模拟真实传感器
真值有了,剩下的就是模拟传感器。这一步的关键是“加了噪声还要保持物理合理性”,不能胡加。我的做法是:
对传统SCADA类量测,在部分PQ节点上生成注入有功和无功功率量测:
matlab复制scada_nodes = [2 3 5 6 8 9 10 12 13 14]; % 假设部分节点有注入量测
sigma_p = 0.01; sigma_q = 0.01; % 标幺值噪声标准差
P_meas = P_true(scada_nodes) + sigma_p * randn(length(scada_nodes), 1);
Q_meas = Q_true(scada_nodes) + sigma_q * randn(length(scada_nodes), 1);
对PMU类量测,在部分节点上生成电压幅值和相角量测。假设PMU装在节点1、4、7、9、11:
matlab复制pmu_nodes = [1 4 7 9 11];
sigma_v = 0.005; % 电压幅值误差标准差,标幺值
sigma_theta = 0.001; % 相角误差标准差,弧度
V_pmu = V_true(pmu_nodes) + sigma_v * randn(length(pmu_nodes), 1);
theta_pmu = theta_true(pmu_nodes) + sigma_theta * randn(length(pmu_nodes), 1);
这里为什么要用标幺值而不直接用电网有名值?因为WLS迭代涉及矩阵求逆,数值尺度差异太大会导致条件数变差,影响收敛和精度。把所有量测统一到标幺值体系下,功率量测和电压量测的数值量级基本都在0.1到1之间,权重矩阵的计算也更干净。
这里还有一个细节:噪声标准差的大小直接决定权重矩阵。σ_v=0.005意味着电压量测误差约0.5%,σ_theta=0.001rad意味着相角误差约0.057°,这基本符合当前商用PMU的精度水准。用这个参数生成的量测,WLS估计出来的状态才会显得靠谱。
3.4 WLS估计主循环实现
WLS主循环是项目的心脏。设计状态向量x时,我把平衡节点(节点1)的相角排除在外,因为θ_1=0是参考角。状态向量排列顺序我习惯写成 [θ_2; θ_3; ...; θ_14; V_1; V_2; ...; V_14],共27个状态变量(14节点系统,平衡节点相角不算)。
量测向量z把所有量测拼在一起,顺序是:SCADA注入有功、SCADA注入无功、PMU电压幅值、PMU相角。量测雅可比矩阵H按照这个顺序一行一行填。关键主循环代码如下:
matlab复制function [V_est, theta_est, iter] = wls_estimate(V0, theta0, z, R, pmu_nodes, scada_nodes, tol)
nb = length(V0);
x = [theta0(2:end); V0]; % 初始状态
W = inv(R);
iter = 0;
while iter < 20
% 从x还原theta和V
theta = [0; x(1:nb-1)];
V = x(nb:end);
% 计算量测函数值h(x)
h = compute_h(V, theta, scada_nodes, pmu_nodes);
% 计算量测雅可比H
H = compute_H(V, theta, scada_nodes, pmu_nodes);
% 残差
r = z - h;
% 法方程
G = H' * W * H;
dx = G \ (H' * W * r);
x = x + dx;
iter = iter + 1;
if max(abs(dx)) < tol
break;
end
end
theta_est = [0; x(1:nb-1)];
V_est = x(nb:end);
end
compute_h和compute_H两个函数是难点。compute_h对每个量测遍历:SCADA功率量测用节点功率方程计算;PMU电压量测直接查当前V;PMU相角量测直接查当前theta。compute_H则按对应的偏导公式填行。
在实现时,我建议用稀疏矩阵存储H和G。IEEE 14节点系统规模不大,用全矩阵也能算,但如果你后面换到IEEE 118节点或者更大系统,稀疏化能省掉大量内存和求解时间。Matlab里用sparse()把H变成稀疏矩阵,G = H' * W * H 自然也是稀疏的,反斜杠求解效率非常高。
3.5 结果对比与可视化
估计做完,对比是重头戏。我通常会画两张图:一张电压幅值对比,横轴节点编号,纵轴电压标幺值;一张相角对比,横轴节点编号,纵轴相角(度)。图上同时画NR真值和WLS估计值,一眼就能看出差别。
误差指标我用了两个:
最大误差:max(|V_est - V_true|),max(|theta_est - theta_true|)
均方根误差RMSE:sqrt(mean((V_est - V_true).^2)),相角同理
下面是我在IEEE 14节点系统上跑出来的一组参考结果(噪声种子固定,量测配置分三档):
| 配置 | 量测条件 | V最大误差(p.u.) | θ最大误差(rad) | V的RMSE | θ的RMSE |
|---|---|---|---|---|---|
| A | 仅SCADA功率量测 | 0.012 | 0.0082 | 0.0048 | 0.0035 |
| B | SCADA + 5个节点PMU | 0.0045 | 0.0018 | 0.0017 | 0.0009 |
| C | SCADA + 9个节点PMU | 0.0031 | 0.0008 | 0.0012 | 0.0004 |
从结果能看出两个规律:第一,PMU加入后,无论电压还是相角,估计精度都有显著提升,尤其是相角误差几乎下降一个数量级,因为PMU直接量测相角,给WLS提供了传统SCADA根本给不了的“硬信息”;第二,PMU数量从5个增加到9个,精度还在提升,但边际收益已经递减,这说明工程上不需要全网装PMU,合理布点就能达到不错的估计效果。
这套对比实验的价值就在这里:它用一个很直观的方式展示了量测冗余度和量测类型对估计精度的贡献,也顺便回答了“为什么现在都在推PMU混合量测状态估计”这个问题。
4. 实测中的关键问题与避坑指南
4.1 迭代不收敛,先别怪WLS
我在调试过程中遇到过好几次WLS迭代发散的情况,一开始以为是算法实现有bug,后来排查发现大部分问题出在数据准备环节。
最常见的原因是初值给得太离谱。WLS是迭代算法,对初值比较敏感,尤其当量测噪声大、冗余度低的时候,初值离真实解太远就可能发散。解决办法很简单:用平启动,V统一设为1p.u.,θ统一设为0,这个初值在绝大多数情况下都能让WLS正常收敛,也符合电网正常运行状态的物理常识。
第二个常见原因是我前面提到的参考角问题。如果状态向量里把平衡节点的θ_1放进去了,或者量测雅可比矩阵里对应的列没有去掉,法方程的G矩阵可能奇异或接近奇异,导致求逆崩溃。解决方法是把参考角对应的状态列删掉,估计完再补回去。
第三个原因是坏数据。我一开始为了模拟真实传感器,在量测数据里随机注入了几个明显的离群点,结果WLS直接跑飞。这是因为WLS对坏数据没有任何免疫力,一个坏量测就能把估计结果拉偏。如果你在调试,先把坏数据去掉或者改成纯高斯噪声,等算法跑通了再考虑加坏数据检测模块。
4.2 权重矩阵怎么设才合理
权重矩阵设计是WLS的精髓,也是最容易出错的地方。
正确做法是:每个量测的权重取1/σ²。σ是量测误差标准差,误差越小的量测权重越大。这个逻辑在数学上对应“最大似然估计在高斯噪声下等价于加权最小二乘”,在物理上等价于“更可信的观测应该对结果有更大的发言权”。
常见错误有三个。第一,权重直接用1/σ而不是1/σ²,导致量测之间的相对权重完全不对。第二,把有名值和标幺值混在一起使用,比如功率量测用MW和Mvar单位,电压量测用kV单位,结果同一个系统里数值量级从0.01到1000不等,权重矩阵的数值尺度严重失衡,G矩阵条件数爆炸,数值稳定性极差。第三,凭感觉调权重,比如为了“让结果好看”把PMU权重调得特别大,这会让估计结果过度偏向PMU量测,反而丢失了其它量测的有效信息。
我在项目里统一把所有量测转换到标幺值,然后按σ²倒数的原则设权重,效果最好,代码也最清晰。
4.3 相角参考基准的那些坑
这个坑特别隐蔽,但理解了之后整个项目就顺了。
传统WLS状态估计里,相角不是绝对量,而是相对于参考节点(平衡节点)的相角。也就是说,不管实际系统里相角是多少,估计算法只能得出“各节点相对于平衡节点的相角差”,平衡节点的θ默认等于0。
PMU则不同,它给的是带GPS时标的绝对相角。如果你在仿真里机械地把PMU的θ量测当成“绝对量”直接丢进WLS,而WLS内部默认平衡节点θ=0,两边参考基准对不上,估计结果就会出现系统性偏差。
在仿真项目里的处理方式是:约定一个统一的参考框架。我在生成PMU量测时,直接用NR潮流解里的θ(它已经是相对平衡节点的相角)加噪声,这样PMU量测和WLS的参考基准天然一致,问题就绕过去了。但这只是仿真里的做法。
在真实系统里,处理这个问题的方法一般是在状态估计模型里额外增加一个“参考相角偏差”状态量,或者利用GPS时标对PMU相角进行坐标变换,把所有PMU相角都归算到同一个参考系。这块属于工程实战的高级话题,做仿真验证时先理解“参考基准必须统一”这一点就足够了。
4.4 可观测性与量测配置的关系
可观测性是状态估计的一个前置条件:如果量测数量不够,或者量测类型搭配不合理,系统的信息矩阵G就会奇异,状态估计算法根本无法启动。
判断可观测性的一个简单方法是检查G矩阵的秩。如果rank(G)小于状态向量的维数,说明量测不足;如果某些节点的电压或相角完全没有量测和网络约束支撑,那它们对应的状态不可观测。
IEEE 14节点这个系统,如果只给少数节点装注入功率量测,很可能部分节点的状态根本推不出来,G矩阵直接奇异。我在做“仅SCADA”配置的时候就遇到过这个问题,解决办法是保证注入功率量测的节点数量足够多,并且每个不可直接量测的节点至少通过一条支路与可量测节点连通。
PMU的加入对可观测性的提升非常明显,特别是相角可观测性。传统SCADA系统里,相角是通过功率量测间接推导的,可观测性对量测配置要求很高;而PMU直接测相角,装一个PMU就相当于直接“锚定”了一个节点的相角,周围节点的相角通过支路方程也能很快确定。这也是为什么从工程角度看,PMU布点策略只需要覆盖部分关键节点,就能让全网状态估计的精度和鲁棒性大幅提升。
5. 还能怎么玩:扩展方向
这个项目做完之后,我顺手往几个方向做了扩展,效果都不错,这里简单列一下。
一是加坏数据检测。WLS本身不抗坏数据,但可以配合标准化残差检验和χ²检验,在迭代收敛后检测并剔除坏量测。原理很简单:如果某个量测的标准化残差特别大,它就很可能是坏数据。这个功能在实际工程里比WLS本身还重要,因为真实系统里坏数据比例不低。
二是换成动态状态估计。PMU刷新率高,静态WLS其实是浪费了时间维度上的信息。可以用扩展卡尔曼滤波或者无迹卡尔曼滤波,把状态估计从“一个断面”升级到“一段轨迹”,可以跟踪系统动态变化,这也是广域测量系统WAMS里很热门的方向。
三是做分布式状态估计。把大电网按区域切块,每个区域独立跑WLS,区域之间通过边界节点交换信息,迭代收敛到全网的全局最优解。这个方向对大型互联电网很有价值。
四是把算例从14节点换到更大系统。代码逻辑基本不用改,只要把bus和branch数据换掉就行。换到118节点系统后,稀疏矩阵的优势会体现得非常明显,跑一次WLS从原来的毫秒级到几十毫秒级,仍然非常快。
最后分享一个我个人的小技巧:做状态估计代码调试时,先跑一遍NR潮流,如果潮流本身不收敛,那只可能是Ybus或者数据有问题;WLS里出现的很多异常,往往都能追溯到这一步。先把“真值”算对,再谈估计精度,否则后面全是白跑。
这套流程我从原理到代码完整跑通之后,对电力系统状态估计的理解明显上了一个台阶。以前看文献里那些“加权最小二乘”“信息矩阵”“混合量测”只是抽象名词,现在闭上眼都能浮现出Matlab代码里每一行矩阵和变量在干什么。如果你也想真正搞懂这套东西,建议不要只读文章,打开Matlab,把代码一行行敲出来,然后换个系统、换组噪声、换个PMU配置,亲手摸一遍,比看十篇教程都管用。
