“这个电价下,光靠燃气轮机跟吸收式溴化锂配合,很可能比不过‘电制冷+燃气锅炉’的常规路子!”——这是我第一次把完整冷热电联供(CCHP)模型跑通、拿到初步帕累托前沿时,对着合伙人说的第一句话。
做综合能源系统运行优化,最大的错觉就是“联供一定比分供省钱省碳”。真实情况是,在特定电价、气价和气网容量约束下,CCHP 机组的启停策略、电热冷三种能量流的分配比例,以及蓄能装置的充放节奏,每一环都在互相拉扯。只盯着单一目标(比如只求运行费用最低)很容易做出“省了钱、碳排放却高了”的方案;反过来,只压碳排放又可能买到高价电、频繁启停机组,实际运维成本根本吃不消。
我花了大半年时间,把“多目标算法 + 冷热电联供型综合能源系统运行优化”这件事从模型建到算法实现,跑了几千次迭代,踩了不少坑,也积累了一些可以直接抄作业的经验。这篇文章不打算给你从头普及其基本原理,而是重点讲清楚三件事:这个系统到底在优化什么,多目标算法在现场落地时到底怎么跟物理约束耦合,以及遇到“帕累托前沿不收敛”“约束违反率居高不下”这些真实问题时,我到底是怎么逐项排查的。
1. 项目概述:一个 CCHP 系统运行优化到底在解什么题
1.1 核心问题拆解:从设备的“能效最优”到系统的“运行最优”
先说结论:这套系统优化的不是某台设备的能效,而是整个园区在 24 小时内的综合用能账单。一个典型冷热电联供系统,物理结构一般包含燃气轮机(或内燃机)、余热锅炉、吸收式制冷机组、电制冷机、燃气锅炉、蓄电/蓄热/蓄冷装置,复杂的还会加上光伏和储电。能量流关系可以用一句话概括:燃气轮机发电的同时产生高温烟气,烟气余热进入余热锅炉制成蒸汽或热水,再分一路去驱动吸收式制冷机,另一路直接供热;电负荷缺口由电网购买,冷负荷缺口由电制冷机补足,热负荷缺口由燃气锅炉调峰。
所以,题目里的“运行优化”落实到具体数学语言上,就是一组“在满足电、热、冷三种负荷需求的前提下,决定每小时每台设备出力的数值”。决策变量包括了燃气轮机的发电功率、余热分配比例、吸收式制冷机开启台数、电制冷机负载率、燃气锅炉出力、储电装置的充放电功率(充为正还是放为正取决于你建模时的符号习惯)、蓄热罐的储/放热速率,以及整个系统与电网的交换功率。
这个问题的规模看起来不大——如果按 24 小时、每小时一个决策点,决策变量大概是 20 到 30 个维度——但它有两个显著难点:
第一,设备模型存在非线性。比如燃气轮机的效率不是恒定的,它跟负载率呈现近似二次关系,低负载率区间效率掉得厉害;吸收式制冷机的性能系数 COP 也随冷负荷率和冷冻水温度变化。如果用传统数学规划方法,这些非线性项就要做分段线性化或引入 0-1 变量描述启停,模型会变得非常笨重。
第二,目标之间天然冲突。我最初觉得“降低运行成本”和“减少碳排放”至少方向一致,跑完第一组实验就被打脸了。天然气价格低于电价的时候,燃气轮机满发反而最省钱,但满发意味着气耗高、碳排放高;反过来,如果为了降碳拼命用余热、少买电,可能要在电价谷段少启机,然后把冷负荷全压到电制冷机上,结果夜间的电费蹭蹭涨。
1.2 为什么单目标优化在这个场景里不够用
很多做过楼宇能源管理的人第一反应是问:“为什么不直接建一个单目标函数,把碳成本加上碳税,或者给碳排放乘一个权重,转成钱,然后调用现成的优化求解器?”
这个方法在理论上是可行的,在工程上却不好落地。问题在于权重系数太难拍脑袋定。碳价怎么设才合理?是用 50 元/吨还是 200 元/吨?如果园区有可再生能源配额考核,是不是还得给“绿电消纳比例”设定一个更大的权重?不同园区、不同季节、不同政策环境下,这个权重的敏感性非常高。我见过一个案例,权重稍微调高一点,优化结果直接从“燃气轮机满发”跳到“几乎不启机”,中间完全没有过渡方案可以选。
多目标算法的好处是:不预设决策者偏好,直接一次性给出一组帕累托最优解集。比如运行成本和碳排放两个目标,算法会告诉你“在每吨碳排放只能降低这些成本之间,存在这么多个可行的运行方案”,决策者再从里面挑一个符合政策要求或预算约束的方案。像 NSGA-II、NSGA-III 这类进化算法,天然适合这种“目标个数 2 到 3 个、决策变量连续+离散混合、约束非线性”的黑盒优化问题。
1.3 项目目标与适用范围:从园区级走向区域级
做这个项目的初衷,其实是想给一个典型的高校/产业园区的能源站做一套日前调度方案。这个园区白天以电负荷和冷负荷为主,夜间有部分热负荷(生活热水),配了两台 1000 kW 级燃气内燃机、一台余热蒸汽锅炉、两台 500 kW 电制冷机、一台吸收式制冷机,外加一个 200 m³ 蓄热水罐。在这个规模下,优化算法单次求解时间控制在 30 秒到 2 分钟内都是可以接受的;但如果放到区域级综合能源系统(几十个能源站、上百个负荷节点),就得考虑时序分解或并行化才能勉强满足在线调度的实时性。
所以这篇探索文章适用的范围是:园区级、楼宇级 CCHP 系统的日前运行优化与方案比选,以及想要理解“多目标进化算法怎么跟能源系统物理模型结合”的研究者或工程师。如果你想做的是实时滚动优化(比如 5 分钟级别),这篇文章的模型思想可以借鉴,但算法效率需要单独优化。
2. 系统建模:目标函数、决策变量与约束条件的逐项拆解
2.1 目标函数设计:我为什么把“一次能源利用率”也放进目标集
多数论文喜欢用两个目标:运行成本和碳排放量。我在实际项目里加了第三个目标——一次能源利用率(PESR,Primary Energy Saving Ratio),相对分供系统而言。原因很简单:成本最低和碳排最低的方案,有时会选择“多买电、少启机”,这会拉低整个系统的能源转换效率,长期看不利于能效考核。
所以最终的目标函数集是这样的:
- f1:日运行总成本(元),包括购电费用、天然气费用、设备启停惩罚费用,减去可能的售电收入。
- f2:日碳排放总量(kgCO₂),按电网平均排放因子和天然气排放因子折算。
- f3:一次能源利用率指标,定义为联供系统相对分供系统的节能率,通常可以换算成“节省的一次能源量”。
这三个目标本质上互相冲突:多买电可能省气,省气意味着碳排放下降,但电网电的间接排放又高;多启燃气轮机虽然增加气耗和碳排放,却能显著提升全厂能源利用率。
在设计目标函数时,有一个标准化的小技巧非常关键:三个目标的量纲和数值范围差别极大。f1 动辄几万元,f2 是几万千克,f3 数值只有零点几。如果不做归一化,进化算法在计算支配关系时会完全被 f1 主导,导致帕累托前沿在 f2、f3 方向上几乎无法展开。我用的方法是对每个目标做 Min-Max 归一化,先跑一轮纯随机采样,确定每个目标的取值范围,然后用归一化后的值做支配判断。这样做还有一个额外好处:可以直观地在图上看到每个目标的可改进空间。
2.2 决策变量与约束条件:哪些是“硬约束”,哪些可以适当松弛
这个系统的决策变量,按照我的建模习惯,分成三类:
一是连续型变量,如燃气轮机发电功率、电制冷机负载率、蓄电装置充放电功率、蓄热罐储/放热功率。这些变量与设备运行特性和物理规律直接相关。
二是整数/0-1 型变量,如吸收式制冷机启停状态、燃气轮机启停状态。引入这些变量之后,优化问题从连续的数学规划问题变成了混合整数非线性问题(MINLP),这也是为什么传统方法求解困难而进化算法反而好用的原因——进化算法不要求模型可导或凸。
三是耦合型变量,比如余热分配比——它连接了燃气轮机余热、吸收式制冷机输入热量和供热端,这个分配一旦变化,整个下半段能量流都会跟着变。
约束条件里,最常见的有这么几类:
设备出力上下限约束。最简单的形式是 P_min ≤ P ≤ P_max,进化算法里常用罚函数处理。
能量平衡约束。这是整个模型的骨架:电平衡要满足发电机出力 + 电网购电 + 储电放电 = 电负荷 + 电制冷机耗电 + 储电充电;热平衡要满足余热供热 + 燃气锅炉出力 + 蓄热罐放热 = 热负荷 + 蓄热罐充热(如果是蓄热式);冷平衡要满足吸收式制冷出力 + 电制冷出力 = 冷负荷。
爬坡约束。燃气轮机一小时内的出力变化不能超过额定值的某个比例(比如 30%),否则实际运行中会触发保护或加速设备损耗。
蓄能装置状态约束。蓄热罐的储热容量有上下限,而且充放热功率受换热器容量限制,还必须满足每日始末状态一致的周期约束——否则算法会为了省成本把热量全留在罐里,第二天早上直接没法调度。
举个实际例子:假设蓄热罐容量为 200 m³,最大储热功率 500 kW,工作温降 40 ℃,则满罐对应的热量大约是 200 × 1000 × 4.18 × 40 ÷ 3600 ≈ 9290 kWh。如果白天燃气轮机满发产余热,蓄热罐边蓄边放,夜间电价便宜时再用燃气锅炉补热——这个策略是优化算法自己“悟”出来的,挺好的范例,但它必须满足“日末蓄热量与日初相同”的周期约束,否则第二天调度完全失真。
2.3 设备模型的简化与标幺化:平衡计算效率与建模精度
我在做第一版模型时,试图把每一台设备的效率曲线都建得非常精细,结果算法每评估一次个体就要跑大量非线性函数,整个寻优速度惨不忍睹。后来我采用了两条措施。
第一,设备效率曲线全部用“负载率-效率查表+分段线性插值”替代。比如燃气轮机的效率曲线,取 25%、50%、75%、100% 负载下的四个效率点,中间用线性插值。这个做法损失了一点精度,但保证算法在迭代过程中计算量稳定。
第二,所有变量和约束在代码里统一做标幺化处理,以该设备额定功率为基准值,把所有功率变量除以额定额定值。这样做有几个好处:一是不同量级的变量(千瓦级和兆瓦级)在交叉变异时不会因为数值差异产生偏向性;二是约束条件写起来不易出错;三是在输出结果时单独乘回基准值,不会影响可读性。
这么做之后,单次个体评估耗时从约 8 毫秒降到了约 1.5 毫秒,整个种群(100 个个体 × 300 代)的优化时间从 4 分钟缩短到了 45 秒,效果非常显著,而且解的精度基本没有明显下降。
3. 多目标算法的选型与实现:从 NSGA-II 到适应性改进
3.1 为什么用 NSGA-II 而不是单纯加权或粒子群
面对“三个目标 + 混合整数变量 + 非线性约束”的问题,选择算法时我首先排除了加权法和数学规划法。加权法的问题前面已经讲过,权重难调;传统非线性规划求解器(比如 ipopt)处理 0-1 变量无能为力;而像 scipy.optimize 里的 SLSQP,虽然能处理连续量但面对整数变量和非凸目标函数时经常被困在局部最优里出不来。
NSGA-II(非支配排序遗传算法二代)是处理这类问题最常用的进化算法。它的核心机制是“非支配排序 + 拥挤度距离”,在每一代进化中同时保留收敛性和多样性。简单说,非支配排序保证种群朝帕累托前沿方向进化,拥挤度距离保证解能均匀铺开,不扎堆在一个区域。
在实际代码里,我用的流行库是 pymoo。这个库封装了 NSGA-II,支持自定义问题和约束处理,内置了多目标性能指标(如 HV 超体积指标、IGD 反向世代距离),对二次开发和结果分析十分友好。另一个备选项是 Platypus,但它的自定义问题接口不如 pymoo 灵活,而且对 Python 新版本的支持时不时的有点问题。
3.2 算法参数设置:种群规模、交叉变异概率怎么调
我最终的参数配置是这样的:种群规模 100,进化代数 300 代,模拟二进制交叉 SBX 的分布指数为 20,多项式变异分布指数为 20,交叉概率 0.9,变异概率取“1/决策变量数”即约 1/30,决策变量总数为 30 个。这样的参数组合在整个调试过程中表现最稳。
关于种群规模多说一句:如果决策变量有 30 个,种群规模不建议低于 60,否则下一代选择压力不够,帕累托前沿的端点容易丢失;但盲目加到 500 也会让收敛代数明显变多——不是跑得慢的问题,而是算法在 300 代内尚未收敛时输出结果会很混乱,反而误导决策。
我踩过的另一个坑是变异概率。用 pymoo 默认的概率是按位变异(对于实数编码不太合理),我一开始直接用默认值,结果跑了 200 代前沿都没怎么动弹,后来把变异算子改成“多项式变异 + 按变量个数取概率”,效果立刻好了很多。其实这个细节在 NSGA-II 的原始论文里讲得很清楚——变异概率用于实数编码时应当控制单个变量的扰动幅度,而不是随机翻转。
3.3 约束处理策略:为什么罚函数数值不能拍脑袋定
进化算法处理约束的主流方法有三种:罚函数法、可行性修复法和基于支配关系的约束支配法。我在第一版代码里用了常规罚函数法,把能量平衡约束的偏差平方乘以一个 1e6 的大数加进目标函数。结果是算法花费了将近 150 代在消除不可行解上,有效搜索的时间被压缩了。
后来改用 pymoo 内置的“约束非支配”策略:在非支配排序时同时考虑约束违反度,这样不可行解之间会先比较谁违反得更少,而可行解永远支配不可行解。这样初始种群里的不可行个体也能提供有效的梯度信息,而不是被直接扔掉。同时,针对能量平衡约束这类等式约束,我额外加了“修复算子”——直接根据不平衡量去调整某个决策变量,比如如果电负荷高了就提高燃气轮机出力,把不可行解“拉回”到可行域内。
这里也分享一个经验:等式约束(比如电功率平衡)比不等式约束(比如蓄热罐容量)难处理得多。等式约束用罚函数很容易形成“数值巨大但不为零”的伪可行解,后期怎么调都卡在微小偏差上。修复算子的本质是利用系统内部的能量流特性,一步就把某个设备的出力补上,避免算法在无效方向上反复搜索。这部分在一般论文里写得很简略,实际工程里却是影响求解质量和速度的关键。
4. 案例实现与结果分析:从帕累托前沿到运行策略的解读
4.1 典型案例设计与参数设定
我做了一个典型夏季工况的算例,匹配前面提到的园区场景。负荷曲线特征是:白天 8:00 ~ 18:00 电负荷高(800 ~ 1200 kW),冷负荷同步走高(峰值约 800 kW);夜间电负荷低(200 ~ 400 kW),热负荷主要为生活热水(60 ~ 100 kW)。
分时电价设为三类:峰段(8:00 ~ 11:00、18:00 ~ 23:00)1.20 元/kWh;平段(11:00 ~ 18:00)0.75 元/kWh;谷段(23:00 ~ 次日 8:00)0.38 元/kWh。天然气价格为 3.2 元/Nm³,天然气低位发热量按 9.7 kWh/Nm³ 计算。电网排放因子取 0.581 kgCO₂/kWh,天然气排放因子取 0.20 kgCO₂/kWh(按热值折算)。
燃气轮机额定功率 1000 kW,最低技术出力按 30% 即 300 kW,发电效率曲线为:30% 负载率时 28%、50% 负载率时 34%、75% 负载率时 37%、100% 负载率时 38%。余热回收率取 45% 左右——也就是说输入燃气轮机的一次能量中有约 45% 可以作为热回收量,具体数值与排烟温度和换热器效率有关。
4.2 帕累托前沿三目标可视化与方案解读
跑完 300 代之后,我拿到了一组分布均匀的帕累托前沿,用平行坐标图和高维散点图进行了展示。这个算例里三个目标的重要结论是:系统运行成本的下限约为 2.4 万元/日,碳排下限约为 18.6 吨/日,而一次能源节能率的上限可以达到 18%。
盯着这个前沿去挑方案时,我发现了一个值得注意的现象——前沿在“成本-碳排放”切的平面上存在一个明显的“拐弯区”。在拐点左侧,每多花约 2000 元成本,碳排放能降 1.5 ~ 2 吨;但拐点过了之后,再降 1 吨碳排放需要多花的钱跳到近 4000 元。这就是工程师直觉里常说的“边际减排成本曲线”在帕累托前沿上的体现。决策者如果面对一个碳配额考核红线,完全可以用这个前沿直接判断出“为了满足红线,今天大约需要额外花多少钱”,而不需要重新跑一遍模型。
在我选中的推荐方案(运行成本 2.75 万元、碳排放 20.2 吨、节能率 14.5%)里,策略表现是这个样子的:燃气轮机白天 8:00 ~ 20:00 基本满发,傍晚电价高峰前蓄热罐提前蓄热,夜间 23:00 之后停机改用谷电驱动电制冷机供冷,燃气锅炉只在凌晨热负荷缺口较大时补燃。这个策略完全符合直觉,但要注意的是,它并不是一拍脑袋就能想出来的——蓄热罐的蓄放时间节点和燃气轮机的停机时间点,差一个小时系统费用就可能高出 10%。
4.3 与单目标优化结果的对比,多目标的价值体现在哪里
为了验证多目标算法的必要性,我做了一个对照实验:只把运行成本作为单目标,用同样的进化算法(此时退化成遗传算法)跑了一次,最后得到的最优成本约 2.35 万元/日,比多目标前沿上的成本下限还要低一点。但看碳排放,这个方案的碳排放量是 25.3 吨/日,比多目标推荐方案高出近 25%。
这是典型的“省钱不省碳”结果。为什么多目标方案没有覆盖到这个更低成本点?因为多目标算法在做非支配排序时,这个点虽然是成本最优,但碳排太高,在和碳排放更低的解做对比时被支配掉了——它不会出现在帕累托前沿上。所以说,多目标优化给出的不是“一个答案”,而是“一个有决策价值的解集”。如果有人跟你说“我的单目标优化也能得到权衡”——别信,除非他真的能把一组不同权重的单目标优化全部跑一遍并画成帕累托前沿,但那样做的计算成本通常远高于直接跑多目标。
4.4 不同季节工况的适应性分析
这套模型如果只做夏季工况,说服力是不够的。我另外跑了冬季和过渡季两个工况,主要发现是:冬季热负荷高但冷负荷几乎为零,吸收式制冷机基本不启用,余热全部走供热路线,蓄热罐的作用变得非常突出;过渡季负荷温和,燃气轮机的运行策略以“跟随电负荷”为主,冷负荷多由电制冷机承担,机组启停次数明显增多。
从算法收敛性角度看,冬季工况因为制冷侧的决策变量少了一个维度(吸收式制冷机关闭),搜索空间缩小,300 代内收敛更充分,前沿均匀性也更好。过渡季工况反而最难——冷负荷时有时无,吸收式制冷机和电制冷机的切换点变得敏感,约束违反率相对高一些。这个现象提醒我们,在真实项目里不要只拿“典型日”的单一工况去决策全年策略,至少应该按“夏/冬/过渡季”各跑一遍,必要时补充极端天气工况(比如连续高温高湿)来检验调度方案的鲁棒性。
5. 常见问题与排查技巧实录
5.1 帕累托前沿收敛慢或前端不完整
最典型的表现是:跑了 300 代,得到的解集只集中在某个目标的中段,端点(比如成本最低但碳排最高的解)始终找不到。这个问题大概率出在两处:一是初始种群多样性不足,二是 SBX 交叉分布指数过大导致子代与父代过于相似。
对于初始种群,我建议用拉丁超立方采样替代纯随机采样,让每个决策变量在可行域内都有均匀的覆盖。对于 SBX 分布指数,如果设为 20 仍然收敛过慢,可以试着降到 10~15,增大子代发散性。另外,如果发现端点缺失,可以考虑每隔若干代引入一次“极端化扰动”——挑选种群中靠近某个目标极值的个体,在其邻域做更大步长的变异。
5.2 能量平衡约束在迭代后期始终有小偏差
这是最容易让人崩溃的问题。现象是:最后选出的解,电平衡偏差在 ±5 kW 以内,看似很小,但乘以 24 小时一年很快就变成不可忽略的能量缺口。
排查思路有两个方向。第一,检查是不是等式约束的单位换算出了问题——我犯过把 kW 和 MW 混用的低级错误,导致热平衡约束看起来满足,实际换算后差了 10 倍。第二,检查修复算子是否只修复了决策变量而没有同步更新关联变量——比如本应因为电制冷机功率减小而减少的耗电量,没有在能量平衡方程里重新计算,平衡就永远差了那一块。
如果修复算子已经加了还是有小偏差,我的做法是在最后筛选可行解时加一个宽容度阈值(比如 ±0.1 kW),这个精度对日前调度来说完全足够,也不会带来实际运行问题。
5.3 如何检验算法是否真的“优化”而不是“碰巧找到好解”
好的多目标优化结果,必须经得起稳定性检验。我的标准做法是:同一个算例用不同随机种子重复跑 10 次,然后计算 10 次帕累托前沿之间的 IGD 指标。如果 IGD 波动幅度在 5% 以内,说明算法稳定;如果几次运行得到的前沿差异很大,多半说明初始种群或优化代数不足。
另外一个非常有效的验证手段是“已知解检验”——人工构造一个策略(比如“白天满发+夜间停机”),算它的三目标值,看它是否被算法输出前沿支配。如果这个合理策略落在前沿内部而不是被支配掉,说明算法可能还没收敛,前沿不是真正的帕累托边界。我就是通过这个办法发现过两次代码 bug——一次是目标函数里忘记加启停惩罚,另一次是燃气轮机效率分段点顺序写反了。
5.4 从论文算法到现场运行,还需要补一堂算
最后想说点论文和工程之间一个常被忽略的差距。算法给出的日前调度方案,在实际运行中一定会遇到负荷预测误差和设备响应滞后。我现在的做法是:把 24 小时滚动窗口切成 4 个 6 小时子窗口,每个子窗口用多目标算法重算一次,只执行第一个小时的调度指令,后 5 小时的方案作为参考。这样做既保证了今天的全局优化性(而不是只看眼前 1 小时),又给实际运行留了修正的余地,实测下来能源账单比固定日前方案再低 3%~5%。
关于蓄热罐的“日末回位”约束,在现场运行中也做了调整:允许最后时刻蓄热量偏离初始值,但加一个软惩罚计入目标函数,这样不会因为死板的周期约束而牺牲前半夜的调度效果。
6. 过程中积累的实操心得与后续可扩展方向
这套模型跑通之后,回头看最大的收获不是算法本身,而是“把物理问题翻译成算法语言”的那几步。决策变量的编号顺序、约束条件的归一化方式、修复算子的设计顺序,每一个看起来很小的代码决定,最后都会实实在在地反映在收敛速度和前沿质量上。我的体会是,多目标优化做得好不好,七成功力在建模和约束处理上,剩下的三成才是调算法参数。
另外,对于想把这个方法推向更复杂场景的人,我提供两个经过思考的建议。
第一,如果系统里接入光伏和储能,目标函数里值得增加一个“弃光惩罚”或“可再生能源消纳率”作为第四个目标。因为单纯的成本和碳排目标无法刻画“可再生能源优先消纳”的政策导向,而这一维度在双碳背景下越来越重要。
第二,如果要做多园区协同优化,决策变量会爆炸式增长(每个园区都有自己的机组和储罐),此时 NSGA-II 的收敛速度可能不够用,可以尝试把问题分解成“上层全局功率分配 + 下层各园区独立优化”的双层结构,上层用 NSGA-II 或 MOEA/D,下层用单目标快速求解器,配合并行计算,才能满足在线调度的时效性。
最后分享一个实际的小技巧:调试算法时,不要一上来就跑完整 24 小时模型。先固定负荷曲线为常数(比如全天都是 500 kW 电负荷、300 kW 冷负荷),把模型简化后跑通,确认能量平衡修复和约束处理逻辑正确,再慢慢换成真实负荷曲线。这个“从静态到动态”的调试顺序,能帮你节省大量的排错时间——我至少因此少加了三个星期的班。