半不变量法概率潮流:免采样的电网随机波动分析利器

发布时间:2026/10/3 8:56:43
半不变量法概率潮流:免采样的电网随机波动分析利器 简介面向电力系统不确定性分析的半不变量法概率潮流计算源码包适用于电气工程研究人员、研究生及从事Matpower二次开发的工程师。包内共3个Matlab的m文件涵盖核心算法主程序、IEEE30节点标准测试数据以及基于Matpower内置潮流函数的求解脚本以紧凑的9KB体积呈现从负荷与出力概率建模、不变量求取到数值计算的关键实现流程。描述系统阐述了半不变量法处理随机扰动的原理、蒙特卡洛模拟与数值求解思路以及在Matpower环境下扩展潮流模块、支持正态分布等不确定模型的方法对理解电力市场与高比例新能源场景下的电网运行状态评估具有很强的参考价值。文件结构简洁便于对照学习概率潮流算法与Matpower内部接口。已有1409人学习下载适合具备Matlab与电力系统基础、希望快速复现或扩展该方法的读者。1. 半不变量法概率潮流不跑几千次采样也能算出电网波动区间做配电网规划或新能源接入评估时最头疼的问题不是算一次潮流而是算“一万次”潮流——风机出力今天一个样、明天一个样负荷曲线也在抖调度要的不是某一个确定断面而是“95%概率下电压不越限”这类概率指标。蒙特卡洛模拟能给出答案但IEEE 30节点系统跑5000次采样单次牛顿法迭代20轮一版结果出来少说要等几分钟调参还得重来。半不变量法Cumulant Method的价值在于它不采样直接用随机变量的各阶半不变量做卷积运算算出节点电压和支路潮流的概率分布速度和蒙特卡洛差两个数量级。这套资源是基于MATPOWER实现的半不变量法概率潮流源码包包含主计算脚本CM.m、IEEE 30节点数据data_ieee30.m和潮流计算入口runpf.m的配套封装适合已经有MATPOWER基础、想做随机潮流分析但不想从零写算法的研究者和工程师。2. 为什么是半不变量卷积太慢蒙特卡洛太贵半不变量把卷积累加变成求和2.1 概率潮流问题到底在算什么确定性潮流求解的是给定注入功率下的电压和潮流分布数学上是一个非线性方程组。概率潮流面对的是注入功率不再是确定值而是随机变量——负荷服从正态分布风电场出力服从Weibull分布光伏可能是Beta分布。此时节点电压幅值、相角和支路潮流都变成随机变量我们需要求的是它们的概率密度函数或累积分布函数。直接做法是用蒙特卡洛对每个随机注入抽样解一次确定性潮流重复几千次对结果做统计。这个方法被当成“金标准”用但计算量实在不友好。另一个思路是做解析卷积随机变量之和的分布是各分量分布的卷积但卷积的数值计算复杂度随变量个数和离散点数快速膨胀工程上基本走不通。半不变量法绕开了这两个麻烦。它的核心逻辑是随机变量的各阶矩和半不变量Cumulant之间有一一对应关系而独立随机变量之和的各阶半不变量恰好等于各分量半不变量的代数和。这意味着卷积运算被替换成了简单求和——这个替换是整件事能提速的根本原因。2.2 矩、半不变量与Gram-Charlier展开的配合半不变量又叫累积量记号上通常用κ_r表示r阶累积量。低阶累积量和中心矩的关系很直观κ_1是均值κ_2是方差κ_3和偏度相关κ_4和峰度相关。工程上常用矩-累积量转换公式例如κ_2 E[(X-μ)²]κ_3 E[(X-μ)³]κ_4 E[(X-μ)⁴] - 3(κ_2)²。高端展开用Cornish-Fisher级数基础版就用Gram-Charlier级数把待求随机变量的概率密度展开成标准正态分布的各阶导数展开到4阶或6阶就能得到不错的精度。这里要特别提一个细节半不变量法对输入随机变量有独立性要求——各节点注入功率的随机分量需要互相独立或者经过处理变成独立变量。如果风机之间相关性很强需要先做Cholesky分解或Nataf变换处理相关性否则各阶半不变量简单相加的前提就不成立了。2.3 在MATPOWER里的实现路径MATPOWER自带的runpf.m做的是确定性潮流输入是MATPOWER的case结构体输出是结果结构体。半不变量法要做的是“在确定性潮流周围叠加随机扰动”算法流程分三步第一步在期望值运行点做一次确定性潮流得到基准电压和相角第二步把节点注入功率的随机扰动通过潮流方程在基准点处的灵敏度矩阵即逆雅可比矩阵映射到节点电压和支路潮流的半不变量第三步用Gram-Charlier展开把这些半不变量还原成概率分布。这套源码包里CM.m是主脚本做的事情就是上面三步的串联data_ieee30.m准备的是IEEE 30节点系统数据runpf.m是对MATPOWER原版runpf的调用封装方便在扰动叠加后反复调用潮流计算。三者关系不复杂但CM.m里对雅可比矩阵的处理方式、对runpf返回值的解析方式是读懂整个代码的关键。3. 源码结构与数据准备先把IEEE 30节点系统的随机模型搭起来3.1 解压后文件之间的关系拿到压缩包解压里面有三个核心文件CM.m、data_ieee30.m、runpf.m。CM.m是入口脚本运行方式是直接在MATLAB命令窗口输入CM前提是当前路径下有data_ieee30.m生成的case结构体和runpf.m。data_ieee30.m本质上是一个MATPOWER格式的数据文件定义了30个节点、41条支路、6台发电机的基准运行数据。runpf.m这个文件在标准MATPOWER里也有同名函数这里之所以单独打包一份是因为概率潮流需要在每次扰动后反复调用潮流计算函数签名做了简化封装返回值只保留需要的字段。建议在MATLAB里这样组织路径% 将解压路径加入MATLAB搜索路径 addpath(D:\probabilistic_power_flow\half_invariant); % 先运行数据文件生成case结构体 mpc data_ieee30; % 查看基本系统规模 fprintf(节点数: %d\n, size(mpc.bus, 1)); fprintf(支路数: %d\n, size(mpc.branch, 1)); fprintf(发电机数: %d\n, size(mpc.gen, 1));data_ieee30需要手动执行一次或者由CM.m自动调用这点看版本而定。在CM.m里找一下是否有mpc data_ieee30这一行如果有则说明入口脚本自己加载数据不需要手动先跑数据文件。3.2 负荷和发电机随机模型的参数设置半不变量法要给出随机注入的分布参数通常的做法是在基准有功和无功负荷上叠加一个零均值的正态分布扰动标准差取基准值的2%~5%。温度、气象、经济活动导致的负荷波动大部分可以用正态分布近似。发电机的随机性则分两类常规火电的出力波动较小一般也按正态处理如果接入风电场则要用两参数Weibull分布拟合风速再通过风机功率曲线转换成功率分布。在CM.m中找如下参数定义段常见写法是这样% 负荷波动标准差比例相对基准值的百分比 load_sigma_percent 0.05; % 发电机出力波动标准差比例 gen_sigma_percent 0.03; % 提取基准负荷与出力 bus_load_real mpc.bus(:, 3); % 节点有功负荷 bus_load_imag mpc.bus(:, 4); % 节点无功负荷 gen_output mpc.gen(:, 2); % 发电机有功出力说明一下mpc.bus矩阵的第3列和第4列就是MATPOWER里的PD和QDgen矩阵第2列是PG。波动标准差按基准值比例设定这个值的选取直接影响结果的方差大小审稿人或者实际项目里会要求提供敏感性分析也就是测试不同的sigma百分比看结果怎么变。3.3 数据校验跑通确定性潮流是概率潮流的前提在半不变量法里基准运行点的确定性潮流结果是一切扰动叠加的参考系。如果基准潮流不收敛后面所有概率计算都是空中楼阁。拿到源码包第一步应该是先在原始数据上跑一次确定性潮流确认系统无异常% 调用压缩包内封装好的runpf results runpf(mpc); % 检查收敛标志1表示收敛 fprintf(潮流收敛标志: %d\n, results.success); % 查看系统总负荷与总网损 fprintf(总有功负荷: %.2f MW\n, sum(mpc.bus(:, 3))); fprintf(总网损: %.4f MW\n, sum(results.branch(:, 14)));正常情况IEEE 30节点系统应该一次性收敛。如果出现不收敛先检查数据文件里是否少了发电机节点、平衡节点的标识是否正确——节点类型代码里类型3是平衡节点类型2是PV节点类型1是PQ节点。再检查支路参数里的电阻电抗是否有零值零阻抗支路会让雅可比矩阵奇异。4. 核心算法实现从随机注入到电压分布CM.m的计算链条4.1 期望值运行点的确定性潮流CM.m的第一步要解决的是在基准值处构造潮流方程并在该点求解。这一步本质上就是一次常规runpf但结果要被后续灵敏度计算引用。在代码实现层面关键是把潮流结果的电压幅值、相角、支路潮流这几个量分别存成独立变量因为它们各自的概率分布在后面是分开求的。典型代码结构如下% 步骤1在期望值运行点求解确定性潮流 base_mpc mpc; base_results runpf(base_mpc); % 提取基准运行量 V0 base_results.bus(:, 8); % 电压幅值基准 theta0 base_results.bus(:, 9) * pi / 180; % 相角基准注意转弧度 P0 base_results.branch(:, 14); % 支路有功 Q0 base_results.branch(:, 15); % 支路无功 % 计算节点注入功率的基准值 P_inj0 mpc.bus(:, 3) - sum(...); % 实际上MATPOWER里可以换成gen和load计算注意相角的单位问题MATPOWER在bus矩阵第9列存的相角单位是度而雅可比矩阵计算时要用弧度这个转换漏掉的话灵敏度矩阵会整体偏差一个系数。这类单位差错在概率潮流结果里很难一眼看出但和蒙特卡洛对照时分布位置会整体偏移。4.2 构建灵敏度矩阵的核心逻辑半不变量法的核心数学步骤是节点注入功率扰动到节点电压相角和幅值的线性映射关系在基准点处近似等于潮流方程雅可比矩阵的逆。这里不做任何重新推导直接借助MATPOWER内部的牛顿法迭代信息。具体做法有两种。一种是在原版runpf基础上改造成输出雅可比矩阵的版本在迭代收敛后把最终迭代点的雅可比矩阵提取出来。另一种是直接调用MATPOWER提供的opf或runpf内部函数接口来取J矩阵。源码包的做法大概率是在runpf.m封装里修改了输出参数让调用方同时得到结果和雅可比。% 步骤2求取灵敏度矩阵逆雅可比 % 假设runpf封装返回了雅可比矩阵J [base_results, J] runpf(mpc, struct(return_J, 1)); % 对J求逆得到灵敏度矩阵S S inv(J); % 按需拆分电压相角对有功/无功注入的灵敏度电压幅值对有功/无功的灵敏度 S_theta_P S(1:nPV, 1:nPQ); S_V_Q S(nPV1:end, nPQ1:end);严格讲极坐标下牛顿法雅可比矩阵的结构是四个分块dP/dθ、dP/dV、dQ/dθ、dQ/dV。半不变量法里如果只考虑有功扰动对相角的影响、无功扰动对电压幅值的影响可以忽略交叉项这是快速解耦思路如果要做完整精度版必须让S矩阵涵盖全部四个分块。README或者代码注释里一般会说明用的是哪种近似。4.3 注入功率半不变量的叠加计算有了灵敏度矩阵S之后节点电压的各阶半不变量 S的各次幂 × 节点注入功率各阶半不变量的组合。这里要处理矩阵和半不变量的维度对齐每个节点电压是一个随机变量它的第r阶半不变量是一个数由所有节点注入功率的半不变量加权组合而来。代码里会体现在对每个节点、每一阶半不变量的循环上风格大致是% 步骤3计算节点电压的各阶半不变量 orders 1:6; % 计算到6阶 n_bus size(mpc.bus, 1); k_V zeros(n_bus, length(orders)); for bus_idx 1:n_bus for r 1:length(orders) order orders(r); % 第r阶电压半不变量 S矩阵第bus_idx行各元素^r 与注入功率半不变量的乘积和 S_row S(bus_idx, :); k_V(bus_idx, r) sum(S_row.^order .* k_inj(:, order)); end end这个循环是整段代码里最耗时的部分但相比蒙特卡洛的几千次潮流计算这个量级完全可以忽略。实际工程里矩阵化写法更快——直接在MATLAB里用矩阵乘法一步算出全部节点的半不变量。如果CM.m里没有做向量化优化对大系统要留意运行时间不过IEEE 30节点不会有压力。4.4 用Gram-Charlier级数还原概率密度得到电压的各阶半不变量后最后一步是把半不变量转换为概率密度函数。Gram-Charlier展开的基本形式是以标准正态分布φ(x)为基准用Hermite多项式逐阶修正。计算时需要先把半不变量转换为中心矩再计算Hermite多项式系数。代码中一般出现如下片段% 步骤4计算概率密度和累积分布 mu k_V(:, 1); % 一阶半不变量就是均值 sigma sqrt(k_V(:, 2)); % 二阶半不变量是方差 skewness k_V(:, 3) ./ (sigma.^3); kurtosis k_V(:, 4) ./ (sigma.^4) - 3; % 标准正态分布的概率密度和分布函数 x linspace(0.9, 1.1, 500); phi_x normpdf(x); Phi_x normcdf(x); % 对每个节点构造Gram-Charlier展开 for i 1:n_bus z (x - mu(i)) / sigma(i); pdf_i(i, :) phi_x .* (1 skewness(i)/6 * hermite(3, z) kurtosis(i)/24 * hermite(4, z)); endGram-Charlier展开到四阶截断时如果随机变量的偏度和峰度太大概率密度会出现负值或振荡。这是半不变量法的一个已知边界后面避坑章节会专门讲怎么识别和应对。5. 验证与结果解读拿蒙特卡洛当尺子什么样的偏差能接受5.1 搭建蒙特卡洛对照脚本半不变量法的计算结果是否可信不能只看它自己输出了什么必须和蒙特卡洛模拟做交叉验证。标准做法是使用相同的随机模型参数生成5000个样本每个样本都执行一次确定性潮流最后对电压幅值、支路潮流的样本集做统计得到均值和标准差作为对照基准。% 蒙特卡洛对照实现 N_mc 5000; V_mc zeros(n_bus, N_mc); for k 1:N_mc % 重新采样负荷和发电机出力 mpc_k mpc; mpc_k.bus(:, 3) mpc.bus(:, 3) .* (1 load_sigma_percent * randn(n_bus, 1)); mpc_k.bus(:, 4) mpc.bus(:, 4) .* (1 load_sigma_percent * randn(n_bus, 1)); mpc_k.gen(:, 2) mpc.gen(:, 2) .* (1 gen_sigma_percent * randn(size(mpc.gen, 1), 1)); % 调用确定性潮流 res_k runpf(mpc_k); V_mc(:, k) res_k.bus(:, 8); end % 蒙特卡洛统计结果 V_mc_mean mean(V_mc, 2); V_mc_std std(V_mc, 0, 2);注意上面的随机采样没有处理发电机功率平衡问题——负荷变了发电机出力也变了系统的总注入和总负荷不一致时潮流可能不收敛。实际中需要把平衡节点出力做相应补偿或者按比例分配给各台发电机这个细节直接决定蒙特卡洛对照是否有效。5.2 精度判断标准与典型偏差范围半不变量法和蒙特卡洛的对比通常看两方面均值偏差和标准差偏差。均值偏差在0.1%以内是正常的因为确定性潮流本身是精确解半不变量法的一阶结果和它等价标准差偏差则取决于展开阶数和非线性程度一般5%以内可接受。对IEEE 30节点这样的小系统母线电压幅值的标准差通常在0.001~0.005 pu之间。如果半不变量法算出的标准差比蒙特卡洛结果大出10%以上先怀疑Gram-Charlier展开截断阶数不够把阶数从4提高到6或8再看看是否收敛如果增大阶数结果抖动厉害说明分布本身的非高斯性太强这时要检查是不是负荷波动标准差设得过大——超过基准值10%的波动任何近似方法都会吃力。5.3 怎样检验分布的形态是否合理除了均值和方差95%置信区间也是工程上常用的概率指标。比如想知道某条线路有功潮流的95%区间上限在半不变量法结果里是对Gram-Charlier展开后的累积分布函数求反函数在蒙特卡洛结果里则是对样本排序取分位数。% 半不变量法95%区间假设已经得到CDF的反函数 V_lower mu - 1.96 * sigma; V_upper mu 1.96 * sigma; % 蒙特卡洛95%区间 V_mc_lower quantile(V_mc, 0.025, 2); V_mc_upper quantile(V_mc, 0.975, 2);正态近似下95%区间用1.96倍标准差就够但Gram-Charlier展开如果检测到明显偏度用1.96倍标准差会低估或高估单侧风险。更严谨的做法是用展开后的CDF数值求解2.5%和97.5%分位点。5.4 结果的工程化输出方式计算出概率分布后给调度或者规划人员看的不应该是一堆PDF曲线而是简洁的指标表每个节点的电压越限概率、每条支路的潮流越限概率、系统整体失负荷概率。CM.m如果只输出分布曲线需要自己再做一步后处理。% 计算电压越限概率假设电压上下限0.95~1.05 pu V_limits [0.95, 1.05]; % 对于每个节点计算超过上限的概率 prob_upper zeros(n_bus, 1); for i 1:n_bus z_upper (V_limits(2) - mu(i)) / sigma(i); prob_upper(i) 1 - normcdf(z_upper); end这个越限概率比单纯看均值方差直观得多——配电网规划里最关心的就是这类指标。6. 避坑与常见问题排查从结果异常反推代码错误6.1 结算结果均值对但方差明显偏小现象半不变量法算出的电压均值与蒙特卡洛一致但标准差明显小于蒙特卡洛结果缩水20%~50%。原因最常见的是灵敏度矩阵只用了一部分分块比如只考虑了有功对相角的灵敏度、无功对电压幅值的灵敏度忽略了交叉项。现代电网中线路电阻与电抗之比并不小有功扰动对电压幅值的影响不可忽略。此外还有一种情况是节点注入功率的方差只累加了负荷的扰动没有考虑发电机出力的随机性导致总注入扰动的方差被低估。解决检查CM.m中灵敏度矩阵的构造是否完整。完整做法是使用全雅可比矩阵S inv(J)而不是分块近似同时检查注入功率协方差矩阵的对角元素是否包含了全部随机源负荷发电机新能源。6.2 Gram-Charlier展开后概率密度出现负值现象计算出的概率密度函数PDF在均值附近正常但往两侧延伸后出现负值或者曲线尾部上下振荡、出现多个峰值。原因Gram-Charlier展开是围绕正态分布的级数修正截断到有限阶时如果真实分布与正态分布差异过大振荡效应就会出现。IEEE 30节点系统的某些节点本身电压分布就比较畸形四阶截断不够。解决提高展开阶数到6阶或8阶并检查偏度和峰度系数的大小。如果展开阶数提高后振荡依旧改用Cornish-Fisher展开或Edgeworth展开这两种展开在尾部表现更稳定。这里还有一个工程替代方案对尾部使用蒙特卡洛抽少量样本补足中间用半不变量法结果混合策略在工程上完全可行。6.3 支路潮流的概率分布结果与蒙特卡洛偏差很大现象电压幅值概率分布对得上但支路潮流的均值或标准差和蒙特卡洛结果差10%以上。原因支路潮流的计算公式里面有电压幅值、相角差的交叉项线性的灵敏度传递只适用于节点电压不能直接套用到支路潮流。CM.m里对支路潮流做了近似的线性化但没有考虑到潮流方程中的二次项。解决对于支路潮流这个量单独在基准点附近做泰勒展开到二阶项保留交叉项的影响。如果你用的半不变量法实现没有这一步输出结果对节点电压可信对支路潮流持保留态度或者只用这个值做粗略评估。6.4 matpower版本差异导致runpf调用报错现象运行CM.m时提示未定义函数或变量runpf或者提示Matpower版本不兼容、输入参数格式错误。原因MATPOWER从7.0开始对runpf的输入输出接口做了调整例如option结构体字段名变化、结果结构体新增了字段。压缩包里的runpf.m可能是基于旧版本MATPOWER6.x或7.0之前写的与当前安装版本冲突。解决先运行matpower的安装验证脚本test_matpower确认安装版本。然后对比当前版本case结构体的字段与代码中访问的字段必要时用fieldnames检查运行结果有哪些可用字段避免直接访问不存在的字段。6.5 负荷波动标准差设得过大导致结果不可信现象把load_sigma_percent从3%调到15%后半不变量法和蒙特卡洛的偏差急剧增大甚至出现电压均值偏移、分布形状明显不对。原因半不变量法本质是在基准运行点的邻域做线性化或二阶近似输入随机波动过大时系统运行点偏离基准点较远雅可比矩阵的线性映射假设失效。15%的负荷波动在很多重载节点会让运行点显著远离基准点。解决在设定波动标准差时控制在5%~8%以内。如果场景要求大波动选择基于点估计法或拉丁超立方采样的概率潮流算法替代。这套源码适合评估正常波动场景不适合极端运行方式评估。如果必须算大波动场景建议多设几个基准运行点分别做半不变量法再合成结果。7. 进阶把概率潮流做成可复用的分析工具7.1 把CM.m改造成一个可配置函数现在的CM.m是脚本结构换一个系统或者换一组参数要手动改代码。值得花半小时把它改造成函数接口输入系统数据mpc和波动参数输出概率分布结果。这样一个工具就能同时用在IEEE 30节点和实际区域的简化等值模型上。function result ppf_half_invariant(mpc, opt) % 半不变量法概率潮流 % 输入: % mpc - MATPOWER case结构体 % opt.load_sigma - 负荷波动标准差比例 % opt.gen_sigma - 发电机出力波动标准差比例 % opt.order - Gram-Charlier展开的最大阶数 % 输出: % result.V_mean, result.V_std - 电压幅值的均值与标准差 % result.P_branch_mean, result.P_branch_std - 支路有功潮流均值与标准差 load_sigma opt.load_sigma; gen_sigma opt.gen_sigma; n_order opt.order; % ... 主体计算与CM.m相同 end这个封装的价值在于只要数据文件是MATPOWER格式任何系统都能直接用。日常操作是把data_ieee30换成实际系统数据修改几个参数就能得到新的概率分布结果。7.2 增加多随机变量相关性输入的处理前面提到半不变量法的前提是随机变量独立。实际中相邻节点的负荷往往同步波动风电场之间的出力也有空间相关性。处理方法是做相关性变换对随机注入向量求协方差矩阵做Cholesky分解然后用分解矩阵生成相关样本。需要修改的是注入功率半不变量的求和部分从独立累加改为考虑协方差矩阵的加权叠加。这段逻辑值得仔细调试它把半不变量法的适用范围从“独立随机变量”扩展到“已知协方差矩阵的一般随机变量”是工程实用化最关键的一步。7.3 关于阶数选择和效率权衡的经验我自己做概率潮流习惯把Gram-Charlier展开阶数设为6。IEEE 30节点系统上6阶展开的计算时间比4阶多不了多少但分布尾部精度提升明显。如果节点数到了几百甚至上千矩阵运算和半不变量叠加部分可以做向量化6阶展开的计算仍然在秒级完成比蒙特卡洛快一到两个数量级。运行一次CM.m之后再看结果我一般先用蒙特卡洛跑500次快速验证均值和方差的偏差在合理范围内才认为这次参数设定有效。从那以后我每次拿到新的概率潮流代码都强制自己先把蒙特卡洛对照脚本跑通再去看半不变量法结果。这么做虽然前期多花十分钟但后面排查问题的成本至少省一半。希望这些经验对你有帮助。本文还有配套的精品资源点击获取