
最近在做配电网储能规划把储能选址定容问题用改进遗传算法在Matlab里跑通了而且储能数量可以按需求任意设定。这篇文章把整个建模、算法改造和代码实现过程完整过一遍给同样踩这条路的同学做个参考。储能选址定容这个事外行听起来像是找个地方放电池真做起来才知道坑有多深——位置、容量、数量三个变量互相纠缠再加上配电网潮流约束传统优化方法根本顶不住。我当时试过穷举法、粒子群最后发现改进遗传算法在这个场景下最稳尤其是当领导随口说出我们想比较装3台和装5台储能的区别这种需求时可任意设定储能数量的设计直接帮我省了一周时间。1. 选址定容的数学模型目标函数、约束条件与决策变量怎么定1.1 为什么选址和定容必须放在同一个问题里解储能选址定容拆开看是三个子问题装在哪、装多大、装几台。很多新手容易先选址再定容或者先定容再选址这种解耦做法在简单场景下能凑合但实际配电网里位置和容量是强耦合的。同一笔投资放在分布式光伏密集的馈线末端和放在负荷中心效果完全不同同一个位置容量配大了浪费配小了电压支撑不够。只有把位置变量和容量变量一起丢进优化模型让算法同时搜索才能真正找到全局最优。我在做这个项目时输入数据是IEEE 33节点配电网负荷和光伏出力都按典型日曲线处理。目标函数选了年综合费用最小包括储能投资等年值、运维费用、向上级电网购电费用和网损费用再叠加电压越限惩罚项。决策变量分为两部分一是储能安装位置用0/1变量表示二是每个候选位置上的储能额定容量和额定功率用连续变量表示。这里有个工程细节储能容量和功率通常是成比例配置的常见的容量/功率比是2小时到4小时所以我直接以容量为决策变量额定功率按容量除以额定放电小时数换算少一个变量搜索空间大幅缩小。1.2 目标函数怎么列才能反映真实成本目标函数我采用的是带惩罚的年综合费用具体形式如下min F C_inv C_om C_elec C_loss C_pen其中 C_inv 是储能投资等年值包含单位容量成本、单位功率成本和等年值系数C_om 是年运行维护费用C_elec 是向上级电网购电年费用C_loss 是网络损耗年费用C_pen 是节点电压越限惩罚项。实际代码里为了让量纲统一所有费用都折成万元/年。等年值系数这里容易算错它是 r*(1r)^n / ((1r)^n - 1)r是折现率n是储能寿命一般取10年或15年。我一开始直接拿投资成本除以寿命后来发现资金时间价值没考虑结果方案偏向大容量储能。改成等年值后容量偏大的方案成本明显上升规划结果合理多了。另外运维费用不能简单按投资成本的固定比例估最好按储能的充放电量折算因为频繁充放的储能运维成本确实更高这个差别在目标函数里体现出来之后算法自然倾向于选择充放电次数更均衡的方案。购电费用和网损费用的计算需要先做潮流计算把储能出力叠加到节点注入功率里然后计算系统从上级电网吸收的有功功率。这里我用了分时电价峰平谷时段的购电单价不同储能会在谷时充电、峰时放电相当于套利这部分收益会直接反映在购电费用里。如果忽略分时电价储能的经济价值会被严重低估优化出来的储能容量大概率偏小。1.3 约束条件里最容易漏掉的两个储能规划模型的约束条件一般包括功率平衡约束、节点电压上下限约束、支路潮流约束、储能容量和充放电功率上下限约束、安装数量约束。功率平衡约束就是每个节点注入功率等于负荷与储能出力之差电压约束通常要求0.95到1.05 p.u.。储能本身的约束包括荷电状态SOC上下限、充放电功率限制、一天内能量守恒日初和日末SOC相同。最容易漏掉的是辐射状配电网的支路电流约束以及储能安装数量的整数约束。如果只约束电压和功率潮流计算可能得到严重过载的支路但电压却没越限。另一个容易忽略的是当设定储能数量为K时二进制位置变量中1的个数必须严格等于K这一步如果不用专门的编码修复策略遗传算法很容易产生大量非法个体导致搜索效率低下。功率平衡约束在实际代码里很难严格逐节点满足因为潮流方程是非线性的。我的做法是把潮流方程作为等式约束交给潮流计算本身去求解每一代个体都通过前推回代法算出节点电压和支路功率如果潮流不收敛直接给个体赋一个很大的惩罚值。这样处理之后等式约束不用显式写进目标函数实现更简单计算速度也更快。2. 标准遗传算法在储能规划中的三个水土不服2.1 二进制编码与连续变量的冲突标准遗传算法最初是面向二进制编码设计的选择、交叉、变异都基于0/1串。但储能定容是连续变量容量可以是500 kW也可以是523.7 kW直接用二进制编码表示连续变量需要很高的编码精度染色体长度暴涨搜索空间指数扩大。比如容量范围0到5 MW精度0.01 MW需要9位二进制三个储能就是27位再加上33个位置位染色体长度60位左右勉强能跑但精度不够。我当时试过把容量离散成50 kW一档结果优化出来的最优值总是卡在某个离散台阶上换一批初始化种群结果波动很大。后来改用实数编码表示容量二进制编码表示位置形成混合编码染色体才解决了精度问题。这部分我建议不要偷懒直接用混合编码。二进制编码只负责在哪里装实数编码负责装多大两个段在交叉时同步操作后面我会给具体代码。2.2 早熟收敛与局部搜索能力不足标准遗传算法有两个痛点一是早熟种群在迭代几十代后基因趋同失去多样性陷入局部最优二是局部搜索能力弱算法后期收敛速度明显变慢甚至在最优解附近来回振荡。我跑标准GA时迭代100次后适应度值的变化率已经小于0.1%但距离穷举法验证的最优解还有明显差距。原因在于选择压力过大时超级个体被反复复制其他个体迅速被淘汰种群失去探索能力。而储能规划问题本身是非凸、多峰、含大量等式约束的优化问题局部最优极多。标准GA的交叉概率和变异概率是固定的前期需要的探索和后期需要的精细搜索无法兼顾。最简单的改进是让交叉概率随进化代数递减、变异概率随进化代数递增但实测效果不如自适应方法好后面我会详细讲。2.3 储能数量可任意设定带来的编码难题这是标题里最特殊的需求。传统GA实现固定数量储能时染色体长度固定比如设定3台就编码3个位置和3组容量。但如果要做不同数量的对比每改一次数量就要重新写一套编码。更麻烦的是如果想让算法自己决定装2台还是5台固定长度编码根本做不到。我实现的方案是用户在参数配置中设定一个储能数量上限K_max算法在种群初始化时随机生成1到K_max之间的整数作为当前个体的储能数量K然后位置编码长度动态变化。但这样做交叉和变异算子会非常麻烦。后来我换了一个更稳妥的思路位置编码仍然是33位二进制每个节点装或不装但加了约束保证1的个数等于用户预设的K值。这样可任意设定储能数量变成了用户任意输入K算法自动生成对应数量的选址定容方案代码复用性大大提高。3. 改进遗传算法的核心改造编码、算子与约束处理3.1 混合编码与染色体结构设计改进后的染色体结构分成三段第一段是位置编码长度为配电网节点数N每一位取0或1值为1表示该节点安装储能第二段是容量编码长度为N对应每个候选节点的储能容量如果没有安装则容量为0第三段是功率编码与容量绑定按容量和额定小时数换算不需要单独编码。位置编码和容量编码在交叉、变异时必须联动如果某个位置由1变为0对应的容量也必须清零如果由0变为1需要随机生成一个容量值。这一点在实现时特别容易出错。我在代码里写了一个repair函数每次交叉变异后调用先修正位置和容量的对应关系再检查位置编码中1的个数是否等于用户设定的K如果多了就随机翻转一些1为0如果少了就随机把一些0翻转为1翻转的同时重新生成容量。这样既保证了数量约束又保留了种群的多样性。3.2 自适应交叉变异概率让种群自己调节搜索节奏标准GA固定交叉概率Pc0.9、变异概率Pm0.1问题在于进化前期需要高交叉来产生新组合后期需要高变异来微调。我采用的是基于个体适应度排序的自适应调整如果个体适应度低于种群平均说明是较差个体交叉概率取较大值0.9变异概率也取较大值0.15让它们有更多机会被改造。如果个体适应度高于平均说明是优良个体交叉概率和变异概率都取较小值尽量保留基因同时为了保证不陷入局部最优给精英个体保留一个很小的变异概率0.02防止基因完全停滞。这个策略比随迭代次数变化的自适应效果好很多因为它直接反映了种群当前的状态。迭代中期如果种群平均适应度接近最优优良个体的变异概率会自动降低避免破坏好解而差个体始终保持较高的探索能力。实测改进后收敛速度比标准GA快了大约40%最终解也稳定得多。3.3 精英保留策略与锦标赛选择的配合标准GA容易丢失历史最优解因为交叉和变异可能破坏优良个体。解决方法是精英保留每一代选出适应度最好的前5%个体不参与交叉变异直接复制到下一代如果下一代出现更好的个体就替换精英池。这样做能保证算法性能单调不下降。选择算子我用的是锦标赛选择tournament selection每次从种群中随机抽出2个个体选适应度较高的进入交配池。锦标赛选择的好处是选择压力可控通过调整锦标赛规模可以控制种群多样性。我一般设置锦标赛规模为3配合精英保留效果不错。需要注意的是精英个体的容量编码和位置编码必须一起保留不能只保留位置不保留容量否则就失去了精英的意义。4. Matlab代码实现从参数配置到主循环的完整梳理4.1 主程序框架与数据结构Matlab代码整体结构分五块参数配置、种群初始化、主循环、潮流计算、结果输出。我用结构体opt存储所有参数包括节点数、线路参数、负荷曲线、光伏出力、储能数量K、算法参数种群规模、最大迭代次数、交叉概率范围、变异概率范围等这样改参数方便不会把魔法数字散在代码里。种群我设计成一个结构数组pop每个元素包含chrom位置编码、cap容量编码、fitness适应度值、violation约束违反量。初始化时随机生成K个位置为1其他为0容量在[cap_min, cap_max]之间均匀随机生成。为了加快收敛初始种群里有20%的个体按启发式规则生成优先放在网损灵敏度高的节点。这个细节让我在算例中减少了约15%的迭代次数。主循环的伪代码如下for gen 1:opt.maxGen % 计算适应度 for i 1:opt.popSize pop(i).fitness calcFitness(pop(i), opt); end % 精英保留 elite selectElite(pop, opt); % 选择、交叉、变异生成新种群 newPop pop; while length(newPop) opt.popSize [p1, p2] tournamentSelection(pop, opt); [c1, c2] crossover(p1, p2, opt); c1 mutate(c1, opt); c2 mutate(c2, opt); newPop(end1) c1; %#okAGROW newPop(end1) c2; end % 用精英替换最差个体 newPop replaceElite(newPop, elite, opt); pop newPop; end4.2 核心算子的Matlab实现要点这里给出一段交叉算子的核心代码以单点交叉为例function [child1, child2] crossover(parent1, parent2, opt) % 位置编码交叉 pos1 parent1.chrom; pos2 parent2.chrom; cp randi([1, opt.N - 1]); % 交叉点 new_pos1 [pos1(1:cp), pos2(cp1:end)]; new_pos2 [pos2(1:cp), pos1(cp1:end)]; % 容量编码联动交叉 new_cap1 [parent1.cap(1:cp), parent2.cap(cp1:end)]; new_cap2 [parent2.cap(1:cp), parent1.cap(cp1:end)]; % 修复 child1 repair(new_pos1, new_cap1, opt); child2 repair(new_pos2, new_cap2, opt); end需要注意的是交叉点cp是节点索引位置的切割不是染色体二进制位的切割。位置编码的每个位对应一个节点容量编码的每个元素也对应一个节点这样两个段可以同步交叉。如果直接把位置编码当普通二进制串交叉会出现同一个节点的位置和容量来自不同父代的情况虽然repair能修正但会浪费大量随机数。变异算子的实现思路是对每个个体先按变异概率判断是否变异然后随机选一个当前安装储能的位置将其容量重新随机生成再随机选一个未安装位置以一定概率将其置1并生成容量同时把另一个已装位置置0保证K不变。我测试过这种交换变异很稳定。4.3 可任意设定储能数量的参数接口用户只需要在opt中设置K值比如opt.K 5程序就会自动搜索5个储能的最优选址和定容方案。这是因为repair函数里强制校验位置编码中1的个数等于opt.K。具体实现如下function ind repair(chrom, cap, opt) % 确保位置编码中1的个数等于opt.K idx_ones find(chrom 1); idx_zeros find(chrom 0); num_ones length(idx_ones); if num_ones opt.K % 随机补充缺失的安装位置 add_idx idx_zeros(randperm(length(idx_zeros), opt.K - num_ones)); chrom(add_idx) 1; cap(add_idx) opt.cap_min (opt.cap_max - opt.cap_min) * rand; elseif num_ones opt.K % 随机移除多余的安装位置 rm_idx idx_ones(randperm(num_ones, num_ones - opt.K)); chrom(rm_idx) 0; cap(rm_idx) 0; end % 清零未安装位置容量 cap(chrom 0) 0; ind.chrom chrom; ind.cap cap; end这样无论用户设定K2还是K50只要K小于节点数代码都能自动适应。如果你的需求是让算法自己决定最优数量可以再加一层外层循环或者把K也作为决策变量编码但那样算子会更加复杂建议先从固定K开始跑通。5. 算例对比改进算法提升多少储能数量K怎么影响方案5.1 测试系统与参数设定我在IEEE 33节点系统上做了完整测试。系统基准电压12.66 kV总负荷约3.7 MW在节点18、22、25接入三组分布式光伏总装机1.5 MW。储能候选节点为所有负荷节点单台储能容量范围0.1 MW到1 MW容量/功率比取2小时。储能单位容量成本1500元/kWh单位功率成本800元/kW寿命10年折现率8%。购电电价采用分时电价峰、平、谷分别为1.1、0.7、0.4元/kWh。算法参数种群规模100最大迭代代数200锦标赛规模3精英数5自适应交叉概率范围0.6到0.9变异概率范围0.02到0.15。分别用标准GA和改进IGA进行10次独立重复实验取平均值。5.2 改进前后的收敛曲线对比从10次实验的平均收敛曲线看标准GA在迭代约70代时陷入一个较高的局部最优年综合费用停留在约165万元IGA在迭代约50代时就已经接近最优最终收敛到149万元比标准GA降低了9.7%。更重要的是稳定性IGA的10次实验最优解标准差只有1.2万元而标准GA是4.8万元。这说明改进算法不仅找到了更优解而且对随机种子的依赖大大降低这对工程应用很重要。在储能数量K3时IGA得到的最优选址为节点11、21、30对应容量分别为0.3 MW、0.45 MW、0.6 MW。节点11靠近馈线中部对改善电压分布有较大贡献节点21和30是光伏接入区域的末端储能安装在附近可以就地消纳光伏减少倒送功率。这个结果符合工程直觉也侧面验证了算法的有效性。5.3 储能数量K不同时最优方案如何变化我特意对比了K2、3、4、5四种情况结果非常有参考价值整理成下面的表储能数量K最优位置年综合费用万元电压合格率2节点18、30155.296.5%3节点11、21、30149.399.2%4节点11、18、21、30147.199.6%5节点8、11、18、21、30150.499.7%K2时最优方案把储能放在节点18和30容量分别为0.5 MW和0.8 MW但末端电压在光伏大发时段仍然偏低。K3时年综合费用降到149万元电压合格率提升到99.2%。K4时费用降到147万元但边际改善已经不明显。K5时费用反而上升到150万元因为储能设备投资增加而额外一台储能带来的网损和购电成本降幅不足以抵消投资。这个结果说明储能数量越多越好在工程上并不成立最优数量往往由边际效益决定。利用可任意设定储能数量这个接口我只要循环调用程序几次就能画出费用随K变化的曲线这在项目汇报时非常有用。如果你需要更精确的全局最优数量可以把K也作为优化变量用双层算法或者整数编码处理但上面的灵敏度分析已经能满足大多数场景。6. 工程落地中的经验教训与下一步扩展6.1 罚函数系数选不好结果全是假的在约束处理上我最初用罚函数法把电压越限和功率不平衡加到目标函数里罚系数取10000。结果算法跑出来的最优方案里电压越限节点几乎不存在但储能利用率很低年综合费用看似很低实际上是因为罚得太重算法倾向于把所有储能容量都装到最靠近变电站的节点避免任何电压风险。后来我把罚系数从10000降到100又试了500发现罚系数为500时结果合理但需要手动调参。更稳妥的做法是把罚函数和约束修复结合起来能直接修复的约束比如储能数量、容量上下限在repair函数中处理不好修复的约束如电压上下限再用动态罚函数。动态罚函数的意思是罚系数随迭代次数逐渐增大前期允许一定程度违反约束来扩大搜索范围后期强制收敛到可行域。我最终用的是这个方案效果比固定罚系数稳定得多。6.2 潮流计算是性能瓶颈必须优化调用方式每次适应度评估都需要调用潮流计算而配电网三相潮流往往需要迭代求解这是整个算法最耗时的部分。IEEE 33节点系统规模不大但200代、100个种群总共要跑20000次潮流每次大约0.05秒单次实验也要近20分钟。如果换到大系统这个计算量完全不可接受。我做的优化有三个一是用前推回代法替代牛顿-拉夫逊法对辐射状配电网收敛更快二是潮流计算中只计算有储能和光伏接入的时段将24小时离散成4个典型时段比如峰、平、谷再加光伏大发时段减小时间维度的计算量三是在适应度评估前先做一个快速可行性初筛明显违反节点电压上下限的个体直接赋予很大惩罚值不调用潮流。这三招加起来单次实验时间从20分钟压到了3分钟以内而且精度损失很小。6.3 从单目标到多目标后续可以怎么扩展目前的目标函数是单目标的年综合费用最小但在实际工程中决策者往往还要考虑电压偏差、供电可靠性、储能投资回收期等目标。我下一步的打算是把目标函数拆成经济性和电压质量两个维度使用带精英策略的非支配排序遗传算法NSGA-II求Pareto前沿让项目方在不同偏好下选择折中方案。如果你对多目标感兴趣可以在现有代码框架上把适应度函数改为多个目标函数选择算子改为基于Pareto支配关系再增加拥挤度距离计算扩展起来并不难。另外还有一些可做的改进把季节性负荷变化纳入典型日曲线而不是用单一典型日考虑储能寿命衰减对年综合费用的影响引入电动汽车充电负荷等不确定性。这些扩展都需要重新建模但核心优化框架不用大改。说回可任意设定储能数量这件事我在实际项目里最常用的操作是一口气跑K1到K6的对比然后把年综合费用曲线交给项目方让他们根据预算和工程偏好选方案。这个流程简单、直观也很能说明问题。如果你正在做类似的储能规划项目建议先把固定K的实现跑通再逐步改成上限寻优逐步验证每步结果这样调试压力会小很多。