开始接触这个课题时,我第一反应就是:又是组合型命题——阶梯碳交易、P2G-CCS耦合、燃气掺氢,任何一个单独拎出来都是热门方向,凑到一起更像是把三座大山叠在了一个调度模型里。但真正把物理过程和经济逻辑捋顺之后会发现,这三个关键词围绕的其实是同一件事:怎么让虚拟电厂在排放约束和运行经济性之间找到那条最优的调度曲线。这篇文章我就把这套模型从物理原理到数学建模再到Matlab代码实现,完整拆开来讲,重点是那些论文里不会明写、但建模时一定会踩的坑。文章面向的是正在做虚拟电厂优化调度、综合能源系统研究,或者需要复现类似模型的同行,哪怕你刚接触P2G和CCS,也建议按这个顺序往下看。
1. 为什么偏偏是"阶梯碳交易、P2G-CCS、燃气掺氢"三件套
想要读懂这套模型,先得搞清楚这三样东西各自解决什么问题,以及把它们放进同一个虚拟电厂(VPP)里的逻辑是什么。我当初刚看到这个组合时也觉得是堆砌名词,直到把碳流和能流画到一张图上,才明白这三个机制在功能上是互补的,少掉任何一个,系统都会面临一块短板。
1.1 虚拟电厂:调度中心眼中的"超级电源"
虚拟电厂本身不是指一个物理电厂,它是一堆分布式资源的聚合体——风电机组、光伏阵列、燃气轮机、储能装置、可调负荷,甚至包括P2G设备都可能被装进这个"篮子"里。对外部调度中心来说,虚拟电厂表现出的是一个可控电源的电气特性;对内,它其实是一个微型的多能互补系统。所以虚拟电厂的优化调度,本质上是在给这个内部小系统做"功率分配+燃料分配+碳配额分配"的三维决策。
传统上,虚拟电厂调度研究的重心是经济调度和新能源消纳:风电多发的时段,燃气机组主动压低出力,储能充电,负荷侧做需求响应。但这里有个被忽略的账——燃气机组出力越低,系统碳排确实越少,但调峰压力全部压给了储能,储能容量是有限的,弃风弃光依然会存在。这时候P2G的价值就体现出来了。
1.2 阶梯碳交易:让二氧化碳排放有"梯度痛感"
碳交易机制在电力系统优化里已经很常见了。但早期模型大多用单一碳价——碳排放量超过配额的部分,统一按一个固定价格购买。这么做在数学上很简单,在现实里却有个明显问题:它不能反映减排边际成本递增的真实规律。
阶梯碳交易不一样,它把碳排放超配额量划分成多个区间,第一阶梯一个价,超得越多,单价越高。比如第一个区间内每吨碳价是20元,第二个区间是30元,第三个区间直接到50元。这样的价格结构给调度模型传递的信号很明确:让系统在一开始就主动避免滑入更高的碳价阶梯。
在Matlab里把这种阶梯结构写成模型,通常不需要搞复杂的非线性求解,因为我们用的是分段线性化的思路,本质上它就是一个带线性约束的分段函数问题。关键是你会不会用二进制变量去控制"阶梯激活",这在后面第3节我会专门展开。
1.3 P2G-CCS耦合:把二氧化碳从废物变成原料
单独讲P2G(Power to Gas),就是指电解水制氢,再把氢和二氧化碳反应生成甲烷。单独讲CCS(Carbon Capture and Storage),就是把二氧化碳从排放源里分离出来,压缩后封存到地下或进行利用。
这两者的耦合点非常直接:甲烷化反应需要二氧化碳作为原料,而CCS装置刚好能提供高浓度的二氧化碳流。于是系统内形成了这样一个闭环:燃气机组排放烟气→CCS捕获二氧化碳→一部分去封存,一部分送入P2G的甲烷化反应器→生成的合成天然气再送回燃气机组燃烧。听起来很优雅,但这里有一个容易误算的地方我放在第2节细说。
从调度角度看,P2G-CCS耦合相当于给系统装了一个"碳调节阀":新能源出力大、电价低的时候,让P2G多消耗电能制氢,同时CCS把碳捕住,既消纳了多余电力,又降低了净碳排放。等负荷高峰、电价高的时候,P2G停下来,合成天然气库存和储能一起顶上。这是一个典型的"时移"机制,把弹性从时间维度上拉了出来。
1.4 燃气掺氢:天然气机组减碳的低摩擦路径
燃气掺氢是指把P2G电解水产生的氢气按一定体积比例掺入天然气管道,混合后的燃料送去燃气轮机燃烧。氢气的燃烧产物只有水,单位热值碳排放为零,所以掺氢比例越高,燃气机组的单位发电碳排放强度就越低。
它不是未来的概念,很多现役燃气轮机已经做了掺氢燃烧的改造试验,只是掺氢比例受限于燃烧稳定性、回火倾向和NOx排放等因素。建模时通常不会让掺氢比过高,常见上限是体积掺氢比20%到30%。我把这个比例设为可配置参数,这样后续灵敏度分析时可以直接扫描掺氢上限,看它对碳排放和运行成本的影响。
简单来说,P2G负责生产绿色燃料,CCS负责提供碳源并降低净排放,燃气掺氢负责让燃气机组更"绿",阶梯碳交易负责用价格信号强制系统做上述这一切。三个机制在物理上是网络关系,在经济上是成本导向关系,这就是整套模型的骨架。
2. 能量流与碳流:先把系统的物理账算清楚
建模之前,我习惯先画一张"能流+碳流"的综合示意图。这一步值得花时间,因为后面所有数学约束都是从这张图里翻译过来的。这里我不画图,用文字描述结构,你在纸上照此画一遍会有很大帮助。
2.1 系统组成与拓扑关系
我的系统模型按这个拓扑搭建:
- 电源侧:风电场、光伏电站、燃气轮机(可掺氢)、电网交互(购电/售电)
- 储能侧:电储能(蓄电池)
- 负荷侧:固定电力负荷
- 碳处理侧:CCS装置,连接燃气轮机排烟
- 气体处理侧:P2G装置(电解槽+氢气缓冲罐+甲烷化反应器),氢气出口分两路——一路汇入天然气管道参与掺氢,另一路进入甲烷化反应器
燃气机组的燃料输入来自两处:外部购买天然气,以及P2G甲烷化生成的合成天然气。掺氢用的氢气不进入甲烷化反应器,而是直接注入燃气轮机燃料管线。这个细节要建模时区分清楚,因为流向不同,约束表达完全不同。
2.2 电能、天然气与氢气的三网耦合逻辑
电能流是最直观的:母线平衡方程里,风电、光伏、燃气机出力、储能放电、电网购电作为电源项,负荷、P2G耗电、CCS耗电、储能充电作为负荷项,左右加和相等。
天然气流需要稍微注意:别把"购气"和"P2G产甲烷"混在一个变量里,建议分开表示。购气量是一个决策变量,P2G产甲烷量是另一个决策变量,两者求和后等于燃气机组的天然气消耗量。
氢气流则是P2G电解水的产出,它面临一个分流决策:A份进甲烷化,B份直接掺氢,A+B等于总产氢量。这个分流比例也是优化变量,不是预先给定的——系统会根据各时段的碳价、气价和电价自动选择把氢"烧掉"还是"转化"。
2.3 一个容易被算错的碳平衡细节
这里是很多初版模型翻车的地方:CCS捕集到的二氧化碳,并不是全部都算"减排"。只有送去封存的那部分二氧化碳,才可以从系统碳排放里扣掉;送去甲烷化的那部分,虽然暂时被固定成了甲烷,但它最终会在燃气轮机燃烧室里重新变成二氧化碳排出去。
所以碳交易成本核算里的净排放写为:
净排放 = 燃气机组燃料燃烧排放量 - CCS封存捕集量送去甲烷化的CO2不参与抵扣,它只是碳的"循环再利用"。如果你把所有捕集量都从排放里减掉,会得到碳排放极低甚至为负的荒谬结果,而大气中的碳总量根本没变。这个逻辑一定要在约束和目标函数里落实清楚,否则后面所有碳交易成本的结论都是错的。
再补充一个CO2需求侧的约束:甲烷化反应需要多少CO2,与进入甲烷化反应器的氢气量是化学计量比关系,两者不能随便配。具体公式我在第4节给出。
3. 阶梯碳交易怎么写成优化模型
阶梯碳交易是整套模型里"最经济"的一组约束,也是把普通线性规划推向混合整数线性规划(MILP)的关键。这一节讲清楚配额定义、阶梯划分和Matlab/Yalmip里的写法。
3.1 配额、超排量与阶梯单价的定义
无量纲化之前,先把参数说清楚。假设系统持有免费碳配额 (M)(单位:吨或kg,建议统一为吨)。每个调度周期(比如一天)结束后,系统产生净碳排放 (E)(就是2.3节算出来的净排放)。
- 若 (E \le M),则系统不需要购买碳配额,多余配额可以按基础碳价出售,形成碳交易收益;
- 若 (E > M),超排量 (\Delta E = E - M) 要被划分到若干阶梯区间,每个区间对应不同的碳价。
阶梯区间可以这样设置(示例参数,实际根据算例调整):
| 阶梯 | 超排量区间(吨) | 碳价(元/吨) |
|---|---|---|
| 第1阶梯 | [0, 100] | 20 |
| 第2阶梯 | (100, 250] | 30 |
| 第3阶梯 | (250, 500] | 50 |
| 第4阶梯 | (500, ∞) | 80 |
可以看到,碳价不是一条水平线,而是一条右脚阶梯。这个结构意味着:系统如果超排250吨,碳交易成本是 (100 \times 20 + 150 \times 30),而不是直接 (250 \times 30)。数学上不能偷懒,不能对总超排量直接乘一个碳价。
3.2 分段线性化的MILP写法
把阶梯函数写成线性约束,常规做法是引入辅助变量和二进制变量。设超排量 (\Delta E) 划分为 (n) 段,第 (i) 段的实际量为 (s_i),第 (i) 段长度为 (L_i),单价为 (p_i)。需要满足:
ΔE = s1 + s2 + s3 + ... 0 ≤ s1 ≤ L1 0 ≤ s2 ≤ L2 0 ≤ s3 ≤ L3 ...但这些约束不足以保证"第二段没填满前,第三段不能有量"的顺序关系。必须引入阶梯激活二进制变量 (z_i)((z_i = 1) 表示第i段被激活):
s1 ≥ L1 × z2 s2 ≤ L2 × z2 s2 ≥ L2 × z3 s3 ≤ L3 × z3 ...这些约束在Yalmip里写起来很简单,就是普通的线性不等式。用Gurobi求解时,这些二进制变量和连续性变量构成的MILP规模并不算大,求解速度完全能接受。
3.3 为什么不要在Matlab里用if语句写碳成本
不少刚接触优化建模的朋友,习惯性地想在Matlab里写:
if DeltaE > 100 cost_carbon = 100*20 + (DeltaE-100)*30; end这在普通数值计算里没问题,但在优化模型里是错的。因为DeltaE是决策变量,它的值在求解迭代过程中不断变化,if的分支关系无法被求解器当作约束来处理。优化求解器要求目标函数和约束都是决策变量的显式表达式,不能有程序语言层面的跳转逻辑。
正确的思路是:把阶梯成本本身也建模成"分段线性函数",用辅助变量和二进制变量表达,让求解器在可行空间里自动选择落在哪个阶梯上。这也是为什么我一直强调,这类碳交易调度模型本质上要落到MILP框架里,而不是靠启发式算法去绕。
4. P2G-CCS耦合和燃气掺氢的建模核心
建模进入到核心环节。P2G不是一台设备,它是一条链;CCS也不是一个黑箱,它有自己的能耗;燃气掺氢更不是简单把氢加进去,它牵扯到热值换算和碳强度计算。这一节把每个环节的公式列出,并标注单位,避免后期量纲混乱。
4.1 电解槽、甲烷化与储氢:P2G的分段建模
电解槽把电能转化为氢能,输入电功率为 (P_{P2G,t})(单位MW),产氢功率按热值计为 (H_{H2,t}):
[ H_{H2,t} = \eta_{P2G} \times P_{P2G,t} ]
(\eta_{P2G}) 是电解槽的电-氢转换效率,工程上常见取值在0.6到0.75之间,取决于电解技术类型。这里我按0.7处理。
电解槽的约束还包括功率上下限和爬坡约束,这跟常规机组是一样的:
[ P_{P2G}^{min} \le P_{P2G,t} \le P_{P2G}^{max} ]
氢气产出后进入分流,设进入甲烷化的氢气量为 (H_{H2,meth,t}),直接掺氢的氢气量为 (H_{H2,blend,t}):
[ H_{H2,t} = H_{H2,meth,t} + H_{H2,blend,t} ]
甲烷化反应器的输入是氢气和二氧化碳,输出是合成天然气(SNG)。化学计量关系是1 mol CO2 与 4 mol H2 反应生成1 mol CH4 和 2 mol H2O。以热值功率表示时,甲烷化的产气热值功率 (F_{SNG,t}) 满足:
[ F_{SNG,t} = \eta_{meth} \times H_{H2,meth,t} \times \frac{LHV_{CH4}}{LHV_{H2}} \times \frac{1}{4} ]
这里的 (\eta_{meth}) 是甲烷化效率(考虑了反应不完全和热损失,取0.8左右)。需要同步消耗的CO2质量流 (C_{CO2,meth,t}) 与氢气量之间的关系是:
[ C_{CO2,meth,t} = \alpha_{CO2/H2} \times H_{H2,meth,t} ]
其中 (\alpha_{CO2/H2}) 由化学计量比和分子量换算得到:每4个单位的氢气热值对应约1个单位的氢气摩尔量,对应消耗1个单位摩尔量的CO2。具体系数我会在8.1节的量纲检查里再强调一次,因为这里是最容易换算错的地方。
4.2 CCS捕集、能耗与CO2供给约束
CCS装置的输入是燃气轮机的烟气,输出是高浓度CO2。设燃气轮机在t时段的燃料热值输入为 (F_{fuel,t}),烟气中CO2生成量与燃料含碳量成正比:
[ E_{GT,t} = EF_{gas} \times F_{fuel,t} ]
这里 (EF_{gas}) 是单位燃料热值对应的CO2排放因子(比如50 kg/GJ量级,按实际燃气组分标定)。
CCS捕集量为:
[ C_{cap,t} = \varepsilon_{CCS} \times E_{GT,t} ]
(\varepsilon_{CCS}) 是捕集率,可设为0.85到0.95之间的常数,也可设为决策变量,本文按常数处理。
CCS捕集不是免费的,它要耗电,捕集每吨CO2大约消耗电能 (e_{CCS})(常见的值是0.2到0.4 MWh/t):
[ P_{CCS,t} = e_{CCS} \times C_{cap,t} ]
这部分电耗必须加入母线平衡方程,很多初版模型漏了它,导致系统电平衡失真。
捕集到的CO2分成两股:一股去封存,一股去甲烷化:
[ C_{cap,t} = C_{seq,t} + C_{CO2,meth,t} ]
其中 (C_{seq,t}) 是封存量,只有它能在碳核算里抵扣排放。同时还要约束甲烷化的CO2需求不超过CCS供给能力,如果CCS供给不足,可以允许系统从外部购买CO2,但我的模型里为了简化,默认CCS容量足够,只限制在CCS捕集范围内分配。
4.3 掺氢比:以热值为纲的单位换算
燃气机组的燃料是天然气和氢气的混合物。掺氢比定义为氢气体积占混合气总体积的比例:
[ \alpha_{H2,t} = \frac{V_{H2,blend,t}}{V_{H2,blend,t} + V_{CH4,t}} ]
问题来了:燃气轮机的出力是按热值折算的,不同燃料的热值差异很大。天然气低位热值约35.8 MJ/Nm³(按纯甲烷算),氢气低位热值约10.8 MJ/Nm³,同样体积的氢气热值只有天然气的30%。所以,必须把混合气的总热值算出来,再结合燃气轮机效率转化为出力:
[ F_{fuel,t} = V_{CH4,t} \times LHV_{CH4} + V_{H2,blend,t} \times LHV_{H2} ]
[ P_{GT,t} = \eta_{GT} \times F_{fuel,t} ]
掺氢约束用体积比写:
[ \frac{V_{H2,blend,t}}{V_{H2,blend,t} + V_{CH4,t}} \le \alpha_{H2}^{max} ]
做单位换算时,注意MW(电)/MW(热)两种功率的单位统一。建议全部用MW作为功率单位,气体量用MWh热值作为统一度量,这样可以在公式里直接加减,但到掺氢比计算时再换算回体积,会出现热值和体积比例不一致的问题。更稳妥的做法是:所有氢气、天然气的"量"都用MWh(热值)表示,掺氢比先用体积比定义,再通过热值换算公式转成热值比约束。
我把这个转换结果提前给出:已知体积掺氢比上限 (\alpha_{H2}^{max}),对应的热值占比换算系数为
[ k_{blend} = \frac{LHV_{H2}}{LHV_{CH4}} ]
热值掺氢比的上限就是:
[ \beta_{H2}^{max} = \frac{k_{blend} \times \alpha_{H2}^{max}}{1 - \alpha_{H2}^{max} + k_{blend} \times \alpha_{H2}^{max}} ]
如果 (\alpha_{H2}^{max}=0.2),即体积掺氢20%,那么热值掺氢比例约7.0%。建模时我直接用体积比表达约束,但所有能量平衡用热值功率表达,注意两套体系不要混用。
4.4 耦合约束与碳循环的数学表达
把前面这些单体模型串起来,就得到系统的耦合约束:
- P2G产氢量与分流约束;
- 甲烷化反应器同步需要氢气和CO2,两者进入反应器时有比例关系;
- CCS捕集的CO2在封存与甲烷化之间的分配;
- 燃气机组掺氢燃料流与外部天然气的补充关系;
- 电力母线平衡中计入P2G和CCS的耗电项。
碳循环的表达,我习惯从"净排放"视角看:
- 从燃气轮机角度,燃烧排放了 (E_{GT,t});
- CCS捕集掉 (\varepsilon_{CCS} \times E_{GT,t}),其中 (C_{seq,t}) 被封存,这是负碳项;
- 甲烷化消耗的 (C_{CO2,meth,t}) 最终随燃料回到燃烧环节,整体不产生净减排。
于是净排放:
[ E_{net} = \sum_t \left( E_{GT,t} - C_{seq,t} \right) ]
配额 (M) 与 (E_{net}) 的差额进阶梯碳交易成本模块。
5. 优化调度的目标函数与约束体系
模型到这里,物理过程都齐了,接下来是目标函数和约束体系的拼装。目标函数要体现"经济性"和"低碳性"的统一,约束体系要覆盖从功率平衡到设备运行边界的全部逻辑。
5.1 目标函数:七项成本逐项说明
目标函数是最小化系统总运行成本,包含七个子项:
[ \min \sum_t \left[ C_{buy,t} + C_{gas,t} + C_{carbon,t} + C_{OM,t} + C_{start,t} + C_{curtail,t} - C_{sell,t} \right] ]
逐项拆解:
- (C_{buy,t}) 是购电成本,向电网买电的费用,等于购电功率乘以分时电价;
- (C_{gas,t}) 是购气成本,外部购买天然气的费用;
- (C_{carbon,t}) 是碳交易成本,就是第3节的阶梯函数算出来的值,注意当 (E_{net}) 小于配额时,此项为负,表示出售配额获得收益;
- (C_{OM,t}) 是运行维护成本,按各设备的出力乘以单位运维成本求和;
- (C_{start,t}) 是燃气机组启停成本,为避免频繁启停加的惩罚项;
- (C_{curtail,t}) 是弃风弃光惩罚项,新能源实际出力低于预测值时,按弃量乘以惩罚系数计入成本;
- (C_{sell,t}) 是售电收益,虚拟电厂向电网卖电的收入。
如果追求严谨,碳交易成本里的配额收益应该加个时间价值系数,但在日前调度模型里通常不做贴现处理,直接按调度周期结算。
5.2 约束体系:从功率平衡到机组爬坡
约束体系我分六个模块列:
电功率平衡约束:风电、光伏、燃气出力、储能放电、电网购电之和,等于负荷、P2G耗电、CCS耗电、储能充电、电网售电之和。这是一条等式约束,每个时段都要满足。
天然气平衡约束:购气量与P2G产甲烷量之和,等于燃气机组燃料消耗量(假设系统没有其他气负荷)。如果模型加入气负荷或储气罐,则扩展成带存储的平衡约束。
氢气平衡约束:电解槽产氢量等于直接掺氢量与进入甲烷化的氢气量之和。如果加储氢罐,则改成含SOC的动态方程。
燃气机组约束:出力上下限、爬坡速率约束、最小启停时间约束(可选)、掺氢体积比上限约束。
储能约束:SOC递推方程、充放电功率上下限、SOC上限下限、充放电不能同时进行的约束(可用二进制变量或互补约束)。
碳约束与CO2分配约束:净排放的计算式、碳交易阶梯的分段约束、CCS捕集量在封存和甲烷化之间的分配约束。
每一类约束在Yalmip里都是一两行代码的事,但要注意别漏项。我最常犯的错是把CCS耗电忘记写进电功率平衡,导致结果里P2G耗电增大、系统反而更不经济的假象。
5.3 为什么整体能写成MILP而不是MINLP
天然气的热值换算涉及氢气热值与天然气热值的比,如果掺氢比是连续决策变量,热值换算会产生非线性项。解决办法是:把掺氢比设定为几个离散档位,比如0%、10%、20%,用整数变量去选择档位。这样一来,每个档位对应的热值换算是线性关系,整体模型就是标准的MILP。
这样处理有三个好处:
- 求解器和理论都成熟,Gurobi或Cplex能快速找到全局最优解;
- 跟实际工程更贴合,现场切换掺氢比例本来就是分档操作的;
- 灵敏度分析更直观,扫描掺氢上限时只需要改参数。
很多人一看到P2G、CCS就下意识想用粒子群或遗传算法去求解,其实没必要。这个模型经过分段线性化之后是典型的MILP,用商业求解器求出来的解有全局最优性保证,远比启发式算法稳定可靠。
6. Matlab实现:从参数到求解器的完整链路
这一节给出可直接上手的实现路径。我用的是Yalmip建模,配Gurobi求解。如果你的环境里没有Gurobi,换成Cplex或开源的CBC也能跑,只是大规模算例下性能会有差异。
6.1 工具箱选择:Yalmip + Gurobi的理由
Matlab自带的linprog和intlinprog也能求解线性规划和MILP,但一旦模型上了规模和复杂度,变量和约束的数量会迅速膨胀,用Yalmip建模的优势就体现出来了:它可以用近乎数学表达式的语法写约束,不用手动构造矩阵。举个简单对比:
用Yalmip写电功率平衡:
Constraints = [Constraints, P_wind(t) + P_pv(t) + P_GT(t) + P_discharge(t) + P_buy(t) == ... P_load(t) + P_P2G(t) + P_CCS(t) + P_charge(t) + P_sell(t)];不需要去手动填充A矩阵和b向量,可读性大大提高。Gurobi的优势在于求解速度快、数值稳定性好,MILP问题的整数变量一旦多起来,Gurobi的cutting plane和启发式策略能明显缩短求解时间。
6.2 数据准备:风电、光伏、负荷与碳参数
输入数据用结构体组织,方便管理和修改:
data.T = 24; % 调度时段数,可扩展为96 data.wind = [ ... ]; % 风电场预测出力,1×T,单位MW data.pv = [ ... ]; % 光伏预测出力,1×T data.load = [ ... ]; % 负荷预测值,1×T data.Price_buy = [ ... ]; % 购电分时电价,元/MWh data.Price_sell = [ ... ]; % 售电分时电价,通常低于购电价 data.Price_gas = [ ... ]; % 天然气购气价,元/MWh(按热值计)碳交易参数单独放一组:
data.M_allowance = 300; % 免费碳配额,吨 data.carbon_levels = [100 150 250]; % 各阶梯长度,吨 data.carbon_prices = [20 30 50 80]; % 各阶梯碳价,元/吨可能需要做一次归一化:如果一天的总碳排放量级在几十吨到几百吨之间,而配额定在300吨,系统很可能不超排,碳交易机制不激活。调试时要先跑一遍基础场景,看看碳排放量落在哪个范围,再调整配额量,否则阶梯结构形同虚设。
6.3 变量定义与目标函数构建
决策变量的定义用Yalmip的sdpvar(连续变量)和binvar(二进制变量):
P_GT = sdpvar(1, T); % 燃气机组出力 P_P2G = sdpvar(1, T); % P2G耗电功率 P_charge = sdpvar(1, T); % 储能充电功率 P_discharge = sdpvar(1, T); % 储能放电功率 SOC = sdpvar(1, T+1); % 储能SOC H_H2 = sdpvar(1, T); % P2G产氢热值功率 F_CH4_p2g = sdpvar(1, T); % 甲烷化产气热值功率 C_cap = sdpvar(1, T); % CCS捕集量 C_seq = sdpvar(1, T); % CCS封存量 E_GT = sdpvar(1, T); % 燃气机组碳排放 s_carbon = sdpvar(1, N); % 碳交易各阶梯排放量 z_carbon = binvar(1, N); % 阶梯激活变量 u_GT = binvar(1, T); % 燃气机组启停状态目标函数用sum和线性表达式组合:
Objective = sum(Price_buy .* P_buy - Price_sell .* P_sell + Price_gas .* G_buy) ... + sum(OM_cost_GT .* P_GT + OM_cost_P2G .* P_P2G) ... + carbon_prices * s_carbon_sum ... + sum(curtail_penalty .* (P_wind_forecast - P_wind_used));6.4 阶梯碳成本与关键约束的代码写法
阶梯碳成本这一块我单独展示,它是这个模型里最需要理解的分段线性化写法:
E_net = sum(E_GT - C_seq); % 净碳排放 DeltaE = E_net - M_allowance; % 超排量 Constraints = [Constraints, DeltaE == sum(s_carbon_delta)]; % 其中 s_carbon_delta 是超排量在阶梯间的分配量用Yalmip表达阶梯激活的顺序关系时,不需要手写大M,因为Yalmip支持implies约束,但为了求解效率,我直接写线性不等式:
% 第一阶梯 Constraints = [Constraints, s_carbon(1) >= 0, s_carbon(1) <= LED(1)]; Constraints = [Constraints, s_carbon(1) >= LED(1) * z_carbon(2)]; % 第二阶梯及之后 for k = 2:N Constraints = [Constraints, s_carbon(k) >= 0, s_carbon(k) <= LED(k)]; if k < N Constraints = [Constraints, s_carbon(k) >= LED(k) * z_carbon(k+1)]; Constraints = [Constraints, s_carbon(k) <= LED(k) * z_carbon(k)]; end end这些约束的含义是:只有当第二阶梯激活时,第一阶梯才被填满到上限;依次类推。这样碳交易成本就通过碳价向量 * s_carbon线性表达在目标函数里了。
6.5 求解设置与结果可视化
求解设置很关键,MILP的求解时间表现对参数很敏感:
ops = sdpsettings('solver', 'gurobi', 'verbose', 2, ... 'gurobi.MIPGap', 0.001, 'gurobi.TimeLimit', 600); result = optimize(Constraints, Objective, ops);MIPGap设为0.1%是业界常用精度,TimeLimit设为600秒防止个别时段卡死。求解完成后用value()函数提取所有变量,画电功率平衡堆叠图、碳成本曲线、P2G产氢与掺氢量曲线等。
我建议至少画这样几张图:
- 各电源出力+购电量的堆叠面积图,一眼看出调度策略;
- P2G耗电与产氢量的双轴图,观察氢能生产与新能源出力的相关性;
- 燃气机组燃料构成图(购气量、SNG量、掺氢量),这是经济调度的核心结果;
- 碳交易成本的阶梯结构图,看模型是否合理触发了各阶梯。
7. 仿真方案设计与结果分析思路
一个优化模型能不能站住脚,很大程度上取决于你设计的对比方案是否合理。只跑一个完整场景自嗨是不行的,必须有控制变量。
7.1 三组对照场景:少一组都说不清贡献
我常用的对照方案是这样设计的:
| 场景 | 碳交易机制 | P2G-CCS | 燃气掺氢 | 目的 |
|---|---|---|---|---|
| S1 | 无 | 无 | 无 | 基准场景,仅做经济调度 |
| S2 | 阶梯碳交易 | 无 | 无 | 单看碳交易对调度的影响 |
| S3 | 阶梯碳交易 | 有 | 有 | 完整场景,看整体协同效果 |
别忘了再补一个场景S4:阶梯碳交易 + 只有P2G-CCS但没有掺氢,或者阶梯碳交易 + 只有掺氢但没有CCS。这样就能拆开P2G-CCS和掺氢各自对碳排放和成本的贡献。
对比指标我通常看这几个:总运行成本、净碳排放量、弃风弃光率、燃气机组发电量占比、P2G产氢总量、碳交易总成本。把结果列成表,比直接在正文里写"效果显著"有说服力得多。
7.2 重点看哪五条曲线
结果分析阶段,我习惯重点关注五条曲线:
- P2G产氢量曲线:与风电出力的相关性。正常情况下,风电大发时段(通常夜间)P2G产氢量应该冲高,这就是"电力时移"的体现。
- 掺氢量曲线:看氢气是优先送去直接掺氢还是甲烷化。如果氢价相对气价高,系统会更倾向把氢直接烧掉,否则更倾向甲烷化。
- CCS封存量曲线:封存量越大,净排放越低,但CCS耗电也越大。曲线形态应该与碳价走势相关。
- 碳交易成本曲线:注意看它是正的还是负的。如果配额设置合理,部分时段应该是负的(出售配额获取收益)。
- 燃气机组燃料构成曲线:购气量、SNG量、掺氢量三者比例的变化趋势。碳价高时,SNG和掺氢占比应该上升。
7.3 灵敏度分析:碳价和掺氢上限怎么影响结果
灵敏度分析是论文和实际项目汇报里加分的环节,也是判断模型稳定的必要步骤:
- 碳价倍数扫描:把所有阶梯碳价乘以0.5、1.0、1.5、2.0,看系统碳排放下降的边际变化。碳价低时,系统可能情愿交碳税也不减碳;碳价高到一定程度后,P2G-CCS会逐步全开,碳排放趋于饱和。这个"拐点"就是碳价的合理定价参考。
- 掺氢上限扫描:从5%扫到30%,看碳排放和运行成本的变化。掺氢比例提高后,燃气机组碳排放降了,但电解槽耗电增加,购电成本和P2G运维成本会上升。成本最低点不一定是掺氢最高点。
- 配额量扫描:配额从紧到松,看碳交易成本和P2G产氢量的变化趋势。配额越紧,模型越有动力启动P2G和CCS,这是机制设计的核心逻辑。
8. 实战踩坑记录与调试建议
最后这部分是我真正想写的。这套模型我前后调了两周,大部分时间都耗在量纲混乱和求解器卡死上,希望下面的经验能帮你直接绕开。
8.1 量纲错误:单位换算的三类高频坑
第一类坑是功率与能量的混用。调度模型常用单位是MWh(电量)和MW(功率),如果时段长度是1小时,二者数值相同,容易让人放松警惕。但一旦改成15分钟一个时段(96时段),功率数值不变,能量数值要乘0.25,忘乘的话电平衡会差出75%的偏差。
第二类坑是气体体积与热值的换算。天然气和氢气的计量单位可能是Nm³/h,但优化模型里通常用MWh(热值)。换算公式:
1 Nm³ 天然气 ≈ 35.8 MJ ≈ 0.00994 MWh 1 Nm³ 氢气 ≈ 10.8 MJ ≈ 0.003 MWh第三类坑是CO2质量与碳含量的换算。甲烷燃烧的碳排放因子按燃料热值算大约是55.8 kgCO2/GJ,但如果你从天然气体积去估算排放,要经过密度和分子量换算。两个渠道的结果必须互相验证,误差超过2%就该回头查单位了。
8.2 求解器表现:从数十秒到数分钟的调优之路
第一版模型在96时段、8台机组、带储能的规模下,Gurobi求解时间经常超过10分钟,有时候还撞上内存爆炸。排查下来主要问题出在冗余二进制变量太多——我为每台机组的每个时段都定义了启停变量和多个阶梯激活变量,整数变量数量直接翻倍。
调优思路有三个:
- 把不常用的启动状态变量用线性松弛替代,或者用聚合机组的方式减少机组数量;
- 给Gurobi设置合适的
MIPFocus参数,如果优先找可行解,设MIPFocus=1;如果优先证明最优性,设MIPFocus=2; - 先跑24时段模型,所有逻辑跑通了再扩展96时段,别一开始就上大规模算例。
8.3 结果合理性自检清单
每次求解完成后,我会按这个清单快速检查一轮:
- 所有时段的电功率平衡等式残差是否在1e-6量级;
- 储能SOC是否始终在上下限内,且首末SOC是否满足设定值;
- 掺氢量是否超过P2G总产氢量;
- 甲烷化的CO2需求是否超过CCS捕集量;
- 净碳排放是否为正,且和燃气机组燃料消耗量在同一个量级;
- 碳交易成本的正负号是否符合配额设定。
如果上面任何一项不满足,优先怀疑约束漏写,而不是求解器问题。特别是储能SOC的递推约束,很多初版模型会把SOC初始值从1开始,而后面的递推没接上,导致SOC曲线乱飞。
根据我个人经验,做这类虚拟电厂优化调度模型,最大的瓶颈往往不是数学功底,而是对物理过程的颗粒度把握——哪些环节要精细建模,哪些环节可以合理简化,直接决定了模型能否顺利求解以及结果是否可信。比如氢气缓冲罐的容量约束我选择做简化处理,而CCS耗电则严格计入,这种取舍要基于你对系统实际运行的判断,而不是盲目堆设备细节。这套模型跑通之后,后续还可以往多场景随机优化、碳市场与电力市场的联合出清方向扩展,但先把这套确定性MILP框架吃透,后面每一步都会顺畅得多。