
接到一个配电网N-1扩展规划的需求第一反应往往是这不就是把每条线依次断开算个潮流吗真动手做才发现问题远不止校核那么轻巧。中压配电网规划里N-1准则指的是任一馈线、主变压器或开关设备退出运行后系统仍能通过网架重构与负荷转供保证全部或重要负荷供电而扩展规划则是在这个前提下确定新增线路的位置、型号和投产时序。这类问题一旦进入Matlab建模就同时撞上0-1整数规划、多场景约束、辐射状拓扑校验、求解器选型等一堆硬骨头。这篇文章把我从理论理解、数学模型搭建到代码调试的完整路径记录下来写给配网规划工程师、电力系统方向研究生以及所有准备用Matlab做网架优化的人。我会尽量把每个决策背后的理由讲清楚而不是只甩一段能跑但看不懂的程序。1. 配电网N-1扩展规划到底在研究什么概念与应用场景1.1 N-1准则的本质配电网的“单点故障不掉链子”底线传统认知里N-1准则似乎更多属于输电网。输电网强调网架坚强任意一条线路或一台主变跳闸后系统还能稳定运行负荷不损失。配电网长期采用“闭环设计、开环运行”大多以单电源辐射状网络轻载运行天然缺少转供通道以前对N-1的校核并不严格。但随着城市核心区供电可靠性要求不断升高以及大量分布式电源、充电桩接入带来新的负荷形态配电网的N-1能力已经成为不少电网公司可研评审的硬性门槛。需要注意的是配电网的N-1和输电网的N-1在工程语义上并不完全一样。输电网关心暂态稳定、断面潮流转移和电压稳定而配电网更关心开断一个元件后被甩开的负荷能否通过另一条路径重新带上电。这意味着规划阶段就要回答三个问题断开的元件是哪一个转供路径存在吗转供路径上有没有容量余量很多新建和改造方案图纸看着很漂亮一到N-1校验就暴露出联络线容量不足、末端线路载流量卡脖子、分段开关位置不合理等问题根源就是规划阶段没有把这些“故障状态”一并放进优化模型里。1.2 传统网架规划与N-1扩展规划的本质差异传统配电网规划一般只考虑正常运行方式下的单一场景用负荷预测结果去推导线截面和线路路径目标是满足各节点负荷需求、降低网损、控制短路电流水平。这种做法在负荷增长平缓、供电面积不大的年代没有问题但放到今天负荷密度高、供电走廊紧张、新能源反向送电频繁单一场景的优化结果往往经不起故障态的推敲。N-1扩展规划的核心差异在于它把“正常运行状态”和“每一个元件退出运行后的故障状态”作为一个联合优化整体。正常运行状态决定网络的基础接线故障状态约束反过来限制基础接线。最典型的情况是正常态下两条馈线各自带自己的负荷看起来干净利落但如果其中一条馈线检修另一条馈线并没有足够的备用容量把负荷全部转带那么方案就不能通过N-1校核。于是规划模型必须在正常状态基础上为每一条馈线预留给对侧的联络通道要么新建一条联络线要么加大某一段线路截面。两类规划问题的对比可以这样看对比维度传统网架规划N-1扩展规划考虑场景数通常1个正常态正常态多个故障态决策变量线路新建/改造与否线路新建/改造故障态转供状态约束规模节点平衡、载流量、电压节点平衡、载流量、电压、辐射状、故障连通性求解难度小规模时可用启发式大规模时必须用MILP或分解算法产出结果一组新建线路新建线路故障态转供路径方案1.3 谁需要这套方法和代码工程师、研究生还是算法爱好者从我这几年接触的情况看需要N-1扩展规划Matlab代码的人群大致分三类。第一类是设计院和地市供电公司的规划专责他们通常面对的是具体工程某个开发区要新增负荷现有10kV线路重载需要确定新建线路和联络开关位置让方案通过评审。这类人群最需要的是能直接改数据、快速出图、导出N-1校核表格的工具而不是复杂的算法论文复现。第二类是高校电力系统方向的研究生。他们研究配电网规划、网架重构、可靠性提升经常需要把一个数学模型落地成可运行的代码用于验证论文中的方法。这类人群更关心模型扩展性比如加储能、加分布式电源、改成多目标优化因此代码结构要清晰变量和约束条例分明。第三类是刚接触Matlab优化建模、想用YALMIP或intlinprog解决实际问题的开发者。他们可能从输电网项目转过来对配电网运行方式不太熟悉容易把辐射状运行约束、N-1开断逻辑搞错。下面从数学模型开始一步步把整个问题打开。2. 数学模型怎么建目标函数、约束条件与N-1场景枚举2.1 目标函数投资成本、运行费用与可靠性代价的权衡N-1扩展规划的目标函数最简单的形式是年费用最小化把一次性投资折算成等年值再加上年网损费用。写出来大致是目标 新建线路投资的等年值 年网损费用 惩罚项投资等年值通常用资金回收系数γ计算γ r(1r)^n / ((1r)^n - 1)其中r为贴现率n为设备使用年限。比如贴现率取8%线路使用期按20年算γ大约为0.10185意味着投资100万元等价于每年支出约10.185万元。这个折算很有必要否则年份不同的投资方案没有可比口径评审时也容易产生争议。网损费用的计算采用简化年损耗小时数法即单位电价乘以最大负荷损耗小时数τ再乘以规划年的峰值网损功率。规划模型一般用线性化潮流近似所以网损本身也做了线性化处理只保留线路电流平方项的一阶近似或直接忽略。若想让结果更贴近实际可以在优化结束后用交流潮流对方案做一次复校把误差控制在规划阶段可接受的范围内。惩罚项通常对应两类情况一类是N-1校核不通过时需要对应切除的负荷价值用单位缺电成本表示另一类是目标函数里硬性约束无法满足时用松弛变量让模型先“可解”再从松弛量的数值判断哪条约束卡得最紧。在实际代码里缺电成本往往设置得远高于线路投资成本比如按每千瓦时几十元甚至上百元计这样才能保证优化结果优先满足N-1而不是优先省钱。2.2 正常运行工况约束线性化潮流与节点功率平衡配电网规划阶段节点功率平衡约束是基础中的基础。典型写法是对每个节点所有流入该节点的线路功率和该节点电源注入之和等于该节点负荷。线路功率受到容量上限约束同时满足允许电压偏移范围。为了能交给整数规划求解器处理通常采用线性化的DistFlow模型或者直流潮流模型。雖然直流潮流在输电网更常见但在配电网规划里作为“初步选线”工具也被广泛使用原因是中压配电网线路较短、压降主要受线路电阻影响纯直流模型会忽略无功和电压幅值因此更严谨的做法是用线性DistFlow。线性DistFlow的核心是把支路功率方程简化为支路首端流出的功率等于末端节点负荷与下游支路功率之和再对每条支路添加容量限制并对节点电压偏移添加近似的线性约束。这样做避免了潮流迭代和三角函数项让整个模型变成一个标准的混合整数线性规划。学过电力系统分析的人会问这样一来电压精确吗答案是不精确但规划阶段看的是“网架结构是否具备N-1转供能力”电压校核完全可以放到后续的潮流复核环节去完成否则模型维数会失控。2.3 辐射状约束单商品流还是生成树配电网正常运行时必须保持辐射状这既是继电保护配合的需要也是限制短路电流的手段。在N-1扩展规划里每个故障场景下的临时转供网络也必须保持辐射状否则合环运行会产生保护误动风险。辐射状约束的建模方式有几种我推荐的是单商品流法。单商品流法的思路很直观把网络想象成一个物流系统根节点向每个非根节点配送1单位虚拟商品每个节点需求恰好为1单位那么任何连通而且不含环的网络都能满足流量平衡。若网络中出现了环必然存在某个环内商品流无法同时满足平衡条件从而被约束排除。实现时给每条线路设一个虚拟流变量方向可正可负容量约束用一个大M值协调线路状态。根节点的虚拟注入为节点总数减1其余节点虚拟注入为-1。对每个运行状态包括正常态和每个N-1场景独立建立一组虚拟流约束。这种方法的优点是表达简单、求解器识别友好缺点是会产生大量辅助变量。每增加一个场景就增加线缆数倍的连续变量和约束当候选线路很多时模型规模会迅速膨胀。另一种生成树约束cutset等式变量更少但需要枚举节点子集在代码实现上麻烦不少大规模工程中很少从零手写。因此单商品流是平衡了易实现性和求解性能的折中选择。2.4 N-1场景枚举把“故障态”也写进优化模型N-1校验对象可以是馈线段、主变、母线甚至是一组接线单元。扩展规划阶段最常用的是对已有线路和候选线路逐条开断。对于每个开断元件k建立一个场景s_k场景中强制该元件退出运行然后要求其余已有线路和候选线路可以通过状态变量重新组合出一个辐射状网络满足故障态负荷供电要求。这里有个容易混淆的点正常态下选定的网架是“基础网架”但故障态下为了实现转供模型允许临时断开原本接通的线路也允许临时接通原本未建的候选线路吗答案取决于工程设定。通常在规划模型中候选线路的建设状态是全局决策变量场景s中能否使用它由两个变量共同决定建设状态x全局不变和场景投入状态y_s运行决策。只有建设了场景中才可能投入。而已有线路的建设状态恒为1但在场景中可以因故障或转供需求而断开。这样处理既符合实际又自然生成了转供方案。N-1场景约束和正常运行约束之间通过全局建设变量紧密耦合。正常态决定哪些线路存在故障态决定这些线路怎么用。缺少任何一个状态模型就会偏向过于乐观或不切实际的方案。这也解释了为什么N-1扩展规划模型规模比普通规划大一个数量级正常态一组约束、每个N-1场景一组约束还要加上连接两层变量的大M约束和辅助变量。3. Matlab代码实现要点数据结构、YALMIP建模与求解流程3.1 输入数据结构怎么设计节点、线路、负荷的定义方式写Matlab代码之前先把数据格式定下来这一步决定了后面所有代码的简洁度。我习惯用两个结构体数组存数据node和line。node结构体每个元素对应一个节点包含节点编号、坐标、有功负荷、无功负荷以及节点类型。负载数据可以是规划年的峰值负荷也可以是逐年负荷序列。若做的是单阶段规划直接用峰值负荷即可。node(1)通常是上级电源节点电压等级高作为整个网络的平衡节点和单商品流的根节点。line结构体每个元素对应一条线路包含首端节点、末端节点、线路电阻电抗、载流量上限、当前是否存在、候选建设造价。线路数据一般来自GIS台账或现场勘测使用Excel维护比较方便Matlab里用readtable批量读取然后转换为结构体或矩阵。下面是一个简化的数据结构示意字段名含义数据类型node.id节点编号doublenode.pLoad节点有功负荷doublenode.qLoad节点无功负荷doubleline.from首端节点编号doubleline.to末端节点编号doubleline.r线路电阻doubleline.x线路电抗doubleline.capacity载流量上限doubleline.exist是否已有线路logicalline.cost候选线路造价double保存数据时建议把已有线路和候选线路放在同一个line数组里用exist字段区分。这样在写约束时仅需判断isExist即可决定该线路的建设状态是否固定为1无需额外维护两套线路索引。这个设计能减少很多烦人的下标错位问题。3.2 为什么用YALMIP而不是手写intlinprogMatlab自带的intlinprog可以求解混合整数线性规划理论上足够处理N-1扩展规划。但实际写起来非常痛苦所有约束要手动拼成A*xb的标准形式场景一多矩阵维度稍有变化就得重写全盘。我强烈建议用YALMIP。YALMIP是一个建模层可以让你用符号变量和约束列表直接描述优化问题底层统一调度CPLEX、Gurobi或intlinprog求解器。YALMIP安装很简单下载压缩包解压后在Matlab里把文件夹加入路径运行savespath即可。求解器方面如果只有教育版Matlab可以先用intlinprog兜底如果手上有CPLEX或Gurobi授权则优先选择后者性能差别在候选线路超过30条时会非常明显。YALMIP的优点不只是语法简洁更重要的是它天然支持约束的循环追加。N-1场景约束是“同一模式重复很多次”的结构用YALMIP可以很方便地通过for循环累积到同一个Constraints变量里。而手写intlinprog时每个场景都要重新定位矩阵的行号、列号排查bug的时间会淹没建模时间。3.3 核心代码框架变量定义、约束循环与求解调用先给出主程序框架。注意下面的代码只体现核心结构完整数据读取和参数设置请参考你自己的工程文件。% 配电网N-1扩展规划主程序框架 % 使用YALMIP建模求解器建议gurobi / cplex / intlinprog loadData(); % 载入node和line数据并在工作区生成node,line变量 nNode length(node); nLine length(line); existFlag [line.exist]; candIdx find(~existFlag); % 候选线路索引 nScenario nLine; % 每条线路开断对应一个N-1场景 % 变量定义建设状态x为nLine维0-1变量正常态线路状态为0-1变量 x binvar(nLine,1); % 1表示线路建成投运 y0 binvar(nLine,1); % 正常态线路投入状态 P0 sdpvar(nLine,1); % 正常态线路有功功率 f0 sdpvar(nLine,1); % 正常态虚拟流 Constraints []; % 正常态约束 Constraints [Constraints, y0(existFlag 1) 1]; % 已有线路正常运行 Constraints [Constraints, y0(candIdx) x(candIdx)];% 候选线路只有建成才能投入 Constraints [Constraints, P0 lineCap .* y0]; % 容量约束 Constraints [Constraints, ... 节点功率平衡 ...]; % 自行补充 Constraints [Constraints, ... 单商品流辐射状约束 ...];进入N-1场景循环前定义每个场景下的状态变量。为了清晰我采用元胞数组存储场景变量yS cell(nScenario,1); PS cell(nScenario,1); fS cell(nScenario,1); for s 1:nScenario yS{s} binvar(nLine,1); PS{s} sdpvar(nLine,1); fS{s} sdpvar(nLine,1); % 场景s中被开断的线路强制退出 Constraints [Constraints, yS{s}(s) 0]; % 未开断的已有线路允许运行也可断开展开转供 otherExist find(existFlag 1); otherExist(otherExist s) []; Constraints [Constraints, yS{s}(otherExist) 1]; % 候选线路仍然受建设状态约束 Constraints [Constraints, yS{s}(candIdx) x(candIdx)]; % 容量与功率平衡约束 Constraints [Constraints, PS{s} lineCap .* yS{s}]; Constraints [Constraints, ... 场景s节点功率平衡 ...]; Constraints [Constraints, ... 场景s辐射状约束 ...]; end需要注意的是容量约束写成PS lineCap .* yS其中的乘法是逐元素乘法YALMIP会将其转化为大M约束。如果lineCap是0则对应线路功率必须为0这与线路退出状态一致。对于被开断的线路由于yS(s)0功率自动被限制为0不需要额外判断。目标函数和求解调用比较简单% 目标等年值投资 网损 investCost sum(x(candIdx) .* lineCost(candIdx)); energyLoss C_loss * tau * sum( P0 .^ 2 .* lineR ./ lineV .^ 2); Objective gamma * investCost energyLoss; % 求解设置gap设小一点有助于提高最优性 ops sdpsettings(solver,gurobi,verbose,2,gurobi.MIPGap,0.0001); sol optimize(Constraints, Objective, ops); % 输出结果 xOpt value(x); y0Opt value(y0);这里有三个细节值得强调。第一P0.^2是二次项会导致模型变成MIQP而非MILP如果没有二次规划求解器就把网损项做分段线性化或忽略规划阶段问题不大。第二大M约束里的M值选取很关键若取得过大求解器数值稳定性会变差若取得过小可能错误限制线路功率。通常取该线路容量上限的1.1倍比较合适。第三输出求解结果前务必检查sol.info是0还是非0。3.4 求解器配置与运行流程从建模到出结果的一次完整跑通求解配置的实操顺序是先确认Matlab工作目录正确再加载YALMIP调用ops时把solver指定为实际可用的求解器不确定时可以用[]让YALMIP自动选择。若运行提示找不到求解器多半是YALMIP路径没设好或求解器未加入Matlab路径。跑通一个小算例的完整流程大概是加载数据 - 定义变量 - 构建正常态约束 - 循环构建N-1场景约束 - 设定目标函数 - 优化求解 - 提取并保存结果。建议一开始不要追求完美目标函数先用最小投资作为目标跑通后再加入网损项这样排查问题时有清晰的起点。4. 算例验证6节点系统从数据到规划结果的全过程4.1 一个可以直接抄的6节点算例数据为了验证代码逻辑我设计了一个6节点中压配电网算例。节点1是上级电源点节点2到节点6为负荷节点所有线路电压等级10kV。已有线路三条1-2、2-3、1-4。候选线路五条2-4、4-6、3-5、5-6、2-5。数据如下。节点负荷数据节点类型有功负荷(kW)无功负荷(kVar)1电源002负荷8003003负荷6002504负荷10003505负荷7002506负荷900300线路数据可自行按工程习惯设定。这里给一组参考值线路阻抗按0.20.3j欧姆/km计算已有线路长度取2km候选线路长度在1.5km到3km之间载流量上限设为5MW候选线路造价按长度折算每公里约80万元。这个小算例的妙处在于节点3和节点6相对孤立一旦1-2或1-4中的某一条检修必须立即找到另一条路径否则必然损失负荷。4.2 规划结果对比基础规划与N-1扩展规划究竟差在哪里先做基础规划目标为最小投资约束只有正常态功率平衡和辐射状不加入N-1场景。这种情况下优化结果会倾向于只建设最便宜的线路把5个负荷节点联通即可典型的解可能是建设2-4和4-6让全部负荷在正常态下由节点1经1-4转供。这样投资很低但故障态非常脆弱断开1-4后节点4、6直接失电断开1-2后节点2、3失电。再加入N-1场景后模型不得不为每一个故障场景寻找可行的转供路径。以断开已有线路1-4为例为保证节点4、5、6供电至少需要从节点2或者节点3侧引入通道。于是可能的结果是新建2-5和4-6形成1-2-5-4-6的通路节点3则由2-3供电。对应地断开3-5场景时节点5和6还可以通过4-6由节点4转供。这样一套方案投资明显高于基础规划但每条线路故障后都不会有负荷被甩开。对比结果可以用表格直观呈现方案类型新建线路组合等年值投资(万元/年)能否通过全部N-1校验基础规划2-4、4-6较低否多个故障场景失负荷N-1扩展规划2-4、2-5、4-6较高是所有开断场景均满足供电从实际工程角度看N-1方案多出的投资本质上是在购买“每一段线路故障时的备用通道和备用容量”。评审会上如果只对比投资额N-1方案肯定吃亏但结合每个方案的供电可靠性指标和缺电损失期望情况往往反过来。规划人员需要处理的就是这样一个“多花钱买一张安全网”的决策问题。4.3 从6节点扩展到33节点数据准备和结果判读的注意点6节点只是验证代码逻辑真正看书、写论文可能要用IEEE 33节点这种典型算例。幸运的是代码不需要改逻辑只需要替换数据文件。IEEE 33节点系统的节点坐标、负荷、支路参数在相关文献中很容易查到导入时留意首端电源节点编号、负荷单位是kW还是MW、线路容量是否需要折算这三点是换数据时最常出错的地方。跑完33节点模型先看求解器退出信息再检查各场景的转供路径最后看新建线路集合和投资费用。有一个容易被忽略的点33节点中有联络开关构成的闭环在规划模型里要小心处理。如果把这些联络开关当作普通线路则正常态辐射状约束会自动要求它们断开N-1场景中它们可以作为转供通道投入这与实际运行逻辑完全一致。5. 调试教训求解慢、无解、辐射状失效这些坑怎么填5.1 模型报“无解”时先检查是不是N-1场景自身矛盾新手最崩溃的是N-1约束一加上去模型立刻无解。我的排查经验是先手动分析一个最简单的N-1场景断开某条已有线路S之后剩余网络在跑通优化之前是否还连着一个根节点如果被开断线路把某个负荷块变成了孤岛而这个孤岛内没有候选线路能够连接回电源那么任何求解器都救不了你必须修正输入数据。把模型简化到只保留这一个场景去掉其他场景约束用线性规划求解器解一下往往几秒钟就能暴露出问题所在。如果单个场景可行再逐步增加其他场景二分定位到是哪一个场景引发了不可行。配合YALMIP的check指令逐条检查约束余量效率会高很多。5.2 辐射状约束失效环网和孤岛同时存在的经典错误辐射状约束出错时结果网络会出现环甚至孤岛。单商品流法出错最常见的原因是根节点序号设错或者虚拟流约束里方向符号写反。另一个原因是候选线路建设状态x和场景投入状态y_s之间的大M约束遗漏了正常态下线路没有建设但场景里却因为y_s为1而强行投入虚拟流在这里绕过约束导致孤岛也能形成连通伪像。调试这类问题建议把优化后的y0和yS矩阵打印出来逐个场景数一数投入线路的数量。正常态如果是6节点投运线路必须是5条否则一定不满足生成树条件。故障场景中投运线路数必须小于等于5条且节点之间连通。直观的人工检查往往比看求解器日志更快。5.3 求解时间爆炸整数变量多、场景多时的降维手段N-1扩展规划模型对规模和求解时间极其敏感。每条候选线路增加若干个0-1变量每个N-1场景又复制一遍状态变量30条候选线路配上30个场景整数变量轻轻松松上千。如果求解器长时间无法收敛可以从三个方向优化。第一预筛选候选线路。在进入MILP之前用一个连通性算法或最大流检验排除掉那些在建与不建对N-1毫无帮助的线路通常能减少大量无意义变量。第二设置合理的MIP gap。工程规划不需要证明全局最优0.5%的gap足够满足评审要求盲目追求gap0会跑很久。第三用Benders分解思想把N-1场景约束从主问题中释放逐步添加被违反的场景也就是“延迟约束生成法”。这个思路我在实际项目里试过效果显著但写代码前要对YALMIP和求解器接口足够熟悉。5.4 YALMIP与求解器联调的常见报错和处理办法YALMIP最常见的两个报错是“No suitable solver for this problem”和“Solver not found”。前者说明模型里存在求解器不支持的约束类型比如二次项、绝对值、逻辑与约束需要检查目标函数和约束中是否混入了非线性项。后者通常是路径问题安装好CPLEX或Gurobi后没有把它们的接口文件夹加入Matlab路径。还有一个非常隐蔽的错误变量定义时用了sdpvar但求值时误用value单变量对应而实际上变量是矩阵或向量。YALMIP里size不一致时不会立即报错而是在value输出后通过plot或表格检查时才发现结果完全错位。每次求解结束后建议立即打印value(x)和value(y0)的维度防患于未然。我个人调试这段代码时最大的体会是N-1扩展规划的问题本身不难理解难的是把“故障场景”和“正常场景”两个层次的变量关系梳理清楚。如果一开始就把正常态和故障态的状态变量混用后面所有求解结果都会被认为是“不可行”然后你会怀疑模型、怀疑求解器、怀疑自己结果只是那个大M的取值少乘了一个0。建议在写代码时让每个场景的变量都独立命名约束分块注释跑通后再考虑优化合并。后续如果你想把储能容量配置、分布式电源选址定容、甚至是多目标可靠性优化加进来这套N-1扩展规划代码完全可以作为底座只是约束和决策变量的数量会再上一个台阶届时优先考虑分解算法而不必执著于单层MILP。