资讯动态

热电联产机组联合优化控制:最大化风电消纳的Matlab建模与求解

发布时间:2026/10/2 4:04:24 来源:尧图企业网站定制
做电力系统优化调度的朋友对“热电联产机组”这几个字应该深有体会。尤其北方供暖期热电机组既要扛供热、又要参与发电运行方式被“以热定电”四个字绑得死死的而风电偏偏夜间反调峰热电机组压不下去、风就上不来弃风率蹭蹭往上涨。我做新能源消纳方向的建模与仿真有一段时间了今天把“风电最大化消纳的热电联产机组联合优化控制”这个项目的核心思路完整拆开来讲——从背后的物理问题到数学模型再到Matlab代码实现、求解器选型、调试经验全部摊开。这篇内容适合正在做电力系统课程设计、毕业设计或者刚接手新能源调度算法研究的同学也适合已经能跑通代码但总被“不可行”“结果不合理”折磨的人文里说的这些坑大概率你也踩过。1. 问题解剖热电联产与风电消纳的矛盾根源1.1 为什么冬季弃风如此顽固冬季弃风不是偶然现象而是一个结构性矛盾。先说风电自身的脾气风电出力受天气驱动夜晚往往是风速最大的时段但夜间恰恰是全社会用电负荷的低谷。这样一来风电大发时段系统最缺的是“向下调峰能力”——也就是把常规机组出力压低、给风电让出空间的能力。再看系统里其他成员。纯凝火电机组有最小技术出力比如一台600MW的机组最小稳定运行出力可能在240MW左右可以往下压不少但热电联产机组不行它不仅要发电还要向热网供热。供热需求在冬季夜间同样处于高位为了满足热负荷机组电出力被抬高到一个较高的下限水平几乎压不下去。系统里热电机组占比越高整体调峰能力就越差风电被迫弃掉的部分就越多。我用一个简单的数量关系帮你建立直觉。假设夜间电负荷是800MW热负荷折算成等效电功率是500MW系统里有两台抽汽式热电机组为了满足热负荷它们的电出力最低也得维持在400MW以上再加上一台最小技术出力200MW的纯凝机组系统最低出力已经到600MW以上。800MW的负荷留给风电的消纳空间只有不到200MW而风电夜间可能发到300MW甚至更多超出部分只能弃掉。这就是冬季弃风的典型场景。1.2 热电机组的“电-热耦合”特性要理解联合优化为什么有效先得把热电机组的工作原理讲透。以最常见的抽汽式汽轮机组为例它从汽轮机中段抽出部分蒸汽去加热热网水这部分抽汽不再进入低压缸做功发电所以机组的电出力和热出力之间存在强烈的耦合关系。这种耦合在数学上表现为一个可行域给定一个热出力电出力只能在某个区间内取值。多个文献里用了不同的线性化描述方式但本质都是一样的——电出力上限随热出力增加而下降因为抽走的蒸汽多了、做功的蒸汽就少了电出力下限随热出力增加而上升因为为了保证供热机组必须维持一个最低的蒸汽流量这部分流量对应的发电功率也随之增加。两个边界一夹就形成了所谓的“热-电可行域”。背压式机组就更特殊了它的电出力和热出力基本成固定比例没有独立调节空间发电完全由供热需求决定。这类机组冬季几乎等同于一个按照热负荷变化同步发电的装置系统里如果背压机组占比高调峰压力会更大。这也是为什么很多优化研究中要专门引入“热电解耦”手段——储热罐、电锅炉、热网蓄热——目的就是把强耦合变成弱耦合。1.3 联合优化控制到底优化了什么既然问题出在“以热定电”那思路就很清晰了能不能在满足供热需求的前提下通过多台机组、多类设备的协调把电出力整体压下去、给风电腾出空间这就是联合优化控制的核心。它和传统经济调度最大的区别在于传统调度把热负荷当作给定值、直接折算成各台CHP机组的电出力下限联合优化则把热负荷的分配也纳入决策——哪台机组承担多少供热、储热罐什么时候充放、电锅炉要不要投入这些都由优化算法统一决定。一句话概括联合优化控制就是在满足电力平衡、热力平衡、机组运行约束的前提下通过协调多台CHP机组的热电出力以及储能、电锅炉等灵活性资源实现弃风最小化、运行成本最小化的综合目标。它解决的不只是“每台机组发多少电”的问题而是“电、热两条平衡链如何协同”的问题。2. 数学模型把调度问题翻译成机器能懂的约束2.1 目标函数如何表达“最大化消纳”先聊目标函数。很多初学者一上来就写“弃风电量最小”但实际上更常见的做法是把弃风用高惩罚系数计入运行成本让优化器在成本驱动下自动提高风电消纳。这样写的好处是当弃风惩罚足够高时优化结果在数值上等价于弃风最小同时目标函数里还能包含煤耗成本、启停成本、电锅炉耗电成本等得到一个更接近工程实际的总成本指标。我常用的目标函数这么写min Σ_t [ Σ_i (a_i * P_i,t^2 b_i * P_i,t c_i) ρ_pen * (P_w_pre,t - P_w,t) ]其中第一项是热电机组和纯凝机组的煤耗成本通常用二次函数近似第二项是弃风惩罚项P_w_pre是风电预测功率P_w是实际并网功率二者之差就是弃风量。ρ_pen的取值很关键我一般取正常煤耗成本系数的10到20倍确保优化器优先消纳风电而不是为了省几块钱煤费故意压低风电。这里补充一个容易忽略的细节如果你做的是纯线性模型比如用linprog二次煤耗项需要分段线性化。YALMIP里可以直接写二次目标然后用能处理二次规划的商业求解器像Gurobi和CPLEX都原生支持这个我后面在求解器部分再细说。2.2 热电机组可行域约束热电机组的建模是整个问题的核心也是最容易写错的地方。对于抽汽式机组我采用线性不等式来描述它的热-电可行域0 ≤ Q_i,t ≤ Q_i,max P_i,t ≥ P_i,min k_i,1 * Q_i,t P_i,t ≤ P_i,max - k_i,2 * Q_i,t第一条是热出力上下限第二条和第三条分别是电出力下边界和上边界斜率k_i,1和k_i,2表示热电耦合强度。k_i,1越大意味着增加单位供热量必须同步增加多少电出力机组调峰能力越差。需要注意这是简化形式。更精确的做法是采用可行域顶点描述法——把机组的热电运行区域写成若干个顶点的凸组合然后通过顶点权重变量来约束P和Q。这种写法麻烦一些但能更真实地反映机组在部分抽汽工况下的非线性特性。实际做项目时如果手上没有厂家提供的详细运行数据就用线性不等式近似如果要做高精度分析再考虑顶点法。我在课程设计和毕设里通常先用简化形式把主流程跑通再逐步细化。2.3 系统平衡与备用约束平衡约束是任何调度模型不能出错的底线。电力平衡约束写成Σ_i P_i,t P_w,t - P_eb,t P_load,t注意电锅炉在这里是作为负荷出现的它的用电功率P_eb要放在等式左侧做减法。很多初学者容易把这部分漏掉导致电平衡永远对不上。热力平衡约束写成Σ_i Q_i,t Q_s,t Q_eb,t H_load,t其中Q_s是储热罐的放热功率放热为正、充热为负Q_eb是电锅炉的供热功率单位要统一为MW或者GJ/h。除了平衡约束工程上还需要考虑旋转备用约束。简单处理的话可以要求常规机组在任意时刻满足Σ_i P_i,max - 系统负荷 ≥ 上备用需求 系统负荷(扣风电) - Σ_i P_i,min ≥ 下备用需求不过很多课程设计不要求包含备用约束加入后模型更容易不可行调试复杂度也会上升。我的建议是如果题目没有明确要求第一版代码先不加把核心调度逻辑跑通后再作为扩展功能加进去。2.4 储热与电锅炉建模要点储热罐和电锅炉是提高风电消纳的关键设备建模并不复杂但细节多。储热罐用能量状态方程描述S_t1 S_t - Q_s,t * Δt - loss_t 0 ≤ S_t ≤ S_max -S_disch,max ≤ Q_s,t ≤ S_ch,maxS_t是储热罐在时段t结束时的储热量Q_s,t对应当前时段的放热正或充热负loss_t是散热损失通常可以简化成一个固定比例。这里要特别注意ΔT的单位换算如果时间间隔是1小时功率MW乘以1小时就是能量MWh如果时间间隔是15分钟要乘以0.25。电锅炉更简单它把电能转换成热能供热功率和耗电功率之间有个效率系数Q_eb,t η_eb * P_eb,tη_eb一般取0.95到0.98。电锅炉的价值在于风电大发时段与其弃风不如把这部分电能转化成热能既满足了热负荷又给风电让出了上网空间。这个思路在工程上叫“以电供热、以热促风”是北方地区提升风电消纳的重要技术路径。3. Matlab实现建模、求解与代码框架3.1 求解器选型与建模环境Matlab里做优化调度最省心的组合是YALMIP加外部求解器。YALMIP是一个建模工具箱你不用手动把问题写成标准矩阵形式直接用符号变量声明决策变量、写约束和目标函数它会自动帮你转换成求解器需要的输入格式。我常用Gurobi或CPLEX作为底层求解器它们在处理混合整数线性规划MILP和二次规划MIQP方面性能非常强。如果手头没有商业求解器授权Matlab自带的intlinprog和linprog也能应对中小规模问题。我做过对比30个时段、5台机组、带储热和电锅炉的问题linprog和Gurobi的求解时间都在秒级差距不大但把时段拉长到96个点、机组数量到10台以上Gurobi的优势就明显了。学生用户可以去申请Gurobi的学术license免费且申请流程很快推荐优先考虑。这里提醒一句YALMIP在不同版本和不同求解器之间的兼容性偶尔会有小问题建议安装完求解器后先在Matlab里运行yalmiptest验证配置是否成功省得后面排查半天发现是链接问题。3.2 算例系统与数据准备我用的算例是一个简化的区域供热供电系统包含两台热电联产机组、一台纯凝机组、一个风电场、一个储热罐和一台电锅炉。调度周期取24小时时间间隔1小时T24。这类算例在文献中非常常见你可以参考公开的IEEE算例数据也可以按典型日曲线的形状自己构造。负荷和风电数据的处理有一个原则单位必须全局统一。我建议统一使用MW作为功率单位能量统一用MWh。热负荷的数值可以和电负荷相近但要理解一个是热功率、一个是电功率二者通过热电机组的热电比产生关联。数据方面我通常先画出原始曲线的形状确认没有突刺和明显异常点再送入模型。另外风电预测功率曲线我建议选取“夜间大、白天小”的反调峰曲线这样才能暴露调度矛盾、体现联合优化的价值。如果你拿一条顺势而为的曲线来做结果对比会非常平淡论文里也不好看。3.3 核心代码结构与关键代码片段下面给一个能直接参考的主程序框架我在实际项目中就是这么组织的%% 数据初始化 T 24; P_load [...]; % 电负荷1x24 H_load [...]; % 热负荷1x24 P_w_pre [...]; % 风电预测1x24 % CHP机组参数 CHP.Pmax [200 150]; % 电出力上限 CHP.Pmin [80 60]; % 电出力下限 CHP.Hmax [150 120]; % 热出力上限 CHP.k1 [0.4 0.45]; % 电出力下限随热出力变化斜率 CHP.k2 [0.3 0.35]; % 电出力上限随热出力变化斜率 % 纯凝机组 P_con_min 60; P_con_max 180; % 储热罐 S_max 300; % 最大储热量 MWh q_s_max 60; % 最大充放热功率 MW % 电锅炉 eta_eb 0.95;然后是YALMIP建模的核心部分%% 决策变量 P_chp sdpvar(2, T); % CHP电出力 Q_chp sdpvar(2, T); % CHP热出力 P_con sdpvar(1, T); % 纯凝机组出力 P_w sdpvar(1, T); % 风电并网功率 P_eb sdpvar(1, T); % 电锅炉耗电 Q_eb sdpvar(1, T); % 电锅炉供热 Q_s sdpvar(1, T); % 储热放热正为放热 S sdpvar(1, T); % 储热容量状态 %% 约束集合 Constraints []; % 电力平衡 Constraints [Constraints, sum(P_chp, 1) P_con P_w - P_eb P_load]; % 热力平衡 Constraints [Constraints, sum(Q_chp, 1) Q_eb Q_s H_load]; % CHP可行域 for i 1:2 Constraints [Constraints, 0 Q_chp(i,:) CHP.Hmax(i)]; Constraints [Constraints, P_chp(i,:) CHP.Pmin(i) CHP.k1(i) * Q_chp(i,:)]; Constraints [Constraints, P_chp(i,:) CHP.Pmax(i) - CHP.k2(i) * Q_chp(i,:)]; end % 纯凝机组 Constraints [Constraints, P_con_min P_con P_con_max]; % 风电出力范围 Constraints [Constraints, 0 P_w P_w_pre]; % 储热约束 Constraints [Constraints, -q_s_max Q_s q_s_max]; Constraints [Constraints, 0 S S_max]; % 储热动态过程 S0 0; % 初始储热量 for t 1:T-1 Constraints [Constraints, S(t1) S(t) - Q_s(t) S0 * (t1)]; end Constraints [Constraints, S(T) S0]; % 调度周期末回到初始值 % 电锅炉 Constraints [Constraints, Q_eb eta_eb * P_eb]; Constraints [Constraints, 0 P_eb 50]; %% 目标函数 rho_pen 500; % 弃风惩罚系数 objective sum(0.02 * P_chp(1,:).^2 10 * P_chp(1,:)) ... sum(0.025 * P_chp(2,:).^2 12 * P_chp(2,:)) ... sum(0.03 * P_con.^2 8 * P_con) ... rho_pen * sum(P_w_pre - P_w); %% 求解 ops sdpsettings(solver, gurobi, verbose, 1); result optimize(Constraints, objective, ops); %% 结果提取 P_chp_opt value(P_chp); Q_chp_opt value(Q_chp); P_w_opt value(P_w); P_eb_opt value(P_eb);这段代码里的储热动态约束我用了一种常见写法但有个细节要提醒你S(t1) S(t) - Q_s(t)如果你希望调度周期内储热罐允许净充放不强制回到初始值可以删掉S(T) S0那行并在目标函数里对最终储热量不加约束。是否强制周期平衡取决于你的研究场景——如果是单日优化很多文献会要求回到初始值如果做多日滚动就不需要。另外sum(P_chp, 1)在YALMIP里得到的是1×T的行向量P_con本身是1×T等式两边维度一致才能匹配。如果你用的是sdpvar(T, 1)的列向量布局所有约束的写法都要跟着调整建议在脚本最开始就统一变量维度方向别一会儿行一会儿列。3.4 求解器参数与数值稳定性调优模型写完之后求解器参数设置也会显著影响效率和成功率。我常用的几个sdpsettings选项ops sdpsettings(solver, gurobi, ... verbose, 2, ... debug, 1, ... gurobi.TimeLimit, 120, ... gurobi.MIPGap, 0.001);debug设为1后如果模型本身有问题YALMIP会额外输出诊断信息帮助定位是哪些约束导致不可行。MIPGap是混合整数规划的相对最优间隙工程上设到0.1%已经足够太小的值会显著拖慢求解速度。数值稳定性方面最常见的坑是量纲差异过大。比如煤耗成本系数是几十、弃风惩罚是几百、储热容量是几百这些数量级还好但如果你把热负荷设成几千GJ、储热容量设成几万GJ目标函数里各项的数值可能相差几个数量级求解器内部处理起来就容易出问题。我的建议是全部统一到MW和MWh量纲让变量值落在0到1000这个区间内。4. 仿真结果与效果剖析4.1 场景设置与方案对比为了体现联合优化的价值我设置了三个方案做对比方案A传统“以热定电”调度即热负荷按固定比例分配给各台CHP机组机组电出力下限由热出力直接折算不含优化。方案B联合优化调度CHP机组的热电出力在可行域内自由优化但不含储热和电锅炉。方案C联合优化调度并加入储热罐和电锅炉。我取了一个大风低温的冬季典型日。风电预测的最大出力达到280MW夜间电负荷低谷只有500MW热负荷峰值达到200MW系统内灵活性资源不足时会面临严重的消纳压力。4.2 结果对比与指标解读仿真结果整理成下表指标方案A以热定电方案B联合优化无储热方案C联合优化储热电锅炉弃风电量MWh812346112弃风率%21.59.23.0系统运行成本万元33.830.228.6CHP总发电量MWh512048604620储热罐充放量MWh——486几个数据点非常值得解读。方案A弃风率高达21.5%这在北方冬季是真实会发生的情况。方案B把CHP机组的热电出力从“固定比例”变为“可行域内优化”热负荷自动向对电出力约束较小的机组倾斜弃风率降到了9.2%——这说明仅仅优化热负荷分配就能带来巨大增益。方案C进一步引入储热和电锅炉弃风率压到3.0%。从调度曲线上看夜间风电大发时段22:00到次日6:00电锅炉开启、储热罐充电CHP机组的电出力相应压到较低水平白天负荷攀升后储热罐放热CHP机组把省下来的电量额度用于高负荷时段发电。整个调度过程相当于把夜间的“热负荷需求”平移到了白天给风电腾出了宝贵的夜间空间。这里也能直观看到储热容量与消纳效果的关系。我把储热罐容量从0增加到400MWh做了一组灵敏度分析弃风率下降曲线呈现明显的边际递减容量从0到150MWh时弃风率平均每增加50MWh下降约4个百分点超过200MWh后每增加50MWh只下降约0.8个百分点。做项目汇报时这类曲线比单点结果有说服力得多。4.3 方案背后热电解耦的物理本质从结果里提炼一个核心逻辑风电消纳空间本质上取决于系统的向下调峰能力。热电机组“以热定电”限制了最小电出力而储热罐和电锅炉的加入本质上是在时间维度上把热负荷从低风电时段挪走或者把风电变成热负荷——一句话在“热”的维度上给系统增加灵活性。这也是为什么业内常说“风电消纳不只是电的问题而是电-热协同的问题”。单纯盯着电网侧做优化空间有限把热网、储热、电锅炉这些热力侧资源纳入联合调控视野一下子就打开了。你在写论文或者做项目总结时把这一层逻辑讲清楚价值会比堆砌仿真数据高很多。5. 常见问题与调试实录5.1 求解器报“不可行”的三板斧排查法我的模型第一次跑的时候就报Infeasible problem了。这种情况不要慌按顺序排查我总结了三板斧。第一板斧检查平衡约束。电力平衡和热力平衡是否在物理上可满足比如电负荷最小值减去所有机组最小出力、再加上风电预测最大值如果是负数说明任何时刻系统出力都高于负荷必然不可行。用一个简单脚本先在不引入决策变量的情况下做可行性预判能省很多时间。第二板斧把目标函数去掉只求可行解。把objective设为一个常数0调用optimize(Constraints, 0, ops)。如果约束本身有解求解器会返回solved如果仍然不可行就说明约束内部有冲突。第三板斧加松弛变量定位冲突来源。在最可疑的约束上加上非负松弛变量例如把电力平衡写成slack_p sdpvar(1, T); Constraints [Constraints, sum(P_chp,1) P_con P_w - P_eb slack_p P_load];求解后看哪些时段slack_p非零那个时段就是问题所在。常见的冲突来源包括储热罐容量太小但热负荷必须靠储热来平衡、CHP机组热出力上界不够支撑热负荷、风电并网功率被约束得过死等。定位到具体时段和具体约束后修改参数就有的放矢了。5.2 结果不合理、出力跳变的排查思路如果求解成功但结果看着怪怪的比如CHP机组出力在相邻时段剧烈跳变、储热罐充放功率反复折腾先别怀疑求解器抽风大多数时候是模型本身的问题。第一个排查项是爬坡约束缺失。我前面的简化模型里没有加爬坡约束实际运行时CHP机组电出力的变化率是受限的。文献中常见写法是Ramp_up 30; Ramp_down 30; for t 1:T-1 for i 1:2 Constraints [Constraints, P_chp(i,t1) - P_chp(i,t) Ramp_up]; Constraints [Constraints, P_chp(i,t) - P_chp(i,t1) Ramp_down]; end end第二个排查项是储热罐的动态约束写反了符号。我之前见过有人把S(t1) S(t) - Q_s(t)写成了S(t1) S(t) Q_s(t)结果是储热罐越放热存量越高调度结果呈现出完全违背物理规律的特性。做结果后处理时除了看数值还应该顺手画一画储热罐容量曲线看看形状是否符合直觉。第三个排查项是目标函数的惩罚系数过小。如果你的弃风惩罚系数ρ_pen只有煤耗系数的两三倍优化器会在“弃风”和“多烧煤”之间权衡得到的可能是一个折中方案弃风率偏高。这个不算bug但你得明白惩罚系数本质上代表了你对弃风的主观厌恶程度想让结果接近“最大化消纳”就把系数调大试到弃风率变化不再敏感为止。5.3 大规模实例的数值稳定性与加速技巧调度周期从24小时扩展到96小时或者机组数量增加到10台以上后求解时间会明显上升。我的经验是优先检查模型里有没有整型变量。YALMIP中如果你不小心把某个本来就是整数的参数声明成了binvar或intvar求解器会把它当混合整数问题来处理速度根本没法比。检查方法很简单在optimize之前用class(Constraints)看类型、用yalmiptest确认求解器识别正确。另一个加速技巧是可以给sdpvar变量显式指定上下界而不是把上下界写进约束集合里。YALMIP内部对变量边界信息的处理比普通约束更高效能帮求解器更快做预求解。写法是P_con sdpvar(1, T); P_con [P_con, bounds, 60, 180];不过这个写法在最新版YALMIP中容易写错我更推荐直接用约束对象设置边界必要时才用边界变量声明。实际项目中最有效的加速手段还是数据归一化——把负荷都除以基准值100功率变量落到0到3之间数值稳定性肉眼可见地改善。5.4 一个容易被忽视的工程问题热网管网的蓄热效应最后聊一个扩展问题。真实系统中热网的供水管和回水管本身就有巨大的热容量管网的蓄热效应也能参与调峰这就是所谓的“热网蓄热”。学术研究中经常把它忽略掉但实际工程里热网蓄热在小时级调度中能贡献可观的灵活性相当于一个天然的储热罐。如果你想把项目做深可以在模型里把热负荷由“单点热平衡”改为“节点热平衡”或者加入热网动态模型比如用有限差分法描述管道内水温分布。这样做模型复杂度会上一个台阶但仿真结果会更贴近实际。我建议先做好基础的储热和电锅炉版本再去啃热网动态建模这块硬骨头否则很容易被复杂的偏微分方程困住半天出不来结果。我自己调试这个项目时有个很深的体会别急着把模型往上堆复杂先把简单的两机系统、24时段案例跑透看明白储热罐在夜间充电、白天放电、风电并网率提升的整个过程再一步步加设备、加约束、加长调度周期。优化模型不像写代码不是功能堆得越多越好——每一个约束都可能成为压垮求解器的最后一根稻草。把调度逻辑想透彻比多写几百行代码更有用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑