ARTICLE DETAIL

资讯详情

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

Matlab热电联产机组联合优化:储热罐解锁风电消纳与调度建模实践

Matlab热电联产机组联合优化:储热罐解锁风电消纳与调度建模实践 风电最大化消纳这几个字做电力调度的人一听就知道分量。尤其是北方冬季供暖期热电联产机组被“以热定电”死死锁住电网想多收风电都腾不出空间。我最近基于Matlab做了一套热电联产机组联合优化控制模型把电、热两侧的资源放进同一个优化框架里协同求解实测下来弃风率能压下去一大截。这个过程里踩了不少坑也沉淀了一套能直接改着用的代码框架今天一次性把思路、建模、代码和排查经验讲透。这套内容适合三类人一是正在写电力系统调度方向论文的研究生可以把这套模型当成基座去扩展二是电网调度或综合能源方向的工程师想搞清楚CHP机组与风电协同控制的工程实现路径三是做Matlab仿真、需要一套能落地跑通优化框架的同学。不管你是哪一类看完至少能把模型骨架和核心代码逻辑搭起来再按自己的数据去改参数、加约束、换算例。1. 弃风的根源热电联产机组为什么锁死了风电空间1.1 先看懂CHP的“以热定电”为什么供暖季风电最难过热电联产机组CHP的原理可以简单理解成燃料燃烧先发电发电后产生的余热通过热网送给用户采暖。相比纯凝火电机组它的综合能源利用效率高不少但代价是电出力和热出力之间强耦合。尤其进入供暖季热网要维持稳定的供水温度热负荷大机组的电出力就不能随便往下压——这就是业内常说的“以热定电”。这里又分成两类情况背压式机组没有可调抽汽口电出力和热出力近似成正比基本没有调节自由抽凝式机组可以调整抽汽量在一定范围内改变热电比但热负荷升高时电出力下限同样会被推高。说白了冬天越是供暖CHP的最低电出力就越高。这个特性直接造成了风电消纳的困局深夜风电大发的时候恰好是电网负荷低谷又恰恰是供暖需求最高的时段。一边是CHP为了供热必须维持较高的电出力一边是风电想多发电却被电网空间卡住最后的结局就是被迫弃风。所以要解决风电最大化消纳首先绕不开CHP这台“灵活的呆子”——它不能停、不能大幅压出力但又直接影响着电网能腾出多少空间来装风电。1.2 一个典型冬季夜间的弃风推演光说原理过于抽象我搭了个测试系统来推演一次夜间调度。系统里有一台300MW的CHP机组、一台100MW的风电场还有一台最小技术出力40MW的不可停调峰机组。夜间电负荷380MW热负荷大约200MW。CHP因为承担200MW供热受热电耦合约束电出力下限被顶到220MW左右。调峰机组最小出力40MW这两部分合计基荷就是260MW。此时风电预测出力150MW电网给风电的空间只剩380减去260也就是120MW。结果很清楚即便风电全发也要至少弃掉30MW。这一个夜晚过去弃风电量就是几十万千瓦时。如果引入储热罐整个画面就变了。白天热负荷低的时候储热罐多吸收一部分热量储存起来夜间由储热罐向热网放热CHP的供热负担从200MW降到120MW电出力下限也随之降到约150MW。此时基荷变成190MW风电空间扩大到190MW150MW风电可以全额消纳。同样是这套系统只多了一个储热装置夜间弃风就从必弃变成了零弃。这就是“联合优化”和搞一套模型的意义不是靠调度员拍脑袋“今晚让储热多放点热”而是把储热罐、CHP、风电、负荷放在同一个优化问题里自动算出每一时刻谁该出力、谁该蓄能、谁该让路。1.3 联合优化控制的本质把热网灵活性搬进电网调度传统调度模式里电网侧按电负荷安排机组出力热网侧按热负荷安排供热两边各管各的。CHP作为连接点通常是一天开始前就把供热计划定死了电出力跟着供热跑几乎没有给风电让路的余地。联合优化控制的本质是把热网侧的调节手段变成电网调度的可用资源。储热罐可以平移热量、电锅炉可以把多发的风电转成热能、热网的管道和建筑热惯性可以允许温度在短时间内小幅波动——这些都是热网侧原本就存在的“灵活性”只不过单靠人工和旧调度流程很难把它们和风电预测精确匹配起来。所以我做的这套模型核心思考就是把“热”当成一种可以储存、可以延迟使用、可以和“电”互相转化的柔性资源和CHP电出力一起纳入优化变量。这样编排出来的调度计划每一项出力都是全局最优解的一部分而不是各个系统各自为政后的折中。下一节详细说这个模型是怎么搭的。2. 联合优化控制的核心思路与建模方案2.1 解耦“以热定电”的三条路线储热罐、电锅炉与热网蓄热想让CHP不再“锁死”风电空间工程上主要有三条技术路线。我梳理过它们各自的特点也都在模型里做过不同程度的验证。第一条路线是配置储热罐也就是蓄热式热电联产。CHP多发电的时候同时多产热把热量存进储热罐夜里热负荷不变但储热罐向热网放热CHP就可以降低热出力、释放电出力空间。这条路线技术成熟、改造简单、成本可控是目前国内北方热电联产系统里应用最广的做法。第二条路线是配置电锅炉或热泵。风电富余时段用多余风电驱动电锅炉制热替代部分CHP供热相当于把“风”存成了“热”。这条路线对消纳风电的响应速度很快但电锅炉本身效率再高也抬高了系统电耗经济性需要仔细算账。第三条路线是利用热网管道的蓄热特性和建筑物的热惯性。热网本身是一个巨大的储能体温度允许在安全范围内波动瞬时供热和末端用热之间其实有时间差。这条路线不需要额外买设备但控制精度要求高模型里需要把热网动态过程表达出来复杂度直线上升。我把三条路线的适用场景做了个对比技术路线成本投入控制复杂度对风电消纳的帮助适合场景储热罐中等较低高已有CHP系统的灵活性改造电锅炉/热泵中高中等很高风电富余量大、需要快速增加消纳能力热网蓄热/热惯性低高中高热网规模大、允许温度调节余地大的系统我实际建模型时采用储热罐作为基础方案原因是它的建模相对可控、物理逻辑清晰非常适合把优化框架跑通之后再做扩展。电锅炉可以作为扩展模块接在模型里只要在热平衡约束里追加一个电转热项就行。2.2 目标函数怎么设计模型才会“主动”多吃风电优化模型的目标函数是“指挥棒”。要让模型主动去消纳风电不能只写一个“系统成本最小”就完事因为火电和CHP的边际成本通常低于弃风惩罚模型不会平白无故去冒风险多收风电。我常用的目标函数是一个综合成本目标函数 min F Σ 煤耗成本 Σ 弃风惩罚其中煤耗成本用CHP电出力的二次函数来近似弃风惩罚项写成风电预测出力与实际消纳出力之差乘以一个惩罚系数。这个惩罚系数的设置很关键——它必须高于CHP的边际发电成本让模型从经济角度“自愿”选择消纳风电而不是被迫接受一个约束。我测试系统里用了这样的取值思路CHP煤耗成本折算后大约在0.35元/千瓦时左右弃风惩罚系数取到1.0元/千瓦时以上。这样只要风电有上网空间模型宁愿多消纳风电也避免出现弃风。还有一点要注意煤耗成本里的二次项会让模型变成二次规划QP。实际工程里为了求解稳定和兼容混合整数约束我更推荐把煤耗曲线分段线性化或者直接近似成线性成本。原因不复杂——风电消纳的核心矛盾在物理约束成本函数的精度只要不扭曲各机组之间的相对经济性对结果的影响完全可控。2.3 约束条件逐条拆解别让物理边界跑偏这一节是整个模型的灵魂。约束写得不对求解器再强也给你返回一个物理上毫无意义的结果。第一是电功率平衡约束。每一时刻CHP电出力加上风电机组实际出力必须等于电负荷。这是个硬约束任何调度计划都必须满足。第二是热功率平衡约束。CHP热出力加上储热罐净放热功率必须等于热负荷。这里储热罐放热算正、充热算负用带符号的变量去建模。第三是CHP运行可行域约束。抽凝式CHP的电出力和热出力之间是个凸多边形可行域通常用几个顶点坐标来定义。在Matlab里把顶点坐标整理成一个矩阵逐边求出法向量就可以写成形如“A * [H;P] b”的线性不等式组。这一步新手最容易出问题——顶点顺序错了或者法向量方向不统一可行域会变得乱七八糟所以我在代码里特意加了注释提醒检查内点方向。第四是爬坡约束。CHP电出力在相邻时段之间的变化不能超过爬坡速率上限。热出力通常也建议加一个变化率限制避免模型给出每秒剧烈波动的热出力计划。第五是储热罐约束。储热罐的储热状态SOC按每小时充放热功率累加同时要限制SOC在容量上下限之间充放热功率也不超过设计值。这里有个模型细节很多教材直接用带符号的净充放热变量不区分充和放好处是模型保持线性如果在意效率差异就要拆分充热和放热两个非负变量并引入二进制变量防止同时充放。第六是风电出力上限约束。风电机组实际出力不能超过预测出力这是消纳问题的物理上限也决定了弃风率怎么算。把这些约束全部写进Matlab之后模型就是一个线性规划LP如果把机组启停整数变量加进来就变成混合整数线性规划MILP。这一整套约束搭完核心框架就立住了。3. Matlab实现从数据准备到求解出图的完整流程3.1 环境选型YALMIP加上商业求解器到底值不值Matlab里搭建优化模型我首选YALMIP这个建模工具箱。它的好处是语法接近数学表达式写出来的约束一眼就能读懂不像手动拼矩阵那样容易出错。比如电功率平衡约束在YALMIP里就是一行“P_chp P_w P_load”对应的矩阵全自动处理好。求解器方面有条件就上CPLEX或Gurobi对LP和MILP的求解速度和稳定性都有保障。如果暂时没有商业求解器授权Matlab自带的linprog和intlinprog也能跑只是在大规模场景下会慢不少。我记得有一次测试24时段、少量0-1变量的模型用自带的intlinprog跑了四十多秒换Gurobi后不到三秒就出结果。对于要做参数敏感性分析或者提升规模的场景商业求解器基本属于刚需。版本方面不用太纠结近年版本的Matlab配YALMIP都没什么兼容性障碍。如果你装的是旧版Matlab记得去YALMIP官网下最新源码包把路径加进Matlab的路径设置里就行。3.2 数据准备清单与预处理模型要跑起来先得把数据备齐。我习惯把所有输入集中到一个脚本或者Excel表里方便换算例。这套模型最少需要以下几类数据数据类别内容说明负荷数据24小时电负荷MW每个时段一个值热负荷数据24小时热负荷MW末端供暖需求风电数据24小时风功率预测值MW消纳上限CHP参数容量、煤耗系数、热电耦合可行域顶点顶点是建模关键储热罐参数容量、初始SOC、充放热功率上限决定灵活性大小其他机组参数最小出力、爬坡速率参与平衡约束预处理主要做两件事一是时间对齐确保所有序列从同一时刻开始、步长一致我的算例都是24个时段、每时段1小时二是单位统一功率用MW、能量用MWh储热罐容量用MWhSOC迭代计算时功率乘以时间步长再累加千万别混。3.3 代码架构与核心实现片段代码我按五个模块来组织数据输入、模型构建、约束组装、求解执行、结果绘图。这样每次只改一个模块不影响其他部分。核心片段的核心逻辑大概是这样的% 决策变量24时段全为连续变量 P_chp sdpvar(1, 24, full); % CHP电出力 H_chp sdpvar(1, 24, full); % CHP热出力 P_w sdpvar(1, 24, full); % 风电实际出力 H_tank sdpvar(1, 24, full); % 储热罐净放热功率正为放热 % 约束集合 Constraints []; % 电功率平衡CHP 风电 电负荷 Constraints [Constraints, P_chp P_w P_load]; % 热功率平衡CHP热出力 储热罐放热 热负荷 Constraints [Constraints, H_chp H_tank H_load]; % CHP运行可行域A_chp和b_chp由顶点坐标生成 Constraints [Constraints, A_chp * [H_chp; P_chp] b_chp]; % 储热罐SOC状态递推初值从参数传入 % 注意储热罐放热为正SOC会减小 Constraints [Constraints, SOC(2:end) SOC(1:end-1) - H_tank(1:end-1)]; % 目标函数煤耗成本 弃风惩罚 Objective sum(CHP_cost_coeff .* [ones(1,24); P_chp; P_chp.^2]) ... lambda_w * sum(P_w_forecast - P_w); % 调用求解器 ops sdpsettings(solver, gurobi, verbose, 1, showprogress, 1); sol optimize(Constraints, Objective, ops); if sol.problem 0 P_chp_opt value(P_chp); % 取出最优解 P_w_opt value(P_w); H_tank_opt value(H_tank); else disp(sol.info); % 打印失败原因 end这段代码是示意性质的真实工程里我建议把决策变量定义、约束构建、求解执行拆成三个函数方便扩展。还有几个地方值得提醒YALMIP里sdpvar(1,24,full)的full代表整个向量都是决策变量默认的toeplitz模式容易搞混SOC迭代写成向量表达式比写for循环要快打算同时充放约束时就要引入二元变量模型升级为MILP。3.4 结果怎么看弃风率、成本与储热曲线的三层校验求解完成后第一件要做的事不是画图而是算指标。弃风率的定义很简单弃风量除以风电预测总量。用Matlab就是一句话curtailment_rate (sum(P_w_forecast) - sum(P_w_opt)) / sum(P_w_forecast);我习惯再把三个场景放在一起对比场景夜间弃风率全天弃风率系统运行成本万元无联合优化CHP固定供热21.4%9.8%132.6联合优化储热罐0%2.3%119.4不同算例的数字会有差异但趋势是一致的储热罐接进联合优化之后CHP电出力下限被压低风电消纳空间明显扩大系统成本同步下降。第三层检查是看曲线。我固定输出三张图风电预测与实际出力对比曲线、CHP电出力时序曲线、储热罐SOC曲线。尤其是SOC曲线正常情况应该是白天慢慢充高、夜间逐步放低如果出现频繁锯齿形波动说明目标函数缺少对储热罐动作的约束这个问题的具体解法在第4章里细讲。4. 实操中的常见问题与避坑指南4.1 模型无解时的定位技巧先别慌用松弛变量找矛盾模型求解器返回“infeasible”几乎每个做过优化调度的人都会遇到。我踩过太多次总结出一个有效的定位套路在怀疑有矛盾的约束上临时加一个非负松弛变量比如电平衡约束改成“等式左边加上松弛变量等于负荷”然后单独求解目标为松弛变量最小化。松弛变量最后不为零就说明问题就出在这条约束上。实际最容易翻车的三个地方我挨个说。第一个是储热罐初末状态设置。很多算例要求一个调度周期结束后SOC回到初始值如果初始SOC设得过高、容量又小加上夜间需要大量放热约束就必然冲突。解决办法是给SOC终值设成可调的或者放宽成“不低于某个下限”。第二个是CHP可行域顶点顺序。顶点坐标顺序写反线性不等式组描述的区域就跟物理事实南辕北辙模型可能无解也可能给出一个荒谬的调度计划。我建议在建模前先单独画一下可行域多边形肉眼确认没问题再接入整体模型。第三个是功率平衡约束里的量纲问题。有的参数是MW有的经过计算变成了MWh或者kW一旦混用模型数值上差了好几个数量级求解器很难收敛甚至直接判定无解。所以在数据预处理阶段统一单位比起事后排查要省力得多。4.2 求解器报错排查YALMIP找不到求解器的典型原因很多时候模型本身没问题卡在了求解器调用环节。最常见的报错是“No suitable solver for the model class”。这句话的意思是YALMIP判断你的模型是某个类型但你配置的求解器不支持它。比如模型里因为成本二次项变成了MIQP而你只配置了CPLEX但没有对应的MIQP接口授权YALMIP就会提示无可用求解器。我的处理顺序是先确认模型类别把二次成本改成线性化让模型回到MILP范畴或者在sdpsettings里明确指定求解器避免YALMIP自己去猜。还有一类问题出在求解器路径配置上。装了Gurobi或CPLEX之后一定要在Matlab里执行环境脚本把路径加进去并且用简单的测试命令验证求解器能被YALMIP识别。我习惯先跑一个只有两三个变量的小模型确认求解正常后再灌入完整的24时段算例。小步验证永远比一次跑大模型然后慢慢找错要高效。4.3 调度结果“太理想”反而危险储热罐频繁充放的修正这是我最想提醒大家的一个坑。有一次我跑出一套结果全天弃风率低到0.2%看着非常漂亮。但把储热罐SOC曲线画出来发现问题大了——SOC在一天之内来回震荡了超过二十次一会儿充到95%一会儿放到5%完全不是现场储热设备能承受的运行方式。问题根源在于目标函数没有给储热罐动作设置代价或约束。在数学上只要充放热不直接产生成本模型就会利用储热罐的灵活性频繁倒腾热量用一个本身就不经济的运行方式去追求表面的成本最优。解决办法有两种。简单粗暴的是直接限制储热罐在一个调度周期内允许的充放转换次数通过一组二进制变量实现温和一些的是在目标函数里加入一个很小的充放动作惩罚项让模型在消纳风电和减少动作之间形成平衡。我实际更推荐后者保留求解速度又不会牺牲风电消纳效果太多。4.4 求解耗时与收敛性调优保持线性是最划算的投资24时段的模型本身规模不大正常应该几秒内求解完毕。如果发现求解时间成倍增长甚至半天出不了一个解先检查模型还是不是线性规划。最典型的非线性来源有两个一个是成本函数里的二次项煤耗曲线写成二次函数后模型从LP变成QP再叠加整数变量就是MIQP求解难度上升一个台阶另一个是约束里的非线性运算比如出现了功率的平方、乘除运算或者指数函数YALMIP会直接把它归为非线性规划很多通用求解器处理不了耗时自然暴涨。我的建议非常直接做这种热电联合优化调度坚持用线性模型。煤耗曲线用分段线性逼近效益损失可以忽略不计但求解稳定性提升是质的飞跃。如果确实需要处理机组启停这类决策变量用二进制变量加上Big-M约束化成MILP交给Gurobi这类求解器拿捏通常都能在可接受时间内完成。最后分享一个我自己用惯的工作流先用固定风电序列、关闭整数变量把模型跑成纯LP确认约束和参数没有低级错误以后再逐步加入整数变量和完整场景。这套流程帮我省下了大量排查时间也保证了每一次优化结果的工程可信度。
返回列表