做配电网重构的项目,最难的不是把潮流算出来,而是想清楚一个问题:你希望用一组开关组合,同时把网损、电压质量和操作代价这些相互打架的指标都照顾好。只盯住一个指标,出来的方案往往会在评审时被一句话问住:“电压怎么样?开关操作次数会不会太多?”所以现在配电网重构的研究和工程实践,基本都往多目标 Pareto 方向走。这篇文章就我整理的一套配电网多目标 Pareto 重构加智能算法的 Matlab 实现,完整拆开讲清楚:问题怎么写、算法怎么选、代码怎么落地、结果怎么读,以及真正做项目时容易踩的坑。内容适合正在做课设、写论文或者要接配电网优化项目的同学直接参考。
1. 配电网重构到底在优化什么:损耗、电压和开关代价的三个角力
1.1 物理背景:为什么“闭环设计、开环运行”给重构留出了空间
配电网和输电网有一个很大的区别:输电网往往强调环网运行,而配电网通常是按闭环结构去建设的,但正常运行时会把部分联络开关打开,让每一个实际馈线段都保持辐射状,也就是“闭环设计、开环运行”。这么做的原因很实在:辐射状网络下保护配合简单、故障定位快、短路电流控制也容易。
重构就是在这个前提下做文章。通过改变分段开关和联络开关的投切状态,让一部分负荷改由另一条馈线或者另一个电源路径供电,网络拓扑就变了。拓扑变了,潮流分布自然跟着变,于是网损、节点电压、馈线负载均衡这些运行指标都有可能同时变化。
比如我常用的一个 33 节点配电网测试模型,平时有几个联络开关是断开的,构成了 5 个基本环。理论上可以把其中任意几个联络开关合上,再把对应的分段开关断开,形成一个新的辐射状网络。这种“重新组合开关状态”的行为,就是正常运行方式下的网络重构。注意它和故障重构不一样,故障重构往往还要考虑停电范围恢复,本文默认讨论的是正常运行状态的优化重构。
网络重构真正复杂的地方在于:它不是线性规划或者连续优化那种可以轻松求导的问题,而是组合优化问题。每个开关只有开和合两种状态,决策变量都是离散的。随着网络规模变大,可能的开关组合数量呈指数级增长,十几个、几十个开关的组合就已经不是穷举能解决的了,这才需要智能优化算法进场。
1.2 三个常见目标函数和约束条件的数学模型
配电网重构里最常出现的三个目标函数,我按工程实用频率排个序:
第一个是系统有功网损最小。这是最经典、也最容易被接受的优化目标。支路有功损耗可以写成:
[ P_{\text{loss}} = \sum_{k=1}^{N_b} I_k^2 R_k ]
也就是把所有闭合支路上电流平方乘以电阻再求和。这个目标直接对应节能降损,是电网公司最容易算清的一笔账。
第二个是节点电压偏差最小。配电网越靠末端电压越低,如果重构方案只盯着降损,有可能低压侧节点电压更差。电压偏差目标有两种写法,一种是取最大偏差:
[ f_2 = \max_i |U_i - U_N| ]
另一种是取所有节点偏差的平方和:
[ f_2' = \sum_{i=1}^{N} \left( \frac{U_i - U_N}{U_N} \right)^2 ]
前者比较“硬”,适合做约束;后者更平滑,适合做优化目标。实际代码里我更喜欢用平方和,因为它在数值上的区分度更好。
第三个是开关操作次数最小。每次开关动作都涉及设备寿命、操作风险和调度成本,所以实际重构不能为了省一点点网损就频繁操作开关。这个目标可以写成:
[ f_3 = \sum_{j=1}^{N_s} |s_j - s_j^0| ]
其中 (s_j) 是重构后的开关状态,(s_j^0) 是初始开关状态。如果前后状态不同,这一项就加一。
除了目标函数,约束条件也很关键。常见的约束有四种:
- 潮流约束:节点功率必须满足基尔霍夫定律,这部分由潮流计算保证。
- 节点电压约束:每个节点的电压幅值要落在规定区间,比如标幺值 0.93~1.07。
- 支路容量约束:流过每条支路的电流或功率不能超过导线热稳定极限。
- 拓扑辐射约束:重构后的网络必须保持连通且无环路,同时不能出现孤岛。
前三个约束都比较好理解,第四个是配电网重构里最特殊、也最容易写错的约束。很多人一开始只做了电压约束和潮流约束,结果跑出来的“最优解”是一个带环甚至解裂的网络,完全没法用。所以拓扑约束要放进代码里作为一个硬性判断条件。
1.3 Pareto 前沿:为什么多目标不等于加权单目标
有人会问:既然有多个目标,把网损、电压偏差、开关次数按权重加起来变成一个目标,是不是更简单?
简单是简单,但问题不少。第一,三个目标量纲差别太大,网损是千瓦、电压偏差是标幺值、开关次数是整数,权重怎么定?拍脑袋给一个权重,结果对权重非常敏感,换一组权重方案就完全不同。第二,如果真正的 Pareto 前沿是非凸的,线性加权法就算把所有权重都试一遍,也可能漏掉中间一批关键解。第三,工程决策本身就不是“给个唯一答案”的事,不同场景下侧重点不一样:平时运行可以多操作几次开关来降损,检修高峰或者负荷波动大时,可能更希望少动开关、维持稳定。
所以多目标优化这里不回避冲突,而是把一组互不支配的最优解找出来。所谓 Pareto 支配,可以这样看:解 A 支配解 B,是指 A 在所有目标上都不比 B 差,并且至少在一个目标上比 B 严格更好。所有不被其他可行解支配的解,就构成 Pareto 前沿。
从代码实现角度,我们要输出的不是一个“最优开关方案”,而是一张 Pareto 前沿上的方案集合,让决策者根据实际场景从里面选。这个思路贯穿了整套 Matlab 代码的设计,也是后面所有算法算子的出发点。
2. 智能算法选型的逻辑:为什么NSGA-II适合配电网开关组合搜索
2.1 组合复杂度决定了不能靠枚举
先说为什么一定要用智能算法。以 33 节点系统为例,如果粗略按 37 个开关来做二进制枚举,状态空间是 (2^{37}),大概是上千亿种组合,直接穷举根本不现实。就算用环路约束把问题约束在每个环只断一个开关,可能还有上万种组合;对更大的实际馈线网络,组合数量只会更夸张。
这类离散组合搜索问题,传统数学优化方法很难直接处理,因为目标函数和拓扑变量之间不是光滑的解析关系,一个开关状态改变,整个树的潮流分布都会改变。而智能算法的思路是:不去追求严格的数学最优,而是通过“种群进化”的方式,在合理时间里逼近一个足够好的解集。工程上这个“足够好”通常就够用了。
2.2 GA、NSGA-II、MOPSO 的对比:离散编码场景差距很明显
配电网重构领域常用的智能算法有好几种,但落实到 Matlab 代码层面,差别其实挺大。
| 算法 | 编码适配度 | Pareto 机制 | 在重构场景的常见表现 | 实现难度 |
|---|---|---|---|---|
| 传统 GA | 好 | 需要外加权重或分层处理 | 单目标降损可以,多目标容易丢解 | 低 |
| NSGA-II | 好 | 原生支持非支配排序和拥挤距离 | 离散组合问题表现稳定,代码生态成熟 | 中 |
| MOPSO | 一般 | 需要额外维护外部存档 | 粒子速度和位置映射到离散开关状态很麻烦,容易早熟 | 中高 |
| 模拟退火 | 只依赖目标函数 | 通常做单目标或人工加权重 | 收敛慢,多目标扩展需要额外机制 | 中 |
我实际写代码时会优先选 NSGA-II,理由很直接:
- NSGA-II 的遗传算子本来就是为离散编码设计的,二进制、整数编码都能直接用,不需要像粒子群那样做速度到开关状态的映射。
- 非支配排序机制可以同时处理多个目标,不用提前定权重。
- 精英保留策略让父代和子代合并之后再排序,不容易丢掉上一代好不容易找到的好解。
- Matlab 实现成本低,核心算子加起来也就几百行,调试起来好定位问题。
MOPSO 也不是不能用,但配电网重构的决策变量是开关状态,粒子群算法核心的“速度更新”在离散空间里变得不太自然,容易导致粒子在一个区域内反复震荡,整体搜索效率反而不如带交叉变异的遗传算法稳定。
2.3 保证辐射状拓扑的编码策略:环路编码与修复
编码方式直接影响算法的成功率和运行效率。最简单的是二进制编码:基因位数等于可操作开关数量,1 表示闭合,0 表示断开。这种方法实现简单,但在 33 节点网络上随机生成一个几十位的二进制串,不满足辐射约束的比例非常高。
为什么?辐射状网络要求闭合支路数刚好等于节点数减一,而且所有节点必须连通。随机开关组合大概率会出现两种情况:要么断开的开关太多,网络解裂;要么闭合的支路数超过节点数减一,形成了环。这两类个体在数学上都是不可行的。
更聪明的做法是环路编码。思路是先把网络中由联络开关形成的所有基本环找出来,然后对每个基本环,基因位记录一个整数,表示在这个环中打开哪一条支路。这样做的直接好处是:每个环默认断一个开关,网络不会出现环,编码本身就覆盖了大量合法拓扑。
但环路编码也不是完美的。基本环之间可能存在重叠,重叠处理不好时,仍然可能出现孤岛。等价于说,环路编码把“无环”这个约束消掉了,但“连通”这个约束还需要额外检查。所以我的做法是:初始化种群时尽量用环路编码保证拓扑合法,评价个体时再用图论检查连通性。两者配合起来,既保留了编码的天然合法性,又不会漏掉因环重叠引起的孤岛问题。
用这种方法跑 33 节点系统时,初始种群的合法个体比例会明显上升,算法能更快把搜索集中到有意义的区域,而不是浪费大量计算资源去评价一堆不可行解。
3. Matlab实现链路:数据表、潮流计算和NSGA-II核心算子的落地
3.1 网络数据结构和开关状态映射
在 Matlab 里实现,第一步是把配电网数据组织成程序能读的形式。我习惯用矩阵来存,不用面向对象的复杂结构,这样调试时直接看变量就能发现问题。
节点数据表大致是这样的:
% busData: [节点编号, 有功负荷(kW), 无功负荷(kvar)] % 第1行是根节点/平衡节点,负荷可以填0支路数据表大致是这样的:
% branchData: [首端节点, 末端节点, R(ohm), X(ohm), 支路编号] % 支路编号也可以直接用行号代替另外再单独维护一个开关状态向量sw,长度等于可操作开关数量,元素为 0 或 1,表示开关断开或闭合。这里要注意,分段开关和联络开关虽然物理上可能都是开关,但在初始状态上不一样:分段开关默认闭合,联络开关默认断开。
潮流计算前,需要根据sw提取出当前激活的支路集合。我一般会先扫描branchData,找出sw里为 1 的支路索引,然后用这些激活支路构建邻接矩阵或者层次遍历需要的父子关系。整个链路的顺序是“开关状态 → 激活支路 → 网络拓扑 → 潮流计算 → 提取目标值”。
3.2 配电网前推回代潮流:用在这里比牛顿法更直观
配电网重构每评价一个个体都要做一次潮流计算,因此潮流函数必须够快、够稳。对辐射状配电网,我强烈推荐用前推回代法,而不是通用的牛顿-拉夫逊法。
前推回代的思想很像从树梢往树根、再从树根往树梢走两遍:
- 回代过程:从最末端的叶子节点开始,根据节点注入负荷和当前电压,计算流向父节点的电流或功率,一层层累加,一直算到根节点。
- 前推过程:从根节点开始,已知根节点电压,按支路阻抗产生的压降,一层层往下推出每个节点的电压。
然后重复这两个过程,直到前后两次迭代的电压变化小于收敛阈值。辐射状网络没有环,这种纵向分层递推天然适配,不需要生成雅可比矩阵,速度比牛顿法快得多,而且不容易出现因初始点不好导致的不收敛。
我给的简化版潮流函数骨架大致这样,实际代码里先用网络结构求出了每个节点在树中的层级顺序layerOrder和父节点编号,再用数组存储支路阻抗:
function [lossTotal, V, ok] = sweepLoadFlow(pd, qd, Rbr, Xbr, layerOrder, parentOf) n = length(pd); V = ones(n, 1); % 标幺值初始化 Ibr = zeros(size(Rbr)); % 每条激活支路的电流 ok = true; for iter = 1:50 dV = 0; % 回代:从叶子往根方向累加支路电流 for k = n:-1:2 nd = layerOrder(k); p = parentOf(nd); Iinj = conj((pd(nd) + 1j * qd(nd)) / V(nd)); % 支路电流 = 本节点注入电流 + 该节点所有子支路电流之和 Ibr(nd) = Iinj; for child = children{nd} Ibr(nd) = Ibr(nd) + Ibr(child); end end % 前推:从根往叶子方向更新电压 for k = 2:n nd = layerOrder(k); p = parentOf(nd); V(nd) = V(p) - (Rbr(nd) + 1j * Xbr(nd)) * Ibr(nd); dV = max(dV, abs(V(nd) - Vold(nd))); end if dV < 1e-6 break; end end if iter == 50 ok = false; % 未收敛,标记为不可行个体 end lossTotal = sum(abs(Ibr).^2 .* Rbr); lossTotal = real(lossTotal); end这个写法里children是每个节点的子节点列表,实际代码可以通过按layerOrder遍历来维护。需要注意一个常见细节:回代时如果某个节点电压非常小甚至为 0,会造成除以 0 的 NaN。所以我会给V加一个下限判断,出现异常就直接把该个体判为不可行。
3.3 径向约束检查与罚函数处理
前面说了,辐射状拓扑是重构方案的命门。这一步必须在潮流计算之前做,因为一个带环或者断网的拓扑根本没有必要去跑潮流。
我用的判断方法是并查集,也叫 union-find,代码量很小但很关键:
function isRadial = checkRadial(sw, branchData, nBus) activeIdx = find(sw == 1); if length(activeIdx) ~= nBus - 1 isRadial = false; return; end parent = 1:nBus; for k = activeIdx' a = branchData(k, 1); b = branchData(k, 2); ra = a; while parent(ra) ~= ra, ra = parent(ra); end rb = b; while parent(rb) ~= rb, rb = parent(rb); end if ra == rb isRadial = false; % 成环了 return; end parent(ra) = rb; end % 检查是否所有节点都在同一个连通分量里 root = 0; for i = 1:nBus ri = i; while parent(ri) ~= ri, ri = parent(ri); end if root == 0 root = ri; elseif ri ~= root isRadial = false; return; end end isRadial = true; end这个函数的逻辑分三层:先看闭合支路数量必须等于节点数减一;再用并查集检查有没有环;最后检查连通性。三层都通过,才是一个真正的树状辐射网。
在 NSGA-II 的主循环里,我处理不可行解的方式不是简单把一个很大的数塞进目标函数,而是采用“分档”思路:先判断拓扑是否合法,如果不合法,直接把这个个体放在一个独立的不可行解集合里,不参与非支配排序;如果合法但潮流不收敛,或者电压越限,再按越限程度做惩罚。这样可以让算法优先在拓扑合法的区域搜索,其次在可行域内部寻找 Pareto 最优解,效果比单纯罚函数稳定得多。
3.4 NSGA-II 的核心算子实现要点
NSGA-II 代码整体逻辑不复杂,但每个算子都有值得注意的细节。主循环可以概括成下面这段伪代码:
for gen = 1:maxGen F = evaluateAll(pop); % 潮流 + 约束检查 + 目标提取 Fronts = fastNonDominatedSort(pop, F); % 快速非支配排序 D = crowdingDistance(F, Fronts); % 计算拥挤距离 parent = tournamentSelection(pop, Fronts, D); offspring = crossoverMutation(parent); % 交叉 + 变异 merged = [pop; offspring]; Fronts = fastNonDominatedSort(merged, evaluateAll(merged)); pop = environmentalSelection(merged, Fronts); % 精英保留 end这里有几个关键点。
非支配排序的目标是把所有个体分成一层一层的 Pareto 堆栈:第一层是不被任何个体支配的解,第二层是去掉第一层后剩余个体中不被支配的解,依此类推。实现时可以对每个个体记录它支配哪些个体、被多少个体支配,逐步剥壳,复杂度大约是 (O(MN^2)),其中 M 是目标数,N 是种群大小。目标数不超过 3 时,这个成本在工程上完全可接受。
拥挤距离的作用是保证前沿上个体分布均匀。对每一层里的个体,按每个目标函数值排序,然后计算相邻个体在这个目标上的差之和,边界个体直接赋一个很大的数,确保前沿两端被保留。计算时需要注意,如果某个目标维度上所有个体值完全相同,距离可能除零或者算出来全是无穷大,这时要加一个保护判断,直接把该目标贡献设为一个常数。
二进制锦标赛选择的做法是:每次从父代里随机抽两个个体,先比较它们所在的 Pareto 层级,层级小的胜出;如果层级相同,拥挤距离大的胜出。这种机制兼顾了“收敛到前沿”和“前沿要分散”两个目标。
交叉和变异在配电网重构代码里我用得相对保守。交叉采用单点或两点交叉,随机交换两个父代个体的一段基因;变异则以小概率随机改变某个开关基因。变异率太高会破坏已经找到的好结构,太低又容易早熟。我一般把变异率控制在 0.05~0.1,或者更直接地每个个体只变异 1~2 个基因位。
4. 试点网络上的执行效果:Pareto前沿如何读
4.1 参数设置和实验设计
我用一个标准的 33 节点辐射状配电网模型来跑这套代码,这个模型有 1 个根节点、32 条分段支路和 5 条联络支路,负荷数据直接写在脚本里,所有开关的初始状态按常规方式设置:联络开关全部断开,分段开关全部闭合。
算法参数我建议这样起步:种群规模 100,最大迭代次数 200,交叉率 0.9,变异率 0.05。这样的设置在这个规模的网络上不会跑太久,同时 Pareto 前沿的分布已经比较完整。如果网络更大,比如有 100 个以上节点,种群和迭代次数都需要适当增加,否则搜索覆盖度不够,前沿上会出现明显的空洞。
我把目标函数设为三个:最小化有功网损、最小化电压偏差平方和、最小化开关操作次数。为了直观,一般配对展示前两个目标,第三个目标用颜色深浅或者散点大小来表示。如果只想快速验证代码,也可以先只跑前两个目标,把第三个目标当成约束条件来写。
4.2 观察结果:损耗、电压偏差、开关操作数的互相让步
从我跑的结果来看,Pareto 前沿上会出现几个明显的趋势。
如果只看单目标最优,也就是完全不限制开关次数和电压时,系统会把几个关键联络开关全部进行组合尝试,最后网损确实能降不少,但开关操作次数会比较多,而且因为负荷都尽量往阻抗小的路径转移,某些末端节点的电压不一定最好。
如果限制开关操作次数为 1 次或者 2 次,可选的方案数量会大幅缩小,网损下降幅度也明显变小。这说明开关操作次数少并不等于代价最小,它压缩了搜索空间,换来的可能是网损优化空间的损失。
如果把网损和电压偏差同时作为目标,经常会出现这种结果:初始网络或者联络开关全开的状态,网损偏大,电压也偏低;而某些重构方案可以通过重新分配负荷路径,把末端电压抬起来,同时降低损耗。因此两个目标并不是完全冲突的,Pareto 前沿靠“腰部”的解往往是工程上最想要的折中方案。
实际操作中,可以给用户做一个简单决策表,从前沿上挑几个代表解,列清楚每个解的网损、最低节点电压和开关动作次数,让用户按自己的运行场景去选。比如:
| 方案 | 网损相对初始值 | 最低节点电压 | 开关操作次数 | 适用场景 |
|---|---|---|---|---|
| 初始方案 | 基准 | 偏低 | 0 | 不调整 |
| 降损激进方案 | 明显下降 | 勉强满足下限 | 多 | 经济压力大时 |
| 折中方案 | 中等下降 | 明显改善 | 少 | 日常运行推荐 |
这里的关键不是提供一个固定答案,而是让决策者看到不同目标之间的真实代价。
4.3 验证 Pareto 解的正确性:如何检查非支配关系
出一个 Pareto 前沿图不等于结果一定正确。我在调代码的时候,会额外写一个小检查函数,把输出的所有前端解两两比较一遍,确认里面没有任何一个解支配另一个解。
检查逻辑很简单:对两个解 A 和 B,判断 A 是否在所有目标上不差于 B,并且至少在一个目标上严格优于 B。如果存在这种关系,就说明非支配排序有问题,可能是目标函数方向写反了,也可能是支配判断里用了大于号而不是小于号。
常见错误是目标方向搞反:比如“网损最小”写成了“适应度越大越好”,那排序出来的“最优”就变成“最差”了。另一个常见错误是忽略约束条件,导致一些电压越限的不可行解混进了前沿。我都会在评估函数里给每个个体记录一个违反约束的标志,然后在非支配排序之前把所有非法个体单独隔离处理,确保最终输出的 Pareto 前沿全部是可行解。
5. 做重构项目常踩的五个坑和代码扩展方向
5.1 第一个坑:不可行解泛滥,拓扑检查被放到了最后
刚开始做重构项目,最容易犯的错误就是把重心全放在潮流计算和 NSGA-II 排序上,而把辐射状拓扑检查当成一个可有可无的补充。后果就是初始种群里有大量带环或断网的个体,每一代都要花大量时间去计算这些没有意义的潮流,算法搜索效率极低。
我的经验是:拓扑检查必须在潮流计算之前,而且是硬条件。先把结构合法性过滤掉,再考虑电压越限之类的软约束。初始化种群的时候也要注意,尽量用环路编码生成合法个体,而不是纯随机二进制。如果两个都做了,代码的鲁棒性会明显上一个台阶。
5.2 第二个坑:潮流不收敛,不一定是代码写错了
前推回代法在辐射状网络上已经非常稳了,但偶尔还是会出现不收敛或者结果异常的情况。遇到这类问题,不要条件反射式地认为是潮流算法本身的问题,通常检查这几个点:
- 支路参数矩阵有没有写反,首末端节点顺序是否一致。
- 负荷数据的单位是不是统一的,标幺值换算有没有出错。
- 网络层次结构是否来自“当前激活支路”,而不是来自初始完整网络。
- 初始电压向量
V是否可能出现接近 0 或者负数的节点,导致回代时除零。
我实际遇到过一次,折腾了半天发现是某个支路的阻抗填成了 10 倍,潮流迭代一直震荡不收敛。所以潮流函数里最好加一个迭代次数上限,超时直接返回“不可行”,不要让异常值一路传到后面的排序逻辑里。
5.3 第三个坑:种群规模和迭代次数的经验取值
种群太小,Pareto 前沿稀疏,甚至会出现某个区域完全没解;种群太大,每次潮流计算都在烧时间,跑一个算例要几十分钟,调参成本很高。
我用 33 节点这种中等规模网络时,感觉 100 个个体、200 代是比较廉价的起步点。如果算法收敛不好,可以先把迭代次数加到 300,看网损和前沿分布是否继续改善。如果已经连续几十代没有明显变化,再增加迭代次数基本是浪费。变异率方面,我建议从 0.05 开始尝试,如果发现种群在局部区域扎堆,考虑稍微提高变异率或者改用自适应变异。
5.4 第四个坑:拥挤距离计算里的边界处理容易踩雷
拥挤距离虽然只有十几行代码,但边界处理有很多细节。默认做法是每个目标排序后的最前和最后两个个体,拥挤距离设成无穷大,这样可以保护前沿端点。但如果某个目标方向上有多个值相同的个体,排序结果里同一批边界个体都会拿到无穷大,精英选择时可能一下子把大量边界个体同时选进来,反而让前沿内部几乎空掉。
我调整后的做法是:边界个体只在端点位置给无穷大,同值的其他个体按实际坐标差计算距离,并且距离算不出有效值时给一个有限常数。这样既保护了端点,又不至于让边界个体过度繁殖。
5.5 第五个坑:代码想扩展时,评估函数要留好口子
配电网重构往往不只是做一次静态重构。实际项目里经常遇到分布式电源接入、负荷随时间段波动、故障后重构恢复供电等需求。这些扩展其实都可以通过修改评估函数来实现,而不需要推翻整个 NSGA-II 框架。
比如 DG 接入,可以把 DG 的有功和无功输出作为负负荷加进节点注入里,潮流函数几乎不用改。多时段重构,则可以把一天划分为几个典型负荷段,评估时对每个时段分别跑潮流,再把总网损、电压偏差和开关动作次数合并成目标。故障重构则是在约束里加入馈线隔离区域,同时把未失电区域恢复供电作为前提。因为代码里评估函数已经在开关状态到目标值的链路上构好了,扩展时只需要调整evaluateAll内部逻辑即可。
这套代码整体跑通后,最实用的“产品形态”是:输入网络数据、算法参数、目标函数选择开关,输出一行行带全部目标的 Pareto 解,然后由决策者在里面挑最终方案。写成函数脚本后,挂在 Matlab 的工作区里,换一个新的配电网模型只需要替换busData和branchData,基本不用动算法主体。
最后再说个实际体会。我最早把这套代码跑通时,是先从单目标网损优化开始,先把潮流和编码两件事验证没写错,然后才把非支配排序、拥挤距离这些多目标算子加进去。这个顺序特别适合新手:单目标调通可能只花半天,多目标框架加上去,第二天就能出结果。如果一开始就直接上 Pareto 排序,一旦结果不好看,很难判断到底是约束检查错了、潮流错了,还是支配关系计算错了。先减少一个目标维度,调试时问题一下子就清晰了。