首页 / 资讯中心 / 文章详情

NSGA-Ⅲ在梯级水电火电联合调度中的Matlab实现与建模实践

NSGA-Ⅲ在梯级水电火电联合调度中的Matlab实现与建模实践 ★ FEATURED ARTICLE
如果给电力系统的多目标优化排一个“最难落地”榜单梯级水电加火电的联合调度绝对能进前三。我前前后后在Matlab上做过几版NSGA-Ⅲ的调度代码最深的感触是难点从来不在“算法不会写”而在于“问题域和算法之间那层薄薄的窗户纸”——水电的时序耦合、火电的爬坡限制、功率平衡约束、多目标之间的冲突关系每一样都直接影响算法能不能跑出真正可用的方案。这篇博客就围绕这个课题从数学建模到算法选型再到Matlab代码的骨架实现和调试心得把整个过程串起来讲清楚适合正在做调度优化、写毕业设计或者刚接触NSGA-Ⅲ的同行参考。先说结论NSGA-Ⅲ确实比NSGA-Ⅱ更适合这类多目标调度问题。因为梯级水电和火电的联合调度通常要同时优化发电成本、污染排放、水电出力平稳性等多个目标目标维数一上去NSGA-Ⅱ赖以维持多样性的拥挤距离就会在高维空间里变得“瞎眼”而NSGA-Ⅲ用预设的参考点来引导种群分布在高维目标空间里依然能保持让人满意的均匀性和收敛性。算法选型对了后面建模和写代码才会顺。1. 梯级水电与火电联合调度的底层矛盾1.1 水火系统为什么要“抱团”优化很多人第一次接触这个课题时会有一个疑问水电和火电不都是发电吗分开优化不就行了实际远没那么简单。火电机组调节速度快、出力稳定但燃料成本和污染排放高水电机组清洁、启动快、爬坡能力强但出力受来水、库容、水头变化这些自然条件限制。更关键的是梯级水电站之间存在物理上的水力联系——上游电站发了电水流经过一段时间延迟汇入下游水库上游弃水到下游还能二次利用这就是“梯级”二字的核心价值。把这两类电源放在同一个优化框架里本质上是在时间尺度和空间尺度上做资源协调时间上需要决定每个调度时段火电发多少、水电发多少空间上需要决定梯级各站的水库蓄水、放水和弃水策略。单独优化某一类电源要么让火电频繁调峰导致成本失控要么让水电追求满发却破坏了上下游的水量匹配最终都会在负荷平衡这个“硬约束”面前翻车。1.2 联合调度要同时满足的几类目标这个课题里说的“多目标”一般少不了一组核心冲突目标总煤耗成本最低、污染物排放最小、梯级水电总发电量最大或者弃水量最小。煤耗成本是一个二次函数加若干线性项的经典模型排放目标也常常用二次函数拟合两者本质上都随着火电出力单调变化但它们的斜率不一样所以最优解不会重合。这意味着不存在一个“全能方案”同时让所有目标都达到极值。我们需要的是Pareto解集一组满足所有约束的方案在其中任何一条目标继续改进都必然导致另一条目标恶化。调度的决策者再根据实际需求成本优先、环保优先、或者水电优先从Pareto前沿里挑一个折衷方案。这正是“多目标优化算法调度模型”结合的价值所在。1.3 为什么单目标加权在这个场景里不够用有同行可能会问把三个目标分别乘上权重系数再加起来变成一个单目标用传统粒子群或者遗传算法去跑不是更省事吗早期确实有很多论文这么干但实际问题里有两个绕不开的麻烦。第一权重怎么定成本目标动辄几十万上百万元排放可能是几千吨水电出力又是兆瓦时级别量纲差了好几个数量级。如果直接把三个目标加权相加排放项的作用会被成本项完全淹没除非做严格的归一化而归一化本身就依赖单次优化的极值估计非常容易失真。第二权重系数一旦变化整个问题的最优解就变了一个。你没法从前一次优化结果里“推理”出新的结果只能重新跑一遍如果要扫出完整的Pareto前沿就得跑很多次。NSGA-Ⅲ这类算法一次运行就能给出整个前沿面效率和实用度明显高出一截。2. 从NSGA-Ⅱ到NSGA-Ⅲ算法选型的判断依据2.1 拥挤距离和参考点法的本质区别NSGA-Ⅱ的核心思想是非支配排序之后用拥挤距离来衡量个体占据的“稀疏程度”优先保留拥挤距离大的个体从而保证解集覆盖整个前沿。这在二维目标空间里效果非常好因为“密集”和“稀疏”一目了然但目标数量升到4个、5个以后体积不是用距离能度量的拥挤距离失去了排序的意义种群会慢慢聚向某一小片区域丢掉前沿的其他部分。NSGA-Ⅲ的改进改用“参考点”整定方法算法先在前沿附近撒一批分布均匀的参考点把每个个体关联到距离最近的参考线参考点到理想点的连线再结合小生境计数决定优先保留哪些个体。这么做的结果就是种群从“自己往稀疏处挤”变成了“按预设的均匀锚点分头排队”整个前沿的覆盖质量完全由参考点的分布决定和目标维数的关系被大大削弱。2.2 调度问题的目标维数决定了哪种机制更有效梯级水电与火电联合调度课题下目标经常是三个起步成本、排放、水电机组的平稳出力或弃水率。如果再考虑电网侧的峰谷差、联络线功率波动四五个目标也很常见。在这个维度区间里NSGA-Ⅱ的拥挤距离法已经开始不稳而NSGA-Ⅲ在3到15个目标范围内都能维持较好的分布性。可以用一个类比来理解NSGA-Ⅱ就像团队出游时大家自由组队谁那边人少就往谁那边走NSGA-Ⅲ更像提前划好了几个景点区域要求每个区域至少留一组人。人多、景点多的时候后者显然不会乱。从Matlab的实现角度看NSGA-Ⅲ多出来的核心组件主要集中在环境选择部分生成参考点、目标归一化、个体-参考线关联、小生境选择。其余进化部分模拟二进制交叉SBX、多项式变异和NSGA-Ⅱ完全一致工程迁移成本不大。2.3 代码层面NSGA-Ⅲ需要新增的三个组件用自己的话梳理NSGA-Ⅲ的Matlab实现相比NSGA-Ⅱ主要在三个地方需要新增逻辑。第一参考点生成。常用Das-Dennis星型设计法对M个目标、每维等分H份生成nchoosek(MH-1, M-1)个均匀参考点。比如3目标、每维等分12份参考点数是91个4目标、每维等分6份参考点数是84个。注意参考点数量最好比种群规模略大这样每个小生境个体共享的“竞争压力”更合理。第二自适应归一化。这一步要做两件事找理想点各目标的历史最小值再用ASF算子算出每个目标方向上的截距把目标空间拉伸到一个量纲统一的范围。没有这一步成本目标和排放目标直接混在一起比较参考点机制就名存实亡。第三小生境保留。把临界前沿的个体逐一投影到参考线上按最短垂直距离关联然后统计每个参考点关联个体数量优先挑选关联数少的参考点附近的个体进入下一代。这套机制保证了多样性也是NSGA-Ⅲ花时间最多的地方。3. 数学模型搭建目标函数、约束体系和变量编码3.1 目标函数的选取与归一化设计建模的第一件事是把每个目标用数学式子写清楚。火电煤耗成本是最基础的目标常用二次函数拟合F_c sum_t sum_i ( a_i * P_f(i,t)^2 b_i * P_f(i,t) c_i )其中P_f(i,t)是第i台火电机组在t时段的出力a_i为二次煤耗系数b_i为一次煤耗系数c_i为固定成本。这个式子里累计了所有机组在所有调峰时段的总运行成本单位通常归算到元。污染排放目标可以类似建模E_t sum_t sum_i ( d_i * P_f(i,t)^2 e_i * P_f(i,t) f_i )系数d、e、f和前一组完全独立体现的是不同厂家机组在不同负荷段的排放特性。如果只考虑一种污染物比如氮氧化物就按上式如果要把多种污染物纳入一般会加一个等效排放转换系数把它变成同一指标。梯级水电侧的目标函数更灵活。如果研究偏重发电效益可以写最大化梯级总发电量W_h sum_t sum_j P_h(j,t) * Δt其中P_h(j,t)为第j个水电站在t时段的出力Δt为时段长度如果研究偏重生态和调度平稳性也可以写最小化弃水量或最小化出力波动。这个目标在Matlab代码里直接决定约束函数里要不要把弃水量作为中间变量返回。我个人建议目标数量不要贪多。这个课题下跑3个目标已经能体现NSGA-Ⅲ的优势4个目标写论文好看但超过4个之后计算的耗时和参数调节复杂度都明显上升。先把3到4个目标做扎实比堆5个目标却每个都失真更实用。3.2 约束条件拆解从功率平衡到水库水量平衡调度模型里最关键的约束有五组在Matlab代码里要全部转换为非负不等式或等式形式。功率平衡约束是最硬性的任何时段全网出力必须等于负荷需求数学表达为sum_i P_f(i,t) sum_j P_h(j,t) P_load(t) P_loss(t)实际代码中这个约束不能直接用“等于”判断因为搜索过程几乎不可能精确相等通常把它转换成违反量 |ΔP|。火电机组约束分为出力上下界和爬坡速率例如P_f_min(i) ≤ P_f(i,t) ≤ P_f_max(i) -P_f_ramp_down(i) ≤ P_f(i,t) - P_f(i,t-1) ≤ P_f_ramp_up(i)爬坡约束是调度问题里导致可行域急剧缩小的元凶之一。初代的解常常因为前后时段出力跳变太大而被判定不可行这在后面的约束处理小节会专门讨论。水电站机组约束包括发电流量上下界、库容上下界和出力-流量-水头关系P_h(j,t) 9.81 * eta_j * Q_g(j,t) * H_j(t)其中eta_j为水电站综合效率Q_g(j,t)为发电流量H_j(t)为当前水头。这条式子是非线性的而且H和水库蓄水量之间又存在函数关系导致它成为代码里最容易出计算偏差的地方。常见简化做法是查表或者用蓄水量线性近似水头。梯级水量平衡约束是水电部分的核心V_j(t1) V_j(t) ( I_j(t) Q_up(j,t-tao) - Q_g(j,t) - Q_s(j,t) ) * Δt这个式子的物理含义是本时段末库容等于时段初库容加上本时段来水和上游电站延迟来的流量再减去发电流量和弃水流量。上一级电站的放水经过水流延迟时间后成为下一级电站的入库流量这就是梯级水电的耦合本质。在代码里这个约束必须放在一个循环里对每个电站依次顺序计算因为级联关系是链状的。最后还有各水库的库容上下限、下游生态流量下限等约束它们通常是简单的线性上下界。3.3 决策变量的编码与解码逻辑决策变量的选择直接决定求解难度。最常见的编码方式是把每个调度时段的所有火电出力和所有水库的库容序列作为实数向量X [P_f(1,1),...,P_f(nT,24), V_1(1),...,V_m(24)]这样编码有一个显而易见的坏处维数太高。假设有6台火电、3个梯级电站调度周期24小时决策变量就有(63)*24216维。高维空间里进化算法的收敛速度和初代可行率都会受到打击。更聪明的办法是缩小决策变量的自由度只对关键的量进行编码其余通过等式关系推算。例如火电出力只编码一部分机组剩下的机组作为平衡机组用功率平衡式反推水电侧只编码各时段各站的发电流量或库容初末值弃水量通过水量平衡推导得出。这样的编码策略能显著减少变量维度我在实际代码里也是这么处理的——先压维度再谈优化。如果不做维度压缩至少要在初始化环节生成一组“尽量可行”的个体比如用启发式规则让火电出力先落在负荷附近再用修正算子去满足爬坡约束而不是从完全随机的点开始。否则初代种群可行率可能连百分之五都不到整个NSGA-Ⅲ会在低效搜索中浪费大量代数。4. 约束处理与Matlab实现的核心写法4.1 Deb规则与约束违反度CV的代码实现多目标进化算法处理约束业界最常用的是Deb在2000年左右提出的支配关系扩展。简单说就是三条规则可行解永远优于不可行解两个都是可行解时按Pareto支配关系比较两个都是不可行解时约束违反度CV小的一方优先。这套规则实现简单不需要任何罚函数系数非常适合在Matlab里直接套进环境选择。约束违反度的计算方式是把所有约束的违反量归一化后再求总和CV(X) sum( max(0, g_k(X)) ) sum( abs(h_k(X)) )其中g_k是不等式约束的违反量h_k是等式约束的违反量。在功率平衡约束下等式违反量取 |P_gen - P_load - P_loss|其他所有越界量都取 max(0, 越界值)。需要注意量纲不同的约束最好各自除以一个基准值再累加否则量级大的约束会主导排序。Matlab里建议把约束检查写成一个独立函数输入解码后的调度方案输出CV值。这样主程序可以随时调用调试时也能单独验证某个方案是否可行。4.2 修复策略让每条解都能被工程落地只有CV排序还不够实际运行中你会发现另一个问题如果只靠“淘汰不可行解”的机制种群里的可行解比例会很低算法在几百代内都学不到足够信息优化效率惨不忍睹。所以我会在约束检查之后加一个“修复步骤”用工程手段把明显违反物理规律的解拉回可行域。最简单的一层修复是限幅每个火电出力和库容先被裁剪到上下界内这能立刻消除大部分越界约束。第二层修复是处理爬坡约束如果一个时段相对前一时段的出力变化超过爬坡速率就把当前时段出力强制设置为 前一时段出力 爬坡方向上的最大变化量。第三层修复是功率平衡把所有变量固定后看总出力比负荷大还是小把差值按比例分摊到火电机组上让等式约束基本归零。这种做法本质上是把优化问题的可行域“拍平”了一部分让进化算法把精力集中在更核心的搜索上。当然修复算子会略微改变解的分布特性所以代码里要保留修复前的原始个体作为对比对象观察修复是否导致种群多样性过快下降。4.3 NSGA-Ⅲ主循环的代码骨架把上面的思路落到Matlab主循环的框架大致如下function [bestChrom, F] NSGA3_UC(Thermal, Hydro, Load, Params) % Thermal/Hydro: 机组参数结构体 % Load: 负荷曲线 % Params: 种群规模、最大代数等 N Params.N; % 种群规模 Tmax Params.Tmax; % 最大迭代代数 nVar Params.nVar; % 决策变量维度 lb Params.lb; ub Params.ub; Z GenerateReferencePoints(Params.M, Params.H); % 参考点 % 生成初始种群 Population repmat(lb, N, 1) rand(N, nVar) .* repmat(ub-lb, N, 1); % 修复与评估 for i 1 : N [chap, cv] repairAndEvaluate(Population(i,:), Thermal, Hydro, Load); % 这里返回修复后的解、目标向量和CV end for t 1 : Tmax % 1. 进化产生子代SBX交叉多项式变异 Offspring GA_variation(Population, lb, ub, Params); % 2. 合并父子代种群 Population [Population; Offspring]; % 3. 环境选择快速非支配排序参考点选择 Population NSGA3_EnvironmentalSelection(Population, N, Z, Params); end % 输出最终Pareto前沿和对应方案 F unique(Population.objs, rows); end节省篇幅起见这里省略了repairAndEvaluate内部的详细代码但它的返回值设计成“修复后的个体、目标值、CV值”三件套在后处理里非常有用。另外由于Matlab的矩阵操作非常方便建议把目标计算尽可能向量化避免多层for循环导致速度不可接受。我在实际跑增量负荷曲线的数据时向量化和循环版本的速度差距可以达到一个数量级。5. 参数调试与运行结果的分析要点5.1 种群规模、迭代次数和参考点设定的经验值这个课题下NSGA-Ⅲ的典型参数范围是种群规模100到200迭代次数300到500SBX的分布指数20左右变异分布指数20左右。参考点数量用Das-Dennis法生成3目标取H12到15比较合适4目标取H6到8比较合适。有一点必须提醒参考点数量并不要求严格等于种群规模。华人在小生境选择里参考点数量略大于种群规模时结果通常更稳因为会给每个参考点方向留下竞争空间。但如果参考点过多而种群过小每个小生境里个体太少选择压力会明显不足Pareto前沿会出现一串稀疏的点影响最终画图效果。在调试时要记录两个指标每一代的可行解比例和非支配前沿的基尼系数或者直接用Pareto前沿的分布性指标。如果可行解比例长期低于30%说明约束处理或修复算子需要加强如果前沿点扎堆在某个角落说明参考点关联机制可能因为归一化不准而失效这时要检查截距计算是否被异常值干扰。5.2 结果解读怎么判断收敛和前面分布质量当三轮迭代的前沿面不再出现明显“推进”时可以判定算法基本收敛。判断方法很简单把第200代、第300代、第400代的前沿放在同一张三维图里观察最外层壳的移动幅度。如果代数增加后前沿只是内部点的替换而外层不动说明收敛了大半再跑下去边际收益不大。前沿分布质量则看两个内容极值点是否存在以及中段是否被均匀覆盖。NSGA-Ⅲ在低维目标下很容易跑出漂亮的前沿但要注意有些“漂亮”是假的——如果火电成本目标和水电出力目标天然强相关前沿会退化成一条线而排放目标独立时才会出现真正的曲面。我在一次实验里发现成本目标和水电出力目标几乎完全线性相关这时前沿薄得像纸片后来检查才知道是修复算子把水电出力强制固定在了某个值附近丢掉了决策多样性修正后前沿面才真正展开。5.3 从Pareto前沿到最终调度决策的落地方法跑出Pareto前沿只是研究的第一步工程上还要从几十个解里选一个执行方案。最简单的方法是用模糊隶属度法每个目标定义满意的上下限把每个解在各目标上的满意度映射到[0,1]区间取加权和最大者为最终决策。公式上就是对第k个目标满意度函数常常取mu_k (f_max(k) - f_k(X)) / (f_max(k) - f_min(k))然后对每个解求所有目标的平均满意度选平均满意度最高的一组调度方案。这种方法简单透明适合在论文里写清楚。还可以加一层工程判断观察所选方案里火电机组的爬坡压力是否过大、水库有没有剧烈的弃水尖峰如果有就在相邻的几个Pareto方案里人工挑一个更稳妥的。我一直觉得多目标优化的最后一步决策必须保留人工参与的入口算法负责给选择空间人负责给选择理由。6. 后续可以继续深挖的三个方向6.1 引入风光新能源后的三电源联合调度把随机性和波动性更强的风电、光伏加入模型是目前很自然的一个扩展方向。这个扩展会让目标函数多一个“减少可再生能源弃电”的维度同时约束增加一个“旋转备用容量”约束来应对预测误差。NSGA-Ⅲ的优势在这里会更加明显因为目标数量升到4到5个之后传统算法基本退场。代码层面最大的变化在于需要加入场景生成和削减模块。多场景随机规划里每个场景都是一组风电、光伏的出力曲线调度目标变成“期望值”约束变成“每个场景下都要满足”。这和原问题的差异不小建议把NSGA-Ⅲ的环境选择部分保留重点关注场景聚合模块怎么写不要让上千个场景把目标计算拖垮。6.2 把随机性纳入模型区间优化与鲁棒优化如果不想引入大量场景可以考虑区间形式把负荷和来水的预测值处理成区间目标函数和约束都变成区间函数优化目标是让区间宽度尽可能小。这种思路工程上非常实用因为调度者不关心预测的精确点只关心“最坏情况下能不能扛住”。用NSGA-Ⅲ跑区间优化需要重写目标函数返回方式每个目标输出两个值下限和上限或者输出一个区间中点和一个半径再在支配关系里对区间范畴做比较。这个方向在Matlab里的实现工作量不大但论文立意和工程价值都很高。6.3 从离线调度转向滚动修正的工程化思路最后一个方向是调度模式本身的升级。传统研究偏向离线计算给定完整负荷曲线一次性算好次日96时段的调度计划再去执行。实际电网调度更像滚动修正每运行一小时就重新优化未来四到六小时把新获得的实测数据带回到模型里。NSGA-Ⅲ的优势在于它的种群信息可以被继承——上一轮跑完的Pareto前沿可以作为下一轮初始种子的来源让优化起点更贴近当前工况显著减少收敛时间。这个方向对我个人来说最诱人的地方在于它让“基于NSGA-Ⅲ的调度研究”从一篇论文变成了一套能持续运转的闭环算法框架工程落地时价值提升得非常明显。如果你刚开始做这个课题我的建议是先把流程拆开第一步只用启发式规则生成可行调度方案第二步用单目标优化验证目标函数写对没有第三步再把NSGA-Ⅲ接上去跑多目标。跳过前两步直接上NSGA-Ⅲ一旦跑出奇怪的前沿你很难判断是算法错了还是模型错了。我踩过这个坑花了好几天时间排查最后发现是水量平衡公式里忘记加水流延迟时间。好在Matlab的交互特性救了我一命——把中间变量打印出来一对比问题一下就定位了。做这类研究最大的敌人不是算法复杂而是你以为写对了但实际上没有的模型细节。希望这篇分享能帮你少熬几个夜。
阅读完成 · 觉得有帮助?
咨询建站