资讯动态

计及氢能的综合能源优化调度:建模、求解与Matlab实现

发布时间:2026/10/18 9:10:02 来源:尧图企业网站定制
计及氢能的综合能源优化调度研究这阵子在课题组里几乎成了标配方向。起因也很直接传统综合能源系统IES里折腾来折腾去就是燃气轮机、电锅炉、储电蓄热那几样碳减排的空间越来越小而氢能作为一种“既能当燃料烧、又能存下来跨时段用、还能通过燃料电池反哺电能”的多面手载体正好把电力系统、热力系统和天然气系统的耦合又抬高了一个维度。我前后帮几位同学搭过这类模型从初版只做电热联供到后来一步步把电解槽、燃料电池、储氢罐塞进调度框架Matlab代码也从几百行膨胀到上千行。这篇文章就把这个项目从建模到落地的完整链路拆开讲清楚包括设备建模、约束设计、求解器选型、代码组织方式以及跑算例时最容易踩的那些坑给后面做类似计及氢能的IES优化调度的同学一条可以照着走的路。1. 为什么偏偏是氢能综合能源调度的“新变量”与建模动机1.1 电热耦合的旧格局到底卡在哪了传统IES的调度模型核心思路很简单以系统运行成本最小为目标同时满足电、热负荷需求以及各类设备的运行约束。设备层面通常包括风力发电、光伏、微型燃气轮机MT、燃气锅炉GB、电锅炉EB、蓄电池ESS、蓄热罐TSS。这种结构有一个天然短板——能量的时间转移能力太弱。蓄电池能存电但容量有限、寿命衰减快蓄热罐能存热但热损失大、做工粗糙燃气轮机倒是能发电又能供热但单位碳排放高而且电和热的产出比并不总能刚好匹配负荷比例。在大量风电、光伏接入之后弃风弃光问题进一步放大了这种缺陷中午光伏大发电负荷却处于低谷蓄电池很快就充满多余的电只能切掉到了傍晚负荷高峰光伏归零又得靠燃气轮机顶上去。说白了系统缺的不是电量而是“把能量从一个时段搬到另一个时段”的能力。氢能恰恰补上了这一环。它的逻辑可以这样理解在电能充裕时段启动电解槽把水电解成氢气和氧气氢气经过压缩后存入储氢罐等到电能紧张或需要清洁燃料时再让储氢罐放氢通过燃料电池发电或者直接供应给加氢站等氢负荷。整个过程看下来氢能相当于一个跨时段、跨能源品种的双向储能和转换枢纽既能消纳可再生能源又能在负荷高峰期反哺电力同时产出的余热还能进入热力系统。1.2 氢能的“三重身份”负荷、电源、储能从调度建模的角度看氢能设备最让人头疼的地方在于它们的身份一直在切换。电解槽是“负荷”而且是出力可调的柔性负荷燃料电池是“电源”但它的燃料来源是储氢罐储氢罐本身又是“储能设备”存在容量上限、压力动态和调度周期内始末状态约束。一个设备集群同时具备负荷、电源、储能三种属性就使得系统耦合强度远超传统电热联供。举一个具体场景来说明某工业园区配了一个 500kW 的电解槽、一套 200kW 的燃料电池和容量 200kg 的储氢系统。白天光伏出力高系统让电解槽满功率运行假设效率为 70%那么消耗 500kW 电功率可以产生大约 10.5kg/h 的氢气。到了晚间负荷峰值来临燃料电池投入按额定 200kW 放电每小时消耗氢气大约 6kg/h。这样算下来白天产氢 8 小时就是 84kg晚间供能 4 小时消耗 24kg罐容仍有富余。这就是氢能在日内调度中的典型价值把光伏的“垃圾电”转化为负荷高峰期的“救援电”。1.3 研究对象典型的“风-光-气-氢”综合能源系统架构本文涉及的Matlab代码实现对应的系统架构是当前论文里最经典的一类输入侧包含风力发电、光伏发电、上级电网购电、天然气网购气转换侧包含燃气轮机、燃气锅炉、电锅炉、电解槽、燃料电池存储侧包含蓄电池、蓄热罐、储氢罐负荷侧则包含电负荷、热负荷和氢负荷如加氢站需求。这个架构不是随意拼凑的它覆盖了学术界和工程界最关心的三个关键场景可再生能源消纳、多能互补支撑、以及低碳化运行。调度目标通常取系统日运行总成本最小化包括购电成本、购气成本、设备启停成本、运维成本再加上弃风弃光惩罚成本。如果论文偏低碳方向还会把碳交易成本或碳排放惩罚项引入目标函数。从Matlab代码实现的角度来说这个架构的建模挑战集中在三点多能流电、热、气、氢在母线处的能量平衡怎么表达设备的非线性特性如电解槽的效率曲线、燃料电池的电热比如何线性化启停状态这类离散变量如何嵌入连续优化框架。下面的章节就按建模→求解→代码→踩坑的顺序逐个展开。2. 氢能子系统的设备建模与能量耦合逻辑2.1 电解槽电到氢的转换效率不是定值电解槽是氢能系统里最关键也最容易建模翻车的设备。很多初学者直接用一个固定效率 n_el 乘以输入电功率得到产氢速率这在概念上能跑通但离实际情况偏差较大。碱性电解槽技术最成熟、文献中用得最多的运行效率会随着负载率变化一般在 60%~80% 之间浮动低负载率时效率明显下降高负载率时效率接近额定点。为了保持MILP的线性结构代码里通常采用分段线性化处理而不是直接引入效率曲线函数。具体操作是将电解槽的输入功率区间 [P_el_min, P_el_max] 等分为 N 段每段对应一个效率系数再引入连续变量和二进制变量标识运行区间。这样既保证了设备模型的准确性又不引入非线性项Cplex/Gurobi 求解效率基本不受影响。另外电解槽还有一个被很多人忽略的约束——最小稳定运行功率。出于电化学性能和设备保护考虑电解槽不能长期在很低的功率下运行一般要求负载率不低于 20%~30%。在代码里要写成P_el(t) P_el_min × z_el(t)P_el(t) P_el_max × z_el(t)z_el(t) ∈ {0,1}这个 z_el 就是启停状态变量。如果漏掉这条约束求解器完全可能给出一个 5% 负载率的“最优解”实际工程中根本不可行。2.2 燃料电池不光发电还附带产热燃料电池在综合能源系统里的建模逻辑和燃气轮机类似输入氢气输出电力和高温余热。比较常用的模型是质子交换膜燃料电池PEMFC单机容量从几十千瓦到几百千瓦不等发电效率大致在 40%~55%热电联合效率可以达到 80% 以上。调度模型里燃料电池的核心关系有两个。第一个是氢燃料消耗与发电功率的关系F_fc(t) P_fc(t) / (η_fc × LHV_H2)其中 LHV_H2 是氢气的低位热值一般取 33.33 kWh/kgη_fc 是发电效率F_fc 是每小时消耗的氢气量。第二个是热电联产关系燃料电池在发电时产生的余热可以通过换热装置进入热力系统满足一部分热负荷。代码里通常引入一个热电比 β_fc让产热量 Q_fc(t) β_fc × P_fc(t)然后接入热母线平衡方程。这里尤其提醒不要忘记燃料电池也有最小出力限制和启停逻辑。许多论文为了简化只用一台等值机组表示整个燃料电池站这没错但最小技术出力约束一定要保留否则调度结果会在“额定满发”和“完全停机”之间跳变现实中没有任何一台机组敢这么干。2.3 储氢罐跨时段耦合约束的核心储氢罐的作用相当于蓄电池只不过存储量以质量千克计状态变量是氢气的存量。在Matlab里常用连续变量 S_tank(t) 表示 t 时段末储氢罐内的氢气量相邻时段的状态转移方程写作S_tank(t) S_tank(t-1) [F_el_in(t) × η_in - F_fc_out(t) / η_out - F_load(t)] × Δt其中 F_el_in 是电解槽注入储氢罐的氢流量F_fc_out 是燃料电池从储氢罐提取的氢流量F_load 是直接供给加氢站等氢负荷的量η_in、η_out 是充放过程中的压缩/减压损耗系数Δt 是调度时段步长通常取 1 小时。储氢罐的约束相对简明容量上下限约束 0 ≤ S_tank(t) ≤ S_tank_max以及调度周期始末状态约束 S_tank(0) S_tank(T)目的是让氢储能系统在一天的运行结束后回到初始状态这样才能做跨日滚动调度。很多新手会忽略始末状态约束最后求出来一个“把罐子里氢用光”的最优解从数学上没错但工程上完全失去了可持续调度的意义。2.4 系统层面的能量平衡三条母线串起整个调度模型把氢能设备放进IES之后系统平衡约束就变成四条电母线平衡、热母线平衡、天然气母线平衡、氢母线平衡。电母线方程是典型的耦合点P_wind(t) P_pv(t) P_MT(t) P_fc(t) P_grid_buy(t) P_ess_dis(t) P_el_load(t) P_eb(t) P_el(t) P_ess_ch(t) P_grid_sell(t)热母线平衡则要考虑燃气轮机的余热回收、燃气锅炉、电锅炉以及燃料电池产热的共同出力总供热量等于热负荷需求部分模型会允许少量弃热。氢气母线平衡相对简单是一条流量平衡关系F_tank_in(t) F_supply(t) F_load(t) F_tank_out(t)这里的四条母线方程是整个优化调度模型的数据中枢Matlab代码中所有设备变量最终都要汇入这四条母线约束里。如果调试阶段算例出现能量不守恒的问题95% 是这四条约束的符号写反了后面第 5 节会专门讲这个排查点。3. 优化目标与约束条件的数学表达一个可求解的MILP框架3.1 目标函数成本最小化之外的三个“隐藏项”最经典的IES调度目标函数是系统日运行成本最小化展开来包含以下四类成本购能成本向上级电网购电费用、从天然气网购气费用气价通常按体积或质量计价统一换算成天然气能耗kW费用。设备运行维护成本燃气轮机、电锅炉、电解槽、燃料电池、蓄电池、储氢罐各自的运维成本通常按出力或吞吐量的线性函数折算。启停成本针对燃气轮机和电解槽每次启动会产生额外成本或寿命损耗建模时在目标函数中加入启动变量对应的惩罚项同时配合逻辑约束强制变量取值合理。弃风弃光惩罚成本与碳成本前者用单位弃能量惩罚价格乘上弃风弃光量后者在部分低碳模型里有单独的碳排放配额约束或碳税成本项。一个关键的“隐藏项”是启停成本不写进目标函数而只写启停逻辑约束会导致模型出现频繁启停的病态解。因为如果不加启停成本MILP求解器会认为每小时自由切换启停状态没有代价于是给出一个技术上荒谬的调度方案。这个问题在我协助复现的多个案例里出现过具体放在第5节展开。3.2 约束条件的松紧设计什么该硬什么该软约束设计直接决定了模型的可解性也反映了研究者对物理系统的理解深度。以我经验有五类约束必须写成硬约束不得松弛四条能量母线平衡约束上文已列设备出力上下限约束含启停状态如燃气轮机和电解槽的出力范围爬坡约束燃气轮机、电解槽的相邻时段出力变化率限制储氢罐、蓄电池的容量上下限和充放能速率约束调度周期始末储能状态一致约束。有两类约束可以做成软约束通过加入松弛变量和惩罚项处理一类是氢负荷、电负荷在极端场景下的少量削减切负荷成本极高但允许发生另一类是热负荷的微小弃热或者不足用惩罚项约束。需要注意的是对蓄电池和储氢罐这些“储能类”设备充放电/充放氢状态的互斥性是一个常见的建模深水区。很多初学者写出的约束是“蓄电池充电功率 × 放电功率 ≤ 0”这在MILP里是没法直接表达的。正确姿势是引入两个二进制变量 z_ch 和 z_dis然后加约束0 ≤ P_ch(t) ≤ P_ch_max × z_ch(t)0 ≤ P_dis(t) ≤ P_dis_max × z_dis(t)z_ch(t) z_dis(t) ≤ 1三组约束合在一起才能严格表达“同一时刻充放电互斥”。这部分逻辑虽有技巧门槛但在Yalmip框架里写起来会很顺手。3.3 非线性环节的线性化把“不好解”变成“能解”氢能设备的引入会在模型里带进三类典型的非线性环节电解槽效率随负载率变化第2.1节燃料电池的氢耗与发电功率之间部分文献写成功率的一次或二次关系天然气管网的流量压强非线性单点模型通常忽略网络模型需要处理。在Matlab代码实现里主流策略是全部线性化后调用MILP求解器。二次效率曲线用分段线性逼近替代功耗关系用分段线性函数压强非线性的处理在单点IES里直接不做只保留等值机组模型。这样做有一个隐性收益MILP模型具备全局收敛性不像MINLP那样依赖初始值这对做科研复现和参数敏感性分析来说非常重要。3.4 求解器选型YalmipCplex/Gurobi为什么是默认组合Matlab下做优化调度建模目前最舒服的方式是Yalmip工具箱加Cplex或Gurobi求解器。Yalmip提供了一套人性化的建模语法可以很容易地把变量定义、约束集合和目标函数以接近数学表达式的方式写出来然后自动转换成求解器需要的标准格式。我个人的经验是默认优先用Gurobi只有在Gurobi不可用或规模足够小的时候才退回Cplex。两者的差别在几百个变量的小算例里几乎看不出来但当调度周期从24小时扩展到168小时型或设备数量翻倍、约束条数上千之后Gurobi的求解速度和内存稳定性会明显好一截。这里注意一点不管用哪个求解器都要在Yalmip里显式指定所在路径并确保授权已在Matlab环境下加载。这一步看起来琐碎实际占了新手“跑不通”原因的六成以上。4. Matlab代码实现框架从数据结构到算例复现4.1 代码目录结构与数据输入模块这个项目的Matlab代码不算特别长但模块界限必须清晰否则后期加设备、改场景时会非常痛苦。一个可行的目录组织如下IES_Hydrogen/ ├── main.m % 主程序定义系统数据调用建模与求解 ├── data/ │ └── case_data.m % 输入数据负荷曲线、风光出力、设备参数、分时电价 ├── models/ │ ├── build_buses.m % 构建四条母线平衡约束 │ ├── build_mt.m % 燃气轮机模型 │ ├── build_el.m % 电解槽模型 │ ├── build_fc.m % 燃料电池模型 │ ├── build_h2tank.m % 储氢罐模型 │ └── build_storage.m % 蓄电池蓄热罐模型 ├── solve/ │ └── solve_ies.m % 调用Yalmip和求解器 └── results/ ├── plot_results.m % 绘图出力曲线、储能状态、成本构成 └── output_report.m % 输出关键指标弃风弃光率、购能成本、氢产耗量main.m的核心任务是定义时间尺度通常是 24 个时段、从case_data.m中加载数据、调用各build_*函数构建约束最后交给solve_ies.m求解并输出结果。这种“一个设备一个函数”的组织方式最大的好处是增加新设备时不需要修改主程序内部的已有模块只要新增一个build_xxx.m在主函数里追加两行调用即可。4.2 设备建模函数的关键写法以电解槽为例一个完整的Yalmip模型函数大概长这样function [cons, cost_expr, var] build_el(data, T) % 变量定义 P_el sdpvar(1, T); % 输入电功率 z_el binvar(1, T); % 启停状态 H_el sdpvar(1, T); % 产氢量 cons []; % 出力范围 cons [cons, P_el data.P_el_min .* z_el]; cons [cons, P_el data.P_el_max .* z_el]; % 爬坡约束 for t 2:T cons [cons, -data.ramp_el P_el(t)-P_el(t-1) data.ramp_el]; end % 产氢量与耗电的分段线性关系简化示例为线性 H_el data.eta_el ./ data.LHV_H2 .* P_el; % 运维成本 cost_expr sum(data.om_el .* P_el); var struct(P_el,P_el, z_el,z_el, H_el,H_el); end注意这里用. *对矩阵做逐元素相乘binvar定义0-1变量Yalmip会自动将变量时间序列向量化处理。最关键的是返回参数里要把该设备涉及的变量集中打包到var结构体里这样在main.m组装四条母线平衡约束时可以直接引用var.P_el等字段省去到处找变量的麻烦。4.3 求解流程与结果输出求解部分的逻辑其实相当简单ops sdpsettings(solver,gurobi,verbose,2, ... showprogress,1, gurobi.TimeLimit,300); result optimize(cons_all, objective_all, ops);optimize函数返回后要立刻检查求解状态如果result.problem 0说明求解成功如果result.problem 1说明模型不可行直接去查约束集合里有没有冲突如果result.problem 3说明求解超时或数字困难要么加时限要么检查是否存在大范围数量级悬殊的系数。结果输出方面建议先输出三个核心指标总运行成本、弃风弃光率、储氢罐全天总充放量。这三个指标能一眼判断出模型到底有没有把氢能系统“用起来”。如果调度结果里电解槽全天都是关机状态那要么是氢价/电价激励不够要么是建模约束把电解槽卡死了需要回头检查数据。4.4 一个简化算例的复现思路与初始参数为了不涉及知识产权问题这里给一个从公开文献典型数值整理的标准算例参数读者可以拿来作为自己代码的自测基准。系统时间尺度取 24 小时步长 1 小时电负荷kW在 500~1200 之间波动热负荷在 300~800 之间波动风光出力曲线为典型的“光伏正午高、风电夜间高”形状。设备参数项数值燃气轮机额定功率400 kW燃气轮机最小出力率30%电解槽额定功率 / 最小功率300 kW / 90 kW电解槽额定效率70%燃料电池额定功率 / 最小功率200 kW / 60 kW燃料电池发电效率45%储氢罐容量200 kg储氢罐初始氢量100 kg蓄电池容量600 kWh分时电价峰/平/谷1.0 / 0.6 / 0.3 元/kWh购气价—2.5 元/m³对于上面这套参数一个合理的调度结果应该是白天光伏大发的午间时段电解槽以较高功率运行晚间燃料电池在负荷峰值时段放宽出力储氢罐在日末回到初始 100kg 附近弃风弃光率控制在 5% 以下。如果你跑出来的储氢罐趋势完全不同大概率是成本参数激励方向错了。5. 跑通算例后的实战总结参数陷阱、收敛问题与结果分析5.1 排查链路从“不收敛”到“结果诡异”MILP模型“求不出解”和“求出离谱解”是两种完全不同的头痛。先说不可行的情况我建议按下面的链路排查基本能解决九成问题第一步检查所有储能设备的始末状态约束。这个最常见——某同学把储氢罐初始量设成 80kg容量上限却只有 100kg而又强行要求日末等于初始量中间还要满足氢负荷结果根本跑不出可行解。第二步检查四条母线平衡方程的符号。电平衡方程里等式右边负荷项忘记加电锅炉和电解槽的功耗就会导致模型“凭空多出一堆电能”给出偏高的风电出力甚至无限购电的反常解。第三步检查爬坡约束的单位。如果出力单位是kW而爬坡速率常数写成了MW级那么约束会瞬间收紧到几乎无法调度直接不可行。如果求解器报告求解成功但结果严重违反物理直觉比如储氢罐一天充放了800kg而容量只有200kg那多半是线性化环节出了问题——典型的是分段线性化时区间变量的约束写漏了导致求解器在变量边界上“作弊”。5.2 氢能设备参数的灵敏度问题算例跑通之后很多人会做参数敏感性分析这里有一个特别容易误导人的点氢能设备的参数与常规设备参数对总成本的影响尺度差异极大但作用方向往往出乎意料。例如电解槽效率从70%降到60%产氢量下降储氢罐在日内的可用氢量变少燃料电池夜间出力被迫减少系统转而依赖更高成本的燃气轮机——总成本上升这是符合直觉的。但如果你把电解槽额定功率从300kW提高到500kW系统总成本未必下降原因是电解槽的运维成本和启停成本随容量增大而增加若光伏余电不够高功率电解槽只能在低负载率下运行效率掉得更厉害。更反直觉的是氢气售价氢负荷卖给加氢站的收入对调度结果影响极大但总成本未必单调下降。原因是氢负荷售量增加时电解槽需要多产氢多消耗的电从哪里来如果这个时段处于电价高峰期那么“多卖氢”反而可能让购电成本暴涨总成本不降反升。做灵敏度分析时这样的“交叉影响”才是论文里的真正亮点也值得在汇报时重点呈现。5.3 对比设计的基准是“有氢无氢”还是“不同氢价场景”结果分析部分我建议至少跑三组对照算例而不是只跑一版主算例第一组是无氢能系统把所有氢能相关设备的投建成本和启停变量直接移除只保留燃气轮机、电锅炉、储电蓄热作为基准方案。第二组是含氢能系统基础运行方案采用上文第4.4节的全部设备氢价取中等水平。第三组是高氢价或强碳排放约束方案用于观察氢能在价格激励或碳约束下是否会被更加充分地利用。这三组之间的差值才是“计及氢能”四个字的价值所在。第一组和第二组的总成本差值体现了氢能设备引入后的净收益第二组和第三组的差异则体现政策强度对氢能利用率的推动作用。在Matlab代码层面只需要在main.m里用一个布尔开关控制是否调用build_el、build_fc、build_h2tank即可十几分钟就能完成全套对比。5.4 一个容易被忽略的边界条件启停次数的隐性约束最后提一个我在多份代码里反复看到的问题很多模型只限制电解槽和燃料电池的最小运行功率却不限制它们的启停次数。调度结果经常出现一天内电解槽启停五六次每段只运行两三小时的情况。从MILP数学角度看这是可行的从电化学装置寿命角度看这是灾难。工程上通常的做法有两种。第一种是简单粗暴地给启动次数加上限例如sum(z_start_el(t), t1..T) ≤ N_max需要额外引入启动变量 z_start_el(t)并用线性约束把“停机到运行的转换”表示出来。第二种做法是增加启动成本惩罚项让求解器自己权衡启停带来的收益和代价。就我观察第二种做法在代码实现上更简洁也更容易在论文里解释“经济性驱动的运行策略”所以更推荐优先使用。6. 最后再分享两个实践中的小技巧第一个是关于求解规模的把控。如果你的调度周期从 24 小时扩展到 168 小时一周设备启停变量和储能状态变量的数量会成倍增长Gurobi 求解时间可能从十秒级暴涨到小时级。这时候别急着换电脑或换机器精度先检查你的储能状态变量是不是被写成了多层嵌套索引数组尽量把时间维压在最外层循环上Yalmip 的稀疏表达效率会高很多。第二个是关于结果绘图和汇报。氢能系统调度结果最好的可视化方式不是堆叠面积图而是双轴图左轴显示电母线供需曲线右轴显示储氢罐存量的变化曲线。这样一眼就能看出“什么时候电解槽在充氢、什么时候燃料电池在放电、氢储能在这中间起到了多大的削峰填谷作用”。我见过很多报告全篇都是复杂的表格反而不如一张这样的双轴图来得直观。我在给某位同学调试项目代码时还注意过一个问题氢负荷为零的时段如果系统仍然让电解槽满功率产氢结果往往是因为储氢罐容量和初始氢量的设置不当导致“必须产氢来填容积”。小心这种约束驱动的伪调度行为它会让你的论文结论失真。最后一个建议和代码水平无关但对做研究很重要。跑完一版结果后别急着改参数重跑先花十分钟回到目标函数和四条母线方程逐项核一下每一笔成本和每一股能流的单位统一了没有。氢能系统最大的隐患不是算法复杂而是kW、kWh、kg、m³、元/吨这类单位在转化过程中悄悄错位。单位不乱模型基本就稳了单位一旦乱掉所有灵敏度分析都会变成一场自欺欺人的数字游戏。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑