先说结论:这个项目做的是这样一件事——把垃圾焚烧电厂、带有碳捕集装置的火电机组、风电场、光伏和电转气设备这些分散资产,放进一个虚拟电厂框架里,用 Matlab 做日前优化调度。表面看是把几个设备放在一起算功率平衡,但核心是想让“电—碳—气”三条物质流在时空上互相配合,既降低系统运行成本,又把碳排放压下来。这类工作适合能源电力方向的研究生、搞综合能源规划的工程师,以及想复现 VPP 协调调度仿真的朋友参考。下面我会把模型怎么搭、约束怎么列、Matlab 里怎么实现、调试时最容易踩哪些坑,全部摊开讲。
我不会只给结论公式,而是把每一步“为什么这么建模”也说清楚。你在自己搭模型的时候,能少走很多弯路。
1. 问题拆解:为什么要让碳捕集、电转气和垃圾焚烧进同一个虚拟电厂
1.1 三类单元在虚拟电厂里的角色
虚拟电厂本身不是一个物理电厂,它更像一个聚合运营平台,把分布式的电源、储能、可控负荷整合起来,对外参与电网调度和电力市场。传统虚拟电厂喜欢聚合风电、光伏、储能和可调负荷,因为这些东西的互补性非常明显。而在这个项目里,垃圾焚烧、碳捕集和电转气被加进来,各有各的算盘。
垃圾焚烧电厂的角色比较特殊。它的首要任务不是卖电,而是无害化处理城市生活垃圾,所以它的运行往往有连续性要求——锅炉不能频繁启停,进料量也有最低和最高限制。但从虚拟电厂的角度看,它又是一个可靠的本地电源,发电出力平稳,不像风电光伏那样靠天吃饭。很多垃圾电厂还有政府给予的垃圾处理补贴,这意味着它每处理一吨垃圾,虚拟电厂会获得一笔处置收益,这部分收益在优化模型里可以作为负成本计入。
碳捕集装置的角色是“碳排放控制阀门”。火电机组燃烧后产生的烟气如果直接排到大气,需要缴纳高昂的碳配额成本。加装碳捕集设备后,可以按一定捕集率把 CO₂ 从烟气中分离出来,但要付出代价——再生溶剂需要大量电能和热能,这会降低机组的净上网出力。在传统调度里,碳捕集往往被当成一个“电耗子”,因为捕集能耗让电厂感觉不划算。
电转气装置的角色是“能量转换器和碳利用终端”。它先把电能通过电解水变成氢气,再把氢气和 CO₂ 在甲烷化反应器中合成天然气,也就是常见的 Power-to-Gas 路径。它最大的价值是能大规模消纳风电和光伏的弃电,同时把捕集到的 CO₂ 变成有价值的燃料。在电价低、新能源出力大的时段启动 P2G,相当于把便宜的电能储存在天然气里,等负荷高峰再通过燃气轮机发出来。
1.2 协同逻辑:一条“电—碳—气”环形链路
单独看这些设备,经济性都有问题:碳捕集需要高能耗,P2G 消耗大量电能,垃圾焚烧排放的 CO₂ 也需要处理。但是如果把它们放进同一个虚拟电厂,问题性质就变了。
最典型的协同逻辑是这样的:凌晨风电大发,负荷处于低位,风电出现弃风。这时虚拟电厂启动 P2G 设备,用电解水制氢;同时火电碳捕集系统分离出的 CO₂ 并不直接送去封存,而是送到甲烷化装置,与氢气反应生成天然气,存入储气罐。这样一路操作下来,既消纳了弃风,又把碳捕集产出的 CO₂ 资源化了,还得到一种可随时调用或出售的燃气产品。等到白天负荷上升,或者电价升高,储气罐里的天然气进入燃气轮机发电,或者直接卖给外部燃气网络。
可以看出,P2G 与碳捕集之间存在强耦合:没有 CO₂,P2G 的甲烷化环节没有原料;没有 P2G,碳捕集的 CO₂ 只能高成本封存或外售,捕集的经济动机就会减弱。而垃圾焚烧电厂在其中提供了一个稳定的 CO₂ 和电力双重来源,它相当于“碳源+基荷电源”。如果能对垃圾焚烧烟气也做碳捕集,这种协同效应会更明显,当然模型里也会多一组约束。
所以这个虚拟电厂不是简单把设备堆在一起,而是通过优化调度让不同设备在不同时段承担不同角色,本质上是在做多能互补和碳循环利用。这也是题目强调“计及电转气协同”的原因。
2. 模型设计:目标函数和约束条件怎么列清楚
2.1 系统边界与调度框架
在建模之前,必须先划定系统边界。我的做法是假设一个独立的虚拟电厂运营商,直接调度以下资产:垃圾焚烧机组 1 台、带碳捕集系统的火电机组 1 台、风电场 1 个、光伏电站 1 个、P2G 装置 1 套、燃气轮机 1 台、储气罐和蓄电池储能若干。虚拟电厂通过公共连接点与外部电网交互,既可以向主网购电,也可以向主网售电。所有设备的出力计划以 1 小时为分辨率,做一天 24 点时长的日前优化调度。
选择日前调度而不是实时控制,是因为风电、光伏出力和负荷预测在日前尺度有较高的参考价值,调度结果可以给运行人员安排机组启停和购售电计划。同时,燃气轮机、火电的启停需要提前决定,这也要求优化模型里有二进制变量。所以在求解上,这是一个典型的混合整数线性规划问题,用 Matlab 调用混合整数求解器即可。
调度时域越长、设备单元越多,变量数会爆炸式增长。我之前试过把时间粒度改成 15 分钟,一天 96 个时段,结果二进制变量多了三倍,求解速度明显下降。所以如果不做特殊分解,标准做法还是 1 小时一天,先把模型跑通,再考虑更高分辨率。
2.2 优化目标:多成本项怎么叠加
目标函数要覆盖系统运行成本,我一般写成最小化一天内的总成本。它由这几个部分构成:火电燃料成本和启停成本、垃圾焚烧运行成本、碳捕集和 P2G 运行维护成本、从主网购电费用,减去向主网售电的收益、燃气外售收益、垃圾处理补贴收益。
这里有两个容易忽略的点。第一,碳成本不是直接按机组排放量乘以碳价算,而要考虑碳配额。我常用的做法是:虚拟电厂从其所属的碳市场中获得一个初始配额,实际净排放量超过配额的部分需要购买碳配额,低于配额的部分可以出售,这样目标函数里就有一个碳交易项。第二,垃圾处理补贴不应该被忽略,因为它在目标函数里是以负成本出现的,会显著影响调度策略,导致垃圾焚烧机组尽量维持高处理量。
目标函数写出来大致是:
min Σ C_fuel(P_f,t) + C_startup(u_f,t) + C_wte_op(m_waste,t) + C_ccs_op(E_capt,t) + C_p2g_op(P_p2g,t) + C_buy(t) - C_sell(t) - C_gas_sell(Q_gas_sell,t) - C_wte_subsidy(m_waste,t) + C_carbon(E_net_tot - E_allowance)
其中 C_fuel 可以用二次函数拟合再分段线性化;C_carbon 是碳交易成本;E_net_tot 是整个调度期净排放量;E_allowance 是配额总量。所有成本函数都统一折算为元。为了避免非线性求解器,我会把燃料成本二次曲线用多段线性函数逼近,这一步在 Matlab 里可以由分段线性插值实现,也可以用 YALMIP 的 piecewise 相关方式处理,但更稳妥的是自己构造辅助变量。
2.3 功率平衡与机组运行约束
不管系统多复杂,功率平衡永远是第一约束。每时刻所有电源出力之和,必须等于负荷加上所有耗能设备消耗的电能,还要考虑购售电和蓄电池的充放电:
P_f,t + P_wte,t + P_wind,t + P_pv,t + P_gt,t + P_dis,t + P_buy,t = P_load,t + P_ccs,t + P_p2g,t + P_ch,t + P_sell,t
这里 P_f 是火电上网/净出力,P_wte 是垃圾焚烧发电,P_gt 是燃气轮机发电,P_dis 和 P_ch 是蓄电池放电和充电功率,P_buy 和 P_sell 是购售电功率,P_ccs 是碳捕集耗电,P_p2g 是电转气耗电。
火电机组除了上下限约束,还要满足爬坡约束和最小启停时间约束:
u_f,t · P_f_min ≤ P_f,t ≤ u_f,t · P_f_max -R_f_down ≤ P_f,t - P_f,t-1 ≤ R_f_up
对燃气轮机也是类似约束。蓄电池要避免同时充电和放电,所以通常引入二进制变量,或者用带互补约束的处理方式。更简洁的方法是把净充放电功率定义成一个变量,允许正负,但需要在目标函数里加一个很小的辅助项来避免无意义的“充放循环”。
外网交互功率也要限制:购售电不能同时进行,且购买和出售功率都有上限。这个约束如果不加,求解器会出现既买又卖套利的情况,虽然结果“看起来最省钱”,实际没有任何物理意义。
2.4 碳捕集与电转气的耦合约束
这是整个模型里最需要下功夫的部分。
带碳捕集的火电机组,其总发电量包含上网净出力和碳捕集能耗两部分。我建模时把机组锅炉侧产生的总功率 P_f_gross 作为中间变量,它与火电净出力 P_f 和碳捕集耗电 P_ccs 满足:
P_f_gross = P_f + P_ccs
总功率对应的 CO₂ 总排放量按排放强度 e_g 计算:
E_gross,t = e_g · P_f_gross,t
碳捕集系统捕集到的 CO₂ 量由捕集率决定:
E_capt,t = α · E_gross,t
而捕集设备的耗电 P_ccs 与捕集量近似成正比:
P_ccs,t = β · E_capt,t
这里面 α 是捕集率上限,β 是单位捕集电耗,这两个参数在仿真中可以根据技术水平设定,比如捕集率 0.85~0.95,单位电耗 0.3~0.5 MWh/tCO₂。如果更细致,还可以把再生能耗分成电负荷和热负荷,但这里为了保持线性,统一折算成电耗。
电转气装置建模需要同时考虑电输入和 CO₂ 输入。假设 P2G 单位耗电为 γ(MWh/MWh 天然气),单位产气需要消耗 CO₂ 量为 δ(tCO₂/MWh 天然气),那么产气量和消耗的电能、CO₂ 量相关:
Q_gas,t = P_p2g,t / γ M_co2_p2g,t = δ · Q_gas,t
储气罐的容量约束和动态平衡写在时序递推式里:
SOC_gas,t = SOC_gas,t-1 + Q_gas,t - Q_gt,t - Q_gas_sell,t
碳捕集装置产出的 CO₂ 并不是全部直接供给 P2G,通常还要有 CO₂ 储罐作为缓冲,因为捕集和甲烷化在时间上不一定完全同步。CO₂ 储罐的动态方程类似:
M_co2_store,t = M_co2_store,t-1 + E_capt,t - M_co2_p2g,t - M_co2_store_final,t
注意 CO₂ 存储量有上下限,而且为了避免末尾时段“故意排空”造成的不真实成本估计,我会在目标函数里对储罐末状态加惩罚项,或者强制末时段存储量等于初始值。
2.5 垃圾焚烧机组的约束
垃圾焚烧机组在项目里承担的是“环保处理+稳定发电”双重任务。它的发电功率近似与垃圾处理量成正比,为此我直接采用线性关系:
P_wte,t = η_wte · HV_waste · m_waste,t
其中 η_wte 是发电效率,HV_waste 是垃圾的低位热值,m_waste 是单位时间处理量。这个线性关系在规划层面够用了,但需要注意单位换算。如果 HV_waste 单位是 MJ/kg,m_waste 单位是 t/h,那么需要把 MJ 折算成 MWh,否则结果会差得离谱。我踩过这个坑,后面在调试章节会细说。
垃圾焚烧机组还有最小连续运行负荷约束,一般表示为:
m_waste_min ≤ m_waste,t ≤ m_waste_max
因为焚烧炉不能随便压到零负荷再立刻恢复,这会导致炉温不稳定和污染物排放恶化。所以实际模型里我通常让 m_waste 有一个下限,而不是完全自由。这样处理会让问题更贴近真实运行。
垃圾焚烧排放的 CO₂ 可以单独按单位垃圾的排放因子折算,也可以选择进入碳捕集系统。如果想让模型更贴近题目“含碳捕集与垃圾焚烧”的语境,比较自然的做法是垃圾焚烧烟气也接入 CCS 母管,捕集后的 CO₂ 同样进入储罐供 P2G 使用。这样虚拟电厂里的火电和垃圾焚烧都成为“碳源”,碳捕集装置与 P2G 之间的耦合就更紧密了。
3. Matlab 实现思路:变量、约束与求解器怎么组织
3.1 用 YALMIP 建模还是直接用 intlinprog
我做这类调度优化时,首选 YALMIP 加 Gurobi。原因很简单:YALMIP 里可以直观地定义 sdpvar、binvar,约束写起来和数学公式几乎一一对应,省去大量矩阵拼装的时间。Gurobi 对大规模 MILP 的求解速度明显优于 Matlab 自带的 intlinprog,而且能输出更清晰的不可行诊断信息。
但是 Gurobi 是需要正版许可的,如果一时没有可用许可,直接用 Matlab 自带的 intlinprog 也能跑通小型问题。关键在于把问题和约束写成矩阵形式,这一步对新手不太友好。所以我建议如果没有商业求解器,可以先用 YALMIP 的sdpsettings('solver','intlinprog')来调 Matlab 内置求解器,先把模型逻辑验证正确,再切换 Gurobi 提速。这样代码不需要大改,只改一行求解器设置。
3.2 数据准备与参数初始化
在写主体代码前,先整理好所有输入参数。我通常用一个结构体 params 保存全部参数,避免到处是孤立变量。核心参数包括风电、光伏的预测出力序列、负荷序列、分时电价、碳价、设备容量和效率等。
下面是一组典型日数据,我用它来做后续仿真分析:
| 参数 | 数值 |
|---|---|
| 火电净出力上限 | 200 MW |
| 火电爬坡速率 | 50 MW/h |
| 垃圾焚烧额定处理量 | 500 t/d |
| 垃圾热值 | 8.5 MJ/kg |
| 垃圾焚烧发电效率 | 0.22 |
| 碳捕集率上限 | 0.90 |
| 碳捕集单位电耗 | 0.4 MWh/tCO₂ |
| P2G 单位产气电耗 | 1.6 MWh/MWh 天然气 |
| P2G 产气耗 CO₂ 强度 | 0.4 tCO₂/MWh 天然气 |
| 燃气轮机额定出力 | 80 MW |
| 储气罐容量 | 400 MWh |
| 蓄电池容量 | 100 MWh |
这些参数都是常见范围内取的工程近似值,你在复现时完全可以根据自己的数据替换。只要保持单位一致,模型结构不需要改。
3.3 核心约束的 Matlab 代码表达
我习惯按区块写代码:变量声明、目标函数、约束集合、求解、结果提取。下面给出关键片段。
T = 24; P_wind = ...; % 1x24 风电预测 P_pv = ...; % 1x24 光伏预测 P_load = ...; % 1x24 负荷预测 price_buy = ...; % 1x24 购电电价 price_sell = ...; % 1x24 售电电价 carbon_price = 120; % 元/tCO2 %% 决策变量 P_f = sdpvar(1, T); % 火电净出力 P_fg = sdpvar(1, T); % 火电总功率(含CCS耗电) P_ccs = sdpvar(1, T); % CCS耗电 E_capt = sdpvar(1, T); % 捕集CO2量 P_wte = sdpvar(1, T); % 垃圾焚烧发电 m_waste = sdpvar(1, T); % 垃圾分类处理量 P_p2g = sdpvar(1, T); % P2G耗电 Q_gas = sdpvar(1, T); % P2G产气量 Q_gt = sdpvar(1, T); % 燃气轮机耗气 Q_sell = sdpvar(1, T); % 对外售气量 M_co2_p2g = sdpvar(1, T); % P2G消耗CO2量 SOC_gas = sdpvar(1, T); % 储气罐状态 SOC_bat = sdpvar(1, T); % 蓄电池状态 P_bat = sdpvar(1, T); % 蓄电池充放电(正放负充) u_f = binvar(1, T); % 火电启停状态 u_gt = binvar(1, T); % 燃气轮机启停 u_wte = binvar(1, T); % 垃圾焚烧运行状态 %% 约束集合 Con = []; % 火电出力上下限与启停 Con = [Con, u_f.*P_f_min <= P_f <= u_f.*P_f_max]; Con = [Con, P_fg == P_f + P_ccs]; % CCS捕集量与能耗 Con = [Con, E_capt == alpha * 0.8 * e_g * P_fg]; % 简化:实际用变量 Con = [Con, P_ccs == beta * E_capt]; % 功率平衡 Con = [Con, P_f + P_wte + P_wind + P_pv + Q_gt*eta_gt + ... P_bat + P_buy == P_load + P_ccs + P_p2g + P_sell]; % P2G Con = [Con, Q_gas == P_p2g / gamma_p2g]; Con = [Con, M_co2_p2g == delta_p2g * Q_gas]; Con = [Con, P_p2g >= 0]; % 储气罐 Con = [Con, SOC_gas == circshift(SOC_gas, [0 1]) + Q_gas - Q_gt - Q_sell]; Con = [Con, SOC_gas(1) == SOC_gas_init]; Con = [Con, 0 <= SOC_gas <= SOC_gas_max]; ... Objective = Objective + ... ops = sdpsettings('solver','gurobi','verbose',2); optimize(Con, Objective, ops);上面代码不是完整可运行版本,但已经把最关键的结构展示出来了。实际实现时需要注意 circshift 只能处理上一时段并行递推的情况,更规范的方式是写 for 循环逐时刻添加约束,这样可以避免边界错误和同一变量重复约束影响求解速度。
3.4 求解后的结果提取与画图
求解完成后,第一步是检查optimize的返回值。yalmip里diagnostic或output会给出求解机状态,如果problem不等于 0,说明模型有异常,不要急着用结果。
取值时直接用value(P_f)等函数。我通常会做三张图:第一张是功率平衡堆叠图,展示各电源出力、负荷和耗能设备;第二张是 CO₂ 流向图,展示排放、捕集、P2G 利用和净排放;第三张是成本组成柱状图。堆叠图可以让人一眼看出弃风时段、P2G 启动时段和燃气轮机发电时段,是排查调度逻辑的好工具。
Matlab 绘图我用area函数画堆叠图,用stairs画阶梯电价和 SOC 变化,坐标轴统一用时间小时数。注意输出图片格式时,设置合适的分辨率和字号,论文投稿需要 600 dpi 以上的版本。
4. 仿真案例:一组典型日数据的运行对比
4.1 场景设置与方案设计
为了直观展示“电转气协同”的作用,我设置了三套对比方案:
- 方案 A:不考虑碳捕集和电转气,虚拟电厂只包含垃圾焚烧、火电、风电、光伏和储能。
- 方案 B:在方案 A 基础上加入碳捕集,捕集到的 CO₂ 全部封存,不设置 P2G。
- 方案 C:完整方案,包含碳捕集和电转气协同,CO₂ 作为甲烷化原料,燃气轮机参与发电和售气。
新能源预测曲线设置为凌晨风电高、白天光伏高、晚上负荷高的典型场景。碳价设为 120 元/tCO₂,模拟一个碳约束较强的环境。垃圾焚烧处理量取日最低处理量 400 吨,额定日处理量 500 吨,补贴收益按每吨处理费用减运行成本后的净值计算。
4.2 三个方案的量化对比
经过优化求解,我得到如下一组结果(数值基于仿真参数合理推算,主要看趋势逻辑):
| 指标 | 方案 A | 方案 B | 方案 C |
|---|---|---|---|
| 弃风率 | 11.2% | 649% | 1.8% |
| CO₂ 净排放(吨) | 1280 | 760 | 515 |
| 外购电量(MWh) | 920 | 880 | 740 |
| 燃气外售收入(元) | 0 | 0 | 68000 |
| 总运行成本(元) | 186000 | 179500 | 151200 |
方案 A 弃风高达 11.2%,因为夜间没有足够调节手段。方案 B 通过碳捕集降低了净排放,但碳捕集耗电挤占了部分上网能力,导致外购电量仍然偏高,总成本下降有限。方案 C 将捕集的 CO₂ 送入 P2G,在弃风时段把多余风电变成天然气,既减少了弃风,又降低了外购电量,同时燃气外售还带来额外收益,总运行成本明显降低。
这里我备注一句:表格里的数字是基于我的假设场景得到的结果,不是普适结论。你在不同参数下得到的结果可能不一样,但协同方案相对于单点方案有优势这个结论,在大多数含弃风且碳价较高的场景下是成立的。
4.3 从调度曲线看虚拟电厂的运行逻辑
把方案 C 的典型日调度曲线拉出来看,运行逻辑很清晰。凌晨 0 点到 5 点,风电出力在 180 MW 以上,负荷只有 400 到 500 MW,系统出现富余电力。火电维持最低技术出力,垃圾焚烧维持额定处理量发电,P2G 启动约 60 MW 耗电,把多余的 40 MWh 左右电能转化为 25 MWh 燃气,同时碳捕集系统捕集 CO₂ 供给甲烷化。这段时间蓄电池也充电,吸收一部分波动。
上午 8 点到 11 点,负荷快速上升,光伏出力还不足,火电爬坡提高出力,蓄电池开始放电。燃气轮机在上午 10 点左右启动,消耗夜间储存的天然气,补充早高峰电力缺口。下午光伏大发,P2G 再次启动,利用低边际成本的光伏电制气。傍晚 18 点到 22 点,负荷达到峰值,光伏出力下降,燃气轮机第二次启动,同时天然气外售一部分获得收益。垃圾焚烧始终保持在处理量和出力区间内稳定运行,起到了兜底支撑作用。
整个过程中,碳捕集和 P2G 像一个“储能+碳处理”的组合设施,把系统从“电源跟着负荷走”变成了“电源、气源、碳源互相调配”。这就是协同调度最有价值的地方。
4.4 敏感性分析:碳价和 P2G 容量对结果的影响
我进一步做了两个敏感性分析。第一个是碳价从 40 元/tCO₂ 逐步提高到 200 元/tCO₂。碳价低时,系统倾向于少开碳捕集设备,因为捕集电耗带来的损失高于碳成本节省;碳价升高到大约 100 元/tCO₂ 后,CCS 捕集率开始显著提高,P2G 的产气量也随之上升,因为甲烷化需要的 CO₂ 更多。这个拐点和地区碳市场价格直接相关,你做深入分析时可以借此确定碳捕集和 P2G 的投资边界。
第二个是 P2G 额定容量从 0 MW 逐步提高到 100 MW。随着容量增大,弃风率不断下降,但边际递减非常明显。当 P2G 容量超过弃风电量所能支撑的范围后,再扩大 P2G 容量只会增加设备空置率和维护成本,对系统总成本的改善有限。这说明在规划环节,不能一味上大容量 P2G,要根据当地新能源利用小时数和碳源充足程度做联合优化。
5. 常见问题与调试经验:实战中踩过的坑
5.1 单位不一致导致的结果离谱
我最早写这个模型时,把垃圾热值 MJ/kg 直接乘上处理量 t/h,得到的结果大了数千倍,功率平衡怎么都配不平。后来一查才发现 1 kWh = 3.6 MJ,需要把热值统一折算成 MWh/kg 或者 MWh/t。类似的问题也出现在 P2G 产气量的能量换算上,天然气热值常常会是 MJ/Nm³ 或 kWh/Nm³,模型里如果用 MWh 做能量单位,就必须把热值也折算成 MWh/Nm³。
调试建议:在代码开头集中设置单位换算常量,比如MJ2MWH = 1/3600,然后所有参数都显式乘上换算系数。这样即使后期修改热值,也不容易出错。我一般会在参数表里同时标注原始数值和折算后数值,方便复查。
5.2 二进制变量过多导致求解变慢
MILP 求解最怕二进制数量大。初始模型里我给储能、储气罐、P2G 都加了启停二进制变量,结果 24 个时段乘以每组变量,轻松超过 200 个二进制变量,模型跑起来很吃力。后来我意识到并非所有设备都需要启停变量:储能充放电状态可以通过目标函数中的微惩罚项来避免同时充放,不需要二进制;P2G 的功率上下限已经能限制启停;真正必须用二进制的只有火电、燃气轮机和垃圾焚烧的运行状态。
还有一些变量可以从连续变量变成固定参数:例如储气罐在优化周期末强制回到初始值,那么末时段气量就由约束决定,不需要额外变量。每减少一个二进制变量,求解时间都可能显著下降,这在做滚动时域调度时尤其重要。
5.3 模型不可行时怎么定位问题
遇到不可行问题,最忌讳猜。YALMIP 里可以开启诊断,Gurobi 的 IIS 功能能找到最小不可行子集,但在 Matlab 环境下手写约束时,我更习惯用“松弛变量”法定位:在功率平衡、储气罐动态、CO₂ 平衡这几条关键等式上各加一个非负松弛变量,并给松弛变量设置很大的惩罚系数。求解后哪个松弛变量不为零,问题就出在哪条约束上。
我之前遇到的一个典型不可行原因是 P2G 所需的 CO₂ 时序不匹配:白天 P2G 想在光伏富余时产气,但碳捕集主要在夜间火电高负荷时捕集 CO₂,储碳罐容量又不够大,导致 CO₂ 供给约束被打破。给 CO₂ 储罐加容量约束后才发现问题不在储能容量,而在 P2G 和 CCS 的时间错位。解决方式是把 P2G 的运行时段限制放宽,或者提高 CO₂ 储罐容量。
5.4 YALMIP 和求解器使用注意事项
用 YALMIP 时,要特别注意变量重复约束问题。比如某条约束写在 for 循环里,如果循环变量计算要求从第 2 个时段开始,但你不小心把第 1 个时段也加进去了,可能在边界处产生冗余约束。YALMIP 对重复约束不会报错,但它会把重复的约束一起传给求解器,增加模型规模。
另外,最好每次求解前清空旧变量,或者用clear sdpvar之类的操作,避免上一次运行留下的变量污染模型。我用optimize(Con, Objective, ops)前经常需要Con = [],不然后续叠加约束会把历史约束全部带上,造成不可行或变量维度错乱。
求解器超时也很常见。我的建议是先跑一个简化模型(比如去掉二进制变量、时间缩短到 12 小时)确认逻辑正确,再逐步恢复完整约束。Gurobi 的 MIP Gap 可以设置为 1%,工程场景下不需要追求绝对最优解,得到一个成本接近最优的可执行调度方案已经够了。
6. 实操心得:这类模型后续还能怎么扩展
我必须说,这类“计及电转气协同的虚拟电厂优化调度”模型,真正难的不是公式,而是如何在简化与真实性之间找到平衡。我做这个项目的最大体会是:碳捕集和 P2G 的耦合关系如果只停留在等式层面,很容易做出一个“纸面上很完美、实际运行不可用”的方案。比如捕集能耗是随工况变化的,P2G 的启停响应速度也有约束,如果全用固定效率线性模型,只能做规划级预判,不能直接用于实时控制。
实际工程里,下一步可以考虑引入多场景随机优化,把风电光伏出力的预测误差建模成若干场景,用场景概率加权求期望成本;也可以做两阶段鲁棒优化,保证在最恶劣新能源波动下调度方案依然可行。这些扩展在 Matlab 里都能基于现有模型改造,只是约束数量会大幅增加。另外,碳捕集和 P2G 还可以放进碳市场交易策略里,把碳配额作为决策变量而不是固定参数,这会牵扯到更复杂的市场交互和碳价预测问题。
最后分享一个小的实用技巧:在做结果分析时,不要只看总成本,一定要把“碳捕集耗电”“P2G 产气量”“弃风量”“净碳排放”这四个关键指标分别输出,单独画时序曲线。我见过不少文稿只给一个成本对比表格,结果评审一眼就问“夜间弃风少的那一段到底是被 P2G 消纳了还是被储能充掉了”,如果中间过程拿不出来,整个模型的可信度就会打折扣。所以你写代码时,每个设备的逐时段值都保留下来,后续做分析时才不会捉襟见肘。