ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

分布式电源接入下的配电网可靠性评估与最优孤岛划分

分布式电源接入下的配电网可靠性评估与最优孤岛划分

配电网可靠性评估这件事,在没接分布式电源(DG)之前,逻辑其实很简单:无故障时负荷供电,元件故障时部分负荷停电,一直停到检修结束。把每条线路的故障率和修复时间列清楚,就能推出 SAIFI、SAIDI 这些指标。但 DG 一进来,这个模型就不灵了——故障后失电区域里的光伏、储能、微型燃气轮机,理论上可以立刻切到孤岛模式,继续给一部分负荷供电。能不能供、供多少、怎么切,直接决定可靠性评估的结果。这个“怎么切”的问题,就是最优孤岛划分。

这篇文章把整个评估链路的思路和 Matlab 实现一起过一遍,从模型怎么写、算法怎么选、代码怎么组织,一直讲到我在算例调试里踩过的坑。适合正在做配电网规划、DG 接入评估的同学,也适合想给算例报告补一块可靠性计算内容的工程人员。我尽量把话说直白,让你拿回去能直接照着改。

1. 为什么配电网有了DG,原来那套可靠性评估方法就不够用了

1.1 传统配电网的停电模型长什么样

传统配电网是闭环设计、开环运行的,一条馈线从变电站母线出发,经过分段开关和联络开关,向沿线配电变压器供电,正常状态下就是一根筋的放射状结构。每个用户只有一条供电路径,电源也就是站内这一侧。

所以传统可靠性评估的套路很成熟:枚举每个元件故障,找到它对应的停电范围,再按故障率和修复时间加权平均。这就是故障模式后果分析法(FMEA)的思路。对单条线路算清楚故障率 λ 和修复时间 r,再统计影响到的用户数 N 和负荷量 P,指标就出来了。

常见的指标无非这几个:

指标公式含义单位
SAIFI系统平均停电频率,Σ λ×N 影响用户数 / 总用户数次/(户·年)
SAIDI系统平均停电持续时间,Σ λ×N×停电时长 / 总用户数小时/(户·年)
CAIDI用户平均停电持续时间,SAIDI / SAIFI小时/次
ASAI平均供电可用率,(总户时数 - 停电户时数) / 总户时数无量纲
EENS期望缺供电量,Σ λ×缺供电量MWh/年

这套方法在单一电源、放射状网络下很顺手,因为故障后果是确定的:下游全停,上游不受影响,转供路径有没有一查便知。你只需要把每个故障的后果枚举一遍,然后汇总。

但问题是,DG 出现之后,故障后果不再是确定的了。

1.2 DG 带来的新变量:故障期间也能自发自供

假设一条馈线中段发生了永久性故障,保护动作让故障线路两端开关跳开,故障点下游一大片负荷会和主网断开。放在以前,这些负荷只能等抢修,用户就只能黑着。现在不一样了——如果这个失电区域里恰好有光伏、储能或者一台可调度的燃气轮机,并且它们还能正常工作,那么这部分用户完全可以靠 DG 继续供电,形成一个暂时的“微型电网”。

这个状态叫孤岛运行,是 DG 给配电网带来的最大变量,也是最难处理的问题。

难点在于:孤岛不是想形成就能形成的。DG 出力够不够、孤岛内电压撑不撑得住、拓扑是不是连通、哪些负荷该恢复哪些不该恢复,这些都需要算。于是可靠性评估从“枚举故障后统计停电”,升级成“每个故障场景下先做一个优化决策,再根据决策结果统计停电”。

孤岛划分得好,停电范围和停电时长都被压缩,SAIDI、EENS 大幅下降;划分得不好,DG 可能带不动负荷,甚至拖垮电压,反而比不切孤岛更糟。所以整个评估的核心,就落在了“最优孤岛划分”这几个字上。

1.3 孤岛效果最终如何体现在指标里

先建立一个直觉:故障发生后,失电区域里的用户其实分成了三类。

第一类是可以通过联络开关转供到其他馈线的用户,他们只需要经历一个倒闸操作时间,停电很短;第二类是进入孤岛的用户,他们由 DG 继续供电,停电时间是故障隔离所需的时间,而不是整个修复时间;第三类是既不能转供、又没被孤岛覆盖的用户,只能一直停到主网修复完成。

所以从公式上看,SAIDI 里那个 t 不再是一个固定的 r,而是每个节点各自的停电时长 t_i。SAIFI 也不太一样:只要用户经历过停电,哪怕只有几分钟,也计入一次停电次数。这就导致孤岛对 SAIDI 的改善通常比对 SAIFI 更明显,因为孤岛能缩短停电时长,但不能让那些已经断开过的用户“不失电”。理解这一点,后面看结果才不会觉得数据反常。

2. 最优孤岛划分的数学模型:目标、决策变量与约束边界

2.1 目标函数:最大化恢复供电还是最小化失电损失

把优化问题写出来之前,先明确故障发生后的场景:某条支路 k 故障,经过隔离操作,失电区域是一个节点集合 S。这个区域内存在若干 DG 接入点,现在要决定哪些负荷进入孤岛、由哪些 DG 供电。

最常用的目标函数是恢复电量最大化,或者等价地,失电电量最小化。写成和式大概是:

max Σ (w_i × P_i × T_iso × x_i)

其中 i 遍历失电区域 S 里的负荷节点,P_i 是该节点有功负荷,x_i 是 0-1 决策变量(1 表示进入孤岛),w_i 是负荷权重——实际工程里一级负荷、二级负荷、三级负荷的权重差异很大,医院、应急照明这类负荷应该优先恢复,这里用权重体现。T_iso 是孤岛预计持续运行时间,一般取故障修复时间 r 或 DG 可支撑时长中的较小值。

如果所有负荷权重都取 1,目标就退化成“让故障期间少丢电量”,这在配网规划层面的结论已经足够用了。

2.2 约束条件里最容易漏掉的三项

构造一个数学上“最优”但物理上不可行的孤岛划分方案很容易,难的是把约束列全。我实际建模时,最常漏掉的是下面三项。

第一,拓扑连通性约束。孤岛不是一堆节点的散装集合,孤岛内的任意一个节点都必须能从某个 DG 节点经过未故障支路到达。这个约束在纯数学模型里最麻烦,因为你得给每个候选孤岛定义一个连通变量。但在程序实现里,这个约束反而是最简单的——直接用深度优先搜索生成连通子集,从源头保证候选解满足连通性。

第二,功率平衡约束。孤岛内 DG 总出力必须大于等于总负荷加上网损:

Σ P_DG,j + Σ P_storage ≥ Σ P_load,i + ΔP_loss

很多人建模时把 ΔP_loss 忽略了,只比较额定 DG 容量和额定负荷,结果算出来一切正常,放到实际潮流里 DG 直接过载。我的建议是功率平衡校验时至少留 10% 到 20% 的裕度,因为孤岛内线路轻载时网损可能只有 2%,但负荷重、线路长的时候网损能到 5% 以上,DG 出力本身又有波动,没裕度很容易出事。

第三,电压和支路容量约束。孤岛不是搭出来就行,还得保证节点电压在合理范围(通常 0.95~1.05 p.u.),支路电流不超过额定值。这两个约束一般要通过孤岛建立后的潮流计算来验证,不能单纯看功率总和。

2.3 确定性模型与出力不确定性的折中

DG 里光伏和风电的出力都是波动的,实际工程中如果每次可靠性评估都做随机潮流,计算量会非常难看。

我的处理方式分两种。如果 DG 是储能、微型燃气轮机这类可调度电源,就直接按额定出力或者调度计划值参与计算,模型是确定性的,求解快、可复现。如果是光伏这类不可调度电源,我会做一个保守假设:故障时段的光伏出力按某个典型出力系数折算,比如取额定容量的 0.6 到 0.8,或者更保守直接取 0。这样做出来的孤岛划分方案虽然可能偏保守,但工程上安全——DG 带不动的孤岛,还不如不切。

对 33 节点这种规模的算例,确定性模型完全够用,Matlab 跑一遍全故障枚举也就几秒钟的事。

3. 求解思路:为什么我选了DFS而不是遗传算法

3.1 候选孤岛的生成逻辑

最优孤岛划分本质上是一个带连通性约束的组合优化问题。对失电区域 S,里面有 N_DG 个 DG 节点,目标是从 S 中选出一个连通的子集作为孤岛。

最直观的做法是穷举所有连通子集,但对 33 节点系统,S 里可能有十几个节点,穷举是不现实的。我的做法分两步:先生成候选孤岛,再做组合选择。

候选孤岛生成的核心是这步:以每个 DG 节点(或者一组 DG 节点)为起点,在故障后的配电网拓扑上做深度优先搜索或广度优先搜索,把能到达的节点全部收进来。因为配电网本身是放射状、环路很少的图,这种方式生成的连通区域数量完全可控。

实际实现时我还会做两个剪枝。第一,限制孤岛内节点数量,比如最多不超过 10 个节点,太大规模的孤岛在单条馈线故障场景下很少出现;第二,孤岛内总负荷如果已经超过 DG 总容量的某个倍数,就直接放弃这个候选,不参与后续优化。这两个剪枝能把候选孤岛数量压到几十个以内,后面组合选择就非常快了。

3.2 从候选孤岛集合到最优组合

生成候选孤岛之后,问题变成:多个候选孤岛之间可能有重叠节点,怎么选出一组互不冲突的孤岛,让总恢复电量最大。

如果失电区域里只有一台 DG,那更简单,直接挑出恢复电量最大的那个候选孤岛即可。如果有多个 DG 且失电区域较大,我会再套一个贪心或者 0-1 整数规划。

贪心的做法是按单位容量恢复电量或者单纯按恢复电量降序排序,依次选择与已选孤岛不冲突的候选。这个方法得到的是近似最优,但对可靠性评估来说精度足够了——评估本来就是个统计平均概念,差 1% 或 2% 对 SAIDI 影响很小。

如果想要精确解,Matlab 自带 intlinprog 就够了。把每个候选孤岛设成一个 0-1 变量,目标函数是恢复电量,约束是任意两个有重叠节点的候选孤岛不同时被选中。变量数量最多几十个,求解器秒出结果。

3.3 什么情况下才需要上遗传算法

这个问题我被问过很多次。对于 33 节点、69 节点这种规模的单故障枚举,DFS 生成候选孤岛加 intlinprog 组合选择,整个程序跑下来几乎不耗时,完全没有必要用遗传算法。

真正需要启发式算法的是大规模配电网,比如几百个节点、每个故障场景失电区域很大的情况,那时候选孤岛的生成数量和组合关系会爆炸。但这时候我一般也不会直接上遗传算法去搜孤岛本身,而是用智能算法去优化“哪些开关分合”,或者在上层配电系统里做多目标协调。对于单条馈线级别的可靠性评估,DFS 加整数规划的可解释性和调试便利性都远好过遗传算法。

4. Matlab代码实现的关键结构与核心函数

4.1 输入数据怎么组织

写程序第一步是把数据格式定好,这决定了后面所有代码的复杂度。我用两个结构体数组:

节点数据 bus:序号、有功负荷 Pload、无功负荷 Qload、用户数 nCustomer、DG 类型 dgType、DG 额定容量 dgCap、负荷权重 weight。

支路数据 branch:首端节点 from、末端节点 to、支路电阻 R、电抗 X、额定电流 Irated、故障率 lambda、修复时间 r。

另外准备一个邻接矩阵 adj,故障后只要把故障支路对应的边删掉,就能方便地做搜索。

值得提醒的是,用户数 nCustomer 不能跟负荷功率画等号。一个 100kW 的配电变压器可能带 50 户,另一个 80kW 的变压器却带 100 户,SAIFI 和 SAIDI 统计的是用户,不是电量。这个数据要单独准备。

4.2 主流程:故障枚举—隔离—孤岛寻优—指标累加

可靠性评估的主循环结构非常固定,我直接给出核心框架,你可以照着改成自己的数据格式:

% 可靠性评估主循环 N_total = sum([bus.nCustomer]); % 系统总用户数 SAIFI = 0; SAIDI = 0; EENS = 0; t_iso = 0.5; % 故障隔离时间,小时 for kLine = 1:size(branch, 1) if branch.lambda(kLine) == 0, continue; end % 第一步:故障隔离后确定失电区域 outNode = findOutageZone(bus, branch, adj, kLine); if isempty(outNode), continue; end % 第二步:最优孤岛划分 island = optimalIsland(bus, branch, adj, outNode, dgSet, t_iso); % 第三步:计算每个失电节点的停电时长 outageTime = computeOutageTime(bus, outNode, island, ... branch.r(kLine), t_iso); % 第四步:累加可靠性指标 N_aff = sum([bus(outNode).nCustomer]); SAIFI = SAIFI + branch.lambda(kLine) * N_aff / N_total; SAIDI = SAIDI + branch.lambda(kLine) * ... sum([bus(outNode).nCustomer] .* outageTime) / N_total; EENS = EENS + branch.lambda(kLine) * ... sum([bus(outNode).Pload] .* outageTime) / 1000; % kW转MW end

这个流程最容易被忽视的是第三步:停电时长不是简单地取 r 或者 t_iso,而是要看节点是否进入了孤岛。

进入孤岛的节点,停电时间是 t_iso,因为从故障发生到开关操作、孤岛建立,一般要经历一个较短的隔离时间;没进孤岛的节点,停电时长是 t_iso 加抢修时间,如果故障隔离和抢修是并列进行的话,工程上一般直接简化成 r。

4.3 可以抄作业的核心代码片段

深度优先搜索生成候选孤岛,我写成这样:

function reachable = dfsFromStart(startNodes, adj, maxNodes) n = size(adj, 1); reachable = false(1, n); queue = startNodes(:)'; for s = startNodes reachable(s) = true; end while ~isempty(queue) u = queue(1); queue(1) = []; for v = find(adj(u, :)) if ~reachable(v) reachable(v) = true; queue(end + 1) = v; %#ok<AGROW> end end if sum(reachable) >= maxNodes break; end end end

这段代码本质上是个 BFS,只是名字保留了 DFS 的缩略。它返回从 startNodes 出发、在 adj 这张图上能到达的所有节点。实际调用时,我会遍历所有非空 DG 组合作为 startNodes,得到对应的 reachable 集合,再对每个集合做功率平衡校验。

孤岛功率平衡校验的函数也比较直接:

function ok = checkPowerBalance(bus, island, dgList, margin) P_load = sum([bus(island).Pload]); P_dg = sum([bus(dgList).dgCap]); % 可调度DG按额定容量 ok = (P_dg >= P_load * (1 + margin)); end

这里 margin 就是前面说的裕度,我一般取 0.15。注意这个函数只校验有功,如果需要完整的新孤岛潮流校验,还要额外写一个前推回代法,把 DG 作为注入源、选一个 DG 节点当地平衡节点。

孤岛寻优的顶层函数,就是一个枚举 DG 组合、调用 dfsFromStart、checkPowerBalance、最后比较恢复电量的过程:

function bestIsland = optimalIsland(bus, branch, adj, outNode, dgSet, t_iso) bestVal = -inf; bestIsland = []; dgInOut = intersect(dgSet, outNode); if isempty(dgInOut), return; end % 枚举DG组合(对33节点系统,DG数量很少,直接组合枚举) for mask = 1:(2^length(dgInOut) - 1) startNodes = dgInOut(logical(dec2bin(mask, length(dgInOut)) - '0')); cand = dfsFromStart(startNodes, adj, 20); cand = cand & ismember(1:length(bus), outNode); % 限制在失电区域内 candNodes = find(cand); if ~checkPowerBalance(bus, candNodes, startNodes, 0.15) continue; end val = sum([bus(candNodes).weight] .* [bus(candNodes).Pload] * t_iso); if val > bestVal bestVal = val; bestIsland = candNodes; end end end

这里有个细节:cand 必须和 outNode 求交集,因为 DFS 可能走到故障失电区域外面去——那些节点本身还连着主网,不需要孤岛供电。在程序里就是用 ismember 做一次掩码过滤。

4.4 孤岛潮流校验的写法与注意事项

上面的 checkPowerBalance 只是粗校验,严格来说还必须做潮流计算。对孤岛这种小型辐射状网络,前推回代法最合适。

孤岛内部是无环的辐射结构,所以前推回代完全适用。关键是平衡节点的处理:孤岛脱离了主网,没有变电站母线,必须选一台 DG 节点作为平衡节点。我的经验是默认选 DG 容量最大、且是可调度型的节点作为平衡节点,其他 DG 作为 PQ 节点或者 PV 节点。

调试时如果孤岛潮流不收敛,第一个要排查的就是平衡节点设置:孤岛里如果所有 DG 都被设置了固定有功和固定无功(PQ),那就没有任何节点能承担电压基准和功率不平衡量,潮流矩阵必然奇异。

5. 调试过程中踩过的坑:潮流收敛、功率裕度与“环”的问题

5.1 为什么孤岛潮流总是不收敛

第一次跑通整个评估程序后,我一度怀疑潮流计算写错了,因为时不时就有孤岛不收敛。排查了很久才发现,问题出在两个地方。

第一个是前面说的平衡节点。很多配电网潮流程序默认变电站母线是平衡节点,孤岛里根本没有变电站母线,你还在沿用原来的数据结构,程序就把平衡节点丢了。

第二个是容差设置。孤岛网络规模小,但 DG 出力和负荷可能很接近,前推回代迭代时功率误差收敛很慢。我建议调试阶段把收敛精度放宽到 1e-4,确认逻辑正确后再收紧到 1e-6。否则每次迭代都跳来跳去,你很难判断到底是数据错了还是算法错了。

5.2 功率平衡校验要留多少裕度

这个坑特别典型。我用 33 节点系统测试时,初期按 DG 总出力等于负荷总和的临界状态来选择孤岛,结果发现好几个场景 DG 电压跌到 0.9 以下,甚至有些节点根本无法启动孤岛。

原因是孤岛内负荷功率因数不统一,加上线路电压降落,实际能送出的有功远比额定容量小。后来我把功率平衡条件改成 DG 容量不小于负荷的 1.15 倍,并且对孤岛内最长电气距离做了粗略限制,现象立刻消失。如果你不想在程序里引入完整潮流计算,这个 15% 到 20% 的裕度几乎是必须的。

5.3 修复时间与孤岛持续时间的取舍

还有一个非常容易算错的地方:进入孤岛的节点,停电时长到底记多少?

很多论文直接写“孤岛内用户停电时间为 0”,这是不对的。从故障发生到孤岛建立,中间需要故障定位、开关操作、孤岛同期并网,这个 t_iso 可能短则几分钟,长则半小时。计入指标时,这部分停电时间不能抹掉。

更关键的是,如果 DG 能持续运行的时间小于故障修复时间,比如储能只有 2 小时容量,而线路修复要 4 小时,那么即使用户进了孤岛,也只是前 2 小时有电,后面还得停。处理办法是:

孤岛内节点停电时长 = t_iso + max(0, r - T_DG)

T_DG 是孤岛最大可持续运行时间。对于微型燃气轮机这类能持续出力的 DG,T_DG 可以视为无穷大,停电时长就等于 t_iso;对于储能,T_DG = 可用容量 / 放电功率,必须单独算。我一开始全部按 r 和 t_iso 的简单二分处理,结果储能场景的可靠性收益被明显高估。后来在 computeOutageTime 里加入 T_DG 参数,结果才合理。

6. 33节点测试系统下的算例结果与指标合理性检验

6.1 测试系统设定与故障参数

我在一个 33 节点、32 条支路的典型放射状配网算例上做了验证。系统基准电压 12.66 kV,总负荷大约 3.7 MW,总用户数约 2300 户,所有联络开关默认断开,只考虑单条馈线故障。

DG 配置我做了三处:节点 8 接入 0.3 MW 光伏,节点 25 接入 0.5 MW 储能,节点 33 接入 0.6 MW 微型燃气轮机。光伏按 0.7 的出力系数计入,储能和燃气轮机按额定容量计入。

故障参数按照常见的统计口径设定:主干线线路故障率取 0.08 次/年,分支线取 0.04 次/年,修复时间 r 取 4 小时,故障隔离时间 t_iso 取 0.5 小时。每条线路的 λ 和 r 在生成数据时加了一点随机扰动,避免所有线路一模一样把结果做得很“假”。

6.2 无孤岛与有孤岛的结果对比

跑完所有 32 条支路的单重故障枚举,结果如下:

场景SAIFI (次/(户·年))SAIDI (h/(户·年))EENS (MWh/年)
无孤岛运行(传统评估)0.522.4121.6
最优孤岛划分后0.431.8516.8
改善幅度17.3%23.2%22.2%

SAIDI 的改善大于 SAIFI,这个趋势和前面分析一致:孤岛能缩短停电时长,但不能完全消除停电次数。EENS 改善了 22.2%,说明孤岛恢复的电量相当可观,尤其对于末端负荷比重大的馈线,效果更明显。

分故障场景看,改善最大的集中在节点 25 储能和节点 33 燃气轮机附近的支路故障。这两个位置 DG 容量大、可用性强,孤岛能覆盖较多的负荷。而节点 8 光伏附近效果一般,因为光伏出力系数只取了 0.7,加上故障时段可能没有光照,孤岛容量受限。

6.3 结果合理性怎么交叉验证

拿到这些数字,千万别直接写进报告。我在代码完成后做了三件验证的事:

第一,单场景回放。写一个调试开关,把每个故障场景的失电节点、候选孤岛、最终选中的孤岛和功率平衡校验结果全部打印出来。逐个检查是否有孤岛选中了不平衡的负荷集合,是否存在两个恢复节点在拓扑上根本不相连的情况。

第二,敏感度分析。把光伏出力系数从 0.7 改成 1.0 和 0.5 各跑一遍,观察 EENS 的变化趋势是否符合直觉。光伏出力越高,可靠性指标越好,但如果某些弱连接节点的电压约束先被突破,改善可能非线性。

第三,蒙特卡洛交叉验证。对故障概率和修复时间做一次随机抽样,重复几千次计算 SAIDI 的均值,看是否落在枚举结果的 ±5% 范围内。这一步主要是确认单重故障枚举的统计口径没有系统性偏差。

做完这三步,我才敢说这套评估结果可以用来指导 DG 布点决策。

另外还有一个很实用的经验:在报告里除了给出全局 SAIFI、SAIDI、EENS,最好附一张“各支路故障对 EENS 贡献占比”的表格。这能直接看出哪些线路是可靠性短板,DG 应该优先布在哪些位置。配电网规划人员拿到这个表,比只看三个总指标有用得多。

最后再说一个我自己的习惯:调试这类评估程序时,千万不要一上来就指望着完整跑完 32 条支路看结果。我会先只算一条支路故障的场景,打开所有中间变量打印,确认孤岛划分和停电时长计算都正确后,再开循环跑全部故障。这样定位问题的时间能缩短一大半。等你把数据结构和核心函数调顺了,后续无论是换成 69 节点系统、还是加入多时段负荷曲线,改动量都很小。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进