☰
虚拟电厂P2G-CCS耦合与阶梯碳交易优化调度的MILP建模
2026/9/28 6:33:53 网站建设 项目流程

1. 项目思路与整体架构拆解

先把这个标题拆开看。虚拟电厂、P2G-CCS耦合、燃气掺氢、阶梯碳交易、优化调度,这几个词堆在一起,本质上是做一件事:在给定风电出力曲线、电负荷曲线、气负荷曲线的前提下,以运行总成本最小为目标,求解未来24小时内虚拟电厂内部各单元——风机、燃气轮机、电解槽、甲烷化反应器、碳捕集装置、储氢罐、掺氢燃气轮机——的最优出力计划。约束条件里除了常规的电功率平衡、机组爬坡、出力上下限之外,还叠了一层碳交易成本的阶梯化处理,以及P2G产出的氢气既可以直接卖给氢负荷、又可以送入燃气轮机掺烧的物料平衡关系。

这个题目在当前“双碳”背景下的研究热度很高,核心原因是它把三条减碳路径——电气化替代(P2G)、末端脱碳(掺氢燃烧)、源头减排(CCS)——全部塞进了一个虚拟电厂的框架里,再通过阶梯碳交易把碳成本内生化。相比传统的虚拟电厂调度模型,多了一个CO₂闭环:CCS捕集到的CO₂不再只是封存,而是直接供给甲烷化反应器,与电解槽制取的H₂合成天然气(CH₄),从而把弃风转化为可储存、可燃烧的气态燃料;燃烧后产生的CO₂又可以再次被捕集。这条闭环路径会让模型的物料平衡约束比普通VPP复杂很多,但换来的是碳排放量的大幅下降。

对谁有用?首先是做电力系统优化调度方向的研究生,尤其是论文里需要做“XX机制下的VPP调度”这类仿真验证的,这代码可以直接改参数、改场景跑对比实验。其次是用Matlab做Yalmip建模的工程师,可以学习怎么把分段线性碳价、双线性耦合项这类非凸约束写成可求解的MILP形式。第三是关注虚拟电厂商业模式的人——阶梯碳交易的引入让碳成本不再是固定常数,而是随排放量递增的阶梯函数,这直接影响燃气轮机的启停策略和P2G的运行时长,调度结果会和传统固定碳价模型有明显差异。

这代码的核心逻辑可以总结成一句话:用碳交易成本做“指挥棒”,让虚拟电厂在电价低谷期多制氢、多捕碳,在电价高峰期间多用掺氢燃气轮机发电,同时用阶梯碳价限制总排放。实现的路径是混合整数线性规划(MILP),Matlab下用Yalmip工具箱建模,Gurobi或CPLEX求解,整个项目文件包含参数初始化、变量定义、约束构建、目标函数、求解器调用、结果画图这几个模块,代码量在一千行左右。

2. 核心数学模型拆解

2.1 虚拟电厂内部能源枢纽结构

我把这个虚拟电厂的拓扑关系先理清楚,因为这决定了后面所有约束怎么建立。电母线侧,风电(WT)和燃气轮机(MT)是电源,电负荷(P_load)是需求,电解槽(EL)和碳捕集装置(CCS)是电耗单元,剩下的功率缺口从上级电网购买(P_buy),多余的电也可以卖给电网(P_sell)。气母线侧,甲烷化反应器(METH)产出的天然气、外部购气(G_buy)汇入天然气母管,供燃气轮机和气负荷使用。氢气母线侧,电解槽(EL)产出H₂,一部分直接供给氢负荷(H_load),另一部分进入储氢罐(HST),还有一部分送去甲烷化反应器合成天然气。

这里最关键的设计是燃气轮机的燃料来源有两个:外部天然气和储氢罐释放的氢气。掺氢比例不是固定的,而是一个可以优化的连续变量(一般设上限为20%-30%,受燃烧稳定性限制)。CCS装置捕集燃气轮机排气中的CO₂,捕集率通常设为85%-90%,被捕集的CO₂一部分送去甲烷化反应器与H₂反应生成CH₄,多余的部分封存。这样整个系统的CO₂流就是:天然气燃烧产生CO₂ → CCS捕集 → 甲烷化固定为CH₄ → 再次燃烧,形成闭环。

2.2 阶梯碳交易模型的线性化处理

阶梯碳交易和传统碳交易的核心区别在于:传统模型是统一的碳价乘以排放量,阶梯模型则是给每个排放区间设定不同的碳价,排放量越大,超出基准配额的部分单价越高。这样做的意义在于更贴近当前全国碳市场的惩罚递增逻辑——配额是免费的,碳排放权是阶梯式购买的,超排越多买得越贵。

用数学语言描述是这样的。首先给虚拟电厂分配免费的碳排放配额 D_base,由机组历史发电量和配额系数决定。实际碳排放量来自两部分:燃气轮机燃烧天然气和氢气产生的排放(掺氢部分按比例折算减碳),以及从上级电网购电隐含的碳排放(按电网排放因子计算)。设实际排放量为 E_total,阶梯碳交易成本 C_co2 的计算函数可以写成:

  • 第1区间(0, E1]:碳价 λ1 = 0.25元/kg
  • 第2区间(E1, E2]:碳价 λ2 = 0.35元/kg
  • 第3区间(E2, E3]:碳价 λ3 = 0.5元/kg

这实际上是一个分段线性函数。在Yalmip里处理分段线性函数有几种做法:一种是用big-M法引入二进制变量表示区间选择,另一种是用Yalmip自带的yalmip('defines')或者pwf函数。我自己更推荐自己搭二进制变量,因为可控性更强,求解效率在大多数情况下也更好。

具体建模方式是这样的。将排放量划分成N个区间段,第i段的排放量记为Cseg_i,对应的单位碳价为λ_i。引入二进制变量z_i判断排放量是否落在第i段,约束条件为:

  • Σ Cseg_i = E_total
  • 对每个区间段赋值: 0 ≤ Cseg_i ≤ I_i,其中I_i是区间的长度上限
  • 连续段的逻辑约束:当 Cseg_i = I_i(达到该段上限)时,z_i = 1 并且下一段的 Cseg_{i+1} 可以大于0,这个逻辑可以用I_i×z_{i+1} ≤ Cseg_i ≤ I_i×z_i这种形式表达

碳交易成本就是 Σ(λ_i × Cseg_i) - λ_base × D_base,其中当排放量低于配额时,这个值为负,相当于虚拟电厂可以出售多余配额获得收益,这给了减排额外的经济激励。

2.3 P2G-CCS耦合过程的能量流与物质流

P2G(Power to Gas)分两步:第一步是电解槽,消耗电能和水,产出氢气和氧气,效率一般在60%-75%;第二步是甲烷化,氢气和CO₂在高温高压和催化剂作用下反应生成CH₄和水。这一步的关键在于需要CO₂作为原料,这就和CCS装置联系起来了。

CCS装置捕集燃气轮机尾气中的CO₂,捕集过程本身需要消耗大量电能,通常按捕集单位CO₂所需的电耗来建模,这个参数大约在0.2-0.4 kWh/kg CO₂之间。捕集下来的CO₂有两种去向:一是输入甲烷化反应器与H₂反应,二是压缩封存。

甲烷化反应的关系式:4H₂ + CO₂ → CH₄ + 2H₂O,也就是说要合成1标准的CH₄,需要4摩尔的H₂和1摩尔的CO₂。在实际建模中,我会简化为化学计量比乘以效率系数,甲烷化效率设为0.75左右,这样输入的H₂和CO₂能量并不完全转化为CH₄,有一部分损耗为热。

这个耦合过程的优化价值在于:当电价低(通常是夜间风电大发时段)时,虚拟电厂会倾向于购入低价电驱动电解槽制氢和CCS捕碳,这两个高耗能单元同时作为柔性负荷;当电价高(白天负荷高峰)时,储氢罐释放H₂掺入燃气轮机燃烧发电,CCS捕集的CO₂和电解槽产出的H₂在白天电价高时合成CH₄动作可能停止(因为电解槽停摆,没有H₂了)。这种“夜间储能、白天释能”的运行模式,在目标函数里是通过峰谷电价差直接反映出来的。

3. 约束条件体系与目标函数构建

3.1 电功率平衡与购售电约束

电功率平衡是整个调度模型的主约束,表达式为:

P_wt(t) + P_mt(t) + P_buy(t) + P_dis(t) = P_load(t) + P_el(t) + P_ccs(t) + P_ch(t) + P_sell(t)

其中P_dis和P_ch是蓄电池的放电和充电功率,P_el是电解槽功率,P_ccs是碳捕集装置功率。风电出力P_wt(t)在调度周期内是已知的预测曲线,实际模型中为防止过于理想化,通常加入弃风惩罚项,允许P_wt(t)在预测值以下运行。

购售电约束需要保证电网交互的合理性:同一时刻不能既买电又卖电,引入二进制变量的做法是:

  • 0 ≤ P_buy(t) ≤ B_buy(t) × P_grid_max
  • 0 ≤ P_sell(t) ≤ (1 - B_buy(t)) × P_sell_max

这样通过一个二进制变量B_buy(t)把买电和卖电互斥掉。但是在很多简化VPP模型中,由于上网电价通常低于购电价,最优解本身就不会出现同时买卖的情况(因为套利空间不存在),所以我个人在做这代码时倾向于不引入这个互斥变量,靠价格机制就可以自然满足,还能省掉几十个二进制变量,求解速度会快不少。

3.2 燃气轮机掺氢建模与碳排放计算

燃气轮机这块是最容易踩坑的地方。掺氢比例α(t) = [H₂消耗的热值] / [天然气和H₂总热值]。我建议以能量为单位来建模,不要用体积或者质量,因为天然气的热值约在36 MJ/Nm³,氢气的热值约在12.7 MJ/Nm³——用体积算的话,掺氢占比稍微一变,能量平衡就乱了。

燃气轮机的爬坡约束是:

  • -R_down ≤ P_mt(t) - P_mt(t-1) ≤ R_up

实际项目中我通常把爬坡约束限定在“等效天然气燃料能量输入”的范围内,也就是掺氢比例变化不能一下子太大,否则燃烧器响应不过来。这个细节很多论文不写,但如果你代入现场设备来看,氢气热值低、火焰传播速度快、燃烧稳定性窗口窄,掺氢比例跳变会导致回火或燃烧振荡。模型里可以这么约束:

  • |α(t) - α(t-1)| ≤ Δα_max

燃气轮机输出功率与燃料输入的关系是P_mt(t) = η_mt × F_fuel(t)×LHV_fuel(t),其中LHV会随掺氢比例变化。为了保持MILP特性,我会固定燃气轮机效率η_mt为常数,同时把“掺氢后总热值输入 = 天然气热值输入 + 氢气热值输入”作为燃料平衡约束,而碳排放则按天然气热值输入折算,掺氢部分不计碳排放(电解制氢如果用的是可再生电力,则被认为是绿氢,全生命周期碳排放为零)。

3.3 储能与储氢装置的动态约束

蓄电池的SOC(荷电状态)动态:

  • SOC(t+1) = SOC(t) + η_ch × P_ch(t) - P_dis(t)/η_dis
  • SOC_min ≤ SOC(t) ≤ SOC_max
  • SOC(0) = SOC(T)(周期末恢复到初值)

储氢罐的体积动态:

  • V_hst(t+1) = V_hst(t) + [H₂产量(t) - H₂去甲烷化(t) - H₂供燃气轮机(t) - H₂售出(t)] × Δt

储氢罐的建模容易忽略的约束是:氢气管网的压力等级。当储氢罐压力过高时,需要额外压缩耗电,这一块在模型里会体现为储氢成本,通常处理成储氢量的函数,但在简化模型中可以忽略。我实际做的代码里没有加压缩耗电项,因为加入了会使模型非线性程度提高,而对结果的影响在典型日仿真中小于2%,性价比不高。

3.4 目标函数:总运行成本最小化

目标函数我拆成了几个部分:

  • 购电成本减去售电收益:Σ(c_ele_buy(t) × P_buy(t) - c_ele_sell(t) × P_sell(t)) × Δt
  • 购气成本:Σ(c_gas × G_buy(t)) × Δt
  • 碳交易成本:按2.2节计算
  • 运维成本:各单元出力的线性系数乘出力
  • 弃风惩罚:Σ(c_wc × (P_wt_pred(t) - P_wt(t))) × Δt
  • 阶梯碳交易成本按上文公式

这里要特别说明运维成本处理。燃气轮机的运维成本按发电量计费,P2G电解槽按耗电量计费,CCS按捕集CO₂量计费,这三块在目标函数里都是线性项,对求解器非常友好。而碳交易成本因为分段函数的存在,已经通过二进制变量线性化了,整个模型就是标准的MILP,可以用Gurobi高效求解。整租下来,目标函数里唯一没有考虑的是启停成本——如果燃气轮机在调度周期内频繁启停,这个成本会比较大,但为了保持模型简洁,在有阶梯碳价约束的情况下,最优解一般不会出现太频繁的启停,这个简化是可以接受的。

4. Matlab实现与代码结构详解

4.1 代码文件的组织方式

整个项目我拆成了5个文件,这样以后换数据、换参数都不需要大改:

  • main.m:主文件,定义所有参数、调用Yalmip建模、求解、画图
  • data_parameters.m:所有设备参数集中存放,包括各类效率、电价、负荷曲线
  • build_constraints.m:约束构建函数,输入变量结构体和参数结构体,输出约束对象数组
  • solve_and_plot.m:求解、结果提取、绘图

这个拆分的好处是,跑第一次用例之后,后面想换场景(比如把阶梯碳价改成固定碳价做对比)只需要改data_parameters.m里的几行,不用动约束构建逻辑。

4.2 Yalmip变量定义与建模技巧

在Yalmip里定义变量的代码段是:

%% 定义优化变量 P_mt = sdpvar(1, T, 'full'); % 燃气轮机出力 P_buy = sdpvar(1, T, 'full'); % 购电功率 P_el = sdpvar(1, T, 'full'); % 电解槽功率 P_ccs = sdpvar(1, T, 'full'); % 碳捕集功率 P_wt = sdpvar(1, T, 'full'); % 风电实际出力有上限约束 V_hst = sdpvar(1, T+1, 'full'); % 储氢罐储氢量 alpha = sdpvar(1, T, 'full'); % 掺氢比例 % 二进制变量 u_mt = binvar(1, T, 'full'); % 燃气轮机启停状态 z_carbon = binvar(1, T, 'full'); % 阶梯碳交易区间选择(数值按边界处理)

Yalmip建模里有几个容易忽视的坑。第一个是sdpvar和binvar定义时,如果维度是(1,T),要用'full'选项或者明确指定,否则有些版本会默认为稀疏结构导致后续约束拼接报维度错误。第二个是约束拼接时用[ ]加;分隔,比如Constraints = [constraints1; constraints2]这种写法,在循环里累加约束时很容易因为分号使用不当导致约束维度错乱,我建议所有约束都用一个cell数组存,最后统一转成约束对象:

Constraints = {}; for t = 1:T Constraints{end+1} = P_mt(t) >= P_min * u_mt(t); Constraints{end+1} = P_mt(t) <= P_max * u_mt(t); end F = [Constraints{:}];

这样做的好处是后续调试的时候可以单独看某个约束的类型和大小,排查不可行性时可视化加约束特别方便。

4.3 阶梯碳交易约束的Yalmip实现

这部分是模型的核心,代码实现如下:

%% 阶梯碳交易约束 E_total = sum(gamma_mt * (F_ng(t) / LHV_ng + alpha(t) * F_h2(t) / LHV_h2) ... + gamma_grid * P_buy(t) * delta_t); % 总排放量kg D_base = 0.8 * sum(P_mt(t) * delta_t); % 免费配额 % 三个区间长度上限 I1 = 10000; I2 = 15000; I3 = 20000; % 区间选择 Cseg1 = sdpvar(1, T, 'full'); Cseg2 = sdpvar(1, T, 'full'); Cseg3 = sdpvar(1, T, 'full'); z1 = binvar(T, 1); z2 = binvar(T, 1); z3 = binvar(T, 1); for t = 1:T % 区间顺序约束 Constraints{end+1} = z1(t) + z2(t) + z3(t) == 1; Constraints{end+1} = Cseg1(t) <= I1 * z1(t); Constraints{end+1} = Cseg2(t) <= I2 * z2(t); Constraints{end+1} = Cseg3(t) <= I3 * z3(t); Constraints{end+1} = Cseg2(t) <= I2 * z1(t) + I2 * z2(t); % 第二段只有前一段满时才启用 Constraints{end+1} = Cseg3(t) <= I3 * z1(t) + I3 * z2(t); % 第三段只有前两段满时才启用 Constraints{end+1} = Cseg1(t) + Cseg2(t) + Cseg3(t) == E_total(t) - D_base(t); Constraints{end+1} = Cseg1(t) >= 0; Constraints{end+1} = Cseg2(t) >= 0; Constraints{end+1} = Cseg3(t) >= 0; end C_co2 = sum(0.25 * Cseg1(t) + 0.35 * Cseg2(t) + 0.5 * Cseg3(t));

这个实现的坑在于区间边界的处理。如果E_total略小于I1和I2的和,但是Cseg3已经分配了非零值,由于约束Cseg2(t) <= I2 * z1 + I2 * z2,Cseg3(t) <= I3 * z1 + I3 * z2,只要z2或者z3被选为1,就会强制Cseg1或Cseg2必须存在非零值,这可能导致实际排放量分布不均匀。我踩过这个坑之后,改用了一种更稳妥的方式:直接把排放量看成整体,做分段函数的y值计算,不拆成三个区间段的累加。用pwf或者自己写逻辑判断就行了。下面这种写法是我后来用的:

E_exc = max(0, E_total - D_base); % 超出配额的部分 % 使用分段线性函数计算碳成本,定义三个断点 C_co2 = pwf(E_exc, [0, E1, E2, E3], [0, lambda1*E1, lambda1*E1 + lambda2*(E2-E1), ... lambda1*E1 + lambda2*(E2-E1) + lambda3*(E3-E2)]);

这里用了Yalmip自带的pwf分段函数定义,底层是自动引入二进制变量的,代码简洁很多,而且不会出现边界问题。对于新手来说,节省了大量纠结二进制变量的时间,推荐使用。

4.4 求解器设置与结果可视化

求解器调用我用的Gurobi,如果没装可以用CPLEX替代,求解器接口都是兼容的。下面是一段求解配置:

options = sdpsettings('solver', 'gurobi', 'verbose', 2, ... 'gurobi.MIPGap', 0.01, ... 'gurobi.TimeLimit', 300, ... 'gurobi.NumericFocus', 2); sol = optimize(F, Objective, options);

这里特别要注意NumericFocus参数。因为阶梯碳交易和掺氢模型中,排放量、热值这些参数的量级差异很大,有的达到几百MW,有的只有零点几,Gurobi有时候会因为数值问题报“Numerical trouble”的警告。把NumericFocus调成2或3可以让求解器更强地预处理模型,但相应地会增加求解时间。我记得在跑24小时、96个时段、含数百个二进制变量的模型时,默认参数下Gurobi可能在30秒内解决,调整NumericFocus到3后偶尔会慢到2分钟,但数值稳定性好很多。实际应用中我一般先用默认参数跑一遍,如果报数值警告再调整。

结果可视化部分我习惯画四张图:第一张是电功率平衡堆叠图(风电、燃气轮机、购电、放电加一起对比负荷、电解槽、CCS耗电),第二张是天然气/氢气流量图(电解槽产氢、去甲烷化的氢、掺氢燃烧的氢、储氢罐储量的变化曲线),第三张是碳交易情况(原始排放量、配额、实际付费区间),第四张是掺氢比例和SOC联合变化图。画图用Matlab自带的plot和area函数就可以,重点是数据提取时要正确从Yalmip结果里读取每个变量的数值,方法是对所有sdpvar使用value()函数,比如P_mt_opt = value(P_mt),直接索引数组。

5. 典型日仿真结果与灵敏度分析

5.1 基准场景下的调度结果

我用的测试数据是典型的风电大发日:低负荷时段(23:00-5:00)风电出力高,电价低;白天的10:00-14:00和18:00-21:00两个峰荷时段,风电出力一般,电价高,负荷也高。P2G和CCS的设备容量设得比较充裕,电解槽额定功率200kW,CCS额定捕集速率50kg/h(换算成耗电功率约12.5kW),燃气轮机装机150kW,储氢罐容量1500m³。

基准场景(阶梯碳交易 + P2G-CCS耦合 + 掺氢比例上限20%)跑下来的典型调度结果:

  • 夜间低谷电价时段(23:00-5:00):燃气轮机停机,风电满发,电解槽满负荷运行,产氢量大约120Nm³/h,一部分存入储氢罐,一部分送甲烷化。CCS装置此时停机,因为没有燃气轮机在排烟。
  • 早高峰(6:00-9:00):燃气轮机启动,初期掺氢比例较高(因为储氢罐有存量),以较低碳成本满足负荷。CCS装置同步启动,捕集的CO₂在白天电解槽停歇时送入甲烷化反应器消耗存量H₂。
  • 午间低谷(12:00-14:00):光伏/风电有波动,电价回落,燃气轮机降出力,电解槽再启动补充储氢。
  • 晚高峰(18:00-21:00):燃气轮机满发,掺氢比例被设置到上限20%,因为此时碳价高,用绿氢替代天然气能显著降低碳交易成本。CCS捕集的CO₂量达到峰值。

这套运行逻辑基本符合预期:P2G工作在电价低谷,掺氢燃烧工作在电价高峰,CCS跟随燃气轮机启停,同时利用低谷电在燃气轮机停机时也不停。

5.2 场景对比:有无阶梯碳交易的差异

我做了三组对比场景:

  • 场景A:传统的固定碳价模型(碳价取0.3元/kg)
  • 场景B:线性递增碳价模型
  • 场景C:阶梯碳交易模型(三档碳价)

三组模型的目标函数结构完全一样,除了碳交易成本部分。结果差异主要体现在碳排放总量上:场景A的碳排放量最大,总排放约3.2吨;场景B减少了4.5%;场景C减少了7.8%。原因很简单:固定碳价的边际减排成本恒定,模型在比较减排成本和其他成本时,只要减排成本高于碳价就不减排;而阶梯碳价在超过第二档后碳价跳到0.35元/kg,超过第三档跳到0.5元/kg,减排的边际收益变大,模型自然更倾向于用H₂替代天然气、用CCS捕碳,即使这会提高购电和运维成本。

但从总成本来看,场景C的总成本反而略高于场景A(高约3%),因为碳减排是“付费”实现的。这个结果要说清楚:阶梯碳交易的目的是抑制高排放,而不是降低运营成本,考核指标应该是碳排放量而不是总成本。做论文对比的时候不要只看成本。

5.3 掺氢比例上限的灵敏度分析

把掺氢比例上限从0%(纯天然气)逐步提高到10%、20%、30%,观察总碳排放和总成本的变化趋势,结果比较有意思:

  • 掺氢比例从0%提到10%,碳排放下降约5%,成本基本不变(因为氢气来自夜间多余风电,生产成本低)
  • 提到20%,碳排放继续下降约4%,但成本开始上升(储氢罐投资虽不计入运行成本,但压缩机耗电增加)
  • 提到30%,碳排放只下降约2%,成本上升明显

原因是掺氢比例提高后,燃气轮机的等效热值降低,要保持同样出力需要更大的燃料体积流量,而且氢气火焰温度高、易回火,模型虽然没做燃烧稳定性约束,但副产水蒸气的热损失变大,导致燃气轮机效率下降——在我的模型里用固定效率处理,实际上效率损失体现为压力损失项,最终反映到燃料消耗上升。所以说掺氢比例设置20%-30%是工程上的甜蜜点,低于10%减排效果不明显,高于30%边际收益递减。这也是很多文献把上限设定在20%的原因。

5.4 碳价区间宽度的影响

阶梯碳交易的区间宽度直接影响减排力度。我尝试了两种区间设置:窄区间(I1=8吨,I2=6吨,I3=6吨,总共20吨基准量)和宽区间(I1=10吨,I2=10吨,I3=10吨)。同样运行一天,窄区间的碳排放量比宽区间低约3%,但碳成本高了12%。这说明区间划分越窄,等于碳价“预升级”的阈值越低,惩罚越早进入高价位区间,但代价是成本更敏感。做碳市场机制设计的人可以拿这个模型去调节区间宽度和碳价水平,看虚拟电厂的响应曲线。

6. 常见问题与调试经验

6.1 模型不可行怎么排查

跑MILP最痛苦的事就是求解器直接告诉你“infeasible problem”。这种情况在VPP模型里太常见了,尤其是加了碳约束、储能初值、周期末值这些强制约束之后。我排查的顺序是固定的:

第一步要检查的是电功率平衡。把一天24小时的负荷、风电预测、燃气轮机上下限、购售电上限都打印出来,手动计算一个时刻的功率净值,看是否始终在可行范围内。最容易出错的地方是电解槽和CCS同时开启时峰值功率太大,导致可分配功率为负。做法是把P_el和P_ccs这两个单元在目标函数里加上权重,或者把它们建模为“可中断负荷”,即允许在极端情况下降载到最大功率的30%,这在工程上也说得通。

第二步是检查储氢罐的初值和末值约束。如果设置了SOC(0)=SOC(T),但全天内氢气的净产量是负的(消耗大于产出),那模型一定不可行。解决办法是把氢气的产量上限调大,或者放宽终值约束为SOC(T)≥SOC_min即可。

第三步是检查阶梯碳交易约束的边界条件。pwf函数在某些Yalmip版本里,如果断点值设置得不合适(比如断点是负数),也会导致模型不可行。把E_exc的取值画出来看看是否在断点范围内。

6.2 求解速度慢怎么办

24时段模型,几百个约束,几百个二进制变量,Gurobi一般几秒就解决了。如果连续变量变成96时段(15分钟粒度),二进制变量翻四倍,求解时间可能暴增到几百秒。我试过几个提速方法,效果从好到差排列:

  1. 提供好的初始可行解。先用固定碳价简化模型求解,把结果(燃气轮机启停状态、储氢罐SOC曲线)作为MIP start传给Gurobi,通常能把求解时间缩短30%-50%。
  2. 减少不必要的二进制变量。比如购售电互斥变量,如果电价时序合理,基本不会出现同时买卖的次优解,去掉后求解速度提升明显。
  3. 约束预求解。手动把一些强约束结合起来,比如燃气轮机的最小运行时间约束(如果加了的话)去掉,改为惩罚项——阶梯碳价本身已经限制了频繁启停的经济性。

6.3 数值量纲错误

这个坑几乎是所有Matlab优化初学者的噩梦。我的建议是:所有参数统一使用kW、kWh、kg、h、元这些基本单位,不要混用MW、MWh。比如天然气热值LHV_ng是36 MJ/Nm³,换算成kWh/Nm³就是10 kWh/Nm³(因为3.6MJ=1kWh),氢气热值换算后是3.54 kWh/Nm³。如果其中一处用了MJ、另一处用了kWh,最后的目标函数值会偏差好几个数量级,而Gurobi不会报错,只会输出一个完全不符合物理意义的结果。

我习惯在参数定义后加一个“量纲校验模块”,自动检查所有变量的取值上下限是否符合物理常识,比如出力不能为负、储氢量不能超过罐容的105%等。这些校验能在模型求解前把参数错误暴露出来。

6.4 结果与预期不符的调试

有一次跑出来储氢罐一天都没怎么用,我一开始以为是约束写错了,查了半天发现是电解槽效率设得太低(40%),制氢成本远高于直接购电加购气,所以模型宁愿不制氢。把效率改成60%之后结果就正常了。这种“不符合预期”的结果,很多时候不是程序错了,而是参数组合下模型的经济性判断就是这么合理。遇到这种情况建议做灵敏度分析,把关键参数(电解槽效率、碳价、购电电价)分别往有利于P2G的方向调,看看结果是否按预期方向移动,以此判断模型逻辑是否正常。

另一个经典问题是掺氢比例约束只做了上限没做下限,导致模型在最优化时永远选择不掺氢(因为氢气成本高)。这其实是正常的,因为无约束优化情况下,只要碳价不是足够高,模型自然倾向于用便宜的天然气。做对比实验时要设置一个场景是“最低掺氢比例10%”,另一个是“不限制最低”——这样才可以看出碳价对掺氢行为的激励作用。

6.5 Matlab编码和工具箱兼容性

这代码依赖Yalmip、Gurobi或CPLEX,跑之前必须确认工具箱加到了Matlab路径中,而且版本兼容。个别情况下Yalmip的版本过低,pwf函数不被支持,那就需要退回用big-M法手写分段函数。

另外中文注释在Matlab老版本(R2023之前)里有时候会乱码,我建议所有源码注释统一用英文,或者中文注释前加%并在文件开头设置好UTF-8编码。R2023版本开始中文兼容性好了一些,但为了省心,团队协作的代码里我规定全部英文注释,只有画图标签和文档说明用中文。

7. 实际运行效果与进一步扩展空间

这套代码完整跑通一次24小时、96时段的仿真,在普通笔记本上,Gurobi求解时间在几秒到几十秒之间,取决于二进制变量数量和你设置的MIPGap。我实测过一次96时段、含“购售电互斥+燃气轮机启停+阶梯碳区间选择”三重二进制变量的模型,用了约300秒,MIPGap稳定在1%以内——对论文仿真来说够用了。

从我个人的角度来看,这类项目真正吃力的是模型部分而不是代码本身。把P2G-CCS耦合、掺氢燃烧、阶梯碳交易这些技术的物理过程和经济学含义掰清楚,Yalmip的代码只是把数学公式翻译成求解器能懂的语言。很多新手一上来就想写代码,结果卡在“不知道怎么把碳交易写成约束”上,我的建议是先拿纸笔把这个虚拟电厂的能量流、碳流图完整画出来,标注每种设备的输入输出和效率,再动笔写代码。这篇文章里的模型框架可以复用到很多研究方向:比如把确定性调度扩展成鲁棒优化或随机优化(处理风电预测误差),或者加入需求响应项,再或者把阶梯碳交易改成碳税、碳配额拍卖等不同机制做对比。框架都是同一个,往里面替换模块就行。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询