计及氢能的综合能源优化调度研究(Matlab代码实现)
做综合能源优化调度这个方向的朋友应该都有体会:刚开始接触时,觉得不就是个线性规划问题嘛,把电、热、气、冷几个母线的平衡约束往那里一摆,目标函数设成成本最小,跑一跑就出结果了。真上手之后才会发现,一旦把氢能这个环节加进来,问题瞬间就不是“多一个设备模型”那么简单了。氢能同时承担了能量载体、储能介质、燃料三种角色,电解槽、储氢罐、燃料电池的联合运行约束和能量平衡关系会让整个系统的耦合程度明显上升。
我在“计及氢能的综合能源优化调度研究”这个课题上断断续续做了一阵子,用Matlab从建模、编码到结果分析完整走了一遍,中间踩了不少坑,也积累了一些算是比较稳定的实现套路。这篇文章把我当时的整体思路、建模方法、代码架构和调试心得整理出来,主要面向正在做或者准备做综合能源优化调度方向的在校学生和入门工程师,也适合想快速复现一个氢能参与调度案例的读者。文章会偏实现细节多一些,但原理部分也不会跳过,毕竟模型建不对或者说约束条件写得不合理,代码写得再漂亮也白搭。
1. 综合能源系统为什么要引入氢能
1.1 从电热联供到“电-热-氢”耦合
很多教材和论文里提到的综合能源系统(Integrated Energy System, IES)通常是以电、热、冷、气四种能源形式为主体,各种能源之间通过热电联产机组、电锅炉、燃气锅炉、吸收式制冷机等设备完成转换和耦合。这类系统的优化调度研究已经很成熟了,核心命题就是在满足各类负荷需求的前提下,协调各设备出力、储能和购能策略,尽可能降低运行成本或者碳排放量。
但我做这个课题的时候关注的并不是传统IES,而是把氢能作为一种独立的能源载体和储能介质引入调度框架。为什么要引入氢能?最直接的原因是风电、光伏等可再生能源的波动性和随机性给电力系统带来很大压力。在“双碳”目标和新能源高渗透率的大背景之外,单从工程角度看,弃风弃光的矛盾就是很实际的经济损失。光伏大发的中午时段,如果电网消纳能力有限,很多新能源电量就只能被舍弃——明明电价便宜甚至免费,却用不上,等于白白浪费。
传统的解决思路是配储能电池,通过时间搬移来削峰填谷。但电池储能有一个问题:它的能量量级和放电时长是有限的,对大规模季节性或者跨天级别的能量搬移并不擅长。氢能恰好在这点上补了短板。电解槽可以在新能源大发时把多余的电转化为氢气,储氢罐解决时间维度上的存储问题,等到负荷高峰或者电力紧张时段,氢燃料电池再将氢气转化为电能回馈电网,或者直接供应给加氢站等氢负荷。
打个比方,电池储能像是短跑运动员,爆发力强但耐力有限;氢能储能更像是“电-氢-电”链条上的一位长跑选手,响应速度没那么快,但能量密度高、存储时间长,适合作为跨时段和跨季节的缓冲环节。
在这个框架下,IES的能源耦合结构从原来的“电-热”双链为主,变成“电-热-氢”三链协同,优化调度的决策空间明显变大。氢能不仅可以作为一种被调度的“储能装置”,还要考虑它作为燃料供给终端负荷的场景。这种双重身份,决定了我们在建模时不能简单地在储能约束里多加一组变量就完事。
1.2 氢能在日前调度中的定位
在日前优化调度的时间尺度下,氢能系统承担的任务很清晰:价格低或者新能源富余时段,启动电解槽制氢,把多余电能变成氢能储存起来;价格高或者负荷高峰时段,燃料电池工作,把储存的氢能变成电能和热能释放出来。在某些场景下,系统还可以直接向外输出氢气(比如卖给加氢站),形成新的售能收入。
这个定位决定了氢能系统的运行方式和传统储能电池是不一样的。第一,电解槽和燃料电池不能同时满功率运行,虽然在物理上也可能允许“一边制氢一边发电”的不合理工况,但在实际调度中通常会设置为互斥或者受到效率约束,避免能量在“电-氢-电”回路里循环损耗。第二,储能罐的约束不仅仅是容量上限,还包括氢气压、温度等物理条件。在简化模型中,通常用储氢量(kg或kWh)和储氢罐容量上下限来表示。第三,氢能系统的效率链不能只看单环节效率,电解水制氢的效率、压缩存储的能耗、燃料电池的发电效率这三段必须分开建模,否则计算出来的总效率会过于乐观,调度结果失真。
我这里在建模时采用了一个相对简单但足够支撑研究的假设:电解槽的制氢效率与功率线性相关,燃料电池的耗氢量与发电功率近似成正比,储氢罐运行不考虑温度和压力动态变化,只考虑容量约束和储放速率约束。这个假设虽然简化了物理过程,但对于研究“氢能参与综合能源优化调度的经济性与调度策略”这个主题来说,精度已经足够,而且求解难度大大降低。如果后续要做精细化研究,再在同一个框架里加入效率曲线、热效应和动态压力模型即可。
2. 系统建模:每个设备都是一个约束
2.1 母线平衡:电、热、氢三条“河”怎么流通
很多没做过建模的人容易把优化调度想成“直接用工具箱求解一个目标函数”的过程,但实际上,建模的难点和核心在于梳理能量流和信息流,任何一个母线的平衡关系出错,求解出来的“最优解”都是空中楼阁。
电母线的平衡方程是最核心的约束。它的形式是:
P_grid + P_wind + P_pv + P_chp + P_fc + P_dis = P_load + P_eb + P_el + P_ch + P_buy
其中P_grid是从上级电网购电的功率,P_wind和P_pv是新能源实际出力,P_chp是热电联产机组的电出力,P_fc是氢燃料电池发电功率,P_dis是蓄电池放电功率,P_load是电负荷,P_eb是电锅炉耗电功率,P_el是电解槽耗电功率,P_ch是电池充电功率,P_buy是弃电惩罚项对应的等效量(在某些模型中还可能有直接向电网售电的项)。
热母线的平衡方程相对简单:
H_chp + H_gb + H_fc_heat + H_dis_heat = H_load
这里H_chp是热电联产的供热功率,H_gb是燃气锅炉供热,H_fc_heat是燃料电池的余热回收功率,H_dis_heat是蓄热罐放热,H_load是热负荷。燃料电池在发电时会产生热能,这部分热量如果不计入模型,热母线就缺了一块能量来源。
氢母线的平衡相对特殊,它更像是一种“库存”平衡:
Q_el - Q_fc_consum + Q_initial = Q_final + Q_supply
其中Q_el是电解槽产氢量,Q_fc_consum是燃料电池耗氢量,Q_initial和Q_final分别是初始和末时刻储氢量,Q_supply是直接售给氢负荷的氢气量。储氢罐在这里承担了缓冲的功能,一个调度周期(通常是24小时)内,储氢量的变化需要满足首末平衡。如果做滚动优化,甚至可以放宽这个首末约束,只需要加一个罚函数确保不会把储氢罐用到极限。
在实际建模时还有一个很容易漏掉的地方:单位统一。电功率单位是kW或者MW,氢量单位在有些文献里是kg,在另一些文献里是kWh。切换单位本身不难,难的是千万别中途换,否则所有系数全部错乱。我当时就是用“kWh”作为全系统统一的能量单位,氢量的kWh换算按照1 kg氢气约等于33.3 kWh的低热值(LHV)来折算。
2.2 设备模型:电解槽、储氢罐、燃料电池不是简单储能
说到设备模型,我建议把每一个设备的模型都当作“一组不等式”而不是一个公式来对待。以电解槽为例,它的约束包括三部分:功率上下限、爬坡速率约束、产氢量与电功率的关系。如果只看一个公式,P_el和Q_el之间的线性关系就够了,但实际调度中爬坡约束非常关键。氢能系统启动响应速度不如电池,电解槽冷启动甚至需要数十分钟,如果调度模型忽略了爬坡限制,求解器会安排“0功率瞬间跳到满功率”这样的不切实际计划,现实中根本执行不了。
燃料电池的约束类似,只是方向相反:它是从氢能到电和热的转换,有最小出力和最大出力限制,同时还有热电比约束。注意一点:燃料电池运行时热电比并不是固定的,在简化模型中我采用了定热电比,但更精细的模型可以设计成电出力较高时段热回收比下降的分段线性关系。
储氢罐的模型要区分能量型和功率型约束。能量型约束是指储氢罐的容量上限和下限,这大家都能想到。容易被忽略的是功率型约束,也就是制氢功率和燃料电池消耗功率同时受制于气管道流量或压缩机能力。比如电解槽产氢速率不能超过某值,燃料电池消耗速率也不能超过某速限值。在Matlab代码里,这组约束的物理意义是“流经管道的流量受限”,我见过不少论文里只写容量约束、漏掉速率约束的,导致结果低估了削峰填谷的代价。
此外还有一个SOC(State of Charge)连续性方程,也就是时刻t+1的储氢量等于t时刻的储氢量加上产氢量减去消耗量和外供量。这里要注意,储氢罐的效率损耗通常不是直接乘在SOC方程上,而是在产氢和耗氢端分别乘上效率和存储损失系数。
我这里列一下我当时用的主要参数(基于常见实践的值,供参考):
| 设备 | 参数 | 数值 |
|---|---|---|
| 电解槽 | 功率范围 | 50 kW - 500 kW |
| 电解槽 | 制氢效率 | 65%(按LHV折算) |
| 燃料电池 | 发电效率 | 45% |
| 燃料电池 | 热电比 | 0.8 |
| 储氢罐 | 容量上限 | 1000 kg |
| 储氢罐 | 储氢速率上限 | 50 kg/h |
| 电网购电 | 峰谷电价 | 峰1.2元/kWh,谷0.4元/kWh |
这些参数不是固定答案,不同项目和论文各有差异,但方向性是一致的:电解槽效率低于燃料电池发电效率的逆过程,这是物理条件决定的,别在代码里把效率设反了,否则可能出现“氢永远不要用”或者“永远只制氢”的极端解。
2.3 目标函数:多目标如何归一化
优化调度的目标函数在很多论文里写成“运行成本最小”,听起来很简单,但拆开看至少包含这几部分:
第一是购电成本。它等于各时段购电功率乘以对应时段的电价,这里的电价是峰谷分时电价,所以调度优化策略很大程度上就是“在谷电时段多用电(包括启动电解槽制氢),在峰电时段少购电甚至通过燃料电池卖电”。
第二是燃料成本。热电联产机组和燃气锅炉消耗天然气的成本,可以用天然气的单价乘燃气量来计算。燃气消耗量通常近似为出力的线性或二次函数。
第三是设备运维成本。每个分布式电源和储能设备都有单位出力的运维成本系数,比如风机运维成本约为0.02元/kWh,光伏为0.01元/kWh,燃料电池因为系统复杂,运维成本会高一些,大概0.03元/kWh左右。
第四是弃能惩罚成本。这是很多论文里容易被忽略的一块。如果没有对弃风弃光设置惩罚项,优化器可能在新能源大发时段直接选择“弃掉”多余电量而不启动电解槽,因为启动电解槽虽然能产氢,但增加了运维成本。如果题目研究的是“促进新能源消纳”,这一步不做,核心目标就丢了。我是通过设置一个较高的弃电惩罚系数来避免这种情况,比如惩罚系数设为0.6元/kWh,高过启动电解槽的单位成本,这样优化器就会权衡“弃电亏钱”还是“制氢挺划算”。
第五是氢能销售收入。如果系统配置了对外供氢,需要在目标函数里加上负项(收入项),激励系统在氢价较高时多产氢外卖。不过氢价和各时段售电价格之间要有合理的数值对比,否则可能出现为了挣氢能收入而疯狂购电制氢的极端行为——这在实际场景中并不经济,因为购电成本和制氢效率决定了“电转氢”是否能挣钱。
我当时实现的目标函数最终形式是:
min F = C_grid + C_fuel + C_om + C_curtail + C_penalty - I_hydrogen
其中C_penalty通常是蓄电池和储氢罐违反容量约束的松弛惩罚。多目标的问题在这里做成了单目标加权求和,通过改变各项的权重系数,可以反映不同调度偏好。比如在低碳场景中,把碳排放系数乘上碳税加到目标函数里,就可以考察氢能对碳减排的影响。
3. Matlab代码架构与核心算法实现
3.1 架构选择:脚本式还是面向对象
Matlab写优化代码,最直观的思路是写一个巨大的线性规划模型,把约束全部堆进去,用linprog或者intlinprog求解。这在一开始确实能快速出结果,但一旦系统的元件数量变多、模型版本迭代频繁,这种“脚本堆叠法”的痛点就会集中爆发:改一个设备的参数要满文件找变量,新增一种设备类型可能要把每个约束都往原有矩阵里插几行,而且不同场景下的设备配置切换非常不灵活。
这个课题里面我选择了基于Matlab OOP(面向对象编程)架构来组织整个模型,当时还犹豫了一下会不会过度设计,后来发现这个决定省下了大量重复劳动。
利用OOP的初衷很朴素:把电网、电解槽、储氢罐、燃料电池、热电联产机组等每个元件封装成一个类,每个类内部维护自己的参数、状态变量和约束生成方法,调度主程序只需要调用各个设备的接口,拿到本时段的出力和约束矩阵,再按照母线平衡关系拼接起来。
比如电解槽类的核心方法大概长这样:
classdef Electrolyzer < handle properties P_min {mustBeNumeric} P_max {mustBeNumeric} eff {mustBeNumeric} ramp_limit {mustBeNumeric} end methods function obj = Electrolyzer(P_min, P_max, eff, ramp_limit) obj.P_min = P_min; obj.P_max = P_max; obj.eff = eff; obj.ramp_limit = ramp_limit; end function [Aub, bub] = addConstraints(obj, T) % 生成电解槽功率上下限和爬坡约束矩阵 Aub = kron(eye(T), [eye(1); -eye(1)]); bub = repmat([obj.P_max; -obj.P_min], T, 1); end end end这段代码只是示意,实际项目中还涉及SOC连续性约束、产氢量与电功率的耦合约束等。但思路已经体现出来了:每个类负责输出自己的A矩阵块,主约束拼接的时候只需要用blkdiag或者矩阵分块拼装即可,笔误和重复约束的概率大幅下降。
3.2 数据流与主程序框架
整个代码的数据流分三层。最底层是设备类,包含各类设备的参数和状态变量;中间层是系统组装层,把设备类实例化并构建整体约束矩阵;最上层是调度求解层,设置目标函数系数、调用求解器获取结果。
主程序框架可以简化为下面几个步骤:
第一步,初始化设备对象。这一步是读取基础参数,创建电解槽、燃料电池、储氢罐、热电联产机组、电锅炉、蓄电池等类的实例。
第二步,读取负荷和新能源预测数据。数据来源可以是历史记录、模拟生成或者外部导入。注意价格数据也是这个阶段读入的,24小时峰谷电价直接影响调度决策。
第三步,构建决策变量向量。我习惯把所有变量按时间顺序展开成一个长向量,格式为[x(1), x(2), ..., x(96)]这样的形式,其中每个x(t)本身又是一个包含当日各设备出力和状态的小向量。这种“整体大向量+分块小向量”的结构,配合reshape函数操作起来最顺手。
第四步,调用YALMIP或者直接调用CVX这类建模工具箱来构建模型。如果偏好原生的linprog函数,也可以手动拼Aeq、beq、Aub、bub和f向量。我当时两种都试过,结论是:如果是线性规划模型,用linprog加手动拼接就可以;一旦加入旋转备用约束或者效率变量的非线性关系,推荐用YALMIP,里面加约束和设定目标函数直观得多,调试起来也方便。
第五步,求解并输出结果。求解完成后,把最优变量从优化变量形式还原成方便理解的表格和图,比如24小时的设备出力曲线、储氢罐SOC变化、母线平衡图等。
3.3 求解器与关键参数调试
关于求解器的选择,线性规划首选linprog,混合整数线性规划用intlinprog,非线性的话根据问题规模可以选fmincon或者利用YALMIP内部调用的gurobi/cplex(如果有授权)。我当时的主要模型是线性约束加线性目标,所以直接用linprog就能求出全局最优解。
但如果设备模型里包含启停变量(比如电解槽在低负荷区间运行效率极差,需要设计成0-1变量控制“开或关”),问题就变成混合整数线性规划(MILP),求解器需要用intlinprog。MILP的求解速度明显慢于纯LP,特别是当调度周期从24小时扩展到168小时(一周)时,变量数量直接乘以7,求解时间上涨可能不止一个量级。
这里有一个实用技巧:先用LP松弛版本跑一遍,观察最优解中有没有不合理的“半开半关”状态。如果这些状态影响着关键结果,再升级为MILP。我当时在仿真中发现,如果没有启停变量,电解槽会被安排在极低的功率区间运行,这在效率曲线中对应的制氢效率非常差,不符合实际。因此设置了最小出力约束(P_min),同时引入启停变量,模型升级为MILP。intlinprog的求解时间在92维变量下小于2秒,完全可接受。
在调试时还有一个容易踩的坑:linprog默认要求目标函数的系数向量f和决策变量顺序一致,如果多个设备变量的顺序在约束矩阵和目标函数里不一致,求解器并不会提示,给出的“最优解”其实是另一个问题的解。我的经验是:用一个全零向量做一次测试,检查是否正确返回“求解成功”且所有变量为零;然后用单位向量逐一测试,逐个检查约束是否有意义,这种“逐列审计”的方法虽然费时间但非常可靠。
另外,价格数据里有一个潜在问题:如果谷电电价过低(比如低于运维成本加折旧),可能出现“即使没有多余可再生能源也大规模购电制氢再放电”的套利行为。这在实际中是可能的对吧?如果谷电0.4元/kWh,电解效率0.65,燃料电池发电效率0.45,那么“电-氢-电”全程效率只有29.25%,意味着0.4/0.2925=1.37元/kWh的成本,高于峰电电价1.2元时就不划算,优化器不会干这事,约束不用专门设置。但如果某个参数组合恰好让电-氢-电“有利可图”,这种循环套利会让调度结果非常怪异,需要仔细排查是否为参数设置不合理。
4. 优化调度结果分析与典型场景
4.1 典型日场景:风电大发加上用电高峰
我在研究中设置了两组典型场景做对比。第一组是不含氢能系统的传统综合能源系统(只含燃气热电联供、电锅炉、蓄电池和燃气锅炉);第二组是在第一组基础上加入电解槽、储氢罐和燃料电池的氢能系统。两组场景下,风电出力和负荷数据完全一致,只改变设备组合。
对比结果跟直觉一致的:加入氢能系统后,弃风率从19.4%下降到了4.2%,总运行成本降低了约7.8%。成本的降低主要来自两部分:一是弃风惩罚支出大幅减少;二是谷电时段利用低成本电力制氢,替代了部分高峰时段的高成本燃气发电。氢能系统带来的储氢罐累积量和燃料电池发电出力曲线也比较符合预期:凌晨风电大发时段,电解槽处于高负荷制氢状态,储氢罐SOC快速上升;早晨电负荷大幅攀升后,燃料电池启动发电,储氢量下降。
但这里有一个问题值得注意:储氢罐和蓄电池虽然都是储能设备,但在调度结果里它们的分工有明显差异。蓄电池更适合短时高频的削峰填谷,它的充电和放电往往发生在一天内相邻的几个时段;储氢罐则承担“跨时段缓冲”的角色,更多参与从凌晨到早晨这种长间隔的能量转移。这种分工结构在调度曲线图上非常直观:电池的SOC曲线波动频率高,储氢罐SOC曲线平缓得多。
4.2 参数敏感性分析:氢价和电解效率的影响
做了基础对比之后,我又做了一组参数敏感性分析,主要考察制氢效率和对外供氢价格对调度策略的影响。
制氢效率从55%提升到70%时,电解槽在相同输入功率下产氢量增加,系统对氢能的使用更加积极。具体表现为:平均电解槽负荷率上升约9个百分点,燃料电池发电量也有一定提升。这说明制氢效率的提升不仅影响产氢量,还会通过“氢变电”的路径影响电力系统的调度布局,这是一个联动效应。
而对外供氢价格的敏感性则更明显。当供氢价格从20元/kg上升到40元/kg时,储氢罐中用于外卖的氢量占比从12%上升到39%,同时热电联产机组的出力也发生了变化。因为外供氢收入远大于“氢转电”的间接收益,系统在负荷高峰期更有意愿启动电解槽制氢——即使这些氢气并不直接用于发电。这说明氢能系统的经济性很大程度上依赖于氢市场的价格信号,如果只研究电力系统而不考虑氢能消纳和价值出路,得出的结论可能会低估氢能的价值。
4.3 与文献结果的对比验证
做完仿真后,我特意找了几篇公开发表的同类研究论文做对比。由于系统和数据不同,绝对数值肯定有差异,但几个关键趋势是一致的:氢能可以有效降低弃风率;氢能系统的经济效益受电解效率、氢价和峰谷价差三个因素共同影响;高频波动的削峰填谷主要由电池承担,氢能更偏向长时间尺度的能量搬移。
对比验证这一步非常重要,很多人做完仿真就结束了,认为模型跑出结果就完事了。实际上,没有与文献结果做对标验证的调度研究,很难判断模型是否存在隐性错误。我当时发现我的“弃风率下降幅度”比别人稍微明显,排查后发现是弃电惩罚系数设置得偏高,降低了惩罚后,数据就合理多了。
5. 常见问题与排查技巧实录
5.1 建模中的常见错误
第一个高频错误是单位不一致。电动率、热功率、氢量、天然气量四个维度的单位如果都是“元/kWh”,那没问题,但氢量经常以kg为单位出现在储氢罐约束里,天然气量又按标准立方米计算,混用之后系数错得相当离谱,但不报错。建议在模型最开始将所有物理量统一折算成kWh,氢用低热值折算,天然气用标准热值折算,设定一个明确的单位基准。
第二个高频错误是热母线上的“热功率”和“热负荷”使用不同的参考温度基准。供热系统的供回水温度不同,热功率的计算需要乘温差系数,如果不注意,热平衡关系虽然数值对上,但实际是两个不同基准下的数据,完全不可比。
第三个问题是SOC连续性的“首末约束”处理。在长期运行调度中,要求储氢罐最终容量等于初始容量是合理的,否则下一周期无法进行。但在短期日前调度中,这个约束可能导致“存储电量被迫在最后一小时放完”的奇怪结果。我的解决办法是在目标函数中加入一个小的非负惩罚项,例如lambda * (SOC_final - SOC_initial)^2,来松弛首末平衡,这样结果更符合实际。
5.2 Matlab编程中的典型坑
我总结Matlab实现过程中最坑的几类问题:
第一类是矩阵维度不匹配但不报错的情况。YALMIP在这一方面做得比较好,约束维度对不上会提示错误,但linprog手动拼接时经常会出现width不匹配但隐式扩展不报错的问题(在较新版本Matlab里,向量和矩阵相加会自动广播),这会导致约束矩阵多出一些零列或错位列,只影响结果不影响运行。防错的办法就是上面提到的“单位列审计”。
第二类是变量初始化与stateflow冲突。如果采用OOP框架,class里的handle属性制是有状态的,前一个场景跑完,后一个场景直接复用同一个对象时,对象的属性可能保留了之前的计算结果。这会导致比如储氢罐SOC初始值不对,甚至可能是上一次的末端值。这个问题排查了好几个小时才找到原因。解决方法是每个场景重新实例化对象,或者给类增加一个reset方法,把所有状态变量归零或恢复到初值。
第三类是求解器容差设置不合理导致的“伪最优”。linprog和intlinprog默认的ConstraintTolerance和OptimalityTolerance是1e-6左右,但如果模型数值尺度差异大(比如目标函数系数范围从0.01到10000),默认容差会导致优化结果与预期差异很大。建议在求解前对变量和约束做归一化处理,或者手动调整容差参数为1e-9,能有效减少这类问题。
5.3 参数调节经验:从“跑得通”到“解得对”
代码“跑得通”和“解得对”之间隔着巨大的距离。如果你看到的调度曲线像锯齿一样跳来跳去,大概率不是策略本身,而是约束有问题。
一个典型的case是电解槽和燃料电池同小时段同时工作,造成能量的无谓损耗。理论上,优化器应该避免这种低效行为,但因为两者的功率变量是两个独立优化变量,如果约束中没有设置互斥关系,求解器有时会让二者同时运行,以利用两者的爬坡边界避免违反其他约束。要根治这个问题,可以设置一个简单的互斥约束:P_el(t) * P_fc(t) = 0,但这是非凸约束,会破坏线性性。在工程实践中,我更多是用效率惩罚或添加一个小的设备启停代价来间接抑制同时运行;或者通过合理的参数设计让同时运行在经济上不划算。
另一个case更隐蔽:当系统中同时存在蓄电池和储氢罐时,优化器可能会让两个存储设备的SOC同时波动,导致能量路径变得复杂冗余。我在一次复现中看到了充满戏剧性的结果——蓄电池在峰电时段放出大量电能,同时热负荷峰段储氢罐也在释放氢能,两者“并肩作战”看似合理,但仔细看却发现蓄电池放出的电量又被电解槽拿来制氢了,相当于白白损失了两层效率。
这说明优化问题的可行域比我们想象的要大得多,很多看似“合理”的调度方案其实内含能量循环。建议在初步结果分析时,画出各个设备的出力曲线叠加图,用肉眼看一遍SOC和功率变化是否存在异常“耦合”,再检查目标函数的各部分占比是否合理。必要的情况下,增加能量流路径约束(比如限制电转氢再转电的总量)来防止无意义循环。
6. 后续扩展:从确定性调度到鲁棒优化
把确定性日前调度的框架跑通之后,可以顺着几个方向做自然扩展。
第一个方向是考虑预测不确定性。风电、光伏和负荷预测值不可能完全准确。如果只做确定性调度,实际运行时会频繁出现偏差,影响经济性和安全性。扩展方式是改成两阶段鲁棒优化或者随机优化,通过场景法或模糊集来描述不确定性。第一阶段做日前决策,第二阶段做实时调整。在Matlab里做这个扩展,已有的OOP架构优势就体现出来了:每个设备类只需要增加一个“后续场景调整”的方法,主程序再嵌套一个求解循环即可。
第二个方向是用强化学习方法替代或补充数值优化。我对PPO(Proximal Policy Optimization)做了一些调研,发现它在处理储能调度和综合能源市场决策这类问题上有独特优势:数值优化方法在模型参数变化时需要重新求解,强化学习通过训练策略网络可以实现快速在线决策。Matlab本身有Reinforcement Learning Toolbox,用起来比从头写Python环境方便,但要注意环境模型搭建和状态空间设计需要一定工程量。我当时的经验是,先把数值优化结果作为“专家示范”,用模仿学习做预训练,再切换到强化学习做微调,收敛速度和稳定性都比直接训练好很多。
第三个方向是把氢能系统与交通领域的加氢站负荷联合调度。加氢站负荷具有很强的不确定性,而且大规模重型卡车加氢的时空分布与电力负荷特征完全不同。我之前单纯研究电力系统与氢能协同的调度模型里,氢负荷是固定的外生量,确实简化了不少。如果能把加氢需求预测模块嵌入模型中,整个系统的联动复杂度和实用价值都会提高。
最后再分享一个我个人在实际调代码过程中的体会:综合能源优化调度这类课题,数学模型和编码能力两手都要抓,但真正让研究有区分度的往往是建模的细致程度——你有没有考虑到设备的爬坡约束、储氢罐的速率限制、热负荷的温度等级差异。这些细节直接决定了“看起来一模一样”的两个研究方案在结果上的巨大差异。
如果手里有一套普适性的Matlab代码框架,后续扩展新设备、新约束、新求解器都是在同一套母版上做叠加,而不是推倒重来。我的建议是,花两天时间把代码架构搭扎实,后面能省出来的时间远超这个投入。另外,在写每一行涉及物理单位的代码时,都把自己当成“在给一个不懂的人讲物理”,这样检查效率会高得多。希望这篇文章能帮到正在做氢能调度方向的朋友,有问题可以随时交流。