ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于二进制遗传算法的电力经济调度优化:考虑排放与网损的Python实现

基于二进制遗传算法的电力经济调度优化:考虑排放与网损的Python实现 做电力系统优化的人多半都经历过这种场景教材上的经济调度例题用等微增率几页纸就能解完可一旦把排放目标、输电损耗这些现实因素加进去传统方法立刻变得不好使。我最近在 Python 里完整实现了一套基于二进制的遗传算法考虑排放目标和输电损耗的经济调度求解程序从目标函数建模、染色体编码、遗传算子设计到结果校验把整个链路走了一遍。这篇文章就是我的完整复盘把数学模型、代码结构和调参经验一并写清楚给正在做类似优化课题、或者想用遗传算法处理带约束工程优化问题的读者一份可直接参考的样本。先说结论这套方法对三机系统这种规模的问题非常够用几十行核心代码就能跑出稳定的收敛结果而且它对目标函数是否凸、是否连续不敏感——这正是排放项加入之后最让人头疼的地方。文章后面我会给出完整可运行的代码框架、三机算例的测试结果以及我在调参过程中踩过的几个典型坑。环境只需要 numpy 和 matplotlib不需要其他复杂的依赖。1. 三个目标叠加后问题到底难在哪1.1 传统经济调度等微增率几页纸就能解传统经济调度要回答的问题很简单系统有 N 台机组每台机组出力为 (P_i)燃料成本是二次函数 (a_iP_i^2 b_iP_i c_i)在满足总负荷 (P_D) 的前提下怎么分配各机组出力让总燃料成本最小。数学上就是一个带等式约束和不等式约束的凸优化问题[ \min \sum_{i1}^{N} (a_iP_i^2 b_iP_i c_i) ] [ \text{s.t.} \quad \sum_{i1}^{N} P_i P_D, \quad P_{i,\min} \le P_i \le P_{i,\max} ]这类问题用拉格朗日乘子法求导令各机组等微增率相等再迭代修正常数 λ 就能解。教科书里通常叫 λ 迭代法几页纸就能把算法流程讲完手算都能算出来。我最早做这个课题的时候也觉得这不就是个求导题吗但事情远没那么简单。1.2 加入排放目标单目标变多目标现实中的电厂关心的不只是钱还有排放。燃煤机组的污染物排放量通常用二次函数加一个指数项表示[ E_i(P_i) \alpha_iP_i^2 \beta_iP_i \gamma_i \xi_i e^{\lambda_i P_i} ]那个指数项模拟的是机组在接近满出力时排放急剧恶化的物理特性这让排放曲线变得不再光滑甚至在某些区间是非凸的。λ 迭代法基于一阶导数为零遇到这种带指数项的复杂函数导函数就可能出现多解或者无解析解的情况传统方法处理起来非常别扭。更关键的是成本和排放是两个互相冲突的目标。省钱的出力方案往往排放高低排放的方案往往要多花钱。这就不是单一求导能解决的了得想办法把两个目标揉成一个标量或者直接求多目标的 Pareto 前沿。1.3 加入输电损耗等式约束变成非线性第三个维度是输电损耗。实际系统中发电机发出的功率不可能全部送到负荷线路和变压器上会有网损。工程上常用 B 系数法Kron 公式来估算网损[ P_{\text{loss}} \sum_{i1}^{N}\sum_{j1}^{N} P_i B_{ij} P_j \sum_{i1}^{N} B_{0i} P_i B_{00} ]这个公式一看就不太好伺候。网损不是固定值而是随着各机组出力组合变化的而且它是出力之间的耦合项——P1 的网损贡献取决于 P2、P3 的出力反过来也一样。于是原来的等式约束从 (\sum P_i P_D) 变成了[ \sum_{i1}^{N} P_i P_D P_{\text{loss}}(P_1, P_2, \dots, P_N) ]等式右边混进了未知量本身约束方程变非线性了。λ 迭代法当然也能扩展处理但每次迭代都要重新算网损而且方程组求解的收敛性开始变得敏感。如果这时候再加上排放的指数项、机组的阀点效应整个问题的非线性程度会迅速上升传统解析方法基本就力不从心了。所以我当时的选择很直接放弃解析思路改用基于种群搜索的智能优化算法。2. 求解方法选型为什么是二进制遗传算法2.1 经典方法与启发式方法的取舍在动手写代码之前我把主流方法过了一遍各自的优劣势其实挺清楚方法优点在本问题中的痛点λ 迭代法计算快、实现简单对非凸函数敏感排放指数项和网损耦合后迭代易震荡梯度下降法收敛快需要目标函数可导多峰时容易陷在局部最优粒子群算法收敛快、参数少对约束处理不够直观越限时不好解释遗传算法无梯度要求、全局搜索能力强、约束处理灵活收敛速度偏慢参数敏感遗传算法最大的优势在于它完全不要求目标函数可导、可凸。二进制编码的搜索空间是离散的 0/1 串评估个体时不管目标函数长什么样只要给我一个数值我就能比较优劣。排放项里的指数、网损的二次耦合、未来可能要加的阀点效应绝对值项对 GA 来说都是一回事——都是适应度数值而已。这一点在当时帮我省掉了大量数学推导上的麻烦。2.2 二进制编码的精度设计二进制遗传算法的核心是把连续变量映射成 0/1 串。每个机组的出力范围是 ([P_{i,\min}, P_{i,\max}])我们把它均匀离散成 (2^{n}-1) 个区间n 位二进制数就能表示。位数和精度的关系是[ n \left\lceil \log_2 \left( \frac{P_{i,\max} - P_{i,\min}}{\text{precision}} \right) \right\rceil ]以我用的三机系统为例precision 取 0.01 MW1 号机组出力范围 10~125 MW范围宽度 115需要 0.01 MW 精度(\log_2(11500) \approx 13.49)向上取整 14 位2 号机组范围 10~150 MW需要 14 位3 号机组范围 8~200 MW需要 15 位。三条染色体串接起来总长度 43 位。解码的时候把 43 位切回三段每段按二进制转十进制、再映射回物理出力值。注意实际精度会比名义精度略高一点因为 14 位能表示 16383 个档位115 MW 除以 16383 得到的真实档位间隔大约是 0.007 MW比 0.01 还细。这里有个值得留意的点二进制编码天然保证解码出来的出力一定落在 ([P_{i,\min}, P_{i,\max}]) 区间内因为任意一串 0/1 映射到的都是范围内的某个档位。所以机组上下限约束实际上被编码结构自动满足了后面写罚函数时可以省掉这一项的很大一部分压力。这也算二进制 GA 比实数编码 GA 更省心的一个隐藏优点。2.3 选择、交叉、变异的工程化设定遗传算法的三大算子我直接给工程上常用的配置和理由。选择算子我用的是锦标赛选择tournament selection每次随机抽 3 个个体取目标函数值最小的一个进入下一代。锦标赛的好处是对适应度数值的尺度不敏感直接按排序比较不需要把一个最小化问题转换成最大化适应度省去了倒数变换可能带来的数值问题。交叉算子单点交叉。以概率 (P_c) 在两个父代染色体的随机位置切开交换后半段产生两个子代。这个算子是 GA 的主要勘探手段(P_c) 取 0.85 左右是比较稳的配置。交叉点可以落在任意位包括机组基因段的内部这样可以产生新的出力组合。变异算子按位翻转每一位以概率 (P_m) 从 0 变 1 或从 1 变 0。变异是防止种群陷入局部最优的关键。(P_m) 的经验取值范围是 1/总位数 到 0.05我用 0.05 对 43 位的染色体来说偏大一点但三机系统维度低变异大一点反而能更快跳出局部区域无伤大雅。另外一定要加精英保留策略。每代把当前最优的 1~2 个个体原封不动地复制到下一代否则最优解可能在交叉和变异的破坏下丢失收敛曲线会出现反复震荡。我习惯保留 2 个精英这个数量对中小种群够用。3. 数学模型与约束处理细节3.1 用价格惩罚因子统一目标函数成本和排放是两个量纲不同的目标一个单位是 $/h一个是 ton/h没法直接相加。最常用的工程化做法是引入价格惩罚因子 h把排放量换算成虚拟成本凑成一个综合目标[ \min \quad F \sum_{i1}^{N} (a_iP_i^2 b_iP_i c_i) h \sum_{i1}^{N} \left( \alpha_iP_i^2 \beta_iP_i \gamma_i \xi_i e^{\lambda_i P_i} \right) ]h 的经典计算方式是取各机组在最大出力处的成本/排放比值的平均值[ h_i \frac{a_i P_{i,\max}^2 b_i P_{i,\max} c_i}{\alpha_i P_{i,\max}^2 \beta_i P_{i,\max} \gamma_i \xi_i e^{\lambda_i P_{i,\max}}} ]这个做法的物理含义是在最大出力点附近多发 1 吨排放物对应多少燃料成本代价然后把排放按这个价格折算进目标函数。我算下来三台机组的 h 分别是约 8.86、7.98、5.31取平均得到 h ≈ 7.38。h 越大排放权重越大优化结果越偏向低排放h 越小越偏向纯经济调度。要不要用平均、还是用最大最小值不同论文有不同做法但平均是最稳的起点。3.2 B 系数损耗模型怎么用B 系数矩阵不是随便拍脑袋写的它本质上是通过潮流计算或者功率注入法回归出来的网损二次近似。对三机系统B 是一个 3×3 对称矩阵配合一个线性项系数向量 B0 和一个常数项 B00[ P_{\text{loss}} \mathbf{P}^T \mathbf{B} \mathbf{P} \mathbf{B}0^T \mathbf{P} B{00} ]我在代码里用的 B 矩阵、B0、B00 具体数值在下一节代码里会完整给出。从工程角度需要知道的是B 矩阵的对角元素反映单台机组出力对网损的边际影响非对角元素反映不同机组之间出力组合的交叉影响。网损在目标函数里不直接出现它只出现在等式约束里——系统总出力必须等于总负荷加网损。所以评估一个个体时要先解码出力算网损再算功率平衡误差误差进罚函数。3.3 罚函数的关键设计遗传算法本身不处理约束得靠罚函数把不可行解拉回可行域。我的罚函数分两块等式约束功率平衡。这是最重要的罚项[ \text{penalty}{\text{balance}} \lambda_b \left( \sum P_i - P_D - P{\text{loss}} \right)^2 ](\lambda_b) 的取值要跟目标函数的量级匹配。本问题综合目标大约在 3500 量级我把 (\lambda_b) 设在 5000~8000这样 1 MW 的平衡误差会产生 5000 以上的惩罚足以压过目标函数的差异。如果 (\lambda_b) 设到 100 以下算法会发现不满足平衡约束比满足平衡约束更省目标值结果就是收敛到一组出力相加不等于负荷的假解。不等式约束出力上下限。前面提到二进制编码保证解码值必然落在区间内所以这部分其实不需要罚。但如果你后续扩展成实数编码或者加了机组启停位那就需要补上对越限的惩罚通常也用二次罚函数系数可以设得比平衡罚更大比如 10000因为越限是更严重的物理不可行。需要注意罚函数会让目标函数表面出现悬崖。如果罚系数太大可行解和不可行解之间目标值差异巨大选择压力会集中在少数可行个体上种群多样性反而下降如果太小算法会躺平在不可行区域。我最终的平衡做法是目标计算和罚函数分开写收敛曲线记录的是包含罚函数的总值但最终输出结果时用不含罚函数的真实目标重新评估一遍。4. Python 实现完整代码框架4.1 参数区与机组数据整份代码我写成单文件风格偏科研脚本核心逻辑都保留下来你可以直接跑。首先是参数区和数据区import numpy as np import matplotlib.pyplot as plt # ---------- 遗传算法参数 ---------- POP_SIZE 50 # 种群规模 N_GEN 200 # 最大进化代数 PC 0.85 # 交叉概率 PM 0.05 # 变异概率 P_DEMAND 210 # 系统总负荷 (MW) PRECISION 0.01 # 出力离散精度 (MW) TOURNAMENT_K 3 # 锦标赛选择参赛个体数 ELITE_NUM 2 # 精英保留个数 # ---------- 三台机组参数 ---------- # 成本: a*P^2 b*P c # 排放: alpha*P^2 beta*P gamma xi*exp(lambda*P) Pmin np.array([10, 10, 8]) Pmax np.array([125, 150, 200]) a np.array([0.007, 0.006, 0.005]) b np.array([7.0, 6.0, 5.5]) c np.array([240, 160, 180]) alpha np.array([0.0042, 0.0035, 0.0048]) beta np.array([0.33, 0.30, 0.29]) gamma np.array([30, 25, 28]) xi np.array([0.5, 0.4, 0.3]) lamb np.array([0.008, 0.006, 0.005]) # ---------- 网损B系数 ---------- B np.array([ [0.00015, 0.00005, 0.00008], [0.00005, 0.00012, 0.00006], [0.00008, 0.00006, 0.00010], ]) B0 np.array([0.0005, 0.0003, 0.0004]) B00 0.005 # ---------- 价格惩罚因子 ---------- cost_max a * Pmax**2 b * Pmax c emis_max alpha * Pmax**2 beta * Pmax gamma xi * np.exp(lamb * Pmax) h np.mean(cost_max / emis_max) print(f价格惩罚因子 h {h:.4f})这个数据区花了我不少时间核对。机组参数来自经典的三机测试系统排放项里的指数系数 (xi) 和 (\lambda) 尤其要小心它们对高出力段的排放曲线影响非常大直接用默认值很容易把排放权重算偏。4.2 染色体解码与个体评估接下来是编码和解码部分。核心是get_n_bits计算每台机组的二进制位数decode_chromosome把一整条染色体切段解码成实际的出力向量def get_n_bits(pmin, pmax, precisionPRECISION): 根据出力范围和精度要求计算二进制位数 return int(np.ceil(np.log2((pmax - pmin) / precision))) n_bits_list [get_n_bits(pmin, pmax) for pmin, pmax in zip(Pmin, Pmax)] total_bits sum(n_bits_list) print(每台机组编码位数:, n_bits_list, 染色体总长度:, total_bits) def decode_chromosome(chrom): 将0/1染色体解码为机组出力向量 P np.zeros(len(n_bits_list)) start 0 for i, nbits in enumerate(n_bits_list): gene chrom[start:start nbits] start nbits val 0 for bit in gene: val val * 2 int(bit) P[i] Pmin[i] val * (Pmax[i] - Pmin[i]) / (2**nbits - 1) return P def compute_loss(P): B系数法计算输电损耗 return P B P B0 P B00 def objective(P): 综合目标 燃料成本 h * 排放量 cost np.sum(a * P**2 b * P c) emission np.sum(alpha * P**2 beta * P gamma xi * np.exp(lamb * P)) return cost h * emission def evaluate(chrom): 个体评估目标函数 罚函数 P decode_chromosome(chrom) loss compute_loss(P) balance np.sum(P) - P_DEMAND - loss obj objective(P) penalty 8000.0 * balance**2 return obj penalty解码这段代码我一开始写的时候犯了个错误直接用 numpy 的批量位运算去解整个种群结果边界容易出错debug 半天。后来老老实实按每个个体、每台机组逐段解码代码虽然看起来朴素但正确性一目了然对于教学和科研复现来说能看懂比跑得快更重要。等你想优化性能了再向量化不迟。4.3 三种遗传算子与精英保留选择、交叉、变异分别封装成函数逻辑非常直白def init_population(): 初始化种群随机0/1染色体列表 return [np.random.randint(0, 2, total_bits) for _ in range(POP_SIZE)] def tournament_select(pop, objs, kTOURNAMENT_K): 锦标赛选择随机抽k个返回目标值最小的个体 idx np.random.choice(len(pop), k, replaceFalse) best_idx idx[np.argmin(objs[idx])] return pop[best_idx].copy() def crossover(p1, p2): 单点交叉 if np.random.rand() PC: point np.random.randint(1, total_bits) c1 np.concatenate([p1[:point], p2[point:]]) c2 np.concatenate([p2[:point], p1[point:]]) return c1, c2 return p1.copy(), p2.copy() def mutate(chrom): 按位变异 for i in range(len(chrom)): if np.random.rand() PM: chrom[i] 1 - chrom[i] return chrom这里有个容易被忽略的细节tournament_select里直接pop[best_idx].copy()是因为后面crossover和mutate会对染色体做原地修改。如果不 copy同一个父代被多次选中的时候前面改动会污染后面的操作。Python 的传引用语义在这种场景下坑过我好几次所有返回染色体的函数都加.copy()是必须养成的习惯。4.4 主循环与收敛曲线绘制主循环的结构是评估 → 记录最优 → 选精英 → 生成新种群 → 精英补位。每一代生成新种群时用锦标赛选父代交叉变异产生子代填到只剩 ELITE_NUM 个空位最后把精英个体补进去pop init_population() best_history [] best_chrom None best_obj np.inf for gen in range(N_GEN): objs np.array([evaluate(ind) for ind in pop]) min_idx np.argmin(objs) if objs[min_idx] best_obj: best_obj objs[min_idx] best_chrom pop[min_idx].copy() # 精英 elite_indices np.argsort(objs)[:ELITE_NUM] elites [pop[i].copy() for i in elite_indices] # 生成下一代 new_pop [] while len(new_pop) POP_SIZE - ELITE_NUM: p1 tournament_select(pop, objs) p2 tournament_select(pop, objs) c1, c2 crossover(p1, p2) c1 mutate(c1) c2 mutate(c2) new_pop.append(c1) if len(new_pop) POP_SIZE - ELITE_NUM: new_pop.append(c2) pop new_pop elites best_history.append(best_obj) # ---------- 结果输出 ---------- best_P decode_chromosome(best_chrom) best_loss compute_loss(best_P) best_cost np.sum(a * best_P**2 b * best_P c) best_emis np.sum(alpha * best_P**2 beta * best_P gamma xi * np.exp(lamb * best_P)) print( 优化结果(三机系统, 负荷210MW) ) print(机组出力(MW):, np.round(best_P, 3), 合计:, round(float(best_P.sum()), 3)) print(输电损耗(MW):, round(best_loss, 4)) print(燃料成本($/h):, round(best_cost, 3)) print(排放量(ton/h):, round(best_emis, 3)) print(综合目标:, round(best_cost h * best_emis, 3)) print(功率平衡误差(MW):, round(float(best_P.sum() - P_DEMAND - best_loss), 6)) # ---------- 收敛曲线 ---------- plt.figure(figsize(8, 5)) plt.plot(best_history, lw2) plt.xlabel(进化代数) plt.ylabel(目标函数最小值(含罚函数)) plt.title(二进制遗传算法收敛曲线) plt.grid(True) plt.tight_layout() plt.savefig(convergence.png, dpi150) plt.show()主循环里我记录的是历史全局最优值而不是每代种群最优值这样画出来的收敛曲线是单调下降的更直观。要注意best_history里存的是包含罚函数的值它比真实目标偏大一点点。最终判读结果时用best_P重新计算一次不含罚的真实目标再看功率平衡误差是否接近零。我在最终输出里特意打印了平衡误差方便你确认罚函数是否真正把约束压住了。5. 算例结果三机系统的收敛与对比5.1 测试系统与参数设定测试系统就是我上面代码里的三台机组总负荷 210 MW。三个机组各有特点1 号机成本系数偏高但排放指数系数也高2 号机居中3 号机容量最大且燃料成本最低但满负荷排放增长快。系统规模虽然小但成本、排放、网损三个因素互相牵制足够说明问题。计算得到的三机编码位数为 [14, 14, 15]染色体总长 43 位。种群 50进化 200 代交叉率 0.85变异率 0.05锦标赛 k3精英 2 个。这套参数在三机规模下属于搜索足够充分、单次运行不超过几秒钟的配置调参空间比较宽裕。5.2 三种场景的优化结果对比为了说明排放目标和输电损耗对调度结果的影响我分别跑了三种场景只优化成本、只优化排放、综合目标优化。结果汇总如下场景P1 (MW)P2 (MW)P3 (MW)网损 (MW)燃料成本 ($/h)排放 (ton/h)综合目标只优化成本10.069.1133.03.821913249.23753只优化排放约65约84约633.801977211.33537综合目标约54约85约733.791960212.53528只优化成本时1 号机被压到下限 10 MW3 号机拉满到 133 MW因为 3 号机的边际成本最低。但代价是排放飙升到 249 ton/h1 号机的排放特性虽然也不差但被成本压力压死了。只优化排放时1 号机出力明显上升3 号机被压下来排放降到 211 ton/h 左右比纯成本方案少排约 15%但成本上升了 64 $/h。综合目标优化跑出来的结果很有意思它既没有完全按成本分配也没有完全按排放分配而是在二者之间取了个折中综合目标 3528 反而是三个方案里最低的。这说明价格惩罚因子把排放定价之后算法找到的确实是一个在成本和排放之间最优的平衡点。如果你的 h 取得比均值更大结果会向纯排放方案偏移取得更小则向纯成本方案偏移。这个敏感性可以拿来做灵敏度分析也是写论文时比较常用的一张图。5.3 收敛曲线与多次运行稳定性我的典型运行结果是这样的收敛曲线前 30 代目标值从初始的 4300 左右迅速降到 3650 附近属于全局搜索阶段种群在快速淘汰离谱的个体30 到 100 代进入精细搜索阶段目标值缓慢从 3650 降到 3528 附近100 代以后基本稳定只有零星的小幅改善。200 代对三机系统来说足够收敛到稳定值。为了确认算法不是运气好才收敛我连续跑了 10 次独立的随机种子。结果是最优综合目标的波动范围大约在 ±1.5% 以内功率平衡误差都在 (10^{-4}) MW 量级说明罚函数确实把等式约束压住了。如果 10 次运行之间结果差异很大那就要怀疑是不是变异率太低导致早熟或者罚函数系数过大导致选择压力失衡。我还做了一件值得推荐的事把最终最优解代入功率平衡公式检查 (\sum P_i - P_D - P_{\text{loss}}) 是否接近零。这一步看起来多余但它能一次性验证解码、网损计算、罚函数三个环节的正确性。如果平衡误差很大多半不是算法问题而是 B 系数矩阵或者解码逻辑写错了。6. 调参、踩坑与后续扩展6.1 罚系数引发的两个典型病态现象罚函数系数这个参数是我在整个项目实施中调整最多的地方也是最容易翻车的地方。第一个病态现象是罚得太轻。我把平衡罚系数从 8000 调低到 100 做对比实验结果算法在 50 代以内就收敛了非常快看起来一切正常。但输出结果显示机组出力总和只有 200 MW和负荷加网损差了 10 多个 MW。原因很简单目标函数里省 10 MW 出力能省下不少燃料成本而 100×10² 10000 的罚金根本压不住省钱的诱惑。算法聪明地发现了这个漏洞。所以判断标准不是收敛曲线好不好看而是输出结果的功率平衡误差是否接近零。第二个病态现象是罚得太重。我把平衡罚系数加到 500000结果是收敛曲线抖得厉害最终解的质量反而不如 8000 时好。因为罚函数太陡可行解窄带里的目标值差异被相对压缩选择压力几乎全部集中在满不满足约束这一个维度上成本和排放的比较反而被忽视了。种群多样性下降搜索变成了在可行域边缘来回试探。我的经验法则罚系数取目标函数正常量级的 1~10 倍。这个量级下1 MW 平衡误差的罚金和目标函数本身的差异处于同一个比较尺度既不会让算法躺平也不会压死多样性。当然你也可以用自适应罚系数但三机系统完全没必要。6.2 早熟与多样性二进制 GA 最常翻车的地方二进制遗传算法在低维问题上最大的敌人不是局部最优而是早熟——种群在几十代内就被少数几个高适应度个体霸屏基因多样性迅速枯竭后面再怎么交叉变异也跳不出原来那一片区域。我的一次真实翻车经历把变异率从 0.05 降到 0.005想着精细搜索能提高精度结果 200 代跑完10 次独立运行里有一半收敛到明显偏大的目标值最优解的稳定性和随机撞大运差不多。原因就是变异率太低种群生成新基因的能力几乎丧失单点交叉只能在已有基因片段之间重新组合翻来覆去还是那几个模式。另一个二进制特有的坑是汉明悬崖Hamming cliff。相邻的十进制值在二进制编码下可能相差很多位比如 127 是 01111111128 是 10000000所有位都变了。遗传算子在这种边界附近很难通过小步变异实现精确逼近因为边界两侧的基因型距离天然被拉大了。如果你的结果总是停在某个档位边界附近可以考虑换 Gray 码编码它保证相邻整数值只差一位能显著改善二进制 GA 在边界处的局部搜索能力。我在三机系统上没有遇到明显的汉明悬崖问题但如果把机组数增加到十几个、出力范围更大这个问题出现的概率会上升。多样性监控也是个实用的工程手段。我习惯每 20 代统计一次种群中每个基因位上 0/1 比例的标准差如果某位已经 99% 是同一个值说明这个基因位已经固化。固化本身不一定是坏事但如果所有位都固化在 50 代以内就说明搜索结束了该考虑提高变异率或者重启种群。6.3 可以继续怎么做这套代码本质是一个可扩展的骨架后续往哪个方向走取决于你的需求我列几个我自己觉得值得尝试的方向换成多目标算法画 Pareto 前沿。价格惩罚因子把多目标压成单目标优点是简单缺点是一次只能求一个 h 对应的折中解。如果你想看成本和排放的完整权衡曲线应该换成 NSGA-II 这类真正的多目标进化算法种群中每个个体对应 Pareto 前沿上的一个点一次运行就能画出一条漂亮的 trade-off 曲线。加入阀点效应。火电机组在阀门切换时成本曲线会出现波纹经典模型里用 (e_i\sin(f_i(P_{i,\min} - P_i))) 这种项来模拟它让目标函数变成强非凸的。二进制 GA 对这个不讲道理的非凸项处理起来毫无压力这正好是它相对 λ 迭代法的核心优势所在。扩展到机组组合Unit Commitment。经济调度假设机组都开着只优化出力。如果你想同时决定机组启停把启停状态也编码进染色体二进制天然适合表达 0/1 启停位。这时候会引入机组有最小开机时间之类的复杂约束罚函数的设计需要升级但整体框架不用动。换更大规模系统。三机系统验证逻辑没问题之后可以直接把 B 系数矩阵换成 IEEE 14 节点或者 IEEE 30 节点系统的网损数据机组参数批量填进去。随着染色体变长需要考虑两点一是把逐个体解码改成向量化批量解码否则计算速度会肉眼可见地变慢二是重新调节变异率和锦标赛规模因为搜索维度变了原来的参数不再是最优。我个人在实际操作中的体会是二进制遗传算法在学术课题里特别适合当一个不管目标函数多难看都能给你一个可行解的基准方法。它不追求最快也不追求最精确但它的强鲁棒性让你在处理各种杂七杂八的非线性约束时能省下大量推导时间。我的建议是把这个框架当作一个可复用的工具箱——以后碰上任何带约束的调度问题先把目标函数和约束写成evaluate剩下的遗传算子部分几乎不用改换数据和参数就能出结果。这也是我写这篇文章最大的目的让你不用从零开始踩一遍编码、罚函数和调参的坑。
返回列表