ARTICLE DETAIL

资讯详情

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

基于NSGA-II的冷热电联供综合能源系统多目标优化调度Matlab实现

基于NSGA-II的冷热电联供综合能源系统多目标优化调度Matlab实现 做综合能源系统优化调度的朋友应该都经历过这种抓狂时刻燃气轮机发多少电、吸收式制冷机供多少冷、蓄能装置什么时候充放每一项决策都会连带影响下一小时的能量平衡更麻烦的是运行成本和碳排放往往互相打架省了钱就可能多排碳能效高了可能设备磨损也上去了。这已经不是一个单目标能解决的调度问题而是一个典型的冷热电联供型综合能源系统多目标优化调度问题。这篇文章我把整套Matlab程序的建模思路、算法实现和调试经验完整梳理一遍包含目标函数、约束条件、NSGA-II的编码与求解框架以及一个夏季典型日的算例演示适合正在做综合能源系统方向课题的研究生、搞园区能源站运行策略的工程师以及想快速上手多目标优化调度的Matlab使用者参考。1. 方案设计为什么一套冷热电联供系统需要多目标算法1.1 冷热电联供系统运行的基本逻辑冷热电联供型综合能源系统的核心思路并不复杂用一台燃气轮机或者燃气内燃机先发电发电过程中产生的高温烟气、缸套水原本是废热把它回收下来一部分通过换热设备直接供热另一部分送入吸收式制冷机最常见的是溴化锂机组来产冷。这样一套流程走下来燃料的化学能不仅发了电还顺带解决了热负荷和冷负荷综合能源利用率能明显往上拉。但系统结构简单运行调度却不简单。一天24小时电负荷有早晚高峰冷负荷在午后飙升热负荷可能集中在清晨和夜间三种负荷曲线形状完全不同步。再加上峰谷分时电价波动、天然气价格、设备出力的上下限和爬坡约束调度员需要在每个时刻决定燃气轮机带多少负荷不足的电量从电网买还是让储电装置放余热利用优先级怎么排是直接供热还是进吸收式制冷电制冷机什么时候顶上蓄冷罐要不要参与平抑峰值。这一串决策不是孤立存在的。上一小时蓄热罐多充了一些热量下一小时的余热分配策略就受影响白天多买了低价电晚上再高价买电就是亏。你要是只盯着某个时刻做局部优化整天的运行成本大概率是一笔糊涂账。所以优化调度本质上是一个跨24小时、多设备、多能源耦合的全局寻优问题。1.2 单目标优化的局限性与多目标算法的必要性很多人会问这个调度问题能不能用一个目标函数直接搞定比如最小化运行成本把碳排放或者能效折算成经济指标加进去。理论上可以实际中也有不少文献这么做但问题在于折算系数的选择非常主观。你把1千克碳排放折算成多少钱直接决定了优化结果偏向省钱还是偏向环保而且这个折算系数在一天内不同时段、不同负荷场景下可能都不该是一个固定值。更本质的问题在于运行成本、污染物排放、一次能源利用率这几个目标通常是互相冲突的。燃气轮机满负荷运行时单位发电成本可能较低但设备处于高负荷工况下燃料消耗总量大排放总量也会上升反过来多用电网购电在谷时段确实便宜可电网侧火电比例高时间接碳排放并不低。这种目标之间的Pareto冲突关系用单一加权和的方式很难刻画清楚。多目标算法就不一样了。它的输出不是一条最优解而是一组Pareto前沿解集——每一个解都代表一种不同的省钱-减碳-提效的权衡方案。决策者拿到这组解之后可以根据实际需求挑选这个园区今天对碳排放有硬性考核那就选排放最低的解这几天天然气降价、电网价格高那就选经济性最好的解。这种灵活性是单目标优化给不了的。1.3 三个核心目标函数的选取逻辑我对一套典型的冷热电联供型综合能源系统做优化调度时最常用的是三个目标函数分别对应经济性、环保性和能源利用效率。第一是经济性目标。由天然气购气费用、电网购电费用、设备运行维护费用、可能的售电收益组成。这个目标最直观量纲是钱单位是元第二是环保性目标。用二氧化碳排放量来衡量包含天然气燃烧的直接排放和购电对应的间接排放单位是千克第三是一次能源利用率。也就是系统输出的电、热、冷总能量与输入燃料能量之比反映设备链条对一次能源的利用程度无量纲通常以百分比表示。这三个目标的重要性排序没有标准答案完全取决于项目背景。所以我建议程序框架里把这个三个目标都实现出来让使用者自行决定保留哪几个而不是写死成固定组合。灵活度对后续改造项目来说很重要。2. 系统建模设备模型、目标函数与约束条件搭建2.1 典型系统结构设备组成与能量流动关系搭建Matlab程序之前先得把物理系统抽象成数学结构。以一个典型的园区级冷热电联供系统为例我通常包含以下基本设备燃气轮机、余热回收装置余热锅炉或换热器、溴化锂吸收式制冷机、电压缩式制冷机、燃气锅炉、蓄电池。其中储冷和储热装置可以做可选模块推介入门阶段先不加把储能系统跑通后再扩展。能量流动关系是建模的骨架必须理清楚。燃气轮机消耗天然气产生电力同时排出高温余热修正后的余热一部分进入换热设备变成热负荷的供应一部分进入吸收式制冷机变成冷负荷的供应外部电网可以买电也可以卖电电制冷机消耗电能补足冷负荷缺口燃气锅炉消耗天然气补足热负荷缺口。2.2 核心设备模型与能量转换效率参数设备建模要从实际设备厂商参数表反推简化模型。以燃气轮机为例燃气轮机发电量(P_{MT})与燃料输入功率(Q_{fuel})之间的关系可以简化为(P_{MT} \eta_{ge} \cdot Q_{fuel})其中(\eta_{ge})是发电效率微燃机一般在30%到38%之间。燃气轮机回收余热为(Q_{recover} \alpha \cdot (1-\eta_{ge}) \cdot Q_{fuel})其中(\alpha)是余热回收系数一般取0.5到0.7受烟气温度和换热器效率影响。电制冷机和吸收式制冷机用COP模型表示(Q_{cool} COP \cdot P_{in})。其中吸收式制冷机COP大约1.2到1.5电制冷机COP大约3到5直观对比就能发现——同样的冷负荷需求用电制冷要消耗大量高品位电能而吸收式制冷用的是本来会被丢弃的余热运行成本差异很明显。这也是联供系统经济性优于传统分供系统的逻辑基础。燃气锅炉简单一点就是燃料输入到底有多大比例能变成热量输出(Q_{boiler} \eta_{boiler} \cdot V_{boiler} \cdot LHV)热效率一般取85%到92%。蓄电池模型要关联时间维度(SOC(t1)SOC(t)\eta_{ch} \cdot P_{ch}(t) \cdot \Delta t - P_{dis}(t) \cdot \Delta t / \eta_{dis})。等待状态我一般写成这种离散递推形式方便在24时段调度程序里用循环去算全天的SOC变化。2.3 目标函数构建与量纲归一化处理在Matlab里写目标函数时最需要小心的是量纲问题因为多目标优化不能简单把多个目标拼成一个加权值。我建议程序里每个目标函数单独写一个子函数输出为标量列向量。举一个经济性目标的完整计算过程假设一天调度24个时段每个时段Δt取1小时天然气价格(c_{gas})按2.5元/立方米(V_{MT}(t))和(V_{boiler}(t))分别为燃气轮机和燃气锅炉在t时段的燃气消耗量电网购电价(c_{buy}(t))和售电价(c_{sell}(t))按峰谷分时取购电功率(P_{buy}(t))售电功率(P_{sell}(t))。总运行成本C就写成[ C \sum_{t1}^{24} \big(c_{gas} \cdot (V_{MT}(t)V_{boiler}(t)) c_{buy}(t) \cdot P_{buy}(t) - c_{sell}(t) \cdot P_{sell}(t)\big) \lambda_{OM} \cdot \sum_{t1}^{24}P_{MT}(t) ]末尾的(\lambda_{OM})是单位发电量的设备维护成本系数。给一个具体数值感受一下燃气轮机额定出力500kW电效率35%天然气低位热值LHV取9.7kWh/立方米则满发时的燃气消耗量约为500kW除以0.35乘以9.7kWh/m³约等于147.3立方米每小时。按2.5元/立方米计算光燃气费每小时就是约368元。这说明成本主体永远是燃料费调度优化的重头戏集中在燃气轮机的出力安排上。排放目标的计算逻辑类似只是把价格换成排放因子。我这里用(E_{CO2} \sum_t e_{gas} \cdot (V_{MT}(t)V_{boiler}(t)) \cdot LHV e_{grid}(t) \cdot P_{buy}(t))其中天然气的折算排放因子大约0.2kgCO2/kWh电网购电的间接排放因子可能从0.4到0.7 kg/kWh不等看不同区域电网的清洁化程度。一次能源利用率PEE的分子是输出的电、热、冷能量之和冷量是吸收式制冷和电制冷的制冷量热量是燃气锅炉和余热利用的热量分母是整个系统消耗的天然气和电网购入电力的等效一次能源之和。这三块目标函数建议都写成独立函数之后换算法、加约束时只需要保留统一接口就行。2.4 约束条件清单运行边界与物理限制约束条件这一块是Matlab程序里最容易出错的地方也是最容易被审稿人或者答辩专家质疑的地方。整理一份我常用的约束清单第一个是功率平衡约束。任何时刻系统的电功率输出燃气轮机发电加电网购电加蓄电池放电必须等于电负荷加电制冷机耗电加蓄电池充电。冷功率、热功率同理。这三个等式约束是调度的基石些许偏差都不行。第二个是设备出力上下限约束。燃气轮机出力不能超过额定容量的上限也不能低于最小的稳定运行功率电制冷机有最小开机和最大出力限制燃气锅炉输出功率有范围蓄电池充放电功率有上限SOC必须在0.1到0.9之间防止过充过放。第三个是爬坡约束。燃气轮机和燃气锅炉的出力在相邻两个时段的变化量有上限。虽然Matlab优化代码中这个约束不一定是最容易被破坏的那一个但不加的话结果很可能出现相邻时段出力剧烈跳变——这在真实设备上是不可能做到的。第四个是蓄能装置的状态耦合约束。蓄电池SOC起始和结束时刻要一致或者回到某个预设值。这个问题在长期运行调度里很重要一个24小时的调度周期不能把电池耗到接近于零否则第二天无法运行。约束处理是后面要详细说的一个技术难点这里先明确一点所有约束必须用向量化的方式写进约束矩阵或者约束判断函数不要散落在主循环里手写判断否则调试后期改一个约束条件要翻遍全程序。3. Matlab实现核心NSGA-II框架下的编码与求解设计3.1 为什么选NSGA-II而不是线性加权实现多目标优化调度最成熟的算法无疑是NSGA-II。虽然近几年也有MOPSO、NSGA-III、SPEA2等算法但NSGA-II胜在稳定、开源代码多、参数调节经验容易找是入门综合能源系统多目标调度最不容易翻车的一个选择。线性加权法的缺点前面说过权重事先定不好得到的是一个脆弱的单点解。而NSGA-II的核心动作是先对整个种群做非支配排序把所有解按Pareto支配关系分层然后在同一层内用拥挤度距离来衡量解的稀疏程度拥挤度大的解优先保留保证Pareto前沿的分布均匀性再通过选择、交叉、变异产生下一代种群。这套机制保证了得到的不是一条最优解而是一组分布均匀的、互相之间互不支配的候选方案。对调度问题来说特别是要给上层决策者做选择的时候这种解集合的价值远大于单点最优解。3.2 染色体编码设计与种群初始化方法编码设计直接决定了搜索空间的规模和寻优效率。我推荐使用实数编码染色体形式如下假设调度周期为24小时决策变量包括燃气轮机24个时段的出力、吸收式制冷机24个时段的制冷量或输入热量、电制冷机24个时段的输入功率、燃气锅炉24个时段的出力、蓄电池24个时段的充放电功率。一个个体就是一个长度为24乘以决策变量数的实数向量。举个例子系统包含5个连续决策设备那么单条染色体的维度就是5乘以24等于120。这个量级对NSGA-II来说并不算大种群规模取200迭代300代左右Matlab在普通台式机上几分钟能跑完。如果加了蓄冷蓄热装置决策变量多两组染色体到168维或者192维运算量会显著上升需要考虑降维技巧。初始化种群时要注意随机生成的初始解大概率不满足冷热电功率平衡约束。我的做法是先生成燃气轮机出力序列和电网交互功率然后根据功率平衡等式推导出其他设备出力最后对超出上下限的设备做裁剪和修正。这样做初始化种群可性行解比例大幅度提高算法收敛速度明显优于完全随机初始化。3.3 约束处理的两种常用手段多目标调度里约束处理是最容易踩坑的环节。功率平衡约束、设备出力上下限、SOC边界这些约束不满足的解必须被淘汰但直接删除又会损失大量有潜力的中间解。我在实测中最常用的两种方式是罚函数法和约束支配法。罚函数法的基本思是在每个目标函数值上叠加一个与约束偏离量成正比的惩罚项偏离越大解越劣算法自然避开不可行域。例如功率失衡总量为100kW罚函数系数取50那么该解的目标函数值就会被放大5000元。罚系数的小小变动都会显著影响优化结果必须结合系统量纲多次实验确定不能拍脑袋定。约束支配法更严谨。在非支配排序时比较两个解优先关系时额外加一条规则可行解恒定优于不可行解两个都是可行解时按Pareto支配来判断两个都是不可行解时比较约束违反总量的大小。NSGA-II的选择算子天然可以嵌入这条规则不用改太多代码。我建议在程序框架中同时保留罚函数法和约束支配法的接口先用约束支配法保证解的质量后续如果想加速再切到罚函数法对比效果。核心的约束实现逻辑可以用一段Matlab代码示意function violation checkConstraints(x) % x为单个个体解码后的决策变量结构体 % 分别计算电功率平衡偏差、冷功率平衡偏差、热功率平衡偏差 % 设备出力越界偏差 % SOC越界偏差并累加得到总的约束违反量 end这个函数返回的数值在约束支配法里直接参与比较在罚函数法里则乘以惩罚系数叠加到目标函数中。3.4 从Pareto前沿到折中解模糊隶属度法算法跑完以后得到的是一整个Pareto前沿解集比如200个互不支配的调度方案。决策者没法一次看200个方案并从中决策所以实践中要从Pareto前沿选出一个折中解。我采用最多的是模糊隶属度法。思路是对每个解分别计算它在各个目标函数上的隶属度值隶属度越接近1说明这个目标表现越好然后把单个解的多个隶属度值取平均得到这个解的折中评价指标找出评价值最大的解作为最终推荐方案。把程序输出的Pareto前沿画成散点图横轴是运行成本纵轴是碳排放量前沿通常是一条向右下倾斜的曲线左边端点代表最低成本解右边端点代表最低排放解中间的折中解群体就是工程上最值得关注的部分。很多人只看最优成本解直接提交报告我强烈不建议因为一旦后续约束条件稍有变化角点解往往是最容易失效的。4. 实操演示夏季典型日调度算例全流程4.1 算例基础数据与负荷曲线准备空谈参数无意义我把一个完整的夏季典型日算例的基础数据列出来方便你对照着搭程序。一个中等规模的园区夏季24小时电负荷、冷负荷、热负荷数据我用下表的典型值描述。电负荷夜间在350kW左右白天最高到800kW左右午间出现峰值冷负荷从上午9点开始上升下午15点前后达到约700kW的峰值热负荷夏季较低主要是生活热水需求维持在80到120kW之间。更详细的数据建议直接录成Matlab里的矩阵或者Excel文件不要写死在代码里方便后面改成冬季、过渡季算例。设备容量按以下配置燃气轮机额定容量1000kW最小出力300kW发电效率0.35余热回收系数0.6吸收式制冷机额定容量600kWCOP取1.2电制冷机额定容量500kWCOP取4.0燃气锅炉额定容量400kW热效率0.88蓄电池容量800kWh最大充放电功率300kW充放电效率0.95SOC上下限0.1和0.9。电价采用峰谷分时电价峰时段10点到15点、18点到21点购电价1.05元/kWh平时段7点到10点、15点到18点、21点到23点购电价0.63元/kWh谷时段23点到次日7点购电价0.28元/kWh。天然气价格取2.5元/立方米维护成本系数取0.02元/kWh。4.2 算法参数设置与调参心得NSGA-II参数方面我的初始经验值是种群规模200迭代代数300交叉概率0.9变异概率取1除以染色体维数约等于0.008模拟二进制交叉和多项式变异分布指数都取20。这套参数在我跑过的多个综合能源系统问题里表现都比较稳定。调参过程中有一个关键经验交叉概率不能太高太高容易破坏设备出力序列的连续性导致相邻时段爬坡量反复跳变变异概率不能太低太低会使得算法在可行域边界上缺乏探索能力Pareto前沿尾端延展性变差。一个调试技巧是每一代结束后在命令行打印当前非支配解数量和前沿覆盖率的变化迭代到后期如果这两项数值稳定不再变化说明可以终止迭代了。还有一个容易被忽略的参数是目标函数方向。NSGA-II内部默认对目标进行最小化而一次能源利用率是越大越好的目标所以必须在目标函数返回时取负值或者用倒数处理否则结果会完全反了。这个方向搞错的报错成本往往很高我在实际项目中不止一次见到有人做完整个算例才发现能效目标被错误地最小化了。4.3 结果分析与调度规律解读跑完300代后Pareto前沿上的典型方案分布具有明显的规律性成本最低的角点解中燃气轮机出力曲线更倾向于在峰电时段满发谷电时段压低出力甚至停机改成电网购电为主而排放最低的角点解中燃气轮机几乎全天维持较高出力水平减少电网购电份额因为电网的间接排放因子比天然气直接燃烧的折算排放更高。折中解的代表性调度规律是白天冷负荷高峰时段吸收式制冷机优先用余热产冷电制冷机作为补充因为用余热制冷相当于免费制冷比用电便宜得多夜间谷电时段蓄电系统低价充电电力负荷低谷时优先让燃气轮机进入最小稳定出力状态保持余热供应给吸收式制冷机和热负荷。跑程序时把三个目标做两两配对画Pareto前沿图你会看到成本与排放之间几乎是强冲突关系而成本与一次能源利用率之间也呈现矛盾趋势。这给决策者的启发是不存在绝对最优的调度方案关键看园区kpi考核的权重。这套程序的价值就在于把决策空间的全貌呈现出来而不是帮你拍板。5. 常见问题与排查技巧实录5.1 求解时间爆炸缩减种群、简化约束还是分时段优化加了储能系统之后染色体维数变大求解时间会明显增长。一个典型的192维优化问题种群200、迭代500代时Matlab跑一次可能超过半小时。这时候第一反应不应该是开熬夜模式等结果而是先找瓶颈。我的排查顺序是先看目标函数里有没有重复计算比如燃气轮机燃料消耗量在成本目标里算了一遍在排放目标里又算了一遍完全可以用中间变量缓存再看约束判断里有没有调用高次循环如果能用向量化运算替代循环速度提升是数量级的最后才考虑牺牲解的质量比如把种群缩小到120迭代缩减到200代先把趋势跑出来。如果以上手段都做完了仍然慢可以考虑把全天24时段拆成高峰、平段、低谷三个独立优化窗口边界条件和储能SOC用衔接值传递。这样虽然牺牲了全局最优性但能解决仿真时间紧张的问题工程上是一种可行的妥协手段。5.2 Pareto前沿分布不均调整拥挤度与交叉变异参数最烦人的问题是跑完迭代以后Pareto前沿上所有解挤成一团只覆盖了一小段目标空间。这说明算法的探索能力不够种群过早收敛了。一个有效的处理方法是适当降低变异概率的同时提高模拟二进制交叉的分布指数。直观理解就是让子代更多继承父代的结构信息同时保持局部微调的能力从而在Pareto前沿端部产生更多候选点。另一个思路是初始化时人为在可行域两端撒一批解比如一个解全部用燃气轮机满发、一个解全部电网购电人为提供极端的起点算法有更大机率向外延展前沿。我在实测中还发现如果三个目标之间的量纲差异过大比如运行成本是几万元、碳排放是几千千克、能效是0.8上下拥挤度距离计算会被量纲大的目标主导。解决方法是先把三个目标分别做min-max归一化处理再进行拥挤度计算。这一步我在很多开源代码里都没看到却是真实工程里影响分布均匀度的关键细节。5.3 约束失效的可疑迹象与快速定位方法优化完成后发现某个时段的电功率不平衡很大直接用源数据检查不出来这种情况通常是模式约束出现冲突。我遇到最多的是SOC首尾不一致问题蓄电池起始SOC设定0.5、结束SOC宽松结果优化结果把所有电量都放在最后几个小时放完SOC直接掉了下去。排查技巧是写一个结果校验脚本专门把解码后的设备出力重新代入约束条件计算每个时段的失衡量并绘制失衡量随时间变化的曲线。哪个时段失衡尖峰明显就重点检查那个时刻的设备到上下限、爬坡约束或者SOC边界。这个脚本调试期就要写好否则后期找问题无异于大海捞针。另一个隐蔽的坑是罚函数法中的惩罚项与目标的量纲不匹配。如果惩罚系数设得太小算法会认为违规无所谓一直输出不可行解设得过大又会破坏Pareto前沿的多样性。我的做法是先运行一次无约束优化把无约束目标值的量级统计出来再根据这个量级去设定惩罚系数的初始值一般设为目标最大值的0.5到1倍即可。5.4 模块化代码结构的几点建议程序写到最后最影响后续复用体验的其实是代码结构。我推荐把整个程序拆成六个模块数据输入模块负责读取负荷曲线、电价、气价和设备参数设备模型模块提供各个设备的能量转换函数目标函数模块计算三个目标值约束模块计算约束违反量主优化流程模块实现NSGA-II循环结果输出模块负责画图和统计数据。这几个模块之间的数据接口要固定下来。我习惯统一使用结构体传递数据比如参数结构体param包含天然气价格、电价曲线、设备效率等所有静态数据决策变量结构体x包含各设备24小时出力矩阵。这样换算例时只需要改数据输入模块换算法时只需要替换主优化流程模块目标函数和约束模块一行都不用动。如果你自己动手复现这套程序我个人建议先不要急着换更花哨的算法老老实实把NSGA-II跑通然后试着改动目标函数去看结果变化。我在实际调试中最大的一个感受是模型本身的精度和约束的完备程度往往比算法先进与否更能决定调度方案的可用性。很多看起来是优化求解器的问题最后查根因都回到建模细节上去了。先把这一步做扎实后面加更多设备、换更复杂的算法才有意义。
返回列表