配电网调度员最怕的不是故障本身,而是故障隔离之后的那几分钟——非故障区还有一堆重要负荷在停电,到底合哪几个联络开关、断哪几个分段开关,才能在保证不形成环网、电压不越限、线路不过载的前提下,把这些负荷全部倒回电源。过去这个决策基本靠经验翻联络图,现在可以把它写成数学模型,用 Matlab 加 Yalmip 工具包直接求解。我最近把整套流程完整复现了一遍:用 DistFlow 方程描述配电网潮流,把电流平方约束做二阶锥松弛,把开关状态和辐射状拓扑写成混合整数约束,最后交给求解器得到恢复方案。整个过程踩了不少坑,尤其是辐射状约束的建模和大M常数的取值,几乎每个环节都要小心。这篇文章就把核心思路、关键代码、33节点系统算例和调参经验完整记录下来,给正在做配电网故障重构、网络重构相关工作的同学一个可以直接参考的落地版本。
1. 故障重构的任务拆解:故障隔离之后的开关决策问题
1.1 故障隔离之后发生了什么
先理清场景。配电网正常运行时是辐射状结构,一棵以变电站为根的树。当某条线路发生永久性故障,保护动作后故障线路两端开关跳开,这时系统被切成两半:上游区域还挂在电源上,继续带负荷;下游区域因为失去了唯一的供电路径,全部停电。
故障重构要做的,就是在故障线路已经被隔离的前提下,通过闭合部分联络开关、断开部分分段开关,重新把失电区域接回电源,同时保持整个网络的辐射状结构。联络开关平时是断开的,它的作用就是给这种极端情况留出倒供路径;分段开关平时是闭合的,负责把网络切成不同供电区段。
这里要明确一个概念:故障重构和网络重构不完全是一回事。网络重构通常指正常运行方式下的优化,目标是降损或者均衡负载;故障重构更强调时间紧迫性和恢复目标,求解结果要能直接指导调度员拉合开关。但两者的数学内核是一致的,都会落到“给定初始状态,求解一组新的开关状态使得目标最优”这个混合整数优化问题上。
1.2 重构要满足的硬约束
不管目标函数怎么写,有几个物理约束是必须满足的,缺一个结果就不可行。
第一,辐射状约束。配电网继电保护是按辐射状配置的,一旦形成环网,短路电流计算、保护定值配合全部要重来,所以重构后的网络必须仍是一棵树。这个约束看着简单,建模时反而最麻烦,后面专门用一章讲。
第二,电压约束。IEEE 33节点这类配电网一般要求电压幅值在 0.95p.u. 到 1.05p.u. 之间。注意配电网线路阻抗偏大,末端电压跌落往往很严重,故障恢复路径如果太长,末端电压可能压线甚至越限。
第三,线路容量约束。每一条运行支路的电流不能超过导线载流量。在标幺化建模中,电流可以用 DistFlow 方程里的变量 l 表示,约束写成 l 不超过对应上限即可。
第四,故障支路必须保持隔离。这是前提条件,故障端线路两端开关都不能闭合,所以故障支路对应的开关状态要固定为0。
第五,负荷可调节。一般假设节点负荷可以按比例切除,也就是引入一个0到1的恢复系数 alpha,实际注入负荷等于 alpha 乘以额定负荷。重要负荷可以设成大权重,迫使求解器优先恢复;可切负荷权重小,必要时先切掉。
1.3 目标函数:三种写法怎么权衡
目标函数我试过三种,实际效果差别很大。
第一种是恢复负荷最大化,这是故障重构最核心的目标。把每个节点的未恢复负荷按权重加总,希望损失最小。权重怎么定?一级负荷(医院、通信、重要用户)权重可以设为100,二级负荷10,三级负荷1。这样求解器在只能恢复一部分负荷时,会优先保重要负荷。
第二种是网损最小化。重构完成后,某些联络开关闭合会让功率走更长的路径,网损可能反而上升。但通常在IEEE 33这类小系统上,恢复负荷目标占主导,网损属于次要矛盾。
第三种是开关操作次数最少。开关操作次数太多会延长恢复时间、增加机构磨损。不过实际中,只要重构方案能恢复全部负荷,多数调度员不会吝惜操作次数。
工程上常用加权组合。我用的形式是:
matlab复制Objective = -1000 * sum(weight .* alpha .* P_load) ...
+ sum(r .* l) ...
+ 0.1 * sum(abs(z - z0));
第一项是未恢复负荷的惩罚,权重加得很大;第二项是网损;第三项是开关操作惩罚,z 是待求解的开关状态二进制变量,z0 是故障隔离后的初始开关状态。注意三项的量纲差异,网损是标幺值,数值很小,所以前面恢复负荷的系数要拉高,否则求解器会为了省一点点网损牺牲负荷恢复,这就本末倒置了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. DistFlow方程到二阶锥松弛:核心数学转化怎么来的
2.1 配电网潮流:DistFlow方程的直觉理解
配电网潮流用 DistFlow 方程来描述,比牛顿-拉夫逊法解完整交流潮流要简洁得多,而且特别适合做优化模型。它把每条支路看成一个通道,支路首端流入的功率,一部分在线路上损耗掉,剩下的供给末端节点以及末端下游的所有支路。
对一条支路 k,假设从节点 i 流向节点 j,定义 P_k、Q_k 为支路首端有功、无功,l_k = I_k^2 为支路电流平方,v_i = U_i^2 为节点电压平方。那么有:
P_k - r_k * l_k = P_load_j + sum(P_m),其中 m 是以 j 为首端的所有下游支路
Q_k - x_k * l_k = Q_load_j + sum(Q_m)
这是节点 j 的有功/无功平衡,支路首端流入功率减去线路电阻/电抗上的损耗,等于节点负荷加下游支路流出的功率。
电压降落方程是:
v_j = v_i - 2*(r_kP_k + x_kQ_k) + (r_k^2 + x_k^2)*l_k
这个方程说明末端电压平方等于首端电压平方减去线路上的纵向压降,再加上一个很小的横向分量修正项。
最后一个关系是电流平方和功率、电压的关系:
l_k = (P_k^2 + Q_k^2) / v_i
这个等式把支路电流和传输功率、首端电压联系起来。整个 DistFlow 方程组的变量都是 P、Q、l、v,全部是实数变量,没有相角,比完整潮流方程简单得多。
2.2 非凸点在哪里:电流平方约束
上面四个方程里,前三组都是线性等式,只有最后一组 l = (P^2+Q^2)/v 是非线性非凸的。如果直接用这个等式去做优化,问题是非线性非凸的混合整数规划,绝大多数商业求解器都处理不了,只能靠粒子群、遗传算法这类启发式算法去碰运气,不保证最优,也不保证每次收敛。
碰到非凸约束,数学上的思路不是去“解”它,而是想办法把它“松弛”成一个凸约束。只要满足松弛条件,并且在最优解处松弛是紧的,那么松弛后的凸问题就和原问题等价。
2.3 SOCR松弛:用凸锥替换等式约束
把最后一个等式改写成不等式:
l_k >= (P_k^2 + Q_k^2) / v_i
也就是允许电流平方大于等于功率和电压确定的最小值。这个不等式的可行域是凸的吗?是的,把它整理一下就能写成二阶锥标准形式。
令 a = 2P_k,b = 2Q_k,c = v_i - l_k,d = v_i + l_k,那么原不等式等价于:
norm([2P_k; 2Q_k; v_i - l_k], 2) <= v_i + l_k
这是一个标准的二阶锥约束,写作 l_k >= (P_k^2+Q_k^2)/v_i 更直观,实现时用上面的锥形式。
为什么可以放心松弛?因为目标函数里有网损项,是 r_k * l_k 加起来,系数为正,求解器会尽量把 l_k 压低。如果松弛后的最优解能让第二个约束取到等号,那这个解就是原问题的可行解。再进一步,只要没有其它强制把 l_k 推高的约束、且目标函数中网损项系数为正,松弛就是紧的。这就是SOCR(Second-Order Cone Relaxation)能用在配电网重构上的理论依据。
实际工程里要注意,如果网络中接了恒阻抗负荷、或者目标函数里没有网损项,松弛可能不紧。后面专门讲怎么检查。
2.4 支路开断时的大M约束
DistFlow方程默认支路都是闭合的。但重构问题里开关状态是0-1变量,支路断开时,功率和电流必须为0,电压关系也不再成立。
处理办法是大M法。对每条支路,引入二进制变量 z_k,1表示闭合,0表示断开,然后用一组带大M的约束控制:
matlab复制M_P = 2; M_Q = 2; M_l = 10; M_V = 2;
C = [C, -M_P*z(k) <= P(k) <= M_P*z(k)];
C = [C, -M_Q*z(k) <= Q(k) <= M_Q*z(k)];
C = [C, 0 <= l(k) <= M_l*z(k)];
当 z=0 时,P、Q、l 全被压到0;当 z=1 时,这些约束只是给出一个很大的上下界,不产生实际限制。
电压方程也要按开关状态做条件化:
matlab复制C = [C, -M_V*(1-z(k)) <= v(to(k)) - v(from(k)) + ...
2*(r(k)*P(k) + x(k)*Q(k)) - (r(k)^2+x(k)^2)*l(k) <= M_V*(1-z(k))];
当 z=1 时,中间那项被强制等于0,恢复DistFlow电压方程;当 z=0 时,不等式两边是大M,电压关系完全放开,允许断开支路两侧节点电压不相等。
这里M的取值很关键。取小了会把可行解截掉,比如本来应该断开的支路因为电压差超过M而被迫闭合;取大了会带来数值问题,二阶锥和混合整数问题的求解器对病态大M非常敏感,经常报数值警告或者收敛变慢。我自己的经验是:功率M取标幺值下最大可能传输功率的1.5倍左右,比如系统基准容量10MVA,最大支路功率一般不超过2p.u.,M_P、M_Q取2就够了;电压M取1到2,因为电压平方的标幺值范围本身就在0.9到1.1之间,差值不会太大。千万别随手取个1e6,那会害了求解器。
3. 辐射状拓扑约束:生成树条件的建模与实现
3.1 为什么不能只有边数约束
辐射状约束是故障重构里最容易写错、也最影响求解结果正确性的约束。很多人一开始会想:配电网是树,树的条件不就是边数等于节点数减1吗?于是只写了一个 sum(z) == N - 1,然后求解。
这个约束是必要的,但远远不够。它只能保证边数正确,不能保证连通性和无环。举个反例:一个33节点系统,如果让前10个节点连成一棵树,后23个节点也连成一棵树,两棵树互不相连,总边数依然是32,满足 sum(z)==32,但后23个节点全是孤岛,没有电源,这个拓扑完全不可行。求解器不会因为“看起来像树”就认为可行,这种假解一旦出现,负荷恢复率、电压全部失真。
所以辐射状约束必须包含两层意思:一是边数等于节点数减1,二是网络必须连通。两者合起来,才等价于一棵生成树。
3.2 单商品流约束:让每个节点都能被“喂到”
连通性约束在数学上有多种写法,图论里的割集约束、环路约束都可以,但在优化建模里最实用的是单商品流约束。思路很朴素:假设变电站根节点是货源,每个负荷节点需要接收1单位虚拟商品,那么为了让所有节点都能收到货,必须存在一条从根到每个节点的路径。反过来,只要所有节点都能收到货,网络就一定连通。
虚拟流约束定义如下。设 F_k 是支路 k 上的虚拟流量,方向与支路潮流方向一致,都是按预设的首端到末端方向。每条支路的虚拟流量有上限,断开时必须是0:
matlab复制C = [C, 0 <= F <= N * z];
根节点(变电站节点1)向整个网络供货,流出总量为 N-1:
matlab复制out1 = find(from == 1);
C = [C, sum(F(out1)) == N - 1];
对每个非根节点,流入减流出等于1:
matlab复制for j = 2:N
in_j = find(to == j);
out_j = find(from == j);
C = [C, sum(F(in_j)) - sum(F(out_j)) == 1];
end
这三组约束合在一起,每个节点净接收1单位虚拟流。如果某个节点和根节点不在同一连通分量里,它的入流就是0,无法满足流入减流出等于1,约束直接判不可行。所以只要边数约束和单商品流约束同时满足,网络就是一棵以根节点为根的树。
在Yalmip里,F 用连续变量即可,不需要声明为整数。为什么连续变量就能保证连通性?因为约束是线性的,整数解会被 z 变量的0-1性质自然带出来。
3.3 多源网络、方向约定与边界情况
上面讲的是单电源辐射状配电网。如果系统里有多个变电站同时供电,也就是多源网络,辐射状约束需要调整。边数约束要改成 sum(z) == N - N_sub,其中 N_sub 是电源节点数量;虚拟
