ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于MATLAB+YALMIP的冷热电联供系统经济优化调度建模与求解

基于MATLAB+YALMIP的冷热电联供系统经济优化调度建模与求解 干这行久了你会发现凡是挂着“多能源优化”“综合能源调度”名字的程序最后落地最难的地方往往不在算法多高深而是如何把物理系统“翻译”成一组能让求解器又快又准处理的数学表达。今天要拆的这套冷热电联供系统CCHP经济优化运行与多能源系统优化的MATLAB程序正是干这件事的。它解决的是这样一个实际问题在电价分时波动、气价固定、用户冷热电负荷逐时变化的条件下24小时内每一台设备该怎么启停、发多少功率才能让整个系统的运行成本最低。适合正在做分布式能源、综合能源、微电网方向毕业设计或工程项目的读者参考特别是打算用MATLAB搭建经济优化调度模型但还没理清建模主线的同学。1. 冷热电联供系统到底在优化什么1.1 一次读懂CCHP的能量流CCHP的核心思想是能量的梯级利用。最常见的配置是天然气进入燃气轮机或燃气内燃机发电发电之后的高温烟气并不直接排掉而是进入余热回收装置冬季直接通过换热器供热夏季则驱动溴化锂吸收式制冷机供冷。这套逻辑听起来简单但真正落到优化模型里你需要把整个系统拆成三条能量母线来理解。第一条是电母线。燃气轮机的发电功率首先汇入电母线电网联络线的购电和售电同样汇入这条母线蓄电池放电相当于往母线注入功率充电则是从母线抽出功率。电母线还要给电制冷机供电最后才是满足用户的电负荷。第二条是热母线。燃气轮机的余热回收功率、燃气锅炉的出力如果有、蓄热罐的放热功率这些是热母线的供给侧。热母线的需求侧包括用户的热负荷和吸收式制冷机消耗的热量。第三条是冷母线。吸收式制冷机的冷出力、电制冷机的冷出力、蓄冷罐的放冷功率供给用户冷负荷。三条母线之间通过设备耦合在一起燃气轮机的发电量直接决定余热回收的上限而余热回收的热量又同时影响供热能力和吸收式制冷的冷产出。这种强耦合关系是最考验建模功底的地方。1.2 经济优化运行要解决的实际问题用大白话讲这个程序回答的是未来24小时每个小时燃气轮机该发多少电、电制冷机该开多少、吸收式制冷机用多少余热、蓄电池什么时候充放电才能使总成本最低。成本端包括几个部分燃气轮机消耗天然气的燃料成本是大头从电网购电的成本在分时电价下峰谷差异明显如果允许余电上网售电收入是一个负成本项设备启停成本和运维成本在目标函数里占比不大但对调度方案的工程可执行性影响不小储能设备的充放电折旧也会影响最终决策。优化的最终输出是一组时间序列调度计划。这组计划可以直接交给能量管理系统或者作为日前调度的参考方案。程序计算出来的结果不是一条曲线的最大值而是基于数学优化得到的一个可执行方案。1.3 为什么用MATLAB来搭这套模型这个话题经常有人争论Python不是也挺好吗我的实际感受是MATLAB做CCHP经济调度有几个不可替代的优势。第一是YALMIP这个建模工具箱。它让目标函数和约束条件的表达方式与数学公式几乎一一对应调试效率非常高。用Python做类似的事虽然Pyomo、PuLP也能实现但生态成熟度和社区资源跟MATLABYALMIP比还是有差距特别是对于从没踩过优化建模坑的新手来说YALMIP的上手曲线平缓得多。第二是MATLAB的toolbox覆盖了从数据拟合、数学优化到符号推导的完整闭环。你在做完优化之后要做灵敏度分析、要验证KKT条件、要画后处理图表这些工作在同一个环境里就能完成不需要频繁切换语言和工具链。第三是代码的阅读门槛低。给导师或项目交付方汇报的时候MATLAB代码的可读性和调试图形的美观度都是加分项。2. 整体建模思路与方案选型建优化模型的顺序很有讲究。我会先建立设备的物理模型再定义经济目标最后补充约束条件。这个先后顺序决定了你思考问题的框架反过来组装容易漏项。2.1 核心设备建模与能量平衡关系先看燃气轮机。最简单的线性模型是把燃料输入功率表示成发电电功率的一次函数。记F_gt(t)为t时段燃料输入功率P_gt(t)为发电功率u_gt(t)为启停状态0-1变量可以写为F_gt(t) a * P_gt(t) b * u_gt(t)其中a是线性系数b是空载燃料消耗系数。实际项目里为了少引入一个非线性往往直接用效率折算F_gt(t) P_gt(t) / η_gtη_gt通常取30%到40%。注意这个模型只在额定工况附近比较准确在低负荷率下会有偏差但作为日前调度已经够用。余热回收量与发电功率强相关通常写成H_rec(t) P_gt(t) * (1 - η_gt) * η_recη_rec是余热回收效率工程上取0.7到0.8比较常见。发电功率有上下限约束和最小技术出力约束写为P_gt_min * u_gt(t) P_gt(t) P_gt_max * u_gt(t)这里用u_gt(t)乘以边界保证了机组停机时出力为0开机时出力必须在最小技术出力和最大容量之间。这条约束是MILP模型的标志性写法处理不当会导致机组可以在停机状态下产生出力求解结果毫无物理意义。吸收式制冷机的建模比较简单冷出力等于输入热功率乘以COP_acCOP_ac一般取1.2到1.5。电制冷机则是消耗电功率产生冷量COP_ec通常取3.0到5.0。这里有一个经常被忽略的点吸收式制冷机的COP低于电制冷机但它的驱动能源是余热机会成本低。夏季工况下两者是竞争关系系统需要比较“天然气发电再用余热制冷”和“直接购电用电制冷”哪种更经济。这个比较的边界取决于电价、气价和两个设备的COP是优化模型里最有价值的经济逻辑之一。蓄电池模型需要引入荷电状态SOC的递推关系SOC(t1) SOC(t) (η_ch * P_ch(t) - P_dis(t) / η_dis) * ΔtSOC(t)是t时段末的荷电状态或按约定取时段初P_ch和P_dis分别是充电和放电功率η_ch和η_dis是充放电效率Δt是时间步长。容量上下限约束、充放电功率上限约束、充放电互斥约束缺一不可。互斥约束用0-1变量和不等式表达u_ch(t) u_dis(t) 1这个约束排除了“边充边放”的假象。在实际调试中如果发现求出的SOC曲线像锯齿一样频繁波动首先就该检查充放电互斥约束是否生效。三条母线的能量平衡方程是整个模型的骨架。电平衡、热平衡、冷平衡各列一条电平衡P_gt(t) P_grid_buy(t) - P_grid_sell(t) P_dis(t) - P_ch(t) - P_ec(t) Load_e(t)等号左边是所有电供给和电消耗的代数和右边是电负荷。热平衡类似冷平衡类似。这组方程如果有个符号写错求解器要么报不可行要么解出一个物理上不可能的方案。2.2 优化目标函数的设定逻辑最基本的单目标经济优化目标函数是min Σ_t [ c_gas * F_gt(t) c_elec_buy(t) * P_grid_buy(t) - c_elec_sell(t) * P_grid_sell(t) c_OM * (P_gt(t) ...) ]第一项是燃料成本第二项是购电成本第三项是售电收益负成本第四项是运维成本。如果要扩展还可以加入启停成本、碳交易成本等。写目标函数有两个细节我提醒大家注意。第一个是购售电互斥约束。如果不加P_grid_buy(t) M_elec * u_grid(t) P_grid_sell(t) M_elec * (1 - u_grid(t))求解器会在电价有利可图时同时设置购电和售电为正人为造出一个套利回路产生荒谬的优化结果。这个约束在论文模板里经常被省略但实际项目里必须加。第二个是启停成本线性化。直接写C_su Σ_t c_su * max(u_gt(t) - u_gt(t-1), 0)max操作是非线性的需要在MILP里引入辅助变量来线性化。不处理的话求解器会绕过这个成本项导致频繁启停。2.3 约束条件怎么列才不漏项做CCHP优化漏约束是返工的最大原因。我把排查清单分享在这里设备容量约束是否对每个设备都写全了上下限并且乘上了对应的启停变量能量平衡方程是否三条母线都覆盖且符号方向正确储能递推方程是否在时间维度上连续起始SOC是否固定是否要求终值回到初始值充放电互斥和购售电互斥是否都加入吸收式制冷机的输入热功率是否和热母线平衡方程连接正确燃气轮机的最小连续运行时间约束是否需要如果机组启动后不能立刻停机联络线功率限值是否考虑进去。2.4 求解器选型内置函数还是YALMIPGurobi如果问题规模只是24时段的单机组、单储能、单制冷机用MATLAB内置的intlinprog完全可以跑通。但我不建议第一版就直接用内置求解器写因为intlinprog的建模方式需要把所有约束拼成标准矩阵形式调试效率和代码可读性都比较差。更推荐的方式是用YALMIP建模求解器可以切换。YALMIP写约束几乎和数学表达式一样方便阅读和验证。遇到模型规模扩大、整数变量增多时把求解器切换到Gurobi或CPLEX即可。我个人的组合是YALMIP GurobiGurobi在MILP求解性能上比CPLEX更快特别是在大整数变量问题上表现更稳定。对新手来说YALMIP intlinprog足够跑通第一版。3. 关键代码实现与参数配置3.1 数据结构与输入参数定义用MATLAB的struct整理参数比散落成单独变量可维护性高很多。以24时段日前调度为例我需要定义负荷数据、分时电价、天然气价格和一组设备参数。T 24; % 调度周期小时 dt 1; % 时间步长小时 % 负荷数据kW实际项目中这部分应从历史数据或预测模型获取 Load_e [100 95 90 85 80 90 110 150 180 200 210 190 180 175 180 200 220 210 180 160 150 140 120 110]; Load_h [80 75 70 65 60 55 50 45 40 45 50 55 60 65 70 80 90 85 80 75 85 90 85 80]; Load_c [30 25 20 15 10 20 40 60 90 120 140 160 150 130 150 160 140 110 80 60 50 40 35 30]; % 分时电价元/kWh c_elec [0.5 0.5 0.5 0.5 0.5 0.6 0.8 0.9 0.9 0.9 0.9 0.8 0.8 0.8 0.9 0.9 0.9 0.9 0.8 0.8 0.7 0.6 0.5 0.5]; % 天然气价格元/kWh按低热值折算后的单位能量价格 c_gas 0.35; % 设备参数 P_gt_max 200; % 燃气轮机最大功率kW P_gt_min 50; % 燃气轮机最小技术出力kW eta_gt 0.35; % 发电效率 eta_rec 0.75; % 余热回收效率 COP_ac 1.4; % 吸收式制冷机COP COP_ec 3.5; % 电制冷机COP SOC_min 0.2; SOC_max 0.9; SOC_init 0.5; P_ch_max 50; P_dis_max 50; eta_ch 0.95; eta_dis 0.95;这里我想多说一句负荷曲线的重要性。如果只是拍脑袋编数据优化结果再漂亮也是数字游戏。实际项目中电负荷、热负荷、冷负荷应该来自现场实测数据、历史典型日曲线或者预测模型。即使暂时没有数据也要用符合物理规律的曲线形状——比如电负荷白天两个峰、热负荷早晚高白天低、冷负荷中午最高——否则结果没有参考价值。3.2 目标函数与约束条件的代码落地用YALMIP定义决策变量时我推荐明确区分连续变量和0-1变量。连续变量用sdpvar定义0-1变量用binvar定义这样约束写的时候不容易混。P_gt sdpvar(1, T); u_gt binvar(1, T); P_grid_buy sdpvar(1, T); P_grid_sell sdpvar(1, T); u_grid binvar(1, T); P_ec sdpvar(1, T); Q_ec sdpvar(1, T); P_ch sdpvar(1, T); P_dis sdpvar(1, T); u_bat binvar(1, T); SOC sdpvar(1, T1);电制冷方面Q_ec P_ec * COP_ec 是一个线性关系直接写成约束或者直接在平衡方程里替换都可以。为了代码可读性我会保留Q_ec变量并在约束里加一行Constraints [Constraints, Q_ec COP_ec * P_ec];YALMIP写约束的时候用循环拼接比较清晰虽然矩阵化写法效率高但调试时很难定位到具体行。我在代码里通常用for循环逐时段添加约束性能开销可接受排错体验好得多。3.3 储能系统的建模与求解蓄电池是CCHP模型里最容易出错的点。递推方程、SOC范围、充放电功率上限、充放电互斥这几组约束必须同时出现。Constraints []; for t 1:T Constraints [Constraints, SOC(t1) SOC(t) (eta_ch * P_ch(t) - P_dis(t) / eta_dis) * dt]; Constraints [Constraints, SOC_min SOC(t1) SOC_max]; Constraints [Constraints, 0 P_ch(t) P_ch_max * u_bat(t)]; Constraints [Constraints, 0 P_dis(t) P_dis_max * (1 - u_bat(t))]; end Constraints [Constraints, SOC(1) SOC_init]; Constraints [Constraints, SOC(T1) SOC_init];最后一个SOC(T1) SOC_init非常关键。它要求经过一个完整调度周期后储能回到初始状态确保日前调度方案的每日可重复性。不加这条约束求解器可能会在夜里把蓄电池放空来降低成本而这样的方案在第二天是无法继续执行的。一次完整的求解代码配置如下Objective sum(c_gas * (P_gt / eta_gt) ... c_elec .* P_grid_buy ... - c_elec .* P_grid_sell); ops sdpsettings(solver, gurobi, verbose, 2); sol optimize(Constraints, Objective, ops); if sol.problem 0 disp(求解成功); else disp(求解失败); disp(sol.info); end目标函数里燃料消耗量我用P_gt / eta_gt来折算这是个简化但有效的表达方式。实际工程中如果要做更精细的建模可以把燃料消耗和发电功率的关系拟合成更准确的分段线性函数再用YALMIP的分段线性建模工具加入。3.4 结果后处理怎么把调度计划展示清楚求解完成后如果只打印一两个数字等于白干了。调度方案的可视化是程序交付的一个重要部分。我通常会画一组图电功率堆叠图、热功率图、冷功率图、SOC曲线、成本构成柱状图。figure; subplot(2,2,1); bar([value(P_gt); value(P_grid_buy); value(P_grid_sell); value(P_dis); -value(P_ch)], stacked); legend(燃气轮机, 购电, 售电, 放电, 充电); xlabel(时段); ylabel(电功率/kW); subplot(2,2,2); plot(1:T1, value(SOC), o-); xlabel(时段); ylabel(SOC); grid on;这种可视化便于快速核对结果是否符合物理直觉。比如SOC曲线应该是一条平滑的充放电曲线如果出现高频振荡多半是约束条件有漏洞。4. 常见问题与排查技巧实录4.1 求解不可行七成原因是约束冲突CCHP模型最常见的问题是求解器返回infeasible也就是找不到可行解。排查流程我按优先级列一下。第一步检查三条母线的能量平衡方程是否正确。电制冷机的输入是电、输出是冷这个跨母线连接最容易对错。热母线的平衡方程里吸收式制冷机的输入热量是一个消耗项不能漏掉。第二步用YALMIP自带的诊断工具看具体是哪条约束导致不可行。先用一个很基准的可行解比如所有设备全关、全部从电网买电测试系统本身是否存在一个明显可行的运行点。如果连这个都不可行说明约束之间存在硬冲突。第三步缩小时间尺度做单时段测试。只取T1或T2手动检查SOC递推关系确保起始和终止状态一致。只要递推关系写错一个小符号T24时误差就会累计到不可行。4.2 购售电同时发生的假象求解结果出现“一边从电网买电、一边向电网卖电”的现象肯定是约束缺失。前面提到过需要加上购售电状态互斥的0-1变量约束。同样的逻辑也适用于储能充放电。这个问题的本质是建模时没有把互斥决策写成数学约束。目标函数里购电成本为正、售电收益为负如果允许求解器自然希望买更多的电再卖出去制造无意义的套利。任何涉及双向功率交换的设备都需要一对互相排斥的0-1变量来保证状态唯一。4.3 求解器报错或求解缓慢的常见原因MATLAB内置的intlinprog处理整数变量时会变慢而CCHP模型中启停变量和状态变量叠加后24时段、3套设备往往超过70个整数变量内置求解器可能需要数秒甚至更久。换用Gurobi或CPLEX通常能缩短一个数量级。另一个技巧是尽量减少整数变量。比如如果电制冷机在运行策略中总是开机就不需要定义0-1状态直接用连续变量。对每台设备审查一遍“这个0-1变量真的必要吗”往往能显著减小问题规模。4.4 多目标优化的权重处理实际项目中除了经济成本常常还要兼顾碳排放或一次能源利用率。多目标优化我推荐两种做法。第一种是线性加权法。把成本和碳排放分别归一化然后加权求和。难点在于归一化系数的选择权重选得不好某个目标会直接支配另一个目标。我通常的做法是扫描权重从0到1重复求解得到一组Pareto前沿解再交给决策者选择。第二种是ε-约束法。把碳排放作为约束设定一个上限值然后在约束下最小化成本。这种方法在工程沟通中更好用因为决策者能直观理解“碳排放不超过某值时成本最低是多少”。4.5 数据质量问题对优化结果的影响这个问题容易被忽视但我遇到的最多。分时电价的峰谷时段设置错位、天然气价格单位不一致、负荷曲线明显偏离实际这些数据错误会在优化结果里被放大——最优解会刻意把高耗能设备安排在“看似便宜”但实际上数据错误的时段运行。建议在跑优化前先做一次数据可视化把电价曲线、负荷曲线、设备参数表打印出来人工核对一遍。数据检查的时间花得很值。5. 我的实操心得与后续扩展方向5.1 几个值得铭记的经验第一模型永远从最简版起步。先只做燃气轮机和电网购电把代码框架跑通确认没有语法错误、没有符号错误再加储能、加制冷设备、加售电机制、加碳排目标。每加一块优化结果和预期对比一次这样可以在一开始就发现新增模块是否引入逻辑冲突。上来直接写完整模型调试难度的乘积会爆炸式增加。第二YALMIP建模完成后我强烈建议再做一版用内置intlinprog实现的相同模型。两个版本相互验证可以确认不是求解器封装出的偏差。这还能帮助你更深入理解MILP的内在逻辑对后续调试受益匪浅。第三结果的合理性判断不能只看目标函数值。我每次跑完都会人工抽查几个时段看看设备是否有不合理的频繁启停、储能SOC是否有异常的突变。这些异常往往暴露了约束或参数的正确性问题。5.2 扩展方向这套模型的扩展能力很强。最直接的扩展是加入可再生能源比如光伏发电作为输入参数那光伏出力的不确定性就需要引入场景法或鲁棒优化。第二个方向是加需求响应把用户侧可时移负荷、可中断负荷作为虚拟资源和储能、备用机组一起参与调度。第三个方向是模型预测控制的日内滚动优化用实时数据修正日前计划的预测误差。第四个方向是在碳交易机制下把碳排放配额纳入目标函数这时候碳价和配额系数会成为新的关键参数。不管选哪个方向这套CCHP经济优化框架的建模思路、求解流程和排查方法都能直接复用。多能源系统优化的复杂性不在某一个单一设备而在于设备之间的耦合关系和约束条件的完备性——这条经验在几乎所有综合能源项目里都适用。
返回列表