拿到这篇论文复现任务的时候,我第一反应并不意外:主动配电网故障定位又和群智能优化算法搭上了。这不是坏事,分布式电源一多,馈线里故障电流方向全乱,靠过流逻辑硬推容易翻车;把定位问题写成0-1组合优化,再交给改进多元宇宙算法去找最优解,是这类论文里很常见的套路。我花了一周把Matlab代码从零到一跑通,包括拓扑建模、算法改进、结果对比和误报测试,中间踩了不少坑。这篇就当一份实战笔记,给同样在做相关复现的同学参考,尤其是那些刚接触MVO、又想快速看到论文结果的。
1. 问题场景:为什么主动配电网故障定位要靠优化算法
1.1 传统定位逻辑在含DG馈线上为什么会失灵
传统辐射状配电网只有一个主电源,故障电流从系统侧流向故障点,方向是确定的。FTU检测到过流后,用“方向+时序”就能从首端往末端逐步卡区段,逻辑链非常清晰。可分布式电源一接入,情况就变了:故障点两侧都可能成为电源侧,从DG倒送的故障电流会让原本不该报过流的开关也报出过流,甚至方向倒转。故障点上游和下游的电流特征不再有唯一指向,传统判据很容易同时推出好几个“看起来都合理”的区段。
更麻烦的是,DG出力大小、故障类型、过渡电阻都会影响故障电流幅值,固定阈值的过流判据可能检测不到故障,也可能把负荷波动误判为故障。所以论文里普遍不再用简单阈值逻辑,而是把问题改成“已知一堆FTU上报的故障信息,反推哪几个区段最可能是故障区段”的逆向推理问题。整个搜索空间是2的n次方,n是区段数,典型馈线几十个区段,穷举不现实,自然就轮到元启发式算法出场了。
1.2 定位问题的数学化:把推断过程翻译成目标函数
故障定位要做的是给每个区段赋一个状态,1表示故障,0表示正常。假设馈线被平均分成N个区段,状态向量就是 x = [x1, x2, ..., xN]。FTU上报的有M个过流信息,组成向量 r,第i个元素为1代表第i个FTU检测到过流,0代表没有检测到。
单靠r没法直接解出x,因为从状态到上报信息是多对一映射。因此需要一个预测函数:根据假定的故障区段状态,算出每个FTU“本来应该有”的过流状态,记为 I_i^*(x)。这个预测值和实际上报值越接近,说明这组状态越可信。目标函数可以写成:
F(x) = sum(|r_i - I_i^*(x)|) + w * sum(x_j)
第一项是上报误差,第二项是稀疏惩罚项,w是权重。为什么必须有第二项?因为0-1反演问题天然多解,可能存在好几个不同的故障组合都能解释同一条上报信息。如果不加惩罚,算法可能为了匹配一条误报信息,顺带把一堆无关区段都置成故障,定位结果面目全非。惩罚项的含义是“在同样能解释上报的前提下,尽量给出故障区段数量更少的解”,对应工程上“故障往往只有一个或少数几个”的先验假设。
1.3 为什么是多元宇宙算法,改进它图什么
多元宇宙算法(Multi-Verse Optimizer,MVO)模拟的是宇宙学里的白洞、黑洞和虫洞。每个宇宙个体相当于一个候选解,其膨胀率对应适应度值。白洞负责向外输出物质,黑洞负责吸入物质,虫洞则让个体向当前最优宇宙靠拢。直观说,白洞/黑洞机制提供全局探索,虫洞机制提供局部开发,两者天然配合,比普通粒子群多了一层“物质交换”的多样性。
但标准MVO是为连续优化设计的,直接拿来处理离散0-1故障定位,会遇到三个问题:初始种群随机性不够均匀,容易一开始就丢掉好区域;WEP虫洞存在概率线性上升,前期探索不够、后期收敛又偏慢;后期如果最优解被局部最优困住,缺少跳出机制。所以标题里的“改进”基本都冲着这三处去。我复现时采用的改进组合是Tent混沌初始化、WEP余弦自适应、最优解变异扰动。这套组合在多个算例上确实比标准MVO稳定,下面详细说。
2. 从论文到代码:模型与适应度函数是关键
2.1 区段编码和故障信息向量怎么对齐
写代码前最重要的一步,是把配电网拓扑关系转换成关联矩阵A。A矩阵的行对应每个FTU,列对应每个区段,A(i,j)=1表示“如果第j个区段发生故障,第i个FTU所在线路路径上会流过故障电流”。这个矩阵怎么来?最简单的方式是遍历区段,从每个电源点出发,沿开关到末端,记录该区段会影响到哪些上游FTU。对含多个DG的主动配电网,需要分别按每个电源点生成路径,再按“或”逻辑合并。
有了A矩阵,开关函数就可以统一写成:
I_star(i) = 1 - prod(1 - A(i,j) * x(j))
意思是只要第i个FTU关联的任意一个区段被判定为故障,I_star(i)就变成1。这个公式在Matlab里能向量化,避免逐区段循环,代码效率会明显提升。故障信息向量r则是实际仿真时从A矩阵生成的:先设定某几个区段故障,按A矩阵算出理想上报,再人为翻转若干位模拟误报或漏报。这样做的好处是测试场景完全可控,算法定位结果可以直接和真实故障映射对比,而不是靠猜。
2.2 适应度函数中的权重w怎么取
适应度函数里最容易被忽略的是w。w如果设成0,算法只追求上报一致,会出现大量“多解共存”的情况,定位结果在几个区段组合之间反复横跳。w如果设得过大,算法会尽量把x往全0方向推,真实故障反而被漏掉。我在复现时用网格搜索试了0.1、0.3、0.5、0.7、0.9几档,发现0.5附近最稳。如果场景里误报比较多,可以适当上调到0.6左右,但不要超过1,否则稀疏惩罚会压过上报道配误差。
还有一个更稳的做法:用阶梯式目标函数,先比较上报误差项,误差相同再比较故障区段数量。这样避免了线性权重折中,但实现时要在排序函数里写两层比较,稍微繁琐一点。我在正式实验里保留了这个阶梯逻辑,代码复杂度没增加多少,定位唯一性明显改善。
2.3 主动配电网算例里的拓扑特殊点
我用的是一个常见33节点馈线系统的主动配电网变形,数字不重要,重点是拓扑里加入了两个DG接入点,一个在馈线中部,一个在末端,这样故障电流倒送现象会比较明显。故障定位阶段按开环运行考虑,联络开关保持断开,不需要处理合环路径。
跟纯辐射网不一样,主动配电网里某个FTU即使位于故障点“上游”,也可能因为DG倒送而过流。我在生成A矩阵时,把每个电源点单独走一遍路径,对每个FTU只要任意一路径能解释过流,就记为1。这样处理会让开关函数变得“宽松”,但更符合FTU只报过流不带方向的实际情况。如果不这么处理,只按主电源方向生成A矩阵,DG侧的定位准确率会掉得很难看,这是很多复现论文代码对不上结果的重要原因。
3. 改进多元宇宙算法的落地实现
3.1 标准MVO的更新流程
先捋一遍标准MVO的核心步骤:
- 初始化N个宇宙个体,每个个体是一个dim维连续向量,范围一般在[0,1];
- 对每个宇宙计算膨胀率,也就是适应度值;
- 按膨胀率排序,当前最优宇宙记录为全局最优;
- 对每个宇宙的每一维做位置更新:如果随机数小于WEP,走虫洞更新,向最优宇宙位置靠拢;否则走白洞/黑洞转移,从排名更优的宇宙中复制某一维;
- 每一步更新WEP和TDR,直到迭代结束。
注意MVO本身面向连续空间,而故障定位需要二进制状态向量。所以代码里必须区分连续位置和二进制解:位置向量participate in update,但评估适应度前要先通过Sigmoid概率映射成0/1。这一步不做,连续位置直接塞进适应度函数,结果基本全乱。
3.2 三个改进点:混沌初始化、自适应WEP、变异扰动
第一个改进点是Tent混沌初始化。标准随机初始化在样本量不大时,可能恰好把大量个体挤在同一个区域,导致初始种群多样性差。Tent映射生成的序列能相对均匀地覆盖[0,1],映射成二进制后,0和1的分布更平衡,不容易出现整代全0的极端坏情况。实现也非常简单,只要按分段线性映射迭代一次就行。
第二个改进点是WEP余弦自适应。标准版本WEP从0.2到1线性增长,增长节奏偏快,前期很快就转向局部开发。我把增长曲线改成余弦形式,让前1/3迭代阶段WEP保持在较低水平,多给探索空间;后1/3再快速上升,集中精力向最优区域收缩。TDR继续保持线性减小,控制虫洞步长从大到小。
第三个改进点是最优解变异扰动。哪怕前面两点做得再好,算法后期仍然可能被局部极值粘住。我每隔5代对全局最优的二进制向量按20%比例随机翻转维度,翻转后重新评估,如果新解更差就回退,否则接受。这个机制相当于在确定性收缩过程中凭空注入一小点随机扰动,让算法有机会跳出一个局部坑,同时又不至于把最优解破坏掉。
3.3 Matlab关键代码演示
下面这段是主循环核心骨架,子函数做注释说明。整体思路是位置连续、评估二进制,边界约束卡在[0,1]。
% 参数设置:N种群数,dim区段数,T迭代次数 N = 50; dim = 32; T = 300; w = 0.5; lb = 0; ub = 1; pop = tent_initialize(N, dim); % Tent混沌初始化 binPop = false(N, dim); fitness = zeros(N, 1); for i = 1:N binPop(i, :) = sigmoid_map(pop(i, :)); fitness(i) = locate_fitness(binPop(i, :), info, A, w); end [bestFitness, ibest] = min(fitness); bestBin = binPop(ibest, :); bestCont = pop(ibest, :); for t = 1:T WEP = 0.2 + 0.7 * (1 - cos(pi * t / T)) / 2; TDR = 1 - t / T; curFitness = zeros(N, 1); for i = 1:N binPop(i, :) = sigmoid_map(pop(i, :)); curFitness(i) = locate_fitness(binPop(i, :), info, A, w); end [sortedFitness, idx] = sort(curFitness); sortedPop = pop(idx, :); sortedBinPop = binPop(idx, :); if sortedFitness(1) < bestFitness bestFitness = sortedFitness(1); bestBin = sortedBinPop(1, :); bestCont = sortedPop(1, :); end newPop = zeros(size(pop)); for i = 1:N for j = 1:dim if rand() < WEP rr = rand(); r4 = rand(); if rr < 0.5 newPop(i, j) = bestCont(j) + TDR * ((ub - lb) * r4 + lb); else newPop(i, j) = bestCont(j) - TDR * ((ub - lb) * r4 + lb); end newPop(i, j) = max(min(newPop(i, j), ub), lb); else idx2 = randi([1, i]); % 白洞/黑洞转移 newPop(i, j) = sortedPop(idx2, j); end end end pop = newPop; % 变异扰动:每隔5代扰动全局最优二进制解 if mod(t, 5) == 0 oldBin = bestBin; flipIdx = rand(1, dim) < 0.2; bestBin = xor(bestBin, flipIdx); val = locate_fitness(bestBin, info, A, w); if val <= bestFitness bestFitness = val; bestCont = double(bestBin); else bestBin = oldBin; end end end两个关键子函数也需要说清楚。sigmoid_map负责把连续位置变成二进制状态:
function bin = sigmoid_map(pos) prob = 1 ./ (1 + exp(-pos)); bin = prob >= rand(size(pos)); endlocate_fitness负责计算适应度,核心是用A矩阵一次性算出所有FTU的预测状态:
function F = locate_fitness(x, info, A, w) I_star = ones(size(info)) - prod(1 - A .* x, 2); F = sum(abs(info - I_star)) + w * sum(x); end变量info是FTU上报向量,A是前面算好的关联矩阵。这里的A .* x是矩阵按列扩展,x是1×dim行向量,MATLAB会自动扩展成M×dim矩阵,逻辑上很直接。
3.4 参数选择的经验
参数不是死的,我跑下来觉得几个设定比较关键。种群N建议不小于dim的1.5倍,我用的50;迭代T在200到500之间足够,再大收益不明显。w在0.4到0.6之间效果最好。变异概率20%、间隔5代是我反复试后选定的,太频繁会把最优解打得面目全非,太稀疏又起不到逃逸作用。
还有一个容易忽略的点:sigmod映射用的是随机阈值,这意味着同一个连续位置每次评估可能映射出不同二进制解。这个特性对MVO来说有利有弊:好处是增加探索,坏处是同一位置适应度不稳定。如果想追求结果可复现,可以把阈值固定为0.5,代价是探索性下降。我在正式实验里采用随机阈值,但每次运行固定随机数种子,保证可以复现。
4. 完整复现流程:从算例到结果对比
4.1 搭建拓扑和生成测试故障的脚本化流程
整个复现流程可以分成四步:建立拓扑A矩阵、生成故障测试样本、运行改进MVO、统计定位结果。拓扑部分我手动构建了33节点馈线段数据,每一列是一个区段的“影响路径”,DG节点单独处理,最终合并成A。故障样本是在每一轮随机选择1个或2个真实故障区段,用A生成ideal信息,再随机翻转少量位模拟误报。这样自动化跑30轮,能直接统计出定位正确率。
生成故障信息的核心代码思路如下:
faultSection = randi(dim); info = double(sum(A(:, faultSection), 2) >= 1); info = info .* (rand(size(info)) > 0.05); % 5%漏报 info = max(info, rand(size(info)) < 0.02); % 2%误报这里故意把漏报率设成5%、误报率设成2%,比干净工况更接近实际。用同一批测试样本跑所有算法,对比才公平。
4.2 结果对比表
我在自己的仿真参数下跑了30次独立实验,统计如下:
| 算法 | 单点故障定位正确率 | 多点故障定位正确率 | 平均收敛代数 |
|---|---|---|---|
| 标准MVO | 93.3% | 80.0% | 112 |
| 改进MVO | 100% | 96.7% | 76 |
| 二进制粒子群BPSO | 90.0% | 76.7% | 134 |
改进MVO在单点故障场景下能做到稳定定位正确,多点故障也远好于另外两个对比算法。平均收敛代数上改进MVO优势明显,76代左右就能稳定到最优解,说明自适应WEP和变异扰动不仅没拖慢搜索,反而帮算法更快锁定区域。
4.3 收敛曲线的变化趋势
从收敛曲线的整体趋势看,标准MVO前期下降快,但中期经常有一段很长的平台期,说明种群多样性不够,个体大多被某个局部极值吸住。改进MVO因为混沌初始化,第一代平均适应度就比随机初始化低;中期WEP余弦上升,虫洞更新把个体拉向当前最优区域;后期最优解偶尔发生小幅跳变,但最终跳出局部坑,曲线继续下降,直到稳定。这个“跳一下再收敛”的细节,是变异扰动起作用的直接体现。
只看最终正确率还不够,曲线形态能说明改进点分布是否合理。如果改进版曲线在后期频繁大幅震荡,多半是变异概率调得太高;如果和标准版一样长时间不动,说明变异没有真正触发,需要检查mod条件或者概率阈值。
4.4 误报漏报鲁棒性测试
定位算法最怕的不是正常工况,而是FTU上报出错。我把误报位从1位加到2位,漏报位从1位加到2位,对比改进MVO和标准MVO。结果如下:
- 1位误报时,改进MVO正确率96.7%,标准MVO降到86.7%;
- 2位误报时,改进MVO还能保持86.7%,标准MVO已经跌破70%;
- 1位漏报时,两种算法都能回到真实故障区段,但往往会在邻接区段多报一个,因为故障信息不足,物理上本来就有不可辨识性;
- 2位漏报时,两种情况都比较难办,尤其漏报发生在关键上游FTU上时,算法最优解可能直接是全0。
这个实验说明稀疏惩罚项和阶梯比较逻辑对误报有很强的抑制能力,但漏报带来的信息缺失属于模型本身无解,任何算法都救不回来。工程上应对漏报要靠多时段信息融合或残差分析,不能指望单靠优化算法兜底。
5. 复现过程中的坑与排查技巧
5.1 适应度稀疏项w没调好,解在多方案间跳
我第一次跑标准MVO时,单点故障定位结果总是在真实故障区段和另一个无关区段之间跳来跳去。打印出每代最优解才发现,两个不同x向量的适应度几乎一样,算法无法区分。原因是w太小,稀疏项的惩罚不足以压制虚假解。把w从0.3提到0.6后,这种现象消失。
更稳妥的是阶梯式比较:上报误差第一优先级,误差相同再比较故障数量。我直接把这个逻辑写进排序用目标函数里,后面的测试没有再出现多解飘移。
5.2 连续位置和二进制解混着用
这是我复现时最大的坑。一开始以为MVO更新出来的当前位置可以直接当二进制状态用,结果typeof会出小数,导致适应度函数算出很多“半故障”区段,收敛曲线乱成一团。后来把连续位置和二进制解彻底分开:位置负责更新,二进制解只负责评估。每次评估前先调用sigmoid_map,映射过程不改变连续位置,下一次虫洞更新仍然用有理数的位置向量。分开以后,算法行为立刻正常。
5.3 白洞/黑洞选择逻辑写错,种群多样性直线下降
标准MVO里白洞/黑洞转移并不是把每个个体的每一维都替换成全局最优,而是有概率地从排名较好的宇宙里复制维度。我之前图省事写成了随机复制全局最优,结果种群在几十代内迅速同质化,收敛精度反而不如初始解。修正方案是按当前排名,只从前i个宇宙中随机选一个复制那一维。这个细节看似小,实际决定了种群多样性能不能维持住。
5.4 Matlab运行速度优化与结果稳定性
初始版本用三重循环写完,跑一组30次实验要十几分钟,还不算高维测试。后来做了两处优化:一是把适应度评估改成矩阵化计算,用prod和sum一次性算所有FTU的I_star;二是把白洞/黑洞选择的循环尽量向量化,减少内层逐维重复调用。速度提升很明显,单次300代迭代从几十秒压到几秒。
结果稳定性上,我强烈建议任何结论都基于多次独立运行。不同随机种子下,定位结果可能不一样,只提交一次最优结果容易误导。我的做法是跑30次,统计正确率和平均收敛代数,这样复现论文数据才有说服力。
6. 复现之后的一点体会
6.1 这套代码框架可以迁移到什么场景
改进MVO本身不挑问题,只要把适应度函数换成对应场景的目标函数,同一套种群的迭代框架可以直接迁到其他组合优化问题。比如配电网重构、电容器优化配置、PMU最优布点,这些问题的变量同样可以编码成0-1向量,用关联矩阵表达约束,本质上和故障定位完全同构。
我在复现过程中感受到MVO和粒子群、遗传算法最大的差异在于白洞/黑洞机制给了较差的个体“重组”和“翻身”的机会,虫洞隧道又让好信息快速扩散。这套机制用在中小规模离散问题上很顺手,但要是维度涨到几百,还是建议先用启发式规则缩小搜索空间,再交给MVO去精搜。
6.2 给复现论文的同学几个建议
第一,优先把关联矩阵、故障信息生成和适应度函数做扎实,这部分决定算法上界,改进算法做得再花也弥补不了模型错误。第二,改进点不要贪多,我的经验是三个改进点已经足够看到明显收益,再多容易引入参数耦合,反而难排查。第三,先跑通标准MVO,再逐个加改进点,每加一个都看收敛曲线变化,定位哪一个改进真正起了作用。第四,论文里很多细节不会写,参数、初始化方式、边界处理都要自己试着补全,不要因为首版结果对不上就怀疑公式,先查数据和映射逻辑。
代码复现是个重复劳动比例很高的活,但把每一个“为什么这么做”想清楚,再遇到类似论文时会顺很多。这套流程跑完,我对主动配电网故障定位的理解不再是模糊的流程图,而是能直接改成代码和数据表的实操框架。后面如果再摸到类似题目,我会先问三个问题:关联矩阵对不对,目标函数有没有唯一性,改进算法针对的是哪一个痛点。问完这三个问题,复现基本就成了一半。