ARTICLE DETAIL

资讯详情

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

电热综合能源系统日前经济调度:从热电耦合建模到Matlab/YALMIP实现

电热综合能源系统日前经济调度:从热电耦合建模到Matlab/YALMIP实现 北方冬季的凌晨风电场出力曲线和城市热负荷曲线几乎是两条背道而驰的线半夜风大、光伏归零热负荷却因为寒潮拉到峰值。这种“风大时电多、夜里负荷低、热负荷又压不住”的矛盾几乎是每个以热电联产机组为主的园区调度室的日常。前些年我在做电热综合能源系统的日前经济调度项目时翻调度日志看到一组数据供热期弃风率一度达到18%。原因很简单——CHP机组按“以热定电”方式运行为了满足供热电出力被死死绑住风电再便宜也挤不进去。这篇内容就来完整讲清楚这个课题从建模到Matlab代码实现的全过程怎么描述热电耦合设备、怎么用目标和约束表达“消纳可再生能源”的意愿、怎么把模型写成YALMIP能直接求解的形式以及算例结果该怎么解读。适合正在做综合能源调度方向研究的硕博生也适合刚接手园区热电优化项目的工程师参考。1. 热电耦合下的“调峰困境”为什么要做日前经济调度1.1 “以热定电”是怎么卡住风电的先把这个核心矛盾讲透。热电联产机组CHPCombined Heat and Power在供热期的工作方式是“以热定电”系统热负荷给定后机组的电出力就被限制在一个很窄的区间内。因为煤在锅炉里燃烧产生的热量一部分用来发电另一部分通过抽汽或排汽送去供热这两者不是独立的而是强耦合关系。打个比方CHP机组就像一辆既拉货又带暖气的卡车发动机转起来必然发热冬天还得靠这个热去供暖。问题是热负荷越高车就越不能熄火可发出来的电如果没人用就成了“半夜开着大功率发动机只为了供暖”的窘境。风电这边又是另一副脾气。夜间风资源通常更好但电网负荷处于低谷白天负荷上来风电出力却经常掉下去。供热季和非供热季的差异更明显非供热季时传统火电可以自由调峰给风电腾空间一旦进入供热期大量CHP机组被热负荷锁定系统可调容量急剧下降风电只能被“挤”出去。这就是为什么很多北方地区冬季弃风率反而更高和直觉相反。要打破这个僵局思路只有两条一是让CHP机组的电出力与热出力“解耦”二是给系统增加额外的灵活性资源。前者可以通过配置储热装置和电锅炉来实现后者对应储能、需求响应等手段。在电热综合能源系统里最常用、最经济的解耦手段就是前两种——这也是本文模型的核心设备。1.2 日前经济调度在“解耦”中扮演的角色有人会问既然问题的根源在设备层面为什么还需要一个“调度模型”因为设备只是提供了可能性怎么用、什么时候用、用多少需要提前安排好。举个例子电锅炉可以消耗电能产热相当于人为增加电负荷。凌晨风电大发时段让电锅炉把风电“吃”进去变成热能送入热网或储热罐白天电负荷高了储热罐再放热替代CHP机组供热让CHP机组可以降出力。这套“低电负荷时把电变成热存起来、高电负荷时放热替机组减负”的策略必须提前一天排好才能让热网、电网、机组、储热装置在24小时尺度上协调运行。这就是日前经济调度的价值所在——它解决的不是某一刻的最优运行点而是一天24小时的时序协调问题。电在那几个小时便宜、热在那几个小时紧张偏偏热负荷的变化还滞后于电负荷这些问题都必须放进同一个优化模型里统一决策。经济性体现在煤耗成本和弃风惩罚的加权取舍可行性体现在电力平衡、热力平衡、设备出力上下限、储热动态等几十条约束同时满足。所以说搭建这个模型的基本逻辑很清晰把设备特性写成约束把运行经济性和弃风损失写成目标让求解器在满足物理规律的前提下挑出一条24小时出力计划。2. 建模前先算清账设备模型与耦合关系2.1 CHP机组的电热可行域怎么描述整个模型里最重要、也最容易被写错的部分就是CHP机组的可行域。不同文献对它的处理差别很大有的用背压式机组简化为直线关系有的用抽气式机组画出四边形可行域。实际工程里抽气式CHP机组更常见它的电出力和热出力不是固定比例而是可以在一个多边形区域内活动。简化后的数学模型一般这样写。对于第 i 台CHP机组电出力 P_chp 和热出力 H_chp 满足如下线性约束组P_min c_h * H_chp P_chp P_max - c_l * H_chp 0 H_chp H_max其中 c_h、c_l 是描述热电耦合斜率的系数。供热抽汽越多机组能够发出的最大电功率就越小最小电功率也越低。这个“梯形”可行域比简单的线性比例关系更贴近实际机组在一定范围内可以在“多发电少供热”和“少发电多供热”之间调节但范围有限。注意一个建模细节很多初学者会把这个耦合关系直接写成 P a * H这是背压式机组模型灵活性最差用于研究“解耦必要性”可以但用于实际园区调度会过于保守。四边形的抽气式模型适用范围更广代价是多几条约束求解压力基本可以忽略。2.2 风电、电锅炉、储热罐的建模细节风电部分日前调度中通常采用“预测出力作为上限”的处理方式。设 W_pref 为风电预测出力序列P_wind 为实际消纳功率那么约束就是0 P_wind W_pref关键区别在于目标函数里要不要加“弃风惩罚”。如果不加惩罚模型很可能为了省煤耗而主动弃风加了弃风惩罚后模型才有激励去尽量消纳风电。惩罚系数的量级不能随便拍脑袋太大则宁可多烧煤也要消纳风电太小则和没加差不多。通常的做法是以煤耗成本为基准再折算环境价值取一个适中的单位弃风惩罚电价。电锅炉的模型相对简单本质是把电能转换为热能效率一般在95%以上H_eb eta_eb * P_eb 0 P_eb P_eb_max虽然公式简单它的调度价值却很大电锅炉相当于给系统增加了一个可控电负荷凌晨让它高负荷运行吃掉风电产出的热可以直供热网或存入储热罐。这样既解决风电消纳又缓解供热压力一举两得。储热罐则是另一个关键设备它像一个“热能充电宝”用动态约束描述S_tank(t1) S_tank(t) * (1 - loss) H_chg(t) * eta_chg - H_dis(t) / eta_dis S_min S_tank(t) S_max 0 H_chg(t) U_chg_max 0 H_dis(t) U_dis_max其中 S_tank 是储热量H_chg、H_dis 分别是充放热功率loss 是散热损失率。储热罐不需要与电网直接耦合它改变的是热力侧的时序结构让热能从“一边产一边用”变成“可以错峰生产和消费”。这一条动态约束就是解耦CHP机组“以热定电”的物理基础。除以上设备外文章如果考虑常规火电机组还需要加上传统出力上下限和爬坡约束P_g_min * u_g P_g P_g_max * u_g P_g(t) - P_g(t-1) ramp_up P_g(t-1) - P_g(t) ramp_downu_g 是0-1启停变量。在含CHP的系统中常规机组角色是补充调节CHP负责基荷供热和部分供电常规机组负责电负荷缺口和调峰。要不要引入启停变量取决于你的算例规模和对机组组合问题的还原程度。纯经济调度ED阶段可以固定机组启停、只优化出力模型会从MILP退化成LP求解难度大幅下降如果要做完整的机组组合UC则必须引入0-1变量。3. 日前调度模型的数学骨架目标函数与完整约束集3.1 目标函数不仅算煤耗还要算弃风把目标函数单独拎出来说是因为它的设计直接影响模型行为。最常见的日前经济调度目标函数是系统总运行成本最小化min sum_t(sum_i( F_g(P_g) F_chp(P_chp) C_wind_curtail * P_curtail ) )展开来看每一项都有讲究常规机组煤耗成本 F_g(P_g)通常写为二次函数 a*P² b*P c。YALMIP支持直接写二次项Python的CVXPY也可以但如果要追求求解速度、把模型保持为纯MILP就需要把二次曲线分段线性化。这个取舍我会在第四章详细说明。CHP机组煤耗成本 F_chp(P_chp, H_chp)这里要注意CHP的成本函数同时是电出力和热出力的函数。不要只写成 a*P_chp² b*P_chp c 的形式而丢掉热出力变量那会与实际煤耗偏差很大。更规范的一种写法是F_chp a0 a1*P_chp a2*H_chp a3*P_chp^2 a4*P_chp*H_chp a5*H_chp^2这是二次形式线性化后也可以转化。实际算例中为了简单常用线性式 F_chp f_p*P_chp f_h*H_chp f_fixed对短期调度的相对优化结果影响可以接受。弃风惩罚项 C_wind_curtail*P_curtail变量 P_curtail 定义为 预测出力 - 实际消纳也就是弃风电量。加入这一项模型就在“多烧煤保证消纳”和“弃风省钱”之间做权衡。惩罚电价取多少合适后面实战部分会给参考区间。如果课题涉及“双碳”目标还可以在目标函数中加入碳交易成本项。这个扩展不改变模型结构只是给煤耗成本再叠一个碳价系数很容易集成。3.2 约束分类与每条约束的物理含义约束是整个模型里量最大的部分也是最容易出bug的地方。总结一下完整约束集第一类是功率平衡约束。电功率平衡要求所有电源出力之和等于电负荷与电锅炉耗电之和sum(P_g) sum(P_chp) sum(P_wind) P_load sum(P_eb)这里必须把电锅炉的耗电以“负荷”身份写进等式右侧这是电锅炉建模中最容易遗漏的点——忘记加 P_eb模型会认为电锅炉的电凭空消失结果出现虚假的消纳效果。热功率平衡则要写成sum(H_chp) sum(H_eb) H_dis - H_chg H_load注意储热罐的充放热写在平衡式左侧同时位次不能乱充热是“热负荷”放热是“热源”。第二类是设备出力上下限和爬坡约束前面已经列过。第三类是储热罐动态约束也是唯一带时序耦合的约束。第四类是风电出力与弃风变量约束P_wind P_curtail W_pref这其实是把“弃风电量”显式地定义出来在目标函数里才能惩罚它。很多文章不写这一条而是直接在目标里惩罚 W_pref - P_wind效果一样但在结果统计时要自己额外算弃风率。最后如果考虑系统安全性还要加旋转备用约束机组上调备用和风电出力之和要大于等于预测负荷加上备用容量。在无储能、快速响应资源稀缺的系统中这条约束往往决定系统能不能接纳更多风电。模型最终的数学性质是如果引入机组启停0-1变量和储热充放状态变量它是一个混合整数线性规划MILP或二次约束规划MIQP如果固定启停计划则退化为线性规划LP。实际研究中MILP用Cplex/Gurobi配合YALMIP可以高效求解24~96时段规模的问题不用担心算不动。4. 从公式到代码Matlab实现的关键动作4.1 数据准备与模型参数化代码实现的第一步不是写求解代码而是把数据规整成两个部分系统参数和时序曲线。系统参数指机组台账、效率、容量、成本系数——这些是常数矩阵时序曲线指24小时的电负荷、热负荷、风电预测出力——这些是1×24的行向量。我的习惯是把所有参数集中放到一个结构体里而不是散落在脚本各处否则后面对比工况时会改到怀疑人生。% 24小时数据示例单位MW par.T 24; par.P_load [120 118 115 112 110 108 105 110 130 140 138 135 132 130 128 132 138 140 142 138 130 125 122 118]; par.H_load [85 83 82 80 78 78 80 82 84 86 88 89 90 90 89 88 87 86 85 84 83 84 85 86]; par.W_pref [35 38 42 45 46 44 38 30 26 22 20 18 16 15 17 21 28 32 36 38 40 42 43 41];注意热负荷的曲线形状夜间并不低反而略高这就是供热期热网的高惯性和用户夜间保温需求共同作用的结果。风电预测曲线则呈现典型的“夜间高、午间低”。两条曲线叠加一目了然能看出调度冲突。4.2 YALMIP建模的核心代码片段使用YALMIP模型代码可以写得非常接近数学表达这也是我推荐它的原因。不需要手写海森矩阵、不需要自己封装求解器接口优化问题的表达和维护都很直观。先定义变量%% 决策变量 u_chp binvar(2, 24); % CHP启停状态 P_chp sdpvar(2, 24); % CHP电出力 H_chp sdpvar(2, 24); % CHP热出力 P_g sdpvar(2, 24); % 常规机组电出力 u_g binvar(2, 24); % 常规机组启停 P_wind sdpvar(1, 24); % 风电实际消纳 P_curtail sdpvar(1, 24); % 弃风电量 P_eb sdpvar(1, 24); % 电锅炉耗电功率 H_eb sdpvar(1, 24); % 电锅炉热出力 H_chg sdpvar(1, 24); % 储热罐充热 H_dis sdpvar(1, 24); % 储热罐放热 S_tank sdpvar(1, 24); % 储热罐储热量然后定义目标函数%% 目标函数 objective sum(sum(0.3 * P_g.^2 20 * P_g 50 * u_g)) ... % 常规机组煤耗 sum(sum(0.25 * P_chp.^2 15 * P_chp 60 * u_chp ... % CHP煤耗简化 5 * H_chp)) ... 80 * sum(sum(P_curtail)); % 弃风惩罚这里弃风惩罚系数取80元/MWh大致量级是煤耗价的1.52倍既能驱动消纳又不会让风电变成无上限资源。实际项目中建议做几组灵敏度扫描后再定。核心约束的添加也很直接%% 约束条件 Constraints []; % 电功率平衡 Constraints [Constraints, sum(P_g,1) sum(P_chp,1) P_wind par.P_load P_eb]; % 热功率平衡 Constraints [Constraints, sum(H_chp,1) H_eb H_dis - H_chg par.H_load]; % 风电消纳与弃风定义 Constraints [Constraints, P_wind P_curtail par.W_pref]; % 电锅炉效率与出力范围 Constraints [Constraints, H_eb 0.95 * P_eb]; Constraints [Constraints, 0 P_eb 50]; % 储热罐动态 S0 30; % 初始储热量 S_tank_ref [S0, S_tank(1:end-1)]; Constraints [Constraints, S_tank S_tank_ref * (1 - 0.02) H_chg * 0.9 - H_dis / 0.9]; Constraints [Constraints, 0 S_tank 200]; Constraints [Constraints, 0 H_chg 40]; Constraints [Constraints, 0 H_dis 40];这里储热罐的动态约束用了向量化写法比在for循环里逐时刻添加更简洁。但要注意向量索引方向S_tank(t) 由前一时刻 S_tank(t-1) 递推得到上面 S_tank_ref 就是“上一位”的取值序列这种写法在YALMIP里完全合法。求解调用%% 求解 ops sdpsettings(solver, gurobi, verbose, 1, mip.tolerances.mipgap, 0.0001); optimize(Constraints, objective, ops); %% 提取并画图 P_chp_opt value(P_chp); H_chp_opt value(H_chp); P_wind_opt value(P_wind); S_tank_opt value(S_tank);选择Gurobi还是Cplex取决于你机器上有哪个license。Gurobi在学术界有免费许可社区支持也比较活跃。如果求解失败第一步不要怀疑模型先看YALMIP返回的诊断信息是数值问题infeasible还是求解器问题license/内存。Infeasible的原因通常不在求解器而在约束本身——这个排查方法很关键后面专门写。4.3 分段线性化该不该做前面提过二次煤耗成本函数的问题。如果机组数量少、时段只有24个直接让Gurobi求解MIQP完全可以。但如果要做96时段、10台机组以上的日内滚动优化MIQP求解时间可能成倍增长这时把目标函数线性化更划算。分段线性化思路很简单把出力范围切成K段每个段上用一个斜率近似原二次曲线。需要引入K-1个0-1变量来保证选择的段连续数学上这是标准“sos2”约束。在YALMIP中可以直接用 sos2 约束Gurobi原生支持SOS2。实现代码大致形态n_seg 4; P_seg binvar(n_seg, 1); % 选择每段激活状态 alpha sdpvar(n_seg, 1); % 段内插值系数 Constraints [Constraints, sum(alpha) 1, sum(P_seg) 1]; Constraints [Constraints, sos2(alpha)]; P_g P_breakpoints * alpha; F_cost F_breakpoints * alpha;注意SOS2约束在YALMIP里要用 sos2 函数包裹权重变量这样求解器才能识别。分段数K取3到5段就足够逼近煤耗曲线的非线性再细分收益很有限。5. 算例结果怎么解读消纳提升要从三个角度验证5.1 基准场景与对比场景设置模型建好之后最关键的验证工作是设置对比方案。没有对比任何“消纳提升”都是自说自话。推荐至少设置三个场景方案一基准传统“以热定电”调度不含电锅炉和储热罐风电按“能消则消”处理。方案二只加电锅炉不加储热罐。此时电锅炉只做即时“电转热”相当于给CHP机组减热负荷。方案三电锅炉储热罐联合配置完全体模型。这样分步设置的好处是方案二和方案三的对比能单独量化储热罐的价值——到底多少弃风消纳增量是储热带来的、多少是电锅炉带来的。5.2 结果分析风电消纳量、运行成本、储热动作以一个中等规模园区示例数据为例我曾经跑出来的结果大致是下面这个趋势数值供参考不同机组参数会有差异指标方案一基准方案二加电锅炉方案三电锅炉储热弃风电量MWh1527813弃风率17.1%8.8%1.5%系统运行成本万元128.4122.7118.2CHP机组总热出力MWh122010801045观察这个表格有三点需要特别注意第一方案三的弃风率从17%压到1.5%这是储热罐的“错峰”能力带来的——电锅炉可以在凌晨瞬间把风电变成热水存起来而不是像方案二那样只能一边产热一边直供热负荷低谷时段的需求限制了它的工作节奏。第二总成本并不是“消纳越多越便宜”的单调关系。理论上如果惩罚系数设置不当可能出现消纳率上升但总成本也上涨的情况这正是惩罚系数在起作用。第三CHP总热出力下降并不代表供热不足而是说明热源结构发生了变化电锅炉和储热罐分担了一部分CHP机组由此可以压低出力相当于把供热压力转移到了风电侧的清洁电能上。画图时我建议至少输出四张风电预测与消纳对比曲线、CHP电出力与电负荷关系、储热罐储热量时序、弃风电量对比柱状图。这四张图能完整支撑“调度模型有效”的结论而且柱状图对论文或汇报材料非常直观。再补充一个论证技巧把方案三的储热罐储热量曲线和风电出力曲线叠加在一张图上。如果凌晨风电大发时储热罐正好处于上升段、午间电负荷高峰时储热罐在放热就说明模型的调度策略符合物理直觉可信度直接提升。这个图和成本表格放一起基本可以覆盖所有审阅者关心的点。6. 实操中的坑与扩展思路6.1 建模期最容易忽略的三个细节第一储热罐的效率和自损耗不能同时取1。有的文章为了简化把充放热效率都设为1、散热损失设为0这样储热罐成了一个完美的能量仓库结果模型会无限倾向“凌晨囤热、白天放热”给出的利用率虚高。实测中罐体的温度和介质不同日散热损失通常在1%~5%充放热效率在0.85~0.95之间这些值会直接影响储热罐的有效容量和调度策略。第二电锅炉耗电必须计入电负荷侧。很多第一次写代码的人会在电功率平衡里漏掉 P_eb 这一项导致系统的“总发电量”和“总用电量”不闭合求解器报infeasible或者给出一个发电量虚高的结果。排查方法很简单把电平衡等式两边的总和打出来看24小时每个时刻是否严格相等。第三热网的热惯性被忽略。日前调度模型如果只做“节点热平衡”意味着热负荷一变热源必须立刻跟上。实际区域供热管网有很强的热惯性管道里的水本身就是储能介质。严谨的做法是在热平衡约束里加入热网回水温度动态或引入“热舒适温度范围”而非“固定热负荷”。不过这种精细化会让模型复杂度上一个台阶作为研究生课题第一版可以先简化在结论里说明假设边界即可。6.2 求解性能优化与不确定性扩展方向如果模型规模大到求解时间不好接受优先检查三个方向一是把机组启停变量的初始值用启发式规则设定并固定变成纯LP求解一次看总成本轮廓二是将储热罐的充放状态变量按照“充放互斥”约束可以用大M法写但实际求解时Gurobi对互补性约束的处理能力很强反而可以在不引入额外0-1变量的情况下先跑一版三是分段线性化目标中的二次项前面已经说过。在模型规模不大2台CHP2台常规机组24时段时Gurobi求解时间通常在10秒以内没必要过度优化。关于风电不确定性确定性日前调度是标准的第一步但它有一个明显的局限性模型默认风电预测完全准确结果在实际运行中可能因为预测偏差导致功率平衡被破坏。往下走有三条常见路线鲁棒优化用不确定集合描述预测误差上下界、随机规划用场景集描述不同概率的误差场景、模型预测控制MPC日内滚动修正。我个人的经验是先扎实把确定性模型做完、结果分析做透再上不确定性方法。确定性模型是地基地基没夯实后面任何一个扩展都会让你分不清问题是出在不确定性处理还是基础模型上。6.3 一个关于惩罚系数的实操建议关于弃风惩罚系数再展开说两句。我见过不少课题在这个参数上栽跟头取200元/MWh时模型宁可每小时多烧很多煤也要把风电拉满取0元时弃风率又高得离谱和“考虑可再生能源消纳”的题目名背道而驰。我建议的做法是先跑一版“无惩罚”模型统计自然弃风率再跑一版“极高惩罚”模型统计最大可消纳量然后在这两个极端之间取几个梯度值扫描画出弃风率和总成本的Pareto曲线。选题上如果碰巧找到“弃风率明显下降而总成本小幅上升”的拐点那组系数就是合适的写论文时放这张双坐标折线图也很有说服力。这个模型的扩展空间比初看起来大得多——接入碳交易就是当下最热的方向之一把碳价加进目标函数看电锅炉储热罐在双碳目标下会不会被更激进地调用或者把电负荷、热负荷、风电预测统一改成区间做两阶段鲁棒调度。但不管往哪个方向走核心还是把基础模型做扎实。本文章的所有公式和代码片段是基于电热综合能源系统调度领域的常见实践整理的通用框架具体设备的效率系数和成本参数需要根据你手上的实际数据替换。希望这份完整的建模和实现思路能帮你少走我当年走过的那几段弯路。
返回列表