
做配电网重构优化这段时间最大的感受是这个问题的难点不在于算法本身有多复杂而在于你怎么把工程问题变成数学模型再从数学模型的解变回可执行的开关操作方案。多目标Pareto重构加智能算法是目前解决这类问题的主流思路。这篇文章不堆公式也不贴说明书式的代码就把我从建模到Matlab实现的全过程讲清楚包括踩过的坑、调过的参数以及每一步为什么这么做。如果你正准备用Matlab实现配电网多目标重构这篇应该能让你少走不少弯路。1. 问题建模为什么配电网重构需要多目标Pareto视角1.1 重构的实质与决策变量配电网通常是“闭环设计、开环运行”。网架里布置了分段开关和联络开关正常运行方式的调整本质上就是通过改变这些开关的断/合状态改变潮流流通路径。重构问题的决策变量就是一组01开关状态。以经典的33节点辐射状配电测试算例来说可操作的开关就有30多个盲目组合就是2的30次方量级的组合空间穷举遍历根本不现实。这是重构问题难的第一层原因。第二层原因是不是任意一组开关状态都能用。配电保护、故障隔离、短路电流水平都要求网络保持辐射状结构闭环供电通常只在极端故障场景下短时允许。转换成数学约束就是网络必须连通且不存在环等价于“闭合支路数等于节点数减1且节点-支路关联矩阵满秩”。这个约束在算法里要硬性处理不是靠惩罚就能糊弄过去的后面讲代码的时候再细说。1.2 三个经典优化目标为什么缺一不可跑重构优化目标怎么定是最常被问的问题。从实际运行和配电网规划视角出发建议至少看三个维度系统网损直接对应经济性。支路损耗之和可以写成公式 Ploss Σ Gij(Vi² Vj² - 2ViVjcosθij)实际代码里直接用潮流结果对各支路损耗求和就行。电压质量末端电压偏移是配网考核的重点。电压偏差目标函数可以取各节点电压与参考电压的差值绝对值之和参考电压一般取标幺值1.0。负荷均衡度衡量各支路负载是否均匀可以用支路负载率的方差来表示方差越小说明网架利用越均衡。这三个目标之间存在本质冲突。一个能显著降损的方案可能把潮流推到某条支路上导致电压改善不明显甚至恶化一个追求电压均衡的方案可能要绕远路供电网损反而上升。这也是多目标优化的立身之本不是求一个“唯一最优解”而是生成一组Pareto前沿解把目标之间的权衡关系摆到台面上让决策者选。1.3 Pareto概念的通俗理解Pareto支配的严格定义是对于两个可行解A和B如果A在所有目标上都不劣于B且至少在一个目标上严格优于B就说A支配B。凡是被支配的解在“多目标最优”的意义上已经没有存在价值可以淘汰没有被支配的那些解构成Pareto前沿。专业名词容易劝退换个说法就很好明白。你买房同时考虑价格和通勤时间。某套房价格更低、通勤也短那另一套又贵又远的房子可以直接删掉但如果一套价格低但通勤远另一套通勤近但价格高这两套就都值得留给决策者再权衡。配电网重构的Pareto前沿就是这样一个方案清单列出各种运行方式的开关组合与三项目标值调度员根据当下最关注什么从清单里选。这也是为什么用加权法把三个目标压成一个往往得不到让人信服的结论——权重系数本身就没有客观标准今天网损权重给0.4明天可能就该给0.6这事没人说得清。2. 算法选型NSGA-II为何能扛起多目标重构这面旗2.1 重构问题的组合爆炸和离散特性重构问题是典型的离散组合优化决策变量是0/1开关状态梯度类算法根本没法用。解空间又随开关数量指数增长穷举即便在小系统上也很吃力。经典数学规划方法在这个问题上也面临非线性潮流约束的壁垒建模和求解门槛都不低。近些年的主流解法就是智能优化算法——不对问题做线性化假设把目标函数当黑盒用随机搜索加进化机制逼近满意解。智能算法在计算速度和全局最优之间做了一个工程上可接受的折中。但单目标智能算法解决不了目标冲突的问题。多目标场景必须用带Pareto机制的智能算法。NSGA-II是这类算法里最经典的框架实现成熟、理论清楚、可扩展性强在配电网重构相关的项目里用得最多。理解了它后面想换别的算法也比较容易。2.2 非支配排序与拥挤度距离在解决什么NSGA-II有两个核心机制必须理解透。第一个是快速非支配排序。每代种群按支配关系分成多个非支配层。第一层的解不被任何其他个体支配第二层只被第一层支配以此类推。选择时优先保留层数靠前的个体这是多目标意义上的“保留强者”。因为多目标里谁强并不唯一所以“强者”的定义由Pareto支配关系给出而不是人为定的权重。第二个是拥挤度距离。同一个非支配层里如果个体都挤在一个角落Pareto前沿覆盖度就差。拥挤度计算的是某个个体周围解分布的稀疏程度距离越大说明越稀疏越该保留。这一步保证了算法不仅在往最好解区域收敛还能把整个前沿尽量撑开。举个例子。前沿上有5个解扎堆在网损低、电压偏差大的区域而网损中等的区域只有1个孤点。淘汰的时候优先淘汰扎堆里的解让孤点留下来。最后得到的前沿边界更宽覆盖的工况更全面决策者可选的方案就更多。2.3 NSGA-II主循环的整体流程NSGA-II的总体流程可以概括成下面几条初始化种群随机生成满足拓扑约束的开关组合。评估目标函数对每个个体做潮流计算求得网损、电压偏差、均衡度。快速非支配排序给每个个体分配非支配层号。拥挤度计算同层内按目标分布的稀疏度打分。锦标赛选择每次随机挑两个个体层号小的胜出层号相同则拥挤度大的胜出选够父代数量。交叉与变异父代经过遗传算子生成子代注意变异后要重新校验拓扑约束。父子合并把当前代和子代放一起重新排序按“层号优先、拥挤度其次”的原则选出下一代。判断是否达到最大迭代次数或前沿是否长时间不再变化否则回到第2步。这套流程保证了精英不会丢失因为父代里的优秀个体在合并后总有机会留在下一代。这也是精英保留策略是NSGA-II相对第一代非支配遗传算法最大的改进。3. Matlab代码实现从数据准备到NSGA-II主循环把代码拆成模块来写不要一股脑塞进一个脚本里。模块化以后换网络数据集只需要改数据读取部分换目标函数只需要改评估函数。下面按模块逐一讲。3.1 数据准备与环路编码网络数据至少要包含节点编号、支路首末端编号、支路阻抗、节点注入负荷还要标记哪些支路上有开关。以33节点辐射状测试算例来说用结构体sysData统一管理数据最省事避免在函数间传递一堆散落的变量。编码方式直接决定初始种群合法率。单纯用二进制随机生成01序列大部分个体根本连不通校验一次失败一次。我用的方法是环路编码先找出网络中每条联络开关闭合后形成的基本环编码时从每个环中随机选一条支路断开。这样闭合支路数天然等于节点数减1再配合连通性校验初始解合法率一般能从20%~30%提升到90%以上。初始化代码框架如下function pop initPopulation(N, loops, branchNum) % loops: 每条联络开关定义的基本环, 每行是该环内支路编号 pop zeros(N, branchNum); for i 1:N ind ones(1, branchNum); % 先全部闭合 for j 1:size(loops, 1) sw loops(j, :); sw sw(sw 0); k sw(randi(length(sw))); % 该环随机断开一支 ind(k) 0; end % 环之间可能有共用支路闭合支路数可能不等于节点数-1 % 基础修复闭合数大于n-1则随机断开支路小于则随机闭合 pop(i, :) ind; end end这段代码的核心就是“每个环至少断一条”它把拓扑校验的负担大幅降低。环的提取可以在数据准备阶段用图论里的基础回路方法一次性算好存入sysData.loops。3.2 辐射状拓扑校验的实现与原理初始化、交叉和变异之后都必须做一次辐射状校验。校验原理前面说过闭合支路数等于节点数减1且节点-支路关联矩阵满秩。Matlab里用rank函数就能算function valid isRadial(n, closedEdges, branchNodes) m length(closedEdges); if m ~ n - 1 valid false; return; end A zeros(n, m); for i 1:m e branchNodes(closedEdges(i), :); A(e(1), i) 1; A(e(2), i) -1; end valid (rank(A) n - 1); end这里有个细节rank对浮点误差不敏感但关联矩阵元素都是整数直接rank不会出数值问题。如果网络节点编号不是从1连续编号需要先做映射否则矩阵维度会错。3.3 前推回代潮流计算配电网重构里用的潮流方法是前推回代法。这是辐射状配电网最经典的计算方法比牛顿拉夫逊法实现简单、速度快。思路是根节点电压已知先假设全网电压为额定值从末端向根节点逐层回代计算支路电流或功率损耗得到根节点的注入功率再从根节点向末端逐层前推计算节点电压。两个方向反复迭代直到前后两次电压差满足容差。前推回代的代码骨架function [V, iter, flag] backwardForward(sysData, swState, V0, maxIter, tol) n sysData.n; V V0 * ones(n, 1); flag false; for iter 1:maxIter Vold V; % 按支路层次顺序做回代 S sysData.load; % 节点复功率注入 for k length(sysData.layerOrder):-1:1 b sysData.layerOrder(k); % 支路编号 % 从该支路末端节点累加该节点流出的功率 % S(start) S(start) S(end) loss(b) end % 按层次做前推 for k 1:length(sysData.layerOrder) b sysData.layerOrder(k); % V(end) V(start) - I(b)*Z(b) end if max(abs(V - Vold)) tol flag true; break; end end end代码里提到的layerOrder是支路计算顺序可以在校验拓扑时用广度优先搜索生成一次。每次重构开关组合变了拓扑层次会变所以每个个体评估时该个体的layerOrder必须重新生成。这个环节是前推回代里最容易被忽略的坑直接用原始支路编号顺序计算收敛会极慢甚至不收敛。先按网络的电气层次排序回代从末梢支路开始前推从根节点相邻支路开始数值稳定性完全不同。3.4 目标函数的计算与约束处理评估个体目标值时先做潮流得到各节点电压和各支路功率。网损是全部支路损耗之和电压偏差是各节点电压与参考电压偏差的绝对值之和负荷均衡度可以定义为支路负载率的均方差或方差。注意三者量纲不同。网损是千瓦级别电压偏差是标幺值负载率是百分比。做拥挤度计算前最好各自做归一化否则量纲大的目标会主导拥挤度计算Pareto前沿的分布会偏向某一个目标整体分布就不均匀。约束方面潮流不收敛或者电压越限都得有交代。做法是不可行个体直接将其所有目标函数设为一个大数比如1e6。这个惩罚值必须大于任何正常个体的目标值否则非支配排序里不可行解会被误认为离群优秀解甚至反过来支配正常解整个种群就带偏了。另外目标函数计算之前先调用isRadial拓扑不合法直接返回惩罚值省得白算一次潮流。这个顺序调理一下能省下不少无效计算时间。3.5 NSGA-II主循环与遗传算子主循环就是把前面几个模块串起来pop initPopulation(N, sysData.loops, branchNum); for gen 1:maxGen [f1, f2, f3] evaluate(pop, sysData); [rank, crowd] fastNonDominatedSort(f1, f2, f3); pool tournamentSelection(pop, f1, f2, f3, rank, crowd, N/2); child crossoverMutation(pool, sysData, pc, pm); comb [pop; child]; [f1c, f2c, f3c] evaluate(comb, sysData); [rankC, crowdC] fastNonDominatedSort(f1c, f2c, f3c); idx selectElite(rankC, crowdC, N); pop comb(idx, :); end plotParetoFront(pop, f1, f2, f3);快速非支配排序的复杂度是O(MN²)M是目标数N是种群规模。实现时别用三层for循环加cell动态增长的方式会慢得让人怀疑人生。提前分配好支配集合的长度或者直接记录dominatedCount和dominatedList用向量化方式做比较。33节点小算例还好几百节点的大网络差别就非常明显。交叉变异算子怎么保持拓扑合法这里有个重要设计。我常用的组合是单点交叉加多点随机翻转变异变异后立即做辐射状校验非法个体做一次修复而不是直接放弃。修复方法很简单重新从它所属的环里随机断一条或者随机闭合一条断开支路。修复比重生成效率高得多因为只改局部保留了大量遗传信息。4. 参数设置、常见问题与调试心得4.1 关键参数的经验值参考参数怎么设直接抄作业可能翻车因为不同网络规模对应不同取值。这里给一组从实际调试中总结的基准范围参数建议范围说明种群规模N80~20033节点算例100左右足够节点越多取大最大迭代次数100~500以Pareto前沿变化稳定为准不必硬跑满交叉率0.8~0.9过高会破坏结构不建议低于0.6变异率0.05~0.15开关级变异按个体内开关数换算锦标赛规模2~42最常见4收敛更快但分布性变差潮流迭代容差1e-4~1e-6过严会浪费大量迭代过松则目标值失真群体规模这块我看到不少人在33节点算例上也取300甚至500其实没必要。个体太多每一代的潮流计算量直线上升而最终Pareto前沿的提升很有限。先取100跑通再按前沿稀疏程度决定要不要加。4.2 常见问题速查表调试过程中大概率会遇到下面这些问题我整理成了速查表问题现象可能原因解决思路初始种群合法率极低用的是纯随机二进制编码改用环路编码从每个基本环中随机断开潮流计算不收敛支路层次未排序、拓扑含环或孤岛、容差过严先检查拓扑校验再用BFS生成计算层次前几代目标值全是1e6潮流模块单独有问题把潮流模块单独拿出来固定拓扑验证结果Pareto前沿集中在一个小区域变异率太低、初始种群多样性不足提高变异率或多种子初始化前沿点太少选择不够种群规模偏小精英保留丢失增大种群检查合并选择时是否误删优秀个体非支配排序极慢用动态增长的cell存储支配集合预分配矩阵向量化比较收敛后前沿稀疏度不均匀目标未归一化导致量纲主导评估函数里对三个目标做归一化4.3 提升运算速度的实用技巧配电网重构的计算瓶颈几乎都在潮流计算上尤其是大网络。几个提速技巧实测下来很有效。第一给评估函数加缓存。同一个开关组合在多轮迭代后会出现重复可以用哈希表缓存目标函数值重复个体直接读缓存结果。我在较大规模网络里跑这一步能省掉大概20%到30%的评估时间。第二按需提前终止。每N代输出一次当前Pareto前沿观察前沿是否还在明显移动。如果连续20代前沿几乎没有变化提前终止循环。省下的迭代时间对大网络非常可观。第三初始解的质量决定了收敛速度。除了用环路编码生成初始种群还可以把几个已知的经典运行方式直接塞进初始种群比如全开关闭合的基准方案以及根据经验调出的几个较好方案。这样一代种群就有几个不错的种子算法不用从全乱状态开始爬山。第四代码层面向量化。评估函数里循环支路做前推回代时能用矩阵运算的地方尽量用矩阵运算。特别在计算支路损耗、节点电压更新这些步骤避免for循环逐条支路累加。Matlab的矩阵运算开销远低于循环。5. 个人实操体会代码写到这里整个框架就通了。我自己踩过最大的坑就是一开始把潮流模块直接嵌进评估函数里结果整个程序跑下来不收敛翻了两天才发现是前推回代时支路层次没有重新排序不是算法的错。后来把潮流单独拆出来先用固定拓扑把结果和已知数据核对确认无误再接NSGA-II排查效率高了很多。如果你也想从零搭一套多目标重构程序我的建议是先把三个模块吃透——辐射状校验、潮流计算、非支配排序。这三个模块任何一个有问题后面画出来的Pareto前沿都不可信。先把33节点经典算例跑通再考虑扩展到实际规模网络这条路走起来最稳。最后一个实用小技巧把Pareto前沿画出来之后顺手把前沿上每个方案的开关操作次数也算出来。在实际调度里两个方案目标值差不多的条件下操作次数少的那个才是现场更愿意用的方案。这个信息不在目标函数里但作为决策辅助价值很高。
拿不准这条消息跟你有没有关系?
工种不同、批次不同,要求可能差很多。打电话把你的情况说清楚,我们按信阳、平顶山本地的口径给你捋一遍。