☰
热电联产机组联合优化控制:基于储热罐与电锅炉的风电消纳策略
2026/10/5 3:15:43 网站建设 项目流程

去年冬天帮一个做园区综合能源的朋友做供热期运行方案优化,凌晨两点那段的运行日志我到现在都记得:室外接近-15℃,电负荷在低谷区间,风场接近满发,但两台热电联产机组依然维持着相当高的电出力——为了满足供热需求,电出力根本压不下来,调度最后只能下达弃风指令。这个画面是理解整个问题的钥匙。风电最大化消纳的热电联产机组联合优化控制,核心要解决的就是这件事:让热电机组别再因为“必须发热”而锁死电出力,把原本属于风电的发电空间腾出来。所谓联合优化控制,就是在满足供热需求的前提下,把热电机组、储热罐、电锅炉这些设备放在一个模型里统一决策,让它们互相配合,把弃风压到最低。

这篇文章适合刚接触电力系统优化调度、做毕业设计或者正在做综合能源项目的同学。我会把问题根源、数学模型、Matlab代码实现框架、算例结果和几个实战中容易踩的坑都过一遍。文章里的参数和代码结构,都是可以直接拿去改参数就跑的。

1. 供暖季弃风的根源:不是电网不够强,是“以热定电”绑死了调峰空间

1.1 风电反调峰特性:把“时间错配”变成了系统压力

风电出力和用电负荷在时序上天然不同步,这在北方供暖期体现得尤其明显。白天用电高峰时段,风电出力往往一般般;到了后半夜,全社会用电负荷降下来了,大风却经常在这个时段“可劲儿吹”。对电网调度来说,这种“负荷低谷时风电反而多”的特性叫反调峰,它直接带来一个问题:系统需要上调峰能力。

什么叫做上调峰能力?就是其他电源能不能在风电多的时候主动降低出力,给风电让路。火电机组有最小技术出力,常规火电能压到额定出力的40%-50%左右就不错了,热电联产机组在供热期更夸张,最小技术出力会被“热”抬得很高。调峰空间就这么被压缩了,风电多出来的电没有地方去,只能弃掉。

我举个具体的数例。假设后半夜电负荷是500 MW,风电场可出力300 MW。如果系统里的电源能把自身出力压到200 MW,那么300 MW风电全能消纳。但如果热电机组因为供热需求,最小电出力抬到了280 MW,那么风电最多只能消纳500-280=220 MW,剩下80 MW只能弃掉。这就是弃风问题的本质:电源侧灵活调节能力不足,风电即使有电也送不出去。

1.2 背压与抽凝:两种机组、两种“锁死”方式

热电联产机组跟普通火电机组的最大区别在于,它的电出力不是独立变量,而是跟热出力耦合在一起的。要理解这个耦合,得先分清两种主流机型。

第一种是背压式机组。它的逻辑很简单:进了汽轮机的蒸汽做完功之后,不排入凝汽器,而是直接去供热。这意味着什么?发多少电,就对应供给多少热,电热比基本固定。你说我想多发电少发热?做不到。想少发电多发热?也不行。它就是个刚性绑定。我经常跟学生打比方:背压机组就像一个固定齿比的自行车,你蹬一圈轮子转固定的角度,没有换挡的空间。在供热期,这种机组只要有热负荷,电出力就基本被定死了,完全没有调节余地。

第二种是抽凝式机组。它稍微灵活一点:蒸汽在汽轮机里做功后,一部分抽出去供热,一部分继续进入凝汽器冷凝。通过调整抽汽量,可以在一定范围内实现热电比的变化。但这里有个关键的物理约束——凝汽器必须保持一定的蒸汽流量,否则真空度维持不住,机组运行安全性会出问题。所以抽凝机组虽然能调,但调节范围依然是受限的,电出力下限会随着热出力的增大而抬升。

用一句话总结:背压机组是“完全锁死”,抽凝机组是“半锁死”。在冬季供热期,这两种机组叠加在一起,系统的调峰能力就被压得死死的了。

1.3 解耦思路:储热罐和电锅炉分别做了什么

既然问题的根源是“热把电绑住了”,那解决思路就很清晰了:把“当前时刻必须供热”这个刚性约束想办法松弛掉,或者让热量来源多元化。

储热罐的思路是“时移”。它就像一个热量蓄水池,在风电富余、热负荷相对不高的时候,让机组多烧点热存进去,同时把电出力降下来;等热负荷高峰来了,再从罐里放热补上。这样一来,机组的“当前热出力”和“当前电出力”就不再是绑死的了,热需求可以通过储热罐在不同时段之间腾挪。

电锅炉的思路是“替代”。风电多的时候,与其弃掉,不如让电锅炉直接用电来产热,替代掉一部分热电机组的热出力。这样热电机组可以把热出力降下来,电出力下限也跟着降,风电消纳空间就出来了。而且电锅炉本身也是一个用电负荷,相当于给风电多了一个消纳出口。

实际工程中,储热罐和电锅炉往往一起用。储热罐擅长“挪时间”,电锅炉擅长“拓空间”,两者配合效果显著。这也是题目里“联合优化控制”的含义所在。

2. 控制模型的核心:目标函数、机组可行域与储热耦合约束

2.1 目标函数:选择“弃风最小”还是“成本最优”

在动手写Matlab代码之前,第一件事是把目标函数定下来。这个问题里有两种常见的目标函数写法。

最简单直接的写法就是最大化风电消纳量,等价于最小化弃风量。目标函数可以写成:

min Σ ( w_avail(t) - w(t) )

其中w_avail是风电预测可出力,w是实际消纳量。这个写法很直观,适合教学和方案对比,因为它直接把弃风量放进了目标函数,结果一目了然。

但实际项目中,我更喜欢用经济成本作为目标函数,其中包含机组煤耗成本、弃风惩罚成本。弃风惩罚系数设得比运行成本高,这样优化器会优先消纳风电,同时也能兼顾机组运行的经济性:

min Σ ( C_chp(p, h) + λ_wind × (w_avail(t) - w(t)) )

其中C_chp是热电机组的煤耗成本,λ_wind是弃风惩罚系数。基于我个人的使用经验,建议初学者先用纯弃风最小做一遍,再换成本目标函数做对比,这样能更好地理解两者在优化结果上的差异。

2.2 抽凝机组可行域的线性化表达

整个模型里最关键的约束就是抽凝机组的可行域。这部分我建议直接用线性不等式来表达,因为线性约束可以直接交给LP/MILP求解器,计算效率高,代码也简洁。

以一台典型的抽凝式机组为例,参数取以下数值:

参数数值含义
H_min50 MW最小抽汽供热出力
H_max300 MW最大抽汽供热出力
P_min(H=H_min时)100 MW最小电出力
P_max(H=H_min时)400 MW最大电出力
P_max(H=H_max时)300 MW受最大进汽量约束时的电出力上限
P_min(H=H_max时)180 MW受最小凝汽流量约束时的电出力下限

把上面这几个点连起来,可以得到一个四边形可行域,对应的线性约束如下:

p ≤ 400 - 0.4 × (h - 50) p ≥ 100 + 0.32 × (h - 50) 50 ≤ h ≤ 300

为什么是这个斜率?上限斜率是(300-400)/(300-50) = -0.4,代表随着热出力增加,最大电出力线性下降;下限斜率是(180-100)/(300-50) = 0.32,代表随着热出力增加,最小电出力线性上升。这两个斜率在代码里就是两个常数,直接写进约束就行。

背压式机组就更简单了,电出力就是一个常数或者跟热出力成正比的线性关系:

p = k × h

我们算例里用的机组1就是背压式,电出力100 MW对应热出力300 MW,等于说电热比固定为1:3。

2.3 储热罐与电锅炉的时序约束

储热罐的建模要特别注意“时序”这个维度。所谓时序约束,是指储热罐的当前储热量(SOC,State of Charge)跟上一时刻的储热量以及本时刻储/放热功率直接相关:

soc(t+1) = soc(t) + η_in × c_in(t) - c_out(t) / η_out

其中c_in和c_out分别代表储热和放热功率,η_in和η_out是储放热效率,一般取0.9-0.98左右。除了递推方程,还有几个次要约束:储热量SOC不能超过容量上限,储放热功率不能超过各自的最大值。另外,同一个时刻最好不要同时储热和放热,实际运行中没有意义还浪费效率。这个问题可以通过引入0-1变量解决,让模型变成MILP:

c_in(t) ≤ C_in_max × bin(t) c_out(t) ≤ C_out_max × (1 - bin(t))

电锅炉的约束就简单多了,它就是一个“用电产热”的装置:

h_eb(t) = η_eb × eb(t) 0 ≤ eb(t) ≤ EB_max

其中η_eb取0.95-0.99。在热功率平衡方程里,电锅炉的产热量和储热罐的放热量一起算作热源;在电功率平衡方程里,电锅炉的耗电量算作新增电负荷。这两条平衡方程是整个联合优化模型的主骨架:

电平衡:P_chp1(t) + P_chp2(t) + w(t) = Load_P(t) + eb(t) 热平衡:H_chp1(t) + H_chp2(t) + c_out(t) - c_in(t) + η_eb × eb(t) = Load_H(t)

3. Matlab代码实现:YALMIP建模到求解器调用的完整链路

3.1 代码结构化设计与参数表

写Matlab代码之前,先把代码结构设计好。我习惯把代码分成五个模块:参数初始化、数据输入、模型构建、求解调用、结果可视化。这样后期改参数、换场景都方便。

参数初始化的核心是定义基础时间尺度和机组参数:

%% 参数初始化 T = 24; % 日前调度,1小时一个时段 dt = 1; % 时段长度,单位小时 % 机组1:背压式 P1_const = 100; % 电出力固定值 MW H1_const = 300; % 热出力固定值 MW % 机组2:抽凝式 H2_min = 50; % 最小热出力 H2_max = 300; % 最大热出力 P2_max_hmin = 400; % H=H_min时的最大电出力 P2_min_hmin = 100; % H=H_min时的最小电出力 P2_max_hmax = 300; % H=H_max时的最大电出力 P2_min_hmax = 180; % H=H_max时的最小电出力 slope_up = (P2_max_hmax - P2_max_hmin) / (H2_max - H2_min); slope_low = (P2_min_hmax - P2_min_hmin) / (H2_max - H2_min); % 储热罐参数 SOC_max = 800; % 容量 MWh c_in_max = 100; % 最大储热功率 MW c_out_max = 100; % 最大放热功率 MW eta_in = 0.95; % 储热效率 eta_out = 0.95; % 放热效率 % 电锅炉参数 EB_max = 100; % 最大电功率 MW eta_EB = 0.98; % 电热转换效率

数据输入部分,把24小时的预测负荷和风电可出力曲线定义成数组。推荐把数据放到Excel或者mat文件里,通过读取函数导入,这样换数据不用改代码。

3.2 YALMIP建模的关键写法

YALMIP是Matlab环境下非常好用的优化建模工具。它的核心思想是先用sdpvar定义决策变量,然后用运算符拼接约束和目标函数,最后交给求解器计算。

%% 定义优化变量 p2 = sdpvar(1, T); % 机组2电出力 h2 = sdpvar(1, T); % 机组2热出力 w = sdpvar(1, T); % 风电实际消纳量 cin = sdpvar(1, T); % 储热功率 cout = sdpvar(1, T); % 放热功率 soc = sdpvar(1, T+1); % 储热罐SOC,多了一个末端时刻 eb = sdpvar(1, T); % 电锅炉耗电功率 bin_s = binvar(1, T); % 储/放热互斥的0-1变量 %% 约束条件拼接 Constraints = []; % 电功率平衡 Constraints = [Constraints, P1_const + p2 + w == load_P + eb]; % 热功率平衡 Constraints = [Constraints, H1_const + h2 + cout - cin + eta_EB*eb == load_H]; % 机组2可行域 Constraints = [Constraints, p2 <= P2_max_hmin + slope_up*(h2 - H2_min)]; Constraints = [Constraints, p2 >= P2_min_hmin + slope_low*(h2 - H2_min)]; Constraints = [Constraints, H2_min <= h2 <= H2_max]; % 储热罐时序约束 Constraints = [Constraints, soc(1) == SOC_init]; Constraints = [Constraints, soc(2:T+1) == soc(1:T) + eta_in*cin - cout/eta_out]; Constraints = [Constraints, 0 <= soc <= SOC_max]; Constraints = [Constraints, 0 <= cin <= c_in_max * bin_s]; Constraints = [Constraints, 0 <= cout <= c_out_max * (1 - bin_s)]; % 电锅炉约束 Constraints = [Constraints, 0 <= eb <= EB_max]; % 风电消纳约束 Constraints = [Constraints, 0 <= w <= wind_avail];

关于SOC的时间对齐,这里有一个细节想提醒大家。soc(1)是初始储热量,soc(T+1)是第24小时结束后的储热量。递推方程里用的是“本时段的储放热功率影响下一时刻的储热量”这个逻辑。这么写的好处是末端状态可以很自然地加约束。

3.3 求解器配置与MILP策略

因为这个模型里引入了储放热互斥的0-1变量,问题变成了混合整数线性规划(MILP)。求解器方面有几种选择:

  • 如果只是教学演示、变量规模不大,用Matlab自带的intlinprog就可以,不需要额外装任何东西
  • 如果数据规模大、求解时间敏感,建议安装Gurobi或CPLEX,配合YALMIP使用
  • 如果不考虑储放热互斥约束,整个问题退化成纯LP,直接用linprog都行

YALMIP求解调用写法如下:

%% 目标函数 % 加权:弃风量 + 煤耗成本(煤耗用线性近似) lambda_wind = 500; % 弃风惩罚系数,远高于发电成本 Objective = lambda_wind * sum(wind_avail - w) + sum(cost_p1 * P1_const + cost_p2 * p2); %% 求解 ops = sdpsettings('solver', 'gurobi', 'verbose', 2); optimize(Constraints, Objective, ops); %% 结果提取 w_opt = value(w); p2_opt = value(p2); h2_opt = value(h2); soc_opt = value(soc);

求解完成后的可视化,我强烈建议至少画两张图:一张是电出力曲线,把机组电出力、风电消纳、电负荷画在一起;另一张是储热罐SOC曲线。这两张图基本能看出整个优化策略的工作逻辑。如果还要发论文,可以再画一张热出力曲线和分场景对比柱状图。

4. 一个典型冬季日的算例:储热和电锅炉到底能多消纳多少风电

4.1 典型日数据与参数设定

为了搞清楚联合优化的效果,我给你一个可以完全复现的算例。假设一个风-热-电系统包含一台背压机组(机组1)、一台抽凝机组(机组2)、一个300 MW风电场、一个储热罐和一个电锅炉。

负荷数据取一个典型冬季日:电负荷夜间低谷400-500 MW,白天高峰750-820 MW;热负荷在供暖期相对平稳,全天保持在600 MW上下。风电可出力曲线设定为:凌晨0点到5点处于高发区间240-300 MW,上午回落到120-180 MW,傍晚又爬升到200-280 MW。这个曲线的形状很有代表性——典型的大风出现在夜间负荷低谷时段。

储热罐容量设为800 MWh,储放热功率上限各100 MW;电锅炉额定功率100 MW,电热转换效率0.98。初始SOC设为400 MWh(半罐)。

4.2 三种方案的结果对比

我跑了三组场景:传统“以热定电”(没有任何解耦手段)、只加储热罐、储热罐+电锅炉联合。结果如下表:

方案风电消纳量(MWh/日)弃风率热电机组日总电出力(MWh)
传统以热定电341418.6%4758
仅加储热罐38957.1%4277
储热罐+电锅炉41940%4118

你可以看到,光靠储热罐,弃风率就从18.6%压到了7.1%;再加上电锅炉,当天风电预测总出力4194 MWh实现了全额消纳,弃风率为0。在计算中我还发现,加入电锅炉后热电机组的总电出力反而降低了640 MWh——因为电锅炉替代了一部分热负荷,热电机组的热出力下限进一步下移。

从时序曲线上看会更直观:凌晨风电大发时段,传统方案中机组2的电出力维持在250-280 MW,让风电只能发220 MW左右;加入储热罐后,机组2可以在夜间降低热出力,由储热罐放热补充,电出力下限降到了150 MW上下;再加上电锅炉分担热负荷,机组2夜间电出力可以降到110-130 MW,风电直接满发。

4.3 容量敏感性:边际效益递减的规律

跑到这一步,你可能会想:既然储热罐和电锅炉有效果,是不是配得越大越好?我特意做了容量敏感性分析,结果很有意思。

储热罐容量从200 MWh增加到800 MWh时,弃风率下降非常明显;但超过800 MWh之后,再增加容量,弃风率的改善幅度就很小了。原因是800 MWh的储热容量已经把夜间风电大发时段的“热量腾挪需求”基本满足了,再大的容量在一天内根本用不满。电锅炉的功率也有类似规律:100 MW时效果显著,但继续加大到150 MW后,对弃风率的边际改善就很有限了。

这个结果说明一个工程上很现实的问题:设备配置不是越大越好,而是要根据风电弃电量、热负荷特性、投资成本综合权衡。我在做园区项目的时候,一般会把设备容量设计成一个可调参数,跑一遍容量扫描,画出“弃风率-设备容量”曲线,再结合造价选拐点位置。

5. 工程落地中我最想提醒的三个坑

5.1 风电预测数据质量决定结果可信度

这是我踩过最大的坑。最开始我把风电预测曲线当成完美数据来做日前计划,结果调度当天实际风电跟预测偏差一大,优化结果就完全失真了。后来我学乖了:在Matlab代码里加上预测误差场景,把风电可出力数据做成多场景集合,或者采用鲁棒优化的思路。对初学者来说,最实际的做法就是——不要只跑一组数据,把风电曲线上下浮动20%各跑一遍,看看结果的变化范围。如果变化太大,说明系统对风电预测的依赖度高,这时候要考虑增加备用容量或者引入实时调整机制。

5.2 储热初始SOC与末端约束:最容易失真的细节

很多人建完模型跑出来一个很漂亮的结果,弃风率近乎0,但仔细检查发现,储热罐是从满载开始、放空结束的——这相当于“白嫖”了一整个罐子的热量,实际日内运行根本不可能复现。要避免这个问题,必须加SOC循环约束,让日初和日末的储热量相等:

Constraints = [Constraints, soc(1) == soc(T+1)];

加了这条约束后,弃风率会有小幅上升,但结果才是真实可执行的。如果是做连续多日的仿真,这个约束更是必不可少的,否则你每天都在消耗前一天“偷来”的热量。

5.3 从日前到滚动:不要把模型的一次求解当成控制

最后一个提醒是关于控制层面的。标题里写的是“优化控制”,但很多实现其实只做了“日前优化调度”。日前优化是开环的,用的是预测数据;真正落地的时候,风电和负荷都在波动,最优决策必须滚动更新。我在实际项目中用的框架是滚动时域优化:每15分钟更新一次预测数据,只执行第一个时段的决策,然后到下一个时段重新求解。Matlab里这个逻辑不难实现,只需要在主循环里不断更新负荷和风电数据,调用同一个优化模型。

如果你做的是毕业设计,能把这个“日前计划+滚动修正”的框架跑通,在答辩里是很大的加分项,因为它说明你理解从理论模型到工程应用的差距。

从原理到代码再到算例,这个联合优化控制模型的完整链条已经跑通了。最后再分享一个小经验:拿到任何优化调度项目,先别急着上复杂算法,先把LP模型跑通、把物理约束理解透,再往里面加整数变量、加不确定性。用我师傅当年的话说,先把最简单的东西做对,再谈花活。这套代码框架我后续也在不断往里面加东西——热网的动态时延特性、电价响应需求侧资源、多风电场时空相关性,每一层都能让模型离实际更近一步,也值得你在这个方向上继续折腾。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询