简介:面向电力系统研究与教学场景的Matlab潮流计算源码包,聚焦IEEE 33节点与69节点配电网的稳态分析,适合电气工程专业学生、电网规划人员及需要验证配网算法的开发者使用。包内共5个文件,包含4个M脚本(如IEEE33Bus.m、IEEE69Bus.m、forwardSweep.m等)用于实现不同规模网络的潮流求解,另有1份PDF说明文档辅助理解程序结构与参数设置,整体仅341KB,轻量易部署。目前已有459人学习下载。通过该资源可掌握基于牛顿-拉夫逊法及前推回代法的Load Flow计算流程,理解节点电压、线路功率等关键输出量的生成逻辑,并能直接修改节点数据或负荷参数以适配自定义配网拓扑,是开展分布式电源接入影响分析、网络损耗评估及算法对比研究的实用工具。
1. IEEE 33节点潮流计算是什么:一套小系统、一堆真问题
把 IEEE 33 节点潮流计算跑通,是不少配电网方向从业者入门时的第一道坎。系统只有 33 个节点、32 条运行支路,看起来比输电网的几百节点简单太多,但真上手你会发现:拿牛顿法直接套,大概率在某个节点电压爆掉;换成前推回代法,又要先搞清楚支路遍历顺序和基准值换算。这套标准算例是配电网潮流计算、分布式电源接入分析、算法对比验证的通用基准,几乎每个做配网规划或光伏并网评估的项目都会拿它当试验场。适合正在做算法验证的开发者、写论文需要基准算例的研究生,以及想快速验证 DG 接入影响的工程人员。这篇笔记按我踩过的路走一遍,从原理到代码,再到翻车现场。
2. 为什么配电网潮流不用牛顿法硬扛:从网络结构到前推回代法的原理取舍
2.1 辐射状结构与支路编号暗含的求解顺序
IEEE 33 节点配电系统的网络结构是典型的辐射状树形电网,一条根节点母线通过 32 条支路向末端供电,另有 5 条联络开关支路在正常状态下处于开断位置。这种结构决定了潮流方向是唯一的:从根节点出发,沿着树杈一路流向末端节点。输电网常见的环网结构在这里几乎不存在,即便合上联络开关形成环,也是极少数工况。辐射状结构带来的直接好处是每条支路的功率方向可以预先确定,不需要像输电网那样反复试探角度。
很多初学者拿到算例数据后,误以为支路表的编号顺序就是计算顺序,从支路 1 一条条推下去。这种做法在部分数据文件里恰好能跑出近似结果,但一旦支路表顺序调整、联络开关状态变化,结果就开始飘。正确做法是先按拓扑关系把父子节点关系建立起来,再决定遍历顺序。我一般用 BFS 从根节点展开,生成一棵以根为原点的树,之后所有计算都按这棵树的层次进行,跟支路表的物理顺序无关。这个习惯能省掉后续加 DG、改拓扑时的一大堆返工。
前推回代法之所以适合这种结构,是因为它不需要形成雅可比矩阵,不需要解线性方程组,只需要两轮遍历。回代从末端节点向根节点累加功率,前推从根节点向末端修正电压,交替迭代直到收敛。这条思路只认辐射状拓扑,恰好和 IEEE 33 节点系统天然匹配。
2.2 功率方程与前推回代的两轮计算
配电网支路电压降落的核心关系可以写成近似式 ΔU ≈ (P·R + Q·X) / U,其中 P 和 Q 是该支路流过的有功和无功,R 和 X 是支路阻抗,U 是首端电压。这个式子是前推回代法的基础:已知上游电压和支路功率,就能算出下游电压。配电网支路 R/X 比值通常较高,不像输电线路那样电感占主导,所以有功功率对电压的影响必须认真对待,忽略 R 的简化在这里会带来明显误差。
前推回代法的思路分两轮。回代轮:从最末端节点开始,把每个节点下游所有节点的注入功率累加起来,算出每条支路从父节点流向子节点的功率,其中注入功率是发电与负荷的代数和,负荷取负值。前推轮:从根节点开始,利用已知的首端电压和刚算出的支路功率,逐条支路往下推,算出每个子节点的电压幅值和相角。两轮交替执行,每一轮用上一轮算出的电压修正功率损耗,再用新的功率修正电压,直到前后两次电压差小于收敛阈值。
实现时需要注意符号约定。我习惯把节点注入功率定义为发电机出力减去负荷,正号表示向电网注入功率;把支路功率定义为从父节点流向子节点的方向为正。这样功率平衡式可以统一写成:父支路流入节点的功率等于所有子支路流出功率之和减去节点注入功率。符号统一之后,代码不容易写乱,排查问题时也只需盯着一个方向检查。
2.3 牛顿法在配电网发散的机理与选择判据
牛顿法在 IEEE 33 节点系统上并不是不能跑,但很挑初值。配电网的高 R/X 比会让雅可比矩阵条件数变差,初值给得不好,迭代几步电压就飞到天上去。另一个问题是配电网节点几乎全是 PQ 节点,电压幅值初值取 1.0、相角取 0 对输电网够用,但对配电系统来说,末端电压可能低到 0.90 左右,初值和真实解偏离太远,牛顿法的收敛域覆盖不住。
前推回代法没有这些烦恼。它不构造雅可比矩阵,不存在矩阵奇异的问题,存储开销也小。对 33 节点这种规模,一次迭代就是两次线性遍历,速度远快于牛顿法。我的选择判据很简单:网络是辐射状或弱环状、节点以 PQ 节点为主、R/X 比值偏大时,默认用前推回代法;只有当网络存在多个独立环网,或者需要计算电压稳定鞍结分岔这类强非线性问题时,才考虑牛顿法及其变体。
两者对比如下:前推回代法实现简单、占内存小、辐射状网络收敛稳定,但无法直接处理环网和 PV 节点;牛顿法通用性强、收敛速度快,在配电网高 R/X 比场景下初值敏感、需要额外处理矩阵病态问题。对 IEEE 33 节点这个算例,前推回代法是性价比最高的选择。
3. 建模先建模数据:IEEE 33 节点系统的参数表与基准值换算
3.1 从标准算例到程序输入:先把支路表和数据表定下来
IEEE 33 节点系统的原始数据一般分成两张表:支路参数表和节点负荷表。支路表给出每条支路的首端节点、末端节点、电阻、电抗、开关状态;节点负荷表给出每个节点的有功负荷和无功负荷。拿到数据后的第一件事不是写代码,而是把两张表整理成程序可以直接读取的格式。
我一般用 CSV 保存支路数据,字段固定为 from_node、to_node、R_ohm、X_ohm、status,其中 status 为 1 表示支路运行,0 表示联络开关处于断开状态。节点负荷表字段为 node、P_kW、Q_kVar。根节点即节点 1 作为平衡节点,不参与负荷累加,但要在程序里单独指定电压幅值和相角。这里有个易错点:IEEE 33 节点系统有多个数据版本,个别支路阻抗在文献中存在微小差异,以你拿到的那份算例文档为准,不要凭记忆硬填。
支路表前几行的典型参考值如下,实际使用时请对照原文校对。
| 首端节点 | 末端节点 | 电阻 R / Ω | 电抗 X / Ω | 状态 |
|---|---|---|---|---|
| 1 | 2 | 0.0922 | 0.0470 | 1 |
| 2 | 3 | 0.4930 | 0.2511 | 1 |
| 3 | 4 | 0.3660 | 0.1864 | 1 |
| 4 | 5 | 0.3811 | 0.1941 | 1 |
| 5 | 6 | 0.8190 | 0.7070 | 1 |
3.2 标幺制换算:12.66kV 和 10MVA 的基准值不是随便选的
IEEE 33 节点系统常用的基准值是:基准电压 12.66kV,基准功率 10MVA。这两个数决定了整个计算的量纲。电阻和电抗必须从欧姆换算成标幺值,负荷也要从千瓦、千乏换算成标幺值,否则程序里数值大小悬殊,迭代收敛极慢甚至发散。
换算公式很直接:阻抗基准值 Z_base = U_base² / S_base,电流基准值 I_base = S_base / (√3 · U_base)。代入数值,Z_base = 12.66² / 10 = 16.02 Ω。把实际欧姆值除以 16.02 就得到标幺阻抗。负荷标幺值则是把千瓦和千乏分别除以 10MVA,比如 200kW 的负荷在标幺制下就是 0.02。
我一般会在程序里写一个换算函数,而不是手动计算:
def ohm_to_pu(r_ohm, x_ohm, u_base_kv=12.66, s_base_mva=10.0): z_base = (u_base_kv ** 2) / s_base_mva # 阻抗基准值,单位欧姆 r_pu = r_ohm / z_base x_pu = x_ohm / z_base return r_pu, x_pu这个函数把欧姆值和基准电压、基准功率作为输入,返回标幺电阻和标幺电抗。参数u_base_kv和s_base_mva是换算的核心,如果换用其他配电网算例(比如 69 节点系统),只需要改这两个值。调用时传入支路表的 R 和 X,返回结果直接作为计算的输入数据。换算不到位的情况下,电压幅值看起来总在 0.99 附近,末端电压和根节点电压差异被淹没,根本看不出哪个节点最薄弱。
3.3 数据文件怎么组织:从节点表到支路表的字段设计
数据文件组织得好不好,直接影响调试效率。我习惯把支路参数、负荷参数、基准值分别放在三个文件里:支路表 CSV、负荷表 CSV、基准值常量写在主程序顶部。这样换算、改拓扑、调整负荷都不用改动计算核心代码,只需要替换数据文件。
支路表设计六个字段就够:from_node、to_node、R_ohm、X_ohm、status、备注。status 字段必须保留,因为联络开关状态切换是配电网潮流分析里最常见的操作之一。负荷表则用 node、P_kW、Q_kVar 三个字段。节点编号这里要特别说明:IEEE 33 节点算例的节点编号从 1 开始,根节点是 1 号,但很多编程语言的数组下标从 0 开始。我的做法是数组直接按下标 1 到 33 存储,下标 0 留空不用,这样节点编号和数组索引一一对应,省去每次减 1 的麻烦,也避免多处转换带来的笔误。
读取数据时用标准 CSV 库解析,逐行转成内部数据结构。步骤是:读支路表,把每条支路存成元组;读负荷表,把负荷存成字典;调用 ohm_to_pu 函数把阻抗全部转成标幺值;给根节点单独设置 V_root = 1.0∠0。这一步做完,数据准备工作就结束了,接下来可以直接进入算法实现。
4. 用前推回代法把 IEEE 33 节点跑通:Python 实现与参数微调
4.1 拓扑解析:从支路表生成前推回代的遍历顺序
前推回代法的第一步是生成树的遍历顺序。常见做法是从根节点出发做 BFS,先建立邻接表,再逐层展开子节点。这里要注意,支路表是单向的,但拓扑关系是双向的,建邻接表时必须把首端和末端都加进去,否则 BFS 走到一半就断了。
下面这段代码把支路表转换成父子关系:
def build_tree(root, edges): # edges: [(from, to, r, x, status)],节点编号从1开始 n = max(max(f, t) for f, t, _, _, _ in edges) + 1 adj = [[] for _ in range(n)] for f, t, _, _, s in edges: if s == 0: # 联络开关断开,不参与拓扑 continue adj[f].append(t) adj[t].append(f) # 无向图,确保BFS能往回走 parent = [-1] * n children = [[] for _ in range(n)] order = [root] # 根到叶的遍历顺序 parent[root] = root for node in order: # 遍历过程中不断追加新节点 for nb in adj[node]: if parent[nb] != -1: continue parent[nb] = node children[node].append(nb) order.append(nb) return order, parent, childrenbuild_tree 函数返回三个对象:order 是从根到叶的节点顺序,parent 记录每个节点的父节点,children 记录每个节点的子节点列表。代码里的 status 判空很关键,如果联络开关状态为 0 还把它加进邻接表,拓扑会变成闭环,前推回代法直接失效。BFS 展开顺序决定了对父节点数组的访问方式,但不需要每层严格排序,只要保证父节点在子节点之前访问即可。
4.2 迭代核心:回代算功率、前推算电压的原理映射
回代和前推是算法的两个核心轮次。回代从叶子节点往根节点走,不依赖支路表的物理顺序,而是依赖 children 关系。前推从根节点往叶子走,用父节点电压和支路功率修正子节点电压。这里的符号约定至关重要:S_br[i] 表示从父节点流入节点 i 的支路复功率,方向为父到子。
回代前推的核心代码如下:
def backward_forward(order, parent, children, S_node, Z_pu, V_root): n = len(parent) S_br = [0j] * n # 支路复功率,下标为子节点编号 # 回代:从叶到根,累加下游功率 for i in reversed(order): if i == root: continue s_total = 0j for c in children[i]: s_total += S_br[c] S_br[i] = s_total - S_node[i] # 前推:从根到叶,修正电压 V_new = [0j] * n V_new[root] = V_root for i in order: if i == root: continue p = parent[i] # 压降公式:V_child = V_parent - conj(S_br / V_parent) * Z V_new[i] = V_new[p] - (S_br[i] / V_new[p].conjugate()) * Z_pu[i] return S_br, V_new回代部分里,reversed(order) 保证每个节点计算时它的所有子节点已经处理完毕。S_br[c] 是父节点流向子节点 c 的功率,对所有子节点求和,再减去节点自身注入功率 S_node[i],就得到父节点流向节点 i 的功率。这个公式是从节点功率平衡推导出来的,不是拍脑袋写的。前推部分用的压降公式是复数形式的精确式,复数用电流和阻抗直接算压降,比有功无功分离的近似式更稳。Z_pu[i] 是父节点到节点 i 这条支路的标幺阻抗,下标是子节点编号。
前推公式里 V_new[p].conjugate() 是父节点电压的共轭。这一步把支路复功率和电压关联起来,如果直接除以 V_new[p] 而不是共轭,相角会算错,电压幅值虽然变化不大,但后续计算 DG 接入场景时相角误差会放大。
4.3 收敛判据、松弛因子与结果校验
有了回代和前推,主循环就是交替执行,直到电压变化小于阈值。我通常把收敛阈值设为 1e-6 标幺值,对应 12.66kV 系统约 0.013V 的精度,足够工程使用。迭代次数上限设 100 次,正常情况下 10 次以内收敛,如果超过 50 次还没收敛就要停下来查数据。
主循环代码如下:
V = [complex(1, 0)] * (len(parent)) history = [] tol = 1e-6 max_iter = 100 for it in range(max_iter): V_old = V[:] S_br, V_new = backward_forward(order, parent, children, S_node, Z_pu, V_root) diff = max(abs(V_new[k] - V_old[k]) for k in range(len(V_new))) V = V_new history.append(diff) if diff < tol: break if diff >= tol: print("Warning: 未收敛,最大电压偏差 %.2e p.u." % diff)收敛判据取所有节点电压幅值和相角综合偏差的最大值。这里用复数模差比单独比较幅值更严格,因为相角偏差也是判断收敛的重要指标,尤其在分布式电源接入场景下。迭代历史 history 后面会用来画残差曲线,这是判断算法是否健康的关键依据。
主循环跑完后要做三项校验:第一,根节点注入功率是否大于全网负荷总和;第二,全网功率平衡是否满足,即根节点注入功率减去所有节点负荷等于全网网损;第三,最低电压节点位置是否在末端区域。如果这三项里任意一项不合理,先回去查数据,不要急着分析结果。网损计算可以用支路电流平方乘以阻抗来算,也可以直接做平衡校验。我习惯两者都做,交叉验证一下结果的一致性。
5. IEEE 33节点潮流计算避坑记录:五个方向性的错误与排查思路
5.1 现象:迭代不收敛或电压爆掉
前推回代法在配电网辐射状结构上收敛很稳,一旦不收敛,八九成是数据单位出了问题。典型表现是迭代几次后电压幅值超过 2.0 或者跌到负数,甚至直接变成 NaN。最常见原因是电阻电抗没有换算成标幺值、负荷功率没有除以基准功率,或者两者混用,导致支路功率和阻抗在数值上差了好几个数量级。
原因分析:标幺制下支路阻抗一般在 0.001 到 0.1 之间,负荷功率在 0.001 到 0.2 之间。如果有人把欧姆值直接当成标幺值用,电阻算出来可能是 0.09 而不是 0.005,数值偏大十几倍,压降公式自然会算出离谱的电压。解决方法是回到 ohm_to_pu 函数核对所有换算结果,逐个支路打印 R 和 X 的标幺值,确认数量级合理再跑迭代。
排查时我一般先打印根节点直接相连的那条支路的标幺阻抗,比如节点 1 到 2 的支路标幺电阻大约在 0.006 量级,如果看到 0.1 以上,基本就是换算漏了。
5.2 现象:结果与教材数值对不上
代码跑通了,收敛也正常,但算出来的节点电压和参考资料上的结果有系统性偏差,比如整体电压偏高 0.01 到 0.02。这时候问题往往不出在算法,而出在根节点的初始条件或负荷符号定义。
原因分析:IEEE 33 节点算例里节点 1 的电压设定为 1.0∠0,但如果程序中根节点电压设置成了别的值,末端电压会整体平移。另一个常见原因是负荷符号写反,把负荷功率当成了注入功率,导致回代时支路功率方向反了,末端电压比根节点还高。解决方法是先校验根节点电压设置,再检查 S_node 数组的符号约定:负荷为正还是为负,必须和回代公式匹配。
我一般会在程序里加一段断言,判断末端节点电压是否低于根节点,如果连续多个末端节点电压高于 1.0,直接报出负荷符号错误。这种检查能省下大量对数据的时间。
5.3 现象:并联电容器接入后结果异常
配电网仿真经常要加并联电容器来改善电压,很多人直接把电容器当成负的负荷,填入固定的无功功率值,结果算出来电压一点没变,或者变化方向不对。
原因分析:电容器在潮流计算里本质是恒阻抗元件,其无功出力随节点电压变化而变化,不是恒定的无功负荷。如果把它当成恒功率无功源处理,电压升高时实际无功出力会变大,电压降低时出力变小,恒功率假设在这个场景下是错的。解决方法是把电容器按恒阻抗模型建模,在每次迭代时根据当前电压修正无功注入,或者直接用 ZIP 负荷模型,将有功无功都拆成恒阻抗、恒电流、恒功率三部分。
修改方式不复杂:在主循环回代之前,遍历所有电容器节点,根据当前电压幅值重新计算节点注入无功,更新 S_node 数组。改完之后再看电压分布,结果就会跟实际物理过程一致。
5.4 现象:分布式电源接入导致迭代振荡
加入光伏或风机后,很多人把 DG 节点按 PV 节点处理,要求电压幅值恒定,结果前推回代法开始振荡,电压在某个值附近来回跳。
原因分析:前推回代法本身不直接支持 PV 节点约束,因为回代公式里的注入无功是固定值,PV 节点的无功需要根据电压偏差动态调整。处理 PV 节点的常见做法是无功修正法,即每轮迭代后根据电压偏差修正无功注入量。
def pv_node_update(V, q_pv, v_target, node_list, k=20): # V: 当前电压复数数组 # q_pv: PV节点当前无功注入 # v_target: 目标电压幅值 for idx in node_list: v_mag = abs(V[idx]) q_pv[idx] += k * (v_target - v_mag) return q_pv参数 k 是无功修正系数,一般取 10 到 30。k 太小收敛慢,k 太大会在目标电压附近振荡。实际调试时先从 10 开始,观察电压幅值的收敛轨迹,如果出现等幅振荡就调小,如果收敛太慢就调大。还要注意 PV 节点无功不能无限增加,超过无功上限时要转成 PQ 节点处理,否则算出的结果没有工程意义。
5.5 现象:程序在支路附近出现死循环
BFS 建树时如果邻接表构建错误,程序会陷入死循环或者漏掉部分节点。典型表现是运行几分钟不结束,或者打印出来的遍历顺序长度不等于节点数。
原因分析:最常见的错误是建邻接表时只加了支路首端到末端的方向,没加反向边,BFS 从根节点出发走几步就卡住,后续节点全部无法访问。还有一种情况是联络开关状态判断写错,把断开的支路也加进了邻接表,导致拓扑从树变成环,BFS 虽然不会死循环,但遍历顺序里会出现重复访问。
解决方法很直接:建树完成后打印 order 长度和所有节点编号,确认每个节点都被访问到。如果少了节点,检查那条支路是否只加了单向边。if status == 0 的判断我一般单独抽出来写,不跟其他条件混在一起,方便排查时一眼看到开关状态的处理逻辑。
6. 从 IEEE 33节点扩展到69节点:验证收敛性与精度的一点点经验
6.1 33节点和69节点的差异与需要改的参数
IEEE 69 节点系统比 33 节点多了一层辐射深度,支路数和负荷数量都翻了一倍以上,但前推回代法的核心代码完全不用动,只需要换数据文件。69 节点算例的基准电压多数版本和 33 节点一致,也是 12.66kV,但拿到数据后先确认基准值,不要假设两者完全相同。迭代次数可能会从 5 次增加到 10 次左右,把 max_iter 从 100 改到 200 即可,收敛阈值 1e-6 保持不变。
6.2 收敛轨迹校验:用残差曲线判断算法是否健康
每次迭代都会记录一个最大电压偏差,把这组数据画成残差曲线是判断算法健康度的好办法。健康的曲线应该平滑下降,没有拐点,没有振荡。如果曲线在中途出现平台或者突然反弹,多半是 PV 节点的无功修正系数不合适,或者某个节点的负荷数据有问题。我一般每跑一个新算例都先看残差曲线,确认收敛形态正常再看电压结果,能省掉不少排查时间。
6.3 一个实用技巧:把迭代过程可视化,哪里不收敛一眼看出
除了残差曲线,我还会把每轮迭代的节点电压画出来对比。重点关注末端节点和 DG 接入节点,这两个位置的电压变化最能反映算法状态。如果末端节点电压在迭代中先升后降再升,说明支路功率回流或者负荷符号有误;如果 DG 节点电压始终达不到目标值,说明无功修正系数偏小。这个习惯是我在某公司的配网仿真项目里踩过几次坑后养成的,价值很高。
这套方向值不值得投入,我的看法是:如果你做配电网规划、DG 接入评估或算法对比验证,把 IEEE 33 节点潮流计算吃透是最短路径,它涵盖了辐射状建模、标幺换算、拓扑遍历、迭代收敛控制这些配网仿真的核心能力,扩展到 69 节点甚至更大系统只是数据量的变化,不是方法路线的变化。希望帮到你。
本文还有配套的精品资源,点击获取