1. 项目概述
做电力系统方向研究的朋友,对“虚拟电厂”这个词应该都不陌生了。这几年双碳目标提得越来越紧,电网里新能源渗透率一路走高,风电光伏的随机性和间歇性让调度员头疼不已——中午光伏大发的时候电价能打到地板价,晚高峰一上来电价又飙得离谱。单纯靠火电调峰,既不够快也不够经济,更不环保。所以业内开始把分散的分布式电源、可控负荷、储能装置聚合起来,组成一个对外统一响应的整体,这就是虚拟电厂(Virtual Power Plant, VPP)。
而我今天要分享的这套Matlab代码,做的不是普通的虚拟电厂聚合调度,它加了两个很有分量的东西:碳捕集和电转气(Power to Gas, P2G),同时还叠加了垃圾焚烧发电机组。说白了,这是一个“算经济账”和“算环保账”同时进行的优化调度模型。代码用Yalmip工具箱建模,调用商业求解器求解混合整数线性规划(MILP)问题,最后输出各个机组的最优出力曲线、碳捕集系统运行状态、P2G设备的启停和产气情况,以及在碳交易机制下的总运行成本。
这类模型在最新的《电网技术》《电力系统自动化》等期刊上非常热门,但我相信大部分刚接触这个方向的人,面对摘要里那些“阶梯式碳交易”“碳捕集电碳耦合”“P2G两阶段运行”的术语,脑子是懵的。这篇文章会把模型的每一个环节拆开讲清楚:从物理原理到数学建模,再到Matlab代码的具体实现方式,以及我在跑通这套模型时踩过的坑。无论你是正在写毕业论文的研究生,还是做综合能源系统规划的在职工程师,这篇内容都可以当作一份可直接参考的复现笔记。
2. 整体架构与关键技术点拆解
2.1 虚拟电厂内部是怎么组织起来的
先把这个虚拟电厂的“家底”盘清楚。模型内部主要包含五类元件:垃圾焚烧发电机组、碳捕集系统、电转气系统(含电解槽、氢气储罐、甲烷化反应器)、风力发电、光伏发电,外加一个电储能装置。
垃圾焚烧机组是整个VPP的基础电源,它不像风电光伏那样看天吃饭,出力稳定可控,可以作为系统里的“压舱石”。但垃圾焚烧发电有一个绕不开的问题——它要排二氧化碳。于是模型把碳捕集系统(Carbon Capture System, CCS)接在了机组尾气端,通过化学吸收法把CO₂从烟气里分离出来。捕集下来的CO₂并不会直接封存,而是送入电转气系统,作为合成甲烷的原料。这样一来,整个VPP就形成了一个“垃圾发电—捕碳—用碳制气—燃气回流”的闭环链条。
电转气系统在这里承担的是双重角色。一方面,它是个大功率电负荷,电解槽可以把风电光伏大发时段的富余电力转化成氢气;另一方面,甲烷化反应器又把氢气和捕集来的CO₂合成为甲烷,甲烷既可以直接卖给天然气网络,也可以存进储气罐留待燃气轮机顶峰时使用。也就是说,P2G既是需求侧响应的“弹性负荷”,又是能量时移的“储存器”,同时还是碳循环利用的“化学工厂”。这三重属性叠加起来,才是标题里“电转气协同”的真正含义。
2.2 碳捕集与P2G协同的物理逻辑
为什么一定要把碳捕集和电转气放在一起说?单独建一个碳捕集模型不也挺常见的吗?问题在于,传统碳捕集模型把捕集下来的CO₂当作纯成本,捕得越多、能耗越高、越亏钱,所以模型在优化时会尽量压低捕集率。而一旦引入了P2G,情况就变了——CO₂变成了生产原料,有了产品价值,碳捕集的经济性就改善了;反过来,P2G的甲烷化反应需要CO₂,如果只靠外部购买,成本高昂还不稳定,碳捕集恰好解决了这个供应问题。所以说,这两个系统是共生关系,协同建模才能让整体运行成本降到最低。
从时间尺度上看,这种协同还有更好的调度意义。风电在夜间大发时电价便宜,P2G电解槽开足马力制氢,氢气和CO₂合成为甲烷存起来;白天用电高峰、电价高企时,储气罐里的甲烷可以反哺燃气机组发电,替代一部分昂贵的外购电。这套“低谷储气、高峰放气”的运行策略,本质上就是在时间轴上搬移能量,跟抽水蓄能、电化学储能的逻辑有相似之处,但能量密度和存储周期上各有长短。
2.3 为什么选择Matlab+Yalmip方案
说句实在话,这种中规模MILP模型,Python里的Pyomo、Gurobi也能做,Julia里的JuMP也很擅长。但Matlab在今天依然是国内电力系统课题组最普及的环境,原因很现实:一是很多老代码、教学资料都是Matlab写的,接手容易;二是Yalmip这个工具箱封装得极为友好,建模和调试的效率非常高,写约束就像写数学公式,出错了报错信息也比较直观。再加上Matlab的绘图能力,跑完优化直接画各机组出力曲线、碳捕集量曲线、储气量变化曲线,一整套流程下来非常顺手。
3. 系统建模:每一块硬骨头的啃法
3.1 垃圾焚烧发电机组建模
垃圾焚烧机组的建模有几个层次要处理。最基础的出力约束跟普通火电机组类似,有出力上下限约束:
P_min ≤ P_MT(t) ≤ P_max
以及爬坡约束:
-Δ_down ≤ P_MT(t) - P_MT(t-1) ≤ Δ_up
但它和火电不同的是,焚烧炉的燃料是城市生活垃圾,处理垃圾本身是有补贴收入的,这部分收益在目标函数里要体现为负成本。同时,焚烧过程会产生CO₂,排放量跟发电量近似线性相关:
E_MT(t) = e_MT · P_MT(t)
其中e_MT是单位发电量的碳排放强度。这里有个实操中容易忽略的细节:垃圾焚烧发电的碳排放系数和煤电不一样,因为垃圾中生物质占比高,生物质部分的碳排放按现行核算规则算“碳中和”,所以真正计入碳配额约束的排放量要乘一个折算系数。模型里常见的处理方式是把总排放乘以一个小于1的系数,或者直接设置一个“净排放率”,具体取值要看采用的是哪一版核算指南。
3.2 碳捕集系统建模
碳捕集系统接在烟气管道上,运行时要消耗电力和热力,其中电力是最主要的部分。常用的简化模型把捕集能耗表示为单位捕集量的线性函数:
E_CCS(t) = λ · C_cap(t)
λ是捕集单位CO₂所需的电耗,典型值在0.15~0.35 MWh/tCO₂之间。捕集量受两个因素限制:一是烟气中CO₂的总量,二是捕集设备的处理能力上限,所以约束是:
0 ≤ C_cap(t) ≤ min(E_MT(t) · e_MT · η_max, C_cap_max)
η_max是最大捕集率,工程上化学吸收法能做到90%~95%。
这里要特别强调一个我在代码里发现的设计细节:碳捕集装置是可以“部分投入”的。也就是说,t时刻可以只开50%的吸收塔处理能力,捕集率并不是非0即1。这个操作灵活性其实是整个模型能在经济性和环保性之间找到平衡点的关键。如果不允许部分投入,模型就只能要么全捕、要么不捕,调度自由度和结果的经济性都会大打折扣。
3.3 电转气两阶段建模
P2G系统分两个阶段:电解水制氢和甲烷化。
电解槽(第一阶段)的输入是电和水,输出是氢气和氧气。效率一般在60%~80%之间,模型里用效率系数η_EL把电功率转化为氢气的热值输出:
H_h2(t) = η_EL · P_EL(t)
电解槽的出力有上下限,还有爬坡限制。注意电解槽的爬坡比燃气轮机慢,启停也有限制,不能频繁启停,这块需要加最小开停机时间约束,否则求解器给出的结果在工程上根本执行不了。
甲烷化反应器(第二阶段)的本质是萨巴蒂尔反应:
CO₂ + 4H₂ → CH₄ + 2H₂O
从反应式可以算出,合成1单位甲烷需要4单位氢气,同时消耗约2.75单位的CO₂。模型里通常不直接写分子量换算,而是用“氢气消耗系数”和“CO₂消耗系数”来表达:
H_meth(t) = η_meth · H_h2_to_meth(t)
C_consum(t) = β_CO2 · H_meth(t)
这里的β_CO2就是单位甲烷产量对应的CO₂消耗量。有一个建模选择要注意:在系统里P2G的CO₂来源有两个,一个是碳捕集的输出,一个是外部购买。模型里默认优先使用碳捕集捕集的CO₂,不足部分才外购,因为外购CO₂价格更高,优化程序会自动做出这个抉择。
3.4 储能与外部交互建模
电储能按照常规的SOC模型处理:
SOC(t+1) = SOC(t) + P_ch(t)·η_ch - P_dis(t)/η_dis
荷电状态有上下限约束,充放电功率有上下限,而且同一时刻不能既充又放——这个约束需要在Yalmip里用二进制变量或者大M法来写,否则会做出同时充放电的荒唐结果。
虚拟电厂和外部电网之间有一个购售电台区。购电时按市场电价付费,售电时按上网电价结算,两个价格往往不一样,模型中要分别用变量表示,配合二进制变量约束购售状态。
4. 优化模型整体框架
4.1 目标函数设计
这套模型的目标是让24小时内的总运行成本最小,成本项包括:
min C_total = C_fuel + C_grid + C_om + C_co2 + C_waste_penalty - R_sell - R_subsidy
拆开看每一项的含义:
- C_fuel:垃圾焚烧机组的燃料成本,跟处理垃圾量相关。实际中垃圾处理有补贴,所以这一项可能体现为负值。
- C_grid:向外部电网购电的费用。
- C_om:各设备的运行维护成本,按出力线性折算。
- C_co2:碳交易成本。采用阶梯式碳价机制——排放量在配额内免费,超出部分按阶梯价格购买;配额盈余的部分可以出售获利。
- C_waste_penalty:弃风弃光惩罚。如果模型允许弃风弃光,那这些被丢弃的电能要计惩罚费用,惩罚系数设置得比售电价高一个量级,引导优化器尽量消纳新能源。
- R_sell:向电网售电的收入,以及P2G产出的甲烷出售给天然气网络或用于燃气机组发电的收益。
- R_subsidy:垃圾处理补贴收入。
这个目标函数设计有讲究:如果只设碳排放成本而不设售气收益,P2G设备基本不会启动,因为电解制氢在大多数电价场景下不划算,模型会把P2G出力降到零。只有当储气收益、售气收益和碳减排收益叠加起来,才能体现出P2G的调度价值。
4.2 约束条件体系
模型的约束体系是一个多层次的树状结构。最顶层是母线功率平衡:所有电源出力加总,减去所有负荷消耗,等于与电网的交换功率。第二层是各设备自身的运行约束,比如出力上下限、爬坡速率、储气罐容量上下限。第三层是耦合约束,最核心的一条是CO₂物料的平衡:
C_cap(t) >= C_P2G_consum(t) + C_storage(t)
意思是碳捕集系统捕集到的CO₂,一部分直接送去P2G做原料,另一部分进入储碳罐暂存。这条约束写得对不对,直接决定模型有没有可行解。我在第一次跑模型时,就是因为忘了给储碳罐建模型,导致P2G在全天大部分时段缺CO₂原料,系统的P2G几乎不出力,结果目标函数值高得离谱。
第四层是碳配额约束,反映整个VPP在碳交易市场中的角色。VPP的配额等于机组实际排放量减去碳捕集量,再和分配到的初始配额作比较,差额部分进入碳交易市场:
E_net(t) = E_MT(t) - C_cap(t) - E_quota(t)
如果E_net为正,说明配额不够用,要去碳市场买配额;如果为负,说明碳配额富余,可以出售获利。这也是碳捕集系统改善经济效益的另一个渠道——捕碳不仅能产出CO₂原料,还能直接减少碳市场上的购买需求。很多做碳排放优化的人只盯着碳捕集的能耗成本,忽略了这个收益项,整体优化结果就差了一大截。
4.3 低碳调度协同的数学表达
把整套协同机制用数学语言整理一下:碳捕集的产出C_cap(t)连接了排放侧E_MT(t)和P2G的原料侧C_P2G_consum(t);P2G的耗电P_EL(t)连接了电力平衡和储气系统;储气系统的输出又回馈给燃气机组或外售。这三个环节环环相扣,中间的约束必须写全。我在代码里把这三个环节单独封装成三个函数模块(carbon_capture_model.m、p2g_model.m、gas_storage_model.m),每个模块内部对应一组约束,方便调试和替换参数。
5. Matlab代码实现的关键细节
5.1 Yalmip建模框架搭建
整个代码的骨架遵循一个标准流程:参数初始化、变量定义、约束组装、目标函数定义、求解器配置、结果输出。用Yalmip定义变量时,有几个常见坑值得提前说一下。
二进制变量一定要显式声明为binvar类型。比如燃气机组的启停变量、储气的充放状态变量、电网购售状态变量,这些如果定义成sdpvar,Yalmip不会自动识别整数性质,最后求解出来可能是0.7这种不伦不类的值,完全没法用。
binvar是Yalmip的类型标识符,表明变量是二进制变量(只能取0或1),必须显式声明,否则无法正确求解MILP。
定义约束时要注意Yalmip对矩阵约束的拼接方式,最常见的是用方括号拼接:
Constraints = [Constraints, P_MT_min <= P_MT <= P_MT_max]; Constraints = [Constraints, SOC_ES(t) == SOC_ES(t-1) + ...];循环写约束时,逐条append的效率极低,建议把每个时段的约束一次性写成向量形式。Yalmip对向量化约束的预处理速度比for循环快很多,尤其是24时段×大量约束的场景,这个优化能让求解时间从几分钟降到几十秒。
5.2 混合整数线性规划的求解配置
这类模型本质上是MILP问题,我用的是Gurobi求解器。安装Gurobi之后,在Matlab里设置求解器非常简单:
ops = sdpsettings('solver', 'gurobi', 'verbose', 2, 'gurobi.TimeLimit', 300); ops.gurobi.MIPGap = 0.001;MIPGap这个参数很重要。Gurobi在求解MILP时用分支定界法,找到一个可行解后,会持续搜索更优解,MIPGap表示当前最优解和理论上界的相对差距,设置为0.001相当于让求解器跑接近最优。如果求解太慢,放宽到0.01能大幅缩短时间。
实操中,我遇到过Gurobi license过期导致求解器启动失败的情况。这里提个醒,运行前先检查一下license有效期。另一个常见问题是模型引入了太多二进制变量,导致求解时间指数级上涨。减少二进制变量的方法之一是限制P2G的启停次数,用“最大启停次数约束”替代逐时段启停变量,可以让模型规模明显缩小。
5.3 结果可视化与分析
跑完优化只是第一步,调度员和导师关心的是结果怎么解读。代码里我写了三组核心可视化函数:
第一组是电力平衡图,把24小时的各电源出力、负荷、购售电画成堆叠面积图,能一眼看出每个时段的电力来源和去向。第二组是碳流平衡图,把垃圾焚烧排放、碳捕集量、P2G消耗量、碳交易量画成柱状对比图。第三组是储气系统SOC曲线,看甲烷存储的日内变化,直观验证“低谷储气、高峰放气”的策略是否被优化器采纳。
画图用的是Matlab自带的plot和area函数,配合自定义的颜色配色。有一个绘图细节:堆叠面积图时,如果多个序列的数值量级差异大(比如电出力是兆瓦级,碳量是吨级),要分两个y轴坐标,否则小量级序列会被压平到坐标轴底端,图形完全看不出来。
6. 常见问题与调试经验
6.1 模型无解,问题出在哪
这是跑优化模型最常遇到的困境:约束写好、目标函数填好,求解器一跑直接报“infeasible problem”。我的排查经验是有固定套路的。首先,检查功率平衡约束是不是被其他约束“卡死”了——最常见的原因是储能SOC的初值设置和末值约束冲突。比如设SOC(1)=0.3,又要求SOC(24)=0.6,而整个系统根本没有足够的充电功率在最后一个时段把SOC抬上去,这样必然无解。
排查方法是把SOC相关的约束暂时屏蔽,看看剩余约束是否可行。如果仍无解,再逐步屏蔽其他约束组,二分定位出问题的那组约束,这个方法效率很高。
6.2 求解时间过长怎么办
24时段的模型,变量数量通常在1000上下,其中整数变量占100左右,Gurobi求解时间在正常情况下一两分钟能搞定。如果跑几分钟还不出结果,优先检查三个方面:一是是否在读写大矩阵时用了大量临时变量,Matlab的for循环里反复append矩阵会产生大量内存复制,这是最常见的性能杀手,解决办法是预先分配内存或者向量化;二是去看看MIPGap设置是否太严,0.0001这种精度对工程问题毫无必要,0.01已经能保证误差在1%以内;三是把Yalmip的verbose开成2,观察Gurobi输出的上下界gap变化,如果gap在200秒内降不下去,说明模型可能存在某种对称性问题,可以考虑增加破坏对称性的约束。
6.3 碳捕集率总是偏低
很多人在结果里看到碳捕集率只有40%,觉得模型“不够低碳”。这个现象其实很正常:碳捕集系统要耗电,捕集1吨CO₂要消耗0.2MWh左右的电能,如果这些电是从电网高价买的,捕碳成本就超过了碳交易市场里的碳价,优化器自然选择少捕。调节的方式有两种,一是把碳交易市场的碳价调高,模拟更严格的碳排放政策;二是修改单位捕集能耗λ,比如从0.35改到0.2。通过参数敏感性分析,你能清晰看到碳捕集系统投运率随碳价、能耗变化的趋势,这种分析本身也是论文里的重要内容。
6.4 P2G设备几乎不运行
P2G出力为零是另一个高频结果。原因主要出在目标函数里P2G的收益设置上。如果甲烷出售价格设置过低,或者储气罐容量设置过小,P2G产出无法转化为实际收益,优化器就会选择不运行。要解决这个问题,需要把P2G的经济性来源拆细:电解槽(耗电)制氢的收入来源包括甲烷销售、参与调峰辅助服务市场获取的补偿、以及通过氢气替代燃气减少的购气费用。在三类收益都纳入模型后,P2G才会真正“活”起来。还有一个容易被忽略的点:P2G在低谷电价时段购电制气,看起来是在“亏钱”制气——但如果把这个行为当作储能在用,低谷储、高峰放,整体套利的逻辑就通了,所以储气罐的容量和初始SOC设置一定要合理,否则P2G没有蓄能空间,经济性就无从谈起。
7. 一个完整的典型案例分析
7.1 基础工况与数据准备
我用一套公开参考数据构建了一个典型算例。虚拟电厂包含一台30MW垃圾焚烧机组,装机容量30MW,单位碳排放强度0.8t/MWh;风电场装机40MW,光伏电站装机20MW;碳捕集系统最大捕集率85%;P2G系统的电解槽容量10MW,甲烷化容量按氢气输入折算约8MW,储气罐容量设为5MWh;电储能容量10MW/20MWh。
分时电价采用典型的峰谷三段式:峰段(08:00-11:00, 18:00-22:00)电价1.1元/kWh,平段(06:00-08:00, 11:00-18:00)电价0.65元/kWh,谷段(23:00-06:00)电价0.3元/kWh。碳配额按机组排放量的70%发放,碳交易价格为52元/吨。
7.2 优化结果解读
跑完24时段的优化,看结果可以看到很清晰的调度策略:
- 风电大发时段(凌晨2:00-5:00),P2G电解槽处于满发状态,功率约为8.5MW,把富余风电转化为氢气存储起来。由于谷电价格极低,外购电也参与了制氢——这个场景下,电解槽相当于一个用低谷电“搬运”能量的装置。
- 碳捕集系统在电价高峰时段的捕集率明显下调,从峰段之前的85%降到60%左右,把电能让给更重要的负荷;夜间电价低谷时捕集率重新拉高到85%,捕下来的CO₂一部分直接进甲烷化反应器,一部分存进储碳罐留给白天用。
- 垃圾焚烧机组全天维持在高出力水平,原因是它的边际成本很低,而且还带来垃圾处理补贴收入,优化器没有理由降低它的出力。
- 储气罐的SOC曲线呈现出清晰的“夜升昼降”模式:夜间P2G满产,储气量逐步累积,白天高峰时段储气罐释放甲烷供燃气轮机补峰,SOC从早上的峰值一路下降,到傍晚降至最低点。
这个结果证明了协同调度的价值:如果没有碳捕集和P2G之间的CO₂物料循环,碳捕集系统的整体经济性会明显变差;如果没有P2G承担“蓄水池”角色,夜间富余风电只能弃掉,弃风率会接近15%;而把两个系统协同起来之后,弃风率降到了4%以下,碳捕集系统的综合运行成本也大幅下降。
8. 模型扩展方向与后续工作建议
这套模型虽然已经覆盖了VPP运行的主要环节,但站在做研究的角度,依然有不少可以深挖的方向。
第一个方向是多虚拟电厂协同。单个VPP的调节能力有限,如果多个VPP之间通过共享储能或共享P2G设备互联,可以在更大范围内实现电力电量平衡。对应的数学模型要增加VPP间的功率交互变量和约束矩阵,复杂度上升明显,但对解决新能源消纳问题的意义更大。
第二个方向是考虑不确定性。目前模型里风电、光伏出力都是确定的预测曲线,实际中预测误差很大。引入场景随机规划(Stochastic Programming)或鲁棒优化(Robust Optimization)后,调度结果会具备抗风险能力,更贴近工程实际。Yalmip对随机规划的支持很成熟——直接复制多组风、光出力场景,在约束上叠加场景下标即可,代码扩展的工作量并不大,主要难点在于场景数增多后问题规模变大,MILP求解时间会成倍增长。
第三个方向是引入需求响应。把可平移负荷、可中断负荷作为可控资源放进VPP模型,让负荷侧也参与调度平衡。很多文章里叫“源荷不确定性下虚拟电厂优化调度”,就是在源、荷两侧同时做文章,配套的代码参数需要增加可调节负荷的容量比例、补偿价格等数据。
从个人实操体会来说,这套模型的代码架构足够清晰,完全可以在上面做二次开发。关键是要吃透每个约束的物理含义,而不是单纯把别人的代码跑出几个图就完事。要把每个参数逐个调一遍,看看目标函数和出力曲线怎么变,才能真正理解模型内部的运行机制,后续无论是写论文还是做工程应用,都能踩在扎实的基础上。