简介:面向电力系统分析与控制场景的MATLAB例程包,聚焦支路断开后潮流越限问题与切负荷紧急控制策略,适用于电力工程专业学生、研究人员及调度运行人员学习参考。压缩包共18个文件,包含17个m脚本与1个asv自动保存备份,整体仅18KB,结构紧凑、模块清晰。例程包含完整的主程序与配套电网模型、节点和支路索引定义,覆盖电网建模、牛顿-拉弗森法潮流求解、越限检测、基于优化算法的负荷切除以及迭代校核的完整流程。各脚本按功能划分,涉及系统参数定义、节点与支路索引转换、故障概率设置、最优切负荷计算等任务,便于按模块阅读与修改。通过这份例程,可以直观理解切负荷策略如何在支路断开后智能选择部分负荷切除,消除过载并恢复系统安全裕度,同时掌握在MATLAB中组织电力网络数据结构、调用潮流函数、扩展遗传算法或粒子群优化等算法的具体实现思路。当前已有196人学习使用,适合作为电力系统稳态分析与紧急控制课程设计、毕业设计或工程预研的起步模板。
1. 潮流越限之后,为什么必须谈切负荷
一条 220kV 线路跳闸,看起来是保护动作,但如果周围线路没有足够的转移裕度,潮流会瞬间堵在某个断面上。电压跌落、线路载流量越过红线,这时唯一能快速扭转局面的手段就是切负荷。loadshed.zip里的 MATLAB 例程解决的就是这个场景:用caseRTS79.m等数据文件建好电网模型,支路断开后重新做潮流计算,识别越限,再通过load shed和linpro按优先级切除最小负荷量,让系统回到安全边界内。适合作 N-1 安全分析课程设计,也适合需要把紧急控制逻辑快速嵌进自有研究的工程师。
2. loadshed.zip 文件拆解:基于 MATPOWER 数据结构的潮流计算底层
loadshed.zip里的文件并不是随意堆在一起,它大致分成三类:数据定义、MATPOWER 索引常量、以及封装好的计算函数。这些文件之间互相依赖,阅读时最好从caseRTS79.m开始,因为它是整个切负荷计算的电网数据源头。
2.1 caseRTS79.m 定义了怎样的电网断面
caseRTS79.m是 IEEE RTS-79 单区域测试系统的 MATPOWER 格式数据,包含了 24 条母线、33 台发电机和 38 条交流线/变压器支路。用loadcase读进来后,它会返回一个结构体mpc,其中三个矩阵是所有计算的基础:mpc.bus存节点类型、负荷和电压限值,mpc.branch存支路阻抗和对地导纳以及长期载流量RATE_A,mpc.gen存发电机接入节点、有功出力和上下限。loadshed程序里的idx_bus.m、idx_brch.m、idx_gen.m定义的就是这些矩阵每列对应的索引常量,比如PD表示负荷有功列,RATE_A表示线路额定容量列,PG表示发电机有功列。
| 矩阵 | 关键列索引常量 | 在切负荷流程里关注的信息 |
|---|---|---|
mpc.bus | BUS_I, PD, QD, VMIN, VMAX, BUS_TYPE | 节点的负荷基数、电压上下限、PQ/PV 类型 |
mpc.branch | F_BUS, T_BUS, R, X, B, RATE_A, BR_STATUS | 支路两端节点、阻抗、长期载流量、投运状态 |
mpc.gen | GEN_BUS, PG, QG, PMAX, PMIN, QMAX, QMIN | 发电机出力范围,切负荷后松约束的边界 |
这三个矩阵的维度可以通过下面几行命令快速确认:
mpc = loadcase('caseRTS79'); fprintf('节点数 %d, 支路数 %d, 发电机数 %d\n', ... size(mpc.bus, 1), size(mpc.branch, 1), size(mpc.gen, 1));loadcase是 MATPOWER 的核心读数据函数,它把.m里的矩阵赋值到结构体的同名域里。上面代码输出节点数 24、支路数 38、发电机数 33,从这里就能知道caseRTS79.m并不是一个很小的教学算例,而是一个足够表现出断面转移和越限的测试系统。之后的idx_*系列索引常量,会在每次修改mpc.bus或mpc.branch的某一行时用到,避免硬编码列号。我一般会先把这些索引常量打印一遍,确认当前 MATPOWER 版本的列定义没有改动,再开始改代码,否则后面所有按列索引赋值的操作都会错位。
2.2 ext2int 与 int2ext:为什么切负荷前必须统一内部编号
MATPOWER 的很多函数在执行时会把外部节点编号转换为从 1 开始的连续内部编号,方便矩阵运算。ext2int.m和int2ext.m就是这组转换的入口。loadshed例程里caseRTS79.m中的节点编号可能本身是连续的,但实际工程数据往往不是,例如某个 110kV 站点的节点编号是 201、202 而旁边 35kV 站点是 5、6。如果直接对mpc.branch按外部编号修改支路状态,容易错位。
切负荷之前,我习惯先统一把数据转到内部编号:
mpc_ext = loadcase('caseRTS79'); mpc = ext2int(mpc_ext); % 转成内部连续编号 target_bus = 4; % 想切 4 号节点的负荷 row = find(mpc.bus(:, BUS_I) == target_bus); mpc.bus(row, PD) = mpc.bus(row, PD) * 0.5; % 切除一半有功负荷 mpc_ext = int2ext(mpc); % 结果再转回外部编号用于展示这里BUS_I是mpc.bus的节点编号列,PD是有功负荷列,两个常量都来自idx_bus.m。ext2int返回的节点编号可能与外部一致,但当原模型包含孤岛或并列节点时,内部编号会出现重排,因此必须在修改矩阵前先做转换。int2ext则是把包含计算结果的结构体映射回用户习惯的外部编号,这样画图、出报表时不需要自己维护一张映射表。
同一压缩包里还能看到loadpro.m、layerscale.m、failprob.m、failrate.m和mcc.m。其中loadpro.m负责生成负荷优先级,layerscale.m负责把切负荷动作分层,failprob.m和failrate.m用于故障概率建模,mcc.m看起来像是做蒙特卡洛重复仿真的入口;layerscale.asv是 MATLAB 自动保存的备份文件,可以忽略。这几个文件把“切哪里、切多少、何时停”的关键逻辑分开了,比全部写在一个脚本里好维护得多。
2.3 makeBdc 与 newrunpf:从数据到潮流计算结果的通道
makeBdc.m在 MATPOWER 内的职责是根据支路参数生成节点电纳矩阵Bbus,这是直流潮流和安全约束经济调度的基础。在loadshed这个例程里,它被linpro.m和切负荷主程序拿来搭线性规划约束,因为交流潮流的非线性会让混合整数规划很难快速求解。newrunpf.m则像是runpf的增强版,它接收mpc,返回包含 bus、branch、gen 运行结果的结构体,内部可能默认处理了ext2int、迭代次数和收敛精度。
res = newrunpf(mpc); if res.success fprintf('系统潮流计算收敛,系统网损 %.2f MW\n', ... sum(res.branch(:, PF_LOSS))); else error('潮流不收敛,先检查原始数据或断开支路后的孤岛'); endres.success是 MATPOWER 结果结构体中的收敛标志位,值为 1 表示牛顿-拉弗森迭代收敛。res.branch的每一行对应一条支路,列PF_LOSS在idx_brch.m中定义为支路有功损耗,但需要确认你的 MATPOWER 版本里该列索引是否一致。如果断开一条关键联络线后系统被撕成两个电气岛,newrunpf通常会直接返回不收敛,此时后续的切负荷计算根本没有基础,所以loadshed例程里会把res.success作为后续步骤的守卫条件,这也是我建议你在自己的程序里保留的判断。
3. 支路开断与越限检测:从 newrunpf 结果里找出需要切负荷的节点
上一章把数据结构和求解器接上了,这一章要解决一个具体问题:断开一条支路后,怎么从潮流结果里判断系统是不是已经处于“非安全”状态。越限是切负荷的触发信号,如果检测点选错,后面所有优化都是空转。
3.1 哪些指标能算作“越限”
潮流计算结果给出每个节点的电压幅值VM和相角VA,每条支路的有功潮流PF、无功潮流QF。是否越限要和运行限值比较。常规判据有三类:支路过载、节点电压越限、发电机无功越限。支路过载最常见,也最直接导致切负荷,因为过载线路如果不快速降载,可能触发距离保护或导线过热。电压越限则反映局部无功严重不足,切负荷有时不针对它,必须结合无功补偿一起看。
| 指标 | 计算方式 | 对应索引常量 | 典型限值 |
|---|---|---|---|
| 线路负载率 | abs(PF) / RATE_A | PF, RATE_A | 大于 1.0 越限 |
| 节点电压 | VM与VMIN/VMAX比较 | VM, VMIN, VMAX | 0.94~1.06 p.u. |
| 发电机无功 | QG与QMIN/QMAX比较 | QG, QMIN, QMAX | 超过上下限即越限 |
注意RATE_A为 0 或 NaN 的支路在数据里表示该线路不设长期容量,我一般把它替换为Inf或者一个很大的数,避免计算负载率时出现除零。另外,不同区域电网的电压合格范围差别很大,切负荷研究里用 0.90~1.10 p.u. 会更保守,直接用系统给定的 0.94~1.06 往往看不出低压减载动作点。
3.2 用 failrate 和 failprob 构造 N-1 故障集
实际调度中不会只假设固定一条支路断开,failprob.m和failrate.m就是用来生成支路开断集合的。failrate.m返回每条支路的故障率(次/年),failprob.m把故障率转换成抽样用的概率值。例程里的常见做法是先按故障率排序,选出一批发概率较高的支路做开断模拟,而不是做 38 条支路的全枚举。全枚举在小系统里没问题,到 IEEE 118 节点就是 186 次潮流计算,再叠加切负荷迭代,单线程 MATLAB 跑起来非常慢。
rate = failrate(mpc); % 每条支路故障率 prob = failprob(mpc); % 概率化结果 [~, order] = sort(prob, 'descend'); topN = order(1:min(10, length(order))); % 取前10个高风险支路 for k = 1:length(topN) mpc_test = mpc; mpc_test.branch(topN(k), BR_STATUS) = 0; % 断开该支路 res = newrunpf(mpc_test); % 这里保存越限结果 endsort的第二个输出是原数组排序后的下标,取前 10 个就得到一个风险最高的故障子集。BR_STATUS为 0 表示支路退出运行,这在 MATPOWER 里是标准做法。如果你的研究只关心某个特定断面,也可以把topN直接替换成手动指定支路编号,这样计算量小,结果也更容易解释。
3.3 越限检测代码与收敛性判断
下面这段检测函数可以直接复用,它把不收敛和越限两种情况分开返回。输入res就是newrunpf的计算结果。
function [overload, volt_viol] = check_limit(res) br = res.branch; rate = br(:, RATE_A); rate(rate <= 0) = Inf; % 去掉无效容量 overload = find(abs(br(:, PF)) ./ rate > 1.0); v = res.bus(:, VM); vmin = res.bus(:, VMIN); vmax = res.bus(:, VMAX); volt_viol = find(v < vmin | v > vmax); endoverload返回的是过载支路的行号,volt_viol返回的是电压越限的节点行号。后面切负荷循环里,每次重新算潮流后都调用该函数,直到两个向量都为空。需要注意,低压网络的负荷通常带有静态电压特性,简单切掉部分有功负荷会降低电压越限程度,但功率因数不变的情况下无功负荷也按比例下降,所以严格来说还要同时削减QD。load shed主程序里一般会把PD和QD同步按比例调整,这也解释了为什么负荷削减量用 MW 表达时需要附带功率因数。
另一个容易踩的坑是孤岛检测。newrunpf返回不收敛时,res结构体里往往没有合法的bus电压值,这时调用check_limit会用 NaN 参与比较,find结果为空但系统实际是失稳的。所以顺序一定是先判断res.success,再调用check_limit。这在后面的切负荷迭代里尤其重要,因为每一轮切除动作都可能把一个弱连接断面彻底断开。
4. 切负荷算法的 MATLAB 实现:线性规划与分轮次削减
越限节点和支路找出来以后,剩下的问题就是切多少、先切哪里。loadshed.zip里同时给了两条路线:一条是linpro.m直接求最优解,另一条是layerscale.m和load shed.m实现的分层分轮次削减。两种方法各有适用范围,下面分别展开。
4.1 切负荷优化建模的变量与约束
用线性规划求解切负荷量,核心假设是用直流潮流描述支路有功。决策变量是各节点削减的有功负荷向量shed。目标函数是最小化所有负荷削减量的加权和,权重由负荷优先级决定:
minimize sum(w(i) * shed(i)) subject to PTDF * (gen - load + shed) <= rate PTDF * (gen - load + shed) >= -rate 0 <= shed <= load| 实现方式 | 求解速度 | 最优性 | 适用场景 |
|---|---|---|---|
| 线性规划 | 快 | 全局最优 | 离线策略研究,适合写论文 |
| 分轮次贪心 | 慢但稳健 | 次优 | 在线紧急控制模拟,便于解释动作逻辑 |
这里PTDF是功率传输分布因子矩阵,gen-load+shed是节点净注入。优先级权重w可以由例程里的loadpro.m生成,它把一级、二级负荷分别映射为高权重和低权重;也可以简单地把重要负荷的权重取 10,普通负荷取 1,让优化结果尽量先切不重要的负荷。注意这个模型忽略了电压和无功,所以解出来只是有功切负荷的参考值,必须回到交流潮流里再校核。
4.2 用优化变量求解最小切负荷量
例程里的linpro.m承担了线性规划求解的入口,MATLAB 优化工具箱自带的是linprog,两者不是一回事。下面的求解骨架我用optimproblem写法,兼容性更好,也方便你在不同 MATLAB 版本之间迁移:
n = size(mpc.bus, 1); nbranch = size(mpc.branch, 1); shed = optimvar('shed', n, 'LowerBound', 0); % 非负 prob = optimproblem('Objective', weight(:)' * shed); % 节点净注入 = gen - load + shed Pnet = gen_p - load_p + shed; % 线路潮流 = PTDF * Pnet flow = PTDF * Pnet; prob.Constraints.flow_up = flow <= rate; prob.Constraints.flow_lo = -flow <= rate; prob.Constraints.shed_ub = shed <= load_p; [sol, fval] = solve(prob);这里用optimvar和optimproblem是 2017b 以后的推荐写法,更老版本可以用linprog的矩阵输入。gen_p是把发电机出力按节点聚合后的向量,load_p是节点总有功负荷矩阵。PTDF可以用makePTDF得到,如果你没有该函数,也可以从makeBdc生成的 B 矩阵推导;例程里保留makeBdc.m很大程度就是为了省掉这一步。
代码里的flow_up和flow_lo分别对应线路正反向潮流限值,rate是各支路额定容量向量,维度与flow一致。weight需要预先定义好,时间紧急时就全部置 1,表示优先保证切除总量最小;有多级负荷时,把一级负荷的权重改成 100,优化器就会自动避开它。求解后sol.shed就是每个节点需要削减的 MW 数。实际使用中我发现这个线性模型对高比例新能源系统偏差较大,因为新能源出力波动会让实际运行点偏离基准潮流断面,所以在做日前预案时会把多个典型断面分别建模,再取各节点削减量的最大值作为保守策略。
4.3 分轮次切负荷:线性规划解不可行时的兜底方案
线性规划存在不可行风险,比如某些节点只有一级负荷,优化器宁愿不切也不违反硬约束。例程里的layerscale.m和load shed.m采用的是分层分轮次策略。基本流程是:第一轮按过载线路两端节点切 5% 的负荷,重新算潮流,如果仍然越限,再在剩余负荷中按优先级切下一轮。这样能把一次大切除拆成多次小动作,更接近实际低频低压减载装置的动作逻辑。
max_round = 10; shed_total = zeros(n, 1); for r = 1:max_round [overload, volt_viol] = check_limit(res); if isempty(overload) && isempty(volt_viol) break; end % 对每个过载支路,取两端节点中优先级最高的一侧 for k = 1:length(overload) br_idx = overload(k); fbus = res.branch(br_idx, F_BUS); tbus = res.branch(br_idx, T_BUS); if weight(fbus) < weight(tbus) cut_node = fbus; else cut_node = tbus; end ratio = 0.05; mpc.bus(cut_node, PD) = res.bus(cut_node, PD) * (1 - ratio); mpc.bus(cut_node, QD) = res.bus(cut_node, QD) * (1 - ratio); shed_total(cut_node) = shed_total(cut_node) + ... load_p(cut_node) * ratio; end res = newrunpf(mpc); end这段代码里F_BUS和T_BUS分别表示支路首端和末端节点,weight是预先计算的优先级向量,数字小表示更重要的负荷。每轮只切 5%,避免一次动作过大导致频率不稳;max_round=10意味着最多削减约 40% 的负荷。实际应用中这个比例要根据系统备用容量调整,负荷频率特性明显的区域要更保守。注意我这里是直接修改mpc.bus,不是res.bus,因为res只是计算结果,只有mpc是下一轮求解的输入。如果你看到例程里某些版本写的是res.bus修改,那很可能是个 bug,在迭代中会导致结果不更新。
5. 跑通 RTS-79 算例并迁移到自有电网模型
前面花了三章把原理和实现都讲透了,这一章落到操作。我会把我自己的运行习惯和调试顺序写出来,包含几个容易让新手卡住的点。
5.1 从零开始运行例程
解压loadshed.zip后,不要直接双击某个.m文件,先确认 MATLAB 当前路径包含全部文件。由于文件里有ext2int.m、makeBdc.m等和 MATPOWER 重名的函数,如果电脑里有多个 MATPOWER 版本,路径顺序会影响运行结果。我用的环境是 MATLAB R2023b,优化工具箱和 MATPOWER 7 以上版本都能兼容这套例程。
cd('D:\work\loadshed'); addpath(pwd); mpc = loadcase('caseRTS79'); res = newrunpf(mpc); check_limit(res)如果返回的overload为空,说明原始断面安全,我们还需要手动开断一条支路:把caseRTS79中节点 14 到节点 16 的联络线置为停运再算一次。修改后check_limit就会得到非空结果,接下来运行load shed就能看到切负荷量。
5.2 调参与验证技巧
| 参数 | 建议值 | 影响 |
|---|---|---|
| 过载阈值 | 1.0 | 越小时越容易触发切负荷,可设为 0.95 留裕度 |
| 每轮切负荷比例 | 5% | 太大可能切过头,太小迭代次数增加 |
| 负荷优先级向量 | [10 1 1 ...] | 拉开权重能让一级负荷最后被切 |
| 最大轮次 | 10 | 防止死循环 |
验证时除了看最终潮流是否越限,我建议额外记录shed_total与过载线路负载率的关系,做成表格观察“每切 1% 负荷,负载率下降多少”。这个方法比只看最终结果更能暴露模型里的薄弱断面。另一个值得关注的指标是切除负荷导致的频率偏移,虽然直流潮流模型看不到频率,但可以用切除总量与系统惯量的比值估算,超过 0.5 Hz 就需要调小单轮切除比例。
5.3 迁移到 IEEE 118 或自定义数据
迁移只需把caseRTS79.m换成其他 MATPOWER 格式的 case 文件,但要注意三点:节点编号必须连续,否则先跑ext2int;RATE_A不能全部为 0,至少给关键线路设定容量;发电机出力上下限要与系统总负荷匹配,否则切负荷还没开始潮流就不收敛。如果case文件里没有mpc.version字段,MATPOWER 会按旧版本解析,建议在文件开头加上mpc.version = '2'。
最后,如果例程的load shed主函数把结果写到工作区,可以用下面这行命令把每个节点的切负荷结果落到 Excel,方便写报告:
writetable(array2table(shed_total), 'shed_result.xlsx');本文还有配套的精品资源,点击获取