先说一个可能打击到不少人的事实:顶刊论文里那一套漂亮的结果图,直接跑作者放出的代码,多数时候是复现不出来的。 这不是作者造假,而是论文篇幅有限,很多工程细节被压缩在了“仿真验证”四个字背后。我自己在复现这篇基于优化模型的配电网可靠性评估研究时,前两周基本是在“跑通—报错—翻论文—改参数—再跑”的循环里度过的。但恰恰是这个过程,让我把“配电网可靠性评估”从教科书概念变成了真正能落地的分析工具。
这篇文章就把我的完整复现过程、踩过的坑、调试心得和最终的Matlab代码架构全部摊开来讲。无论你是刚接触可靠性评估的研究生,还是已经在做配电网规划但不太熟悉优化建模的工程师,这篇都能给你一条可以直接上手的路径。我会着重讲清楚三件事:优化模型为什么要这样建、Matlab里每一步在干什么、以及哪些地方最容易让结果“看起来对但实际错”。
1. 配电网可靠性评估的数学本质:为什么用优化模型而不是枚举仿真
在进入代码之前,必须先把这个底层逻辑掰扯清楚。很多人一提配电网可靠性,脑子里蹦出来的就是蒙特卡洛模拟:随机抽样元件故障时间、模拟开关动作、统计负荷停电时长。这确实是经典做法,而且没错,但它有一个隐藏痛点——它只能“评估”,不能“寻优”。
1.1 传统可靠性评估方法的两座大山
传统的可靠性评估手段主要有两类:
解析法(故障模式后果分析法FMEA、最小割集法、网络等值法等)的核心思路是:枚举所有可能的故障事件,计算每个事件对负荷点的影响,再聚合得到系统指标。它的优点是精度高、解释性强,缺点是计算量随系统规模指数爆炸。配电网节点数从几十涨到几百,枚举空间就变得不可接受了。
蒙特卡洛模拟法的核心思路是:用随机数模拟元件故障和修复过程,通过大量统计得到指标的期望值。它的优点是对复杂系统适应性强,缺点是为了让小概率高影响事件(比如两回线同时故障)收敛,需要的仿真时长非常惊人,而且每一次“场景变化”都得重新仿真——这让“优化”无从谈起。
1.2 优化模型带来的范式转变
当你把可靠性评估问题表达成一个**混合整数线性规划(MILP)**时,事情就不同了。
可靠性评估的底层脉络其实是:系统里哪些元件可能故障、故障发生后哪些负荷会失电、通过什么操作能恢复供电。如果你把可能的故障场景写成一堆约束条件,把“开关状态”“是否切负荷”这些决策变量放进模型,优化器就能同时回答两个问题:
- 在给定网络拓扑下,最严重的故障场景会造成多大的失负荷?
- 通过调整分段开关和联络开关,能把这部分损失降到多少?
这正好对应了可靠性评估中的“最小失负荷”概念——系统在故障情况下,通过最优调度手段仍无法避免的负荷损失,才是真正需要统计的可靠性短板。
1.3 顶刊为什么喜欢这个角度
最近几年发表在电力系统顶刊(IEEE Transactions on Power Systems、Applied Energy 等)上关于配电网可靠性的文章,大量采用优化模型,原因很实际:
- 它能和网架规划、分布式电源选址定容、储能配置这些上层优化问题无缝衔接,一个模型同时算规划和运行,不用来回迭代两套工具;
- MILP求解器(Gurobi、CPLEX)进步太快,处理几百个节点、上千个场景的可靠性模型已经只在秒级到分钟级;
- 结果天然带有“经济性”注释——失负荷量乘以单位停电损失费用,可以直接用于投资决策。
所以,复现这篇论文的关键,不是背下它的流程图,而是看透它把可靠性指标“翻译”成约束和决策变量的手法。这个手法一旦掌握,换任何网络、任何扩展场景都能用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 可靠性指标与约束映射:把SAIFI、SAIDI翻译成数学表达
论文的实验部分放了一堆指标曲线,但这些指标不是天上掉下来的。你要在Matlab里真正算出来,必须清楚每个指标在模型里是怎么被表达和统计的。
2.1 核心可靠性指标体系
配电网可靠性评估里最常用的指标组如下:
| 指标 | 全称 | 公式含义 | 工程意义 |
|---|---|---|---|
| SAIFI | System Average Interruption Frequency Index | 用户停电总次数 / 用户总数 | 平均每户每年停几次电 |
| SAIDI | System Average Interruption Duration Index | 用户停电总时长 / 用户总数 | 平均每户每年停多久 |
| CAIDI | Customer Average Interruption Duration Index | 停电用户的总停电时长 / 停电用户总数 | 每次停电平均持续多久 |
| ASAI | Average Service Availability Index | 用户正常供电时长 / 总需求时长 | 供电可用率 |
| ENS | Energy Not Supplied | 失负荷总电量 | 全年总缺供电量 |
| AENS | Average Energy Not Supplied | 总缺供电量 / 用户总数 | 平均每户缺供电量 |
在优化模型的框架下,SAIFI和SAIDI通常不直接作为决策变量,而是通过“各场景下的失负荷状态”聚合得到。具体来说:
- 场景 $s$ 下,节点 $i$ 的失负荷二元变量为 $z_{i,s}$(0表示供电,1表示失电);
- SAIFI的分子 = 对所有场景、所有节点求 $\sum_s \sum_i z_{i,s} \cdot N_i$($N_i$是节点$i$的用户数);
- SAIDI的分子 = 对所有场景求 $\sum_s D_s \cdot \sum_i z_{i,s} \cdot N_i$($D_s$是场景$s$对应的故障持续时间);
- ENS = 对所有场景求 $\sum_s D_s \cdot \sum_i (1-z_{i,s}) \cdot P_{L,i}$($P_{L,i}$是节点$i$的有功负荷)。
这里的核心技巧是:故障场景不是连续变量,而是离散的“事件集合”。可靠性优化模型和马甲层级的优化(比如经济调度)最大的不同就在这——经济调度里场景数是固定的,可靠性模型里场景数由你截断的故障阶数决定。
2.2 场景生成:一阶、二阶故障的正交组合逻辑
论文里为了控制计算量,一般考虑到的故障场景并非全部组合,而是采用“N-1准则 + 部分N-2”策略。我在复现时按以下逻辑生成场景集:
- 一阶故障:任意一条馈线段的主动故障,场景数 = 线路段数;
- 一阶故障+检修状态:某条线段故障的同时,另一条线路处于计划检修,场景数 = 线路数 × 检修率;
- 二阶故障:最严重的前若干组双回线同时故障(通常是同塔双回或共沟电缆段),场景数靠经验或历史故障统计数据截断。
每个场景都对应一个故障持续时间参数 $D_s$。在模型里,$D_s$ 有两种处理方式:固定值(按配电网规程查表)或者由故障定位、隔离、转供电和修复时间累加。论文里用的是后者,这也是“优化”发挥价值的地方——好的开关配置能显著缩短隔离和转供电时间,进而降低SAIDI。
2.3 配电网辐射状拓扑约束的线性化表达
配电网可靠性评估里绕不开的一个硬约束是:正常运行状态(以及大多数故障后的重构状态)必须保持辐射状拓扑。也就是网络里不能有环,否则保护的整定配合会乱套,可靠性分析也就失真了。
在MILP里表达“无环”有几种做法,这里我推荐一种在Matlab代码里好实现且求解效率高的方法:虚拟潮流法(单商品流约束)。
设 $x_{ij}$ 为支路$(i,j)$的连通状态(0-1变量),设 $f_{ij}$ 为从根节点(变电站出口)流向节点的虚拟单位潮流。约束组如下:
$$
\sum_{j \in \delta(i)} f_{ij} - \sum_{j \in \delta(i)} f_{ji} = 1, \quad \forall i \in \mathcal{N} \setminus { \text{根节点} }
$$
$$
- M x_{ij} \le f_{ij} \le M x_{ij}
$$
$$
f_{ij} \ge 0
$$
这些约束的含义是:每个非根节点都必须有1个单位的虚拟流量净流入,而流量只能存在于已连通的支路($x_{ij}=1$)上。只要虚拟流量能送达到所有节点,就说明拓扑从根节点出发是一棵连通树,天然无环。
我当时第一次跑模型时,省了虚拟潮流约束,只保留了用户数平衡($\sum x_{ij} = N_{node} - 1$),结果优化器给出的“最优重构方案”里出现了两个互不相通的环,SAIDI算出来异常乐观。后来把虚拟潮流一加,结果马上就正常了。这个坑希望能帮看到的同仁避开。
2.4 失负荷量与潮流约束的耦合表达式
可靠性评估模型里,故障后重构、切负荷策略的数学表达如下。对每个节点 $i$,在场景 $s$ 下:
$$
\sum_{j \in \delta(i)} P_{ji,s} + P_{g,i,s} + P_{L,i,s}(1 - z_{i,s}) = \sum_{j \in \delta(i)} P_{ij,s} + P_{L,i,s}
$$
其中 $P_{g,i,s}$ 是分布式电源在场景$s$下的出力,$P_{L,i,s}$是节点$i$的负荷功率。等式左边是注入功率(上游馈线流入 + 本地电源 + 保留负荷),右边是流出功率(下游馈线流出 + 总负荷)。当 $z_{i,s}=1$ 表示该节点被切负荷,实际供给的负荷为 $P_{L,i,s}(1-z_{i,s})=0$,相当于大部分负荷退出运行。
变压器容量约束在论文里也做了简化——用一条线路载流量限制替代完整的潮流方程,这是在配电网里常用的近似:因为配电网主要呈辐射状、电压等级低、线路电阻偏大,完整的牛顿-拉夫逊潮流在这种优化-评估耦合模型里计算成本太高,且短期内不会作为决策的边界条件。如果你后续要接入电压约束,需要在H3里加入DistFlow线性潮流模型,这里先按下不表。
3. Matlab建模全流程:从节点支路数据到YALMIP可解模型
进入实操环节。我用的环境是 Matlab R2022a + YALMIP + Gurobi 10.0。YALMIP是个建模层工具,它能把上节里那些数学表达式直接写成代码,再翻译给底层求解器。Gurobi负责解MILP,速度和稳定性都比Matlab内置的intlinprog好不少,尤其是当场景数上千时差距非常明显。
3.1 输入数据结构设计:别用稀疏矩阵硬扛
配电网的拓扑信息,我建议不要直接塞稀疏矩阵,而是用结构体数组组织:
| 字段 | 含义 | 示例 |
|---|---|---|
| branch_num | 支路编号 | 1 |
| fbus | 首端节点 | 2 |
| tbus | 末端节点 | 3 |
| r | 支路电阻(Ω) | 0.341 |
| x | 支路电抗(Ω) | 0.210 |
| length | 支路长度(km) | 0.65 |
| rate | 载流量(A) | 320 |
| switch_type | 开关类型(0无/1分段/2联络) | 1 |
| repair_time | 故障修复时间(h/次) | 3.0 |
节点数据用另一组结构体:
| 字段 | 含义 | 示例 |
|---|---|---|
| node_num | 节点编号 | 12 |
| pload | 有功负荷(kW) | 145.5 |
| qload | 无功负荷(kVar) | 52.3 |
| cust_num | 用户数 | 68 |
| section | 所在馈线段编号 | 3 |
这种结构的好处是:后续做故障场景枚举时,可以直接按“支路号”索引,不用每次都在稀疏矩阵里查找非零元素。实际算下来,在200+节点的算例里,这种索引方式比稀疏矩阵查找快了保守3倍以上。
3.2 YALMIP建模核心代码段
下面这段代码是模型搭建里最核心的部分,我直接贴出精简版骨架:
matlab复制% 定义决策变量
% x_ij: 支路连通状态
% z_i_s: 节点i在场景s失电状态
% f_ij_s: 支路ij在场景s的虚拟潮流
x = binvar(n_branch, 1, 'full');
z = binvar(n_node, n_scenario, 'full');
f_flow = sdpvar(n_branch, n_scenario, 'full');
% 场景相关连通状态变量
x_s = binvar(n_branch, n_scenario, 'full');
% 约束组1:正常运行拓扑辐射状约束
C = [];
% 虚拟潮流单商品流约束
for i = 1:n_node
if i == root_node
continue;
end
in_idx = find(branch(:,2) == i); % 流入i的支路
out_idx = find(branch(:,3) == i); % 流出i的支路
C = [C, sum(f_flow(in_idx,1)) - sum(f_flow(out_idx,1)) == 1];
end
% 支路连通与虚拟潮流上限约束
for k = 1:n_branch
C = [C, -1e3 * x(k) <= f_flow(k,1) <= 1e3 * x(k)];
end
% 约束组2:场景s下的重构拓扑约束
for s = 1:n_scenario
for i = 1:n_node
if i == root_node
continue;
end
in_idx = find(branch(:,2) == i);
out_idx = find(branch(:,3) == i);
C = [C, sum(f_flow(in_idx,s)) - sum(f_flow(out_idx,s)) == 1];
end
for k = 1:n_branch
C = [C, -1e3 * x_s(k,s) <= f_flow(k,s) <= 1e3 * x_s(k,s)];
% 故障支路在该场景下必须断开
if ismember(k, fault_branch_set{s})
C = [C, x_s(k,s) == 0];
end
end
end
% 约束组3:失负荷与潮流平衡
for s = 1:n_scenario
for i = 1:n_node
inflow_expr = 0;
out_expr = 0;
for k = 1:n_branch
if branch(k,2) == i
out_expr = out_expr + p_flow(k,s);
end
if branch(k,3) == i
inflow_expr = inflow_expr + p_flow(k,s);
end
end
% 父节点注入 + 本地DG - 流出 = 保留负荷
C = [C, inflow_expr + p_dg_s(i,s) - out_expr == pload(i) * (1 - z(i,s))];
end
end
注意这里的 p_flow(k,s) 是随场景变化的连续变量,代表支路k在场景s下传输的有功功率。为了保持MILP的线性特性,我没有引入电压幅值变量,而是用它和节点功率平衡直接耦合——这在10kV中压配网误差可接受,因为可靠性评估关注重点是“有没有电”而非“电压高低”。
3.3 故障场景与持续时间的前置计算
在做YALMIP建模之前,需要用一段脚本生成 fault_branch_set{s} 和 duration(s)。我参考论文附录的算法2,按以下步骤实现:
matlab复制function [fault_branch_set, duration, weight] = gen_scenarios(branch, node, cfg)
n_branch = size(branch, 1);
scenario_list = {};
duration_list = [];
% 一阶故障
for k = 1:n_branch
if branch(k, switch_type) == 0 % 无开关的线路段
scenario_list{end+1} = {k};
% 修复时间作为持续时间基准
duration_list(end+1) = branch(k, repair_time);
end
end
% 二阶故障:按故障概率降序取前N_pair对
pairs = find_top_pairs(branch, cfg.n_pair);
for p = 1:length(pairs)
scenario_list{end+1} = {pairs{p}(1), pairs{p}(2)};
% 联合故障持续时间按最严重者+重叠系数估算
duration_list(end+1) = max(branch(pairs{p}(1), repair_time), ...
branch(pairs{p}(2), repair_time)) * cfg.overlap_factor;
end
fault_branch_set = scenario_list;
duration = duration_list;
end
这里有个地方容易踩坑:场景的权重(发生概率)不能直接当常数放进目标函数里,因为MILP里场景权重乘以失负荷变量后,会和可靠性指标的定义打架。更稳妥的做法是把每个场景视作等概率的“代表性故障事件”,算出结果后再用各场景的实际概率加权聚合出SAIFI/SAIDI/ENS。很多复现失败的案例,问题就出在这:把场景概率直接乘进目标函数,求出来的开关配置严重偏向高概率场景,导致极端但低概率事件下的指标被掩盖。论文采用的方式是分两步走:第一步用等权重场景求开关配置优化;第二步用真实概率回代验证指标。
3.4 求解与结果导出:Gurobi参数设置的经验值
求解MILP时,Gurobi的参数设置直接决定耗时。下面是经过我反复调试后,对这种可靠性评估模型效果很不错的参数组合:
matlab复制ops = sdpsettings('solver', 'gurobi', ...
'verbose', 2, ...
'gurobi.Threads', 8, ...
'gurobi.TimeLimit', 600, ...
'gurobi.MIPGap', 0.001, ...
'gurobi.MIPFocus', 1);
optimize(C, objective, ops);
MIPFocus=1 是让求解器偏向加速找到可行解而非证明最优性;MIPGap=0.001 的意思是当最优解和可行解的相对差距小于0.1%时提前停止。对可靠性评估来说,0.1%的近似误差对SAIFI/SAIDI的数值完全没有影响,但能把求解速度提升数倍。我一开始用 MIPGap=0 硬算,185节点的算例跑了快40分钟才出结果,后来放宽到0.001,5分23秒就收敛了,指标差异在第四位小数才出现。
4. 复现中的四个暗礁:实测排错与应对方案
这一节是全文最重要的一节。复现工作最耗时间的不是建模本身,而是模型跑起来之后出现的那些“看似合理但实际错误”的结果。我把我真实踩过的四个坑全部列出来,附上判断依据和解决办法。
4.1 拓扑约束缺失导致“孤立节点还在供电”的假象
现象:优化完成后,检查结果发现某个节点在场景$s$下明明与任何带电支路都不连通,但 z(i,s)=0(不失电)。SAIDI异常低。
排错链路:我用 plot_bus_voltage 画了场景$s$下的连通子图,发现该节点是个孤儿岛。检查约束组2,发现虚拟潮流约束我写的是“对每个非根节点,流入减流出等于1”,但根节点本身没限定必须有源注入。优化器很“聪明”地让孤立岛里所有支路虚拟潮流为0,同时所有节点虚拟潮流净流入为0——这样就绕过了强制连通的约束。
根因:虚拟潮流法的单商品流约束,只强制了“节点有净注入”,但没有约束根节点到每个节点的流动路径。需要额外加“根节点必须有足够虚拟流出量”的约束:
matlab复制% 根节点虚拟流出约束
for s = 1:n_scenario
out_idx = find(branch(:,2) == root_node);
C = [C, sum(f_flow(out_idx,s)) >= n_node - sum(fault_branch_set{s})];
end
这个约束确保从根节点流出至少覆盖所有非故障节点的虚拟流量。加上之后,孤儿岛现象立即消失。
提示:这种问题常见的排查工具是画连通子图。我在调试阶段写了一个
check_topology(adj_matrix, root)函数,遍历所有节点看是否有从根节点可达的路径。每次求解完先跑一遍再下结论。
4.2 场景概率权重与指标聚合的错位
现象:最终算出的SAIFI和论文里的数值差了一个数量级。论文里0.389次/户·年,我算出来0.0412次/户·年。
排错链路:我先检查场景生成,确认一阶故障场景数量是支路数。再看目标函数里的失负荷变量权重——我发现代码里 objective = objective + weight(s) * z_sum(s),这里的 weight(s) 是故障概率 $\lambda/(\lambda+\mu)$ 而不是1。当我把所有场景单位化、求出决策方案后再回代真实概率,SAIFI就从0.0412变成了0.3785,对上了。
根因:场景概率和可靠性指标的关系不是线性的。SAIFI的定义是“停电次数 ÷ 用户数”,分子里每个场景贡献应该是一次“事件”而非“概率加权事件”。优化时用概率加权可以帮助求解器区分轻重缓急,但结果统计时必须回到定义本身。
4.3 DistFlow线性化带来的误差在长线路末端被放大
现象:加入电压降约束后(约束节点电压在0.95~1.05p.u.),模型结果出现部分节点电压越限但未被惩罚的异常。
排错链路:我检查了线性化DistFlow中,线路电压降落项 $V_i - V_j = (r_{ij}P_{ij}+x_{ij}Q_{ij})/V_0$。在长线路末端,由于线路电阻大、负荷重,实际电压偏离额定值比较明显,直接把 $V_0$ 当常数代入会导致计算电压与实际电压误差超过2%。这时候做可靠性评估反而被虚假的电压越限误导,切负荷决策偏保守。
应对:对于10kV配电网络,线路长度普遍在2km以内时,这种误差影响不大。但如果你的网络里有超过5km的长线路或者电缆,建议在评估阶段先用全交流潮流校验一下优化方案里的电压最小值,不必直接把精确潮流放进优化模型。
提示:在复现时,我用了两段式方法:优化阶段用线性潮流,校验阶段用Matpower跑精确交流潮流。两个结果对比,误差在1.2%以内——这个流程可以复用到你的工程里。
4.4 Gurobi求解MILP卡顿时的降维技巧
现象:238节点、3馈线系统,完整场景集有468个,模型连续变量和二元变量加起来超过2.1万个,求解器跑满600秒仍不收敛。
排错链路:我用 gurobi_iis() 检查了不可行约束集,发现没有不可行,但求解过程上下界收敛极慢。原因主要是场景间高度耦合(共用一套开关决策变量),导致分支定界树非常深。
应对方案:我用 Benders分解思路做了一层简单的场景解耦:先松弛掉部分场景的约束做初步求解,然后把最优解带回全场景验证。实际更有效的做法是把整个模型按“故障场景”切分:主问题只包含正常运行状态约束 + 开关变量;子问题是各场景下的最小失负荷计算(此时开关固定),然后通过割平面来回迭代。这块代码实现虽然复杂,但求解速度提升非常显著——同样的算例从“600秒未收敛”变成“3分10秒收敛到1%最优间隙”。
如果你不想一开始就上Benders,可以用更朴素的 场景采样:随机抽取20%的场景求解一次,得到的开关配置,再在所有场景中验证可靠性指标。这个技巧在论文的图4(场景数量vs指标偏差曲线)里有验证,100个场景和468个场景的SAIDI偏差只有0.03%,说明可靠性指标对场景采样并不敏感。
5. 算例实测:IEEE RBTS Bus 2 系统的完整复现结果
理论说得再多,不如直接看数。我用论文的测试系统——IEEE RBTS Bus 2(一个经典的中压配电网可靠性测试系统)做了完整复现。该网络包含4条馈线、22个负荷点、38条线路段。系统总峰值负荷12.29MW,用户总数约3000户。
5.1 基础参数与场景设置
- 馈线故障率:0.065次/km·年(论文表3里的值);
- 平均修复时间:线路5小时/次;变压器200小时/次(按表4);
- 分段开关:每条馈线装2个;
- 联络开关:馈线对之间各1个;
- 场景截断:一阶故障38个 + 二阶故障按地理相邻抬高故障率取前20对。
5.2 优化结果与传统方法对比
| 指标 | 传统FMEA法 | 论文方法(复现值) | 差异 | 说明 |
|---|---|---|---|---|
| SAIFI (次/户·年) | 0.423 | 0.389 | -7.8% | 优化配置减少了故障隔离范围 |
| SAIDI (小时/户·年) | 4.816 | 3.927 | -18.4% | 联络开关转供缩短了停电时长 |
| CAIDI (小时/次) | 11.39 | 10.09 | -11.4% | 平均单次停电时长下降 |
| ASAI (%) | 99.94 | 99.955 | +0.015% | 可用率提升约1.3小时/年 |
| ENS (MWh/年) | 136.4 | 114.8 | -15.8% | 每年少损失约21.6MWh |
从表里能看出:优化模型对SAIDI的改善幅度明显大于SAIFI。这很好理解——SAIFI主要取决于故障发生频率和分段开关的数量,而SAIDI还受转供电时间影响,模型优化的重心正是故障隔离与转供路径决策。
5.3 场景灵敏度分析
论文里还有一个关键的灵敏度结论:当馈线故障率从0.065上升到0.13(翻倍),SAIDI的增幅在优化模型下远小于传统FMEA法。
| 故障率 (次/km·年) | FMEA法SAIDI (h) | 优化模型SAIDI (h) | 优化收益 |
|---|---|---|---|
| 0.065 | 4.816 | 3.927 | 18.4% |
| 0.10 | 7.412 | 5.887 | 20.6% |
| 0.13 | 9.637 | 7.354 | 23.7% |
| 0.16 | 11.498 | 8.709 | 24.3% |
这组数据说明了可靠性优化模型的鲁棒性:故障率越高,优化方案对开关配置的利用越充分,相对收益反而越大。这也是在配电网规划阶段值得用这个模型跑一遍的原因——它不仅仅告诉你“现在可靠性是多少”,还能帮你确定“哪里加开关收益最大”。
我在此补充一个规划层面的使用建议:跑完优化模型后,把每个候选开关位点对应的边际可靠性收益($\Delta SAIDI / 开关成本$)列一个表,直接作为投资排序的依据。这一步论文没有细讲,但实际做规划时非常有价值。
6. 代码运行指南与扩展方向
6.1 文件结构与运行顺序
复现工程在Matlab下的结构如下:
code复制repo_root/
├── main_optimization.m % 主程序,按顺序调用
├── load_case.m % 读取测试系统数据
├── gen_scenarios.m % 生成故障场景集
├── build_reliability_model.m % YALMIP模型构建
├── solve_milp.m % 调用Gurobi求解
├── compute_indicators.m % 从优化结果聚合指标
├── plot_results.m % 画可靠性指标对比图
└── data/
├── rbus2_branch.xlsx
├── rbus2_node.xlsx
└── rbus2_switch_config.xlsx
主程序的运行逻辑是:
load_case()加载网络数据和开关初始状态;gen_scenarios()生成故障场景,这一步会打印每个场景的故障元件、故障持续时间和故障发生概率;build_reliability_model()构建YALMIP模型,内部包含拓扑约束、场景约束、失负荷约束和切负荷目标;solve_milp()调用Gurobi求解并输出求解状态、间隙和求解时间;compute_indicators()根据解得的开关状态和切负荷变量,聚合SAIFI/SAIDI/CAIDI/ASAI/ENS等指标;plot_results()输出SAIDI对比柱状图、场景失负荷热力图和典型故障下的开关动作时序图。
6.2 核心函数接口示例
以 define_switch_action(id) 为例:
matlab复制function action = define_switch_action(branch_id, switch_type)
% 定义开关动作类型
% switch_type: 0-常闭分段开关 1-常开联络开关 2-带遥控功能开关 3-手动开关
switch switch_type
case 0
action = 'open'; % 故障隔离时需分闸
case 1
action = 'close'; % 转供电时需合闸
case 2
action = 'auto'; % 故障自愈,无需人工动作
case 3
action = 'manual'; % 人工到场操作,耗时按30分钟计
end
end
这个函数在场景枚举时会被反复调用,用于生成不同开关配置下的转供路径,并累加每次开关动作对应的停电持续时间。
6.3 从复现走向扩展的三个方向
复现出论文结果只是第一步。这套模型的价值在于它可以往多个方向延伸:
方向一:考虑分布式电源(DG)接入。只要给每个节点加一组DG出力变量,并把出力上限和启停状态纳入约束,模型立刻就能评估DG对可靠性的提升效果。论文里DG渗透率场景的图表就是这么得到的。
方向二:考虑储能(ESS)的孤岛运行。故障后利用储能对重要负荷继续供电,模型里要加入储能荷电状态(SOC)时序约束、充放电功率限制。这会大幅增加变量维度,但指标收益也很明显——特别是对医疗、数据中心这类重要负荷。
方向三:多层规划耦合。外层做网架改造投资决策,内层做可靠性优化,两层通过投资预算和可靠性目标形成耦合。这也是顶刊近年最热的“convexification of distribution system planning with reliability”方向。
6.4 关于复现态度的一句老实话
最后说点实在的。复现顶刊论文除了训练建模功底之外,最大的收获就是逼你弄清楚每个公式背后的工程假设。这篇论文里的优化模型不是多了不起的数学创造,但它胜在把配电网运行里的工程师直觉(哪里装开关、怎么转供最划算)系统性地编码成了数学约束。
你自己动手跑通一遍之后,再回头看可靠性导则里的那些经验公式,视角会完全不一样——你会清楚知道那些经验值是从什么假设推导来的,什么时候适用,什么时候会失准。这种“拆解—重建—再验证”的能力,才是复现类工作最有价值的部分。
我在整个复现过程中用到的全部Matlab脚本和测试数据,都按照“可运行、可复现、可扩展”的标准做了整理。如果你也卡在某个MILP建模细节或者Gurobi参数调优上,可以顺着这篇文章的思路重新排查一遍。大概率,问题就出在我踩过的这四类暗礁里。
