ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

配电网动态多目标重构与改进粒子群算法Matlab实现详解

配电网动态多目标重构与改进粒子群算法Matlab实现详解 配电网重构这个方向做过的人都知道静态重构跑得再漂亮一到实际项目汇报就会被问一句“负荷是实时变的你算的是哪个时刻的方案”这一问就把静态重构拉回到动态的现实中。配电网动态多目标重构说白了就是在时间轴上重复做拓扑寻优同时还得控制开关动作次数属于典型的动态约束下的多目标优化问题。这篇文章把我的完整实现方案包括改进粒子群算法的设计思路、Matlab代码结构、调参经验和踩坑记录一次性整理出来。适合正在做配电网优化方向的研究生或者需要做节能降损方案设计的工程师参考照着这个思路走至少能少走两个月弯路。1. 配电网动态重构到底在解决什么问题1.1 从静态到动态重构问题的本质变化配电网重构的物理本质很直白配电网里有一批分段开关常闭和联络开关常开通过改变这些开关的开合状态就能改变整个网络的拓扑结构。拓扑一变潮流的分布路径就变网损、电压分布、负载率跟着全变。静态重构就是取某个负荷断面比如某天下午2点的负荷数据在这个断面下找一组开关状态让网损最小或者电压最稳。但真实电网里负荷是24小时波动的。早上工厂开工、晚上居民用电高峰、凌晨低谷负荷曲线起伏很大。如果你只用一个断面做重构早上最优的拓扑到晚上可能就不是最优了甚至可能让某个支路过载。动态重构就是把这个时间维度拉进来把一天的负荷曲线分成若干个时段每个时段给出一个最优拓扑同时还要让整体的开关动作次数尽量少。因为实际运维里开关动作是有机械磨损的动作一次要记一次操作成本一天之内动作二三十次现场运维人员肯定不接受。这里有个容易混淆的点动态重构不是把24个断面各算一遍静态就完事了。如果不加开关动作约束24个小时独立优化结果可能是每小时拓扑都不同相邻时段之间开关状态差异巨大这在实际工程里完全不可接受。所以动态重构的核心难点在于时间维度上的“耦合”处理——既要每个时段局部最优又要在整个时间轴上控制开关切换成本这比静态重构高了一个维度。1.2 目标函数多目标不是简单加权动态多目标重构的目标函数体系通常包含三类指标。我用工程上最常见的组合来说明。第一个目标是网络损耗最小。有功网损的计算公式是所有支路电流平方乘以支路电阻求和即 Ploss Σ(Ii² × Ri)。网损直接对应经济效益配电网的线损率是供电企业的重要考核指标降损是重构最直接的目的。第二个目标是电压质量改善常用的是节点电压偏移最小化。用所有节点的实际电压与额定电压之差的平方和来表示即 Voffset Σ((Vi - Vrated)²)或者用最大电压偏移量。电压质量直接关系到用户侧设备的安全运行也是电能质量考核的硬指标。第三个目标是开关动作次数最少这个目标最体现“动态”二字。一天内所有联络开关和分段开关的状态变化次数总和越少越好工程上一般要求一天不超过4到6次否则运维成本太高。这三个目标经常互相冲突。降网损最好的拓扑很可能需要多次切换开关才能实现电压改善和网损最小也不是完全同向的。多目标处理的标准做法是引入Pareto支配关系不人为给三个目标配权重而是求出一组非支配解集让决策者根据实际运维偏好去选。这里我要特别提醒很多初学者上来就搞加权求和把三个目标揉成一个数这个做法在动态重构里非常容易出问题。因为网损数值千瓦级别和电压偏移数值标幺值0.01级别以及开关次数个位数级别数量级差异巨大权重稍微给得不对结果就偏向某一个目标解的质量很难控制。用Pareto方法天然规避了量纲问题。1.3 约束条件辐射状拓扑是最容易被忽略的坑动态重构的约束条件常规的说法是三大类潮流方程约束、节点电压上下限约束、支路容量约束。这些用数学式子写出来不难但在实际编码求解时最大的坑是辐射状拓扑约束。配电网正常运行时必须保持辐射状结构也就是不能有环否则继电保护配置全部失效短路电流水平也完全改变。这个约束看起来简单但在粒子群这类群体智能算法里处理起来特别棘手。你随机生成一组开关状态很可能就形成了环或者出现了孤岛。我自己的经验是辐射状约束不要靠罚函数硬压而是从编码设计上就直接保证可行性。具体怎么做第三节讲编码的时候我会详细展开。这里先说结论决策变量的编码方式决定了约束处理的难度用基于环路号的编码方式每个基本环路只选一个开关断开就能天然保证网络是辐射状。算法设计里编码方案比罚函数参数重要得多。2. 改进粒子群算法的选型逻辑与改进方向2.1 标准PSO在配电网重构上的三个短板粒子群算法PSO大家应该都熟悉模拟鸟群觅食行为每个粒子代表解空间里的一个候选解通过个体最优和群体最优来更新速度和位置。为什么配电网重构不直接用标准PSO我实际测试下来的感受是三个短板特别明显。第一个短板是二进制编码效率低。配电网重构的决策变量是开关状态很多文献用二进制表示1闭合、0断开每个开关占一维。以33节点系统为例有37条支路那粒子就是37维的二进制向量搜索空间是2^37这是个天文数字。而且二进制PSO的更新本质上是每个维度独立地按概率翻转维度之间没有联动关系导致算法很难学到有效的拓扑模式。第二个短板是早熟收敛。标准PSO在迭代后期粒子容易向群体最优聚集多样性急剧下降一旦陷入局部最优就很难跳出来。配电网重构的解空间是非凸的局部最优极多早熟问题几乎是必然发生。第三个短板是约束处理困难。直接搜二进制开关状态搜出来的解大概率不满足辐射状约束。虽然可以用罚函数强行把不满足约束的解淘汰掉但罚函数系数很难调调大了搜索效率极低调小了又可能搜出环网解。2.2 编码改进环路编码是核心突破针对第一个和第三个短板我用的是环路编码方式。实现思路是这样的先通过图搜索算法从每个联络开关出发沿着配电网络找到对应的基本环路。一个基本环路就是由一条联络支路和一段配电线路组成的闭合回路。然后每个粒子只在每个环路上选择一个开关断开粒子维度就等于联络开关的数量。以经典的IEEE 33节点系统为例它有5个联络开关对应5个基本环路那么粒子就是5维的向量每一维的取值是该环路上某个支路的编号代表断开这条支路对应的开关。这样编码有几个直接好处一是搜索空间从2^37骤降到5个环路的组合空间数量级大幅缩小二是只要保证每个环路选且只选一条支路断开就能保证最终拓扑不含环三是每个粒子天然对应一个辐射状网络前提是解码后还需要检查连通性不能出现多个孤立岛屿。这个编码改进对算法性能的提升是决定性的。我测试过用二进制编码和环路编码跑同一个33节点算例相同迭代次数下环路编码找到的最优网损比二进制编码低大约5%到8%而且收敛速度快了不止一倍。关键是环路编码几乎不产生不可行解算法的搜索效率全部用在找最优拓扑上而不是浪费在罚函数反复试探上。2.3 参数自适应惯性权重与学习因子的时变策略标准PSO的速度更新公式是v w×v c1×r1×(pbest - x) c2×r2×(gbest - x)。w是惯性权重c1和c2是学习因子。这个公式里w控制全局搜索和局部搜索的平衡c1和c2控制粒子向个体最优和群体最优的靠近程度。我的改进方案有两处。第一处是惯性权重线性递减从初始的0.9逐渐降到0.4。迭代早期w大粒子飞行速度快探索范围广有利于跳出局部最优迭代后期w小粒子在原地精细搜索有利于收敛。这个策略在许多优化问题中都有效配电网重构也不例外。不过要注意递减方式线性递减就够用不用搞太复杂的自适应曲线实测效果差别不大反而线性递减参数少、更稳健。第二处是学习因子的异步时变。c1初始较大约2.5、后期减小到0.5c2初始较小约0.5、后期增大到2.5。这样设计的原因是迭代早期粒子应该多向自己的历史最优学习保持个体多样性迭代后期粒子应该多向群体最优学习加快收敛速度。两个学习因子随时间交换主次位置这个改进在动态重构这种多峰问题上比较有效能明显降低早熟概率。2.4 多目标处理Pareto支配与外部档案前面说了动态重构是多目标问题我采用的是Pareto支配加外部档案的思路。具体机制是每一轮迭代把所有粒子进行非支配排序把当前的非支配解放入一个外部档案集档案集有容量上限比如50个解。当档案集满了用拥挤距离排序去掉最密集区域的解保留分布在Pareto前沿不同区域的解保证解的多样性。迭代结束时外部档案集里的解就是一组Pareto非支配解。决策者可以在这些解里根据实际偏好选一个如果今天馈线负载率告警就优先选电压改善好的解如果今天有检修计划不方便动作开关就优先选开关动作次数少的解。这种灵活性是单目标加权求和解不具备的。我踩过的一个坑是外部档案更新过于频繁导致算法在中间阶段过度开发而搜索不足。后来加了更新门限只有新一代粒子中真正出现了能够支配档案中某些解的个体才更新档案否则不更新。这个细节让算法的收敛性和多样性都更稳。3. Matlab实现的核心环节拆解3.1 数据准备节点支路模型与负荷曲线Matlab实现的第一步是数据组织。我习惯用两个矩阵来定义配电网拓扑一个是节点数据矩阵bus每行对应一个节点包含节点编号、有功负荷、无功负荷、电压基准值等另一个是支路数据矩阵branch每行对应一条支路包含首端节点、末端节点、支路电阻、支路电抗、容量上限。以33节点系统为例bus矩阵是33行branch矩阵是37行其中5条是联络支路初始状态为断开。负荷数据不能只用一个断面需要准备一天的负荷曲线。工程上常见做法是用相对负荷系数乘基准负荷比如24个时点的负荷系数向量load_coeff每个时点都对应一组所有节点的负荷值。动态重构的前提就是这个时变负荷曲线。有个细节要提醒配电网的负荷数据正常情况下节点功率因数是近似恒定的无功负荷可以按有功负荷乘一个固定功率因数来估算不必每个节点单独配无功曲线否则数据处理量会大很多而且对结果影响很小。我在实际建模中用的是恒功率因数近似效果足够好。3.2 潮流计算前推回代法的Matlab实现配电网重构过程中要反复计算潮流潮流计算的速度直接决定整个算法的耗时。配电网的特点是辐射状结构、R/X比偏高用牛顿-拉夫逊法容易不收敛而且每次都要重新形成雅可比矩阵计算开销大。这个场景下最合适的是前推回代法。前推回代的做法分两步第一步回代也叫前推从末端节点开始假设各节点电压为额定值逐支路向前推算支路电流和各节点注入功率第二步是回代向前推算电压从源节点开始根据支路电流和支路阻抗逐支路向后推算各节点电压。两步交替迭代直到两次迭代的节点电压差小于阈值。Matlab里用稀疏矩阵和向量化运算能显著提速。核心代码逻辑大致是这样的% 根据当前拓扑形成节点-支路关联关系 % 前推从末端向首端累加支路电流 for k numel(branch_order):-1:1 i branch_from(k); j branch_to(k); current(j) conj(load_power(j) / voltage(j)); current(i) current(i) current(j); end % 回代从首端向末端更新电压 for k 1:numel(branch_order) i branch_from(k); j branch_to(k); voltage(j) voltage(i) - (branch_R(k) 1i*branch_X(k)) * current(j); end这个实现里最关键的是branch_order的排序必须按从源节点到末端节点的层级顺序排列才能保证前推回代的方向正确。我写了一个广度优先搜索函数来生成这个排序每次拓扑变化后重新生成一次。实测33节点系统单次潮流计算在毫秒级69节点系统也只要几毫秒完全能满足粒子群迭代几千次的计算需求。3.3 基本环路搜索与粒子解码环路编码的实现难点在于怎么找到每个联络开关对应的基本环路。我的做法是先读入拓扑数据识别所有初始断开的联络开关然后逐个联络开关处理——把该联络开关临时闭合用深度优先搜索或者广度优先搜索从联络开关的一端出发找一条到另一端的最短路径路径经过的所有支路加上联络开关本身就构成了一个基本环路。以33节点系统为例5个联络开关生成5个基本环路。每个环路的长度不一有的包含8条支路有的包含14条支路。粒子编码时每个粒子是一个5维向量每一维的取值范围是0到该环路长度减1的整数表示该环路上第几个支路被断开。解码过程就是把这些索引映射回真实的支路编号然后更新拓扑状态。这里有个非常重要的细节解码后的网络虽然每个环路都断了一条支路理论上没有环但有可能出现孤立节点或者多个互不连通的子网络。因为一个节点如果同时处于多个环路中断开选择可能把某个节点从主网络里“切”出去。处理办法是在每次解码后做一个连通性检查从电源节点做一次广度优先搜索如果所有负荷节点都能被访问到则网络连通否则丢弃该粒子或者重新解码。这个检查用矩阵运算实现非常快但必须做。3.4 动态时间分段K-means聚类与典型断面动态重构的时变负荷处理我采用的方法是K-means聚类分段。思路是把一天24小时的负荷曲线数据点聚成k类每一类的聚类中心作为该时段的典型负荷相邻同类时点连续合并成时段最终得到若干个重构时段。分段数k的选取很关键。我测试过k从2到6的情况结论是k等于3到4时效果较好。k太小比如2段早上和晚上的负荷高峰被揉在一起重构方案的降损效果不明显k太大比如6段以上每个时段都很短拓扑切换频繁开关动作次数指标会恶化而且计算量成倍增加。实际项目中我一般先用肘部法则看聚类中心距离随k的变化趋势选曲线拐点处的k值通常落在3到4。分段完成之后动态重构的求解策略是每个时段内以该时段的典型负荷断面为目标调用改进粒子群算法求一个Pareto最优解集然后用一个上层决策在全部时段之间做协调目标是整天的开关动作总次数最小同时尽量保持每个时段解的Pareto最优性。这个协调我用的是动态规划思想以相邻时段拓扑差异造成的开关动作次数为转移代价逐时段递推选择。这个两层结构实现起来逻辑清晰每个模块可以单独测试强烈推荐这个方案。3.5 算法主流程与终止条件整个动态重构算法的主流程我组织成如下几个模块顺序调用第一步加载网络数据和负荷曲线数据生成基本环路信息。第二步对负荷曲线做K-means聚类确定时段划分。第三步对每个时段分别调用改进粒子群求解器得到该时段的Pareto解集。第四步把所有时段的解集组合成一个全局候选池用动态规划做时段间协调选出整日综合最优的拓扑序列。第五步输出结果包括各时段拓扑状态、网损、电压指标、开关动作次数以及计算耗时。粒子群求解器的内部流程是初始化种群迭代循环每次迭代更新惯性权重和学习因子计算每个粒子的潮流并得到目标函数值做Pareto非支配排序更新外部档案集更新粒子的个体最优和群体最优更新速度和位置最后检查终止条件。终止条件我用的是“最大迭代次数收敛停滞检测”当外部档案集在连续30次迭代内都没有更新说明算法已经收敛到稳定状态提前退出节省计算时间。这个策略实测能省下约三成算力而且不损失解的质量。4. 参数调试与实验结果分析4.1 算法参数设置经验值参考改进粒子群算法的参数设置我经过多次试验得到一个比较稳的默认配置分享出来供参考。种群规模取30到50。配电网重构的决策维度等于环路数33节点系统是5维50维以内的决策空间30个粒子就足够探索了。69节点系统有5个联络开关同样适用。如果系统更大比如上百节点、七八个联络开关种群适当增加到60左右。最大迭代次数取200到300。配合收敛停滞检测实际运行大多数情况在一百多代就停下来了。太小的迭代次数小于100在动态重构多时段场景下Pareto前沿还没成型不建议。惯性权重w从0.9线性降到0.4学习因子c1从2.5降到0.5、c2从0.5升到2.5这些参数的调整幅度不用太精细线性变化就够关键是异步策略的方向别搞反——早期重个体探索、后期重群体收敛。4.2 时段划分对结果的影响我用33节点系统做了对比实验负荷曲线取某地区典型夏季日均负荷k取2、3、4、5各跑一次结果对比非常直观。k2时全天被分成两个时段早高峰和晚高峰被混在一个时段里该时段的典型负荷取均值后重构拓扑只对“平均状态”最优早晚两个真正的高峰时段反而网损下降不明显。全日网损降低率大约只有11%。k3时典型时段大致对应上午负荷爬坡、下午平段晚高峰、夜间低谷三段网损降低率提升到约14%开关动作次数在4到6次之间这个结果已经比较符合工程预期了。k5时网损降低率还能再提高约1.5个百分点但开关动作次数增加到9到11次明显超出了实际运维可接受范围。这就验证了前面说的动态重构不能单纯追求网损最优必须靠开关动作次数约束来平衡。工程上定k值时建议先问清楚现场对开关动作次数的容忍度再反推合适的时段数。4.3 33节点算例的典型结果以33节点系统为例我给出一次典型运行的结果数据。基准情况下不重构全日网损累计约268千瓦时的电量损失。改进粒子群动态重构后全天分3个时段时段切换共动作5次全日网损降到约230千瓦时降损率约14.2%。全网最低节点电压从基准情况的0.913标幺值提升到重构后的0.938电压质量改善明显。总计算耗时在普通笔记本8核i7上约40秒。这个结果和我看到的许多文献数据是吻合的。33节点系统的重构降损潜力大致就在10%到15%区间如果哪个算法号称能把网损降30%基本可以判断算错了比如用错了潮流基准或者目标函数没算对。拿到算例结果之后建议自己先用枚举法或者简单启发式方法验证一遍基准值再跑改进算法确认改进量不是虚的。5. 常见问题与排查技巧实录5.1 潮流计算不收敛怎么办前推回代法在辐射状网络下非常稳定但如果出现不收敛大概率是拓扑出了问题。最常见的两种情况一是解码出的网络存在孤岛被断开的支路导致一部分节点脱离主网络形成电气死岛潮流根本算不通二是支路顺序排序错误前推回代方向弄反了。排查方法很简单在潮流计算函数入口处加一个断言检查从电源节点出发能否访问所有节点如果不能就返回一个很大的网损值当作不可行解丢弃。另外把支路拓扑画出来看看Matlab里用gplot或者graph对象可视化一下拓扑问题一眼就能看出来。我刚开始调试的时候至少有五分之一的时间花在检查拓扑连通性上把这个检查做成断言之后这类问题就很少再困扰我了。5.2 粒子群早熟收敛怎么解决如果跑了几次算法发现每次都收敛到同一个解而且这个解明显不如预期那基本上就是早熟收敛了。我的处理手段有三个按优先级排序。第一是检查外部档案更新策略是不是档案更新太频繁导致算法过早集中到少数几个解附近。如果不是第二步是调整惯性权重和学习因子关键是让c1在前期保持较大值甚至可以给粒子增加一个随机变异操作——以很小的概率比如0.05随机重置某几个维度让粒子跳出局部最优。第三步是把初始种群改成混沌初始化用Logistic映射生成初始位置让初始解在解空间分布更均匀而不是完全随机散布这个技巧在配电网重构的多峰问题上效果还行。如果以上都不行建议增加种群规模。不过种群规模超过80之后边际收益就很低了不如从编码和约束处理上找原因。5.3 计算时间太长怎么优化动态重构的计算量是时段数乘上单时段粒子群的计算量如果时段数取5、迭代次数取300总潮流计算次数大约是5×300×5075000次每次几毫秒总共也要几十秒。还可以接受但如果你跑的算例更大或者需要重复调参时间就有点急了。我的优化思路有三个。第一是向量化潮流计算的Matlab代码避免在循环里用标量运算充分利用矩阵运算的向量化加速。第二是配置并行计算Matlab的parfor可以把种群内每个粒子的潮流计算分配到不同核上我是8核机器实测能缩短到单核的三分之一到四分之一。第三是加上收敛停滞检测前面说过了很多算例到一百五六十代就停了省下接近一半的迭代时间。另外提一句如果只是在验证算法建议先用33节点小算例跑通全部流程之后再换大系统。我用69节点系统直接调试初期代码每次改bug要等很久效率极低。先小后大这是被反复证明有效的调试验证路径。5.4 动态协调结果开关动作过多如果时段间协调做完后开关动作次数仍然偏高最直接的做法是降低分解时段的k值从5降到3。还有一种情况是各时段Pareto解集里供协调选择的解形态单一比如所有解对应的开关状态都差异很大动态规划没有“平滑”的中间选项可选。这个问题的根源在于单时段优化过于强调网损最优没有把“与上一时段状态的差异”纳入考量。我的临时处理办法是给单时段的目标函数里加一个小的“惯性项”如果某个开关状态和上一时段的推荐状态相同就加一个小奖励分。这么做会让每时段解集里自然包含一些“改动较小”的候选解给动态规划提供更多选择空间。这个技巧不算复杂但实际效果很明显开关动作次数能减少两三成。5.5 测试系统与基准数据选择配电网重构研究常用的标准算例是IEEE 33节点系统和IEEE 69节点系统这两个系统的参数在Matpower中可以直接获取网上也有很多现成的Matlab数据文件可以下载。选算例时有几点建议一是先用33节点系统做算法验证它的最优网损值在文献里有大量对照方便判断你的算法是否跑偏二是如果做动态重构需要准备一条符合实际的负荷曲线不要用恒定的负荷系数否则体现不出动态重构的优势三是在数据文件里保留支路的初始开关状态标记哪些是联络开关、哪些是分段开关这个信息在后面环路搜索时要反复用到。我个人建议把33节点系统作为“基准测试平台”所有算法改进都先在这个平台上验证效果确认有效后再迁移到69节点或者135节点系统。用标准算例的好处是文献对照充分你的结果是好是坏一目了然评审或答辩时也更有说服力。在实际操作中还有一个容易被忽略的点Matlab版本差异对性能的影响。我早期用旧版本平台跑粒子群循环结构效率偏低后来换到较新的版本向量化代码的执行效率有明显提升。如果你在写代码时尽可能用向量化替代for循环不仅当前版本跑得快以后换环境也不用担心性能退化。潮流计算函数、环路搜索函数、解码函数这三块是核心建议单独写成函数文件并添加注释后续想尝试其他算法比如遗传算法、模拟退火时可以直接复用这套底层工具。配电网动态重构这个课题算法本身是一部分工程化的数据组织和模块拆分能力往往才是决定项目能不能按期交付的关键。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进