
做过多微电网项目的人都有同感大多数时候瓶颈不在“运行”而在“规划”。哪里架联络线、哪些微电网之间互联、要不要走冗余回路这些拓扑设计问题一旦规模上来组合数量是几何级数膨胀。传统的人工推演加启发式规则在小规模场景还能凑合微电网数量到几十个、候选支路上百条之后可以用“上亿种可能”来形容没有一套能在解空间里高效搜索的算法工程上基本很难往下走。我之前在项目里把这一整块处理成了很经典的框架基于约束差分进化算法的多微电网拓扑设计个体用矩阵形式编码整个寻优过程直接在Matlab里完成。这套方案的优势在于不要求目标函数连续、不要求约束可微只要能写出适应度函数就能跑天然适配拓扑设计这种“既有离散决策变量、又有非线性运行约束”的复杂问题。这篇文章把设计思路、算法原理、代码实现和踩坑过程完整过一遍适合正在做微电网规划、配电网网架优化、能量管理系统里线路拓扑优化相关工作的同学参考。1. 为什么把多微电网拓扑设计变成“矩阵优化”问题1.1 从“单微电网”到“多微电网”模型的维度跃迁单个微电网的拓扑结构通常是给定的比如一个交流母线带多个集中负荷、储能和光伏规划时只需要往里配逆变器容量、储能功率。一旦扩成多微电网问题性质就变了多个微电网之间可以互联A微网的冗余电量到底是自己储能、还是通过联络线送给B微网B微网缺电时选择就近接入还是绕路这些接线关系本质上变成了“一张网”的设计。我见过不少项目初期用人工排线、按负荷就近原则一个个试节点数超过十几个以后方案数量直接爆炸人工很难评价哪一个结构更优。这里说的拓扑设计一般包括三个层面第一是决定哪些候选线路需要建设也就是网络的连通结构第二是决定线路的容量和型号选择这通常和拓扑决策耦合在一起第三是在拓扑确定之后校验电网的安全运行和功率分配。三层耦合起来离散变量和连续变量混在一起标准的凸优化工具并不能直接吃下。这也是我倾向于用群体智能算法做上层搜索器的原因它不关心问题是不是凸的团队只要把评价函数写好算法就能在巨大的解空间里自动寻优。而矩阵表达在其中的角色就是把拓扑的离散结构转化成算法在连续空间里可以操作的编码。一条候选支路就是“选”或“不选”的0/1决策全部支路拼起来就是邻接矩阵。这个思路和图的邻接矩阵定义完全一致也符合配电网计算里常用的节点关联矩阵。用矩阵承载个体开发者能直接调用Matlab极其成熟的矩阵运算向量化评估比逐条支路循环快出几个数量级。这个点很多论文一笔带过实际工程中差别非常大同样是500个个体评估一次用for循环可能要跑好几分钟改成矩阵批量计算后几十秒就能出结果直接影响一天能做多少组参数试验。1.2 邻接矩阵一种干净自然的编码方式具体到编码我常用的约定是这样的设微电网节点数为N候选联络支路数为M定义决策向量x的长度为Mx(m)∈[0,1]表示第m条候选支路被选入最终拓扑的程度。与此同时把x还原成N×N的邻接矩阵AA(i,j)A(j,i)1代表节点i和j之间有实际连接其余位置为0。用“支路列表映射到矩阵”的方式比直接用N²长度的矩阵更省内存也更容易处理候选线路集合的工程约束。差分进化在连续空间里搜索x(m)不会天然等于0或1所以评估时要用阈值做二值化再做一次对称化和去自环处理。二值化这个操作在DE里其实是双刃剑。一刀切会产生大量“接近0.49的候选项被淘汰、接近0.51的被保留”的边界效应导致搜索随机性偏大。我的处理方式是在DE变异交叉阶段保留连续实数值只在适应度函数内部投影成离散拓扑投影之后不回写。这样既让DE的搜索空间连续光滑又能保证最终解天然满足拓扑的离散性。这个细节看着不起眼却直接决定了算法找得到找不到更好的解。不少复现论文失败的同行根因就在这里——他们把决策变量完全整数化群体搜索的“进化动力”反而被削弱了。矩阵化的另一个好处是很多图论性质可以直接用现成函数检查。比如判断是否满足辐射状我一般先检查支路数是否等于N-1再用图的连通分量确认全图连通。这两条合起来就是辐射状网络的充要条件。写成代码就是graph(A)、conncomp(G)这类内置API十几行就能完成根本不用自己写深度优先搜索。1.3 约束怎么进模型等式、不等式与辐射状网络接下来是约束部分。多微电网拓扑设计常见约束包括节点功率平衡等式约束、支路容量上下限不等式约束、节点电压允许范围不等式约束、供电可靠性指标不低于某个阈值以及配电线路常见的辐射状拓扑约束。辐射状约束属于组合约束在线性规划里其实不太好写而群体算法里直接用图性质判断反而很简单要求支路数N-1且全图连通。它在我这套代码里以惩罚形式进入总违反量。关于目标函数我通常同时考虑投资成本和运行成本。投资侧包括新建联络线的年化建设成本、设备维护成本运行侧可以用简化网损公式近似。具体实现时如果节点规模不大可以直接接Matpower做潮流计算如果只想验证算法逻辑就用线性化的网损近似。后面的代码先用贴近工程实际的简化模型展示算法骨架也方便后续替换成正式潮流接口。2. 约束差分进化算法与矩阵编码的配合逻辑2.1 差分进化为什么能用来搜索拓扑差分进化是比较老牌的群体优化算法核心思想就是拿种群里的差分向量指导变异。它参数少、思路直观、对目标函数没有连续性要求在电力系统这种处处非凸的领域生存能力比梯度法强得多。标准DE有三步变异产生新候选解交叉混合候选解与父代选择时保留更优个体。典型变异公式写成v x_r1 F×(x_r2 - x_r3)其中F是缩放因子控制差分向量的扰动量。F太大搜索激进F太小容易原地打转工程上常取0.5左右后面我会给一组参考参数。把DE放到矩阵编码上并不需要特别改动变异公式只要记住操作的是向量化后的决策向量就行。每个个体是一行长度为M的向量M等于候选支路数量。变异得到的v是实值向量交叉时按二项式规则逐个维度抽签抽中概率受交叉率CR控制。CR越大子代继承变异向量的维度越多。这套流程和普通DE一模一样区别只在评估之前要把向量还原成矩阵。所以“矩阵优化”字面上指的是个体的本质结构是矩阵数学上它仍然是连续空间上的向量搜索。我遇到过一种理解偏差以为“矩阵优化”是要把DE全部改写为矩阵式的运算。其实不需要。真正要注意的是向量还原为矩阵时的维度对齐以及二值化后的对称性修复。只要处理好这几个映射关系DE照常运行完全不用改算子。2.2 约束处理罚函数不够用直接上可行性法则群体算法处理约束有几种流派。最省事的罚函数法是把违反约束的量乘以很大权重加进目标函数实现容易但罚权重需要反复试设置不好非可行解就会在种群早期占优势或者反过来让搜索过于保守。我更推荐Deb的可行性法则它在选择环节直接引入偏好两个个体都可行时选目标函数值更小的个体A可行、个体B不可行无条件保留A两个个体都不可行保留总约束违反量更小的。这个规则最吸引人的地方是没有罚权重参数不会因为参数设置不当导致搜索失衡在“可行域占比不高”的多微电网问题上尤其管用。我做过一个20节点、候选支路40条的算例可行拓扑在所有状态组合里的占比经常不到3%。这时候只靠罚函数前几十代种群会被大量不可行个体塞满收敛速度肉眼可见地下降。换用可行性法则后算法每一代都往约束违反量小的方向走去即使暂时找不到全局可行解也能先找到接近可行域的路径。当然也要平衡一点可行性法则太强调约束在可行域极小的问题上有时候会损失目标函数的多样性。可以引入温和的松弛策略比如前5%迭代代允许“少量违反但目标更优”的个体进入种群再逐步收紧。这个在调参部分会详细讲初版代码先用纯可行性法则就好。2.3 从连续值到离散二值精度修复与“投影”技巧代码细节上把连续向量还原成拓扑矩阵需要一套稳定流程我拆成四步从向量x映射到矩阵AA(i,j)x(idx(i,j))做对称化处理A(AA)/2对角线清零排除节点自环按阈值把连续值转成0/1然后进入图论约束检查。这个流程每次评估前都要执行所以尽量不要用三重循环要用索引映射。我在代码里用了一张预计算的索引表一次算好位置关系之后每次直接用几千次评估下来能省下相当可观的时间。还有一个坑要留意DE变异后x可能跑到[0,1]区间外面我用截断法直接clamp到边界实测比“反射”或“随机重新初始化”更稳定尤其在约束比例高的问题上。阈值选0.5不是绝对真理。如果最终解数量总是不满N-1条支路适当下调到0.45会提高边的存活率如果冗余回路太多可以上调到0.55。这个阈值本身也可以做成自适应参数但初版不建议变量太多反而不好对比。3. Matlab工程实现从建模到跑通主程序3.1 程序整体结构怎么搭我习惯按“数据准备—种群初始化—DE主循环—结果后处理”四段式组织工程目录每段一个函数文件后续调参数、换场景都方便。完整目录结构如下de_topology/ run_main.m % 主程序入口 initNetwork.m % 生成案例数据节点坐标、负荷、候选支路 initPopulation.m % 初始化种群个体 fitnessEvaluation.m % 适应度与约束违反量计算 constrainDE.m % 约束差分进化主循环 plotResults.m % 收敛曲线和拓扑可视化主程序入口并不复杂设置参数、调用函数、出图。所有参数集中在结构体里方便批量扫参。结构大致是这样的%% 主程序入口 clear; clc; rng(2025); para.N 15; % 微电网节点数 para.NE floor(1.5 * para.N); % 候选支路数量 para.NP 40; % 种群规模 para.F 0.5; % 变异缩放因子 para.CR 0.9; % 交叉率 para.maxGen 200; % 最大迭代代数 para.alpha 1e3; % 目标函数中投资成本权重 load(networkCase.mat); % 场景数据含节点坐标、负荷、候选支路 [X, fval, violation, history] constrainDE(para, net); plotResults(net, X, fval, history);3.2 初始化与矩阵索引表第一步不是急着随机生成个体而是先把“向量维度和矩阵位置”的映射关系算好。以候选支路列表的形式建一个edgeSet第m条候选支路连接(u_m, v_m)那么向量第m维就对应矩阵的(u_m, v_m)位置。初始化代码比较简单function X initPopulation(NP, M) X rand(NP, M); % 每个个体都是M维的0~1向量 end初始个体均匀随机生成候选支路被选中的期望概率是50%。如果你对初始结构有一些工程经验可以把随机分布中心压低到0.3附近让初代拓扑更稀疏减少后续调整成本但初版用均匀分布就足够。3.3 适应度函数把向量还原成拓扑并计算成本适应度函数里先做连续到离散投影再用简化网损公式估算运行成本。我在简化模型里假设线路阻抗已知、节点注入功率已知网损用I²R近似这个近似在验证算法骨架阶段完全够用。真实工程可以在这个位置替换成Matpower潮流。function [f, conV] fitnessEvaluation(x, net) n net.N; % 步骤1向量 - 邻接矩阵 A zeros(n, n); for m 1:size(net.edges, 1) i net.edges(m, 1); j net.edges(m, 2); A(i,j) x(m); A(j,i) x(m); end A (A A) / 2; A(A 0.5) 1; A(A 0.5) 0; A(eye(n) 1) 0; % 步骤2辐射状拓扑检查 conV 0; edgeNum sum(A(:)) / 2; G graph(A); comps conncomp(G); if edgeNum ~ n - 1 || max(comps) ~ 1 conV conV abs(edgeNum - (n - 1)) * 5; conV conV (max(comps) - 1) * 10; end % 步骤3简化网损与投资成本 load_ratio net.load; % 每个节点的负载系数 loss 0; for m 1:size(net.edges, 1) i net.edges(m, 1); j net.edges(m, 2); if A(i,j) 1 loss loss net.r(m) * (abs(load_ratio(i) - load_ratio(j)))^2; end end invest edgeNum * net.lineCost; f net.beta * loss invest; end这一段有几处可以再优化。循环遍历候选支路一般不超过几百条影响很小如果到上万条支路建议把net.edges整理成稀疏邻接索引用矩阵运算一次算完。另外电压差这里用了近似公式真要做严谨慎核就要换潮流模型我在工程落地部分会再强调。3.4 约束差分进化主循环的实现主循环是算法心脏。我按标准的变异—交叉—选择三步走但在选择步骤里嵌入了可行性法则。核心代码大概长这样function [bestX, bestF, history] constrainDE(para, net) NP para.NP; M size(net.edges, 1); X initPopulation(NP, M); f zeros(NP,1); g zeros(NP,1); for i 1:NP [f(i), g(i)] fitnessEvaluation(X(i,:), net); end history zeros(para.maxGen, 1); for gen 1:para.maxGen for i 1:NP % 变异随机取三个互不相同的个体 r randperm(NP, 3); while any(r i) r randperm(NP, 3); end v X(r(1),:) para.F * (X(r(2),:) - X(r(3),:)); % 交叉二项式交叉 jrand randi(M); u zeros(1, M); for d 1:M if rand() para.CR || d jrand u(d) v(d); else u(d) X(i,d); end end % 边界处理 u min(max(u, 0), 1); % 评估 [fu, gu] fitnessEvaluation(u, net); % 可行性法则选择 if (g(i) 0) (gu 0) if fu f(i) X(i,:) u; f(i) fu; g(i) gu; end elseif (g(i) 0) (gu 0) X(i,:) u; f(i) fu; g(i) gu; elseif (g(i) 0) (gu 0) if gu g(i) X(i,:) u; f(i) fu; g(i) gu; end end end [bestF, idx] min(f); history(gen) bestF; end bestX X(idx,:); end这段代码有几个细节容易被忽略。第一个是randperm之后要排除当前个体处理不好会让变异包含自身模态多样性下降。第二个是二项式交叉里jrand的作用它保证子代至少有一个维度来自变异向量否则交叉后可能和父代一模一样算法原地踏步。第三个是可行性法则的判断顺序我坚持把“都可行”“A可行B不可行”“A不可行B不可行但违反量更小”三个分支分开写不会用一句“gug(i)”一刀切避免可行个体被不可行个体挤掉。主循环的速度瓶颈通常在适应度函数上。想加速可以把适应度函数改成批量版本一次性评估整个种群但代码可读性会下降。我的建议是先跑通单个体版本确认逻辑没有毛病再考虑性能优化。3.5 参数设定与结果后处理参数方面我给出一个偏保守的参考区间适合15到30节点规模的多微电网案例参数含义参考范围我的常见取法NP种群规模20 ~ 8040F变异缩放因子0.3 ~ 0.80.5CR交叉率0.6 ~ 1.00.9maxGen最大迭代代数100 ~ 500200阈值二值化阈值0.4 ~ 0.60.5后处理阶段我会画两条东西一条是收敛曲线用来判断是否收敛稳定另一条是最终拓扑图用graph画出来能直观看到哪些联络线被选中。Matlab画拓扑网络很简单一行plot(graph(A), XData, net.x, YData, net.y)就能完成。如果没有节点坐标可以生成spring layout它会自然呈现漂亮的簇状图和实际多微电网的聚集形态很接近。收敛曲线我建议画成半对数坐标DE前期下降快、后期变化小线性坐标下后期几乎看不出差别。4. 调参、踩坑与工程落地4.1 收敛慢、早熟用参数和初始化自救复现过不少差分进化改进版本之后我发现很多“算法失效”的案例并不是算法本身不行而是参数和初值设置有问题。多微电网拓扑场景最容易出现两个症状一是前期种群始终找不到可行解个体全被罚函数拉在原地二是收敛曲线前50代猛降之后完全不动明显早熟。针对第一个症状我建议检查约束是否过于苛刻。比如“支路数必须等于N-1”这种等式约束如果初始随机个体几乎都离它很远DE要花很多代才能靠近。一个实用技巧是先用松弛版问题跑一遍只优化目标不怎么加约束得到一个大致合理的拓扑雏形再把这个雏形作为初代部分个体剩下个体随机生成。这种“热启动”能让收敛速度提升30%以上。针对第二个症状可以把F设置成随代数调节的自适应模式前期用大F保证搜索后期用小F精调。我常用F随代数线性从0.8降到0.3实测比固定0.5平均下降5%左右的最终目标值。CR则反过来前期低后期高用来在后期保留更多交叉多样性。这些都是经验规律换算例后还是建议用控制变量法做一组参数敏感性试验不要盲从。4.2 大规模矩阵运算的效率优化当节点数涨到50以上、候选支路数百条时适应度函数里的循环结构会变得非常拖慢。我做过一组对比15节点跑一代大约0.3秒50节点沿用单个体循环评估一代可能要5秒以上200代就是十几分钟起步这还没算调试时反复运行的次数。大规模场景下必须做两个优化一是对候选支路索引做预计算把edges映射到稀疏矩阵的位置索引二是用向量化计算替代适应度里的for循环。比如网损计算可以写成向量形式A zeros(n,n); idx sub2ind([n n], net.edges(:,1), net.edges(:,2)); A(idx) x; A (A A)/2; A double(A 0.5); A(1:n1:end) 0; lossMatrix abs(net.Pload * ones(1,n) - ones(n,1) * net.Pload.); loss sum(sum(lossMatrix .* A .* net.Zmat));这样把逐条支路的for循环换成一次矩阵点乘速度能提升一个数量级。实际工程中我还建议先清洗掉明显没有工程意义的候选支路例如两端距离超过线路最大允许长度的直接从edgeSet里删掉。这不仅加快速度还能缩小搜索空间、提高解质量。4.3 常见报错与排查速查表调试中常遇到的坑我整理成了一张速查表有类似症状直接按表排查能省很多时间症状可能原因排查与修复所有个体约束违反量一直很大二值化阈值或边界处理出错检查A矩阵是否对称、对角线是否清零打印中间状态收敛曲线水平不动变异向量等于当前个体检查r是否排除了i检查CR是否过小导致交叉失效结果拓扑大量环网辐射状约束没进入违反量确认edgeNumn-1和连通分量数量两个条件都写了结果总缺支路或多余支路阈值与DE搜索不匹配做阈值敏感性扫描0.4~0.6报错Input must be non-negativesub2ind索引出现非法位置检查候选支路端点编号是否越界或重复大规模运行内存不足矩阵重复创建预分配所有数组避免循环内创建大型矩阵4.4 从算法验证到工程落地的几点体会最后聊聊工程落地。很多论文里的约束差分进化算法在标准算例上效果很好但拿到真实多微电网工程会遇到更多麻烦。第一实测负荷是时序变化的单一负荷场景优化出来的拓扑在高峰时段可能不适用这时建议用多个典型日的负荷曲线做多场景加权评估不要只取单点数据。第二实际线路投资不是简单线性函数和地形、走廊宽度、施工条件都有关这些参数不准算法再强也只是空中楼阁。我通常请造价工程师把典型线路造价折算成年化系数再进入优化这件事比重构算法重要得多。另外拓扑设计结果通常要交给详细仿真做校核差分进化得到的是“粗优方案”最终还要用严格潮流、稳定性、N-1校验复核。这一点我在项目里吃过亏算法导出的拓扑在简化模型里很好但放到正式潮流环境里个别节点电压接近下限。后来我在目标函数里加了电压偏移惩罚项同时规定算法输出必须再跑一遍精确潮流不通过就淘汰方案工程可用性提升非常明显。还有一个经验是把中间结果“可视化”做扎实。算法跑完只给一行最优解是没法说服评审和电网侧的。我习惯把每代的目标值、违反量、支路选择率都记录下来画成子图配合最终拓扑图一起输出。支路选择率曲线尤其有价值它显示某些候选联络线几乎每代都被选中说明这些线路是拓扑骨架另一些只在少数代出现说明它们是边际选项。这种可视化视角能帮工程师快速定位关键线路也方便向非算法背景的同事解释优化结果。最后给一个实用建议真正常用的是把差分进化作为上层生成器下层接入潮流工具做校核而不是把算法和潮流耦合得过紧。也就是说差分进化负责搜索“较优拓扑候选集”潮流工具负责验证候选集里的每个拓扑是否满足运行约束。这种分层结构让算法部分保持轻量拟合潮流部分可以随时升级成更精确的模型扩展性非常好。我在这套框架上做过升级把单目标改成了多目标Pareto搜索把静态场景换成了多场景加权改动成本都不高核心的矩阵编码和约束差分进化逻辑一直稳定复用。