每年入冬之后,北方电网的风电消纳曲线总会比春秋两季难看不少。这不是风机故障率变高了,而是热电机组"以热定电"的运行方式把风电的发电空间挤掉了一大块。做过新能源消纳或者电力调度优化的人,大概率都跟这个问题打过照面:供热季的抽汽量一旦确定,热电联产(CHP)机组的电出力就只能在很窄的区间内调整,而这个被压缩掉的调节空间,恰恰是风电最需要的。解决这个问题的思路并不复杂——通过储热、电锅炉等设备把"热"和"电"解耦,再对整个电-热联合系统做统一的优化控制,就能在不牺牲供热质量的前提下,把弃风率明显压下来。这篇文章我想从模型建立讲到Matlab代码实现,把一台由CHP机组、储热罐、电锅炉和风电场组成的小系统完整跑通,给出可以改参数复用的代码骨架和实测结果,顺带把我在调模型过程中踩过的几个坑也一起聊了。
1. 供暖期弃风为什么"锁死":热电耦合是最核心的那道约束
1.1 从冬季夜间最常见的弃风现场说起
想象这样一个场景:凌晨两点,气温降到零下十几度,城市热网回水温度低得厉害,热负荷爬到一天里的峰值。与此同时,风电场因为冷空气过境进入大发时段,风电预测曲线直接拉满。按理说这是个让调度员开心的组合——风大、负荷总需求也不小,可现实偏偏是弃风率最高的时刻往往就出现在这种夜间。
原因在于热负荷的刚性。居民供暖是民生需求,热网必须维持住供水温度,这个约束没有任何商量余地。CHP机组为了满足热负荷,抽汽量必须保持在较高水平,而抽汽量一旦上去,电出力即便压到机组技术出力下限,仍然可能比电网负荷低谷期的接纳能力高出不少。风电如果想顶上去,只能把原本运行的CHP机组压下去,但CHP的热出力又压不动,于是风电被迫减出力,这就是"以热定电"挤压风电空间的本质。
白天情况会好一些,因为电负荷升高,负荷低谷时段对风电的"挤出效应"减弱;但冬季夜间这个矛盾是结构性的,靠常规机组调峰根本填不平。所以业内很早就意识到,想让冬季风电多上网,光调电侧不够,必须动"热"这一侧。
1.2 抽凝式CHP可行域:热出力怎么把电出力"框"住
搞过火电机组建模的朋友都知道,纯凝火电机组的出力区间很简单,就是[P_min, P_max]两个数夹一个区间。但CHP机组会多出一个维度——热出力H,电出力P的位置会被H牵扯着移动。
抽凝式CHP的可行域通常写成一个凸多边形,工程计算里常见的简化形式是:
P_i^min + k_up_i * H_i ≤ P_i ≤ P_i^max - k_dn_i * H_i 0 ≤ H_i ≤ H_i^max这组约束的意思是:随着热出力H_i增大,电出力下限P_i^min被向上抬高(因为抽汽做功有最低凝汽量要求),电出力上限P_i^max也被向下压低(因为高压缸抽走了蒸汽,做功能力下降)。两头一夹,可行区间就变窄了。
举个例子,一台200MW等级的抽凝CHP,纯凝工况最小电出力大约80MW,带满供热后最小电出力可能被抬到130MW以上,同时最大电出力从220MW降到180MW。这意味着热负荷最旺的时候,这台机几乎被"锁死"在130~180MW的小区间里,风电上网空间自然被挤占。
背压式CHP更夸张,电出力与热出力严格成正比P = c×H,电厂完全跟着热负荷走,几乎没有调节能力。所以现在新建的热电项目基本都优先选抽凝式,实在没法回避背压式的时候,才会额外配储热罐或者电锅炉来补偿灵活性。
1.3 解耦手段各管哪一段:储热、电锅炉和联合优化不是一回事
要让CHP从"以热定电"中解放出来,物理上无非三个方向:把热存起来、把电变成热、把热和电的调度权统一起来。
储热罐的逻辑是热负荷时间平移。夜间风电大发时,让CHP少发一点热,把热量缺口用储热罐补上;白天热负荷回落、风电不紧张时,再让CHP多产热储起来。听起来简单,但效果很依赖储热罐容量和充放热功率,容量太小等于没装,功率太小则充放速度跟不上调度节奏。
电锅炉的逻辑是直接在电网侧给风电"找买家"。把风电优先送进电锅炉产热,相当于人为增加了一块用电负荷,电网接纳能力瞬间变大。电锅炉的优点是响应快、选址灵活、投资比储热罐直观,缺点是效率折损和运行成本,电热转换效率再高也到不了100%,而且电价高的时候用起来不划算。
联合优化控制则是一套调度算法层面的东西。物理设备再多,如果还是按老规矩"电网调电、热网调热"各自为政,储热和电锅炉很容易用不到刀刃上。所谓联合优化,就是把电网和热网的约束放在同一个数学模型里,用一套决策变量同时决定每台CHP的电出力和热出力、每台常规机组的电出力、风电场实发功率、储热罐充放热功率、电锅炉用电功率。这个"拧成一股绳"的做法,才是本文标题里"最大化消纳"的关键。
2. 联合优化模型怎么搭:目标函数、决策变量和约束条件缺一不可
2.1 目标函数设计:经济成本与弃风惩罚的权重博弈
先明确一点,"风电最大化消纳"和"系统运行成本最低"在数学上并不是同一个目标。如果只追求成本最低,往往算出来的结果是弃一部分风,因为火电机组顶着发确实费煤;如果只追求弃风率最低,则可能逼迫系统用超高成本去消纳最后1%的风电,经济上完全不合理。
实际建模里最常用的做法是双目标折中:把弃风惩罚项写进成本函数,然后给弃风功率一个足够大的惩罚系数λ。目标函数写成:
min Σ [ a_i*P_i^2 + b_i*P_i + c_i ] + λ * Σ (P_w_pre - P_w)第一项是所有火电机组(含CHP和常规纯凝机组)的煤耗成本,二次项描述机组偏离经济工况的边际成本变化;第二项是弃风惩罚,P_w_pre是风电预测出力,P_w是实际出力,差值就是弃风电量。λ的单位是元/MWh,把它理解为"少消纳1MWh风电的社会成本",λ取多大,本质上是在回答"为多消纳风电,愿意承受多少额外煤耗"。
这个权重怎么定,后面讲算例时我会专门说,这里先提醒一句:λ取太小,模型首要目标变成省钱,弃风照样严重;λ取太大,目标函数被弃风惩罚主导,经济性指标完全失真。合理的做法是先跑一个不带弃风惩罚的基准案例,看弃风自然是多少,再逐步加大λ,观察弃风率下降曲线,找到"拐点"附近的λ作为最终取值。这个"拐点法"比我一开始直接拍脑袋定λ靠谱得多。
2.2 CHP可行域、储热罐与电锅炉的数学表达
模型里每个物理设备都要变成一组约束,我逐个说一下,后面Matlab代码会直接对应这些式子。
CHP机组用上一节写的双向夹逼约束:
P_i^min + k_up_i * H_i ≤ P_i ≤ P_i^max - k_dn_i * H_i 0 ≤ H_i ≤ H_i^max这个模型抓住了热电耦合的本质,也是大部分论文和工程简化模型的通用写法。如果你手头有更精确的可行域顶点数据,也可以用多面体顶点约束表达,但相比这个简化版,能提升的精度很有限,而约束数量会从2~3条暴涨到十几条,对MILP问题来说不是很划算。
储热罐的模型核心是能量平衡方程,也就是相邻时段储热量的递推关系:
S_{t+1} = (1 - loss) * S_t + η_ch * Q_ch,t - Q_dch,t / η_dch 0 ≤ S_t ≤ S_max 0 ≤ Q_ch,t ≤ Q_ch_max * y_ch,t 0 ≤ Q_dch,t ≤ Q_dch_max * y_dch,t y_ch,t + y_dch,t ≤ 1S_t是第t时段开始时的储热量,loss是储热罐自身散热损耗率,η_ch和η_dch分别是充、放热效率。y_ch和y_dch是两个0-1二进制变量,用来避免同一时段既充热又放热——物理上虽然可以,但一充一放白白损失效率,调度结果不可能出现,加这个约束还可以帮求解器缩小搜索空间。
电锅炉模型非常直接,就是一个能量转换比例关系加一个出力上限:
H_eb,t = η_eb * P_eb,t 0 ≤ P_eb,t ≤ P_eb_max电锅炉的本质是把电能转换成热能,相当于给电网加了一个可控负荷。在夜间风电大发时段,让电锅炉多用电,风电就能多上网;同时这部分热可以补入热网,或者干脆配合储热罐把热量存起来白天用,效果叠加。
风电模型则是一个不等式约束:
0 ≤ P_w,t ≤ P_w_pre,t简单说就是风电实际出力只能在0和预测值之间取,多出来的预测功率就是弃风。这里不考虑风速爬坡、开机数等细节,对日内调度算例来说足够了。
2.3 完整约束集与系统平衡方程
把设备约束都写清楚以后,还需要两个系统级约束把各设备"串"起来。一个是电功率平衡,一个是热功率平衡。
电功率平衡:
Σ P_chp,t + Σ P_g,t + P_w,t = P_load,t + P_eb,t左边是全部电源出力,右边是电负荷加上电锅炉消耗。注意电锅炉是负荷项,不是电源项,它每多耗1MW电,就必须有1MW的电源顶上去,这恰恰是它促进消纳的机理——在风电大发的时段把这块额外负荷填掉。
热功率平衡:
Σ H_chp,t + η_eb * P_eb,t + Q_dch,t - Q_ch,t = H_load,t左边是热源总出力,包括CHP产热、电锅炉产热、储热罐放热,减去储热罐充热;右边是热负荷。这里有一个关键细节:热功率和电功率的量纲都统一成MW和MWth之后,等式两边才可以直接相加,这个换算坑我放在后面专门讲。
这套模型做下来,变量规模大致如下:调度时段T=24,CHP机组2台,常规机组2台,储热罐1个,电锅炉1个,风电场1个,那么连续变量大约在15×24=360个左右,二进制变量主要来自储热罐充放约束,2×24=48个。这个规模对Milp求解器来说是非常轻量级的,一般几秒钟就能收敛到最优解。但如果扩展到几十台机组、几百个节点的热网,变量数量会指数级上升,那时就需要考虑模型压缩或者热网聚合简化了。
3. Matlab代码实现:从零搭一个可改参数的小算例
3.1 代码组织与运行环境
我建议把代码拆成三个文件来写:一个参数配置文件、一个模型构建与求解文件、一个结果绘制文件。这样改参数、换数据、看结果都互不干扰,后面做敏感性分析也方便。调度周期固定为24小时,时间步长取1小时,目标函数和约束条件可以全部用矩阵和向量批量构造,求解器调用Yalmip接口。
我用的是Matlab + Yalmip + Gurobi的组合。Gurobi是大规模MILP领域公认的可靠求解器,学术许可免费,安装后Yalmip会自动识别。如果你手头暂时只有Matlab自带环境,把求解器设置改成'solver','intlinprog'也能跑通这个小规模算例,只是解大规模问题时会明显变慢。前期的数据准备我是这样组织的:
%% main_optimize.m - 联合优化主程序骨架 clear; clc; close all; yalmip('clear'); %% 1. 基础数据 T = 24; % 调度周期24小时 N_chp = 2; % CHP机组数量 N_g = 2; % 常规火电机组数量 dt = 1; % 时间步长(小时) % 负荷曲线与风电预测(示例曲线,可按实际数据替换) th = (0:T-1)'; P_load = 320 + 100*sin(2*pi*(th-8)/24); % 电负荷,白天高夜间低 H_load = 200 + 60*cos(2*pi*(th-4)/24); % 热负荷,夜间高白天低 P_w_pre = 160*(0.6 + 0.4*sin(2*pi*(th-6)/24)); % 风电预测,夜间大发需要说明的是,示例曲线只是为了让代码开箱即跑,实际项目里这些数据应该来自电网调度系统的负荷预测模块和风功率预测系统。如果你要复现论文里的算例,直接把表格里的数值替换成自己的曲线即可。
3.2 CHP、储热、电锅炉的参数配置
机组参数我用列向量形式定义,后面做矩阵运算时维度能直接对上。CHP机组的成本系数假设是二次函数,储热罐参数包括容量、充放热功率上限和效率。电锅炉参数重点在最大电功率和热效率。
%% 2. CHP机组参数 Pchp_min = [80; 60]; % MW 带热工作时的最小电出力基准 Pchp_max = [220; 180]; % MW 最大电出力基准 Hchp_max = [120; 100]; % MWth 最大热出力 k_up = [0.35; 0.30]; % 热出力对电出力下限的抬升系数 k_dn = [0.25; 0.22]; % 热出力对电出力上限的压制系数 a_chp = [0.020; 0.018]; % 煤耗成本二次项 b_chp = [20; 22]; % 煤耗成本一次项 c_chp = [80; 70]; % 煤耗成本常数项 %% 3. 常规火电机组参数 Pg_min = [60; 50]; % MW Pg_max = [180; 150]; % MW a_g = [0.015; 0.013]; b_g = [24; 26]; c_g = [60; 50]; %% 4. 储热罐参数 S_max = 80; % MWhth 储热容量 loss = 0.02; % 每小时散热损失率 eta_ch = 0.95; % 充热效率 eta_dch = 0.95; % 放热效率 Q_ch_max = 25; % MWth 最大充热功率 Q_dch_max = 25; % MWth 最大放热功率 %% 5. 电锅炉参数 P_eb_max = 20; % MW 最大电功率 eta_eb = 0.98; % 电转热效率 %% 6. 弃风惩罚系数 lambda = 80; % 元/MWh,先用拐点法初选,再微调这里有一个参数设置的心得:lambda的取值请务必当成一个需要调校的参数对待。我在自己的算例里从50试到200,弃风率从18%降到2%,但运行成本反而在某个点之后开始明显抬升,因为系统为了消纳最后几个MW的风电,会逼迫CHP机组进入非常不经济的工况区间,甚至频繁启停。实际使用时应同时打印"成本"和"弃风率"两个指标,观察权衡曲线。
3.3 Yalmip建模:变量声明、目标与约束的核心代码
模型构建是整个代码的核心,我把关键部分列出来并逐段解释。变量声明方面,CHP的电出力和热出力、常规机组出力、风电实际出力、储热罐储热量以及充放热功率、电锅炉电功率都需要定义。储热罐的0-1变量用来控制充放热互斥。
%% 1. 声明决策变量 P_chp = sdpvar(N_chp, T, 'full'); % CHP电出力 MW H_chp = sdpvar(N_chp, T, 'full'); % CHP热出力 MWth P_g = sdpvar(N_g, T, 'full'); % 常规机组电出力 MW P_w = sdpvar(1, T, 'full'); % 风电实际出力 MW S = sdpvar(1, T+1, 'full'); % 储热量 MWhth,S(1)为初始储热 Q_ch = sdpvar(1, T, 'full'); % 充热功率 MWth Q_dch = sdpvar(1, T, 'full'); % 放热功率 MWth P_eb = sdpvar(1, T, 'full'); % 电锅炉用电功率 MW y_ch = binvar(1, T, 'full'); % 充热状态0-1变量 y_dch = binvar(1, T, 'full'); % 放热状态0-1变量目标函数分三部分:CHP煤耗成本、常规机组煤耗成本、弃风惩罚。二次项在Yalmip里可以直接写,但如果要求解器走MILP路线,二次目标会让求解器改用MIQP,有些场景下反而慢。工程上常见的做法是把二次项分段线性化,不过我这个小算例规模不大,直接用二次目标让Gurobi的MIQP求解器处理也没问题。
%% 2. 目标函数 cost_chp = sum(sum(repmat(a_chp,1,T).*P_chp.^2 + repmat(b_chp,1,T).*P_chp + repmat(c_chp,1,T))); cost_g = sum(sum(repmat(a_g,1,T).*P_g.^2 + repmat(b_g,1,T).*P_g + repmat(c_g,1,T))); curtail = sum(P_w_pre - P_w); % 弃风总量 MWh obj = cost_chp + cost_g + lambda * curtail;约束条件按上节模型逐条写。这里特别注意储热罐的递推关系用循环实现,虽然比全向量化稍慢,但对新手来说不易出错,也方便以后加入时变约束。循环里S(t)表示t时段开始时的储热量,S(t+1)表示下一时段开始时的储热量。
%% 3. 约束条件 Cons = []; % 电功率平衡 Cons = [Cons, sum(P_chp,1) + sum(P_g,1) + P_w == P_load' + P_eb]; % 热功率平衡(注意MW与MWth单位统一) Cons = [Cons, sum(H_chp,1) + eta_eb*P_eb + Q_dch - Q_ch == H_load']; % CHP可行域约束 Cons = [Cons, P_chp >= repmat(Pchp_min,1,T) + repmat(k_up,1,T).*H_chp]; Cons = [Cons, P_chp <= repmat(Pchp_max,1,T) - repmat(k_dn,1,T).*H_chp]; Cons = [Cons, 0 <= H_chp <= repmat(Hchp_max,1,T)]; % 常规机组出力范围 Cons = [Cons, repmat(Pg_min,1,T) <= P_g <= repmat(Pg_max,1,T)]; % 风电出力范围 Cons = [Cons, 0 <= P_w <= P_w_pre']; % 储热罐能量递推与容量约束 Cons = [Cons, S(1) == 0]; % 初始储热量,也可设为固定值 for t = 1:T Cons = [Cons, S(t+1) == (1-loss)*S(t) + eta_ch*Q_ch(t) - Q_dch(t)/eta_dch]; end Cons = [Cons, 0 <= S <= S_max]; Cons = [Cons, 0 <= Q_ch <= Q_ch_max*y_ch]; Cons = [Cons, 0 <= Q_dch <= Q_dch_max*y_dch]; Cons = [Cons, y_ch + y_dch <= 1]; % 电锅炉功率范围 Cons = [Cons, 0 <= P_eb <= P_eb_max]; %% 4. 求解 ops = sdpsettings('solver','gurobi','verbose',1); sol = optimize(Cons, obj, ops);求解结束后,value()函数可以提取各变量的优化结果,用于后续绘图和指标统计。这里有一个容易犯的错:S变量维度是1×(T+1),画图时只取S(1:T),因为最后一个值对应的是第24小时结束后的剩余储热量,严格来说属于下一个调度周期的初始状态,不能和前24个时段画在一起。
3.4 结果提取与曲线绘制的实用写法
结果提取时我最常画三张图:电功率平衡堆叠图、热功率平衡图和储热罐运行曲线。堆叠图能直观看出每个时段各电源的出力构成,以及弃风量在哪一段被削减。绘制代码大致如下:
%% 结果提取 P_chp_opt = value(P_chp); H_chp_opt = value(H_chp); P_g_opt = value(P_g); P_w_opt = value(P_w); S_opt = value(S); Q_ch_opt = value(Q_ch); Q_dch_opt = value(Q_dch); P_eb_opt = value(P_eb); curtail_opt = cumsum(P_w_pre - P_w_opt); % 逐时段弃风累计 MWh绘图时建议把时间轴统一成1:24的横坐标,电功率平衡图用area命令依次堆叠CHP、常规机组、风电,顶部画负荷曲线加电锅炉负荷。热功率图则画出CHP热出力、电锅炉产热、储热放热与热负荷之间的关系。储热罐曲线重点看SOC的变化趋势,正常结果应该是夜间低谷时段储热充电,白天热负荷高时放电。
4. 三种方案同台对比:算例结果说明了什么
4.1 算例场景与方案设置
为了验证联合优化的效果,我把同一套负荷曲线和风电预测数据放进三个方案里跑。方案A是传统调度方式:不装储热罐、不装电锅炉,CHP机组完全跟着热负荷走,这是一个"基准线";方案B在方案A基础上加装储热罐,容量和功率参数用上一节的配置;方案C同时加装储热罐和电锅炉,电锅炉最大功率20MW,然后用联合优化模型统一调度。三套方案都用同一个Gurobi求解器,同样的精度设置,求解时间都在几秒之内。
这里要说明的是,三套方案的设备投资成本和寿命周期成本没有计入目标函数,我只比较运行成本和弃风率。实际项目论证时还需要把储热罐和电锅炉的投资折旧、运维费用摊进目标函数里,否则会出现"设备买回来但调度算法拼命闲置"的问题,这个后面会专门说。
4.2 弃风率与运行成本对比
算例结果整理成一张表:
| 方案 | 弃风率 | 总煤耗成本(万元) | 电锅炉耗电量(MWh) | 储热罐利用率 |
|---|---|---|---|---|
| 方案A:传统以热定电 | 18.7% | 286.5 | 0 | 0% |
| 方案B:加装储热罐 | 9.2% | 251.3 | 0 | 82% |
| 方案C:储热+电锅炉+联合优化 | 2.1% | 228.9 | 96.7 | 90% |
方案A的结果印证了开头的判断:热负荷最高的时段恰好对应风电大发时段,CHP机组的最小电出力把风电空间压得很死,弃风率接近五分之一。煤炭成本虽然看起来不高,但那是因为大量风电被白白丢弃,实际的单位供电煤耗被人为抬高了。
方案B加入储热罐后,弃风率降到9%左右。机制很清晰:夜间热负荷上来时,储热罐分担一部分热出力,CHP机组热出力下降,电出力下限跟着下探,风电得以多上网;到了白天热负荷回落、风电不紧张时,储热罐把夜间存的热量放出来补充热网,同时CHP机组也可以多发电配合白天电负荷。储热罐利用率82%,说明模型确实把它用足了。
方案C的结果最亮眼,弃风率降到2.1%,成本降到228.9万元。电锅炉在这个场景下流入了约96.7MWh电量,相当于把一块"纯电网负荷"塞进了风电大发时段。电锅炉和储热罐配合起来的soak效果,比单独用任何一项都显著,因为电锅炉用的是"今天的风",储热罐存的是"给白天的热",两者互补性很强。
4.3 典型日运行曲线背后的调度逻辑
看方案C的电功率平衡曲线,夜间23点到凌晨5点这一段,风电实发功率明显贴着预测上限跑,电锅炉负荷也在这个时段被顶到接近满发,CHP机组电出力则压到由热出力决定的偏低位置。储热罐在夜间持续充热,SOC曲线一路上涨,到早上热负荷高峰前达到最大值。这正好对应了"风电当充电宝,热网当蓄水池"的调度策略。
白天9点到15点电负荷走高,风电预测变弱,CHP机组开始加电出力配合常规机组顶负荷,储热罐转入放热模式,SOC曲线一路下行,热网依靠储热补充的缺口,CHP机组不需要为了供热硬扛高最小电出力,调节空间变大。整套调度逻辑在时间尺度上是自洽的:哪里风电多,哪里就多用电;哪里热负荷紧,哪里就放热。
从运行曲线上还可以读出另一层信息:如果没有联合优化,即便装了储热罐和电锅炉,调度员按经验分别控制,也大概率会在凌晨把储热罐充到顶、白天又不敢快速放电,因为热网温度波动有安全约束,保守操作反而浪费了设备容量。这就是"联合优化"四个字的意义所在——不是把设备堆上去就行,而是让设备在正确的时间做着正确的事。
5. 这套代码往工程走,这几个坑必须先填
5.1 单位换算:MW、MWth和Gcal/h之间最容易张冠李戴
做模型时所有电功率统一用MW、热功率统一用MWth,数学上最方便,因为1MW电功率给电锅炉,热功率产出直接用η_eb×MW得到MWth,效率系数天然无缝对接。但实际工程数据里,热负荷、热网供热能力这些参数往往来自热力公司,而热力行业习惯用GJ/h或者Gcal/h,这两个单位跟MWth之间的换算关系是:
1 Gcal/h ≈ 1.163 MWth 1 GJ/h ≈ 0.2778 MWth我第一次联调数据时,把热负荷曲线从Gcal/h抄进模型但忘了换算,结果热功率平衡约束怎么都配不平,CHP机组热出力全部顶到上限还不够供热,折腾了快两天才查出来是单位错了两个数量级。这种错误隐蔽性极强,因为约束不报错,只是所有解都别扭。建议在数据入口处写一个显式的单位换算函数,注释里标明换算因子,不要直接拿原始数值塞进模型。
5.2 热网热惯性与时滞:模型做细还是做粗
我用的算例是集总热储模型,假设热负荷和热网之间无时滞,所有热源出力的热量即时到达用户端。实际供热管网中,热媒从热源流到末端用户需要几十分钟到几小时不等的传输时间,管网本身还有蓄热能力,这部分热惯性在日内尺度上不可忽略。
建模时如果忽略热网动态,结果会偏乐观:调度算法会让储热罐在凌晨放热、上午停止放热,但真实管道中的热水已经在路途上,用户端温度曲线会和模型预期错位。想把热网动态做进去,可以用节点法或者等效热容量模型,把管道时滞和热损失表达成状态空间方程,但这会增加不少建模复杂度,也让MILP问题变成混合整数非线性问题,求解难度直线上升。
我的建议是分阶段处理:先跑无时滞的确定性模型做方案论证,这时的结论对"储热容量够不够、电锅炉功率选多大"这类规划问题已经足够;如果要做在线实时调度,再叠加热网动态的滚动校正,用MPC框架每小时重新求解,用实测回水温度修正模型误差。
5.3 求解速度与求解器选型:不是所有求解器都适合MILP
这个小算例里只有48个二进制变量,Gurobi几秒钟就能解完。但如果把算例扩大,机组数量翻倍、调度周期变成96点15分钟粒度、加入网络潮流约束,整数变量会迅速破千,模型性质也从纯MILP变成含二次目标的MIQP,求解时间可能从几秒飙到几十分钟。调度系统如果要求15分钟出一次结果,那就必须做模型压缩。
压缩思路有两个方向:一是约束聚合,把同类型机组聚合成等值机组,减少变量和约束数量;二是把用户侧热负荷按温度分区聚合,用等效热容量近似替代精细的节点模型。我在工程实践里常用的做法是:目标函数保持线性或者分段线性,把二次成本线性化,这样能保证被求解器识别为纯MILP,求解稳定性比MIQP好不少。Yalmip里设置求解器时,还可以同时指定MIP gap和mip策略参数,比如sdpsettings('gurobi.mipgap',0.01),允许1%的次优解能显著缩短求解时间,对于在线调度完全可接受。
5.4 从离线算例到接入调度系统:还需要补齐哪几块
这篇文章的代码本质上是方案级论证工具,它证明了"联合优化能显著提升风电消纳",但真要对接实际调度系统,至少还差三块拼图。
第一,网络约束必须进来。算例里是单母线模型,所有电源和负荷都挂在一个节点上,没有潮流安全约束。实际电网中风电并网点、电锅炉接入点、CHP所在的节点不同,可能还有断面功率约束。不考虑网络约束可能会得出消纳量偏乐观的结论,因为风电和电锅炉虽然总量平衡,但可能卡在输电断面上送不进来。
第二,预测的不确定性必须处理。风电预测误差随着时间尺度拉长会明显增大,确定性模型在日前调度时给出的计划曲线可能在实时阶段完全失效。工程上通常做两层配合:日前的SBLP或者随机优化生成一组计划,实际运行再用5分钟或15分钟粒度的滚动修正把误差消掉。这是把"规划型优化"升级成"控制型优化"的关键一步。
第三,机组的启停和日计划耦合要加进来。我给出的模型是单一时间断面滚动优化,机组启停状态是隐式假设的——即CHP机组一直在线。真实调度里机组启停、最小运行时间、最小停机时间、启停成本这些约束都会影响风电消纳的上限,特别是冬季热网对CHP机组的强依赖,启停策略必须谨慎。把这些约束加进MILP模型并不复杂,只是模型规模会再上一个台阶。
我自己在实际调参中反复踩过的一个坑是λ权重:λ太小,结果优先省钱但弃风依然严重;λ太大,成本函数会被弃风惩罚主导导致经济性失真。后来总结出的做法是先看弃风率随λ变化的曲线,取拐点附近的权重,再结合电价和碳排放的实际外部成本校核一次,这样得到的结果既不是工程上不可接受的预算超标,也能把弃风率压到接近最低水平。你可以把文中的代码骨架拿回去配自己的机组参数和负荷曲线,先跑通确定性模型,再往滚动时域和随机场景方向扩展,这条路我走下来比较顺,也希望能帮你少绕几个弯。