碳捕集与电转气协同的虚拟电厂优化调度:MATLAB建模实现
刚开始拿到这个题目的时候我脑子里跳出来的第一反应是碳捕集、垃圾焚烧、电转气、虚拟电厂这几个东西单拎出来都不新鲜但把它们塞进同一个优化调度模型里就有点意思了。这相当于在一个虚拟电厂里同时解决两个问题一个是让碳捕集设备不再是单纯的“耗电包袱”另一个是让垃圾焚烧这种带碳排放的电源在低碳约束下还能保住经济性。折腾完这套MATLAB调度模型之后我可以负责任地说这个组合不是炫技而是目前园区级综合能源里最接近闭环的一个玩法。这篇内容适合谁看你如果已经在做虚拟电厂、综合能源系统优化、碳捕集利用封存一体化建模或者你正在写相关方向的开题、结题报告需要一个能跑通、能讲清原理的参考框架那这篇分享应该能帮你省掉不少弯路。我会从建模思路、设备建模、MATLAB代码实现到调试坑位完整过一遍。1. 从问题到方案先想清楚再动代码做优化调度最忌讳的是一上来就抄模型。我见过不少同学拿到一个“含碳捕集虚拟电厂”的题目直接把碳捕集按固定负荷塞进功率平衡然后跑完结果发现系统净碳减排为零因为碳捕集那部分电费根本没人买单优化器直接把碳捕集关了。所以第一步不是编程而是把手里的资源梳理清楚想明白它们之间到底怎么产生协同。1.1 为什么要让碳捕集和电转气站在一起传统碳捕集的困境比较直白捕获一吨二氧化碳要消耗不少电和热运行成本摆在那如果捕下来的碳没有去处那它就是纯成本。你优化调度的时候目标函数一定会让碳捕集出力尽量低甚至停机。但问题是很多场景下碳捕集设备需要维持一定的最低运行工况频繁启停也不现实。电转气Power to GasPtG的价值在于给碳捕集提供了一个“碳消纳出口”。它把电能转化成氢再和二氧化碳甲烷化生成天然气。这个过程中二氧化碳变成了产品原料而不是留着待处理的废物。更关键的是PtG消耗电力的时机和碳捕集捕碳的时机可以错开也可以同步当电价低的时候PtG吃便宜电干活同时让碳捕集提高捕集比例给它供碳。这样一来碳捕集从“固定成本”变成了“可调节负荷”虚拟电厂多了一个可调资源。所以这个系统的核心就一句话用电转气去牵制碳捕集的运行状态把碳流和电流绑定起来。你优化的时候真正在权衡的是“多捕一吨碳需要花多少电和热成本”和“多产一立方天然气能带来多少收益或减少多少碳排放成本”之间的账。1.2 系统里都有哪些单元能量和碳怎么流动我这边搭的虚拟电厂本质上是一个聚合了多种分布式资源的园区级主体统一对外参与电网调度。里面主要有这么几块带碳捕集装置的燃气发电机组、生活垃圾焚烧发电单元、电转气站以及一个外部电网联络关口。为了体现虚拟电厂的灵活性模型中还预留了新能源出力曲线和可平移负荷的接口但主案例没把储能加进去原因后面说。能量流的逻辑大概是这样的燃气机组和垃圾焚烧机组发电功率送入母线一部分供给园区负荷一部分送去碳捕集装置和电转气装置剩余功率经由联络线卖给电网或者从电网买电补充。PtG这边从母线取电从碳捕集装置取二氧化碳产出的天然气可以外售或者供给园区气负荷。碳流的逻辑是两个电源产生含碳烟气碳捕集装置按一定比例捕集捕集下来的碳一部分直接送去PtG甲烷化其余部分可以外送储存或用于其他用途。净排放等于烟气排放总量减去捕集量和PtG固定下来的量。我把这层关系整理成一张表方便建模时对照单元输入输出关键耦合关系燃气机组天然气电、烟气烟气CO2进入碳捕集垃圾焚烧机组生活垃圾电、烟气烟气CO2可参与捕集碳捕集装置电、热、烟气CO2、净烟气能耗与捕集量线性相关PtG站电、水、CO2天然气耗电量与CO2消耗量线性相关电网关口购电/售电/需要满足联络线功率约束这里有个容易忽略的点垃圾焚烧机组虽然是处理废弃物的但它同样有碳排放在模型里应该按实际烟气排放因子来算。很多文献会把它当“零碳”电源处理实际工程中处理高含水率的垃圾时烟气CO2浓度并不低。我这个模型里统一按排放因子法处理只是在碳捕集的接入方式上做了区分。1.3 目标函数和约束条件的基本盘优化调度的目标函数定位为“系统日运行成本最小化”这项成本包含购电成本、燃气成本、垃圾处理补贴收入的反向项、碳捕集运行成本、PtG运行成本还有碳交易成本或者碳收益。为什么不把售电收入写得特别复杂因为虚拟电厂作为价格接受者日前市场的分时电价是给定的售电收入其实就是负成本放进目标函数天然会优化。约束方面最基本的包括母线功率平衡、联络线功率上下限、机组出力上下限与爬坡约束、碳捕集装置的捕集比例范围、PtG设备的输入功率范围以及碳流耦合约束。如果加入储能还要考虑SOC状态转移方程。我的主案例不加储能是因为加了储能之后最优解可能会把大量灵活性都压在储能上碳捕集和PtG之间的耦合关系反而不容易看出效果。先跑通基础版再加储能做对比分析这样能更清楚地看到每一项新增资源的边际贡献。2. 核心设备怎么建模碳捕集、垃圾焚烧、电转气逐个拆解这部分是模型能不能跑出合理结果的关键。我之前见过有人把碳捕集建成一个“单位发电量抽掉固定比例电耗”的模型颗粒度太粗导致捕集率和发电负荷强耦合丢失了碳捕集本身的调节能力。这里我按工程上比较容易落地的方式建模。2.1 碳捕集装置不是一个固定负荷而是一个可调工作点碳捕集系统的运行通常有一个工作范围比如捕集率可以在30%到90%之间调节。捕集率的调节意味着再生能耗跟着变化所以模型里要引入捕集率变量而不是固定值。烟气中的CO2总量跟着上游机组出力走捕集量等于烟气CO2总量乘以捕集率。再生能耗可以建模成捕集量的线性函数也可以加一个固定能耗项用来体现设备在最低运行工况下的“待机成本”。我实际用的线性模型是P_ccs_t a_ccs * C_cap_t b_ccs其中P_ccs_t是碳捕集装置消耗的电功率C_cap_t是捕集的CO2量a_ccs是单位捕集能耗系数b_ccs是固定运行电耗。这个模型的好处是简单、线性可以直接进混合整数线性规划框架而且和实际控制逻辑吻合你让捕集率提高再生塔的能耗就会增加但不会超过某个上限。捕集率本身是一个连续变量但要注意它的运行范围不能设成0到100%因为再生塔不能随便关停。这里我设的是30%到90%具体数值取决于采用的技术路线。这样虚拟电厂在进行经济性判断时可以选择“少捕一点”或“多捕一点”而不是非开即关。2.2 垃圾焚烧机组出力模型和烟气耦合垃圾焚烧机组在调度模型里和常规火电类似需要考虑出力上下限和最小连续运行时间但由于焚烧炉的蓄热特性实际出力调节速率通常比燃气机组慢。我这里把垃圾焚烧出力设成可调范围浓缩在60%到100%额定出力之间并且给它一个相对缓慢的爬坡约束这样更贴近实际。垃圾焚烧机组产生的CO2排放量我按排放因子法折算把处理垃圾量、吨垃圾发电量和排放因子对应起来。简化处理时E_wte_t e_wte * P_wte_t单位是吨/小时。这部分烟气CO2同样可以接入捕集装置。需要注意的是垃圾焚烧机组的碳排放折算因子和燃气机组完全不同不能用同一个参数。对很多园区项目来说垃圾焚烧发电有处理补贴这部分收入是实打实的经济支撑。在目标函数里我是把补贴处理成“负成本”也就是每处理一吨垃圾给一个固定补贴这样调度模型会倾向于让焚烧机组多出力而不是停机。这其实也符合实际项目诉求焚烧炉一旦启动尽可能保持稳定运行。2.3 电转气装置用等效系数把化学过程压缩成线性约束电转气的内部过程包含电解制氢和甲烷化两步如果完整建模会很复杂。好在对于日前调度来说我们关心的是外部电气接口和碳接口不需要去解化学反应动力学。所以我把PtG看成一个“电转天然气”的黑箱输入是电功率输出是天然气功率和CO2消耗量。天然气产出和CO2消耗量都可以用线性转换系数表达。我用的系数是PtG整体电转气效率大约在50%到60%之间每消耗1MWh电大约可以产出0.5到0.6MWh的天然气同时消耗大约0.1到0.12吨CO2。这个数字来自电解制氢电耗约50kWh每公斤氢、甲烷化效率约50%到60%的工程经验范围。你可以根据自己用的设备参数调整但注意要保证化学计量上大致自洽。这个模型的精髓在于PtG运行功率是一个连续可调变量和碳捕集捕集下来的CO2之间通过一个“碳平衡约束”连接起来。如果不加这个约束PtG就会变成纯粹的虚拟负荷失去和碳捕集的协同意义。必须让捕集量大于等于PtG消耗量且二者之间有一个小时级的对应关系。2.4 联络线关口与外电网的交互虚拟电厂和外电网的交互在模型里是一个带上下限的功率变量。我这里把购电和售电视为同一个有符号变量的正负这样线性规划处理起来最方便。购电成本按分时电价乘以购电功率售电收入按分时电价乘以售电功率由于变量是有符号的目标函数里只需要一个线性项。这里有个细节联络线功率限制不只是最大值有时候还要设最小成交功率因为在实际调度中虚拟电厂参与市场交易时会有市场规则约束比如最低申报电量。我在基础版模型里没加这个因为会引入二进制变量增加求解难度。如果你的场景需要可以在此基础上扩展“阶梯报价”或“最小成交电量”约束。3. MATLAB里怎么把模型写出来从数学到代码的一次性落地模型结构定型之后代码实现反而是最轻松的部分。我这里用的是MATLAB YALMIP建模再交给商用求解器求解。YALMIP的好处是语法接近数学表达式写调度模型时不容易把行索引和变量维度搞混调试起来也方便。我个人不太建议直接用MATLAB自带工具箱手写约束矩阵除非你需要极致的求解性能否则调试成本会高到怀疑人生。3.1 求解框架选型为什么是YALMIP而不是直接手写对于这种带连续变量和少量整数变量的优化调度问题最稳妥的路径是建模为混合整数线性规划。YALMIP的作用是帮你把数学表达式转成求解器能认的标准形式省掉手动拼矩阵的过程。我常用的是Gurobi或者CPLEX这一类商用求解器你机器上如果装了开源求解器也能跑只是求解速度会慢一些。需要提醒一点调度模型的变量数量其实并不大典型场景也就24个时段连续变量几百个、整数变量几十个关键是约束矩阵的条件数和数值尺度。如果各变量量纲差异太大比如电功率是百兆瓦级CO2量是吨级目标函数系数跨了三个数量级求解器就会变得很敏感。后面我会专门讲数值尺度处理。3.2 参数配置与数据结构设计所有的参数我都建议放进一个结构体变量里统一管理不要散在脚本各处否则后面改场景参数时你会疯的。下面是我建模时的参数清单参数类别典型数值说明燃气机组额定功率300 MW可调范围30%-100%爬坡约束垃圾焚烧额定功率2×15 MW2台机组配置独立启停碳捕集捕集率范围30%-90%对应烟气中CO2捕集比例单位捕集能耗0.25 MWh/t不含固定电耗碳捕集固定电耗4 MW维持再生塔运行PtG额定功率20 MW电解制氢甲烷化整体PtG碳消耗系数0.11 t/MWh每MWh电耗对应CO2消耗量分时电价0.3-1.2 元/kWh峰谷差驱动PtG运行这段参数表不是随便写的。比如碳捕集固定电耗那一项如果你设成零优化器会在晚上电价低的时候把碳捕集完全关掉第二天早上再启动这在实际工程中是行不通的。加了固定电耗之后模型会自动权衡“停掉省电”和“重启代价”之间的矛盾虽然我并没有显式建模启停成本但固定电耗的非线性效果已经能让结果更合理。3.3 目标函数和约束的YALMIP代码骨架下面这段代码是我提炼出来的核心片段不包含完整的数据读取和结果输出但足够你理解建模的结构。变量定义上P_gas是燃气机组出力P_wte是两机垃圾焚烧总出力P_ccs是碳捕集电耗beta是捕集率P_p2g是电转气功率P_grid是联络线功率。注意为了能让捕集率作为变量出现表达式里会有beta乘以机组出力的非线性项这里我用的是外推处理既然烟气CO2和出力线性相关捕集量可以直接定义为一个新变量C_cap然后用C_cap小于等于beta_max乘以烟气CO2生成量、大于等于beta_min乘以生成量来实现范围约束避免真正的双线性项。%% 决策变量 P_gas sdpvar(1, T, full); % 燃气机组出力 P_wte sdpvar(1, T, full); % 垃圾焚烧总出力 C_cap sdpvar(1, T, full); % 碳捕集量 P_ccs sdpvar(1, T, full); % 碳捕集电耗 P_p2g sdpvar(1, T, full); % PtG电耗 P_grid sdpvar(1, T, full); % 联络线功率购电为正 N_gas binvar(1, T, full); % 燃气机组启停状态 %% 目标函数 objective sum(price_buy .* max(P_grid, 0) - price_sell .* max(-P_grid, 0) ... c_gas * P_gas / eta_gas ... c_ccs * P_ccs ... c_p2g * P_p2g ... c_carbon * (E_total - C_cap - C_p2g_fix * P_p2g) ... - subsidy_wte * P_wte); %% 功率平衡约束 C []; C [C, P_gas P_wte P_re - P_ccs - P_p2g - P_load P_grid]; %% 碳捕集约束 C [C, C_cap beta_min * (e_gas * P_gas e_wte * P_wte)]; C [C, C_cap beta_max * (e_gas * P_gas e_wte * P_wte)]; C [C, P_ccs a_ccs * C_cap b_ccs]; %% 机组相关约束 C [C, P_gas P_gas_min .* N_gas]; C [C, P_gas P_gas_max .* N_gas]; %% PtG与碳协同约束 C [C, P_p2g 0]; C [C, P_p2g P_p2g_max]; C [C, C_cap C_p2g_fix * P_p2g]; %% 求解 ops sdpsettings(solver, gurobi, verbose, 0); optimize(C, objective, ops);这里要特别解释目标函数里的max函数。YALMIP里面max(P_grid, 0)虽然能写但会引入辅助变量不是最优雅的写法。更推荐的做法是把P_grid拆成购电和售电两个非负变量或者直接用分段线性函数表达。我贴这段是为了让你看清思路实际工程代码里我通常会把购电和售电分成两个变量来处理数值稳定性好很多。3.4 求解与结果输出的基本流程求解完成后第一件事不是看图而是检查求解器返回的求解状态和最优性间隙。我习惯用optimize的返回值做断言如果不满足最优解就直接退出防止拿错误结果去做分析。结果输出上我会把P_gas、P_wte、P_ccs、P_p2g、P_grid等变量按24小时画在同一张图里观察功率平衡关系是否自洽。还有一个实用技巧把碳捕集量、PtG的CO2消耗量和净碳排放量单独画一条曲线。如果净碳排放曲线出现负值说明模型确实实现了“负碳”时段这时要回去检查碳捕集能耗和PtG耗电是否是同一时段否则可能只是数据假象。4. 调试实录与常见问题这些坑我基本都踩过代码跑通只是第一步能让结果符合物理直觉才是真正的功夫。这一节我写几个最典型的坑每个都是我在实际调试中遇到过的不是理论推测。4.1 无可行解碳捕集和PtG的耦合约束把系统锁死了最常碰到的情况是加了“C_cap 碳消耗系数 * P_p2g”这个约束之后模型直接无可行解。原因是碳捕集量受上游排放总量和捕集率上限约束你让PtG在某个时段满负荷跑但该时段燃气机组恰好处于低出力烟气CO2总量根本不够捕集量上限于是约束冲突。排查思路是先把问题拆成两个模型一个是只保留碳捕集不接PtG另一个是只保留PtG不考虑碳源。分别求解都能过再接起来通常问题就出在耦合约束过于苛刻。解决办法不是把约束删掉而是给PtG加一个额外的CO2外购通道也就是允许从外部购买CO2作为补充。这个在工程上是合理的园区PtG站本来就可以外购碳源。加上外购CO2变量后模型健壮性立刻上来了。4.2 碳捕集装置变成了“摆设”优化结果里捕集率一直贴着下限这个情况非常有意思说明模型逻辑没问题但经济参数有问题。碳捕集运行成本大于碳价收益时优化器当然选择最低捕集率。解决办法是调整碳价参数或者给碳捕集加一个最低捕集量的政策性约束。我在调试时习惯先做一组灵敏度分析让碳价从低到高变化看捕集率什么时候开始上升。这个拐点对应的是碳捕集装置的正边际收益点能帮你判断参数设置是否合理。如果碳价已经很高捕集率还是贴着下限那就要查碳捕集固定电耗是不是设得太大了。b_ccs等于4MW的时候意味着即使不捕碳也要白白消耗4MW电24小时就是96MWh。如果电价峰值时段较多这个成本极高。我最终把固定电耗降到2MW结果捕集曲线明显更积极了。4.3 数值尺度问题功率是100MW碳量是几吨目标函数项差三个数量级这个坑容易忽略但一旦撞上最典型的表现就是求解器报告numerical trouble或者最优性间隙始终降不下去。YALMIP本身能处理一定范围的数值尺度但如果在同一约束里同时出现“P_gas300MW”和“C_cap3吨”这种量级求解器内部的预处理器就不好办了。我的处理办法很粗暴直接把碳流相关变量的单位放大100倍也就是用“百吨”作为碳量的基本单位烟气排放因子和捕集系数同步换算。这样所有变量都在0.1到100这个量级求解器跑起来稳很多。你可以在代码里做单位换算不用改物理含义结果展示时再换算回吨。4.4 PtG在峰时电价也运行目标函数里忘记算碳收益这个坑出现在初期版本。当时我把碳捕集成本和PtG运行成本算得很重但没有给PtG消耗CO2这一项设置正的碳收益结果PtG在什么时候都不该运行模型干脆让它停机。后来我在目标函数加上了“碳减排收益”项这个收益等于PtG消耗的CO2量乘以碳价。这样低电价时段PtG吃廉价电制甲烷再把CO2固定下来两个收益叠加运行时段就合理了。要注意这里碳价和前面的碳交易成本是同一个参数出现两次没关系但要确保符号一致。捕集和利用是两边都受益如果符号搞反了目标函数会出现严重的“送钱”问题结果就是功率平衡直接失真。4.5 垃圾焚烧机组出力为什么一直顶格补贴参数反了方向还有一次调试我检查结果时发现垃圾焚烧机组全天满发没有任何调节动作。一开始我还以为是合理的毕竟有补贴后来发现是因为我在目标函数里把补贴写成了正号相当于烧垃圾还要花钱但功率平衡约束又逼着它发电结果就是贪心算法让它一直满发。这个错误很隐蔽但检查量纲时能发现补贴项的符号和购电成本项一致说明一定是负号反了。所以每个经济项写完之后我都建议做一个“符号自检表”购电成本正、售电收入负、燃料成本正、补贴收入负、碳交易成本根据净排放方向定正负。把这个表贴在代码注释里能帮后续接手的人少踩很多坑。5. 模型扩展方向与我的个人实操体会这套基础模型跑稳定之后可以做的扩展其实很多。但我想先说明一点扩展功能之前一定要把基础模型的行为研究透因为你后面加的任何模块都会和碳捕集与PtG的耦合产生新交互。5.1 加储能之后会改变什么储能加入后最直接的影响是功率平衡里的灵活性增加了。你可能发现碳捕集和PtG的耦合关系不再那么“紧”因为储能可以在高电价时段释放电力支撑碳捕集在低电价时段充电。这样一来净碳排放曲线会变得更平滑但碳捕集和PtG之间的协同关系依然存在只是被储能部分掩盖了。我建议做对比实验同一场景下先跑无储能模型再跑有储能模型并记录碳捕集总量、PtG产气量、净碳排放、日运行成本这四项指标。你会发现储能的边际价值并不仅仅体现在套利收益上而是体现在让碳捕集和PtG能够更从容地按碳价信号运行。5.2 把碳捕集储能罐加进去如果项目对捕集后的CO2存储时长有要求可以给CO2增加一个存储罐变量相当于一个“碳库”。这时捕集量不再必须和PtG消耗量同时匹配可以白天多捕、晚上再把CO2送到PtG。这样模型的时序耦合更强但也更接近实际工程。碳库的状态转移方程和储能SOC类似需要设初始储量、上下限和末状态约束。我在实际项目里加过这个效果很明显碳捕集装置不再需要跟随PtG的瞬时功率捕集率可以更稳定地维持在高位整体经济性提升了不少。代价是决策变量多了一组连续变量求解时间几乎可以忽略。5.3 最后的实操体会如果让我给第一次做这类模型的朋友一个建议我会说别急着把模型做复杂先把碳捕集和PtG的耦合逻辑理清楚哪怕是一个6时段的算例也好你把这个6时段模型的24个约束画成一张表搞清楚每个约束里的变量是否都有物理含义再往大算例扩展。我这套模型从第一版到现在改动最大的不是代码而是对“碳捕集到底算什么角色”的理解。一开始我也把它当污染治理设备后来才意识到在虚拟电厂框架里它本质上是一个可调负荷、一个碳源、一个经济调节器。当你把这三个角色统一到优化模型里很多原来觉得不合理的结果都会变得顺理成章。希望这篇分享能帮你在建模的路上少踩几个坑多跑出几张让自己满意的曲线。