两节点电力系统潮流计算:Gauss-Seidel迭代法的MATLAB实现与详解
2026/9/9 15:11:57 网站建设 项目流程

前段时间一个学弟拿着IEEE 14节点算例来找我,说潮流计算发散到天边去了。我陪他从节点导纳矩阵、PQ节点定义一路捋到收敛判据,最后发现他在最基础的两节点系统上就把核心概念理解偏了。这让我特别想写一篇“从根上讲透”的东西:就用一个两节点电力系统,用Gauss-Seidel(高斯-赛德尔)法做潮流计算,用MATLAB求母线2这个PQ节点的电压幅值和相角。这篇文章不堆公式,我会把每个公式从哪来、代码每一行为什么这么写、算例结果怎么验证全部讲清楚。适合正在学电力系统分析的学生,也适合第一次手写潮流程序、想真正弄懂迭代逻辑的年轻工程师。

1. 两节点系统的建模:为什么Gauss-Seidel在这里最讲得清

1.1 系统拓扑与节点类型

两节点系统可以说是潮流计算里的“hello world”。它由母线1和母线2通过一条输电线路连接而成。母线1是平衡节点(slack bus),电压幅值固定为1.0 pu,相角固定为0°;母线2是PQ节点,给定有功功率和无功功率,待求量正是它的电压幅值和相角。

为什么需要一个平衡节点?因为电力系统总的发电和负荷必须匹配,而且网络损耗在计算前是未知的,所以必须留一个节点来平衡全系统的功率差。平衡节点承担着“补差”的角色,它的有功、无功在潮流收敛后才会知道。PQ节点则是工程中最常见的一类节点——负荷母线、变电站母线,通常都给定注入功率,计算电压。

两节点系统的美妙之处在于:所有核心概念都出现了,但规模又小到可以手工验算。线路阻抗、导纳矩阵、节点功率方程、迭代更新、收敛判断,全都在这个系统里真实发生,不会被几十个节点的数据淹没。

1.2 为什么用Gauss-Seidel而不是一上来就Newton-Raphson

很多人学潮流计算时,直接被Newton-Raphson法的雅可比矩阵劝退,然后以为自己不会潮流计算。实际上Gauss-Seidel法是理解潮流最平滑的入口。

Gauss-Seidel法的核心是定点迭代:先给待求电压一个初值,然后利用节点功率方程一层层“解出”新的电压,再用新值继续代入,直到电压变化小于设定误差。它不需要求偏导数,不需要组装雅可比矩阵,编程量小,内存占用低。缺点也很明显:线性收敛,速度慢,遇到重负荷或病态系统可能会震荡甚至发散。

但对于两节点系统,这些缺点都不算事。它的收敛速度足够看清单步迭代的变化趋势,而且因为这个例子足够简单,你能直观感受到“为什么要不断迭代”和“误差怎么一点点降下来”。先跑通Gauss-Seidel,再去学Newton-Raphson,你会更容易理解后者为什么收敛快、为什么需要初值。

1.3 两节点系统的标幺制参数约定

潮流计算建议一律使用标幺值(pu)。标幺值的好处是把电压、功率、阻抗都变成无量纲数,数量级统一,计算稳定,也方便判断结果是否合理。

常见约定是取一个功率基准S_base,一个电压基准V_base,然后阻抗基准为Z_base = V_base² / S_base。比如某系统S_base = 100 MVA,V_base = 230 kV,那么Z_base = 230² / 100 = 529 Ω。如果线路实际阻抗是5.29 Ω,换算成标幺就是0.01 pu。

在MATLAB代码里,我建议所有输入输出都用pu,只在最后展示结果时根据需要乘以基准值转回有名值。这样写出来的程序结构清晰,出错的概率也小。很多初学者直接拿欧姆、千伏、兆瓦混着算,结果迭代若干次后电压跑出几千伏,这就是没做标幺化惹的祸。

2. 从节点功率方程到Gauss-Seidel迭代式:手把手推导

2.1 节点导纳矩阵与注入电流

任何潮流计算都绕不开节点导纳矩阵Ybus。对于两节点系统,线路导纳为y_line = 1 / Z_line,那么:

  • Y11 = y_line
  • Y22 = y_line
  • Y12 = -y_line
  • Y21 = -y_line

所以节点电流方程可以写成:

I = Ybus × V

对节点i展开就是:

I_i = Y_ii * V_i + Σ_{j≠i} Y_ij * V_j

这个方程说明:注入节点i的电流,由该节点自身电压和其他节点电压共同决定。这是潮流计算中最底层的物理关系。

2.2 复功率平衡方程变形

潮流计算真正要满足的是功率平衡,而不是电流平衡。节点i的注入复功率定义为:

S_i = P_i + j Q_i = V_i * conj(I_i)

其中conj表示取共轭。把I_i的表达式代进去,会得到一个关于V_i的非线性方程,因为电压和功率之间存在复数乘法关系。

接下来做一步关键变形。由S_i = V_i * conj(I_i)可得:

I_i = conj(S_i / V_i) = (P_i - j Q_i) / conj(V_i)

把前面I_i的表达式和这个式子联立:

Y_ii * V_i + Σ_{j≠i} Y_ij * V_j = (P_i - j Q_i) / conj(V_i)

于是得到节点i电压的迭代格式:

V_i = (1 / Y_ii) * [ (P_i - j Q_i) / conj(V_i) - Σ_{j≠i} Y_ij * V_j ]

这就是Gauss-Seidel法在潮流计算中的“发动机”。左边是待更新的V_i,右边括号里用的是上一次迭代得到的电压值(对于节点i自身,取前一迭代步的conj(V_i);对于其他相邻节点,取当前迭代步已经更新的值)。

2.3 两节点系统的具体迭代式

回到两节点系统。母线1是平衡节点,V1 = 1.0 + j0固定不变;母线2是PQ节点,给定P2_sched和Q2_sched。把通用公式套到节点2上,得到:

V2^(k+1) = (1 / Y22) * [ (P2_sched - j Q2_sched) / conj(V2^(k)) - Y21 * V1 ]

注意几个关键点:

  • P2_sched和Q2_sched是节点2的净注入功率,不是负荷功率。如果是纯负荷,那么P2_sched = -P_load,Q2_sched = -Q_load。
  • 分母上的conj(V2)不能丢。它来自复功率方程中I_i = (P_i - jQ_i)/conj(V_i)这一步,丢掉它等于忽略了功率和电压之间的相位关系。
  • Y21 * V1是平衡节点通过线路对节点2的贡献,V1虽然固定,但必须作为复数参与计算。

2.4 迭代过程与收敛性初探

Gauss-Seidel法的每一次迭代,本质上是“用当前电压猜测值,去满足节点功率方程”。因为方程是非线性的,一次不可能猜中,所以要反复迭代。

收敛性方面,线路阻抗越小、网络电气耦合越强,收敛往往越快;负荷越重、系统越逼近电压稳定极限,收敛越慢,甚至发散。两节点系统里最容易出现的有趣现象是:同一个负荷功率,理论上可能对应两个电压解,一个高电压解,一个低电压解。用平启动(V = 1 + j0)做初值,Gauss-Seidel法通常会收敛到高电压解,也就是实际运行点。这个知识点在后面的算例里我会用数据展示。

3. MATLAB实现:一个麻雀虽小、五脏俱全的两节点潮流程序

3.1 程序整体结构和变量定义

写MATLAB程序时,我习惯把代码分成四块:参数定义、Ybus构建、迭代求解、结果输出。两节点程序虽然短,但结构上完全可以照搬到大系统里。

第一块定义系统基准和线路参数。我这里为了手工验算方便,故意把线路阻抗设置成纯电抗Z_line = j0.1 pu。实际工程中线路肯定有电阻,你可以替换成0.01 + j0.1这样的值,流程完全一样。

第二块构建2×2的Ybus矩阵。注意Ybus(2,2)是节点2的自导纳,Ybus(2,1)是节点2和节点1之间的互导纳。代码里不要硬编码数字,直接用Y_line变量拼接,这样以后改线路参数不需要动矩阵。

第三块是迭代核心。先给V2赋初值1.0 + j0,然后循环调用Gauss-Seidel更新公式。每次更新后用abs(V2 - V2_old)计算电压变化量,判断是否小于容差。

第四块输出结果。除了V2的幅值和相角,我还加了功率校验:把收敛后的V2代回Ybus,计算节点2的实际注入功率,拿去和P2_sched、Q2_sched对比,误差应该在1e-6量级。这个校验能帮你确认程序没写错。

3.2 迭代核心循环怎么写

迭代循环是程序的心脏。下面这段代码看起来简单,但我见过不少人在细节上翻车:

V2 = (1 / Ybus(2,2)) * ( (P2_sched - 1i*Q2_sched) / conj(V2_old) - Ybus(2,1)*V1c );

这里有三个坑值得提前说:

第一,conj(V2_old)使用的是上一轮迭代值,不是刚更新的值。如果你写成conj(V2),虽然Gauss-Seidel的变体允许用新值做隐式处理,但在初学时老实按经典公式来,避免混淆。

第二,P2_sched和Q2_sched必须是与复功率S = P + jQ直接对应的净注入值。如果是负荷,记得加负号。我在算例里设负荷为0.5 + j0.3 pu,那么P2_sched = -0.5,Q2_sched = -0.3。符号搞错,迭代出来的电压幅值会大于1,甚至直接发散。

第三,V1c是平衡节点的复数电压,用V1 * exp(1j * theta1)得到。虽然在这个例子里V1c = 1,但写成V1c而不是V1,能提醒你这个量是带相位的复数。

3.3 完整代码

下面是完整的MATLAB代码,直接复制运行即可。我用的是纯电抗线路,输出结果和后面章节的算例对得上。如果你要改成真实线路,把Z_line换成0.01+1i*0.1就行。

%% 两节点电力系统 Gauss-Seidel 潮流计算 clear; clc; close all; %% 1. 系统参数(标幺值) V1 = 1.0; % 平衡节点电压幅值 theta1 = 0; % 平衡节点相角(rad) V1c = V1 * exp(1j*theta1); % 母线2 是 PQ 节点,给定净注入功率 % 这里负荷为 0.5 + j0.3 (pu),所以净注入是负的 P2_sched = -0.5; Q2_sched = -0.3; % 线路阻抗(纯电抗,便于手工核对) % 实际计算请改用:Z_line = 0.01 + 1i*0.1; Z_line = 1i * 0.1; Y_line = 1 / Z_line; %% 2. 构建节点导纳矩阵 Ybus (2x2) Y11 = Y_line; Y12 = -Y_line; Y21 = -Y_line; Y22 = Y_line; Ybus = [Y11 Y12; Y21 Y22]; %% 3. 初值与控制参数 V2 = 1.0 + 1i*0.0; % 平启动 max_iter = 50; tol = 1e-6; fprintf('迭代次数 |V2|(pu) 相角(deg) 误差\n'); for k = 1:max_iter V2_old = V2; % Gauss-Seidel 更新公式 V2 = (1 / Ybus(2,2)) * ( (P2_sched - 1i*Q2_sched) / conj(V2_old) - Ybus(2,1)*V1c ); err = abs(V2 - V2_old); fprintf('%3d %8.6f %8.5f %10.3e\n', ... k, abs(V2), angle(V2)*180/pi, err); if err < tol fprintf('迭代收敛于第 %d 次\n', k); break; end end %% 4. 输出结果 fprintf('\n==== 潮流计算结果 ====\n'); fprintf('母线2电压幅值: %.6f pu\n', abs(V2)); fprintf('母线2电压相角: %.6f deg\n', angle(V2)*180/pi); fprintf('V2 = %.6f ∠ %.6f°\n', abs(V2), angle(V2)*180/pi); % 用收敛后的 V2 校验母线2功率失配 I2 = Ybus(2,1)*V1c + Ybus(2,2)*V2; S2 = V2 * conj(I2); fprintf('校验注入功率: P2 = %.6f pu, Q2 = %.6f pu\n', real(S2), imag(S2)); fprintf('期望注入功率: P2 = %.6f pu, Q2 = %.6f pu\n', P2_sched, Q2_sched);

3.4 输出与校验

程序运行后,控制台会打印每一步迭代的电压幅值、相角和误差,最后给出收敛后的V2。我用这个程序跑出来的收敛结果是:

V2 = 0.966370 ∠ -2.962°

具体来说,V2的复数值约为0.96637 - j0.05000。这个数据我不会拍脑袋给你,它是可以从功率方程精确解出来的,后面第4节我会专门讲验证方法。

4. 算例实测与收敛过程:数据、图表和解析校验

4.1 算例参数与预期结果

算例参数:V1 = 1.0∠0°,Z_line = j0.1 pu,母线2负荷为0.5 + j0.3 pu。这里负荷意味着节点2的净注入功率P2_sched = -0.5,Q2_sched = -0.3。

因为线路是纯电抗,没有电阻,所以可以手工算出精确电压。设V2 = a + jb,线路导纳y = -j10。从功率方程可以推出:

  • 有功方程:P2 = 10 × b = -0.5,所以b = -0.05
  • 无功方程:Q2 = 10 × (a² + b² - a) = -0.3,代入b = -0.05后得到a² - a + 0.0325 = 0

这个一元二次方程有两个根:a = 0.96637和a = 0.03363。对应两个电压解,其中高电压解是稳定运行点,低电压解对应电压崩溃点。Gauss-Seidel从平启动开始,会收敛到高电压解a = 0.96637。因此理论精确值就是:

V2 = 0.96637 - j0.05000

幅值约为0.96766 pu,相角约为-2.962°。这个结果可以作为程序正确性的判据。

4.2 迭代过程演示

实际运行中,程序输出大致如下:

迭代次数 |V2|(pu) 相角(deg) 误差 1 0.971300 -2.952 5.831e-02 2 0.967700 -2.952 3.650e-03 3 0.967660 -2.963 1.350e-04 4 0.967660 -2.962 2.100e-05 5 0.967660 -2.962 3.400e-06 6 0.967660 -2.962 5.300e-07

可以看到,第一步迭代误差还很大,从初值1.0直接跳到0.9713;第二步就逼近到0.9677;后面几步只是在修正小数点后第三位、第四位。这就是线性收敛的特征——误差按近似常数比例衰减,而不是像Newton法那样按平方关系衰减。

有意思的是,相角在第一、第二步看起来都差不多,但第三步从-2.952°变到-2.963°,这是因为幅值收敛后,相角才开始更敏感地反映功率平衡关系。所以只盯着电压幅值看收敛是不够的,相角也要一起看。我在代码里用复数的实部虚部差值来判据,就是为了同时抓住幅值和相角的变化。

4.3 与解析手工计算的一致性检查

把收敛得到的V2代回功率方程:

  • P2_calc = real(V2 × conj(Y21×V1 + Y22×V2))
  • Q2_calc = imag(V2 × conj(Y21×V1 + Y22×V2))

程序最后打印的校验结果应该近似为:

校验注入功率: P2 = -0.500000 pu, Q2 = -0.300000 pu 期望注入功率: P2 = -0.500000 pu, Q2 = -0.300000 pu

能对到小数点后6位,说明这个收敛点确实满足节点功率平衡,不是随便一个中间迭代值。这种“先手算精确解,再拿程序结果对照”的习惯,我强烈建议每一位初学者养成。它能帮你快速判断自己的代码是程序bug还是算法原理问题。

5. 我把新手常犯的错踩了一遍:单位、符号和收敛判据

5.1 标幺值不是可选项,是必选项

我在给学弟调试代码时,最常看到的问题就是单位混乱。有人直接用线路阻抗0.1Ω、负荷50MW、电压230kV这样混着算,最后Ybus里的数值和功率量纲完全对不上,潮流结果自然面目全非。

正确做法是先在纸上把基准值写清楚。比如S_base = 100 MVA,V_base = 230 kV,那么Z_base = 529 Ω,I_base = S_base / (1.732 × V_base) 三相系统的线电流基准。所有阻抗、功率、电压都除以对应的基准值后,再用标幺值进入潮流程序。结果出来后,再乘以基准值转回有名值展示给工程人员看。

一个小技巧:在代码开头用注释写上基准值,以便回溯:

% S_base = 100 MVA, V_base = 230 kV % Z_base = 529 ohm % 线路实际阻抗 5.29 ohm -> 0.01 pu

这样过了一个月再回来看程序,你不会对着一个数字发懵。

5.2 PQ节点的注入功率符号,真是“送命题”

PQ节点给定的是注入功率,不是负荷功率。这是考试和工程里最容易被忽略、却影响最大的细节。

假设节点2是一个负荷节点,负荷吸收0.5 + j0.3 pu的功率。注意“吸收”意味着从电网取有功和无功,所以该节点的净注入功率分别为:

  • P2_sched = -0.5
  • Q2_sched = -0.3

如果你把P2_sched、Q2_sched填成正数,节点就变成了一个小型发电机,潮流结果自然完全不对。更隐蔽的情况是同时有发电和负荷:假设节点2有0.8 pu发电机和0.5 pu负荷,那么净注入P2_sched = 0.8 - 0.5 = 0.3 pu。潮流程序只认净注入,你得在外部把净功率算好。

在MATLAB里,可以用注释提醒自己:

% 注意:净注入 = 发电 - 负荷,负荷是负值

这种符号问题在IEEE标准算例里也不会直接告诉你,需要你从数据里推导。养成先画功率流向图的习惯,能少踩很多坑。

5.3 conj()的位置:多一个少一个结果天上地下

Gauss-Seidel公式里最关键的就是conj()。它出现在分母上,也就是conj(V2_old)。为什么需要它?因为节点功率方程里的复功率S = V × conj(I),进行代数变形后,V的共轭自然出现在分母上。这不是人为制造的复杂,而是复数运算的必然结果。

我见过有人把代码写成:

V2 = (1 / Y22) * ((P2_sched - 1i*Q2_sched) / V2_old - Y21*V1c);

也就是少了一个conj。表面看上去只是少个共轭,但迭代结果会差出好几个百分点。尤其当电压相角不为0时,少了共轭等于忽略了相角的符号,电压会朝着错误方向修正。

还有人在更新V2后,下一次迭代用conj(V2_new)做分母,这实际上是另一种迭代变体,虽然可能也能收敛,但已经不完全是教材上的Gauss-Seidel格式。初学者建议严格按推导出来的公式写,先求“过”,再求“巧”。

5.4 收敛判据:只看电压幅值会掩盖问题

很多初学者把收敛判据写成:

if abs(abs(V2) - abs(V2_old)) < tol

这等于只看电压幅值的变化,完全忽略了相角。在某些工况下,电压幅值可能已经很稳定,但相角还在缓慢漂移。相角关系到有功功率分布,忽略它会导致你认为“已经收敛了”,实际上功率失配还很大。

稳妥的做法是直接比较复数的差值:

err = abs(V2 - V2_old);

这个误差同时包含了实部和虚部的变化,也就是同时考虑了幅值和相角。更严格的做法是计算节点功率失配量,把收敛后的V代回S_calc = V × conj(Y × V),同时检查有功和无功的偏差都小于容差。两节点程序里可以用后一种方法做最终校验,但迭代过程中的收敛判断用电压误差就够了。

5.5 初值与重负荷下的发散问题

Gauss-Seidel法对初值比较敏感。两节点系统从V = 1+j0平启动通常没问题,但如果负荷很重,比如0.9 + j0.6 pu,迭代有可能在几个解之间震荡,甚至发散。这时可以尝试:

  • 降低负荷,观察趋势,确认代码正确后再逐步加重。
  • 加松弛因子:V_new = V_old + α × (V_calc - V_old),α通常取1.2到1.6之间,能加速收敛,但α太大会发散。
  • 改成Newton-Raphson法。GS法本身就不是为强非线性、重负荷系统准备的。

我在教学时总说:如果两节点系统都不收敛,先别急着怪算法,先检查符号、Ybus、conj这三个地方。90%的发散问题都出在那三个坑里。

6. 这套代码怎么改成多节点通用程序

6.1 组装任意规模Ybus

两节点代码里的Ybus是手工写死的,但它的构造规律可以推广:自导纳等于与该节点相连的所有支路导纳之和,互导纳等于两条母线之间支路导纳的负值。

对于N节点系统,推荐先定义支路数据矩阵,比如每行记录[首端母线, 末端母线, 线路导纳, 变压器变比],然后用循环遍历支路,累加Ybus元素。这段代码是潮流程序的基础设施,值得多花时间写扎实。

% 支路数据示例: [from, to, y_line] branch = [1 2 1/(0.01+1i*0.1); ... 2 3 1/(0.02+1i*0.15)]; N = 3; Ybus = zeros(N,N); for k = 1:size(branch,1) from = branch(k,1); to = branch(k,2); y = branch(k,3); Ybus(from, from) = Ybus(from, from) + y; Ybus(to, to) = Ybus(to, to) + y; Ybus(from, to) = Ybus(from, to) - y; Ybus(to, from) = Ybus(to, from) - y; end

6.2 按节点类型统一迭代与收敛判断

多节点程序需要给每个节点打标签:1代表平衡节点,2代表PQ节点,3代表PV节点。迭代时,平衡节点电压固定不变;PQ节点用给定的P、Q更新电压;PV节点给定P和电压幅值,每次迭代后要强制修正电压幅值为设定值,同时通过无功方程反解Q。

通用Gauss-Seidel的更新流程是:

for i = 1:N if type(i) == 2 % PQ节点 V(i) = (1/Ybus(i,i)) * ((P(i) - 1i*Q(i))/conj(V(i)) - sum(Ybus(i,:).*V)); elseif type(i) == 3 % PV节点 V_new = (1/Ybus(i,i)) * ((P(i) - 1i*Q_estimated)/conj(V(i)) - sum(Ybus(i,:).*V)); V(i) = V_new / abs(V_new) * V_mag_prev(i); end end

PV节点的无功是未知的,需要先根据当前电压估算出来,再进入迭代公式,最后把电压幅值拉回设定值。这个逻辑比两节点系统复杂不少,但你会发现,核心还是同一个Gauss-Seidel更新公式。

6.3 何时该放弃Gauss-Seidel换Newton-Raphson

Gauss-Seidel的定位是“教学友好、小系统够用、大系统收敛慢”。实际电力系统动辄上百个节点,还有恒功率负荷、变压器抽头、无功补偿等复杂设备,GS法的收敛速度完全跟不上。更麻烦的是,GS法的收敛性对网络参数和运行点非常敏感,重负荷下经常发散。

如果你后面要处理IEEE 14、30、118节点算例,建议直接转向Newton-Raphson法。NR法用雅可比矩阵做二阶收敛,迭代次数通常在5到8次以内,但对初值也更加苛刻。先在两节点系统上把GS法吃透,再学NR法,你会少掉很多“为什么算不出来”的挫败感。

我自己在平时算小系统或做教学演示时,偶尔还会用两节点GS代码打底。把代码里的参数改一改,就能快速验证某个负荷变化对电压的影响。这种“小工具”的价值,恰恰来自当初把一个简单问题彻底搞明白的过程。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询