遗传算法微电网优化调度:Python实现与参数整定
简介这是一套基于Python的遗传算法微电网优化调度完整项目面向电力系统、能源管理及智能算法学习者与开发者。项目将光伏、风电、储能与常规机组统一建模可支持并网与孤岛两种运行模式以运行成本、碳排放和供需平衡为约束利用遗传算法的选择、交叉与变异迭代搜索最优发电组合及负荷分配策略适用于课程设计、毕业设计或科研对照实验。压缩包共555个文件约2.91MB以85个py源码为算法核心配以140个js、94个html与41个css等前端资源构成可视化界面另含pyc编译文件和图片、字体等素材目录结构清晰上手门槛较低。目前已有2000余人学习下载。资源内置配置、模型、个体编码、适应度计算、种群管理与主程序等模块使用者可调整各能源单元参数通过前端页面查看并分析调度结果也能够在此框架上扩展多目标优化或不确定性场景对理解遗传算法工程落地及微电网节能降碳调度均有直接帮助。1. 遗传算法微电网优化调度先讲清楚这个标题到底在求什么微电网优化调度要回答的问题很具体拿到未来24小时的负荷、光伏出力、风电出力预测曲线以后每一小时电池该充多少放多少、柴油机发多少电、向大电网买或卖多少电才能在满足所有运行约束的前提下让总运行成本最低。难点在于这是个带连续变量和离散开关的非凸问题柴油机有出力与爬坡限制电池有SOC上下限电网交互还叠加峰谷分时电价传统线性规划要做大量线性化而遗传算法直接对变量编码进化绕开那一大堆凸化处理。下面这套实现只依赖numpy完整的遗传算法python代码详解在第三章算例与参数整定在第四章。适合已经能写Python、想把手头调度问题快速跑成第一版内核的工程师和研究生。2. 建立微电网调度模型目标函数、约束与Python数据结构2.1 决策变量怎么划分为什么把电网功率当松弛变量微电网的典型拓扑包含光伏、风电、蓄电池、柴油发电机和联络线。调度周期取24小时、步长1小时每个时段都有负荷、光伏、风电的预测值以及从电网买电和向电网卖电两套分时电价。决策变量我选两个序列电池出力P_bat(t)放电为正、充电为负和柴油机出力P_diesel(t)。电网交换功率P_grid(t)不参与编码而是由功率平衡方程推导出来当作松弛变量。这样设计的好处是遗传算法个体维度固定为48两个24维序列搜索空间可控坏处是P_grid可能越限必须在适应度函数里用罚函数处理。如果反过来把三个序列全部编码个体变成72维收敛速度会明显变慢第一版不建议这么干。2.2 目标函数和四条硬约束目标函数是日运行成本最小化包含四项柴油燃料成本fuel_price × P_diesel(t)按线性折算实际中柴油机有最低技术出力线性近似后可在第六章做分段线性修正电池退化成本α × |P_bat(t)|把充放电循环折算成每kWh的损耗购电成本max(P_grid(t), 0) × price_buy(t)售电收入max(-P_grid(t), 0) × price_sell(t)从总成本里扣除。约束一共四条功率平衡、电池SOC递推与上下限、柴油机出力与爬坡限制、联络线功率上限。SOC的递推关系是SOC(t) SOC(t-1) - P_bat(t) × Δt / Cap充放电一小时SOC就相应变化P_bat / Cap。2.3 用dataclass把算例参数组织起来写遗传算法之前先把参数结构定下来后面所有函数都引用它避免散落一地的魔法数字。from dataclasses import dataclass import numpy as np dataclass class MGParam: load: np.ndarray # 24h 负荷预测单位 kW pv: np.ndarray # 24h 光伏预测出力kW wind: np.ndarray # 24h 风电预测出力kW price_buy: np.ndarray # 24h 购电价元/kWh price_sell: np.ndarray # 24h 售电价元/kWh cap: float 1000.0 # 电池容量 kWh soc_min: float 0.2 # SOC 下限 soc_max: float 0.9 # SOC 上限 soc_init: float 0.5 # 初始 SOC p_bat_max: float 250.0 # 电池最大充/放功率 kW diesel_min: float 0.0 # 柴油机最小出力 kW diesel_max: float 300.0 # 柴油机最大出力 kW ramp_max: float 120.0 # 柴油机每小时爬坡上限 kW/h grid_max: float 400.0 # 联络线功率上限 kW fuel_price: float 0.68 # 柴油折算成本 元/kWh bat_degrade: float 0.12 # 电池退化折算 元/kWh这里把电池容量设为1000kWh、最大充放功率250kW意味着满功率充放一小时SOC变化0.25不会出现一个时段内数值突变。柴油机爬坡上限120kW/h峰谷切换时出力变化被限制住。grid_max用来约束倒送功率和购电上限分布式光伏大发时段容易触及。3. 遗传算法python代码详解实数编码、进化算子与主循环3.1 个体编码和罚函数最省事的约束处理方法个体是一个48维实数向量前24维是电池各时段出力后24维是柴油机各时段出力。约束处理采用罚函数法SOC越限、电网功率越限、爬坡越限都按超限程度累加一个很大的惩罚量。惩罚量级要高于正常日运行成本这个场景下日成本是几千元量级所以用1e4到1e5保证任何带约束违规的个体都不可能成为全局最优。为什么不直接限制编码范围来避免越界SOC是跨时段递推量单点修改一维变量会连锁影响后续时段的SOC没法靠简单clip解决只能罚。3.2 锦标赛选择、单点交叉、高斯变异的具体实现选择算子用锦标赛每次从种群随机抽3个个体留下适应度最小的那个交叉用单点交叉交叉点落在48维向量中间变异用高斯扰动每个维度以一定概率叠加正态分布噪声。def tournament_select(pop, fits, k3): idx np.random.choice(len(pop), k, replaceFalse) best idx[np.argmin(fits[idx])] return pop[best].copy() def crossover(p1, p2, rate0.85): if np.random.rand() rate: return p1.copy(), p2.copy() point np.random.randint(1, len(p1) - 1) c1 np.concatenate([p1[:point], p2[point:]]) c2 np.concatenate([p2[:point], p1[point:]]) return c1, c2 def mutate(ind, rate0.15, sigma12.0): ind ind.copy() mask np.random.rand(len(ind)) rate ind[mask] np.random.normal(0, sigma, mask.sum()) return ind锦标赛选择里fits[idx]是适应度数组的子集argmin取最小值的索引再映射回原种群下标这一步容易写错的地方是忘记.copy()导致后续交叉变异直接改掉了父代。变异里的sigma12.0需要和决策变量量级匹配电池和柴油机出力都是几十到几百kW12kW的扰动在合理范围如果把sigma设成50后代容易被随机噪声带偏。3.3 适应度函数与带精英保留的遗传算法python主循环适应度函数按2.2节的目标函数和罚函数逐项累加def fitness(ind, p: MGParam): n 24 p_bat ind[:n] p_dz ind[n:] penalty 0.0 # 1) SOC 递推并加罚 soc p.soc_init for t in range(n): soc - p_bat[t] / p.cap # 步长1小时 if soc p.soc_min: penalty 1e5 * (p.soc_min - soc) elif soc p.soc_max: penalty 1e5 * (soc - p.soc_max) # 2) 电网交换功率越限加罚 p_grid p.load - p.pv - p.wind - p_bat - p_dz penalty 1e5 * np.sum(np.maximum(0, p_grid - p.grid_max)) penalty 1e5 * np.sum(np.maximum(0, -p_grid)) # 3) 柴油机爬坡越限加罚 ramp np.abs(np.diff(p_dz)) penalty 1e4 * np.sum(np.maximum(0, ramp - p.ramp_max)) # 4) 运行成本燃料 电池退化 购电 - 售电 cost_fuel (p_dz * p.fuel_price).sum() cost_bat p.bat_degrade * np.abs(p_bat).sum() cost_grid (np.maximum(p_grid, 0) * p.price_buy).sum() \ (np.minimum(p_grid, 0) * p.price_sell).sum() return cost_fuel cost_bat cost_grid penalty注意p_grid是功率平衡的剩余项正值表示从电网买电负值表示倒送。np.minimum(p_grid, 0) * p.price_sell得到的是负数加到总成本里等于扣掉售电收入。爬坡约束用了np.diff只对相邻小时做差24维柴油机序列产生23个差分值。主循环加入精英保留每代把适应度最好的5个个体原样复制进下一代防止最优解在交叉变异中丢失def ga_run(p, pop_size100, max_gen200, elite5): n 24 pop np.zeros((pop_size, 2 * n)) pop[:, :n] np.random.uniform(-p.p_bat_max, p.p_bat_max, (pop_size, n)) pop[:, n:] np.random.uniform(p.diesel_min, p.diesel_max, (pop_size, n)) best_hist [] best_fit np.inf best_ind None for gen in range(max_gen): fits np.array([fitness(ind, p) for ind in pop]) order np.argsort(fits) best_hist.append(fits[order[0]]) if fits[order[0]] best_fit: best_fit fits[order[0]] best_ind pop[order[0]].copy() # 精英直接进入下一代 new_pop [pop[i].copy() for i in order[:elite]] while len(new_pop) pop_size: p1 tournament_select(pop, fits) p2 tournament_select(pop, fits) c1, c2 crossover(p1, p2) c1 mutate(c1) c2 mutate(c2) new_pop.append(c1) new_pop.append(c2) pop np.array(new_pop[:pop_size]) return best_ind, best_hist初始化时电池出力在两倍最大充放电功率区间内随机柴油机在最小和最大出力之间随机。best_ind单独保存因为每代结束后pop会被新种群覆盖不另存就会丢历史最优。new_pop[:pop_size]是为了处理while循环可能多塞一个个体的边界情况。4. 24小时算例与参数设定遗传算法收敛性分析与必调参数4.1 构造可复现的负荷、光伏、风电与分时电价场景用固定随机种子生成一组典型日数据负荷是早晚双峰曲线光伏是中午单峰风电叠加低频波动电价按峰平谷三档设计。所有曲线都是24维数组直接传给MGParam。rng np.random.default_rng(42) t np.arange(24) # 早晚双峰负荷单位 kW load 520 180 * np.exp(-((t - 12) ** 2) / 10) \ 150 * np.exp(-((t - 19) ** 2) / 6) rng.normal(0, 15, 24) # 光伏只在白天出力的钟形曲线 pv np.clip(240 * np.sin(np.pi * (t - 6) / 12), 0, None) # 风电均值90kW附近波动 wind np.clip(90 60 * np.sin(2 * np.pi * t / 24) rng.normal(0, 12, 24), 0, None) # 分时电价高峰1.3元平段1.1元低谷0.42元 price_buy np.where((t 8) (t 12), 1.1, np.where((t 17) (t 22), 1.3, 0.42)) price_sell price_buy * 0.6 param MGParam(loadload, pvpv, windwind, price_buyprice_buy, price_sellprice_sell)负荷最低点出现在凌晨约520kW上下晚高峰约850kW。光伏在正午达到240kW峰值风电全天在60到150kW之间。这个场景的容量配比是刻意设计的电池、柴油机、联络线任何单一资源都不足以覆盖晚高峰必须多资源协同遗传算法才有真正的优化空间。4.2 必调的六个参数与推荐区间参数推荐区间设置依据与常见坑pop_size80 ~ 20048维连续变量80以下容易陷局部最优超过200收益递减max_gen150 ~ 400150代内通常明显收敛400代后基本不变cross_rate0.8 ~ 0.9低于0.7进化太慢后代与父代高度相似mut_rate0.1 ~ 0.2每个个体约5到10个维度被扰动过高会退化成随机搜索mut_sigma5 ~ 15与决策变量量级匹配一般不超过变量范围的十分之一elite3 ~ 10精英太少最优解可能回退太多会压制种群多样性上面这套参数对应种群100、迭代200代时每次运行约2万次适应度评估。纯numpy实现跑完大概几秒到十几秒性能不是瓶颈瓶颈在参数不匹配导致的收敛停滞。4.3 收敛曲线怎么看区分真收敛和提前停滞运行完把best_hist画出来横轴是代数纵轴是适应度。适应度包含罚分量量级跨度大建议用对数坐标import matplotlib.pyplot as plt best_ind, hist ga_run(param, pop_size120, max_gen250) plt.plot(hist) plt.yscale(log) plt.xlabel(generation) plt.ylabel(best fitness (log)) plt.grid(alpha0.3) plt.show()正常的收敛曲线是前50代快速下降、之后斜率趋缓、最后水平。如果曲线在前20代就完全走平多半是提前收敛种群多样性耗尽此时先调小mut_sigma或调大mut_rate而不是加迭代次数。如果曲线一直在波动没有下降趋势先检查罚函数是不是太高导致可行域被惩罚梯度完全屏蔽再检查初值范围是否明显偏离合理区间。5. 调度结果验证与落地时的三个实用技巧5.1 用谷充峰放规则法给遗传算法结果做下限校验遗传算法结果对不对最直接的办法是写一个无优化的规则策略做对比。最简单有效的规则是谷段充电、峰段放电柴油机关停缺电全部由电网补充def rule_baseline(p): p_bat np.zeros(24) p_bat[0] -p.p_bat_max # 00:00-01:00 低谷充电 p_bat[20:22] p.p_bat_max # 20:00-22:00 高峰放电 p_dz np.zeros(24) p_grid p.load - p.pv - p.wind - p_bat - p_dz cost np.maximum(p_grid, 0).dot(p.price_buy) \ np.minimum(p_grid, 0).dot(p.price_sell) \ p.bat_degrade * np.abs(p_bat).sum() return cost这个规则策略的SOC变化是初始0.5充1小时到0.75峰时放2小时到0.25正好不低于0.2约束不越界。遗传算法优化出的成本应当低于或接近这个规则法结果如果反而更高优先检查SOC初始值和罚函数系数大概率是模型公式写错而不是算法问题。5.2 惩罚系数量级要和日成本同量级以上罚函数最容易被忽视的是量级匹配。日运行成本是几千元罚系数用1或10的话越界个体照样可能评上最优用1e6又会让适应度曲面出现悬崖遗传算法被大量罚分吓住所有个体都缩在远离边界的区域反而找不到贴着约束边界的最优解。1e4到1e5是这类调度题目的经验区间。另外对电池和柴油机变量做边界修复比纯罚更高效个体代入适应度前先做一次np.clip把越界变量拉回范围能显著改善收敛性代价是损失一小部分搜索随机性。5.3 末段SOC约束与自适应变异让调度结果可直接落地日常滚动调度要求一天结束时SOC回到初始值附近否则第二天的调度起点漂移日复一日误差累积。加上终端约束在适应度里追加|SOC(24) - soc_init|的罚分就能迫使算法给出可循环的调度方案。配合自适应变异提高后期局部搜索精度def mutate_adaptive(ind, gen_rate, base_sigma12.0): sigma base_sigma * (1.0 - gen_rate) 2.0 * gen_rate return mutate(ind, rate0.15, sigmamax(sigma, 1.5))gen_rate是当前代数占总代数的比例进化前期用大步长探索后期自动收窄到小步长精修比固定sigma更容易在最后几十代里压出几百元的成本差。本文还有配套的精品资源点击获取