ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

风光互补制氢合成氨系统容量-调度优化:MILP建模与Cplex实现

风光互补制氢合成氨系统容量-调度优化:MILP建模与Cplex实现 1. 先把复现对象拆开看风光互补制氢合成氨系统的容量-调度优化看到这个标题我第一反应是这又是一个把“容量规划”和“小时级运行调度”绑在一起做的混合整数线性规划问题。风光互补制氢合成氨本质上是一条“可再生能源发电→电解水制氢→储氢→合成氨”的长链条而“并/离网”则决定了这条链和外部电网之间到底能不能有功率交换。用 Cplex 求解说明大概率是把它建模成了 MILP需要通过整数变量处理启停、时段互斥等逻辑。这类复现最容易出现的问题是模型还没搭好就急着调求解器结果 Cplex 报 infeasible一头雾水。所以我想先把系统边界和模型逻辑讲清楚再给出一条可以直接照着跑的 Matlab 实现路线。无论你是做论文复现、课题预研还是刚接触能源系统优化这篇文章的重点都是帮助你理解“为什么这么建模、Cplex 到底在解什么”而不是只给你一串能跑的代码。先说系统组成。风电和光伏提供主要电力电解槽把水变成氢储氢罐解决发电和用氢之间时间不匹配的问题合成氨装置消耗氢气和空分系统来的氮气一起生成氨。所谓容量优化是决定风、光、电解槽、储氢罐、合成氨装置各装多大所谓调度优化是在给定容量下决定每个时刻哪些设备出力、启停、充放氢、买卖电。标题里的“并_离网”意思就是这套系统既可以接在电网上运行也可以完全孤岛运行两种模式下约束和成本结构完全不同。1.1 并网与离网差别不在“多一根线”而在调度自由度离网模式的硬约束非常直接任意时刻风电加光伏的出力必须等于电解槽耗电、合成氨装置耗电、辅助系统耗电和弃电之和。电网不存在所以不能买电也不能卖电超出需求的电只能丢弃缺电时氢气产量或合成氨产量就得往下压。这意味着离网模型里容量配置的“冗余度”一定偏大因为风光出力天然波动你要么多装风机光伏保证低出力日的供氢量要么多装储氢罐把富余时段的氢存下来。并网模式的本质改变是增加了“购电”和“售电”两个自由度。缺电时可以买电富余时可以卖电甚至可以利用峰谷电价差把电解槽和合成氨装置的运行时段挪到低电价时段。但并网也不是免费的你要为变电容量、并网接口付出成本而且大多数论文里会加“同一时段不能同时买电和卖电”的约束否则模型会自动做低买高卖的无风险套利产氢量全部变成零只给电网发电这显然不符合原意。1.2 容量和调度为什么要一起优化而不是分两步很多入门者在复现时习惯先固定一组容量再去做经济调度跑完再手动调整容量。这样做在一两个典型日下能蒙对方向但一旦加入储氢罐的动态约束容量和调度就彻底耦合了。举个简单例子储氢罐装小了为什么会影响光伏装机因为白天光伏大发时氢装不下晚上没光时电解槽又没电可用整个系统被“卡”在时序约束里光伏再多也发挥不了作用。电解槽装大了为什么可能反而推高弃电率因为低出力时段电解槽吃不饱但又不能频繁启停强制最低负荷会让系统更僵硬。所以容量的最优解本质上是在“投资成本”和“运行约束是否满足”之间找平衡。只有把容量变量和小时级调度变量放进同一个模型一起优化Cplex 才能通过求解器内部的边界、割平面和分支定界逻辑同时判断“某个容量方案能不能跑得通”和“跑得通时运行成本是多少”。这也是为什么标题里叫“容量-调度优化分析”而不是单纯的容量配置。2. 模型主体拆解目标函数、变量和约束复现论文时我会先把模型“翻译”成三张表目标函数是什么变量有哪些约束有哪些。这三张表对了代码只是机械装配。2.1 目标函数先看口径年费用还是净现值常见的目标函数是年化总费用最小[ \min ; C_{\text{year}} C_{\text{cap}} C_{\text{om}} C_{\text{buy}} - C_{\text{sell}} ]其中 (C_{\text{cap}}) 是设备投资的年化成本通常用资本回收系数 CRF 把一次性投资折算到每年[ CRF \frac{r(1r)^n}{(1r)^n - 1} ]这里 (r) 是折现率(n) 是设备寿命。复现时必须先跟论文校对这个口径有的论文用净现值有的用等年值有的把项目周期统一设成 20 年有的分设备寿命单独折旧。这些细节直接影响容量结果尤其是风电和光伏寿命长、电解槽寿命短如果不分开折旧电解槽会被低估成本导致容量偏大。(C_{\text{buy}}) 是并网模式下的购电费用(C_{\text{sell}}) 是售电收入。离网模式下这两项直接置零。很多复现版本里还有弃电惩罚项但原论文如果没写就不要自己加因为惩罚系数会改变容量方案的倾向。2.2 变量设计容量变量是“头”时刻变量是“身子”我习惯把变量分成两类。第一类是容量变量一般只有几个风电装机 (P_{wt})、光伏装机 (P_{pv})、电解槽容量 (P_{el})、储氢罐容量 (H_{st}})、合成氨装置容量 (P_{am})。这些变量是连续非负变量个别论文会要求离散装机台数那就用整数变量表示模型会难解很多。第二类是调度变量数量和典型日数、时间步长直接挂钩。常见的有风电实际注入功率 (P_{wt}(t))光伏实际注入功率 (P_{pv}(t))电解槽耗电 (P_{el}(t))产氢流量 (F_{h2}(t))储氢罐状态 (S_{h2}(t))合成氨装置用氢流量 (F_{am}(t))合成氨装置耗电 (P_{am}(t))并网模式下购电 (P_{buy}(t))、售电 (P_{sell}(t))弃电量 (P_{curt}(t))设备启停状态 (z_{el}(t))、(z_{am}(t))其中启停状态必须用二进制变量这就是 Cplex 要处理整数变量的原因。如果原论文还考虑了电解槽爬坡、最小连续运行时间、储氢罐初始状态和终态相等那么对应约束也会带着二进制变量出现模型规模会明显增大。2.3 关键约束每一条都有物理含义我复现时最重视的是下面这组约束建议按顺序建模功率平衡。离网模式[ P_{wt}(t)P_{pv}(t)P_{el}(t)P_{am}(t)P_{other}(t)P_{curt}(t) ]并网模式加两项[ P_{wt}(t)P_{pv}(t)P_{buy}(t)P_{el}(t)P_{am}(t)P_{sell}(t)P_{other}(t)P_{curt}(t) ](P_{other}) 是辅助设备负荷比如空分制氮、冷却循环、照明控制等很多人复现时会漏掉导致模型结果偏乐观。风光出力约束。风电和光伏实际注入功率不能超过当时的可用功率。可用功率等于归一化出力系数乘装机容量[ P_{wt}(t) \le CF_{wt}(t) \cdot P_{wt,cap} ][ P_{pv}(t) \le CF_{pv}(t) \cdot P_{pv,cap} ]这两个约束看着简单却决定了容量变量的可行性如果装机很大但资源系数很低模型会大量弃电成本反而上升。电解槽约束。电解槽有最小运行负荷一般用二进制变量[ P_{el,min} \cdot z_{el}(t) \le P_{el}(t) \le P_{el,cap} \cdot z_{el}(t) ]产氢量和耗电之间用效率系数关联。如果效率是常数就是线性约束如果论文里给的是部分负荷效率曲线就需要线性化处理通常取分段线性近似。储氢罐动态约束。储氢罐是时间耦合的关键[ S_{h2}(t1)S_{h2}(t)F_{h2}(t)-F_{am}(t) ]同时有容量上下限以及调度周期末状态等于初状态的循环约束。循环约束不能省否则模型会把储氢罐里的氢“白送”给末端时段导致结果失真。合成氨装置约束。合成氨装置同样有最小负荷约束和启停二进制变量同时耗电量和用氢量、产氨量之间存在转化系数。要注意氨本身也可以有储罐如果原论文考虑氨储罐还要再加一个储氨量动态约束模型会复杂很多。并网交互约束。一般要限制联络线功率上限并加购电、售电互斥约束[ P_{buy}(t) \le P_{buy,max} \cdot z_{grid}(t) ][ P_{sell}(t) \le P_{sell,max} \cdot (1-z_{grid}(t)) ]很多初版模型不写互斥约束Cplex 虽然通常也不会傻到同时买和卖但一旦出现同时买卖目标函数里会莫名其妙多出成本结果不可信。3. 用 Matlab 和 Cplex 复现的完整路线模型理清楚后就是工程实现。Matlab 写优化模型的体验比 Python 好的一点是矩阵操作直观但缺点是变量一多索引容易乱。我的做法是把变量区块化再用索引函数定位而不是裸写 A 矩阵。3.1 环境准备把 Cplex 接到 Matlab首先确认 Cplex 版本和 Matlab 版本兼容。IBM Cplex 安装目录下通常有一个matlab文件夹里面是官方提供的接口函数。在 Matlab 里执行addpath(genpath(C:\Program Files\IBM\ILOG\CPLEX_Studio2211\matlab)); savepath; which cplexmilp如果which能定位到cplexmilp说明接口已经通。老版本接口一般用cplexmilp(f, Aineq, bineq, Aeq, beq, lb, ub, ctype, x0, options)其中ctype是一个字符向量C表示连续变量B表示二进制变量I表示整数变量。新版本也有面向对象的接口比如cplex Cplex(model_name); cplex.Model.obj f;两种方式都能用我建议先学cplexmilp这种函数式接口调试起来更快。没有 Cplex 授权时可以先用 Matlab 自带的intlinprog跑小算例逻辑通了再换 Cplex。但要注意intlinprog和cplexmilp的输入参数不完全一样换接口时不要直接复制。3.2 数据准备先搞清楚时间尺度容量-调度联合优化的数据分两部分资源数据和设备参数。风电、光伏要准备逐小时归一化出力系数并网模型还要有分时电价。如果原论文用的是全年 8760 小时那你的调度变量会非常庞大Cplex 未必跑得动。复现时经常用典型日替代比如按季节和天气类型用 k-means 聚成 12 类典型日再乘以对应天数作为权重。这样做可以大幅压缩模型规模。但是典型日数量不能拍脑袋。我踩过的坑是只用 4 个典型日时储能配置结果比论文低很多因为极端天气被平均掉了加到 12 个典型日后储氢罐容量明显上升。如果你的复现结果和论文差距很大第一步不是调 Cplex 参数而是检查典型日数量和聚类方式。3.3 矩阵装配用索引函数避免 A 矩阵错位这里给一个简化示例变量区块划分如下% 变量区块 % x(1) 风电装机 % x(2) 光伏装机 % x(3) 电解槽装机 % x(4) 储氢罐容量 % x(5) 合成氨装置装机 % x(6:5nT) 风电逐时注入 % x(...) 其他逐时变量按同样方式排列在代码里我会定义一个结构体保存索引位置nT 24; idx.cap_wt 1; idx.cap_pv 2; idx.cap_el 3; idx.cap_h2 4; idx.cap_am 5; idx.Pwt (t) 5 t; idx.Ppv (t) 5 nT t; idx.Pel (t) 5 2*nT t; % 继续追加其他变量区装配约束矩阵时只通过索引函数访问变量位置比如功率平衡约束Aeq zeros(nT, nVar); beq zeros(nT, 1); for t 1:nT Aeq(t, idx.Pwt(t)) 1; Aeq(t, idx.Ppv(t)) 1; Aeq(t, idx.Pel(t)) -1; Aeq(t, idx.Pam(t)) -1; Aeq(t, idx.Pcurt(t)) -1; beq(t) 0; end这样虽然代码看起来啰嗦但每一行都能对应模型里的一个约束排查问题时可以直接跳到对应循环不用猜。调用 Cplex 求解的示例ctype char(ones(1, nVar) * C); ctype(idx.z_el_start:idx.z_el_end) B; ctype(idx.z_am_start:idx.z_am_end) B; options cplexoptimset(Display, iter, TolGap, 0.0001); [x, fval, exitflag, output] cplexmilp(f, Aineq, bineq, Aeq, beq, lb, ub, ctype, [], options);TolGap是 MIP 相对间隙。论文复现通常设到 0.1% 或 0.01% 就够设成 0 会让求解时间成倍增加。工程上更聪明的做法是先设 1%看大体容量结果再收紧到 0.01% 做最终确认。3.4 结果回读和后处理Cplex 返回的x是一个长向量里面按你定义的变量区块排列。回读时继续用索引函数拆包cap_wt x(idx.cap_wt); cap_pv x(idx.cap_pv); cap_el x(idx.cap_el); cap_h2 x(idx.cap_h2); Pwt arrayfun((t) x(idx.Pwt(t)), 1:nT); Pel arrayfun((t) x(idx.Pel(t)), 1:nT);拿到容量结果后不要急着画图。先做一个“可行性检查”把最优解回代到所有约束中看最大违反量是多少。如果违反量在 1e-6 量级说明建模和求解都正常如果出现 1e-2 量级的违反基本可以确定是单位不一致或变量范围设置错误。4. 复现时最容易踩的坑这类题目的坑往往不在数学上而在工程细节上。我把实际复现中遇到的典型问题整理成了一份排查清单。4.1 模型不可行先拆后合Cplex 返回 infeasible 时不要马上怀疑求解器。我一般按这个顺序排查先跑纯离网、去掉并网相关变量看能否可行。如果不可行放宽储氢罐终态等于初态的约束看模型是否变可行。如果变可行说明储氢时序有问题。如果仍不可行把电解槽最小负荷约束去掉看是否可行。这一步能判断是不是启停二进制变量和最小负荷把可行域压没了。最后检查功率平衡约束的符号尤其是P_wt和P_pv到底是“注入”还是“输入”方向反了会让所有方程错位。Cplex 本身也提供冲突集分析新版接口可以输出不可行约束子集但数据量大时不如自己拆模型直观。4.2 求解时间太长热启动和 MIP gap 是好朋友容量-调度联合优化一旦把二进制变量加到全年 8760 小时Cplex 可能几个小时出不来。这时候有两条路一是缩小时间尺度用典型日二是给 Cplex 一个热启动初始解。热启动在 Cplex 里可以用x0参数传一个可行解。比如先用较粗的模型跑一遍把结果作为完整模型的初始解。Cplex 的 MIP 求解器会从这个解开始优化分支定界树能剪掉大量无效区域。这个方法在复现论文时特别有效尤其是你要反复微调参数微调完之后模型结构没变热启动能让求解时间从小时级降到分钟级。4.3 数值尺度系数差太多Cplex 也会犯迷糊我见过最隐蔽的坑是成本系数和功率变量量级差太大。比如装机成本用元/千瓦电费却用元/兆瓦时氢质量用千克储能容量用立方米一个目标函数里既有 1e8 的项又有 1e2 的项Cplex 的数值容差会变得很难看结果经常出现“微小违反约束但表面可行”的问题。我的建议是统一用以下单位功率MW电量MWh氢量kg成本万元电价元/MWh 或元/kWh但目标函数中所有金额单位一致同时把所有变量上下限缩放到 0 到 1e4 左右成本系数也换算到同一量级。这样 Cplex 的预处理效果更好求解速度更快结果也更稳。4.4 典型日数量和权重错误典型日缩减不是简单的“挑几天”。每个典型日必须带一个权重系数表示这个代表日实际覆盖了多少天数。你在代码里把 8 个典型日的调度结果直接相加然后说这是全年运行成本那基本是错的。正确做法是在目标函数里按权重累加[ C_{\text{op}} \sum_{d1}^{N_d} w_d \sum_{t1}^{nT} \left( c_{buy} P_{buy}(d,t) - c_{sell} P_{sell}(d,t) \right) ]权重写错会让投资成本和运行成本的比例完全失调。复现论文时我会先用一年的平均资源数据做一个粗校核算出的年发电量、年制氢量和论文结果对比如果偏差超过 5%就先查典型日权重而不是继续调模型。4.5 论文结果对不上的常见隐藏假设很多论文不会把每一个公式都写在正文里。比如合成氨装置可能只是一个大功率连续设备也可能有明确的启动能耗电解槽可能允许 0% 到 100% 宽范围运行也可能有 30% 最小负荷并网模型的购电电价可能分峰平谷也可能是单一电价。这些细节都会改变容量结果。复现到一半发现对不上先回头检查这些“小假设”而不是怀疑 Cplex。5. 一个可参考的简化算例为了说明完整流程我准备了一个简化的沙盘算例。参数不来自任何特定论文只用于展示模型结构和结果解读方式复现时请用论文数据替换。5.1 基础参数设备投资成本寿命年运维费率风电6000 元/kW20 年2%光伏3500 元/kW20 年1%电解槽5500 元/kW15 年3%储氢罐1200 元/kg15 年1%合成氨装置4000 元/kW20 年2%资源数据这样设置风电年利用小时数约 2400 小时光伏约 1500 小时合成氨需求设定为每年固定吨位。并网模式电价采用峰平谷三段峰段 1.2 元/kWh平段 0.7 元/kWh谷段 0.3 元/kWh。5.2 离网模式结果在离网模式下模型会倾向于多装储氢罐来抵消风电和光伏的天然波动。一个典型结果是风电装机偏大光伏装机适中电解槽容量略小于风光的峰值功率储氢罐容量明显大于单日氢气需求。为什么会这样因为离网系统不能买电缺电时只能靠储氢顶住所以储氢成本虽然高却是保证合成氨装置连续运行的唯一手段。离网模式的弃电率通常会高于并网模式这不是模型有问题而是物理边界决定的。弃电率高意味着系统为“可靠性”多付出了装机成本这在离网方案里是合理的。5.3 并网模式结果并网模式加入购电和售电后容量结果会发生两种典型变化。如果峰谷电价差很大电解槽容量可能增加因为模型会在谷段大量买电制氢等到峰段再把氨合成或储存下来这样虽然买了电但总成本和离网相比反而下降。如果谷段电价仍然很高并网模式就只会作为“兜底”容量配置和离网差别不大购电量也很少。这个算例提醒我们并网不是简单地把离网方案砍小而是根据电价曲线重排调度时序。复现论文时如果并网结果和离网结果只差几个百分点要怀疑电价曲线是不是给得太保守。5.4 结果校验表校验项方法合格线功率平衡将最优解回代功率平衡约束最大偏差小于 1e-6储氢终态检查调度期末储氢量等于初值偏差小于 0.1 kg年制氢量用典型日权重折算与论文目标值偏差小于 5%目标函数用同样的成本和效率参数重算与论文目标值偏差小于 5%容量趋势对比各设备装机比例与论文主要结论一致6. 我复现完之后的几点体会这类项目做多了最大的体会不是代码多熟练而是“对模型物理含义的敬畏”。Cplex 再强也只会按你给的约束找最优解你给它单位错的系数它照样给你一个精致的错误答案。6.1 先跑通最小系统再加大规模我建议所有新手复现时都先做一个 24 小时、单典型日、去掉并网和储氢罐的版本确认功率平衡和电解槽逻辑没问题后再逐步加入储氢、并网、多典型日。每一步增加的东西越少出错时越容易定位。我见过太多人一开始就上 8760 小时加 Cplex 热启动结果 infeasible 后完全不知道问题在哪。6.2 复现论文时要把公式和代码对照成一个“表格”每写一个约束就在代码旁边注释对应论文公式编号同时注明量纲和取值来源。这个方法很笨但非常有效。因为复现的本质不是能跑出数字而是你的模型假设和论文一致。只有把每个公式的物理含义、参数来源、边界条件都对照清楚结果才能经得起推敲。最后再分享一个实用小技巧Cplex 跑完后把目标函数拆成投资成本、运维成本、购电成本和售电收入四项分别输出而不是只看总成本。多数情况下论文的结果和你对不上的原因就藏在这几项里。投资成本偏高但运行成本偏低说明容量偏大购电成本很高说明并网约束太松或电价数据没对齐。拆开看一分钟就能定位问题而不是对着总成本反复挠头。
返回列表