
去年做虚拟电厂调度项目的时候我卡在了一个看起来很小、实际却决定全局的问题上碳成本到底该怎么进优化模型如果仅仅把碳价当成一个常数那么P2G、CCS这类设备的经济价值根本算不清楚——它们的存在本质就是少排碳和转化碳碳价一旦从固定值变成阶梯式分段函数整个调度方案的形态都会跟着变。这篇文章围绕基于阶梯碳交易的含P2G-CCS耦合和燃气掺氢的虚拟电厂优化调度来展开把这套系统的原理、数学模型和Matlab实现思路完整捋一遍P2G-CCS耦合为什么能把风电消纳和碳捕集串成闭环燃气掺氢的比例边界怎么定、掺多少才划算阶梯碳交易怎么用分段线性约束写进MILP模型以及我用MatlabYalmipGurobi实际复现时踩过的坑和经验。文章适合正在做虚拟电厂、综合能源系统调度、碳交易机制建模、或者想用Matlab做优化复现的同学参考。1. 这套系统到底在解决什么问题1.1 虚拟电厂调度遇上的三个现实问题虚拟电厂VPP说白了就是把分散的风电、光伏、燃气机组、储能、可调负荷这些资源聚合成一个整体统一接受调度指令。传统VPP研究的重点是怎么把电平衡做好但放到现在这个语境里事情没这么简单了。第一个问题是高比例风电接入带来的弃风。风电大发时段往往负荷不高电网消纳不了VPP层面看到的就是有电卖不出去。这时候如果能有一套设备把多余的电转化成其他能源形态存起来弃风问题就能缓解一大半这也是P2G被引入的根本动机。第二个问题是燃气机组作为调节电源承担了顶峰和填谷任务但它烧的是天然气碳排放实打实存在。VPP要响应减排要求燃气机组又不能直接拆掉怎么办要么给它配碳捕集要么想办法减少它的直接碳排放——燃气掺氢就是从这个角度切入的。第三个问题是当碳交易机制介入之后碳成本成了调度里不可忽略的一项。以前做优化时碳价是一个固定常数减排多少、成本增减多少线性变化模型里体现不充分。但现在很多研究采用阶梯碳交易超排量越大单位碳价跳得越高成本曲线变成了分段函数。这让原本线性可加的成本模型变得复杂也让P2G、CCS这些设备的减排价值变得更有讨论空间。这三个问题其实是耦合在一起的风电富余时靠P2G把电转成氢或甲烷储存燃气机组靠掺氢降低碳排放CCS把燃烧产生的CO2捕集下来送回P2G做甲烷化原料最后碳交易机制给这一整套循环定价。分开建模型都不难难的是把三者统一在一个优化框架里让它们互相配合。1.2 P2G-CCS耦合和燃气掺氢为什么总被放在一起说单独看P2G它的核心是电解水制氢副产品是氧气。但氢不好存储运输很多研究会在后面加一步甲烷化让氢和CO2反应生成甲烷这样就获得了和天然气兼容性很好的燃料可以直接进入燃气管道或者储气罐使用。这里的关键来了甲烷化需要CO2作为原料。CO2从哪儿来如果从空气中捕集成本极高经济上根本转不过来。但如果系统里有CCS设备燃气轮机燃烧排放的CO2被捕集下来一部分存储封存另一部分直接供给P2G甲烷化这就形成了一个电-氢-气-碳闭环。风电电解水制氢氢和捕集来的CO2合成甲烷甲烷再给燃气机组烧燃烧排放的CO2又被CCS捕集回来。这个循环里碳被反复利用而不是排到大气中风电的弃电也被转化成了可储存的燃料。燃气掺氢则是另一条减碳路径。P2G制出来的氢气不一定要全部甲烷化可以抽取一部分直接掺入天然气中送给燃气机组燃烧。氢气燃烧不产生CO2所以掺氢比例越高单位发电量的碳排放越低。但掺氢比例不能无限提高受限于燃气轮机的燃烧稳定性、热值变化和管道设备改造程度。所以这三者的关系是P2G提供氢源CCS提供碳源二者耦合形成气体燃料制备链燃气掺氢是这条链的终端使用环节阶梯碳交易则是倒逼整条链运转起来的经济信号。1.3 阶梯碳交易到底改变了什么不搞碳交易建模的人可能觉得碳价高一点低一点也就那样但真正把阶梯碳交易写进优化模型之后你会发现它改变了设备调度的优先级。假设免费碳排放配额是固定的实际排放超过配额后超排部分按阶梯定价第一档单价低第二档单价高第三档更高。这意味着如果你的排放量正好落在第二档边界附近多排一吨碳的边际成本突然跳升。优化模型面对这种非线性成本会怎么反应它会主动调整设备出力要么降低燃气机组出力要么增加CCS捕集量要么把P2G制出来的氢掺进燃气里减少排放因为所有这些减排动作的收益都被放大了——它们省下的不只是那点配额内的碳成本而是躲过了高价档位的那部分费用。从数学上看阶梯碳交易引入的是一个分段线性函数。处理分段线性函数的经典方式有两种一种是用SOS2约束配合凸组合另一种是用二进制变量做大M约束。把碳成本分解成若干区间内的线性段然后让模型自动判断落在哪个区间。这个处理如果做不好约束写错了优化结果可能直接错到离谱。2. 系统架构与能量流动从风电到绿电、绿氢、绿甲烷2.1 设备组成与电、气、碳三种流虚拟电厂内部大致包含以下设备风电机组、光伏可选、燃气轮机、燃气锅炉如果有热负荷、电储能、P2G设备电解槽甲烷化反应器、CCS捕集装置、储氢罐、储气罐、上级电网购售电通道。电能量流风电/光伏发电 → 供给负荷、电储能充电、P2G电解槽耗电、CCS装置耗电、或通过上级电网外送。燃气轮机发电 → 供负荷或外送。气体流P2G电解水产出氢气一部分直接进入储氢罐另一部分分流到掺氢接口与天然气混合甲烷化反应器消耗氢气和CO2产出甲烷送入储气罐或直接进入天然气管道储气罐/天然气管网的燃气供应燃气轮机。碳流燃气轮机燃烧天然气含掺氢混合气排放CO2CCS捕集烟气中的CO2一部分送去甲烷化做原料剩余部分视为减排。这个架构最核心的部分在于电-碳耦合。常规VPP里电是电、碳是碳两者之间唯一的联系是燃气机组的排碳因子。但这里P2G电解槽要用电、CCS捕集也要用电电和碳通过两套设备产生了双向绑定多给P2G送电就能多产氢/甲烷从而减少燃气机组对天然气外购的依赖多给CCS送电就能多捕集CO2这些CO2又变成P2G的原料。系统因此多了一层用电来减碳的自由度。2.2 P2G-CCS的碳闭环逻辑P2G分两步走。第一步是电解水2H2O 2H2 O2这步需要大量电能目前碱性电解槽的效率在60%-75%之间效率不是100%所以转化为氢的能量和消耗的电能之间有一个折算系数。第二步是甲烷化也就是Sabatier反应4H2 CO2 CH4 2H2O。这个反应需要1份CO2配4份H2产出1份甲烷。CCS的作用是把燃气轮机排烟里的CO2分离捕集下来。捕集本身也要耗电通常捕集单位质量CO2需要消耗一定电能这个能耗系数直接影响整个系统的净收益。如果CCS耗电太高捕集1吨CO2花的电费比碳价还贵那经济上就不划算。在建模时碳闭环的约束主要体现为CCS捕集量上下限M_cap_min ≤ M_cap(t) ≤ M_cap_maxCCS捕集能耗P_ccs(t) β_ccs × M_cap(t)β_ccs是单位捕集电耗甲烷化用碳量不能超过捕集碳量和储碳罐存量之和甲烷化产气量与耗碳量满足化学计量关系实际工程里还有一个细节值得注意甲烷化需要的是高浓度CO2而CCS捕集下来的CO2纯度取决于捕集工艺。如果用的是燃烧后化学吸收法CO2纯度可以达到95%以上直接可以用如果是其他工艺可能需要额外提纯环节这里在模型里通常不细化直接假设满足纯度要求即可。2.3 燃气掺氢的运行边界燃气掺氢并不是想掺多少就掺多少。天然气管道和燃气轮机对掺氢比例都有硬约束实际工程中常见的是按体积分数限制一般在10%-20%左右部分改造过的机组可以到30%但那是另一个话题。建模时直接用掺氢比例上限α_max来表示这个限制。掺氢之后燃气轮机烧的是混合燃料混合燃料的低热值会变化。天然气的低热值约9.7 kWh/m³氢气的低热值约三倍于此按质量但密度极小所以按体积算的话氢气低热值约3 kWh/m³。掺氢体积比为α时单位体积混合气的热值需要按比例加权计算。如果用质量分数表示换算关系又要重新算一遍这里特别容易出单位错误。在燃气轮机出力模型里发电功率P_gt和燃料耗量F_fuel的关系是P_gt η_gt × F_fuel × LHV_mix。如果α是决策变量LHV_mix就会随α变化F_fuel和LHV_mix的乘积就会产生非线性项这对MILP求解器来说是不友好的。我在后面第4节会专门说怎么拆分处理这个双线性项。3. 阶梯碳交易的数学模型化分段定价怎么写成约束3.1 阶梯碳价的政策逻辑与成本表达式阶梯碳交易的建模思路可以这样理解系统有免费配额E_quota实际排放E_total由燃气机组燃烧排放、外购电间接排放等组成。净超排量x E_total - E_quota。如果x小于等于0碳成本为0甚至可以把富余配额出售获得收益但保守起见很多模型不考虑这个负值收益。阶梯定价的具体形式通常是这样的超排量在[0, q1]区间碳价为c1超排量在(q1, q2]区间超出q1的部分碳价为c2超排量在(q2, q3]区间超出q2的部分碳价为c3注意阶梯碳价不是一旦超排超过q1所有排放都按c2算而是分段累进类似阶梯电价。所以总碳成本可以写成C_carbon c1×x1 c2×x2 c3×x3其中x1、x2、x3是各区间内的超排量满足x1x2x3 x且0 ≤ x1 ≤ q10 ≤ x2 ≤ q2 - q10 ≤ x3 ≤ q3 - q2上限视几个阶梯而定。这个表达式本身是线性可加的形式但要注意如果不对区间顺序加以约束优化器有可能把超排量直接分配到高价区间来避免进入高价——这听起来不对实际是因为目标函数是求最小化碳成本如果区间顺序约束写得不完整模型会把高价的x3设为0、低价的x1填满这恰好符合分段累进的逻辑。所以关键在于低区间填满才能用高区间这个隐含顺序在大M约束里通过二进制变量来强制实现。3.2 分段线性化的两种实现方法方法一是SOS2约束。Yalmip里有sos2函数可以把碳成本构建成断点和函数值的离散点组合。设置断点向量bp [0, q1, q2, q3]对应函数值fv [0, c1×q1, c1×q1c2×(q2-q1), c1×q1c2×(q2-q1)c3×(q3-q2)]然后用sos2变量选择相邻断点插值成本函数就变成连续的凸分段线性函数。SOS2的思路直观不过如果超排量和碳成本两端都是变量使用起来有一定约束要求。方法二是大M法配合二进制变量这个更通用尤其适合自定义复杂的阶梯逻辑。具体做法是引入K段二进制变量z_kk1,...,K表示超排量落入了第k段将总超排量x分解为各段量x_k之和对每段施加边界约束z_k×下界_k ≤ x_k ≤ z_k×上界_k加上Σz_k1保证只落在唯一区间写成约束的形式就是x1 x2 x3 x0 ≤ x1 ≤ q1×z10 ≤ x2 ≤ (q2-q1)×z20 ≤ x3 ≤ (q3-q2)×z3z1 z2 z3 1z_k ∈ {0,1}看起来简单但在实际运行中我遇到过几个隐蔽问题后面第7节会细说比如不加epsilon时模型可能因为浮点误差在区间边界处反复跳变再比如z_k为0时如果当地下界写成严格正数会导致强制x_k≥ε而引发不可行。3.3 碳配额与实际排放的计算口径碳配额怎么定各模型假设不一样。有的按历史排放强度下降比例核定有的按单位电量排放基准核定。在VPP调度模型中比较常见的处理是E_quota λ×L_total其中λ为单位负荷对应的配额系数L_total是系统总负荷。也可以用装机容量来定但负荷配额更贴近实际调度。实际排放E_total的计量口径也要说清楚。燃气机组直接燃烧排放肯定算外购电属于间接排放要不要算进VPP头上有争议但很多研究为体现电碳耦合会把购电的间接排放也纳入在模型里乘以电网平均排放因子。CCS捕集下来的碳算不算减少排放算。P2G甲烷化固定下来的碳算不算如果CO2最终以甲烷形式被燃烧那这个碳最终还是排了只是延迟排放所以严格来说不能双重抵扣。但CCS将捕集碳送去储存而非参与甲烷化时这部分可以视为永久封存可作为减排量抵扣。模型里要明确区分这两种去向否则碳账会算错。4. 优化调度的目标函数与约束体系4.1 目标函数哪些钱必须算进去目标函数一般取调度周期内的总运行成本最小化调度周期常见为24小时步长1小时。我建议把成本项列成一个结构清晰的表达式逐个对照检查有没有遗漏min C_total C_gas C_grid C_om C_carbon C_curtail - C_sell - C_byproduct其中C_gas天然气购买成本 外部购氢成本如果系统没有自产氢气但部分掺氢需求需要外购C_grid从上级电网购电费用C_om各设备运行维护成本通常简化为单位出力的线性成本系数之和C_carbon阶梯碳交易成本第3节已经建模C_curtail弃风弃光的惩罚成本通常远高于发电边际成本迫使模型尽量消纳C_sell向上级电网售电收入如果模型允许反向售电C_byproductP2G产氢、产甲烷对外销售的收益如果模型允许VPP将多余气体卖给外部实际过程中很多人容易漏掉C_byproduct导致P2G设备在多余产氢时没有收益驱动优化结果会过少地启动P2G。虽然调度模型里P2G的主要作用是消纳弃风但如果产出的氢气/甲烷既没有内部需求也不能外售那P2G确实没有启动动机。因此要么不给P2G额外外售收益直接设为内部闭环消纳要么允许外售。建模前一定要把假设写清楚不然结果解释起来很容易打架。4.2 电功率平衡与燃气轮机模型电功率平衡是整个模型的主轴约束写出来就是P_wind(t) P_pv(t) P_gt(t) P_es_dis(t) P_buy(t) L_e(t) P_es_ch(t) P_p2g(t) P_ccs(t) P_sell(t)等式左边是电源侧右边是负荷侧。P2G和CCS的电耗在右边作为可调负荷参与平衡。这个结构很直观风电大发时P_p2g增大消纳弃风系统需要电力顶峰时P_gt增大、P_p2g降低。多能流系统的电平衡本质上就是在调配这些用电/发电设备的运行点。风电出力约束很简单0 ≤ P_wind(t) ≤ P_wind_forecast(t)即在预测可用功率范围内可调弃风量的计算就是预测值减去实际出力。燃气轮机模型需要注意几点出力上下限P_gt_min ≤ P_gt(t) ≤ P_gt_max爬坡约束-P_ramp_down ≤ P_gt(t) - P_gt(t-1) ≤ P_ramp_up如果考虑启停还要引入二进制启停变量和最小开停机时间约束。对于以天为周期的调度爬坡约束不建议省略因为燃气机组频繁启停、大幅爬坡在实际运行中不可行爬坡约束能让调度结果更贴近真实。4.3 P2G与CCS的输入输出约束P2G电解水制氢的输入输出关系M_h2(t) η_el × P_el(t) / LHV_h2其中P_el是电解槽耗电η_el是电解效率M_h2是产氢量单位统一后LHV_h2是氢气热值。产出的氢气有三个去向直接掺氢用、甲烷化反应用、存入储氢罐。因此有M_h2(t) M_h2_mix(t) M_h2_meth(t) M_h2_st(t)甲烷化反应的化学计量关系按能量折算每消耗1 MWh的氢气热能大约对应固定比例的CO2消耗和CH4产出。我习惯把所有气体量统一换算成能量单位MWh这样天然气的购买量也可以直接乘购气价格单位不会乱。如果一定要用物理体积单位则必须查表换算燃料热值但单位一多特别容易错。CCS的捕集模型相对简单M_cap(t) ≤ η_ccs × E_gt_CO2(t)其中E_gt_CO2(t)是燃气轮机燃烧产生的CO2总量η_ccs是最大捕集率。捕集量 M_cap(t) 的去向有两个甲烷化用碳 M_co2_meth(t) 和封存碳 M_co2_store(t)。产出的甲烷量再进入储气罐或直接供燃气机组。CCS装置本身的电耗用捕集量乘以单位电耗系数计算这个系数通常在模型中给定不随负载变化是一种线性近似。4.4 掺氢比例约束与燃料热值换算掺氢比例的精确约束要看用什么单位定义。工程上常见的是体积分数但优化模型里质量和能量更容易线性表达。建议用能量替代率来定义掺氢能量占比β_t它表示混合燃料中氢气提供的热值占总热值的比例。设燃气轮机的总燃料热值输入为F_GT_h(t) P_gt(t)/η_gt那么F_GT_h(t) F_NG_h(t) F_H2_h(t)其中F_NG_h是天然气供给的热值F_H2_h是氢气供给的热值。掺氢能量占比β_t F_H2_h(t)/F_GT_h(t)约束0 ≤ β_t ≤ β_max。这样写燃气轮机燃料消耗就拆成了两个线性项不再出现热值×流量的乘积双线性项这是处理掺氢模型的一个关键技巧。碳排放的计算也随之线性化E_gt_CO2(t) F_NG_h(t) × EF_NG这里EF_NG是单位热值天然气对应的CO2排放因子氢气部分排放为0。如果按体积掺氢比定义LHV_mix会进入公式产生非线性按能量占比定义后公式干净很多。实际项目中我建议优先用能量占比做分析如果需要输出体积掺氢比最后再用热值换算回去展示。燃气轮机燃料消耗也算购气成本C_gas_ng Σ F_NG_h(t) × price_ngC_gas_h2 Σ F_H2_h(t) × price_h2这里的price_h2可以是P2G自产氢的内部成本也可以是外购氢价格。自产氢的内部成本在目标函数中其实已经通过P2G电耗和运维成本体现了不能重复计算。我一般采用P2G产氢成本 电解槽耗电费用 运维成本产出的氢气以内部转价格供货给燃气轮机目标函数里只保留一次避免重复计费。4.5 储能/储氢/储气的动态约束电储能采用标准的SOC模型SOC(t) SOC(t-1) η_ch×P_es_ch(t) - P_es_dis(t)/η_disSOC_min ≤ SOC(t) ≤ SOC_maxSOC(0) SOC(T)0 ≤ P_es_ch(t) ≤ P_es_ch_max×u_ch(t)0 ≤ P_es_dis(t) ≤ P_es_dis_max×u_dis(t)u_ch(t) u_dis(t) ≤ 1最后一条是防止同一时刻既充电又放电。如果觉得二进制变量太多影响求解速度也可以不加u_ch和u_dis单纯的正负约束下优化器不会同时充放可以省一点变量但结果可能轻微失真——视模型规模取舍。储氢罐和储气罐的模型类似S_h2(t) S_h2(t-1) M_h2_st(t) - M_h2_release(t)S_ch4(t) S_ch4(t-1) M_ch4_produce(t) - M_gt_gas(t)各储罐有容量上下限初末状态一般设为相等表示调度周期内的连续性。如果需要考虑那些储罐之间能量密度不同的问题比如同样容量下氢的能量密度只有天然气的三分之一那么约束里的上限系数就要换算。这些细节用能量单位建模时可以简化但注释里最好写明物理量纲不然两个星期后回来看代码自己可能都想不起某系数是哪儿来的。5. MatlabYalmip求解的实现要点5.1 环境配置与求解器选型这个模型最终是一个混合整数线性规划MILP因为阶梯碳价和储能防同充同放引入了二进制变量。Matlab里我推荐Yalmip做建模层求解器选Gurobi或Cplex。Yalmip不是求解器是一个建模工具箱它把约束和目标函数翻译成底层求解器能识别的问题格式。对学术复现来说Yalmip的好处是建模代码几乎和数学表达式一对一改约束非常方便。具体安装没什么好说的Gurobi需要申请学术licenseCplex现在IBM有社区版。Yalmip直接GitHub下载在Matlab里addpath(genpath(yalmip))就行然后运行yalmiptest检查是否识别到求解器。如果显示找不到Gurobi通常是环境变量没配好Yalmip在Windows下通过系统PATH寻找gurobi求解器可执行文件。5.2 变量定义与约束写入模板我直接给一个简化的代码骨架照着改参数就能跑通%% 基础参数 T 24; price_ng 3.2; % 天然气价元/kWh热能 price_grid_buy 0.5; % 购电价元/kWh carbon_price [60, 120, 200]; % 各阶梯碳价元/tCO2 carbon_bound [100, 250, 500]; % 各阶梯上限tCO2 %% 决策变量 P_gt sdpvar(1, T); % 燃气轮机出力 P_wind sdpvar(1, T); % 风电实际出力 P_p2g sdpvar(1, T); % P2G耗电 F_ng sdpvar(1, T); % 天然气热值输入 F_h2 sdpvar(1, T); % 氢气热值输入 M_cap sdpvar(1, T); % CCS捕集量 S_h2 sdpvar(1, T); % 储氢罐存量 P_es_ch sdpvar(1, T); % 储能充电 P_es_dis sdpvar(1, T); % 储能放电 x_carbon sdpvar(length(carbon_price), T); % 各碳阶梯量 z_carbon binvar(length(carbon_price), T); % 碳阶梯标记 %% 目标函数 Objective sum(price_ng * F_ng) sum(price_grid_buy * max(P_wind P_gt - load, 0)) ... sum(carbon_price * x_carbon) 0.1 * sum(P_gt); % 运维成本系数示例 %% 约束 Constraints []; % 电功率平衡 for t 1:T Constraints [Constraints, P_wind(t) P_gt(t) P_es_dis(t) ... load(t) P_p2g(t) P_es_ch(t)]; end % P2G产氢与耗电关系 Constraints [Constraints, F_h2_total eta_el * P_p2g * 0.0036]; % 按单位换算 % 燃气轮机掺氢约束 Constraints [Constraints, P_gt eta_gt * (F_ng F_h2)]; Constraints [Constraints, F_h2 beta_max * (F_ng F_h2)]; % 阶梯碳约束大M法 for t 1:T Constraints [Constraints, x_carbon(1,t) carbon_bound(1)*z_carbon(1,t)]; Constraints [Constraints, x_carbon(2,t) (carbon_bound(2)-carbon_bound(1))*z_carbon(2,t)]; Constraints [Constraints, x_carbon(3,t) (carbon_bound(3)-carbon_bound(2))*z_carbon(3,t)]; Constraints [Constraints, sum(z_carbon(:,t)) 1]; Constraints [Constraints, sum(x_carbon(:,t)) E_total(t) - E_quota]; end %% 求解 ops sdpsettings(solver, gurobi, verbose, 2, debug, 1); ops.gurobi.MIPGap 0.01; ops.gurobi.TimeLimit 300; optimize(Constraints, Objective, ops); %% 结果提取 P_gt_opt value(P_gt);这里的F_h2_total我还没定义完整实际代码里需要把产氢、掺氢、储氢的关系补全。但整体逻辑就是这样把物理关系翻译成一行行约束。需要提醒的是Yalmip里sdpvar默认是连续变量binvar是0-1变量intvar是整数变量。混合整数问题最忌讳的是二进制变量满天飞能合并表达的状态就合并表达。5.3 求解器参数设置与数值技巧Gurobi跑MILP时MIPGap是精度和速度的平衡点。论文复现用0.011%足够实际工程可以放宽到2%-3%求解速度会快很多。TimeLimit建议设置因为MILP在大规模下有可能会长时间卡在上界更新上设置300秒或600秒防止无限等待。还有一个很容易忽略的问题数值尺度。目标函数里如果既有几千kW的电量交易量级10^3又有几百吨的碳排放量级10^2还有掺氢的小数比例量级10^-2这些数值混合在一起时Gurobi内部的数值尺度处理可能出问题表现为求解器日志里出现numerical trouble或者求解结果明明可行但质量很差。做法是尽量统一量纲比如所有能量单位统一成MWh所有碳排放统一成吨购电价统一成元/MWh价格和量级都在合理范围内求解器会舒服很多。6. 算例设计与结果解读四种场景的对比逻辑6.1 场景划分与参数设置要验证模型效果建议至少跑四个场景场景A基础VPP含风电、燃气轮机、储能碳价为固定值场景B加入P2G-CCS耦合碳价为固定值场景C完整系统P2G-CCS燃气掺氢碳价为固定值场景D完整系统碳价改为阶梯碳交易固定碳价和阶梯碳价对比时阶梯断点要设计得不那么温和比如第一档刚好满足历史平均排放第二档落在稍微超排的区间第三档定得很高这样才能看出模型在边际碳价跳升时的行为变化。典型参数可以参考风电预测出力采用某日中午高、夜间低的数据负荷曲线采用双峰曲线燃气轮机爬坡率取额定容量的20%/hP2G电解效率取70%CCS捕集率取90%捕集电耗取0.2 MWh/tCO2掺氢能量占比上限取10%储能容量取系统负荷的15%左右。6.2 结果图里最值得看的三条线第一个必看的是电功率平衡堆叠图。横轴是时间纵轴是功率风电、燃气、储能放电、购电从下往上堆叠负荷、P2G耗电、储能充电从上往下堆叠。重点观察夜间风电大发时段P2G耗电是否抬升、弃风是否被消纳。如果P2G在夜间没有动作说明模型的电价/成本参数还不合理。第二个必看的是CCS捕集量与P2G产氢/产甲烷的对应曲线。碳闭环运转得好捕集量和甲烷化耗碳量应该呈咬合状态即捕集量跟得上甲烷化需求。如果捕集量长期大于甲烷化用碳说明封存比例偏高系统整体减碳路径依赖CCS封存如果捕集量紧张说明P2G甲烷化需求过旺可以考虑通过购氢补充。第三个必看的是掺氢比曲线。掺氢比应该在全天大部分时段贴着上限跑尤其是燃气轮机出力大的时段。如果掺氢比始终为零说明氢气的获取成本高于天然气成本掺氢在经济上不占优——这时候要检查P2G是否频繁启动、产氢成本是否设置过高。6.3 阶梯碳价对调度策略的真实影响从结果上看场景C到场景D的调度策略通常会出现几个明显变化。第一燃气机组的出力曲线被压低或转移。阶梯碳价让燃气机组在某些时段变得不经济系统倾向于让燃气机组避峰运行把负荷高峰交给上级电网购电或者用储能提前转移电量。第二CCS捕集率上升。当碳排放量逼近阶梯边界时多捕集一吨碳不仅省下当前碳价费用还可能让系统从高阶梯回落到低阶梯省下的是边际高价档与低价档之间的差额。这个激励比固定碳价下强得多。第三P2G的启动时段从夜间消纳弃风扩展到碳价敏感时段。也就是说即使不是在风电大发时段只要系统碳排放量处于高阶梯边缘P2G也会启动因为多用电制氢减少了燃气消耗从而减少碳排放避免了跳入更高碳价档位。这种跨时段、跨能源形态的耦合效应是阶梯碳交易模型最值得展示的结论。7. 实测中的坑与排查方法7.1 单位换算错误引发的灾难这是我在完成这个模型时犯过最离谱的错。天然气价格通常是按每立方米多少元给的而模型里用的是热值单位。1 m³天然气的热值大约9.7 kWh如果你忘了把体积乘以热值购气成本直接差了一个数量级。反过来碳排放因子如果按单位体积CO2排放给而燃料量已经换算成热值排放计算也会对不上。我的习惯是列一张换算备忘气体/项目 | 数值 | 单位 天然气热值 | 9.7 | kWh/m³ 氢气热值 | 33.3 | kWh/kg按质量或约3 | kWh/m³按体积 标准CO2排放因子 | 0.2 | kgCO2/kWh天然气热能 甲烷化反应 | 4份H2 1份CO2 → 1份CH4 2份H2O把单位全部统一到能量MWh 质量tCO2后再进模型基本不会因量纲出问题。尤其要注意的是气体体积受温度压力影响论文里给的数值和实际工程条件会有偏差但建模阶段直接用标况值即可。7.2 分段碳价约束导致不可行大M法处理阶梯碳价时我一开始把x_k的下界直接写成0上界写成区间宽度乘以z_k然后加上Σx_k x的等式约束。跑出来的结果是当z_20且x_20时等式x1x2x3x依然成立看起来没问题。但实际求解时有些求解器会在区间边界处出现数值问题x2被赋给了一个极小的正数而对应的z2仍为0破坏了大M强制关系模型报infeasible。解决方法是给x_k的下界加一个小的epsilon或者改用SOS2约束再或者干脆把变量定义成累计超排量的凸组合形式。我后来用的是Yalmip自带的分段线性函数接口它内部会处理边界连续性省了很多手工调试时间。7.3 掺氢双线性项的破解思路燃气轮机耗气量模型如果按出力 效率×总燃料×混合热值来写混合热值随掺氢比变化那两个变量相乘就成了双线性项Gurobi作为MILP求解器无法直接处理双线性目标或约束。我的解决方案在前面已经提到把燃料拆分成天然气和氢气两个独立变量用能量占比定义掺氢比这样热值函数被隐式消掉约束全是线性的。如果你必须用体积掺氢比来展示结果可以在模型求解完之后做一个后处理换算把最优的F_H2和F_NG换算成体积比输出而不是直接把体积比作为决策变量放进模型。还有一种情况是CCS捕集能耗系数如果随负荷变化也会产生双线性项。这种情况下我建议固定为常数或者用分段常数近似。学术复现中折线近似完全够用没必要为精确的非线性特性把整个模型拖入非线性规划的泥潭。7.4 Yalmip报错信息的解读与加速技巧Yalmip最常见的报错是Nonlinear constraints detected。出现这个意味着你的约束里有变量相乘、指数、对数等非线性表达式。先检查燃料热值那一段再检查是否有把SOC乘以效率之类的操作。Yalmip的报错会直接提示哪一行出现的非线性按着提示一行行排查就行不要瞎猜。另一个常见问题是求解很慢。变量量大、二进制变量多MILP求解时间随二进制变量数量飞速上升。我的经验是先跑一个无二进制版看看结果是否合理再逐步加入防同充同放、阶梯碳价的二进制变量这样定位问题快。其他技巧包括用yalmip(clear)清理旧模型缓存避免约束叠加导致求解器卡死所有参数尽量在循环外预计算不要在每个for t里重复计算常量储能初末状态约束优先写成等式减小解空间Gurobi的MIPFocus参数可以设成2让求解器更关注下界改善适合大规模难收敛的问题跑完整套模型以后我最大的感受是这类多能流耦合调度的难点其实不在模型本身有多复杂而在于把碳交易机制、物理化学反应和设备运行约束翻译成同一套数学语言。阶梯碳交易的分段逻辑、掺氢的线性化拆解、P2G-CCS碳闭环的计量口径每一个环节单独拎出来都不难但组合在一起时任何一处单位错误或者约束松弛都会让结果彻底失真。MatlabYalmip这套工作流的优势在于约束可以一行对一行地对应到数学表达式上让我把精力留在机制设计而不是编程调试上。最后再分享一个小技巧代码里每个模块的注释尤其是碳交易分段和掺氢比例转换这两处我当时写得很详细结果两周后回头改参数时直接在注释提示的位置改就行完全不用重新推一遍公式。如果你打算在这个模型基础上扩展——比如加碳捕集的储碳罐容量约束、或者把P2G电解槽改成动态响应模型——建议也把注释写得仔细一点。模型将来要改的往往不是求解逻辑而是那些看起来不起眼的假设条件。