做配电网规划的人第一次接触N-1多半会有个错觉这是输电网才玩的游戏。实际上当城市中压配电网负荷密度上来以后单辐射网络的供电可靠性很快就撑不住。我去年给某地区做配电网扩展规划仿真时把N-1约束加进优化模型里结果和传统规划方案差异非常大——投资多了将近三成但故障后的失负荷量下降了一半。这篇就围绕配电网N-1扩展规划的建模思路、求解方法和Matlab代码实现展开把我在这个项目里踩过的坑和实际能跑通的方案一并写出来。适合正在做配电网规划课题、或者需要用Matlab搭建可靠性规划算例的工程师和数据建模人员参考。1. 从单辐射到N-1配电网规划为什么会突然变得这么复杂1.1 传统规划习惯与可靠性需求之间的断层传统配电网规划最常用的做法是辐射状网架加导线截面选择目标函数就是投资费用和网损费用最小约束条件是节点电压上下限、支路电流上限最多再校验一下各线路负载率。这种规划逻辑在负荷密度低、供电半径大的农村电网里没什么问题因为负荷小、线路裕度大单条线路故障导致的影响面有限。但城市核心区、工业园区这类场景完全不一样。这里负荷密度高10千伏馈线的负载率动辄超过70%一旦某条线路或者某个分段开关故障退出相邻线路根本没有足够的剩余容量完成转供用户停电时间从原来的“几分钟复电”变成“几个小时的抢修”。这个时候你再回头看规划方案就会发现传统的经济性优化没有把事故后的运行状态纳入约束规划结果在可靠性维度上是盲区。N-1校验解决的就是这个问题系统在正常运行方式下任意一个元件线路、主变压器、断路器故障退出运行后系统仍然能保持稳定运行不损失负荷或者只损失允许范围内的负荷。放到配电网规划里N-1扩展规划就是在网架优化的过程中把“任一线路故障后剩余网络仍然能对所有负荷供电”作为一条必须满足的约束条件。1.2 N-1校验不是锦上添花而是确定性约束要注意N-1准则在输电网里是安全稳定性的硬约束但到了配电网很多人会把它理解成“接线方式满足手拉手就行”这是不够严谨的。手拉手供电只保证了两个电源之间有联络通道并没有保证故障后通道容量够用、电压合格。N-1扩展规划要做的是把这种定性要求变成定量约束放到优化模型里作为等式或不等式条件。我在建模时把N-1分成两个层次来理解。第一层是拓扑可行性断开任意一条线路后剩下的图仍然连通所有负荷节点都能通过某个电源点供电。第二层是运行可行性转供后的各支路潮流不越限节点电压在允许范围内。多数扩展规划研究只做第一层因为第二层要嵌入潮流计算计算量明显增加但实际工程里只看连通性远远不够。我自己的做法是在优化迭代过程中用第一层做快速筛选淘汰掉大量不满足N-1的个体对通过拓扑校验的候选方案再做第二层完整潮流校验。这样既保证了搜索效率又不至于让结果停留在纸面连通。1.3 扩展规划的“扩展”到底扩在哪里“扩展规划”这个说法和“新建规划”不一样的地方在于它面对的不是一张白纸而是已经有一定网架基础的系统。假设当前网络有12个节点、11条在运线路未来5年负荷增长之后有4个节点需要新增负荷接入同时有8条候选走廊可以新建线路。问的是选哪几条线路建能让新增负荷接入、满足N-1准则、投资和运行费用最小。这种带初始网架的扩展问题比单纯的新建规划更贴近实际但也更复杂。难点在于初始网架未必满足N-1你可能需要在规划中同时考虑对现有线路的扩容改造和新线路的建设决策变量的维度一下子多了一倍。我在Matlab里用二进制变量表示线路是否新建或者扩容用图论的连通性来判断N-1约束是否满足用前推回代法做潮流验证核心思路就是把工程问题拆成可以迭代求解的数学问题。2. N-1校验的数学本质先摆脱潮流计算的思维惯性2.1 我把N-1拆成什么来看N-1校验最直观的做法是枚举所有元件故障逐个重新算潮流。问题是配电网扩展规划里每个候选方案都要枚举几十条线路故障每条故障都要解一次潮流优化的外层再加上遗传算法或者粒子群几百上千次迭代计算量直接爆炸。这里必须用一个工程上的简化思路把N-1校验拆成拓扑层和运行层拓扑层用图算法快速过滤运行层才动用潮流。拓扑层的数学本质非常简单给定一个无向图G(V,E)其中V是节点集合E是支路集合有一个电源根节点r。如果删除任意一条支路e∈E之后G-e仍然是包含所有负荷节点的连通图那么这个图在拓扑层面就满足N-1。判断连通性可以用深度优先搜索、广度优先搜索或者并查集复杂度都是O(|V||E|)比潮流计算快几个数量级。配电网还有一个特殊性是正常运行状态必须保持辐射状也就是图必须是一棵树。对于一棵树删除任意一条边必然会把图分成两个连通分量其中一个分量失去电源连接。所以在辐射状网络上做N-1拓扑校验唯一通过路径就是网络本身存在冗余联络也就是正常运行是开环的树结构但故障后可以通过合上联络开关形成带环的转供路径。2.2 连通性判断的快速做法在Matlab里判断连通性我推荐用邻接矩阵加BFS的实现不需要调用复杂图论工具包。注意配电网节点数量级一般在几十到几百邻接矩阵完全存得下遍历速度也够快。具体流程是把当前网架转成邻接矩阵BFS从根节点出发如果能访问到所有负荷节点那么连通。function isConnected checkConnectivity(adjMatrix, rootNode, nLoadBus) visited false(size(adjMatrix,1),1); queue rootNode; visited(rootNode) true; while ~isempty(queue) currentNode queue(1); queue(1) []; neighbors find(adjMatrix(currentNode,:) 0); for i 1:length(neighbors) if ~visited(neighbors(i)) visited(neighbors(i)) true; queue(end1) neighbors(i); end end end isConnected all(visited(1:nLoadBus)); end这里有一个实践细节rootNode是变电站母线的节点编号nLoadBus是负荷节点的总数。如果孤立节点在矩阵初始化时就存在BFS自然访问不到会直接判不连通。这个函数只用了队列和邻接矩阵不依赖额外工具箱跑10万个个体也就几秒钟。2.3 什么时候必须回来算潮流拓扑连通不等于运行可行。故障后转供路径存在但转供经过的线路可能过载末端电压可能偏低尤其是原线路负载率接近上限的时候。所以对一个拓扑上通过N-1的候选方案我还会对最严重的几个故障场景做潮流校验。配电网潮流计算我推荐前推回代法。它只适用于辐射状网络但配电网正常运行恰好是辐射状故障后转供路径合环恢复成新的辐射状运行方式也满足前推回代法的使用条件。前推回代法的收敛性对高R/X比的配电线路比牛顿法好不需要求雅可比矩阵代码实现简单内存占用小。我在Matlab里封了一个函数输入是节点功率、支路阻抗、网络拓扑输出是节点电压和支路潮流。迭代流程很简单先假设所有节点电压为1.0标幺值从末端节点向根节点回推计算支路功率再从根节点向末端前推修正节点电压反复迭代直到两次电压差小于1e-6。对配电网级别的算例十几轮就能收敛。3. 规划模型的骨架目标函数、决策变量、约束条件的组合逻辑3.1 目标函数怎么定才贴近实际决策N-1扩展规划的目标函数不能只写投资费用。我见过不少论文把目标函数写成min f C_invest最后规划结果就是找一个最小的网架但N-1约束往往逼着它多建好几条联络线投资费用并不小。更合理的做法是把年投资费用、年网损费用和缺电惩罚费用放到一个目标里权重按工程决策偏好设置。投资费用计算方式很简单新建一条线路的投资等于线路长度乘以单位造价如果要考虑扩容就按扩容段长度乘以扩容单位造价。年网损费用用典型日负荷曲线下的线损电量乘以电价。缺电惩罚费用是在N-1故障场景下如果存在无法转供的负荷按失电量乘以单位缺电成本折算成经济损失并乘上故障概率系数。fitness investCost annualLossCost penaltyCost;注意单位量纲必须统一。我的做法是先按万元/年做归一化再把三个部分用权重系数w1、w2、w3加权求和权重由决策者根据投资回收期和可靠性要求设定。实际调参时发现缺电惩罚权重如果太低优化算法会倾向于少建联络线N-1约束形同虚设权重太高又会过度投资。我习惯先跑一组不加N-1约束的baseline用它的失负荷量作为参考再反推合理权重。3.2 约束条件的优先级和冲突关系模型的约束条件按优先级分成三组。第一组是硬拓扑约束正常运行方式必须是辐射状也就是支路数等于节点数减一且图连通。这个约束在遗传算法编码阶段就要保证单靠罚函数容易让搜索过程在大量非法解之间浪费迭代。第二组是电气运行约束各节点电压在0.93到1.07标幺之间各支路电流不超过载流量上限。超标按越限比例加重罚函数但上限设得太大也不行会让算法只盯着罚函数而忘了优化真实目标。第三组是N-1安全约束任意一条在运线路开断后剩余网络连通、所有负荷能转供、转供后的支路不过载。这组约束是计算量最大的部分不能放在适应度函数里逐项全算需要配合第2章说的拓扑快速筛选和运行层逐条精算。这里有个很容易忽略的冲突正常运行要辐射状N-1又要求有冗余通道二者的本质矛盾决定了你必须把网架建成“开环运行闭环备用”的结构。也就是说规划结果里所有新建的联络线在正常状态下是开断的联络开关只有在故障状态下才闭合转供。我在建模时用了一个开关状态向量来区分运行边和备用边备用边不参与正常运行潮流计算但参与N-1校验的转供路径搜索。3.3 一个可以落地的算例场景我用来调试代码的是一个模拟项目X的算例14个节点2个变电站电源点初始在运线路12条候选新建线路走廊8条未来负荷增长率按年8%叠加。每条候选走廊有长度、单位阻抗、载流量、造价四个参数。对这个规模决策变量个数只有8个二进制位但如果把扩容选项也放进去变量扩到14个搜索空间从256跳到16,384还是很小跑遗传算法几百代就能稳定收敛。算例规模的选择对调试很重要。我第一次是在某个高校的33节点标准系统上直接开始写遗传算法结果调试时每一次改动都要等很长的运行时间效率极低。后来换成14节点小算例一秒钟能跑几十代各种问题和思路在几分钟内就能验证完。等所有代码逻辑理顺了再回到33节点甚至更大规模几乎不需要改核心代码只是运行时间变长而已。4. Matlab代码实现的主干数据、矩阵、搜索、优化四个模块4.1 数据组织与参数矩阵Matlab代码的结构我分成四个模块数据准备、潮流计算、N-1校验、优化主循环。数据准备阶段把网络信息整理成三张表节点表、支路表、候选线路表。节点表包含节点编号、负荷功率、节点类型支路表包含首端节点、末端节点、电阻、电抗、载流量候选线路表在支路表基础上加一个建设费用字段。这里有一个特别重要的习惯所有节点编号必须从1连续编号到N中间不能跳号。我在调试时遇到过节点表里漏了某个节点结果后期邻接矩阵维度怎么都对不上BFS函数范围也写错排查了很久。建议在数据初始化之后立刻检查节点数、支路数的维度一致性用一个assert语句直接卡住代码assert(max(bus(:,1)) nBus, 节点编号不连续); assert(length(branch(:,1)) size(adjMatrix,1) - 1, 支路数与节点数不匹配);邻接矩阵的构造可以写成一个独立函数输入是支路表和一个选线向量输出是当前网架的邻接矩阵。选线向量就是优化算法中的决策变量每一位对应一条支路是否投入。这个函数在优化的每一次适应度评估里都会被调用所以实现要尽量精简。function adj buildAdj(nBus, branch, select) adj zeros(nBus, nBus); selectedBranch branch(select 1, :); for k 1:size(selectedBranch,1) nodeI selectedBranch(k,1); nodeJ selectedBranch(k,2); adj(nodeI, nodeJ) abs(1 / (selectedBranch(k,3) 1i*selectedBranch(k,4))); adj(nodeJ, nodeI) adj(nodeI, nodeJ); end end4.2 潮流计算函数与连通性判断潮流计算函数我建议按标准的前推回代实现但有一个注意点输入拓扑必须是无环的树结构。如果在含环网络里直接跑前推回代会不收敛所以函数开头要对支路数做一个判断支路数等于节点数减一才继续计算。这也再次说明N-1建模里正常运行和故障转供时使用的是两套不同的网络拓扑。我在代码里把连通性判断和潮流计算分开封装原因有二。一是连通性判断在N-1校验中会被高频调用必须轻量二是潮流计算的输入输出结构更重只在有候选方案通过拓扑筛选后调用。分开封装之后优化主循环的逻辑非常清晰。4.3 优化算法的编码和适应度函数优化算法我选遗传算法因为决策变量是0/1二进制串遗传算法天然匹配这种编码。种群规模设60到100交叉概率0.85变异概率0.05最大迭代代数200。适应度函数内部按以下顺序执行根据决策向量生成邻接矩阵检查辐射状约束通过后调用N-1校验函数然后算潮流网损最后汇总投资费用和罚函数得到适应度。N-1校验函数的流程是核心我贴一个简化版的伪代码function [pass, loadLoss] checkN1(adj, branch, select, loadBus) nBranch size(branch(select1,:), 1); pass 1; loadLoss 0; for k 1:nBranch adjTemp adj; % 删除第k条支路 nodeI branch(k,1); nodeJ branch(k,2); adjTemp(nodeI, nodeJ) 0; adjTemp(nodeJ, nodeI) 0; % 拓扑连通性快速判断 if ~checkConnectivity(adjTemp, rootNode, nBus) pass 0; loadLoss loadLoss totalLoad; continue; end % 对严重故障场景做潮流校验 % 计算转供后支路负载率、节点电压 % 如果越限则pass0并累加失负荷 end end这个函数的计算量主要在for循环里的nBranch次连通性判断。对于14节点算例nBranch大约在12到20之间一次N-1校验要跑十几二十次BFS配合遗传算法200代、种群80个个体总计算量在可接受范围内。如果节点规模到上百个就需要把N-1校验函数做向量化优化或者用并行工具箱加速。5. 实测中踩过的坑N-1扩展规划最容易翻车的四个环节5.1 节点编号不连续导致的矩阵维度对不上这是最隐蔽也最坑的一个问题。我从标准算例迁移数据时某些孤立节点编号跳过去了邻接矩阵仍是按最大编号生成的BFS函数里visited数组的长度和实际节点数不一致。表面上看代码没报错但连通性判断永远返回false所有方案都判不满足N-1遗传算法里的适应度全部被罚函数主导搜索直接退化。建议在数据处理后加一个维度校验同时把根节点编号从矩阵最大索引改成显式输入变量。另一个相关问题是负荷节点和纯联络节点的区分。有些节点没有任何负荷N-1校验时不需要保证它们全部连通BFS判断的目标节点集合要按负荷节点列表提取而不是所有节点。5.2 连通性判断里被忽略的电源方向配电网可以有多个变电站电源点正常运行时各电源点分区分片供电。N-1故障后某个电源点可能短时退出剩余电源点要承担全部负荷。如果连通性判断只以某一个根节点出发做BFS就可能得出错误结论。我的做法是把所有电源点都作为BFS的起始节点集合同时压入队列只要负荷节点能被任意一个电源点连通就认为拓扑上满足N-1。这个细节在单电源配网里无所谓但双电源甚至多电源网架里必须改。否则你规划出来的所谓N-1网络其实只保证了某个电源点的连通真正故障发生在另一个电源点时系统照样丢失负荷。5.3 N-1约束过强导致规划结果过于保守把N-1作为硬约束放进模型之后我第一次跑出来的结果让我怀疑代码写错了所有候选线路几乎全被选中投资费用直冲上限N-1全部通过但工程上这显然不现实。回头看问题出在N-1约束对每条线路都要求100%不失负荷且没有区分故障概率。一条寿命末期、故障率极高的老旧线路和一条全新线路在N-1校验里地位相同。解决思路是引入选择性N-1或者把N-1约束从“必须全部满足”调整为“允许少量失负荷但限定在可接受范围”。我在项目里采用的做法是对负荷密度低的末端线路允许N-1故障后损失不超过该线路峰值负荷的10%对核心区线路则坚持严格N-1。这样既保证了关键用户供电可靠性又不会导致规划方案投资失控。5.4 初始种群全部不满足约束导致搜索停滞遗传算法的初始种群如果随机生成大概率大多数个体连辐射状约束都满足不了。配电网辐射状要求支路数等于节点数减一且连通随机选边的结果要么边太少导致大量孤立节点要么边太多直接成环。初始种群几乎全部非法适应度全是罚函数选择压力不对算法很难找到好的区域。我的解决办法是用启发式初始化先用Prim算法或广度优先生成一棵最小生成树保证基本连通再随机叠加一些候选联络线作为N-1冗余。这样生成的每个初始个体天然满足辐射状运行条件N-1满足与否交给后续进化改进搜索效率和收敛质量明显提升。6. 从我实际项目的经验看结果解读与后续扩展6.1 判断结果是否合理的几个指标代码跑通只是第一步规划结果合不合理需要从多个维度判断。我一般看四组指标一是投资总额和单位新增负荷投资二是所有线路负载率的均衡度三是N-1故障场景下的最大失负荷量四是系统网损率变化。只用投资额一个指标评价方案会掩盖很多问题比如可能出现某条主馈线负载率高达95%一旦故障就大面积停电的隐患。均衡度指标可以用负载率方差来量化各线路负载率差别越小说明网络容量分配越合理N-1转供的裕度分布越均匀。我改造过一版代码把负载率均衡度作为第二目标函数用多目标遗传算法同时优化投资和均衡度规划出来的网架明显更稳。6.2 从静态N-1走向N-1-1和时序模拟标准的N-1只考虑任意单一元件故障但实际运行中双回线路同塔故障、主变检修时另一台主变故障这类N-1-1场景也会发生。把N-1校验从单重故障扩展到双重故障计算复杂度呈指数级上升一般不会放到优化模型里但我建议至少做一次规划完成后的N-1-1场景离线校核。如果校核结果不满足可以根据具体薄弱环节对规划结果做小范围修正。另外分布式电源大规模接入之后配电网的N-1行为会变得更加复杂。光伏和储能的出力随天气波动故障后可能提供转供支撑也可能因为保护动作直接脱网。后续扩展可以考虑把分布式电源出力场景建模为多状态问题在N-1校验时分别按最大出力和零出力两种极端情况校核保证方案在不确定性下仍然可靠。这块我还在继续做等数据跑完整理好再单独写一篇分享。做N-1扩展规划最大的体会是不要一开始就追求复杂的优化算法和完备的约束先用小算例把N-1校验函数跑正确、跑透彻再逐步把规模放大、把约束加细。代码层面多做模块化封装数据组织做好维度校验能省去后面大量排错时间。如果你最近也在做类似的配电网规划仿真欢迎照着上面的代码思路先搭一版跑跑看遇到具体问题我们再一起讨论。