做配电网规划这些年,我从“凭经验定光伏位置”起步,到后来试过枚举、粒子群,再到认认真真把遗传算法用在分布式电源优化配置上,算是把这套流程彻底摸透了。当配电网遇上遗传算法,真上手做一轮优化,你会明白它解决的核心问题就是“分布式电源装哪、装多大、达成什么目标”。今天这篇不是教科书式科普,而是我实际跑过算例、踩过坑之后的完整复盘:为什么DG的安装位置和容量必须一起优化,目标函数和约束条件怎么搭才靠谱,染色体怎么编码最省事,潮流计算怎么配合GA跑,参数怎么调才不容易早熟。正在做配电网规划、微电网或储能配置的工程师,以及正在写电力系统课程设计的学生,照着这篇的思路,完全可以直接复现一整套“遗传算法+配电网潮流”的优化框架。
先补充一句背景。所谓分布式电源优化配置,简单讲就是回答“在哪里装、装多大、装什么类型的分布式电源”这三个问题,要求在满足电网安全稳定运行约束的前提下,让某个指标——通常是网损、电压质量或者经济收益——达到最优。人工试凑基本不现实,32个节点里选3个位置装DG,组合就有几十万种,再叠加连续容量变量,搜索空间大得惊人,所以必须上智能优化算法,而遗传算法就是其中经典又实用的一种。
1. 配电网分布式电源优化配置的问题拆解
1.1 为什么“位置+容量”必须当一个整体来优化
辐射状配电网最大的特点,是线路呈放射状从变电站向负荷点延伸,不同节点对系统运行特性的影响差异极大。节点越靠末端,线路等值阻抗越大,电压对功率变化越敏感。这个时候,分布式电源的接入位置直接决定了它对电网的作用方式:装在末端,它像就地补偿器,能撑电压、减损耗;装在首端,它更像一股从源头进来的额外电源,对末端问题帮不上什么忙。
如果只优化容量、不考虑位置,你很可能把DG堆在不需要支撑的地方。反过来,只看位置、忽略容量,也可能出现DG容量远大于该节点负荷和网络承载能力的情况,返送功率导致电压抬升甚至越限。位置和容量就像一枚硬币的两面,必须一起写进优化变量。
从数学模型看,这是一个典型的混合整数非线性规划问题。节点编号是离散整数变量,容量是连续或离散的实数变量,潮流方程是强非线性约束。常规优化工具很难直接处理这种组合,这也是大家最终都转向遗传算法这类智能方法的根本原因。
1.2 三类主流目标函数,怎么选怎么搭
目标函数是优化的指挥棒,定错了后面全白干。先列三个我在工程和研究中实际用过的方向。
第一个就是最小化系统有功损耗,数学式可以写成 min Ploss = ΣGij(Vi² + Vj² − 2ViVjcosθij)。这个目标的好处是物理意义清晰、结果好量化。在典型33节点算例里,合理配置DG后系统线损往往能降30%~50%,数字直观,给领导汇报也好讲。
第二个是最小化电压偏差,常见表达式是 min VD = Σ((Vi − Vref)/Vref)²。光伏大发时段末端电压抬升是配电网的实际痛点,用这个目标可以直接改善电压质量。如果DG装得位置合理,电压曲线会整体变平缓,末端电压被有效抬高。
第三个是年综合费用或经济收益最大化,把DG的建设投资、运维成本、发电收益全部折算成年值再求和。这个目标站在投资方角度最实用,但建模时涉及电价、贴现率、寿命周期等一串假设,数据一多就容易失真。
多数情况下我们做的是加权多目标,但有一个坑必须提醒:不同目标的量纲和数值尺度差得极远。线损可能是200多kW,电压偏差是0.1的量级,经济收益则是几十万元,不归一化直接加权的话,数值小的目标会被算法直接忽略。我的做法是先把每个目标除以它自身的基准值做归一化,再乘权重,这样每个目标对最终适应度的贡献才可比。
1.3 约束条件的典型清单与处理手段
约束条件中最基础的是潮流等式约束,即每个节点的注入与流出功率必须满足基尔霍夫定律,这部分在潮流计算里天然满足。真正需要显式处理的是不等式约束:
- 节点电压上下限,一般按0.95p.u.到1.05p.u.控制;
- 支路电流或视在功率不超过导线载流量上限;
- DG渗透率上限,工程中常取总负荷或变压器容量的20%~50%;
- 单台DG容量上下限,以及安装位置的候选节点集合。
约束处理首推惩罚函数法,把违反约束的程度以惩罚项形式叠加到目标函数里。比如某个个体电压越限0.02p.u.,就在目标值上加上惩罚系数乘以越限量平方。惩罚系数的调法要有策略,先给一个中等值,观察进化结果里违反约束的个体比例,再逐步加大,直到所有保留方案都满足约束,同时又不至于让适应度曲线变得过于平缓。
2. 遗传算法的原理与配电网适配性分析
2.1 用一次配电网方案迭代看懂选择、交叉、变异
遗传算法的核心要素就四个:个体编码、适应度评估、选择、交叉变异。把它放到配电网场景里理解最直观。
假设我们要在某个33节点馈线上选3个点装光伏,每个个体就是一份“3个安装点加各自容量”的配置单。程序先随机生成40份这样的配置单,形成初始种群;然后对每份配置单调用一次潮流计算,得到线损值,再把线损映射成适应度。接着进入进化循环:从种群中用锦标赛法挑出部分优秀个体作为父母,让两个父母交换配置单里的部分信息,生成两个新配置单;再以一定概率随机改动某个配置单的节点或容量。这样循环50到80代后,种群里的配置单会越来越趋近于线损最低的那个方案。
需要明确的是,遗传算法是概率型随机搜索,单次运行得到的是近似最优解,不是数学意义上的全局最优。工程上通常需要用不同随机种子跑多次,取效果稳定且可复现的方案。
注意:适应度方向千万别搞反。目标是求线损最小,适应度就要写成线损的反向函数,比如 fitness = 1/(Ploss+c) 或 fitness = C − Ploss。我第一次写就直接把线损当适应度用了,结果算法一路把种群推向线损最大,跑了60代曲线一路走高,差点以为自己发现了一个“反优化算法”。
2.2 GA、PSO、模拟退火和枚举,到底该用谁
不同算法各有脾气,我用一张对比表把它们摊开来看:
| 算法 | 全局搜索能力 | 离散变量处理 | 收敛速度 | 实现成本 | 参数敏感性 |
|---|---|---|---|---|---|
| 枚举法 | 完全 | 支持 | 节点多时极慢 | 低 | 无 |
| 遗传算法 | 强 | 天然支持 | 中等 | 低 | 中 |
| 粒子群 | 中 | 需改造 | 快 | 低 | 较高 |
| 模拟退火 | 较强 | 支持 | 慢 | 中 | 高 |
| 差分进化 | 较强 | 一般 | 较快 | 中 | 中 |
组合数学碰上枚举法很尴尬。32节点选3个位置,方案数已经有几万量级;选6个位置就是上百万量级,继续往上组合数直接爆炸。粒子群收敛虽快,但它的核心迭代公式是为连续空间设计的,处理节点编号这种离散变量必须加取整和映射,取整会让搜索空间出现大量锯齿,容易困在局部极值。模拟退火理论上能收敛到全局最优,但初始温度、降温系数、邻域算子这些参数一串没调好,实际效果经常赶不上随机搜索。
综合下来,遗传算法能天然处理离散与连续混搭的变量,又不像模拟退火那么娇气,所以在配电网DG优化这个领域,它成为默认主力是有道理的。
2.3 染色体编码方案:二进制还是混合编码
编码是整个遗传算法的地基。在DG优化配置里,常见做法有两类。
第一类是全二进制编码,节点位置和容量都转成二进制串拼接起来。优点是概念简单、和教科书一致,缺点是染色体长度随候选节点增加而变长,搜索空间随之膨胀,容量精度还受二进制位数限制。
第二类是整数与实数混合编码,我实际用最多。每个DG用两个基因表示:第一个基因是节点编号,整数;第二个基因是容量,实数。3个DG就是6个基因,例如[5,450,12,600,18,350]表示节点5装450kW、节点12装600kW、节点18装350kW。这种编码长度紧凑,不随网络规模膨胀,还能分别为位置基因和容量基因设计不同的变异策略。位置基因变异时直接替换成当前未被占用的节点,容量基因变异则在允许范围内加正态扰动。
这里有个坑:同一个节点被多个DG基因选中,会导致DG容量叠加,物理上相当于该节点装了一个大容量电源。但如果你的候选方案里要求各DG分散接入,就必须在变异或初始化阶段做去重修复。我最省事的做法是变异后调用一个去重函数,把重复的位置基因随机改成其他空节点。
3. 完整实操流程:在33节点配电网跑通GA优化
3.1 算例选择:为什么用IEEE 33节点系统
IEEE 33节点系统大概是配电网优化文献里出镜率最高的算例了。33个节点、32条支路,基准电压12.66kV,总负荷约3715kW加2300kvar,包含了一条完整中压馈线的典型特征:负荷沿线路分布、末端节点电压偏低、线路损耗偏高。用它做GA优化配置,方便和大量已发表论文的结果对比,验证算法思路是否正常。
拿到数据后我建议先做一件事:把原始数据录好以后手动跑一遍潮流,把基态线损和各母线电压记录下来。标准的IEEE 33系统基态线损大约在202kW附近,最低节点电压在0.91p.u.左右,不同数据版本会有一些差异。这个基线值就好比标尺,后面算法跑出任何结果都要拿它来对比。如果优化后线损比202kW还高,那不用看,算法实现肯定有问题。
3.2 潮流内核怎么写:前推回代法的MATLAB核心流程
配电网辐射状结构决定了前推回代法最合适,它不需要形成导纳矩阵做矩阵求逆,实现起来直观,速度也快。我把自己常用的核心流程整理成示意代码,重点看结构和顺序:
function [V, Ploss, iter] = bfw_sweep(bus, branch, dg) % bus: Nx3,[节点编号, P负荷(kW), Q负荷(kVar)] % branch: Mx4,[首端节点, 末端节点, R(ohm), X(ohm)] % dg: Kx2,[节点编号, P注入(kW)],Q注入默认0,可按需求扩展 tol = 1e-6; maxIter = 30; N = size(bus,1); M = size(branch,1); V = ones(N,1); Vold = zeros(N,1); for iter = 1:maxIter % --- 回代:从末端往首端推支路电流 --- I = zeros(M,1); for k = 1:M child = branch(k,2); dgP = 0; dgQ = 0; if ~isempty(dg) idx = find(dg(:,1) == child, 1); if ~isempty(idx) dgP = dg(idx,2); dgQ = dg(idx,3); % 没有的话就是0 end end S = (bus(child,2) - dgP) + 1j*(bus(child,3) - dgQ); I(k) = conj(S / V(child)); % 再把该支路电流累加到父支路上,这里需要预先算好的层级列表 end % --- 前推:从首端往末端更新节点电压 --- Vnew = V; for k = 1:M parent = branch(k,1); child = branch(k,2); Vnew(child) = V(parent) - (branch(k,3)+1j*branch(k,4))*I(k); end deltaV = max(abs(Vnew - V)); V = Vnew; if deltaV < tol break; end end Ploss = sum(abs(I).^2 .* branch(:,3)); % 三相有功损耗简化值 end这里必须提醒:回代和前推的遍历顺序不是乱来的。回代要先从叶子节点一层层往根节点收缩,前推正好相反,必须先更新离根节点最近的那一层。实现时可以预先用深度优先搜索生成一个层级列表,按层级顺序遍历,比每次循环都递归搜索要快得多。DG节点在潮流里当负的负荷处理,也就是注入有功功率,这部分逻辑在节点功率汇总里完成。
3.3 GA主循环和算子怎么实现
写GA主循环之前,先把参数定好。以装载3台DG、候选节点为除变电站节点外的32个节点为例:
popsize = 40; maxgen = 60; pc = 0.9; pm = 0.1; numDG = 3; nodeList = 2:33; % 剔除变电站节点后的可安装节点 capMin = 50;capMax = 800; % 单台DG容量范围,kW % 初始化种群 pop = zeros(popsize, 2*numDG); for i = 1:popsize nodes = randperm(length(nodeList), numDG); caps = capMin + (capMax-capMin)*rand(1, numDG); pop(i,:) = reshape([nodeList(nodes); caps], 1, []); end % 适应度预计算 fit = zeros(popsize,1); for i = 1:popsize fit(i) = evaluateFitness(pop(i,:), bus, branch); end % 主循环 elite = 2; bestFit = zeros(maxgen,1); for gen = 1:maxgen newpop = pop; % 精英保留 [~, idx] = sort(fit, 'descend'); eliteSet = pop(idx(1:elite), :); % 锦标赛选择 + 交叉 + 变异 for i = elite+1:2:popsize parent1 = pop(tournamentSelect(fit), :); parent2 = pop(tournamentSelect(fit), :); if rand < pc [child1, child2] = twoPointCrossover(parent1, parent2); else child1 = parent1; child2 = parent2; end child1 = mutate(child1, pm, nodeList, capMin, capMax); child2 = mutate(child2, pm, nodeList, capMin, capMax); newpop(i,:) = child1; if i+1 <= popsize newpop(i+1,:) = child2; end end pop = newpop; pop(1:elite,:) = eliteSet; for i = 1:popsize fit(i) = evaluateFitness(pop(i,:), bus, branch); end bestFit(gen) = max(fit); end这里面的 evaluateFitness 就是把基因解码成DG注入数据,调用前面写的潮流函数,再返回适应度。tournamentSelect 是随机抽3个个体比较适应度。twoPointCrossover 是在容量段上做两点交换,位置基因只跟同一位置基因换。mutate 里分位置基因替换和容量基因扰动两部分。
写完主循环后,单独抽几个个体人工验证一下潮流结果和基因解码是否对得上。这一步能省掉后续大半的调试时间。
3.4 怎么读输出:收敛曲线、电压曲线与方案落地
GA跑完,我只输出三类图:最优适应度随代数的变化曲线、优化前后各节点电压曲线、优化前后各支路损耗柱状对比。这三张图放在一起,能很直观地判断算法质量和方案合理性。
收敛曲线要是前期快速下降、后期平稳,说明参数设置基本合理。曲线迟迟不下降,优先查适应度方向,再查基因解码。曲线中间突然反弹,基本就是精英保留没放好,或者变异率太高毁掉了最优解。
电压曲线是方案检验的照妖镜。优化前末端电压可能在0.91p.u.以下,优化后应该整体抬升,且任何节点都不超1.05p.u.。支路损耗对比则能看出哪些重载线路被有效卸载了。
在33节点算例里,一组合理参数下的优化结果大致是线损比基态下降40%上下,末端电压提升到0.97p.u.以上。但具体数值完全取决于你设定的DG总渗透率阈值和单台容量范围。把DG总容量设得特别大,线损自然降得多,但这种方案在实际工程里可能出现光伏无处消纳的问题,反送功率还可能让变电站母线电压升高到越限。所以我一直强调,优化结果必须回到物理约束里再看一遍,而不是只盯着适应度数字。
4. 遗传算法的参数调优与收敛性控制
4.1 种群规模与迭代次数的合理匹配
种群规模的底线,我个人的经验公式是决策变量维数的5到10倍。3台DG、6个决策变量的时候,40个个体够用;10台DG、20个决策变量的规模,种群最好到60到100。迭代次数不要拍脑袋,先设60代,看收敛曲线末段是否已经平坦;如果最后10代还在明显下降,就把代数加到100甚至150,直到曲线尾部平缓为止。盲目加大种群比盲目增加迭代的副作用要小,但计算时间也涨得快。
4.2 交叉率变异率的经验区间与自适应策略
固定参数最稳妥的区间是交叉率0.7~0.95,变异率0.02~0.2。在这个区间里,交叉率往高调侧重探索新组合,变异率往低调侧重保留已有优质基因。我更推荐自适应变异率,比如写成 pm = 0.1 * (1 - gen/maxgen) + 0.02,也就是gen较小的阶段变异率高,种群探索范围大;gen接近maxgen时变异率收敛到0.02,种群转入精细搜索。这种策略比固定参数能明显缓解“前期就缩到某个局部极值”的毛病,收敛曲线的末端也更稳。
4.3 防止早熟和局部收敛的三个实用招数
早熟的特征是整个种群的个体在迭代中段就变得高度相似,适应度曲线长时间走平。我试过比较有效的手段有三个。
首先是精英保留,每代原样保留适应度前2名的个体,保证最优解不至于在交叉变异中被销毁。其次是降低锦标赛选择的选择压,原来抽5个个体比赛太严了,改成抽2到3个,给多样性留出空间。最后是在变异算子中增加低概率的“重启机制”,比如用1%的概率把某条染色体整个重置成随机新方案,相当于给种群打一针强心剂。
做完这些,如果种群还是早熟,那就回到初始种群质量上找问题。可以用启发式规则生成部分初始个体,比如优先把DG放在负荷重、电压低的末端节点附近,让初始种群直接处在一个高质量区域里,同时配合随机个体保持多样性。
5. 常见问题与排查技巧实录
5.1 适应度曲线一路走高,最后收敛到一个离谱方案
如果适应度曲线一路走高,最后收敛到一个离谱方案,基本可以锁定是方向错误。比如目标是线损最小,却把线损直接当适应度值并取max,结果算法会把种群推向线损最大的方案。排查办法很简单:随机抽出几个个体,手动算一下它们的线损,对着适应度看方向一致不一致。
另外一个常见原因是惩罚项方向写反,导致违反约束的方案反而得到高适应度。出现这种情况,把惩罚项从目标函数里先用0替代跑一遍,排除惩罚代码本身的错误干扰,再逐步加回惩罚项观察变化。
5.2 优化结果每次运行都不一样,波动很大
遗传算法是随机算法,不同初始种群得到细微差异是正常的,但如果最优目标值在一次运行和另一次运行之间差出十几甚至二十多个百分点,说明算法没有充分收敛。我的排查顺序是:先检查初始种群质量是否差得离谱,再看迭代次数够不够,再看变异率是否过高导致最优解频繁被破坏。修复后建议做重复性测试:固定随机种子跑10次,统计最优值、平均最优值和标准差,这个标准差就是方案稳定性的量化指标。如果标准差能控制在目标值的1%以内,这个方案才敢拿去汇报。
5.3 潮流计算在部分个体上不收敛
DG优化配置的搜索过程里,碰到DG接入容量很大或者接入位置很极端的个体,潮流方程可能直接发散。这不是算法的bug,是搜索到了不可行区域的自然表现。处理思路是“挡在入口处”:在潮流函数里设最大迭代次数,比如20次,超过直接返回一个大惩罚值;同时在初始化阶段就做预筛,剔除明显把电压推出1.2p.u.这种极端个体。这样做比在适应度函数里硬算一个发散结果要快得多也稳得多。还有一个小技巧是逐步增加DG容量而不是一次给满,比如先给50%容量跑通,再逐步加大,这样潮流发散会少很多。
5.4 几个我踩过的、容易让人抓狂的小bug
节点编号从1开始这种问题看起来简单,但危害非常大。变电站节点是电源节点,根本不能装DG,可很多代码里节点1就是根节点,初始化时随机选到了它,潮流一算整个系统从根节点开始的电压分布全变了,结果自然全部错乱。我遇到过最诡异的一次是,优化后的DG配置方案里有台光伏装在变电站母线上,线损看起来降得很漂亮,可校核时才发现这个方案根本不能落地。所以初始化阶段必须显式把变电站节点从候选集合里剔除,并且每次解码基因时都再校验一次,防止任何越界的节点编号溜进潮流计算。
还有容量基因越界没修复的问题。很多算子做完变异之后,容量会变成超出上限的浮点数,比如上限800kW的基因,变异后变成1537.6kW,而潮流计算里把它当成了负负荷直接叠加,电压直接被推到1.4p.u.,整个适应度评估彻底失真。处理办法可能显得笨,但很有效:每次变异后加一个修复函数,把所有基因拉回合法区间,再做进一步的去重和越界检查,最后才允许进入潮流计算。宁可多吃一点计算量,也别让一个非法个体污染整个种群。
最后是收敛曲线的误判。我见过不少同学把适应度曲线的纵轴设成对数坐标,画出来的曲线在起始段下降得特别陡峭,看起来很快就收敛了,可实际目标函数只降了不到1%,后面几十代都是在原地兜圈子,根本没有真正优化。这里我给自己立了一个规矩:统一用线性坐标画收敛曲线,并且把目标值标尺打在图上,这样任何人为造成的“视觉收敛”都骗不过自己。
过了这些问题之后,整个GA+配电网框架就基本扎实了。说到最后,我还有一个坚持了很多年的私人习惯:不管遗传算法给出多么漂亮的结果,我都会把优化方案再放到几种不同负荷水平下重新跑潮流验证,比如50%、80%、120%负荷。毕竟实际电网的负荷是波动的,一个只在单一场景下最好的方案,放进工程里是不可靠的。做一步韧性校验,方案才真正有说服力。