☰
风光制氢合成氨系统容量-调度耦合优化:MILP建模与Cplex求解实践
2026/10/8 9:10:37 网站建设 项目流程

1. 这个系统到底在算什么:风光制氢合成氨的容量-调度耦合问题

1.1 完整物理链路拆解

先把我理解的系统摆出来。一个典型的并/离网风光互补制氢合成氨系统,物理上包含两大部分:发电侧和化工负荷侧,中间用制氢储气环节连接。

发电侧是风电场加光伏电站,两者在时间尺度上互补——光伏白天出力集中、夜间归零,风电则往往在夜间和过渡季节出力更好,配合起来能明显提高年等效满负荷小时数。化工负荷侧的核心是合成氨装置,采用经典的哈伯-博世工艺,需要的是氢气和氮气在高温高压催化剂作用下反应生成氨,其中氮气来自空分装置,氢气则由电解水制氢提供。

这个系统的链条是这样的:风光出力→功率变换后送入电解槽→电解水产出氢气→氢气经压缩后存入储氢罐→储氢罐向合成氨装置供氢,同时联合空分装置提供的氮气→产出液氨→进入氨储罐外运销售或就地存储。

在离网模式下,电网不参与功率交换,整个系统必须依靠风光出力、储氢缓冲和调节手段自平衡;在并网模式下,系统可以与电网进行双向功率交换,缺电时购入、盈余时卖出,电网相当于一个容量无限大的“缓冲池”。这两种模式的可行域和最优解差异非常大,后面我会展开说。

1.2 容量和调度这两个问题为什么要耦合在一起

很多人第一次接触这个题目时会想:先把设备容量定下来,再去做运行调度不就行了?实际不行,或者说这样做得到的一定是次优解。

容量配置决定的是设备投不投、投多大——风机装多少MW、光伏装多少MWp、电解槽装多少MW、储氢罐是多少千克、合成氨装置额定年产多少吨。这是一组整数或者连续变量,直接影响投资成本。调度决策决定的是给定容量之后,每个时段各设备怎么运行——电解槽出力多少、储氢罐充放多少、氨合成装置负荷率多少、是否从电网购电。这是一组时间序列变量,直接影响运行收入和运行成本。

两者的核心矛盾在于:容量配置越保守,投资越低,但风光波动带来的制氢连续性不够,合成氨装置被迫频繁启停或降负荷,单位成本上升;容量配置越激进,投资摊销高,但运行时系统调节裕度大,合成氨装置可以稳定高负荷运行。最终经济性如何,取决于两者叠加后的年度总成本,所以必须把容量变量和调度变量放进同一个优化框架里联立求解。

2. 设备建模:从物理过程到数学约束

2.1 风、光出力特性的建模方式

风电出力建模,行业内最常用的是功率曲线法。把风速时间序列代入标准风机功率曲线,得到单位装机出力系数,再乘上装机容量就是实际出力。功率曲线的典型特征是:低于切入风速(通常3m/s左右)不出力,达到额定风速后出力恒定,超过切出风速(一般25m/s)为了保护风机而停机。

光伏出力建模相对更标准化一点,核心是日照辐照度。简化模型里用单位装机出力系数随辐照变化,再考虑温度和组件效率的小幅折减。更精细的做法是用PVsyst或SAM先算好全年8760小时的归一化出力序列,直接以矩阵形式输入到Matlab里,省去在优化模型里内嵌气象模型的计算负担。

在我复现这个项目的过程中,风光的建模建议直接采用“场景时间序列+单位容量出力”的结构,也就是说,把资源数据预处理为每个时段单位装机下的出力系数K_w(t)和K_pv(t),实际出力就是容量变量乘以对应系数。这样处理的好处是:容量变量是优化变量,而出力系数是已知参数,整个功率平衡约束天然保持线性,Cplex处理起来非常友好。

2.2 电解槽、储氢罐与合成氨单元的建模

电解槽建模是整篇论文里最容易被简化也最容易出错的一块。有两条路线可以选择:

第一条是效率恒定路线,电解槽的产氢量直接等于输入电功率除以单位电耗常数,典型值在4.5-5.5 kWh/Nm³氢气之间;第二条是变效率路线,电解槽在部分负荷下效率下降或上升,需要用分段线性函数拟合电耗曲线。恒效率模型求解简单,但会高估低负荷运行时的产氢量;分段效率模型更贴近设备实际特性,代价是模型里要引入分段线性化结构,增加二进制变量。

从复现角度,我的建议是先按恒效率模型把框架搭通,再在此基础上扩展为分段效率模型做对比。直接上来就做分段线性化,调试难度会高很多,容易陷入“模型没错但就是不收敛”的困境。

储氢罐的建模相对简单,本质是一个带上下限的能量/物料存储缓冲。动态方程是:t时刻罐内储氢量 = t-1时刻储氢量 + 电解槽产氢量 - 供给合成氨装置的氢气量。压缩机的电耗需要单独建模,通常简化为单位压缩电耗乘以压缩氢气量,在功率平衡方程里计入电负荷。

合成氨装置的建模需要抓住两个关键点:一是氮氢配比约束,哈伯-博世工艺的理论氢氮比是3:1,实际运行中一般控制略低于3:1以保证反应速率,但在系统级模型里按理论配比处理即可;二是装置的最小技术出力,合成氨装置因为催化剂床层温度稳定性要求,一般不允许在过低负荷下运行,这个最小负荷率约束(比如额定产能的40%)会显著影响调度结果。

2.3 并网/离网的差异体现在约束上

并网模式和离网模式在目标函数上的差别主要在购售电项,在线路结构上的差别主要在一个等式约束。

并网模式下:系统电功率平衡方程右边多出“购电功率-售电功率”这一项,购电有电价成本,售电有上网电价收益,电网相当于承担了一部分调节压力。

离网模式下没有这一项,系统电功率供需必须严格实时平衡,同时为了保证系统全年可靠运行,储能(储氢罐)的容量必须覆盖长时间的低出力窗口。更严格的做法还会引入失负荷惩罚项,在极端场景下允许少量甩负荷,以避免为了一个极端小时把整个系统容量堆得过高。

这个差异是容量优化结果分化的根本原因。并网模式下电网可以兜底,最优容量往往偏向最小化投资和运行的总成本,风光配比可以更激进;离网模式下系统必须保障合成氨装置的基本连续运行,储氢和电解槽的容量会被明显放大,等效满负荷小时数相应下降,整个系统的投资强度显著提高。

3. 优化模型构建:目标函数、约束与Cplex可解形式

3.1 年度化总成本怎么算

优化目标我采用的是行业内最通用的“年度总费用最小化”框架,由四部分组成:

  • 年化投资成本:各设备的单位投资乘以容量,乘以资金回收系数CRF后折算成等额年值。CRF = r(1+r)^n / ((1+r)^n - 1),其中r是贴现率,n是设备寿命。不同设备的寿命不同,电解槽的寿命普遍按10-15年考虑,风电和光伏按20年考虑,合成氨装置按20年考虑,这一点在复现时必须分开处理,不能统一用同一个寿命年数。
  • 年运行维护成本:按投资或产出量的一定比例估算。
  • 购电成本:并网模式下向电网购电的费用,实时电价乘以购电量。
  • 售电收益:向电网卖电的收入,也是一个负数成本项。

如果系统对外销售氨产品,目标函数里还要扣掉氨销售收入。但在纯复现“容量-调度优化”的项目里,我建议处理成“给定满足合成氨产量需求前提下的最小成本”,而不是产销联合优化,这样能避免氨价格假设对结果的主导性影响,也更贴近论文通常展示的分析框架。

3.2 为什么用MILP形式而不是直接上非线性

原始问题里处处是乘法和非线性:容量变量乘出力系数本身是线性,但如果考虑电解槽效率随负荷变化,出现的是效率与出力相乘的非线性项;合成氨装置开停机是整数变量与连续变量的乘积;储氢罐的充放氢速率可能与罐内压力有关。

Cplex擅长的是线性规划和混合整数线性规划,直接扔一个非线性问题进去要么无法求解要么性能极差。所以建模阶段的关键功夫在于:把非线性能线性化的线性化,不能线性化的用分段近似。

这就涉及MILP模型改写:

  • 设备容量投建与否用0-1变量;
  • 启停逻辑用0-1变量乘Big-M约束;
  • 效率曲线用分段线性化,每一段用二进制变量锁定区间;
  • 连续变量与二进制变量的乘积通过引入辅助变量和M约束消除。

这套处理做完后,整个模型变成一个标准的混合整数线性规划MILP,Cplex可以高效求解至全局最优或近似最优。

3.3 关键约束的数学表达

我挑几个核心约束列出来,方便对照代码理解:

  • 电功率平衡约束(n取1到T的每一个时段): P_w(t) + P_pv(t) + P_buy(t) = P_el(t) + P_load_aux(t) + P_sell(t) 其中P_el(t)是电解槽输入电功率,P_load_aux(t)是空分、压缩机、合成氨辅助系统等全部电负荷。

  • 电解槽产氢约束: H_prod(t) = η_el · P_el(t) η_el是单位电耗折算的产氢效率,恒效率模型里是常数。

  • 储氢罐动态平衡: S(t) = S(t-1) + H_prod(t) - H_to_ammonia(t),且 S_min ≤ S(t) ≤ S_max

  • 合成氨装置供氢约束: H_to_ammonia(t) = R_ammonia(t) · h_unit R_ammonia(t)是t时刻合成氨装置的产氨速率,h_unit是单位产氨耗氢量。

  • 开停机逻辑约束: R_ammonia(t) ≤ M · u_am(t),u_am(t)是0-1变量,表示装置是否运行; 同时R_ammonia(t) ≥ R_min · u_am(t),强制低于最小负荷时只能停机。

  • 投资与运行的变量交叉约束: 任意时段电解槽功率不得超过已投建容量,即 P_el(t) ≤ Cap_el · M_el,M_el是投建变量,这样容量为0时电解槽就无法运行。

这些约束在代码里看起来密密麻麻,但逻辑拆开其实很清晰:功率平衡是线性的,储氢动态是线性的,只有启停和投建涉及0-1变量,但也是标准的MILP结构。

4. Matlab+Cplex落地:配置、代码骨架与调试心得

4.1 Cplex接入Matlab的完整流程

Matlab里调用Cplex有两种主流方式:第一种是直接用IBM Cplex Optimization Studio for Matlab的官方接口,第二种是用Yalmip这个建模工具箱,后端指定cplex。我复现时用的是第二种,原因是Yalmip的建模语法更接近自然数学表达,写约束排错都更快。

接入流程分四步:

  1. 安装Cplex。如果是学习复现,可以使用社区版,但要注意社区版有变量数限制,复杂大模型可能跑不了完整规模,需要简化场景或用校园版。
  2. 安装Yalmip,下载后把文件夹路径addpath到Matlab的搜索路径里。
  3. 在Matlab中运行yalmiptest,确认输出中cplex求解器被识别并标记为可用状态。
  4. 设置求解器参数:ops = sdpsettings('solver','cplex','verbose',2)构建一个通用的选项集。

这里有个极易踩的坑:Cplex安装后如果环境变量没设置好,Matlab调用时会报“找不到cplex”的错。解决方法是在Cplex安装目录的cplex/matlab子目录里运行setup脚本,或者手动把对应文件夹addpath,别只装了软件不去配置Matlab接口。

4.2 核心代码骨架

我用Yalmip语法给出一个最小可行的模型骨架,变量定义和约束框架是完整的,具体参数值需要按目标论文或实际数据填入:

%% 数据载入与基础参数 load('wind_pv_schedule.mat'); % 假设包含K_w、K_pv两个8760x1的归一化出力系数 T = 8760; r = 0.08; n_life = 20; CRF = r * (1+r)^n_life / ((1+r)^n_life - 1); %% 决策变量定义 Cap_w = sdpvar(1,1); % 风电装机容量 MW Cap_pv = sdpvar(1,1); % 光伏装机容量 MWp Cap_el = sdpvar(1,1); % 电解槽装机容量 MW Cap_h2 = sdpvar(1,1); % 储氢罐容量 kg P_buy = sdpvar(T,1); P_sell = sdpvar(T,1); P_el = sdpvar(T,1); S_h2 = sdpvar(T,1); H_prod = sdpvar(T,1); H_to_ammonia = sdpvar(T,1); R_ammonia = sdpvar(T,1); u_am = binvar(T,1); % 合成氨装置运行状态0-1变量 %% 目标函数 investment_cost = CRF * (c_w * Cap_w + c_pv * Cap_pv + c_el * Cap_el + c_h2 * Cap_h2); operation_cost = sum(price_buy .* P_buy) - sum(price_sell .* P_sell) + ... c_om * (Cap_w + Cap_pv + Cap_el) + c_ammonia_run * sum(u_am); objective = investment_cost + operation_cost; %% 约束集 Constraints = []; % 功率平衡 Constraints = [Constraints, K_w .* Cap_w + K_pv .* Cap_pv + P_buy - P_sell - P_el - P_aux == 0]; % 电解槽产氢 Constraints = [Constraints, H_prod == eta_el * P_el]; % 储氢罐动态 Constraints = [Constraints, S_h2(2:end) == S_h2(1:end-1) + H_prod(2:end) - H_to_ammonia(2:end)]; Constraints = [Constraints, 0 <= S_h2 <= Cap_h2]; % 合成氨供氢与配比 Constraints = [Constraints, H_to_ammonia == R_ammonia * h_unit]; Constraints = [Constraints, R_ammonia <= Cap_ammonia * u_am]; Constraints = [Constraints, R_ammonia >= R_min * u_am]; % 容量上限 Constraints = [Constraints, P_el <= Cap_el, P_el >= 0]; %% 求解 ops = sdpsettings('solver','cplex','verbose',2); optimize(Constraints, objective, ops);

这个骨架里的关键在于约束构建时用了向量化写法,避免在循环层面逐个时段写约束——在Matlab里用Yalmip写循环约束不是不行,但8760个小时全用循环写的话,模型生成时间会很感人,向量化能把建模时间从几分钟压缩到几秒。

4.3 求解容差与性能调优

Cplex默认的MIP相对容差(MIPGap)是0,即要搜到全局最优才停,但大规模MILP搜到全局最优可能非常耗时。实际复现时我的做法是把相对gap设到1%-2%以内就接受,因为工程问题的数据精度本身达不到1%以下,为了0.5%的gap多跑几小时不值得。

需要调整的参数集中在两个地方:

  • MIPGap:设成0.01或0.02,让求解器在目标值相对差距小于该阈值时提前终止。
  • MIPLimits:控制变量数和节点数上限,防止模型失控时无限期跑下去。
  • 时间上限:给一个最大求解时间,比如3600秒,时间到就返回当前最优可行解。

还有一个非常实用的小技巧:先用不含整数变量的松弛模型解一次,得到初始可行解赋给原模型作为warm start。在Cplex里可以通过SDPSETTINGS里的startvalue参数把松弛解或者预跑解传进去,能显著减少分支定界的搜索量,尤其是模型规模大时效果明显。

5. 复现过程中那些按文档操作根本发现不了的坑

5.1 参数标定:目标函数量纲和单位换算的连环坑

复现这类论文,第一道坎永远是参数单位。风电和光伏的容量单位是MW和MWp,电解槽的容量单位用的是电功率MW,储氢罐单位是kg,合成氨装置单位是吨/年或kg/h。目标函数里要把所有成本统一到“元/年”这一个量纲上,任何一处单位换算出错,成本项就会失去可比性,优化结果必定跑偏。

尤其注意电解槽产氢量的单位换算。如果你用5.0 kWh/Nm³这个典型单位能耗,那么1 Nm³氢气约等于0.0899 kg,也就是说产1 kg氢气需要约55.6 kWh电。这组换算关系必须单独写清楚,每次复现时先做一次手动验算,确认代码里电解槽在满负荷下一年能产多少氢、对应多少氨产量,符不符合基本的物料守恒。

5.2 风光资源数据怎么解决

论文数据通常来自特定地区的气象站或再分析资料,复现时往往拿不到原始数据。我的替代方案是:使用公开的典型气象年数据,把该地区的逐时风速和辐照度提取出来,再通过简单的功率曲线和辐照模型转换成归一化出力序列。

这里有一个值得注意的地方:如果你用的是全年8760小时连续数据,模型的调度变量就有8760个维度,加上整数变量之后规模会很大。很多论文会采用“典型日聚合”的方法,选冬季典型日、夏季典型日、过渡季典型日各几天代表全年运行,把调度规模压缩到几百个时段以内,大幅降低计算负担。

作为复现者,建议先把8760小时全时段模型跑通,再对比典型日聚合模型的结果。如果两种方法的最优容量差距在可接受范围内,那说明聚合方法有效;如果差距大,通常是典型日的选取有偏,需要重新聚类或者加权重。

5.3 不可行解的定位方法

我在调试过程中遇到最头疼的不是求解慢,而是模型直接报infeasible,而且你完全不知道是哪个约束打架了。

我总结出一套可行的定位流程:

  1. 把所有设备容量变量固定为一个合理的手算值,删除容量优化自由度,只留调度变量,此时模型变成一个纯线性规划或小规模MILP。如果连固定容量都无法找到可行调度,说明约束本身有矛盾,问题出在建模逻辑上。
  2. 如果固定容量后可行,说明问题出在容量变量和调度变量相互作用上,这时把容量上界逐步放大,找出第一个可行的容量范围。
  3. 检查储氢罐初始状态。如果初始化S_h2(1)=0,同时还要求合成氨装置第一个时段就保证最小负荷运行,很可能因为无氢可用导致不可行。正确的做法是允许初始储氢罐有一定储量,或者让合成氨装置在初始时段处于停机状态。
  4. 检查Big-M值是否取得足够大。M值取小了会错误地把可行域截断,这是MILP不可行最常见的原因,尤其注意合成氨装置的产氢约束,M至少要大于装置满负荷对应氢耗量的1.2倍。

5.4 为什么并网模型和离网模型的结果差异这么大

复现中另一个值得拿出来单独讲的是并网和离网结果的对比逻辑。

并网模式下,由于电网的存在,系统允许风光出力不足时购电补足,因此容量配置倾向于让风光系统恰好覆盖自身电耗和售电收益的平衡点,储氢罐的作用更多是日内的削峰填谷,容量不需要太大。风光过剩时还可以卖电获得收入,所以在某些电价结构下,系统甚至会主动过度配置光伏和风电,通过售电盈利来补贴制氢合成氨成本。

离网模式下没有电网支撑,储氢罐承担的就不光是日内的能量平移,还包括连续多天低风速、低辐照情况下的长周期补偿。这时候储能容量会大幅膨胀,动辄从并网模式的几百公斤上升到几吨级别的储氢罐。同时合成氨装置的最小负荷限制会逼迫系统保留足够冗余的电解槽容量,以保证在风光低谷期依然能凑够功率维持合成氨运行。

如果你复现出来的结果里,离网模式的电解槽容量和储氢罐容量比并网模式大出好几倍,不要惊讶,这是物理逻辑决定的,不是代码bug。反过来,如果两个模式结果差不多,才需要回头检查离网模型是不是漏了可靠性的约束条件。

6. 从优化结果反推系统设计的工程判断

6.1 如何看待最优容量组合的结果

求解完成得到最优容量后,我建议不要急着把结论写进报告,先做几项合理性检验:

  • 计算风电和光伏的等效满负荷小时数。把全年发电量除以装机容量,风电一般在1800-2600小时,光伏一般在1000-1600小时。如果算出来的数值偏离这个范围太多,要么是资源数据有问题,要么是最优解没有落在真实经济平衡点上。
  • 计算电解槽的年利用小时数,即全年总制氢电耗除以电解槽装机容量。如果最优方案里电解槽的利用小时数很低,说明设备投了太多,边际投资的收益可能被高估了。
  • 看储氢罐的充放周期。正常的日内调度里,储氢罐应该呈现近似昼夜循环的波动特征;如果最优解里储氢罐几乎一直满着或者一直空着,说明它的容量约束没有起作用,这个投资大概率可以削减。

这些检查能帮你区分“模型最优解”和“工程合理最优解”。优化模型给出的是数学上的最优,但只有通过指标校验的解,才能拿去支撑真正的方案设计。

6.2 敏感性分析怎么做最有价值

复现到这一步后,项目最核心的增量价值往往在情景分析部分。推荐做三组敏感性分析:

第一组是设备投资单价的变化,重点看电解槽投资对最优容量的影响。现在碱性电解槽和PEM电解槽的成本差距仍然不小,投资单价下调20%-30%对容量配置的冲击,决定了这条技术路线未来的竞争力。 第二组是电价结构的变化,重点看购电价和售电价之间的价差。如果峰谷电价差拉大,储氢罐的套利作用会显现,最优容量里储氢的比例会上升。 第三组是合成氨装置最小负荷率的变化。从30%调整到50%,每次都会改变离网模式下的容量配置结构,这条约束对离网系统的影响程度,值得单独画一张曲线出来。

6.3 这个项目还能怎么扩展

最后说一句我自己的判断。风光制氢合成氨系统的容量-调度优化属于典型的多能互补系统优化问题,理论上已经很成熟,但实际工程落地中,制氢合成氨装置的动态响应特性、设备长期衰减曲线、以及制氨价格波动对投资吸引力的影响,才是决定一个项目能不能真正盈利的关键。这些细化方向在原论文框架上都能继续延伸,入口就在于把当前模型里的恒效率设备假设逐步替换成动态响应模型,再叠加市场价格的随机场景。相比从零搭一套新系统,在已有的Cplex+MILP框架上做增量扩展,是性价比最高的研究方式。

我在实际复现过程中的体会是,这个项目的门槛不在优化算法本身,Cplex已经把求解器那些复杂的分支定界、割平面、启发式搜索全部包掉了,真正的门槛在于两件事:一是对风光制氢合成氨物理链条的理解是否准确,建模顺序是否合理;二是对MILP线性化技巧的熟悉程度,能否把真实的工程问题干净地翻译成求解器能高效处理的形式。这两件事做好了,从论文到代码的复现过程会顺畅很多,调试时间也能省下大半。

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

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

立即咨询