
做能源系统优化的朋友应该都有体会“冷热电联供型微网”这个概念听起来很清晰——燃气轮机发电、余热回收制热制冷、多能互补——但真正要把一套含冰蓄冷空调的调度策略跑起来坑是一个接一个。这个项目的核心是两件事让电、热、冷三种能量在微网内部高效协同同时引入冰蓄冷空调作为既有冷负荷又带储能属性的柔性资源再用多时间尺度优化调度去对冲光伏出力和负荷预测的不确定性最后用Matlab代码把整个模型和求解过程落地。这篇文章我会把建模思路、多时间尺度的衔接逻辑、Yalmip求解的关键代码、以及实际调试中踩过的那些坑一次性拆开说清楚。这个项目适合这几类人看做微网和综合能源系统优化调度研究的学生和工程师、刚入手Yalmip工具箱想找完整练手案例的Matlab用户、以及正在做空调负荷管理或需求侧响应的从业者。看完你至少能明白一套完整的含冰蓄冷CCHP微网调度代码是怎么从零搭起来的哪些地方容易出错以及怎么用最少的时间把结果跑出来。1. 项目整体设计与思路拆解1.1 冷热电联供微网的能量流本质冷热电联供型微网本质上是一套小型的分布式能源系统。燃气轮机燃烧天然气发电产生的高温烟气经过余热回收装置一部分供给吸收式制冷机产生冷量一部分通过换热器供给热负荷。跟传统“电网买电电空调制冷燃气锅炉供热”的分供模式相比联供最大的优势是能源梯级利用高品位的热能先发电中低品位的余热再用来制冷和制热综合能源利用率能到80%以上。但联供微网也带来了一个天然的麻烦电、热、冷三条能量流是强耦合的。燃气轮机一旦发电烟气余热就跟着产生冷和热没法像电那样按需独立启停。尤其是冷负荷波动大的夏季如果只盯着电力平衡做调度很容易出现“电够了但冷不够”或者“为了供冷白白浪费了余热”的尴尬局面。这就是为什么冷热电联供微网的调度不能简单套用普通微网的模型。从数学角度看这个系统是一个多输入多输出的能量转换网络。输入侧是天然气、电网购电、光伏出力和风速输出侧是电负荷、热负荷和冷负荷中间经过燃气轮机、余热锅炉、吸收式制冷机、电制冷机、蓄冰槽、蓄电池等设备完成能量的时空转移。优化调度的任务就是在这套能量网络中找出一组设备出力组合让总运行成本最低同时满足所有供需平衡和设备物理约束。1.2 冰蓄冷空调给调度带来了什么冰蓄冷空调放在这个系统里本质上是给冷负荷装了一块“电池”。常规电制冷空调是即发即用制冷机耗电产生冷量冷负荷消耗冷量二者实时平衡。但冰蓄冷系统多了一个蓄冰槽夜间电价低谷时电制冷机满负荷制冰把冷量以冰的形式储存起来白天电价高峰时制冷机可以减少甚至停止运行靠融冰释放冷量来满足部分冷负荷。从电网角度看这是经典的削峰填谷从微网调度角度看这等于把刚性冷负荷变成了柔性可调资源。调度员可以在电价低谷、光伏大发、余热充足的时候多制冰存起来在电价高峰、冷负荷尖峰的时候多融冰释放灵活度一下提升了一个档次。不过这个“电池”跟蓄电池有一个本质区别蓄电池的充电功率和放电功率可以独立控制而蓄冰槽的传热特性、冰水温度、融冰速率都受物理条件限制建模的时候必须处理好制冰工况和融冰工况的切换。这也是这个项目相比普通CCHP调度最特殊的地方——冷侧多了一个带时间耦合的状态量蓄冰槽什么时候制冰、什么时候融冰、融多少都要跟电侧和热侧的调度一起协同优化。1.3 多时间尺度调度到底在解决什么问题只做一套日前24小时计划够不够不够。原因很现实光伏出力预测、冷热电负荷预测都不可能做到完全准确。尤其是中午光伏大发时段如果日前计划是固定不变的实际运行大概率会偏离最优状态甚至出现供需失衡。多时间尺度调度的核心思路就是把调度决策拆成两层日前调度提前24小时制定时间尺度为1小时确定燃气轮机启停、冰蓄冷蓄放冰策略等需要提前安排的大决策。这类决策调整代价高比如燃气轮机启停不能频繁变化蓄冰槽的冰也不能白天临时决定“今天不蓄了”。日内调度在运行当天滚动执行时间尺度缩短到15分钟或1小时基于最新的光伏和负荷预测信息修正各可控设备的出力计划。当预测跟实际偏差较大时只需要在日前计划基础上做局部调整不需要推倒重来。这种“全局粗调局部精调”的框架是应对不确定性的主流方案。这个项目把日前和日内两级调度同时建出来用同一套Matlab模型跑通这也是我觉得最值得复现的地方。2. 系统建模设备模型与约束拆解2.1 供能设备模型建模的第一步是把每一个设备的输入输出关系转化成数学约束。这里以最常见的CCHP系统配置为例包含燃气轮机、余热回收装置、吸收式制冷机、电制冷机、蓄冰槽和蓄电池。燃气轮机模型。核心变量是发电功率 (P_{GT}(t)) 和消耗的天然气燃料成本。燃气轮机的发电效率随负载率变化精确建模用非线性曲线但调度模型为了可解性通常采用分段线性化。简化模型可以写成[ F_{GT}(t) \frac{P_{GT}(t)}{\eta_{GT}} \cdot \frac{1}{LHV_{gas}} ]其中 (F_{GT}(t)) 是天然气消耗量(\eta_{GT}) 是发电效率(LHV_{gas}) 是天然气低位热值。烟气余热回收量 (Q_{rec}(t)) 近似为[ Q_{rec}(t) P_{GT}(t) \cdot \frac{1-\eta_{GT}}{\eta_{GT}} \cdot \eta_{rec} ]这里注意余热回收量不是独立的决策变量它跟发电功率一一绑定。这意味着燃气轮机一旦多发电余热就多制冷制热侧也跟着变。建模时这个耦合关系必须写进约束不能把电功率和热功率当独立变量分别优化。吸收式制冷机。利用余热产生冷量模型最简单输入余热、输出冷量中间乘一个制冷系数 (COP_{ac})[ Q_{ac,out}(t) Q_{ac,in}(t) \cdot COP_{ac} ]但要注意吸收式制冷机的出力有上下限而且如果余热回收量不够它没法凭空制冷。电制冷机。耗电产冷同样有COP[ Q_{ec,out}(t) P_{ec}(t) \cdot COP_{ec} ]在含冰蓄冷的系统里电制冷机通常有双工况常规制冷工况和制冰工况。两种工况的COP不一样建模时要分开处理。制冰工况下电制冷机的输出不再直接进入冷负荷而是转化为蓄冰槽的蓄冰量。2.2 冰蓄冷空调最容易写错的地方冰蓄冷系统建模的难点在于蓄冰槽的状态约束。蓄冰槽引入了一个状态变量 (SOC(t))表示t时刻蓄冰槽的蓄冰量占比取值范围[0,1]。状态转移方程是[ SOC(t1) SOC(t) - \frac{Q_{ice,out}(t)}{CAP_{ice}} \frac{P_{ice,in}(t) \cdot COP_{ice}}{CAP_{ice}} ]这里面有几个容易出错的细节制冰和融冰不能同时进行。这在数学上必须用0-1整数变量控制写成 [ u_{ice}(t) v_{ice}(t) \leq 1 ] 其中 (u_{ice}(t)1) 表示制冰(v_{ice}(t)1) 表示融冰。如果不加这个约束优化器极有可能在同一个时刻既制冰又融冰——净效果相同但白白耗电这种错误很隐蔽不仔细看结果根本发现不了。制冷机在制冰工况下工作冷量不直接供给冷负荷。所以冷功率平衡方程里电制冷机的制冷出力要区分“供冷模式”和“制冰模式”。融冰放冷速率有上限这个上限通常正比于当前蓄冰量 [ Q_{ice,out}(t) \leq k_{melt} \cdot SOC(t) \cdot CAP_{ice} ] 意思是冰越少能融出来的速率越低物理上很好理解。如果不加这个约束优化器会在需要冷量时一次性把所有冰全放出来这在工程上做不到。蓄冰槽的跨日清零约束。调度周期结束时蓄冰槽SOC必须回到初始值通常是0或某个设定值否则第二天的计划就失真了。2.3 目标函数与三条能量平衡约束目标函数设定为系统总运行成本最小[ \min \sum_{t1}^{T} \left[ C_{gas}(t) C_{buy}(t) \cdot P_{grid}(t) C_{om}(t) C_{curtail}(t) \right] ]其中 (C_{gas}) 是燃气轮机燃料成本(C_{buy}) 是购电价(C_{om}) 是设备运行维护成本(C_{curtail}) 是弃风弃光惩罚项。光伏和风电的边际成本为零所以如果约束里没有惩罚项优化器不会主动弃掉它们但实际中受线路容量和功率平衡限制弃风弃光还是会发生惩罚项能让模型在“必要的弃”和“不必要的弃”之间做出合理取舍。三条能量平衡方程是约束的核心电功率平衡 [ P_{GT}(t) P_{grid}(t) P_{pv}(t) P_{wt}(t) P_{load}(t) P_{ec}(t) P_{ice,in}(t) P_{bat,ch}(t) ] 注意左边是四个电源右边除了电负荷还要加上电制冷机耗电、制冰耗电和蓄电池充电。热功率平衡 [ Q_{rec,heat}(t) Q_{boiler}(t) Q_{heat,load}(t) ] 如果余热回收的热量不够燃气锅炉补充如果有多余也可以在模型里加入热储能消纳。冷功率平衡 [ Q_{ac,out}(t) Q_{ec,cool}(t) Q_{ice,out}(t) Q_{cool,load}(t) ] 这是含冰蓄冷系统的核心平衡式冷负荷由吸收式制冷、电制冷机供冷和融冰供冷三者共同承担。这三条平衡方程看起来简单但每条都牵扯到多个设备的耦合变量。实际建模时我最常犯的错误就是写电平衡时忘记把制冰耗电加上结果系统在夜间显示“功率平衡”实际上电量对不上。3. 多时间尺度调度机制与Matlab实现3.1 日前与日内的衔接逻辑这个项目用的多时间尺度框架具体衔接方式如下日前调度以1小时为间隔共24个时段。已知量是光伏、风电和冷热电负荷的日前预测曲线决策量是燃气轮机各时段出力、电网购电计划、电制冷机和吸收式制冷机的出力分配、蓄冰槽各时段的蓄放冰计划。求解完成后得到一组日前计划其中最关键的是燃气轮机的出力基线和蓄冰槽的SOC变化轨迹。日内调度以15分钟为间隔采用滚动优化窗口。假设预测时域为4小时即16个15分钟时段每15分钟滚动一次。日内调度的已知量是更新后的超短期预测决策量是各可控设备的出力修正量。重点来了——日内调度不会推翻日前决策而是在日前计划基础上做局部修正。比如日前计划里燃气轮机10点出力500kW日内调度发现光伏实际出力比预期多了100kW那么就在约束中加入[ P_{GT}(t) P_{GT}^{DA}(t) \Delta P_{GT}(t) ]其中 (\Delta P_{GT}(t)) 是日内修正量范围比日前出力范围小得多。这个修正量通常还要加一个调整速率限制防止燃气轮机出力在短时间内剧烈波动。在Matlab里实现两级调度最简单的方式是把求解函数封装成一个带参数的函数function result solve_schedule(pred_data, type, prev_plan) % type DA 日前调度 % type ID 日内调度prev_plan 是日前计划基线 ... end日前调度调用一次拿到全天的计划日内调度在一个循环里反复调用每次输入最新的预测数据和上次的计划然后把第一个时段的决策结果作为实际执行值滚动向前。3.2 Yalmip建模的核心代码段Matlab里的优化建模我推荐使用Yalmip工具箱它最大的好处是语法接近数学表达式模型改起来非常直观。配合CPLEX或Gurobi求解混合整数线性规划MILP速度和稳定性都有保障。第一次跑这个项目的人我建议先装Yalmip再装一个求解器这两个装好剩下就是写代码的事。变量定义部分的代码大致长这样% 决策变量 P_GT sdpvar(1, 24); % 燃气轮机发电功率 P_grid sdpvar(1, 24); % 电网购电功率 P_ec sdpvar(1, 24); % 电制冷机功率 Q_ac sdpvar(1, 24); % 吸收式制冷输出 Q_ice_out sdpvar(1, 24); % 融冰供冷功率 SOC sdpvar(1, 25); % 蓄冰槽蓄冰状态多一个末端时段 u_ice binvar(1, 24); % 制冰状态标志 v_ice binvar(1, 24); % 融冰状态标志约束构建的核心代码段Constraints []; % 电功率平衡 Constraints [Constraints, P_GT P_grid P_pv_pred P_wt_pred ... P_load P_ec P_ice_rate * u_ice]; % 冷功率平衡 Constraints [Constraints, Q_ac P_ec * COP_ec Q_ice_out ... Q_cool_load]; % 冰蓄冷SOC状态转移 Constraints [Constraints, SOC(t1) SOC(t) ... - Q_ice_out(t) / CAP_ice ... P_ice_rate * COP_ice * u_ice(t) / CAP_ice]; % 制冰与融冰互斥 Constraints [Constraints, u_ice(t) v_ice(t) 1]; % 融冰速率受当前蓄冰量限制 Constraints [Constraints, Q_ice_out(t) k_melt * SOC(t) * CAP_ice]; % 周期末SOC回到初始值 Constraints [Constraints, SOC(25) SOC(1)];这里有一个我自己踩过的坑SOC变量的维度是1×25但蓄放冰约束是按t1到24写的如果索引不一致Matlab会报维度错误。解决办法是在循环里统一用t做索引并且把SOC(t1)关联到SOC(t)这样逻辑最清晰。目标函数代码Objective sum(C_gas * F_GT) sum(C_buy * P_grid) ... sum(C_om_ec * P_ec) sum(C_curtail * P_curtail); Ops sdpsettings(solver, cplex, verbose, 1); optimize(Constraints, Objective, Ops);3.3 结果分析与可视化看什么模型跑完之后最容易忽略的是结果分析这一环。代码能跑通不等于模型正确我拿到结果的第一反应永远是先画出四张图电功率平衡图看四条电源曲线和负荷曲线的加和是否吻合重点检查制冰耗电有没有占掉一块空白。热功率平衡图看余热回收和锅炉供热的分配比例。冷功率平衡图看电制冷、吸收式制冷和融冰供冷三条曲线如何叠加覆盖冷负荷。蓄冰槽SOC曲线看蓄冰时段是不是落在电价谷段融冰时段是不是集中在电价峰段。如果SOC曲线在峰段反而上升、谷段反而下降那说明目标函数或约束有方向性错误。Matlab里画图的代码很简单figure; stairs(1:24, P_GT, LineWidth, 1.5); hold on; stairs(1:24, P_grid, LineWidth, 1.5); stairs(1:24, P_pv_pred P_wt_pred, LineWidth, 1.5); stairs(1:24, P_load, --k, LineWidth, 1.2); legend(燃气轮机, 电网购电, 可再生电源, 电负荷); xlabel(时间/h); ylabel(功率/kW);跑通第一个版本后建议做一个“对照实验”把冰蓄冷空调的蓄放冰约束去掉即电制冷机只能实时供冷重新求解对比两组结果的经济性和冷负荷供给方式。这个对照实验能非常直观地说明冰蓄冷的价值——你会看到蓄冰工况下总成本下降、峰段电制冷出力明显降低。这也是论文里最常用的灵敏度分析思路。4. 调试经验盘点常见问题与解决实录4.1 求解器报无解第一步排查哪里模型写好后第一次跑最容易碰到的问题就是infeasible。这时候不要慌按顺序排查先查功率平衡约束。我碰到过最多次的情况是电平衡里漏加了制冰耗电项导致夜间时段系统“凭空”多了一部分电量。排查方法很简单把约束逐个注释掉看到哪条约束被注释后模型变可行问题就出在那条约束上。再查蓄冰槽边界。SOC初始值设为0.5半槽冰但结束约束又要求SOC回到0.5这没问题但如果初始值设置为0某天冷负荷特别大导致夜间根本蓄不满冰SOC末值达不到0就会无解。解决办法是把跨日SOC约束改成不等式允许少量偏差并加上惩罚或者给蓄冰槽容量留出足够裕量。最后查负荷数据的单位。这个问题非常基础但经常发生——热负荷习惯用kW冷负荷习惯用kW或RT冷吨如果不统一转换系数功率平衡完全错位。1冷吨约等于3.517kW这个换算在代码里一定要统一。4.2 SOC曲线不合理的背后原因有时候模型能求解但结果里的SOC曲线不对劲——比如白天电价高峰期SOC还在上升。如果白天电价高峰期SOC还在上升第一反应是检查电价序列看是不是数据录入时把谷时段和峰时段标反了。排除了电价问题就要查蓄冰槽的功率上限约束看看是不是给得太宽导致即使电价不是最低的时候制冷机制冰也依然经济。还有一种情况是热侧和冷侧的耦合出了问题吸收式制冷机出力可能受余热约束不足电制冷机被迫在白天出力如果此时电价已经很高优化器宁愿用更便宜的谷电制冰结果SOC在白天继续上升这说明冷负荷平衡其实已经失衡。这时候要回头检查冷负荷的数据曲线是否合理。4.3 求解耗时过长与线性化技巧MILP问题一旦时段数变多、整数变量变多求解耗时就会指数增长。这个项目里整数变量主要来自制冰融冰状态0-1变量和蓄电池充放电状态变量。解决思路有三个第一减少整数变量。如果工程上允许蓄冰槽的制冰功率可以按固定速率处理用一个功率档位描述这样整数变量只有“制冰/不制冰”两个状态而不是连续的多个功率档位。第二如果只需要粗略估算可以先把SOC的连续模型跑一遍用最优解作为整数规划的初始可行解再传给MILP求解器。Gurobi和CPLEX都支持通过MIP start提供初始解能显著加速求解。第三把求解时间限制放开的同时设置一个合理的MIP gap。工程上MIP gap控制在1%以内就已经很可靠了不必追求到0.01%。设太大结果不可信设太小耗时指数上升1%是经验和学术之间比较平衡的点。4.4 常见问题速查表现象最可能的原因排查与处理办法求解器报infeasible功率平衡漏项、SOC边界设定不合理逐条注释约束定位检查跨日SOC约束SOC曲线白天还在上升电价序列错位、冷负荷数据异常核对峰谷电价时段检查吸收式制冷余热输入是否受限求解耗时超过10分钟整数变量过多、约束过于密集合并制冰功率档位设置MIP gap提供初始解结果出现制冰融冰同时进行缺少互斥约束增加 uv1 约束冰蓄冷系统不产生任何经济效益峰谷电价差太小、蓄冰槽容量设置过大检查电价差调整蓄冰槽容量或冷负荷峰值日内调度频繁触发启停日前计划约束太紧日内修正范围太小放宽日内修正量的上下限增加修正速率限制5. 项目可以怎么进一步扩展这个模型跑通之后往上扩展的方向其实很多。第一个直接的想法是加入蓄电池跟冰蓄冷形成“电储能冷储能”的混合储能系统此时电平衡方程里多一项充放电功率蓄电池的SOC约束跟蓄冰槽SOC约束结构几乎一模一样代码复用的成本很低。第二个方向是把碳交易机制考虑进去。碳排放配额、碳价、燃气轮机和电网购电的碳排放系数相乘目标函数里加一项碳成本项这样调度结果会明显偏向低碳运行也更有政策研究价值。第三个方向是引入需求响应。跟冰蓄冷同源的思路冷负荷中有一部分是柔性负荷比如空调温度设定可以上下浮动通过价格激励让用户调整负荷曲线本质上跟冰蓄冷是互补的——冰蓄冷是供给侧平移冷量需求响应是主动调整冷负荷。把这两个放一起冷侧的灵活性就更完整了。另外如果要追求更贴近实际可以考虑把日前调度里的燃气轮机启停状态也建模成整数变量这样日前决策里多了一组更贴合工程实际的0-1变量。代价是模型规模和求解时间都会上一个台阶但这也正是多时间尺度调度研究里最常遇到的取舍问题。6. 最后再说几句代码之外的体会我在实际跑这个项目的过程中最深的一个感受是模型的技术细节固然重要但真正让项目走通的关键环节其实是“数据的可信度”和“结果的解释性”。虚拟数据建模时一切都很完美光伏曲线光滑得像学校教科书冷负荷峰值整整齐齐。但只要换成实际抄表的负荷数据各种问题就暴露出来了数据的采样间隔不一致、个别时段有缺失值或噪声尖峰、单位在不同来源的表格里不统一。这些数据问题如果不在预处理阶段解决后面调再多的约束也没有意义。所以如果你要用这个项目第一周先别急着写代码把数据集对齐、清洗、插值好这一步做扎实了后面所有工作都会顺很多。另一方面结果算出来后不要只看总成本数字。我习惯了每次跑完先追问几个问题蓄冰槽在什么时候蓄的冰融冰供冷占了冷负荷多大比例燃气轮机的出力曲线跟电价的波峰波谷对应关系是否合理。如果这些问题的答案都能用一句话解释清楚那说明你的模型是“有灵魂”的如果解释不通哪怕最优解数值没问题也一定有哪些约束或参数需要重新审视。这套代码跑通之后回过头来再看“冷热电联供型微网”这个标题你会发现它其实是一个容器里面装的不只是蓄冰槽的方程和优化器的求解记录更重要的是你从物理理解到数学建模再到代码落地的完整链路。把这个链路走完遇到其他类型微网项目时你大概率也能很快上手。