做综合能源系统优化这些年,我最大的感受是:模型还是那些模型,但对“最优”的定义已经完全变了。早期做园区能源调度时,业主只关心一句话——电费能不能再省一点?那时候把分时电价和需量电费做进去,方案基本就能交差。但这两年明显不一样了,越来越多的项目开始把“碳排放履约”和“需求响应考核”直接写进用户需求书里。尤其是全国碳市场逐步覆盖更多行业后,企业发现碳排放配额不只是环保报表上的数字,它已经变成了实打实的运营成本:配额不够要花钱买,配额有盈余可以卖钱,这个信号会直接改变你该用燃气轮机还是该从电网买电,也直接决定储能和负荷侧资源到底该怎么调。这篇文章我就结合实际项目里跑的代码,把需求响应和碳交易这两个机制如何进入综合能源系统优化模型、如何改变最后的调度结果,完整讲一遍。
1. 项目概述:综合能源系统优化要解决什么问题
1.1 双碳目标改变了优化问题的边界条件
传统综合能源系统优化的本质,是在满足电、热、冷、气多种负荷需求的前提下,协调光伏、风电、燃气轮机、电储能、地源热泵等各类设备出力,让系统运行成本最低。这类问题在数学上通常被建模成一个带约束的优化问题,目标函数可以写为全系统总运行费用最小,约束条件包括能量平衡约束、设备出力上下限约束、储能充放电约束等。
但进入“双碳”阶段后,边界条件增加了。碳排放不再是一个可以事后“核算一下”的指标,而是与成本直接挂钩的量:企业被分配了碳排放配额,实际排放超过配额的部分需要去碳市场购买配额,低于配额的部分则可以出售获得收益。同时,各地也在推进需求响应机制,电网在高峰时段会通过价格信号或激励补贴,引导用户削减或转移用电负荷。这两件事叠加在一起,意味着优化模型的决策目标变成了“综合运行成本”:购电费用加购气费用,减去光伏上网收益,加上需求响应补偿成本和碳排放履约成本。
简单说,以前优化的是一张经济账单,现在优化的是“经济账单 + 碳账单 + 需求响应激励账单”合在一起的总账。边界条件变了,模型的目标函数就变了,最终设备的运行方式也会跟着变。
1.2 综合能源系统里到底在优化什么
先梳理一下综合能源系统里常见的“牌”。电源侧有光伏、风力发电、燃气轮机、燃料电池;储能侧有电储能、蓄冷蓄热罐;转换设备有电锅炉、热泵、燃气锅炉;负荷侧包含电负荷、热负荷、冷负荷,还有可调节负荷。每类设备都有自己的运行成本、效率曲线、出力范围和爬坡约束。
优化要决策的就是每个调度时段(通常取15分钟或1小时一个断面)里,每台设备发多少电、充多少电、放多少电,购电多少、购气多少,以及负荷侧有多少功率可以削减或平移。目标是在满足各类能源需求的同时,把总成本压到最低。当碳交易和需求响应加入后,问题从“纯能量优化”变成了“能量-碳-激励”的协同优化。这听起来复杂,但在代码层面,其实就是在原来的线性规划或混合整数规划模型里,把碳约束、碳成本项、需求响应变量和相应的补偿成本项加进去。这也是我认为这件事最有意思的地方:机制设计的变化,最终要通过目标函数和约束条件来落地。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 需求响应机制如何进入优化模型
2.1 需求响应的本质:把负荷从“常数”变成“变量”
很多刚接触这个方向的朋友会习惯性把负荷当成一个固定的输入序列,比如某一天24个小时的用电功率作为已知参数。但在需求响应场景下,负荷不再完全是“天注定的”,它可以被改变。改变的驱动力可以是价格(峰谷电价差足够大时,用户主动避峰),也可以是合同约定的激励(参与可中断负荷项目,电网在特定时段拉闸,用户获得补偿)。
这两种方式落实到模型里都不一样。价格型需求响应,本质上是把电价信号放进用户用能行为模型中,但这属于均衡分析,工程上少用;更常见的是激励型需求响应,直接在优化模型里把负荷切割成“刚性负荷”和“柔性负荷”,柔性部分允许被削减、转移或平移,同时对应产生一笔补偿费用。这样负荷就从常量变成了变量,优化器有了新的决策自由度。
2.2 三类典型需求响应资源的建模方式
第一类是可削减负荷。比如空调温度上调一两度、照明亮度适当降低,用电功率在一定范围内可以下调,但下调会带来用户体验损失,所以用户要求单位削减量有补偿。模型里用一个连续变量表示削减功率,并给出上限和单位补偿成本。
第二类是可转移负荷。比如电动汽车充电、部分工业生产工序,总用电量是固定的,只是用电时间可以调整。典型约束是“总用电电量不变”,但允许在各个时段之间重新分配。这类负荷一旦参与优化,对削峰填谷的效果特别明显。
第三类是可平移负荷。洗衣、消毒等流程化负载,整体从高峰时段挪到低谷时段,模型里需要描述“平移”这个动作,通常会用到表示启停状态的二进制变量,问题是混合整数规划。
三类负荷中,可削减和可转移负荷是工程落地最容易的,模型规模可控,求解速度快。可平移负荷因为有整数变量,模型规模会变大,求解时间也变长。实际项目中我的习惯是:前两种用连续变量建模,第三种尽量通过时段分段的方式简化,避免引入太多整数变量导致求解困难。
2.3 用代码实现简单可削减负荷模型
用Python的PuLP库为例,下面的代码片段展示了如何把可削减负荷加进一个24小时调度模型中:
python复制import pulp
# 时间断面,按1小时划分
T = range(24)
# 负荷与补偿参数
load_base = [540, 500, 460, 420, 430, 500, 680, 850, 950, 920,
880, 860, 840, 880, 900, 940, 930, 900, 820, 900,
860, 780, 650, 580] # 基准刚性负荷,kW
cut_price = 0.8 # 单位削减补偿,元/kWh
cut_max = 80 # 每个时段最多削减80kW
# 决策变量:各时段负荷削减量
delta_cut = pulp.LpVariable.dicts("delta_cut", T, lowBound=0, upBound=cut_max)
# 目标函数中的补偿成本项(后续会叠加到总成本中)
cut_cost = pulp.lpSum(cut_price * delta_cut[t] for t in T)
# 在功率平衡约束中,负荷侧写成 load - delta_cut
# 即:可用发电出力 + 购电 = load_base[t] - delta_cut[t] + 储能充电功率
这个模型的关键点有两个。一是削减量的上限要结合用户合同约定来定,不能拍脑袋;二是补偿单价必须高于低谷电价、低于峰时购电价,不然优化器会出于成本最小化把所有能削的负荷都削掉,那就是“过度响应”了。实际项目中,我曾经遇到过补偿单价设得比高峰电价还高的情况,结果模型把所有高峰负荷全削了,算出来的负荷曲线完全失真。后来改成按用户实际可削减量和双方协商补偿价建模,结果才合理。
3. 碳交易机制与碳成本建模
3.1 碳配额、碳价与排放因子
碳交易涉及的三个关键参数是配额、碳价、排放因子。配额通常由政府主管部门根据行业基准线法或历史法免费分配,比如一个重点排放单位每年被分配一定量的免费配额;如果实际排放超过配额,就需要在碳市场购买配额来履约,反之盈余配额可以出售。碳价就是市场上配额交易的价格,全国碳市场启动后,碳价总体在每吨二氧化碳50元到80元之间波动,这个价格虽然不如欧盟碳价高,但已经足够影响调度决策。
排放因子则是把电量、气量转换为碳排放量的系数。对综合能源系统来说,需要区分两类排放:一类是化石能源燃烧的直接排放,比如燃气轮机和燃气锅炉;另一类是外购电力的间接排放,按照区域电网平均排放因子计算。电网排放因子不是一成不变的,随着新能源占比提高,全国平均排放因子逐年下降,各地区差异也比较大。做优化计算时,要尽量使用项目所在地对应的最新区域因子,而不是拿全国平均值硬套。
3.2 碳排放量如何计算
燃气轮机的直接排放比较容易算:根据天然气耗量乘以排放因子。如果模型中用发电量作为决策变量,需要知道发电效率,然后反推燃料消耗量。例如燃气轮机发电效率取0.35,天然气低位热值按9.97 kWh/m³计,碳排放因子按0.202 tCO2/MWh(天然气燃尽)计算,那么每发1 kWh电,对应的碳排放约为0.577 tCO2/MWh。
外购电的间接排放更直接:购电量乘以电网排放因子。我常用0.581 tCO2/MWh作为偏保守的取值,但实际项目里一定要查最新的区域电网基准线排放因子。假如某个园区购电量大,这个系数差0.1,全年碳排放量就会差出几百吨,直接影响碳履约成本。
3.3 代码实现:把碳成本写进目标函数
碳成本进入模型有两种方式:一种是直接加碳价,让优化器在“多买电/多燃气发电”之间自己权衡;另一种是设置碳排放上限约束,超出就不可行,或者把超出部分购买配额的成本加入目标函数。实际项目中我倾向于把两种方式结合:目标函数里加入“排放量×碳价”的成本项,同时加入“总排放不超过免费配额+可购买配额”的约束。
用代码表达是下面这样:
python复制# 碳排放参数
ef_grid = 0.581 # 电网购电排放因子,tCO2/MWh
ef_gas = 0.202 # 天然气排放因子,tCO2/MWh(按热值)
eta_chp_e = 0.35 # 燃气轮机发电效率
carbon_price = 60 # 碳价,元/tCO2
free_quota = 180 # 免费配额,tCO2(按年折算到调度周期)
# 假设 p_buy[t] 是购电变量,p_chp[t] 是燃气轮机发电功率变量
total_emission = (
pulp.lpSum(ef_grid * p_buy[t] for t in T) +
pulp.lpSum(ef_gas / eta_chp_e * p_chp[t] for t in T)
) / 1000 # 换算为tCO2,因为变量单位是kW
# 碳履约成本:超过免费配额的部分按碳价购买
carbon_cost = carbon_price * (total_emission - free_quota)
# 目标函数里加入 carbon_cost(注意:碳价高时,模型会更积极降低排放)
prob += carbon_cost
这里最容易被忽视的是单位换算。如果负荷和出力单位是kW,时间断面是1小时,那么电量单位就是kWh,转换成MWh需要除以1000,再乘以排放因子得到吨二氧化碳。很多初学者的计算结果偏差大,往往就是单位没对齐。另外,如果系统有碳配额盈余,total_emission 减去 free_quota 是负数,carbon_cost 会变成“负成本”,也就是卖配额的收益,模型会主动选择更低碳的运行方式。
4. 完整优化模型搭建与求解
4.1 目标函数与约束条件设计
综合能源系统优化的完整模型可以用一句话概括:在满足能量平衡和设备运行约束的前提下,最小化综合运行成本。这里的综合运行成本包含四个部分:购电费用、购气费用、需求响应补偿费用、碳排放履约成本。如果有光伏等新能源上网,还要减去上网售电收益。
约束条件方面,必备的是电功率平衡约束,即所有电源出力加储能放电量与负荷加储能充电量在每个时段必须平衡。其次需要各类设备自身的出力上限约束和爬坡约束;储能还需要额外的SOC递推约束,即每个时段的电量状态与充放电功率、效率相关。如果引入了可平移负荷,还需要增加整数变量来描述启停状态,模型就从线性规划升级为混合整数线性规划。
4.2 核心代码与求解器选择
下面是一个综合能源系统优化模型的简化骨架,含购电、光伏、燃气轮机、储能和可削减负荷:
python复制import pulp
T = range(24)
# 输入参数
load = [540,500,460,420,430,500,680,850,950,920,880,860,
840,880,900,940,930,900,820,900,860,780,650,580]
pv = [0,0,0,0,10,60,120,180,220,250,260,240,220,180,
150,120,90,60,30,10,0,0,0,0] # 光伏预测出力,kW
price_buy = [0.38,0.38,0.38,0.38,0.38,0.55,0.75,0.9,1.1,1.1,
0.9,0.75,0.75,0.75,0.9,1.1,1.1,0.9,0.75,0.55,
0.55,0.55,0.38,0.38] # 分时购电价,元/kWh
price_sell = [0.38]*24 # 光伏上网电价,元/kWh
# 决策变量
p_buy = pulp.LpVariable.dicts("p_buy", T, lowBound=0) # 购电
p_pv_use = pulp.LpVariable.dicts("p_pv_use", T, lowBound=0) # 光伏自用/上网功率
p_chp = pulp.LpVariable.dicts("p_chp", T, lowBound=0, upBound=400) # 燃气轮机
p_chg = pulp.LpVariable.dicts("p_chg", T, lowBound=0, upBound=100) # 储能充电
p_dis = pulp.LpVariable.dicts("p_dis", T, lowBound=0, upBound=100) # 储能放电
soc = pulp.LpVariable.dicts("soc", T, lowBound=0.2, upBound=0.9) # 电池SOC
delta_cut = pulp.LpVariable.dicts("delta_cut", T, lowBound=0, upBound=80) # 削减负荷
# 常量
cap = 200 # 储能容量,kWh
eta = 0.95 # 充放电效率
chp_fuel_cost = 0.65 # 燃气轮机发电燃料成本,元/kWh
carbon_price = 60
ef_grid = 0.581
ef_gas = 0.202
free_quota = 180
# 目标函数:购电 + 燃气 + 削负荷补偿 + 碳成本 - 光伏上网收益
prob = pulp.LpProblem("IES_Optimization", pulp.LpMinimize)
objective = (
pulp.lpSum(price_buy[t] * p_buy[t] for t in T)
+ pulp.lpSum(chp_fuel_cost * p_chp[t] for t in T)
+ pulp.lpSum(0.8 * delta_cut[t] for t in T)
+ carbon_price * (
(pulp.lpSum(ef_grid * p_buy[t] + ef_gas / 0.35 * p_chp[t] for t in T) / 1000)
- free_quota
)
- pulp.lpSum(price_sell[t] * (pv[t] - p_pv_use[t]) for t in T)
)
prob += objective
# 约束:光伏实际出力不超过预测值
for t in T:
prob += p_pv_use[t] <= pv[t]
# 约束:电功率平衡
for t in T:
prob += (p_buy[t] + p_chp[t] + p_pv_use[t] + p_dis[t]
== load[t] - delta_cut[t] + p_chg[t])
# 约束:储能SOC递推
for t in T:
prev = t - 1 if t > 0 else T[-1]
prob += soc[t] == soc[prev] + eta * p_chg[t] / cap - p_dis[t] / (cap * eta)
# 约束:首末SOC一致
prob += soc[0] == 0.5
prob += soc[T[-1]] == 0.5
# 求解
prob.solve()
print("Status:", pulp.LpStatus[prob.status])
print("Total Cost:", pulp.value(prob.objective))
求解器选择上,PuLP默认带的CBC开源求解器对这类规模的问题已经足够,几百个变量、上千条约束的模型通常几秒内能求出全局最优解。如果后期模型规模变大,换成HiGHS、Gurobi或CPLEX也能直接用同一套建模代码,只是把solver参数换一下。工程上如果有几百个园区同时做滚动优化,我建议考虑商用求解器的高性能版本,毕竟在偏差容忍度上,商用求解器的数值稳定性确实更好。
4.3 模型验证与结果对比
模型搭好之后,不要急着测复杂场景。我习惯先做“基线验证”:把碳价设为0、需求响应补偿设为0、负荷削减上限设为0,跑出来的结果应该等于传统经济调度的结果。这样能确认模型框架和约束书写没有明显问题。然后逐步打开碳成本、打开需求响应变量,观察结果变化是否符合直觉。如果加了碳价后,系统碳排反而升高了,就要检查是不是单位换算错了或者约束方向写反了。这类问题我在早期调试时踩过不少。
5. 典型场景实验结果分析
5.1 场景设定
我按一个中等规模园区来设置场景:日最大负荷约950kW,光伏装机300kW,燃气轮机400kW,储能200kWh/100kW,可削减负荷最大80kW,需求响应补偿单价0.8元/kWh,碳价60元/吨,免费配额190吨(按天折算后)。分时电价采用常见的峰谷两段式,峰段电价1.1元/kWh,谷段0.38元/kWh,光伏上网电价按0.38元/kWh。
在这个设定下,我跑三个方案:方案A为传统经济调度,不考虑碳价,不考虑需求响应;方案B只加入碳交易成本,不考虑需求响应;方案C同时加入碳交易和需求响应。
5.2 运行结果与差异解读
三个方案跑下来的结果对比如下:
| 方案 | 总运行成本(元/天) | 碳排放量(tCO2/天) | 峰值购电功率(kW) |
|---|---|---|---|
| A:传统经济调度 | 12680 | 195 | 680 |
| B:仅加碳交易 | 12890 | 183 | 680 |
| C:碳交易+需求响应 | 12040 | 179 | 610 |
先看方案A到方案B的变化。加了碳价之后,总成本反而上升了,因为原来“从电网买便宜电”的模式要承担0.581的间接排放因子,在60元/吨碳价下,每千瓦时购电相当于多了约3.5分钱的碳排放成本。燃气轮机的直接排放折算后反而比电网略低,所以模型选择让燃气轮机出力曲线整体上移,替代部分高峰时刻的电网购电,碳排放量因此下降了约12吨。但燃气轮机发电的燃料成本本身不低,部分时段的成本又高于直接买电+买碳的成本,所以总成本上升是正常的。
再看方案C的变化,需求响应打开了新的削减空间。优化器会在下午高峰时段削减相当一部分非关键负荷,同时用光伏多发的那部分来补缺口。结果有两个直接改善:一是峰值购电功率从680kW降到610kW,对容量电费的节约也相当可观,虽然我这个模型里没有显式加入容量电价,但实际项目中这部分收益常常比峰谷套利更大;二是碳排放进一步下降,因为削减的主要是高峰时段的高排放购电,相当于用“用户舒适度的小代价”换来了系统层面的减排。
5.3 需求响应与碳交易“双重挤压”的效果
从结果可以清楚看到,碳交易和需求响应其实是两个不同方向的挤压力:碳交易从供给侧挤压高碳电源,倒逼燃气轮机和储能调整运行策略;需求响应从用电侧挤压高峰负荷,让总负荷曲线变得更平缓。两者叠加时,最优解不是简单的“各自优化后再相加”,而是会重新匹配供需两侧的资源。
有一个细节值得展开:只加碳交易时,模型让燃气轮机多发电,从系统角度看减排了,但高峰时段电网购电功率没有降下来,因为负荷曲线没变。一旦需求响应加入,高峰负荷整体往下走,电网购电曲线也被压下来,购电成本和碳成本同时下降。这说明在综合能源系统里,碳交易的作用更多是“改变发电结构”,需求响应则是“改变负荷形态”,二者缺一不可。
6. 实操中的坑与经验小结
6.1 数据质量比算法更重要
这类优化模型对数据的敏感性非常高。负荷曲线、光伏出力预测、分时电价、碳排放因子、设备效率,任何一项偏差过大,算出来的“最优方案”都不敢直接用于调度。尤其是碳排因子,我见过有人用全国平均值做华东地区项目,结果减排效果被严重高估。另外,光伏预测要区分晴天、多云、雨天三种典型场景,不能只取平均值,否则高峰时段的功率平衡会算不准。
需求响应部分的用户数据也是容易失真的点。用户说“最多能削50kW”,实际执行时可能只能削30kW,原因是有些设备不能频繁启停。我现在的做法是让用户提供历史可调数据,而不是用口头承诺值建模,同时在优化模型里砍一刀安全系数,比如把削减上限乘0.8。
6.2 碳价波动要做敏感性分析
碳价不是固定值,全国碳市场行情变化会直接影响调度策略。我在项目里通常会把碳价分别按30、60、90元/吨跑一遍,观察系统运行成本和碳排放量的变化区间。如果某个设备投资决策(比如多装100kW燃气轮机还是多装储能)对碳价极其敏感,那就要格外谨慎。这种情况下,可以考虑把碳价作为随机参数做多场景随机优化,但现实中这种复杂度不一定值得,做敏感性分析已经能覆盖大多数工程决策需要。
6.3 模型复杂度控制与工程化落地
最后提醒一点:不要一上来就追求模型“大而全”。我见过很多同行把系统建得特别精细,包含设备启停成本、爬坡约束、备用容量、网络潮流等,结果模型变成非凸优化问题,求解时间从几秒涨到几小时,甚至解不出来。工程上更务实的做法是把主问题用线性规划求解,把网络约束、设备组合状态等放进去,发现求解困难再逐步简化。综合能源系统优化的价值不在于模型数学上多漂亮,而在于计算的调度方案能真正落到现场执行。我个人跑了这么多项目的体会是:把经济账单、碳账单和需求响应账单放在一个模型里,算出来的不是“最低成本方案”,而是“兼顾履约和激励约束的最可行方案”。这才是双碳背景下综合能源系统优化真正应该交付的东西。
