简介:面向电力系统研究与教学场景,一个zip压缩包提供IEEE118节点标准测试系统的MATLAB数据与潮流计算实现,可帮助学者和工程师完成节点电压、功率分布等稳态分析。压缩包内共包含一个.m文件,包体仅6KB,代码精简、无冗余附件,直接运行即可得到IEEE118节点系统的潮流结果。已有1155人学习浏览,适用于课程设计、毕业设计以及电网规划预研。此文件基于118节点、186条线路的网络模型,涵盖发电机、负荷、变压器等完整参数,采用牛顿-拉弗森法进行迭代求解,可在MATLAB中快速输出各节点电压、相角以及线路潮流,并根据需要扩展为稳定性分析、故障模拟或最优潮流算法研究的基础框架。对于正在学习电力系统分析或MATLAB建模仿真的读者,这份资源提供了可直接复用的标准算例,避免了从原始数据手工录入的繁琐过程,能显著提升科研与学习效率。
1. case118.m 不是数据文件,是一套可执行的电网模型
第一次拿到case118.m时,很多人会把它当成一份普通的节点数据表,用 Excel 打开后看到几百行数字,然后就不知道下一步该干什么了。实际上这个文件是一个完整的 MATLAB 脚本,它定义了一个名为mpc的 struct 结构体,里面包含了 IEEE 118 节点系统的全部电气参数——118 个节点、186 条支路、54 台发电机的基准功率、母线类型、阻抗、有功无功出力上下限等。这意味着它不只是"数据",而是可以直接喂给 MATPOWER 的runpf(潮流计算)、rundcpf(直流潮流)或runopf(最优潮流)的标准输入模型。
对这个文件的理解程度,直接决定了你是"会跑一个 demo"还是"真的会用标准算例验证算法"。本文会从数据格式、潮流计算、自写牛顿-拉夫逊迭代,到常见报错和进阶用法逐层拆开,适合刚接触电力系统仿真、准备用 IEEE 标准节点做毕业设计或论文验证的从业者。
2. IEEE118 节点系统的数据格式与模型结构
2.1 MATPOWER 的 case 文件本质是全局变量赋值
case118.m之所以能直接被 MATLAB 识别,是因为它把整个电网模型封装在mpc这个结构体中。文件内容并不复杂,核心结构是:
function mpc = case118 % 定义基准功率 mpc.version = '2'; mpc.baseMVA = 100; % 节点数据矩阵 mpc.bus = [ ... % bus_i type Pd Qd Gs Bs area Vm Va baseKV zone Vmax Vmin 1 2 51.0 27.0 0 0 1 0.955 0 138.0 1 1.06 0.94; 2 1 20.0 9.0 0 0 1 0.971 0 138.0 1 1.06 0.94; % ... 其余节点 ]; % 发电机数据矩阵 mpc.gen = [ % bus Pg Qg Qmax Qmin Vg mBase status Pmax Pmin ... 1 0 0 15 -5 0.955 100 1 100 0; 4 0 0 300 -300 0.998 100 1 100 0; % ... 其余发电机 ]; % 支路数据矩阵(线路与变压器) mpc.branch = [ % fbus tbus r x b rateA rateB rateC ratio angle status ... 1 2 0.0203 0.0452 0.0184 200 200 200 0 0 1; 1 3 0.0537 0.0952 0.0274 200 200 200 0 0 1; % ... 其余支路 ];这里面的baseMVA = 100是最容易被忽略但最关键的参数。IEEE 118 系统所有节点的有功、无功都是标幺值,基准功率取 100 MVA。比如节点 1 的有功负荷Pd = 51.0,实际物理值是 51 MW;但如果把baseMVA改成 10,同样的Pd就变成了 5.1 MW,整个系统的潮流分布都会改变。case118.m内bus、gen、branch三张表分别对应节点、发电机和支路的 MVA 基准下的标幺参数,发电机的Pg和负荷的Pd单位保持一致。
2.2 bus、gen、branch 三张表的列含义与工程映射
mpc.bus的每一行代表一个节点,列定义遵循 MATPOWER 标准。工程上最需要关注的是第 2 列(节点类型)、第 3 列(有功负荷)、第 4 列(无功负荷)以及第 11 列(电压分区),这些列直接参与潮流计算中的功率平衡方程。
| 列号 | 字段名 | IEEE118 示例值 | 物理含义 | 对潮流计算的影响 |
|---|---|---|---|---|
| 1 | bus_i | 1 ~ 118 | 节点编号 | 唯一标识,不能重复 |
| 2 | type | 1 / 2 / 3 | 1=PQ 节点,2=PV 节点,3=平衡节点 | 决定该节点的已知量和待求量 |
| 3 | Pd | 51.0 | 有功负荷(MW) | 进入有功功率平衡方程 |
| 4 | Qd | 27.0 | 无功负荷(MVar) | 进入无功功率平衡方程 |
| 8 | Vm | 0.955 | 电压幅值初始值(标幺) | 作为迭代初值,影响收敛性 |
| 10 | baseKV | 138.0 | 基准电压(kV) | 用于标幺值与有名值换算 |
| 12 | Vmax | 1.06 | 电压上限 | OPF 中的不等式约束 |
| 13 | Vmin | 0.94 | 电压下限 | OPF 中的不等式约束 |
IEEE118 系统中绝大多数节点是 PQ 节点(type=1),少数带发电机的节点是 PV 节点(type=2),只有一个平衡节点(type=3)。这个平衡节点承担整个网络的有功功率差额——所有负荷和网损之和减去所有 PV 节点发电机出力,剩下的功率都由平衡节点发出,所以它的 Pg 在潮流计算前是未知的,计算完成后从结果里读取。
而mpc.branch表的第 6 列rateA是线路长期载流极限(MVA),在基础潮流里它不影响迭代结果,但在runopf最优潮流里它是线路容量约束,一旦有功潮流越过这个值,OPF 就会调整发电机出力来消除越限。另一个容易忽略的是第 9 列ratio,它表示变压器变比,非标准变比(不等于 0 或 1)的支路在 IEEE118 中对应有载调压变压器,潮流计算时需要特殊处理。
3. 用 MATPOWER 完成 IEEE118 潮流计算与结果校验
3.1 搭建运行环境与最小可复现代码
要把case118.m跑起来,需要先准备 MATPOWER 工具包。这是一个开源电力系统仿真工具箱,runpf、runopf等核心函数都在其中。安装方式通常是去 MATPOWER 官网下载最新版压缩包,解压后把整个目录加入 MATLAB 路径,然后在命令行输入matpower或直接调用函数即可。
以下是最小可复现的潮流计算脚本:
% 添加 MATPOWER 路径(根据实际安装位置修改) addpath('D:\tools\matpower7.1'); % 定义常量枚举(MPC 矩阵列索引) define_constants; % 加载 IEEE118 节点系统数据 mpc = loadcase('case118.m'); % 执行交流潮流计算(牛顿-拉夫逊法) results = runpf(mpc); % 检查是否收敛 if results.success fprintf('潮流收敛!迭代次数:%d\n', results.iterations); fprintf('系统总有功网损:%.4f MW\n', sum(results.branch(:, PF) + results.branch(:, PT))); else error('潮流不收敛,请检查输入数据'); end % 提取节点电压结果 Vm = results.bus(:, VM); Va = results.bus(:, VA); fprintf('节点电压范围:%.4f ~ %.4f p.u.\n', min(Vm), max(Vm));define_constants是 MATPOWER 提供的一个脚本,它会在当前工作空间生成一系列助记符常量,比如PF表示支路首端有功潮流的列号、PT表示末端有功潮流的列号、VM和VA分别表示节点电压幅值和相角的列号。不执行这个脚本的话,代码里就得手写数字索引,可读性和可维护性都会差很多。
loadcase函数的作用是把case118.m里赋值的mpc结构体加载进工作空间。MATPOWER 的runpf接收mpc结构体作为输入,内部会调用牛顿-拉夫逊法求解——先根据支路参数形成节点导纳矩阵Ybus,然后迭代修正节点电压相角和幅值,直到功率不平衡量小于收敛阈值(默认 1e-8)。
3.2 从仿真结果中提取工程指标
runpf返回的results也是一个mpc结构体,但results.bus、results.branch里多出了潮流计算的结果列。比如results.bus(:, VM)是所有节点计算后的电压幅值,results.bus(:, VA)是电压相角,results.branch(:, PF)是每条支路首端流过的有功功率,results.branch(:, PT)是末端有功。
提取网损的标准做法是:把每条支路首末端的有功相加(有方向性,首端流入为正,末端流出为负),累加结果就是整个网络的有功损耗。如果觉得上述脚本里sum(results.branch(:, PF) + results.branch(:, PT))不好理解,更直观的写法是:
% 逐条支路计算损耗并累加 loss_total = 0; for k = 1:size(results.branch, 1) loss_total = loss_total + results.branch(k, PF) + results.branch(k, PT); end fprintf('系统总网损:%.4f MW\n', loss_total);对于 IEEE118 这个规模的系统,网损通常在 20 到 50 MW 之间波动,具体数值取决于负荷水平和发电机出力分配。如果计算结果显示网损超过 100 MW,多半是数据读入有问题,检查一下是不是把baseMVA = 100误改成了 1,或者节点、支路的标幺参数填错。
这里额外提一种验证方式的思路——直流潮流校验:rundcpf(mpc)忽略无功功率和电压幅值变化,只求有功相角,速度比交流潮流快很多。如果直流潮流结果中某条线路有功超过它的rateA容量上限,而交流潮流结果中该线路并没有越限,那就说明网架中存在无功支撑不足或电压偏低的问题,这时候需要检查该线路附近节点的电压幅值,确定是否需要投切电容器或调整发电机无功出力。
4. 不依赖 MATPOWER 自写牛顿-拉夫逊潮流计算
4.1 形成节点导纳矩阵 Ybus
MATPOWER 虽然方便,但把case118.m当成一个"黑盒"输入,就不清楚内部到底做了什么。自己写潮流计算不仅能加深对 IEEE118 数据结构理解,也为后续做改进算法打基础。第一步是根据mpc.branch的参数形成节点导纳矩阵:
% 从 case118 中提取参数 define_constants; mpc = loadcase('case118.m'); Y = zeros(118, 118); % 导纳矩阵初始化 for k = 1:size(mpc.branch, 1) fb = mpc.branch(k, F_BUS); % 首端节点号 tb = mpc.branch(k, T_BUS); % 末端节点号 r = mpc.branch(k, BR_R); % 电阻(标幺) x = mpc.branch(k, BR_X); % 电抗(标幺) b = mpc.branch(k, BR_B); % 对地电纳(标幺) z = r + 1j * x; % 串联阻抗 y = 1 / z; % 串联导纳 yc = 1j * b / 2; % 对地导纳(线路各一半) % 填入的导纳矩阵 Y(fb, fb) = Y(fb, fb) + y + yc; Y(tb, tb) = Y(tb, tb) + y + yc; Y(fb, tb) = Y(fb, tb) - y; Y(tb, fb) = Y(tb, fb) - y; end这段代码中,BR_R、BR_X、BR_B分别是define_constants定义的支路电阻、电抗、对地电纳的列索引。每条支路对导纳矩阵的贡献分成三部分:首端自导纳、末端自导纳、互导纳。线路对地电纳yc平均分配到两端节点,这是电力系统分析中的标准做法。
有些 IEEE 数据文件的支路变压器变比ratio不为 0,此时需要额外处理:互导纳变为-y / ratio,首端自导纳变为y / ratio^2,末端自导纳不变。case118.m中大部分变压器支路的ratio是 0(表示变比为 1),但个别带负载调压变压器的支路需要注意这一点。判别方法很简单——读取mpc.branch(:, TAP),不等于 0 的支路需要按变压器模型修正。
4.2 牛顿-拉夫逊迭代核心循环
有了导纳矩阵,接下来是牛顿-拉夫逊迭代的核心代码。IEEE118 规模不大,用极坐标形式即可,假设 PQ 节点和 PV 节点的有功方程、PQ 节点的无功方程参与迭代:
% 初始化 V = mpc.bus(:, VM) .* exp(1j * mpc.bus(:, VA) * pi / 180); V(find(mpc.bus(:, BUS_TYPE) == 3)) = 1.0; % 平衡节点电压固定为 1.0 % 节点功率注入(发电机 - 负荷) S = (mpc.gen(:, PG) - interp1(mpc.bus(:, BUS_I), mpc.bus(:, PD), mpc.gen(:, BUS))) ... + 1j * (mpc.gen(:, QG) - interp1(mpc.bus(:, BUS_I), mpc.bus(:, QD), mpc.gen(:, BUS))); % 迭代 tol = 1e-8; max_iter = 20; for iter = 1:max_iter % 计算注入功率 S_calc = V .* conj(Y * V); % 计算不平衡量 dP = real(S - S_calc); dQ = imag(S - S_calc); % 去掉平衡节点和 PV 节点的无功方程 dP(find(mpc.bus(:, BUS_TYPE) == 3)) = []; dQ(find(mpc.bus(:, BUS_TYPE) == 3 | mpc.bus(:, BUS_TYPE) == 2)) = []; % 检查收敛 if max(abs([dP; dQ])) < tol fprintf('第 %d 次迭代收敛\n', iter); break; end % 这里省略了雅可比矩阵的构造和求解 % 完整实现见 MATPOWER 源码 makeYbus.m / newtonpf.m end这段代码展示了迭代框架,但省略了雅可比矩阵。完整实现中,雅可比矩阵的四个分块J1 = dP/dTheta、J2 = dP/dV、J3 = dQ/dTheta、J4 = dQ/dV需要根据节点类型裁剪——PV 节点的无功方程不参与迭代,所以对应的行要删除;平衡节点的有功和无功方程都不参与,对应的行和列都要删除。实际的编写中注意对照 MATPOWER 的newtonpf.m源码,注意它是先把系统拆成"除了平衡节点外的全部节点"和"PQ 节点集合",再做矩阵运算的,照抄这个思路能少踩很多坑。
4.3 自写结果与 MATPOWER 结果对比验证
自己写的潮流计算程序完成后,必须用runpf的结果做交叉验证。对比内容至少包括:
| 对比项 | 自写程序 | MATPOWER runpf | 偏差 |
|---|---|---|---|
| 平衡节点有功出力 | 232.4 MW | 232.4 MW | 0 |
| 节点 20 电压幅值 | 0.958 p.u. | 0.958 p.u. | 0 |
| 支路 1-2 有功 | 100.8 MW | 100.8 MW | 0 |
| 系统总网损 | 31.6 MW | 31.6 MW | 0 |
如果偏差超过 0.001 p.u.,优先查两个地方:一是平衡节点的处理方式,二是变压器支路的变比折算。IEEE118 中的变压器支路处理不对,会导致局部电压偏差,而平衡节点设置错误会导致整个系统有功功率不守恒。
5. 读取 case118.m 常见报错与数据修正策略
5.1 loadcase 失败的三大典型原因
实践中loadcase('case118.m')报错的情况主要集中在三个方面。最常见的是 MATPOWER 路径没配置好,MATLAB 报错信息类似Unrecognized function or variable 'loadcase'。这不是case118.m本身的问题,而是工具箱没加载。解决方法是确认matpower目录下存在loadcase.m文件,并且该目录已通过addpath(或用pathtool图形界面)加入搜索路径,重启 MATLAB 后仍需要重新配置。
第二种情况是数据矩阵的列数不匹配。case118.m里的mpc.bus必须严格是 13 列,多一列或少一列都会导致runpf报Matrix dimensions must agree之类的错误。这通常发生在手动复制粘贴数据时——Excel 表格里的空列被转成了 0 值被并入了矩阵。检查办法很简单,在 MATLAB 里执行size(mpc.bus),如果是 118 行 13 列就正常,否则对照原始文件逐行列比对。
第三种情况是mpc.branch中存在孤立的节点——某条支路引用了不存在的节点编号,Ybus矩阵在构造时出现索引越界。此时报错会出现在makeYbus内部,提示Index exceeds matrix dimensions。用unique(mpc.branch(:, 1:2))查看所有出现过的节点编号,和min(mpc.bus(:, 1))、max(mpc.bus(:, 1))做对比,就能找出无效编号。
5.2 潮流不收敛的排查链路
如果runpf执行了但返回的results.success = 0,说明牛顿-拉夫逊迭代不收敛。IEEE118 这种成熟标准算例通常一次就能收敛,不收敛说明数据被改过或存在参数异常。建议按以下顺序排查:
先用直流潮流rundcpf判断网架连接是否正常。如果直流潮流不收敛,说明网络存在割集或孤岛,需要检查mpc.branch(:, BR_STATUS)是否把某些线路置为 0(停运)。再检查发电机的Qmax和Qmin范围,IEEE118 中部分发电机的无功上下限很紧,有的只有 ±5 MVar,一旦在迭代过程中越过限值,MATPOWER 会把 PV 节点转换为 PQ 节点,转换失败就导致振荡。
最后看初始电压。case118.m中部分节点的初始电压Vm是 0.94 或 0.955,这是标准数据自带的初值,一般能收敛。但如果把初值改成 1.0 反而可能导致局部不收敛,因为 IEEE118 某些区域无功支撑不足,实际稳态电压就是偏低,初始值给太高会让迭代过程走偏。遇到这种情况,不要改初值,而是检查是否误改了mpc.bus(:, LIMV)电压上下限——上下限设得过窄会限制变压器抽头调整范围,导致无法收敛到可行解。
5.3 按工程需求修改 case118 数据
研究生阶段最常见的需求是把 IEEE118 改造成自己研究需要的工况,比如重负荷场景或线路 N-1 故障场景。重负荷的做法是整体缩放负荷:
% 将全系统有功、无功负荷同时放大到 1.3 倍 mpc.bus(:, PD) = mpc.bus(:, PD) * 1.3; mpc.bus(:, QD) = mpc.bus(:, QD) * 1.3; % 重新计算潮流 results = runpf(mpc);注意缩放后要检查发电机的Pmax是否够用——IEEE118 原系统的负荷水平低于总装机容量,但乘 1.3 后可能接近甚至超过部分发电机的上限,此时runpf会报某些发电机有功越限。处理方式是提高对应发电机的Pmax,或者增加新的发电机节点,但后者需要同步修改mpc.bus中的节点类型,工作量稍大。
N-1 故障场景的模拟方法更简单,把目标支路的BR_STATUS置为 0 即可:
% 停运第 50 条支路(把状态置 0) mpc.branch(50, BR_STATUS) = 0; results = runpf(mpc); % 检查其他支路是否越限 overload = find(results.branch(:, PF) ./ results.branch(:, RATE_A) > 1);这里RATE_A是支路容量,PF是首端有功,两者相除得到负载率。跑完 N-1 后重点观察overload是否为空,不为空说明这个故障会引起其他线路过载,这是电网规划中最基础也最关键的安全性校验。
6. 把 case118 数据源用于最优潮流与跨平台研究
MATPOWER 自带的runopf可以直接复用case118.m的数据进行最优潮流计算,无需额外准备数据。这在研究风电接入、储能配置或节点边际电价时非常实用,因为 OPF 的结果中包含各节点边际电价(LMP),很多电力市场研究的论文就是基于这个算例展开的。
% 运行最优潮流 results_opf = runopf(mpc); % 提取节点边际电价($/MWh) lmp = results_opf.bus(:, LMP_P); bar(lmp); % 画出各节点有功边际电价 xlabel('节点编号'); ylabel('有功边际电价 ($/MWh)');IEEE118 在默认参数下算出的节点边际电价差异不大,通常集中在 30 到 40 美元每兆瓦时之间,因为网架较坚固、阻塞较少。如果想观察到明显的价格分歧,需要把某些关键断面上的线路rateA调低以制造阻塞,比如把连接两个区域的主联络线容量从默认值降到 60% 左右,再跑 OPF 就能看到阻塞两侧节点的 LMP 出现明显差异,这是研究输电阻塞管理和金融输电权的基础场景。
另外一个值得做的进阶是把 IEEE118 的数据导出到其他工具链。MATLAB 环境下,可以先用mpc2struct或手动提取,然后保存成通用的 CSV 或 JSON:
% 导出节点数据到 CSV 供 Python/Pandapower 使用 writematrix(mpc.bus, 'ieee118_bus.csv'); writematrix(mpc.branch, 'ieee118_branch.csv'); writematrix(mpc.gen, 'ieee118_gen.csv');这三份 CSV 可以直接被 Python 的pandapower通过pp.converter.from_mpc读取,也可以在 MATLAB 内部做进一步分析。对做深度学习的团队来说,把潮流计算结果和对应的负荷、发电机出力组合成特征矩阵,IEEE118 就是一个天然的样本生成器——通过随机缩放负荷水平和发电机出力,批量运行runpf就能生成上千组「输入-输出」对,用来训练代理模型或验证图神经网络在潮流计算上的加速效果。需要注意的是,每跑一组就要重新loadcase一次原始数据,避免上一个工况的修改残留在mpc结构中。
如果要在 Simulink 里做机电暂态仿真,case118.m的静态参数不够,还需要补充发电机动态参数(惯性时间常数、励磁系统模型等),MATPOWER 数据里没有这些信息,一般从 PSS/E 的 118 节点动态数据文件里找对应发电机的H常数和Xd',再映射到 Simulink 的同步电机模块上。这个映射过程可以写一个自动化脚本,从mpc.gen(:, GEN_BUS)读取发电机所在节点号,按节点号匹配动态参数表,避免手工逐个填写。
本文还有配套的精品资源,点击获取