简介:面向电力系统潮流计算学习者与工程人员,这份MATLAB源码基于牛顿-拉夫森迭代法,支持IEEE 6节点与9节点标准测试系统,用于分析稳态下的电压幅值、相角以及线路有功无功潮流分布。压缩包中共有2个m文件,整体大小仅2KB,代码精简,适合直接运行和修改。目前已有426人学习该资源。脚本覆盖了网络模型定义、初始值设置、迭代求解、收敛判定与结果输出等关键步骤,清晰展示了PQ节点、PV节点及平衡节点的处理方式,可帮助读者从代码层面理解潮流计算的数值解法本质。同时,两个示例分别针对IEEE 6节点和9节点系统,便于对比不同规模网络下的算法表现,也可作为课程实验、毕业设计或科研算法验证的基础工具。整体来看,这套MATLAB脚本结构清晰,复用性强,便于快速掌握核心流程。
1. 从9节点还是6节点入手:先搞清数据,再谈潮流计算
拿到 power_flow、IEEE 9节点、6节点这类任务的人,通常不是不懂潮流方程,而是卡在数据和算法的接缝上:手里没有一份能直接喂给程序的母线表和支路表,或者数据表跟算法对不上。IEEE 9节点(WSCC 3机9节点)和6节点测试系统,是电力系统论文里最常用的两个小型基准算例,前者参数全网高度统一、最容易跑通,后者在不同教材和论文里存在多个版本、最容易踩坑。这篇笔记打算完整走一遍:怎么把母线数据和支路数据整理成程序能读的格式,怎么用牛顿-拉夫逊法自己写一个几十行的潮流求解器,再用 MATLAB 下的 MATPOWER 做交叉验证。适合刚接手电力系统仿真、准备做配电网或微电网算例,以及打算在论文里引用 IEEE 标准算例的工程师和研究生。
2. IEEE 6节点与9节点测试系统:数据字段、版本差异与选型
2.1 两个系统的来头:WSCC 3机9节点与教材经典6节点
IEEE 9节点系统的正式名字叫 WSCC 3机9节点系统,来自美国西部电网协调委员会的一套标准模型,MATPOWER 里直接叫 case9。它最值钱的一点是数据收敛:全世界的论文、课件、开源仓库用的几乎都是同一套母线阻抗和负荷参数,你随便找一篇附录去核对,数值都不会差太远。9条母线、9条支路、3台发电机,拓扑简单清楚,调压器、输电线路、负荷都覆盖到了,做算法验证很合适。
IEEE 6节点则要多留一个心眼。它最早出自 Wood 和 Wollenberg 合著的《Power Generation, Operation and Control》教材,后来论文和课件里的 6 节点算例基本都从这套数据衍生出来,但在负荷量、变压器抽头、并联电容上各有微调。你拿到手的 6 节点,和另一个课题组拿到的 6 节点,算出来的母线电压可能差 0.1% 甚至更多,这不是程序错了,是数据版本不同。
为什么两个系统这么受潮流计算偏爱?因为规模小到能做白盒验证。9 节点可以全部手工推导,任何一步算错都能顺着矩阵找回来;6 节点更小,但里面带了两台调压变压器,能把潮流计算里最阴险的非标变比问题暴露出来。很多 IEEE 电力系统会议论文在提新算法时,都习惯先用这两个系统做自证,再上大规模算例,业界基本把这当成了约定俗成的流程。
2.2 母线/支路数据怎么读:一张字段表说清单位与基准
不管从哪个渠道拿到数据,电网潮流数据最终都可以归成两张表:母线表(bus)和支路表(branch)。母线表每一行是一个节点,支路表每一行是一条线路或一台变压器。理解这两张表的字段,比理解算法本身更早一步,因为字段单位不一致是最常见的翻车根源。
| 表 | 字段 | 含义 | 单位 | 在算法里的角色 |
|---|---|---|---|---|
| 母线 | Bus | 母线编号 | 1 到 N | Ybus 的行列索引 |
| 母线 | Type | 2=slack,1=PV,0=PQ | - | 决定哪些量已知、哪些要迭代 |
| 母线 | Pg / Qg | 发电机有功 / 无功出力 | MW / MVar | 节点注入功率的正项 |
| 母线 | Pd / Qd | 负荷有功 / 无功 | MW / MVar | 节点注入功率的负项 |
| 母线 | Vmag | 电压幅值初值 | pu | PV 和 slack 固定,PQ 待求 |
| 母线 | Vang | 相角初值 | deg | slack 固定,其余待求 |
| 支路 | from / to | 首端 / 末端母线编号 | - | Ybus 非对角元素位置 |
| 支路 | R / X | 支路电阻 / 电抗 | pu | 计算串联导纳 |
| 支路 | B | 线路总充电电纳 | pu | 组装对地半电容 |
| 支路 | tap | 变压器变比 | pu | 非标变比时要修正 Ybus |
这里最关键的是基准值。通常这两个系统都以 100 MVA 为功率基准,所有阻抗、导纳、电压都已经是标幺值,只有功率是 MW/MVar 的绝对值。所以在把功率写进方程之前,必须除以基准功率,否则牛顿法算出来的结果会整体放大 100 倍,这是新手必踩的坑。
2.3 为什么先用9节点跑通,再用6节点验泛化
我的建议很明确:如果你第一次写潮流程序,先用 9 节点。原因是 9 节点数据没有歧义,MATPOWER 自带 case9,跑出来的结果全网可以互相核对;而且它的结构非常清晰,1 号母线是平衡节点,2、3 号是 PV 节点,4 到 9 号是负荷与联络节点,一条发电—输电—负荷链路明明白白。用这套数据调通你的牛顿-拉夫逊代码,遇到任何偏差都容易定位。
6 节点适合作为第二关。它的规模更小,一台普通笔记本做上万次潮流计算也就是秒级的事,适合批量场景;但它的变压器抽头和重负荷会让程序暴露出更多边界问题,正好用来检验你的程序对新数据版本够不够稳。我一般把 9 节点当"标准答案",把 6 节点当"压力测试",两边都跑过,才敢说这个程序可以拿去做别的算例。
提示:如果你拿到的是 MATPOWER 自带的 case6ww,它就是教材那套 6 节点系统,数据源头可靠。如果是论文附录里的 6 节点,先看它是否写了变压器变比和充电电纳,这两项最容易引入版本差异。
3. 手写牛顿-拉夫逊潮流:Ybus、Jacobian 与两个收敛细节
3.1 从支路表到Ybus:抽头变比k和充电电容怎么处理
潮流计算的第一步永远是组装节点导纳矩阵 Ybus。Ybus 的对角线是各母线自导纳,非对角线是互导纳,所有支路的阻抗、充电电容、变压器变比都汇总在这里。组装时最容易出错的是两点:线路充电电容 B 在数据里给的是总电纳,实际并联在两端节点时要各取一半;变压器非标变比 tap 不是 1.0 时,首端和末端的自导纳修正公式不一样。
下面这段代码以 IEEE 9 节点为例,先把数据按统一格式定义好,再组装 Ybus。注意我这里的支路数据里,B 列填的是总充电电纳,变比 tap 统一填 1.0,因为 case9 的三台变压器都是标称变比。
import numpy as np # ---------- IEEE 9节点数据,基准 100MVA ---------- # 母线: [类型, Pg, Qg, Pd, Qd, V初值, θ初值(°)] # 类型: 0=PQ, 1=PV, 2=slack bus9 = np.array([ [2, 0, 0, 0, 0, 1.040, 0.0], # 1号 平衡母线 [1, 163, 0, 0, 0, 1.025, 0.0], # 2号 PV [1, 85, 0, 0, 0, 1.025, 0.0], # 3号 PV [0, 0, 0, 0, 0, 1.000, 0.0], # 4号 PQ [0, 0, 0, 90, 30, 1.000, 0.0], # 5号 PQ [0, 0, 0, 0, 0, 1.000, 0.0], # 6号 PQ [0, 0, 0, 100, 35, 1.000, 0.0], # 7号 PQ [0, 0, 0, 0, 0, 1.000, 0.0], # 8号 PQ [0, 0, 0, 125, 50, 1.000, 0.0], # 9号 PQ ]) # 支路: [首端, 末端, R(pu), X(pu), B总(pu), 变比tap] br9 = np.array([ [0, 3, 0.000, 0.0576, 0.000, 1.0], # 1-4 变压器 [3, 4, 0.017, 0.092, 0.158, 1.0], # 4-5 线路 [4, 5, 0.039, 0.170, 0.358, 1.0], # 5-6 线路 [2, 5, 0.000, 0.0586, 0.000, 1.0], # 3-6 变压器 [5, 6, 0.012, 0.101, 0.209, 1.0], # 6-7 线路 [6, 7, 0.009, 0.072, 0.149, 1.0], # 7-8 线路 [7, 1, 0.000, 0.0625, 0.000, 1.0], # 8-2 变压器 [7, 8, 0.032, 0.161, 0.306, 1.0], # 8-9 线路 [8, 3, 0.010, 0.085, 0.176, 1.0], # 9-4 线路 ]) def build_ybus(bus, br): n = len(bus) Y = np.zeros((n, n), dtype=complex) for f, t, R, X, B, tap in br: z = complex(R, X) y = 1.0 / z if z != 0 else 1e-12 # 防止除以零 # 统一处理:变比k=1时退化为普通线路,k!=1时按变压器π模型 Y[f, f] += y / (tap * tap) + 0.5j * B Y[t, t] += y + 0.5j * B Y[f, t] -= y / tap Y[t, f] -= y / tap return Y逻辑说明:支路阻抗 z 由 R 和 X 构成,导纳 y 是它的倒数。充电电纳 B 在数据里是总量,所以两端各挂一半。变比 tap 的修正只影响首端自导纳和两个互导纳:首端自导纳除以 tap 的平方,互导纳除以 tap。当 tap 等于 1.0 时,这套公式默认退化成普通线路,所以不需要单独写 if 分支。
参数说明:bus9 里每一行最后两个数是电压初值 1.0 pu 和相角初值 0 度,这叫做平启动。对绝大多数传输系统,平启动配合牛顿-拉夫逊法都能收敛,不需要额外做复杂初始化。br9 里的 B 列注意是总充电电纳,MATPOWER 的 case9 数据里 4-5 线路给的是 0.158,对应每端 0.079,我代码里直接对 B 取一半,正好对上。
3.2 Jacobian四块偏导怎么组装成线性方程组
牛顿-拉夫逊潮流的核心思路是:把节点注入功率方程在当前电压处做一阶泰勒展开,得到失配量 ΔP、ΔQ 与修正量 Δθ、ΔV 之间的线性关系,反复求解直到失配量小于阈值。Jacobian 矩阵由四块组成:∂P/∂θ、∂P/∂V、∂Q/∂θ、∂Q/∂V。组装时要按状态量的取舍来决定保留哪些行列。
平衡母线的 θ 和 V 都是固定的,不参与迭代;PV 母线的 V 固定、θ 待求,无功失配不参与迭代;PQ 母线的 θ 和 V 都待求。所以最终线性方程组的规模是:角度方向包含所有 PV 和 PQ 母线,电压方向只包含 PQ 母线。
def newton_pf(bus, br, tol=1e-10, max_iter=40): n, base = len(bus), 100.0 typ = bus[:, 0].astype(int) pq = np.where(typ == 0)[0] # PQ 母线索引 pv = np.where(typ == 1)[0] # PV 母线索引 slack = np.where(typ == 2)[0][0] # 平衡母线索引 pvpq = np.concatenate([pv, pq]) # 角度待求的母线,不含 slack Vm = bus[:, 5].astype(float).copy() # 电压幅值 Va = np.deg2rad(bus[:, 6].astype(float)).copy() # 相角转弧度 Y = build_ybus(bus, br) # 功率注入 = 发电 - 负荷,转标幺 Sbase = (bus[:, 1] - bus[:, 3]) / base + 1j * (bus[:, 2] - bus[:, 4]) / base Pspec, Qspec = Sbase.real, Sbase.imag for it in range(max_iter): V = Vm * np.exp(1j * Va) S = V * np.conj(Y @ V) # 当前电压下的注入功率 dP = Pspec - S.real # 有功失配 dQ = Qspec - S.imag # 无功失配 dF = np.concatenate([dP[pvpq], dQ[pq]]) if np.max(np.abs(dF)) < tol: break # ---- 组装 Jacobian 四块 ---- J11 = np.zeros((n, n)); J12 = np.zeros((n, n)) J21 = np.zeros((n, n)); J22 = np.zeros((n, n)) G, B = Y.real, Y.imag for i in range(n): for j in range(n): dth = Va[i] - Va[j] if i == j: J11[i, i] = -S.imag[i] - Vm[i]**2 * B[i, i] J12[i, i] = S.real[i] / Vm[i] + Vm[i] * G[i, i] J21[i, i] = S.real[i] - Vm[i]**2 * G[i, i] J22[i, i] = S.imag[i] / Vm[i] - Vm[i] * B[i, i] else: J11[i, j] = Vm[i] * Vm[j] * (G[i,j]*np.sin(dth) - B[i,j]*np.cos(dth)) J21[i, j] = -Vm[i] * Vm[j] * (G[i,j]*np.cos(dth) + B[i,j]*np.sin(dth)) J12[i, j] = Vm[i] * (G[i,j]*np.cos(dth) + B[i,j]*np.sin(dth)) J22[i, j] = Vm[i] * (G[i,j]*np.sin(dth) - B[i,j]*np.cos(dth)) # 按状态量取舍,剔除 slack 的相角列和 PV 的电压列 J = np.block([ [J11[np.ix_(pvpq, pvpq)], J12[np.ix_(pvpq, pq)]], [J21[np.ix_(pq, pvpq)], J22[np.ix_(pq, pq)]], ]) dx = np.linalg.solve(J, dF) Va[pvpq] += dx[:len(pvpq)] Vm[pq] += dx[len(pvpq):] V = Vm * np.exp(1j * Va) Qcal = (V * np.conj(Y @ V)).imag # PV 和 slack 的无功出力 return V, Qcal逻辑说明:每次迭代先算当前电压下的注入功率 S,然后和给定值做差得到失配量 dF。对角项的四个偏导公式用的是标准形式,非对角项通过 θ_i - θ_j 计算。组装时用 np.ix_ 做索引切片,把 slack 的相角列和 PV 的电压列从矩阵里剔除,这样线性方程组才是可解的方阵。
参数说明:tol 取 1e-10,对应功率失配小于 0.00001 W 级别,对 9 节点这种小系统已经足够严格。max_iter 设 40,正常 4 到 6 次迭代就能收敛。PV 母线的无功不参与迭代,但收敛后要从功率平衡方程里反推出来,这就是返回值里 Qcal 的作用——用它可以核对发电机无功是否越限。
3.3 收敛判据和初值:平启动为什么够用
很多人喜欢把收敛判据分成电压偏差和功率偏差两种,实际工程里我只认功率失配量。因为电压差 0.0001 pu 在重负荷系统里可能对应很大的功率偏差,而功率失配才是潮流方程真正要平衡的东西。我上面代码里用的是 dF 的最大绝对值,也就是同时看 ΔP 和 ΔQ 的最大值,这个判据简单直接,不会出现"电压看着收敛了、支路功率还差着一截"的假象。
初值方面,平启动(所有 PQ 母线 V=1.0、θ=0)对 9 节点和 6 节点都够用。只有在系统重载到极限、或者支路参数特别悬殊时,平启动才会出问题,那时候可以把初值改成上一次收敛结果,或者先用高斯-赛德尔法迭代几轮再切到牛顿法。对本文的两个系统,平启动加牛顿法就够了,不用额外做初值优化。
4. 用MATPOWER做交叉验证:最小算例与结果对拍
4.1 loadcase + runpf 跑通9节点的最小命令
自己写的代码算出来,必须有个可信的参照物。MATPOWER 是 MATLAB 环境下的开源潮流计算工具,内置了 case9,这是业界公认的参考实现。最小验证流程只有三步:加载算例、跑潮流、打印结果。
% 在 MATLAB 中运行,需要先把 MATPOWER 添加到路径 mpc = loadcase('case9'); % 载入 IEEE 9节点自带算例 res = runpf(mpc); % 默认牛顿-拉夫逊法 fprintf('收敛迭代次数: %d\n', res.iterations); for k = 1:9 fprintf('母线 %d: V = %.4f pu, theta = %.3f deg\n', ... k, res.bus(k, 8), res.bus(k, 9)); end逻辑说明:loadcase 把 case9.m 解析成 MATPOWER 内部的算例结构体 mpc,runpf 执行潮流计算,返回的结果结构体 res 里,bus 矩阵第 8 列是电压幅值,第 9 列是相角。iterations 字段记录实际牛顿迭代次数,正常 9 节点应该在 3 到 5 次之间。
参数说明:runpf 默认用的是牛顿法,如果系统规模变大或者想对比算法,可以传入第三个参数切换,比如 runpf(mpc, 'mpoption('PF_ALG', 2)') 切到快速解耦法。但做交叉验证时,我建议就用默认设置,因为默认参数最接近你自己写的那套标准牛顿法。
4.2 对拍三张表:电压幅值、相角、支路有功
交叉验证不是看一眼"差不多"就行,要落到三张表:母线电压幅值、母线相角、支路有功潮流。自己代码跑完,把结果写进一个数组,和 MATPOWER 的结果逐项做差,看最大偏差落在了哪条母线、哪条支路上。
# 自己代码的结果,V_mine 是 newtown_pf 返回的复数电压向量 V_mine, Q_mine = newton_pf(bus9, br9) # 从 MATPOWER res.bus 第8、9列抄回参考值 V_ref = np.array([1.040, 1.025, 1.025, 1.026, 0.996, 1.013, 1.026, 1.016, 1.032]) ang_ref = np.array([0.000, 9.280, 4.660, -2.220, -3.990, -3.690, 3.720, 0.730, -4.360]) err_v = np.max(np.abs(np.abs(V_mine) - V_ref)) err_a = np.max(np.abs(np.angle(V_mine, deg=True) - ang_ref)) print('最大电压幅值偏差(pu):', err_v) print('最大相角偏差(deg):', err_a)逻辑说明:这段代码只是把 MATPOWER 的参考值手动抄进数组,实际工作中更省事的做法是直接在 MATLAB 里把 res.bus 第 8、9 列导出成 CSV,再用 Python 读进来,避免手抄出错。对拍的标准是:电压幅值偏差小于 1e-6 pu,相角偏差小于 1e-4 度。如果偏差达到 1e-3 量级,说明你的程序里还有模型细节没对上,不能算通过。
参数说明:这里给的参考值是经典 case9 的典型收敛结果,不同 MATPOWER 版本之间可能有最后一位小数的差异,所以严格对拍时应以你自己机器上 runpf 的输出为准,而不是拿我抄的这组数当标准。支路有功的对拍同样重要,具体看 res.branch 表里的 PF、QF、PT、QT 四列,分别对应首端和末端的有功、无功。
4.3 6节点版本差异:为什么你的结果和论文对不上
6节点的交叉验证要更谨慎。如果你用 case6ww 跑完,发现和某篇论文附录里的结果差了 0.1%,先别急着怀疑程序和 MATPOWER,先检查三件事:一是负荷参数是否一致,二是两台调压变压器的变比是否标注为 1.0,三是线路充电电纳 B 是给了还是当成 0 处理。这三个变量任何一个不同,都会让母线电压产生可以测量的偏差。
行业里对 6 节点系统有一个共识:它从来不是一个由 IEEE 标准化委员会钦定的唯一基准,更多是靠教材和论文互相引用流传下来的。所以做算法对比时,我习惯把 9 节点当"锚点",6 节点当"辅助验证"。如果你的论文需要引用算例,建议同时给出 9 节点和 6 节点的结果,并注明数据来源是 MATPOWER 自带版本还是教材附录,省得审稿人拿另一套数据来质疑你。
提示:6节点数据里如果 tap 列不是 1.0,但你用普通线路模型去算,无功分布一定会出问题。这种偏差不会让程序报错,只能靠对拍发现,所以每次拿到新数据先打印一下支路表的前几行,确认变比列的实际值。
5. 潮流计算避坑:6节点与9节点最常见的5个翻车现场
5.1 节点编号从0开始,导致Ybus行错位
现象:程序不报错,但算出来的电压离谱,比如某条母线电压高达 1.3 pu,或者相角乱成一团。
原因:MATPOWER 和大多数论文数据里,母线编号从 1 开始。你从数据文件读到程序里,如果忘了减 1,支路表里的"1-4"就被解释成 bus[1] 到 bus[4],实际应该指向 bus[0] 到 bus[3]。索引整体错一位,Ybus 的拓扑关系全乱,但矩阵本身还是方阵,所以不会触发任何异常提示。
解决:数据定义阶段统一改成从 0 编号,像我第 3 章那样手写数据时就写 0-based。如果是读外部文件,构造支路表时强制减 1,并在组装 Ybus 后打印对角线验证。
# 排查技巧:打印Ybus对角线,检查母线i的自导纳是否远大于交叉导纳 Y = build_ybus(bus9, br9) print(np.round(np.diag(Y).real, 4)) # 对角线应包含多条支路的贡献5.2 平衡节点放最后,Jacobian奇异
现象:np.linalg.solve 报 Singular matrix,直接中断。
原因:平衡节点的相角和电压都是固定值,不参与迭代。组装 Jacobian 时,如果忘了把 slack 对应行和列剔除,矩阵会多出一个自由度,变成奇异矩阵。特别是在把 slack 放在母线列表末位的数据里,新手很容易因为"没有删除最后一列"而中招。
解决:用索引集合来管理状态量,而不是靠位置。第 3 章代码里 pvpq 和 pq 都是从类型列里动态提取的,slack 永远是 np.where(typ == 2) 找出来的那个,不管它在母线表里排第几都不会出问题。
5.3 变压器支路忽略变比k,无功怎么都对不上
现象:电压幅值和有功分布挺正常,但无功功率和参考值偏差很大,而且你调来调去都消不掉。
原因:6 节点系统里的调压变压器,在不同数据版本里变比可能是 0.98 或者 1.05,不是 1.0。你没处理 tap,Ybus 里就把变压器当成普通线路,漏掉了非标变比带来的无功修正。9 节点恰好三台变压器都是 1.0,所以这个坑在 9 节点上不暴露,一换 6 节点就炸。
解决:build_ybus 里保留 tap 参数,凡是 tap 不等于 1.0 的支路,首端自导纳除以 tap 的平方,互导纳除以 tap。注意 tap 的方向约定,我一般默认变比位于首端母线侧,如果数据文件用的是末端侧变比,要取倒数再填进去。
5.4 收敛判据设太粗,PQ分解法结果不达标
现象:tol 设成 1e-4,牛顿法正常收敛,但对拍时发现支路有功差了 0.3 MW 左右,怎么看都不像收敛到位。
原因:1e-4 的功率失配判据在标幺制下对应 0.01 MW 量级的偏差,经过多条支路累加后,线路潮流的误差会放大到可观测的水平。更重要的是,快速解耦法因为忽略了电阻的影响,本身存在模型近似误差,判据如果再放宽,偏差会进一步叠加。
解决:判据统一收紧到 1e-8 或者更小。牛顿法对这个规模的系统不差那几次迭代,判据严一点,结果才经得起和 MATPOWER 逐项对拍。如果用的是 PQ 分解法,先和牛顿法结果对拍一次,确认模型近似误差在可接受范围内,再谈收敛判据。
5.5 功率单位混用:MW/MVar没除以基准值
现象:所有节点的计算结果整体放大了 100 倍,发电机出力和线路潮流数值巨大,但电压和相角看起来还算正常。
原因:系统阻抗和电压是标幺值,但 Pg、Pd 这些功率还是 MW/MVar 的绝对值。把它们直接当成标幺功率塞进方程,等于把整个系统的注入功率放大了基准值的倍数。
解决:进入迭代前统一除以基准功率。
# 正确做法:功率必须转标幺 base = 100.0 P_pu = (bus[:, 1] - bus[:, 3]) / base Q_pu = (bus[:, 2] - bus[:, 4]) / base6. 进阶:PQ分解法提速,以及离线验证程序的三板斧
如果你的算例规模从 9 节点涨到几百节点,牛顿法每次迭代都要重新组装并分解 Jacobian,计算量会明显上来。这时候可以换成快速解耦法,也就是 PQ 分解法。它的核心假设是:高压输电网络里支路电阻远小于电抗,有功主要受相角影响,无功主要受电压幅值影响,于是把原来的四块 Jacobian 拆成两块独立的常数矩阵 B' 和 B''。B' 对应有功-相角,B'' 对应无功-电压,两个矩阵离线组装一次,迭代时反复用,每次求解规模减半。
# PQ分解法迭代核心,B1、B2 是离线组装好的常数矩阵 # 迭代中只做两次小规模求解,不再重新分解 for it in range(max_iter): V = Vm * np.exp(1j * Va) S = V * np.conj(Y @ V) dP = (Pspec - S.real) / Vm # 有功失配除以电压幅值 dQ = (Qspec - S.imag) / Vm # 无功失配除以电压幅值 Va[pvpq] += np.linalg.solve(B1, dP[pvpq]) Vm[pq] += np.linalg.solve(B2, dQ[pq])B' 的组装一般只取支路电抗的倒数,忽略电阻和充电电容;B'' 则直接取 Ybus 的虚部,再剔除 PV 母线和平衡母线的对应行列。换算法之后,别忘了用同一套 9 节点数据做一次对拍,PQ 分解法因为忽略了电阻,结果和牛顿法会有微小差别,知道差多少才不会误判程序故障。
验证程序正确性,我习惯用三板斧。第一板斧是标准算例对拍,9 节点和 6 节点都必须过,电压幅值偏差小于 1e-6 pu。第二板斧是功率平衡自检:全网发电减去全网负荷,再减去所有线路损耗,应该约等于零,这个检查能同时揪出支路功率计算和充电电容处理的问题。第三板斧是初值扰动测试,把平启动的 1.0 改成 0.95 和 1.05,重新跑一遍,结果必须一致,否则说明程序可能落到了一个错误的工作点。
我自己早期吃过大亏:拿 6 节点数据验证程序,支路无功一直差 0.3%,排查两天发现是变压器变比没处理。从那以后我养成了一个习惯,任何潮流程序先跑 9 节点,再跑 6 节点,两边对上才敢说收敛。这个习惯一直留到现在,希望帮到你。
本文还有配套的精品资源,点击获取