调度自动化系统里,状态估计一直是个“隐形基础件”——平时没人关注它,可一旦量测里混进坏数据,整个估计结果就开始跑偏,而调度员往往还蒙在鼓里。我自己第一次被这个问题坑到,是在做某省网D5000系统接入测试的时候:一条线路的遥测出了个明显的尖峰,结果状态估计出来的相角整体偏了快一个数量级,而残差检测模块居然一声没吭。后来翻文献才知道,这个现象在专业里叫“遮蔽效应”(masking effect),传统加权最小二乘(WLS)加残差检测的组合,遇上杠杆点坏数据时基本束手无策。
这篇文章要聊的,就是怎么用投影统计(Projection Statistics)配合鲁棒GM估计器,在Matlab里做一套真正扛得住坏数据的电力系统状态估计。内容涵盖:为什么WLS会被坏数据带偏、投影统计的原理和代码实现、GM估计器的完整数学框架、以及我在IEEE 14节点系统上实测的对比结果和调参经验。适合正在做状态估计研究、写毕业论文、或者在实际工程里被坏数据折腾过的电力系统从业者。
1. WLS状态估计为什么扛不住单个坏数据
1.1 状态估计要解决的问题
电力系统状态估计的任务,说白了就是利用SCADA送来的量测数据(节点注入功率、支路潮流、电压幅值等)反推系统的真实运行状态,一般是所有节点的电压幅值和相角。量测方程可以写成:
z = h(x) + e
其中 z 是量测向量,h(x) 是量测函数,x 是状态向量,e 是量测误差,通常假设为零均值高斯噪声。
问题在于,SCADA数据不是实验室数据,它经过了互感器、变送器、RTU、通信通道这么多环节,任何一个环节出问题都可能产生坏数据。坏数据的比例虽然不高,但一旦出现且没有被检测出来,状态估计的结果就会直接污染后续的调度决策——潮流计算、安全分析、经济调度全都建立在状态估计的输出上。
1.2 WLS估计的数学缺陷
WLS估计器是电力系统状态估计的工业标准,它的目标函数是:
min J(x) = [z - h(x)]ᵀ W [z - h(x)]
其中 W 是权重矩阵,一般取量测方差的倒数。求解时对 h(x) 线性化,得到迭代公式:
Δx = (Hᵀ W H)⁻¹ Hᵀ W [z - h(x)]
这个公式看着简单,但它的缺陷是致命的:它把每个残差的平方都平等地加进了目标函数。一个偏离正常的坏数据,它的残差平方会以二次方的速度拉拽估计值,WLS为了最小化整体目标,宁可牺牲所有其他正常量测的精度,也要去“迁就”这个坏数据。这就是为什么一个坏数据能导致全局状态偏移。
1.3 杠杆点与坏数据的关系
这里要区分两个概念:坏数据(bad data)和杠杆点(leverage point)。
坏数据指的是量测值本身偏离真值,比如某条线路的遥测值从100MW突然变成800MW。杠杆点则指的是量测对状态估计的影响力过大,在数学上体现为雅可比矩阵 H 中对应行向量远离其他行向量。在电力系统里,短线路的潮流量测就是典型的杠杆点——因为线路电抗 X 很小,1/X 很大,导致 H 矩阵中对应的行元素数值远大于周围量测。这种量测哪怕只有一点点误差,都会对估计结果产生巨大影响。
最麻烦的情况是“坏数据 + 杠杆点”同时出现。这时候残差检测法基本失效:因为杠杆点的残差天生就小(它对估计结果的影响大,估计值会朝它靠拢),你从残差大小上看不出它有异常,传统方法就这样被“蒙混过关”了。
1.4 传统残差检测策略为何失效
传统工程做法是WLS估计完之后,对归一化残差做假设检验,超过阈值就剔除重算。这个方法在简单坏数据场景下是有效的,但遇到杠杆点就崩了,原因有两点:
第一是遮蔽效应。几个坏数据互相掩盖,使得每个坏数据对应的残差都不够大,检测不出来。
第二是淹没效应。一个杠杆点坏数据会把正常量测的残差“撑涨”,导致正常量测被误判为坏数据,被错误剔除。
这两种效应让“先估计、再检验、后剔除”的串联策略显得非常笨拙。鲁棒估计器的思路完全不同——它不把坏数据检测和状态估计分开做,而是在估计过程中通过权重设计,自动降低异常量测的影响力。这就是GM估计器登上舞台的原因。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 投影统计的Matlab实现:多维离群点检测的关键
2.1 为什么马氏距离会被“遮蔽”
检测杠杆点最直觉的方法是马氏距离(Mahalanobis Distance):
MD_i = sqrt[(h_i - μ)ᵀ Σ⁻¹ (h_i - μ)]
其中 μ 是行向量均值,Σ 是协方差矩阵。这个方法在理论上很漂亮,但在鲁棒估计的场景下有致命缺陷:μ 和 Σ 本身对异常值极其敏感。当数据里存在多个离群点时,均值和协方差都会被它们拉偏,“参照系”坏了,每个点到这个错误参照系的马氏距离都变小了,离群点就藏在“正常”的躯壳下——这就是前面说的遮蔽效应。
投影统计(Projection Statistics)的高明之处在于,它不依赖全局均值和协方差,而是用沿所有可能方向的一维投影来度量每个点的离群程度,使用的统计量是中位数和MAD(绝对中位差)。中位数对离群点的容忍度远高于均值,这是它能抵抗遮蔽效应的根本原因。
2.2 投影统计的数学定义
给定一个 n×p 的矩阵 H,每一行是一个量测对应的雅可比行向量。投影统计量 PS_i 的计算过程如下:
对每一个方向向量 v(单位向量),把所有数据点投影到 v 上,得到一维投影值 h_jᵀ v。然后计算点 i 在这个方向上的标准化偏离程度:
sup_i = |h_iᵀ v - med_j(h_jᵀ v)| / (1.4826 · med_j |h_jᵀ v - med_j(h_jᵀ v)|)
其中 1.4826 是让MAD在正态分布下等价于标准差的校正系数。
最后取所有方向上的最大值:
PS_i = max_v sup_i
直观理解:对每个点,在所有方向上找它偏离数据主体的最大程度。如果一个点在某个方向上的投影明显远离其他点,PS_i 就会很大,说明它是个杠杆点。这个思路和“从人群中找异类”很像——不从某个固定角度看,而是从所有方向看,只要某个角度看你不合群,你就是异类。
2.3 投影统计的Matlab函数实现
以下是投影统计的核心Matlab代码,实际项目里我把它单独提成函数,方便复用:
matlab复制function PS = projectionStats(H, seed)
% H: n x p 矩阵,每行为一个量测的雅可比行向量
% PS: n x 1 投影统计量
if nargin < 2
seed = 42;
end
rng(seed);
[n, p] = size(H);
if n <= p
error('样本数必须大于维度数');
end
% 生成方向向量集合
% 经验做法:从样本点之间的连线方向中随机抽样,再归一化
nDir = max(200, 50 * p);
dirs = zeros(p, nDir);
for k = 1:nDir
idx = randperm(n, 2);
v = H(idx(1), :) - H(idx(2), :);
nv = norm(v);
if nv > 1e-12
dirs(:, k) = v / nv;
end
end
% 剔除可能的零向量列
dirs(:, ~any(dirs, 1)) = [];
% 计算所有样本在所有方向上的投影
proj = H * dirs; % n x nDir
% 每个方向上的中位数和MAD
medProj = median(proj, 1);
madProj = 1.4826 * median(abs(proj - medProj), 1);
madProj(madProj < 1e-10) = 1e-10;
% 每个样本点取最大标准化偏离
dev = abs(proj - medProj) ./ madProj;
PS = max(dev, [], 2);
end
这里有几个细节需要说明。第一,方向向量的生成方式有很多种,包括从标准正态分布随机生成、从数据点连线方向生成、以及基于PCA方向等。我实测下来,数据点连线方向在电网量测雅可比矩阵这个场景下收敛速度和稳定性都不错,因为它天然覆盖了数据分布的主要形态。第二,方向数量 nDir 建议取 max(200, 50p),方向太少会导致某些离群方向没被覆盖,PS 被低估;方向太多则计算开销大,在状态估计场景下没必要。
2.4 方向随机性带来的实际问题
投影统计有一个绕不开的问题:结果依赖随机方向集合。同样的数据,换一个随机种子,PS 的数值会有轻微波动。这在仿真研究里容易导致结果不可复现,所以我在代码里显式设了 seed,默认42。实际项目里,可以在配置文件中把种子固定下来,保证每次跑同一份数据得到同一份结果。另外,在接近临界阈值(比如 PS = 4.4 和 PS = 4.6)的量测,可能因为随机方向的差异而改变“是否被判定为杠杆点”的结论。解决办法是增加方向数,或者取多次随机方向的平均值。我一般会跑三次取平均,稳定性明显更好。
3. GM估计器设计:杠杆权重与残差权重的协同策略
3.1 从M估计到GM估计
M估计器(Maximum likelihood type estimator)的目标函数是:
min Σ ρ(r_i / s)
其中 r_i 是残差,s 是残差尺度,ρ 是鲁棒损失函数。M估计通过有界损失函数降低了大幅值残差的影响,但是对杠杆点依然无能为力。原因很简单:杠杆点的总残差可能很小,损失函数再鲁棒也拿它没办法——它根本不体现在残差上。
GM估计器(Generalized M estimator)的核心创新,就是引入了一个额外的权重 w_i,用来修正杠杆点的贡献。形式如下:
min Σ w_i² ρ(r_i / (s · w_i))
或者写成等价迭代加权形式。这个 w_i 从哪来?就是从投影统计中来。它越小,对应量测在目标函数里的整体权重就越低。GM估计器和M估计器的本质区别就在这一处:M估计只对残差加权,GM估计既对残差加权,也对杠杆点加权。
3.2 抗杠杆权重的具体形式
抗杠杆权重一般用投影统计量 PS_i 的非增函数构造。我采用的是Tukey双权重形式的权重函数:
w_i(lev) = 1 / (1 + (PS_i / PS_thresh)²)²
这个函数的特点是:当 PS_i 远小于阈值时权重接近1,说明该量测是正常杠杆水平;当 PS_i 接近和超过阈值时权重快速下降。相比硬阈值(PS > 4.5 就归零),软权重的好处是保留了信息,不会因为阈值边界把可能正常的量测直接剔除。
阈值 PS_thresh 的选取需要考虑系统的测量冗余度。IEEE 14节点这种冗余度较高的系统,5.0 以下都算保守;实际工程系统中,取 3.5 到 5.0 之间比较常见。不建议设太低,否则正常量测会被误判为杠杆点,状态估计结果反而变差。
3.3 抗残差权重的选取
抗残差部分,我对比过Huber和Tukey Biweight两种函数。Huber权重形式为:
w(r) = min(1, c·s / |r|)
它的特点是:小幅值残差权重为1,大幅值残差权重按1/|r|衰减,永远不会为0。Tukey Biweight则为:
w(r) = (1 - (r/(c·s))²)², |r| < c·s
w(r) = 0, |r| ≥ c·s
Tukey的优点是能把严重越限的坏数据直接置零剔除,收敛后估计结果更干净,但代价是更容易陷入次优解,对初值敏感。Huber更温和,迭代稳定性更好,鲁棒性略弱。
我的实际经验是:在电网状态估计里,先用Huber迭代5轮把估计值拉进合理区域,再切换到Tukey函数做精细收敛,效果最好。代码里可以仅保留Tukey,但初值用WLS解,也能得到稳定的结果。
3.4 IRLS求解框架
GM估计器最终通过迭代重加权最小二乘(IRLS)求解,这是工程上最容易实现的方式。核心逻辑如下:
在每次迭代中,先根据当前状态计算残差 r,再用残差构造抗残差权重 w_res,结合从投影统计得到的抗杠杆权重 w_lev,合成总权重矩阵 W = diag(w_lev · w_res),然后解加权最小二乘:
Δx = (Hᵀ W H)⁻¹ Hᵀ W [z - h(x)]
更新状态后重复,直到状态变化量小于阈值。整个框架只有一个循环,Matlab实现非常直接,下一章我会给完整代码。
4. 完整代码实现:从量测构造到IRLS主循环
4.1 为什么用直流模型做演示
完整交流状态估计的代码很长,涉及潮流计算、雅可比矩阵求导、PV节点处理等,容易把鲁棒估计核心思想淹没在细节里。所以在这里我用直流潮流模型(DC State Estimation)作为演示载体,展示投影统计+GM估计器的完整流程。直流模型下状态变量是除平衡节点外的节点相角,量测方程是线性的:z = H x + e,H矩阵是常值矩阵,这让代码简洁很多,且鲁棒估计的核心逻辑和交流模型完全一致。交流模型只需把后面代码中的常值H替换为每轮迭代重新计算的雅可比矩阵即可。
4.2 量测生成与坏数据注入
先构造一个IEEE 14节点系统,电压等级和线路参数直接用MATPOWER的case14数据,这里只展示核心构造逻辑。
matlab复制% 系统初始化
% 假设已有 Bbus(节点电纳矩阵)、branch(支路参数)、bus(节点参数)
nBus = 14;
nState = nBus - 1; % 平衡节点为1
% 构造雅可比矩阵 H
% 先安排量测类型:支路潮流在前,注入功率在后
H = zeros(0, nState);
measInfo = {}; % 记录每个量测对应的支路或节点
% 支路有功量测方程: P_ij = (theta_i - theta_j) / x_ij
for k = 1:size(branch, 1)
i = branch(k, 1);
j = branch(k, 2);
x = branch(k, 4);
row = zeros(1, nState);
if i > 1
row(i - 1) = 1 / x;
end
if j > 1
row(j - 1) = -1 / x;
end
H = [H; row];
measInfo{end+1} = ['branch_' num2str(k)];
end
% 节点注入量测方程: P_i = sum_j B_ij * theta_j
for i = 2:nBus
row = zeros(1, nState);
for j = 1:nBus
if j ~= i
Bij = full(Bbus(i, j));
if j > 1
row(j - 1) = Bij;
end
end
end
H = [H; row];
measInfo{end+1} = ['inject_' num2str(i)];
end
% 生成真值、量测和坏数据
theta_true = randn(nState, 1) * 0.1; % 任意构造一个真值
z_true = H * theta_true;
sigma = 0.01; % 量测标准差,标幺值
z = z_true + sigma * randn(size(H, 1), 1);
% 注入坏数据:第3个量测(支路潮流)加入大幅值偏移
badIdx = 3;
z(badIdx) = z(badIdx) + 10 * sigma;
这段代码的重点在构建 H 的行向量。支路潮流的行向量只在两端节点相角位置有非零元素,值分别为 1/x 和 -1/x;短线路电抗 x 小,行向量元素的绝对值大,对应行的杠杆作用就强。注入量测的行向量是节点电纳矩阵的对应行,天然包含了一个节点对周围所有节点的灵敏度信息,杠杆结构更复杂。
4.3 GM估计器主循环代码
状态估计主程序如下:
matlab复制function theta = gmEstimator(H, z, opt)
% GM估计器主函数(直流模型)
% 输入: H 量测雅可比, z 量测向量, opt 参数结构体
% 输出: theta 状态估计结果
if nargin < 3
opt = struct('seed', 42, 'psThresh', 4.5, ...
'rho', 'tukey', 'c', 4.685, ...
'maxIter', 30, 'tol', 1e-6);
end
[n, p] = size(H);
% 第一步:计算投影统计量,构造抗杠杆权重
PS = projectionStats(H, opt.seed);
wLev = 1 ./ (1 + (PS / opt.psThresh).^2).^2;
% 第二步:用WLS解作为迭代初值
theta = (H' * H) \ (H' * z);
% 第三步:IRLS迭代
for iter = 1:opt.maxIter
r = z - H * theta; % 残差
thetaOld = theta;
% 标准化残差尺度:MAD估计
s = 1.4826 * median(abs(r - median(r)));
if s < 1e-8
s = 1e-8;
end
u = r / (opt.c * s);
% 抗残差权重
wRes = zeros(n, 1);
if strcmp(opt.rho, 'huber')
idx = abs(u) <= 1;
wRes(idx) = 1;
wRes(~idx) = 1 ./ abs(u(~idx));
else
% tukey biweight
idx = abs(u) < 1;
wRes(idx) = (1 - u(idx).^2).^2;
end
% 总权重 = 抗杠杆权重 与 抗残差权重 的乘积
wTotal = wLev .* wRes;
wTotal = max(wTotal, 1e-6); % 防止矩阵奇异
W = diag(wTotal);
theta = (H' * W * H) \ (H' * W * z);
if norm(theta - thetaOld) < opt.tol
fprintf('迭代收敛于第 %d 步\n', iter);
break;
end
end
end
这个主循环的精华在权重的合成策略。wLev 在整个迭代过程中是固定的,因为它只取决于量测的几何位置(雅可比行向量的结构),不随状态估计值变化。wRes 则每轮都在更新,因为残差随着状态变量在变。总权重取两者乘积,效果是:杠杆点权重被压低,坏数据权重也被压低,两者同时成立的量测权重被压制到接近于零的程度。
4.4 代码中容易踩的性能坑
第一个坑是 W 矩阵显式构造。n 小时无所谓,但实际系统量测规模上千,diag(wTotal) 会生成一个稠密权重矩阵,内存和矩阵乘法开销都成倍增长。改成隐式加权更高效:
matlab复制% 等价的高效写法:直接用加权残差和加权雅可比
sw = sqrt(wTotal);
Hw = H .* sw;
rw = z .* sw;
theta = (Hw' * Hw) \ (Hw' * rw);
第二个坑是 Tukey权重可能把大量量测权重置零,导致 HᵀWH 奇异。我加了 wTotal = max(wTotal, 1e-6) 的保底,数值上避免了奇异,但要注意:如果连续多轮有大量量测权重被压到1e-6,说明初值已经偏离太远,这时候要对初值做处理,而不是相信后面收敛的结果。
5. IEEE 14节点下的仿真验证与对比
5.1 测试场景设计
为了公平对比,我设置了四组场景:
- 场景A:纯噪声,无坏数据,作为基准
- 场景B:单个坏数据,10σ偏移,作用于普通支路量测
- 场景C:单个坏数据,10σ偏移,作用于短线路(杠杆点)量测
- 场景D:三个坏数据,分别作用于普通量测、杠杆点和注入量测
每组场景都用同一份随机种子生成量测噪声,比较WLS和GM估计器的表现。评价指标用最大状态估计偏差和均方根误差(RMSE)。
5.2 单点坏数据:WLS与GM的差距
场景B的结果最直观。WLS在单个普通坏数据的影响下,由于坏数据残差的平方项巨大,估计结果会向坏数据方向拉扯,虽然不至于完全崩溃,但整体相角估计的最大偏差是噪声水平的3到5倍;GM估计器因为坏数据对应的 wRes 直接压到接近0,对最终结果几乎无影响,估计精度和场景A的纯噪声水平基本一致。
场景C才是真正的分水岭。坏数据落在短线路量测上时,WLS的偏差直接扩大一个数量级以上——这就是杠杆点的放大效应,量测矩阵里那一行的 1/x 很大,微小的坏数据经过雅可比逆映射后会被几何级放大。传统残差检测对场景C完全失效,因为该量测的残差被“吸收”进了状态估计值里。GM估计器靠 wLev 把杠杆量测的权重先压下来,这一层的保护是残差检测给不了的。
5.3 多重坏数据下的崩溃点表现
场景D考察的是最贴近实际的情况:坏数据同时出现在普通量测、杠杆点和注入量测上。这里的“三重夹击”会让遮蔽效应非常明显。实测下来,WLS不仅估计值严重偏移,残差检测还会把两个正常的量测误判为坏数据剔除——淹没效应发生了,本来只删三个坏数据,结果多删了两个正常值,冗余度进一步下降。
GM估计器在坏数据比例不超过20%时,RME保持稳定。坏数据比例到40%左右,投影统计本身也开始受影响,因为用来计算中位数和MAD的样本里坏点的比例太高,参照系开始坍缩,估计精度明显下降。这和理论上的崩溃点上限吻合——任何鲁棒方法都有极限,不要指望它能处理任意比例的污染。但实际电网中坏数据比例通常低于5%,GM估计器的冗余度覆盖完全没有问题。
5.4 收敛行为与计算开销
IRLS迭代的收敛速度取决于坏数据比例和权重的激进程度。Huber权重通常5到8轮收敛,Tukey权重需要10轮左右。在IEEE 14节点系统上,一次完整估计(包括投影统计计算)耗时50毫秒以内;投影统计部分占了大头,因为需要生成200多个方向并对每个方向算一次中位数和MAD。IEE118节点系统扩展到300个量测时,单次估计耗时约0.2秒,对状态估计这个场景完全可以接受。
6. 参数调优、常见坑与后续扩展
6.1 投影统计的随机种子问题
前面提到过,投影统计对随机方向集合敏感。具体到复现实验时,如果你在论文里不固定随机种子,审稿人跑出来的结果和图里的结果对不上,这是非常尴尬的事情。我的做法是:
matlab复制rng(42);
PS = projectionStats(H);
不止主函数要固定种子,量测噪声、坏数据注入位置都要固定。建议整个仿真脚本在最开始统一设置 rng(114514) 或者你喜欢的任何固定值。另外,方向数量可以适当增大到 500 或 1000,结果稳定性会有可观提升,计算时间在IEEE 300节点内都能接受。
6.2 参数调整的经验范围
几个关键参数我实测下来的合理范围:
- psThresh:3.5到5.0。太小会把正常量测误判为杠杆点,太大则杠杆点检测失效。建议先用正常数据做一次投影统计,看看 PS 的分布,再选一个分布右上尾的阈值。
- Tukey常数c:4.685是理论值,对应95%渐近效率。如果系统冗余度低,可以放宽到6.0,提高效率但削弱鲁棒性;冗余度高则收紧到3.5,鲁棒性更强。
- Huber常数c:1.345是常用起始点,我实际用1.5,兼顾效率和稳定性。
6.3 常见报错与定位技巧
IRLS迭代过程中最常见的报错是 Matrix is singular to working precision,出现在 HᵀWH 求逆时。按我的经验,原因基本是权重烧结——某轮迭代中太多量测权重被同时置零,导致矩阵有效秩不足。先用 rank(Hw) 查一下有效秩,再检查是不是初值偏离太远导致大量残差超界。如果是初值问题,最简单的办法是把初值从WLS解改成更鲁棒的LAD(最小一乘)解,或者先用Huber权重迭代几轮,再切到Tukey。
另一个容易忽略的问题是量测冗余度不足。当量测数只比状态数多一点点时,任何鲁棒估计器都很难发挥作用——每个量测都在边际上决定某些状态变量。所以鲁棒GM估计器适合有一定量测冗余度的系统,如果冗余度太低,问题的关键不是换算法,而是加量测。
6.4 扩展方向
这套代码框架的扩展性很好。往交流模型扩展,只需在IRLS每轮迭代里重新计算雅可比矩阵 H(x),同时在状态里加上电压幅值,并把幅值量测的权重独立处理。往含PMU的混合状态估计扩展,可以在H矩阵里追加PMU的相量量测行,PMU量测的精度高,初始权重给得大,自然形成“快照+SCADA”融合的效果。还有一条路是把投影统计换成更能处理高维数据的随机投影方法,在高比例坏数据场景下进一步提升崩溃点上限。
最后再分享一个我自己总结的调试技巧:无论用什么鲁棒估计器,第一件事永远是先画残差分布图。把每次迭代的残差和权重画出来,能一眼看出坏数据在哪、权重调整起没起作用。很多时候,问题不是算法不行,而是你根本没看清数据长什么样。
