尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

光热电站-ORC-P2G综合能源优化调度的Matlab实现与约束建模

光热电站-ORC-P2G综合能源优化调度的Matlab实现与约束建模 综合能源优化调度这个方向这几年最热门的玩法之一就是把光热电站、有机朗肯循环和P2G放进同一个模型里用Matlab做滚动调度。我最近正好在Matlab里把这么一套代码从头到尾跑通了这套系统初看有点混搭实际理解之后会发现它特别完整光热电站负责可控的可再生发电ORC负责回收低品位余热P2G负责把富余电力转成天然气——三个环节各管一段但能量在中间是环环相扣的。这篇就把我从建模到代码落地、再到踩坑复盘的全过程写出来给想复现同类项目的朋友一个可以直接跟着走的地图。1. 先把能量流理清楚CSP、ORC、P2G在这个系统里的角色1.1 光热电站不是太阳能发电而是带储热的可调度电源很多刚接触光热电站CSP的人第一反应是这不就是光伏的另一种形式吗。实际上两者在调度模型里的地位完全不同。光伏没有储能出力曲线基本由光照决定调度员只能被动消纳而光热电站自带熔盐储热系统集热场收集的热量可以先存进热罐等到晚高峰再放出来发电。所以CSP更应该被理解成一个可平移出力的电源。我在代码里把光热电站拆成三个模块集热场、储热罐、发电岛。集热场负责把太阳能变成热储热罐负责在时间维度上搬运热量发电岛负责把热变成电。这三个模块之间的热量分配就是调度决策的核心。白天阳光好而电价低的时候可以把集热场的热量存起来而不是立刻发电晚上没有阳光但电价高的时候储热罐放热发电岛满负荷运行。这种弃光不弃热、储热不储电的特性是CSP区别于光伏风电的最大价值。1.2 ORC解决的是低温余热怎么再利用的问题有机朗肯循环Organic Rankine Cycle, ORC说白了就是一种利用低温热源发电的热力循环。我们熟知的蒸汽朗肯循环用水的沸点太高100℃左右的余热根本带不动汽轮机但有机工质比如R245fa沸点低60℃到200℃的热源就能让它蒸发做功。所以在综合能源系统里ORC的角色是余热回收器。在我这个系统里ORC的热源从哪里来主要两个一是光热电站发电岛排出的低品位余热二是P2G过程电解槽和甲烷化反应产生的反应热。这两股热汇入一根余热母管ORC从母管中取热发电。这样一来原本要被冷却塔散掉的废热变成了额外的电能输出。模型里我用一个效率系数把输入热功率直接折算成输出电功率再配上热源可用量的上限约束就够用了。1.3 P2G把多余电能变成天然气打通电网和气网P2GPower to Gas是电转气的简称。常见路线是电解水制氢氢气再和二氧化碳发生甲烷化反应生成合成天然气直接注入天然气管道。整个过程耗电但不耗气相当于把电网里的富余电力变成气网里的能量。为什么要引入P2G因为光热电站虽然可控但还是会碰到热量太多、储热罐满了、电价又太低的尴尬局面。这时候与其弃热不如让P2G加负荷——它把电吃掉CSP就能多发电多余的热量就有了去处。P2G产出的天然气还可以替代一部分外部购气减少系统运行成本。所以P2G的价值不是省电而是给系统增加了一条能量出路。1.4 一次完整的能量流动示例24小时视角我给你一个24小时视角的典型场景。白天8点到16点DNI太阳法向直射辐射强集热场产热多。早高峰和晚高峰电价高发电岛多发电午间电价低多余热量充入储热罐同时P2G启动用低价电制气。16点之后太阳落山集热场产热为零发电岛改由储热罐放热维持出力一直撑到晚高峰结束。ORC则在白天吸热发电夜间如果余热母管没有足够热源就降出力甚至停机。这个调度结果不是拍脑袋拍出来的而是优化模型根据分时电价、负荷曲线、设备成本和效率自动算出来的。明白了能量流后面所有约束公式就都有了物理意义。设备角色与输出形式一览设备能量输入能量输出在系统中的角色CSP集热场太阳能热能系统的热量源CSP储热罐热能热能时间平移系统的热蓄水池CSP发电岛热能电能可控电力源ORC低品位余热电能余热回收增值P2G电能天然气电-气耦合转换2. 设备建模不复杂关键是约束要写全2.1 CSP三模块建模集热、储热、发电CSP的建模我习惯分三块写每一块就是一个公式组。集热场产热功率 Q_sf 由DNI、集热面积 A_sf 和集热效率 η_sf 决定Q_sf(t) DNI(t) × A_sf × η_sfDNI是外部输入A_sf是设计参数。这里注意DNI的单位一般是 W/m²A_sf是 m²算出来的结果是W要除以1e6转成MW否则后面单位全乱了。储热罐用一个状态变量 E_tes 表示罐内热量MWh为单位。相邻时段的储热量关系E_tes(t1) E_tes(t) η_ch × Q_ch(t) - Q_dis(t) / η_dis其中 Q_ch 是充热功率Q_dis 是放热功率η_ch 和 η_dis 分别对应充放热效率。储热罐有容量上限 E_tes_max充放热功率也各有上下限。发电岛发电功率 P_csp 与输入热量 Q_pb 之间P_csp(t) η_pb × Q_pb(t)发电功率有上下限 P_csp_min、P_csp_max还有爬坡约束。这三个模块之间靠热量平衡串起来Q_sf(t) Q_dis(t) Q_ch(t) Q_pb(t) Q_dump(t)这里的 Q_dump 就是弃热变量。它非常重要——没有这个变量模型会无解因为热量必须守恒有了它代价函数里才体现弃热有成本优化器才会想办法让P2G和ORC多出力来减少弃热。2.2 ORC的线性效率模型与余热来源约束ORC的模型最直接输入热功率 Q_orc_in输出电功率 W_orc用一个综合效率 η_orc 折算W_orc(t) η_orc × Q_orc_in(t)余热母管的总可用热量 Q_waste_avail 是外部余热 Q_waste_ext 加上P2G工艺余热 Q_p2g_heatQ_waste_avail(t) Q_waste_ext(t) Q_p2g_heat(t)ORC取热量不能超过母管可用量0 ≤ Q_orc_in(t) ≤ Q_waste_avail(t)ORC本身也有额定容量上限 W_orc_max爬坡速率限制按实际情况加。这里我踩过一个坑如果不把母管余热量约束写进去优化器会让ORC凭空变出热量来满足热负荷结果电出力虚高跑出来的数据完全不能用。余热来源约束就是ORC模型的物理边界。2.3 P2G转化约束和化学反应余热P2G的输入是电功率 P_p2g输出是天然气功率 G_p2g按热值折算G_p2g(t) η_p2g × P_p2g(t)η_p2g 是电解甲烷化的综合效率工程上大概0.55到0.65。也就是说1 MW电进去能出来0.55到0.65 MW热值的气。P2G有最小技术出力 P_p2g_min 和最大出力 P_p2g_max还有启停成本。如果模型不引入二进制变量就只做连续调度P_p2g_min 通常设0要做更精细的调度再上binvar。P2G还会产生反应热Q_p2g_heat(t) μ_p2g × P_p2g(t)μ_p2g 我习惯取0.2左右。这股热进入余热母管成为ORC的热源之一。模型里P2G、ORC、CSP就是这么互相咬合的P2G消耗电能→产生余热→ORC把余热转化为电→系统总购电成本下降。能量在设备间绕了一个圈最后都体现在目标函数里。2.4 电、气、热三张平衡表整个系统的能量平衡分三张表每张都必须是流入流出。电平衡P_buy(t) - P_sell(t) P_csp(t) W_orc(t) P_load(t) P_p2g(t)P_buy 是向电网购电P_sell 是向电网售电P_load 是系统电负荷。如果购电价始终高于售电价优化器不会同时买卖两个变量可以共存如果遇上特殊峰谷价差建议加一个互斥约束防止病态结果。气平衡G_buy(t) G_p2g(t) G_load(t)G_buy 是外部购气G_load 是气负荷。P2G产气直接对冲购气成本。如果系统允许向气网售气可以在等式右侧加一个售气项目标函数里加售气收益。热平衡Q_heat_buy(t) (1 - η_orc) × Q_orc_in(t) Q_p2g_heat(t) Q_load(t)这里把ORC发完电后的尾热和P2G反应热都列为供热来源不够部分靠外部购热 Q_heat_buy 补齐。这样写的好处是把梯级利用体现出来了热量先进ORC发电再出来供热一热两用。三张平衡表对应三个网络。光热电站跨越了热和电P2G跨越了电和气ORC跨越了热和电。这就是综合能源四个字的含义不是三个独立系统而是靠设备间的耦合约束物理绑定在一起。3. 目标函数与求解器选型让模型算得动又算得对3.1 目标函数不止是买电买气成本优化调度最常见的目标是最小化总运行成本。我在代码里写的目标函数包括六项obj_t price_buy × P_buy - price_sell × P_sell price_gas × G_buy c_heat × Q_heat_buy c_om_csp × P_csp c_om_orc × W_orc c_om_p2g × P_p2g c_dump × Q_dump逐项解释一下购电成本、售电收益、购气成本、购热成本、三类设备的运维成本最后是弃热惩罚。这里的 c_dump 很关键如果设成0优化器发现弃热比发电还省事就会大面积弃热设一个远高于发电运维成本的惩罚值P2G和ORC才有存在的意义。我一般取 c_dump 80 元/MWh大概两倍于光热发电的边际成本。目标函数里每一项都有现实含义不是随便凑的。购电价是分时电价我常用的数据是峰时1.1元/kWh、平时0.7、谷时0.35售电价按低于购电价处理气价按2.2元/m³左右折算成能量价格单位元/MWh。这些参数会让调度结果出现明显的削峰填谷特征结果图好看物理上也说得通。3.2 LP还是MILP变量类型决定求解难度如果只做连续调度不考虑设备启停状态和最小技术出力这个模型是纯线性规划LPMatlab自带的 linprog 就能解。但实际项目里光热电站夜间停机、P2G启停、ORC投入或切除都需要二进制变量来表达。一旦引入 binvar问题就变成混合整数线性规划MILP。MILP的求解难度比LP高一个量级。我建议的做法是第一版跑纯LP验证设备约束和平衡方程写对了第二版再给设备加最小技术出力和启停成本升级成MILP。这样做的好处是一旦MILP结果异常你可以回到LP版本逐步对比定位是约束问题还是整数变量引入的问题。我这里还有一条经验基础模型尽量保持线性。ORC的效率、P2G的转化率如果用二次曲线描述模型会变成QP甚至NLP求解时间翻几倍收益却不大。工程上完全可以把变效率简化成两段分段线性函数既保留效率随负荷变化的特征又不破坏线性结构。3.3 Yalmip CPLEXMatlab里的标准玩法Matlab里做优化调度我强烈建议用Yalmip做建模层。它把变量定义、约束集合、目标函数封装得非常干净底层求解器可以自由切换CPLEX或Gurobi。我项目里用的是CPLEX原因是教育版许可证好拿安装也简单。ops sdpsettings(solver, cplex, verbose, 2); ops.cplex.mip.tolerances.mipgap 0.01; % 1%的gap足够工程使用 sol optimize(cons, objective, ops);Yalmip对初学者有个特别友好的地方约束写错导致模型不可行时把sdebug, 1 加上去它会告诉你哪条约束出了矛盾。我在第四章里写的代码就是基于Yalmip的语法来组织的。底层求解器用Gurobi也完全兼容只需要把solver改成gurobi。4. 代码落地一套可复现的Matlab项目结构4.1 数据准备典型日负荷与DNI曲线代码的第一步永远是准备数据。我会单独建一个data_define.m脚本里面放三块内容设备参数、分时电价、时序曲线DNI、电负荷、气负荷、余热供给。设备参数我建议用结构体组织避免变量名冲突。% 光热电站参数参考100MW槽式电站 csp.P_csp_max 100; % 发电上限 MW csp.P_csp_min 10; % 技术出力下限 MW csp.eta_sf 0.42; % 集热场效率 csp.eta_pb 0.38; % 发电岛效率 csp.eta_ch 0.97; % 充热效率 csp.eta_dis 0.97; % 放热效率 csp.E_tes_max 500; % 储热容量 MWh csp.A_sf 600000; % 集热面积 m^2 % ORC orc.eta 0.16; % 综合发电效率 orc.W_max 20; % 额定容量 MW % P2G p2g.eta 0.60; % 电转气综合效率 p2g.P_max 30; % 最大耗电 MW p2g.P_min 0; % 最小耗电 MW p2g.mu_heat 0.20; % 反应热产出系数这里有一个参数要注意ORC的η取0.16不代表效率低而是低温ORC的真实水平就在12%~20%之间。发电效率看着不高但能源来源是废弃热量边际成本几乎是零所以它仍然有经济价值。DNI曲线我用典型日数据的实测序列时间分辨率取1小时24个点就够了。如果要更精细可以用15分钟间隔但设备爬坡约束要同步调整。首次调通建议只用24点验证逻辑之后再扩展T96。4.2 决策变量声明用sdpvar统一管理在Yalmip里所有决策变量都用sdpvar或binvar声明。我习惯按设备分组声明变量名和模型里的物理量一一对应后面写约束时不会搞混。T 24; P_csp sdpvar(T, 1); % CSP发电功率 Q_ch sdpvar(T, 1); % 充热功率 Q_dis sdpvar(T, 1); % 放热功率 Q_dump sdpvar(T, 1); % 弃热功率 Q_pb sdpvar(T, 1); % 进发电岛热功率 E_tes sdpvar(T1, 1); % 储热罐热量T1是为了存末状态 W_orc sdpvar(T, 1); % ORC出力 Q_orc_in sdpvar(T, 1); % ORC输入热功率 P_p2g sdpvar(T, 1); % P2G耗电 G_p2g sdpvar(T, 1); % P2G产气 Q_p2g_heat sdpvar(T, 1); % P2G反应热 P_buy sdpvar(T, 1); % 购电功率 P_sell sdpvar(T, 1); % 售电功率 G_buy sdpvar(T, 1); % 购气功率 Q_heat_buy sdpvar(T, 1); % 购热功率E_tes声明成 T1 是因为储热状态要覆盖整个调度周期最后一个元素对应调度结束时刻的储热量这个值后面要在约束里和初值挂钩。4.3 约束构建循环写法和矩阵化写法怎么选约束构建是代码里最容易出错的地方。我推荐第一步先用 for 循环一行一个约束简单直观跑通了再说优化。下面这段就是储热罐的核心约束cons []; for t 1:T % 集热场热量平衡 Q_sf DNI(t) * csp.A_sf * csp.eta_sf / 1e6; cons [cons, Q_sf Q_dis(t) Q_ch(t) Q_pb(t) Q_dump(t)]; % 发电岛 cons [cons, P_csp(t) csp.eta_pb * Q_pb(t)]; % 储热罐状态转移t1时刻 cons [cons, E_tes(t1) E_tes(t) csp.eta_ch * Q_ch(t) - Q_dis(t) / csp.eta_dis]; % 储热容量和充放热上限 cons [cons, 0 E_tes(t) csp.E_tes_max]; cons [cons, 0 Q_ch(t) 80]; cons [cons, 0 Q_dis(t) 80]; % P2G约束 cons [cons, G_p2g(t) p2g.eta * P_p2g(t)]; cons [cons, Q_p2g_heat(t) p2g.mu_heat * P_p2g(t)]; cons [cons, p2g.P_min P_p2g(t) p2g.P_max]; % 母管热量与ORC Q_waste_avail Q_waste_ext(t) Q_p2g_heat(t); cons [cons, 0 Q_orc_in(t) Q_waste_avail]; cons [cons, W_orc(t) orc.eta * Q_orc_in(t)]; cons [cons, 0 W_orc(t) orc.W_max]; % 电力平衡 cons [cons, P_buy(t) - P_sell(t) P_csp(t) W_orc(t) P_load(t) P_p2g(t)]; cons [cons, 0 P_buy(t), 0 P_sell(t)]; % 气平衡 cons [cons, G_buy(t) G_p2g(t) G_load(t)]; cons [cons, 0 G_buy(t)]; % 热平衡 cons [cons, Q_heat_buy(t) (1 - orc.eta) * Q_orc_in(t) Q_p2g_heat(t) Q_load(t)]; cons [cons, 0 Q_heat_buy(t)]; end % 储热罐周期始末状态 cons [cons, E_tes(1) 0.5 * csp.E_tes_max]; cons [cons, E_tes(T1) 0.5 * csp.E_tes_max]; cons [cons, 0 E_tes(T1) csp.E_tes_max];循环写法的缺点是当 T 很大时约束集合会变得冗长求解器构建模型的时间也会变长。第二个版本我通常会把循环内的纯时序约束换成矩阵形式比如P_csp P_csp_max * ones(T,1)。但矩阵化版本可读性差调试起来让人头痛。我的原则是先循环后优化模型验证无误前不做性能优化。4.4 求解输出与结果可视化求解代码写在solve_model.m里加一个结果合理性断言比直接画图更重要ops sdpsettings(solver, cplex, verbose, 1); sol optimize(cons, objective, ops); if sol.problem ~ 0 error(求解失败: %s, sol.info); end P_csp_val value(P_csp); P_p2g_val value(P_p2g); W_orc_val value(W_orc); E_tes_val value(E_tes);结果可视化我习惯画三张图第一张是发电与用电功率曲线CSP、ORC、P2G、购电堆叠在一起第二张是储热罐SOC曲线第三张是天然气平衡柱状图。这三张图能在5秒内判断调度结果是否合理。曾经我跑出来的储热罐SOC曲线在夜间莫名其妙上升一查是放热效率公式写反了——这种问题通过看图一眼就能发现。5. 跑通后的复盘那些不会写在论文里的坑5.1 储热罐的始末状态约束不能省第一次跑模型时我把储热罐初始热量设成满罐只约束调度周期内不能超过容量上限结果优化器直接在第一个时段把储热全部放完之后的时段靠购买电力应付。这个结果虽然数学上最优物理上完全没意义——没人会把这两天唯一的一次储热机会在第一小时用光。解决办法就是我在第四章代码里写的初值设为一个合理值比如50%容量末端约束留足余量不低于初值。这样调度策略会保持周期性今天的储热余量能留给明天模拟多日滚动调度时结果才可信。这个坑表面上是约束缺失本质上是模型没有远期眼光。和电池储能调度一样储热罐的SOC末端状态不约束等价于你借了未来一天的热量来补贴今天成本核算失真。5.2 单位换算和天然气热值系数极易翻车综合能源系统最大的单位陷阱有两个热功率和电功率之间的换算、天然气体积和能量的换算。DNI乘集热面积算出来是W要除以1e6变成MW。我亲眼见过同学忘了做这步除法Q_sf变成几千MW约束无解折腾半天。天然气的热值更隐蔽如果气负荷给的是m³/h而P2G产气功率用的MW两边根本没法直接相加。常用的换算关系是1标准立方米天然气低热值约9.7 kWh也就是0.0097 MWh。我建议把所有能量统一成MW和MWh再进模型。气负荷、购气量、产气量全部按热值功率MW表示电价和热值再用各自的价格系数统一成元/MWh这样才保证目标函数里每一项都是同一个量纲。5.3 P2G装进系统并不会自动发电要看价格信号这是整个项目里最反直觉的一个现象。我第一版代码跑完后打开结果发现P2G整个调度周期出力为零当时我以为模型写错了。后来查目标函数才意识到P2G的综合效率只有0.61 MW电进去只出0.6 MW的气。如果电价格和气价格相当P2G怎么算都是亏损的优化器当然不会让它出力。要让P2G真正参与调度要么在目标函数里给弃热设置高惩罚逼系统给多余热量找出口要么给产气设置额外收益比如碳排放积分折算、政府补贴要么利用分时电价低谷期拉低购电成本让低价电变高价气变得有利可图。这三种机制在设计目标函数时至少要占一样否则P2G就是一个永远不出力的摆设。这种装了不用的现象恰恰说明优化调度模型的价值——它不会因为设备存在就自动利用它只有经济逻辑成立设备才会被调度。这也是做综合能源仿真最迷人的地方每台设备的命运都由价格信号决定。5.4 求解器gap、调试顺序和结果合理性检查最后聊几条工程经验。第一MILP的求解gap不要一开始就设0%我习惯先设1%跑通流程后再收紧。CPLEX在高gap下找到的次优解往往已经符合工程预期直接设0会大幅增加求解时间对24×7的滚动调度来说不值得。第二调试顺序很重要先跑纯LP版本确认所有平衡约束通过、所有变量非负再逐个加入整数变量每加一个跑一次结果对比加入前后的出力差额。一次性把多个二进制变量全加进去出了问题根本不知道是谁引起的。第三任何调度结果都必须通过极端场景测试才有说服力。把DNI设为0跑一遍系统应该完全依靠储热和购电维持负荷把电价全部设为极高值再跑一遍CSP应该在储热允许范围内满发。这两个测试如果结果不符合预期说明约束里还有逻辑漏洞。我自己做这套代码最大的体会是建模和编程本质上是一体的物理约束写不全代码再漂亮跑出来的都是垃圾结果。每一条约束都要能对应到实际设备的物理限制每个变量都要能指出它来自哪个设备哪个环节。带着这个思路去做这套含光热电站、有机朗肯循环和P2G的综合能源优化调度项目你也会有自己的实践经验的。
返回列表