
配电网故障重构这个话题做配网自愈和主动配电网的人这两年应该都不陌生。说白了就是某条馈线上出了永久性故障之后通过拉开故障段两端的开关、合上联络开关把非故障失电区域倒到其他馈线上继续送电。我以前用传统启发式算法做得比较多最近换了个思路用Matlab加YALMIP把问题建模成二阶锥规划SOCP来求解跑完33节点算例之后整体感觉是松弛精度够用、求解速度快、代码还特别好维护。这篇文章就把这套建模流程和踩过的坑整理一下给做配电网重构、故障恢复、自愈控制方向的朋友做个参考也算给自己留个备忘。文里的重点不是推导某个公式而是把“从问题描述到可运行代码”之间那段经常没人讲清楚的细节补全。1. 项目整体思路把故障重构拆成数学问题1.1 故障重构的本质开关倒闸与负荷恢复配电网故障重构本质上是一个“离散拓扑寻优 连续潮流计算”混合的问题。故障发生后系统里有两种开关分段开关正常时闭合和联络开关正常时断开。故障重构做的事就是改变这些开关的通断组合让失电区域从故障馈线上脱离由相邻馈线转供。拿一个典型场景来说某10kV馈线的第5段线路发生永久性短路故障故障点两侧的分段开关跳开中间那段被隔离但故障点下游还有一大片负荷本来可以由相邻馈线带着走。此时如果合上某个联络开关把下游区域挂到邻居馈线上那这些用户就能继续用电。问题在于变电站容量有限、线路载流量有限、电压跌落不能超限到底合哪个联络开关、断开哪些分段开关才能既恢复最多负荷又让网损尽量低这就是故障重构要解决的问题。从数学上看这是一个混合整数非线性规划MINLP。里面有0-1变量开关状态又有连续的电压、电流、功率变量而且潮流方程本身是带平方项的等式约束。如果直接丢给非线性求解器小系统凑合能算规模一大或者开关组合一多就非常痛苦。33节点系统大约有37个可操作开关理论上有2的37次方种组合穷举的念头最好早点打消。1.2 为什么选二阶锥而不是传统启发式以前做配网重构大家用得最多的还是遗传算法、粒子群、模拟退火这类启发式算法。我也写过不少但有一个很难受的问题每次迭代都要反复调潮流而且算法的随机性导致多次跑的结果不稳定参数稍微调一下解就变了。更麻烦的是启发式算法只能给你一个“看起来还行”的解很难告诉你这个解距离最优到底有多远。二阶锥规划SOCP的思路完全不同。它先把非凸的潮流方程做松弛处理把一个MINLP问题转换成混合整数二阶锥规划MISOCP。这个新问题虽然是混合整数但连续部分已经是凸的可以用分支定界加内点法这类成熟框架求解商业求解器比如Gurobi、CPLEX、Mosek都能直接处理。关键点在于对于辐射状配电网的DistFlow模型这个二阶锥松弛在绝大多数实际场景下是精确的也就是说松弛后的最优解就是原问题的最优解或者非常接近。这就把“随机搜”变成了“确定性求解”每次跑出来的解都是可重复的而且求解器会给出一个最优性间隙你能明确知道当前解距离理论最优有多远。为什么不直接用半定规划SDPSDP对配电网潮流方程也能做松弛而且精度有时候比SOCP还好但计算代价高很多。配网重构本身就是一个混合整数问题整数变量一多SDP那层半定矩阵变量会让求解规模膨胀得非常快跑起来慢得让人怀疑人生。SOCP是精度和速度之间的经典折中配合配电网本身的辐射状结构足够用了。1.3 建模闭环潮流、拓扑、目标函数三者耦合刚开始做的时候容易犯一个毛病就是只顾着把潮流方程松驰好忽略了拓扑约束结果算出来的“最优方案”根本没法在现实里操作——要么出现环网要么出现孤岛。后来我把建模拆成三条线对着三条线逐个检查思路就清楚多了第一条线是物理约束。DistFlow方程、节点电压上下限、支路电流上限、变电站出口功率不能超容量这些是任何可行方案都必须满足的底线。第二条线是拓扑约束。网络必须保持辐射状不能有环而且除变电站节点外所有节点都得连通故障支路必须从拓扑里强制清除。这里有一个很关键的坑光约束“闭合支路数等于节点数减一”是不够的因为在一个不连通的网络上这个等式同样可能成立必须额外加连通性约束。第三条线是目标函数。故障重构的目标不是简单的“网损最小”。在失电场景下恢复负荷才是第一优先级网损是第二优先级。所以目标函数里通常要同时包含网损项和切负荷惩罚项两者之间的权重设置直接决定优化结果偏向哪一头。这三条线的约束在Matlab里被集中写进YALMIP的约束数组里表面上看是一大段约束实际上每一条都能在原始物理问题里找到对应。后面凡是遇上求解不可行或者解不合理我都会先对着这三条线逐个排查效率比瞎调参数高得多。2. 二阶锥建模的关键细节松弛与约束处理2.1 DistFlow 方程的 SOCP 松弛配电网潮流方程通常写成支路潮流形式也叫DistFlow方程。每条支路k连接节点i和节点j方向上定义了有功P_ij、无功Q_ij、电流平方L_ij、电压平方V_i和V_j公式长这样P_ij - r_ij * L_ij 节点j下游所有支路有功之和 节点j的负荷有功Q_ij - x_ij * L_ij 节点j下游所有支路无功之和 节点j的负荷无功V_j V_i - 2(r_ij * P_ij x_ij * Q_ij) (r_ij^2 x_ij^2) * L_ijL_ij * V_i P_ij^2 Q_ij^2前三行都是线性的麻烦出在第四行。L_ij * V_i P_ij^2 Q_ij^2 是一个二次等式非凸直接写在模型里会让整个问题变成非凸的MINLP。二阶锥松弛的思路就是把这个“等式”放松成“不等式”L_ij * V_i P_ij^2 Q_ij^2。这个不等式的含义很直观支路电流不能小于承载当前功率所需的最小电流。在辐射状配电网中大部分情况下最优解会自然压到这个不等式边界上也就是等式成立所以松弛是精确的。把这个不等式改写成标准二阶锥形式常见写法是|| [ 2P_ij; 2Q_ij; L_ij - V_i ] || L_ij V_i在YALMIP里直接写成cone约束其实很简洁Constraints [Constraints, cone([2*P(k); 2*Q(k); I2(k) - V(i)], I2(k) V(i))];这个式子我第一次看到的时候总觉得哪里别扭后来验算了一遍才踏实。把两边平方展开左边等于4P²4Q²(L-V)²右边等于(LV)²一整理就得到P²Q² L*V跟前面那个不等式完全等价。这段推导建议读者自己动手写一次能避免后面很多符号方向上的低级错误。2.2 辐射状拓扑约束的 YALMIP 实现拓扑约束是配网重构里最容易写错的部分。我的经验是采用“闭合支路数固定 单商品流连通性”的组合方案。闭合支路数约束很好写。假设系统里有nl条候选支路全部节点数nb那么闭合支路总数必须等于nb-1Constraints [Constraints, sum(z) nb - 1];但只有这一条还不够。若网络被切成两个互不相通的子网比如左边5条支路、右边6条支路总数依然是nb-1但右边的负荷根本送不进去。解决办法是引入一组虚拟潮流变量f每条支路对应一个模仿“商品”从变电站流向所有负荷节点。对每个非根节点来说流入这个节点的虚拟商品量应该刚好是1代表该节点获得一份供电同时对每条支路虚拟潮流只允许在支路闭合时存在。% f(k) 为支路k上的虚拟潮流z(k)为开关状态 Constraints [Constraints, -M_f * z(k) f(k) M_f * z(k)]; % 对每个非根节点 j所有流入该节点的虚拟潮流之和等于 1 % 这里需要用支路-节点关联矩阵Incidence 来写避免循环里重复叠加 for j 2:nb Constraints [Constraints, sum(f(find(branch(:,2) j))) - sum(f(find(branch(:,1) j))) 1]; end这段代码的思路是把每个节点的“净流入虚拟潮流”固定为1变电站节点根节点不做这个约束。配合闭合支路数nb-1就能同时保证网络连通且无环。这里有两个容易踩的坑第一个坑是M_f的取值。这个大M不需要太大因为虚拟潮流只是拓扑连通性的辅助变量不是真实功率。我一般取nb就够取太大反而会松弛分支定界拖慢求解速度。第二个坑是关联矩阵方向。上面代码里默认了branch第一列是支路起点第二列是支路终点如果你的数据定义顺序和这个不一致约束会完全反掉导致所有支路都断开求解器直接报不可行。我吃过这个亏排查了很久才发现是数据方向写反了。另外故障支路需要强制断开Constraints [Constraints, z(fault_line) 0];这是故障重构里和普通配网重构最大的区别。普通重构是找最优拓扑故障重构是在“故障支路已断开”的前提下找可行拓扑这个约束加不加结果天差地别。2.3 求解器选型与性能取舍在Matlab里做二阶锥建模我通常用YALMIP做建模层后面接Gurobi或CPLEX求解MISOCP。YALMIP的好处是约束写起来和数学表达式几乎一一对应调试起来非常直观。求解器方面Gurobi和CPLEX对二阶锥分支定界的支持已经非常成熟选哪个都不算错。Mosek对锥规划的支持也很扎实但我在混合整数场景下用得少一些。实际上手之后会发现求解器的参数设置比选哪家品牌更影响体验。我自己常用的几个设置MIPGap设到0.5%甚至1%。故障重构工程上不需要千分之一的最优性证明0.5%的间隙跑的飞快省下的时间可以多做几组故障场景对比。让求解器用并发模式。Gurobi里并发多线程对MISOCP有一定加速但也不是绝对算例规模小的时候反而有线程调度开销。给0-1变量设置初始值。基于原始运行状态除了故障支路外其他开关状态大概率保持原样用YALMIP的assign函数给z一个合理的初始解能明显加快分支定界收敛。还有一点YALMIP的版本和求解器接口一定要匹配。老版本YALMIP对二阶锥的原生支持存在一些问题升级到新版之后cone约束的传递和求解器原生二阶锥指令的对接会顺畅很多。如果发现求解器输出的日志里根本没有出现锥规划相关的迭代信息多半是YALMIP把锥约束转成了某种内逼近形式这会影响松弛精度值得留意。3. Matlab仿真算例从33节点模型到故障场景设计3.1 算例参数与故障场景设置仿真算例我选的是IEEE 33节点标准配网系统这个系统在配电网研究里几乎属于“默认实验平台”数据公开、规模适中做故障重构验证非常合适。标准33节点系统有32条分段支路加5条联络开关总共37条候选支路。基准电压12.66kV基准功率我习惯取1MVA的整数倍。原数据里负荷单位通常是kW需要先换算成标幺值再代入模型。举个例子某个节点负荷是90kW用1MVA做基准标幺值就是0.09。电压约束一般取0.95到1.05pu但配电网故障场景下允许适当放宽到0.90到1.10pu这会让可行域大很多求解也更容易。故障场景我设置为第5条支路上发生永久性故障这条支路两端的开关被保护装置切断。按照上面的约束写法直接把z(5)强制设为0。这样一来原本由节点6到18供电的下游区域全部失电成了孤岛。想要恢复这些负荷只能通过合上某几条联络开关把它们挂到另一条馈线的末端。联络开关在正常状态下是断开的模型里z初始值都是0但这里它们成了决策变量优化算法会从5条联络开关里挑合适的来合。系统里所有负荷是否都能恢复取决于相邻馈线的容量和电压支撑能力。为了保证模型有解我给每个节点都加了切负荷变量允许在找不到完全恢复方案时切除部分负荷。这个设计让模型从“铁板一块”变成了“软约束”只要存在任何可行的恢复路径求解器都不会直接报无解。3.2 目标函数与权重系数的归一化处理目标函数需要同时照顾两个目标恢复负荷和降低网损。我采用的写法是Objective sum(r .* I2) alpha * sum(load_curtail)前面的sum(r.*I2)是总网损以标幺值计算再乘以基准功率可以得到实际功率。后面的sum(load_curtail)是被切除的负荷总量单位也是标幺值。alpha是惩罚系数它的大小直接决定优化器愿意“牺牲多少网损去换恢复”。权重系数不能随便拍脑袋。如果alpha太小优化器可能为了省网损而主动丢掉一部分负荷这在实际故障处理中是不可接受的如果alpha太大又会让网损项形同虚设算出来的方案电压全都顶到上限附近运行裕度很差。我的经验做法是先把两项的量级调成可比的比如正常运行网损大约是0.2pu基准1MVA而切负荷即使切1个节点也是0.09pu。这时候alpha取500到1000就能让“切负荷惩罚”远远大于“网损收益”优先保证负荷恢复。另外一个更稳妥的思路是序贯优化第一阶段只求切负荷量最小把最优切负荷总量固定第二阶段在这个总量约束下再最小化网损。这种方法逻辑上最干净也不存在权重调参问题只是要多跑一轮求解。我建议初次做研究的朋友先试序贯优化理解了结果后再说权重法也不迟。3.3 核心代码实现与结果解读下面是我在Matlab里的建模核心片段结构上贴出来可以当模板用。完整代码里还有读取网络数据、计算关联矩阵的部分篇幅原因不展开但主线都在这里。%% 决策变量全部用YALMIP符号变量 V sdpvar(nb, 1); % 节点电压幅值平方 I2 sdpvar(nl, 1); % 支路电流幅值平方 P sdpvar(nl, 1); % 支路首端有功功率 Q sdpvar(nl, 1); % 支路首端无功功率 z binvar(nl, 1); % 开关状态1闭合0断开 curt sdpvar(nb, 1); % 每个节点的切负荷量 Constraints []; %% 目标函数网损 切负荷惩罚 Objective sum(r_ohm .* I2) * Sbase 1000 * sum(curt); %% 遍历每条支路构造DistFlow约束与锥约束 for k 1:nl i branch(k, 1); j branch(k, 2); % 支路功率平衡这里用到了节点注入关系实际需根据支路方向组装 Constraints [Constraints, P(k) - r_pu(k) * I2(k) flow_out_p(k)]; Constraints [Constraints, Q(k) - x_pu(k) * I2(k) flow_out_q(k)]; % 电压降落方程 Constraints [Constraints, V(j) V(i) - 2 * (r_pu(k) * P(k) x_pu(k) * Q(k)) ... (r_pu(k)^2 x_pu(k)^2) * I2(k)]; % 二阶锥松弛本质是 L*V P^2 Q^2 Constraints [Constraints, cone([2*P(k); 2*Q(k); I2(k) - V(i)], I2(k) V(i))]; % 开关断开时支路有功无功和电流全部强制为0 M 20; % 功率大M值注意不要太大也不要太小 Constraints [Constraints, -M * z(k) P(k) M * z(k)]; Constraints [Constraints, -M * z(k) Q(k) M * z(k)]; Constraints [Constraints, I2(k) M_line(k) * z(k)]; end %% 拓扑约束闭合支路数固定 单商品流连通 Constraints [Constraints, sum(z) nb - 1]; % 这里省略每个节点的虚拟潮流净流入约束写法见上一节 %% 电压电流上下限 Constraints [Constraints, 0.90^2 V 1.10^2]; %% 故障支路强制断开 Constraints [Constraints, z(5) 0]; %% 求解 ops sdpsettings(solver, gurobi, verbose, 2, gurobi.MIPGap, 0.005); optimize(Constraints, Objective, ops);这段代码跑完典型结果是这样的支路5故障后系统自动合上联络开关17-32具体编号看数据定义失电区域全部恢复供电没有发生切负荷。重构后的最低电压从故障状态下的约0.86pu恢复到了0.94pu以上满足电压下限要求。网损标幺值大约在0.22到0.25之间比正常运行时的0.20略高但考虑到这是非正常运行状态下的转供方案这个代价完全可以接受。求解时间在Gurobi默认参数下大约七八秒把MIPGap放到0.5%之后降到两秒内。需要提醒一点联络开关的编号和具体合闸位置在不同文献里可能不同因为33节点系统的联络开关定义有两种常见版本合闸结果也会跟着变。关键是看结果趋势而不是纠结于具体是第几条开关。4. 常见问题与排查技巧实录4.1 模型不可行先别急着调参数遇到求解器报“infeasible”时第一反应不要是去改权重而是把约束逐条检查。根据我的经验故障重构模型不可行十有八九是下面几个原因第一故障支路强制断开后失电区域在拓扑上已经没有任何可行的供电通道了。比如故障点设在了某个末端分支的唯一入口下游区域又没有联络开关那无论优化算法怎么折腾都不可能恢复供电。这种情况下模型报不可行是正确行为说明该场景需要增加联络开关或者考虑分布式电源孤岛运行。第二单商品流的连通性约束里虚拟潮流方向写反了。这个问题我前文提过检查方法是随便挑一条已知可行的拓扑放进约束里然后用YALMIP的checkset命令查看约束残差。如果某条约束残差明显不对打印出相关变量的值就能定位到哪条支路方向反了。第三电压上下限设置得过紧。故障重构暂态过程中允许电压短时偏低一段时间如果直接用0.95到1.05pu的常态约束可能间接把可行解全堵死了。我的做法是先用0.90到1.10pu跑一版确认所有负荷恢复方案存在之后再收紧电压限值评估方案质量。这是工程上非常实用的解耦技巧。第四切负荷变量没有正确接进潮流方程。节点功率平衡方程里如果切负荷变量只写进了目标函数忘了写进潮流注入项那模型会以为负荷永远按原始值全额注入切了也白切自然会导致无解或者切负荷量离保护值十万八千里。检查的方法是看最优解里curt变量对应的节点潮流是否同步变化了。4.2 二阶锥松弛不精确怎么判断和处理不是所有场景下二阶锥松弛都是精确的。如果求解完成后发现最优解的锥约束残差比较大比如L*V明显大于P²Q²那说明松弛不紧解出来的“最优网损”其实是不可实现的因为物理上电流就是那么多不可能再少。我遇到松弛不精确的场景主要有两类一类是网络中有重载长线路电压偏低导致锥约束整体放大另一类是目标函数里网损权重太低使得优化器不愿意把支路电流压到物理下限。处理办法有几个方向第一种是在目标函数里加一个极小权重的“锥紧化”项比如加上epsilon乘以所有支路的锥残差把松弛间隙往零的方向推。关键在于epsilon要非常小比如1e-6否则会改变原优化问题的最优解。第二种是检查电压初始点。YALMIP在求解时会自动给连续变量赋初始值但这些初始值不满足潮流物理特性时内点法迭代早期可能会跑偏。用assign函数给V向量一个平坦的合理初值比如所有节点1.0pu能明显改善松弛质量。第三种也是最稳妥的是跑完之后用二阶锥松弛的最优解去重新算一次精确潮流对比两者误差。常见结果是小系统误差在0.1%以内工程上完全可接受。如果误差超了1%那就要回头检查模型哪里写歪了而不是继续拧参数。4.3 求解效率优化平常跑着玩和认真做研究是两套玩法同样是33节点系统如果只是验证算法对不对默认参数几分钟能跑完就能接受。但如果要做多场景对比、蒙特卡洛故障枚举或者扩展到100节点以上的系统求解速度就是硬指标。我的优化顺序是第一步做变量削减。把所有不可能参与重构的支路比如变电站直接出线的首段开关设置为固定闭合不参与0-1决策。每少一个整数变量分支定界的搜索树都可能小一个量级。第二步做问题削减。故障支路下游的失电区域如果存在多个联络开关可以把故障隔离区域之外的网络压缩成等值负荷模型只对失电区域内部的拓扑做精细优化。这个方法在扩展算例里非常有效33节点上不明显但到大型系统就体现出来了。第三步调求解器参数。Gurobi的MIPGap从默认的1e-4放宽到5e-3对配网重构这种工程问题来说基本无损但求解速度通常会快一半以上。还可以试一下开启求解器自带的启发式算法给整数变量一个可靠初始解通常第一次分支就能剪掉一大块。第四步如果求解器一直卡在一个子问题上考虑把大M值缩小。支路功率的M值从几百缩小到20到50虚拟潮流的大M缩小到节点数能显著收紧LP松弛下界分支定界的上界和下界差距会缩小得很快。反直觉的是很多人以为大M越大越安全实际上大M过大只会让松弛变得更弱分支定界需要探索的节点变多。这个细节是我实际调参才体会到的。最后再分享一个小经验二阶锥配电网重构这套模型不只是能做故障重构。把故障支路约束去掉它就是正常运行方式下的网损优化重构把单时段的DistFlow改成多时段联立的约束它就可以处理动态重构问题在目标函数里加入分布式电源的出力变量就能顺带考虑DG接入后的重构策略。建模框架是通用的换的只是约束条件和目标函数的组合方式。用Matlab加二阶锥做配电网重构入门阶段可能要把松弛、拓扑、求解器这些概念一个个过一遍但一旦这套框架搭起来后面加场景、加约束、加设备模型都只是在这个骨架上的增量工作。这也是我推荐新手直接上SOCP而不是写一堆遗传代码的原因——求解器帮你处理了最难的全局寻优你只需要把物理问题用数学语言表达准确就够了。