“含冰蓄冷空调的冷热电联供型微网多时间尺度优化调度”这个标题我第一次接手的时候心里其实挺没底的。因为这几个关键词单拆出来每一个都能单独撑起一篇论文合在一起又是一个非常典型的“系统级优化”问题。但它真正要解决的事情落到底层其实非常接地气一个园区或者建筑群同时有电负荷、热负荷、冷负荷既有燃气轮机这种热电联产设备又有冰蓄冷这种冷量存储装置怎么在满足所有负荷的前提下让一天下来运营成本最低。更麻烦的是光伏和风电这类可再生能源出力不稳定预测又不可能百分之百准确怎么通过时间尺度上的层层递进来消化这些不确定性才是这个项目的核心命题。这篇文章我打算把整个项目的建模思路、数学表达、Matlab实现路线和实战中容易踩的坑完整串一遍适合正在做微网调度、综合能源系统优化或者准备用YALMIP上手混合整数规划的同行参考。1. 项目定位与整体思路拆解1.1 这个项目到底在优化什么很多刚接触这个课题的人容易把注意力全部放在设备建模上觉得只要把燃气轮机、吸收式制冷机、冰蓄冷槽的数学模型写出来问题就解决了一半。但实际上优化调度的核心从来不是“设备模型有多精确”而是“目标函数和约束条件能不能真实反映运行逻辑”。这个项目的优化目标非常明确在满足电、热、冷三类负荷的前提下最小化整个微网在调度周期内的总运行成本。成本项包括燃气轮机的燃料费、补燃锅炉的燃料费、从主网购电的费用同时如果微网有富余电量可以上网还要考虑售电收益。如果模型里引入了储能设备和可再生机组通常还会加上储能充放损耗折算、弃风弃光的惩罚项。决策变量也分层次连续变量是各设备的出力功率、蓄冰槽的蓄冰量和融冰供冷量、从电网的购电功率整数变量则是设备的启停状态比如燃气轮机要不要开机、补燃锅炉处在什么工况、冰蓄冷机组是制冰模式还是制冷模式。这种连续变量加整数变量的组合决定了最终的求解问题是混合整数线性规划或者混合整数二次规划在Matlab环境下最常见的做法就是用YALMIP工具箱建模再交给CPLEX或Gurobi这类商业求解器去解。1.2 为什么冷热电联供加冰蓄冷值得专门做一版调度如果不是亲身经历过这个项目我可能也会觉得“这不就是个普通的设备组合嘛”。但实际算一遍就会发现冷热电联供系统和冰蓄冷空调之间存在强烈的互补关系而这种互补恰恰是优化调度价值的最大来源。燃气轮机发电之后会产生大量高温烟气这部分余热如果不回收就直接白扔到空气里了。通过余热锅炉回收烟气热量可以产生蒸汽或热水来驱动吸收式制冷机供冷或者通过换热器供热。也就是说燃气轮机发一度电除了电能本身还附带了一部分可以免费利用的冷或热。这种“电带动热、热带动冷”的梯级利用逻辑是冷热电联供系统能比传统分供系统省钱的本质原因。而冰蓄冷空调解决的是另一个问题电价的峰谷差。吸收式制冷机虽好但它的出力调节范围有限响应速度也不快。当冷负荷突然冲高或者光伏出力骤降的时候纯靠吸收式和电制冷机硬扛很容易造成配电网的峰值负荷过高。冰蓄冷空调的核心逻辑是“削峰填谷”——夜间低谷电价时段开电机制冰把冷量以冰的形式储存起来白天高峰时段融冰供冷避开高电价。这样一来空调系统的电耗结构就被彻底改写了。放在同一个微网里这两类设备还要跟燃气轮机、光伏、储能一起竞争同一个投资和运行预算。谁在哪个时刻出力、出多少力本质是一个联合优化问题不把成本、效率、约束放在同一个数学框架里算光靠经验调度很难拿到最优解。这也是这个项目值得做详细建模和代码实现的原因。2. 冷热电联供微网的设备建模与能量流2.1 设备拓扑与能量流梳理设备模型写之前先把整个系统的能流拓扑理清楚。我按照一个典型园区微网的配置来搭建模型主要设备包括微型燃气轮机、余热锅炉、补燃锅炉、蒸汽型吸收式制冷机、电制冷机、冰蓄冷空调系统以及光伏和风电两类可再生机组微网通过联络线与主网连接允许购电和售电。能量流方向是这样的天然气进入燃气轮机燃烧发电电流入母线分别供给电负荷、电制冷机、冰蓄冷机组的压缩机和各类辅助设备燃气轮机排出的高温烟气进入余热锅炉产生蒸汽蒸汽分成三路一路驱动吸收式制冷机制冷一路直接参与供热一路进入补燃锅炉再提温提压。电制冷机和冰蓄冷空调直接消耗电能来制取冷量蓄冰槽在电价低谷时段蓄冰高峰时段融冰供冷。这里要特别提醒一点很多初版模型容易把冷负荷和热负荷混在一起处理觉得“反正都是温度需求”。但实际建筑用能里冬季的热负荷和夏季的冷负荷峰值时间是错开的同一个设备在同一个时段不可能既充分供冷又充分供热所以建模时一定要把冷、热、电三条母线分开各自做功率平衡再通过设备模型把三条母线耦合起来。2.2 燃气轮机与余热回收建模燃气轮机的建模不能只看发电效率。它最重要的特性是“发电和产热同时发生”而且发电功率直接影响烟气余热量这给约束条件带来了耦合性。燃气轮机的发电功率记为燃料消耗量用表示发电效率和输出功率之间的关系通常用分段线性函数逼近。在简化模型中燃料成本直接表达为如果考虑启停还需要引入0-1变量表示机组运行状态配合爬坡约束限制相邻时段发电功率的变化幅度余热锅炉回收的烟气热量受燃气轮机发电功率的影响发电功率越高烟气温度和流量越大可回收的余热量一般也越多。工程上通常表示为这里要注意是一个关于输出功率的分段函数在电网调度模型中常用线性近似但必须校验近似误差。我做过一次测试如果忽略余热量随发电功率的非线性关系燃气轮机出力在中低负荷段的调度结果会出现明显偏差冷负荷高峰期会让吸收式制冷机供冷不足最后只能靠电制冷机高价补冷整体成本反而升高。2.3 冷热电负荷平衡约束能量平衡约束是模型里最直观、也最容易出错的部分。电功率平衡、热功率平衡、冷功率平衡必须独立成行同时它们之间通过设备模型彼此关联。电功率平衡简单一些总发电加购电量等于总用电量。这里“总用电量”包含电负荷、电制冷机的耗电、冰蓄冷机组压缩机的耗电以及微网自身的辅助电耗。表达式可以写成热功率平衡则是余热锅炉产生的热量加上补燃锅炉燃料直接转化的热量经过换热器后满足热负荷。冷功率平衡是这个项目最关键的约束因为吸收式制冷、电制冷、融冰供冷三者共同承担冷负荷这三条平衡约束意味着任何一个设备的出力变化都会通过母线平衡影响到其他所有设备的运行点。我在调试模型时发现新手最容易犯的错误是只给电制冷机画了模型却忘了它的电耗要回到电平衡方程里或者只写了融冰供冷的能力却忘记了蓄冰槽的状态更新方程。每一条能量流都必须有明确来源和去向否则解出来一定是“看似最优、实则物理不成立”的结果。3. 冰蓄冷空调与冷量时间平移的建模要点3.1 冰蓄冷空调的运行机理与经济价值冰蓄冷空调并不是什么黑科技它的本质是把制冷过程搬到一个更合适的时间段来完成。晚上电价低的时候用电驱动压缩机制冰把冷量以冰的潜热形式储存在蓄冰槽里白天电价高、冷负荷也高的时候停止或者减少制冷机的运行直接让冰融化来提供冷量。为什么要用冰来存冷而不是直接用电储能因为电储能设备单位容量的造价远高于蓄冰槽而且空调负荷的冷量需求往往以小时级的“大块头”形式出现电化学储能很难扛住这种短时高功率冲击。冰蓄冷相当于把制冷负荷切掉了一整块直接减少了高峰时段的电力需求对配电网容量配置的影响非常可观。我做过一个简单的经济性估算供参考某地峰段电价按1.0元/kWh、谷段电价按0.3元/kWh计算一台额定制冷量500kW的电制冷机EER取4.0如果白天高峰时段直接制冷产生4000kWh冷量需要耗电1000kWh电费1000元。改用冰蓄冷夜间蓄冰工况下制冷机组COP约为2.8存下同样的4000kWh冷量需要耗电约1429kWh电费只有429元。即使算上蓄冰槽的散热损失和运维成本单日节省的电费也非常可观。这就是峰谷电价差驱动下最典型的削峰填谷套利逻辑。3.2 蓄冰槽的动态数学模型蓄冰槽在模型里的地位相当于电储能里的电池它的核心动态方程是蓄冰量的状态转移。用表示时段t的蓄冰量单位是kWh制冰功率和融冰供冷功率分别用和表示则状态更新为其中是蓄冰槽自损耗系数归入了融冰不彻底和保温损失工程上一般取3%到7%之间。需要强调的是这个系数看起来很小但在连续96个时点的滚动计算里它会导致蓄冰量逐渐偏离真实值如果模型里没有校准日内滚动调度到下午时段融冰功率会莫名其妙偏小。蓄冰槽的运行约束包括容量上限、蓄冰速率上限和融冰速率上限而且同一个时段不允许同时制冰和融冰这个逻辑用线性约束不好表达常规做法是引入两个0-1变量区分工况除此外蓄冰槽还有一个非常重要的周期约束。因为调度是按天滚动进行的为了保证第二天还能正常使用当天结束时的蓄冰量必须回到初始值或者至少不小于某个下限。这个约束我刚开始建模的时候漏掉了结果解出来白天的融冰量特别大把所有冰都用光了第二天开机直接没有冷源可用整个调度计划在次日失效。周期约束的形式很简单但它是保证方案可持续性的关键。3.3 蓄冰策略在微网中的角色在冷热电联供微网里冰蓄冷的角色不再是单纯的“削峰填谷”它还是一个非常灵活的可调资源。燃气轮机的发电功率和电负荷并不是完全同步的当可再生出力波动导致系统需要调整时冰蓄冷机组可以通过改变制冰功率或融冰速率来快速吸收或释放冷量相当于给冷母线配了一个缓冲池。我在实际项目里处理的某园区场景中夏季冷负荷峰值出现在下午2点前后而光伏出力峰值在中午12点左右如果只是单纯让吸收式制冷机跟着冷负荷走中午光伏大发时段就要白白弃掉一部分电量。把冰蓄冷引入后中午时段用光伏电力驱动冰蓄冷机组制冰下午峰值时段融冰供冷可再生能源的消纳率提升了近10%。这种“跨时段平移可再生能源电量”的能力是普通电制冷机不具备的也是优化调度模型里最能体现增值的地方。4. 多时间尺度优化调度框架4.1 为什么单一时间尺度不够用如果风光预测完全准确一天做一次日前调度就够了。但现实中的可再生能源预测误差在小时级尺度上经常达到20%以上冷热负荷预测也受天气、人流、设备状态影响很难做到始终精准。日前计划给定了各设备的大致出力和启停计划但这些计划在几小时之后很可能已经偏离实际最优。多时间尺度的本质是分层递进。先在前一天制定未来24小时的计划这是“粗调”确定设备启停和大致出力然后在实际运行当天每隔15到30分钟滚动一次短期计划基于最新的负荷实测和超短期预测修正各设备的出力这是“精调”。如果系统要求更高还可以在最底层加一个分钟级或秒级的实时控制层但通常已经不在优化调度的范畴里了。多时间尺度框架的关键点是每一层调度必须保持决策变量的一致性。日前调度定了燃气轮机的启停日内滚动时一般不允许翻转启停状态能调的只有出力水平否则设备频繁启停不仅成本高还会影响实际可行性。4.2 日前计划与日内滚动修正的分工日前调度在整个框架里承担的是“结构性决策”角色。调度周期24小时时间分辨率我采用1小时在每天凌晨根据次日预测数据求解一次得到的结果包括各机组24小时的启停状态、各时段的大概出力水平、蓄冰槽的蓄放冰计划以及与电网的交易电量计划。由于提前了十几个小时这层计划面对的主要是系统的基本运行框架对预测精度要求不高但对模型稳定性和计算速度要求很高。日内滚动调度承担的是“修正性决策”角色。每次滚动一般取未来4小时的窗口时间分辨率缩短到15分钟每15分钟重新求解一次。滚动窗口里的预测数据比日前精度高很多所以日内层可以对各设备出力做精细调整。比如光伏中午突然被云遮了预测功率从400kW跌到250kW日内调度就会立刻提高燃气轮机出力、增加购电量并适当降低电制冷机功率改用蓄冰槽融冰供冷来补充冷负荷缺口。两个时间尺度之间存在约束衔接日内滚动调度的初值必须对齐日前计划的当前值蓄冰槽的当前蓄冰量、燃气轮机的当前出力都作为日内层的初始条件参与优化。这里在代码实现上其实就是把状态变量当作已知参数传入同时把日前计划的启停变量固定住。4.3 滚动窗口的边界处理滚动优化的边界处理是很多初版代码容易出问题的地方。第一个边界是窗口末尾的蓄冰量如果在窗口末尾把蓄冰量压到零下一次滚动的时候就无冰可用所以一般要加一个终端约束让窗口末尾蓄冰量不低于某个合理值这个值通常由下一次滚动的预测冷负荷决定。第二个边界是购电功率的平滑性。日内滚动每15分钟执行一次如果两次滚动之间购电功率跳变过大会对配电网造成冲击所以在模型里要加购电爬坡约束限制相邻两次滚动购电参考值的变化幅度。实际操作中很多人直接沿用日前计划的购电功率作为基准日内只修正设备内部的功率分配这样既保证了对电网的友好性又留出了内部优化的自由度。5. Matlab代码实现路线5.1 工具箱选型与求解器配置Matlab环境下做混合整数线性规划我推荐直接用YALMIP工具箱它本质是一个建模层底层的求解器可以自由切换。项目里我用的组合是MATLAB YALMIP CPLEX。如果CPLEX的许可证不好搞也可以换成Gurobi对于这种规模在几千个变量、几百个整数变量的问题两个求解器性能差别不大。YALMIP的语法对熟悉Matlab的人非常友好定义sdpvar是连续变量定义binvar或者integer是0-1整数变量约束条件用方括号拼接目标函数直接写线性表达式最后一句optimize就完成了求解。可以提前预设求解器的参数比如设置最大求解时间、MIP gap、输出求解日志方便在调试时观察求解过程。5.2 决策变量与目标函数的代码实现决策变量的定义直接对应模型设计。以日前调度为例时间粒度设为1小时全天24个时段设备数量包括燃气轮机、补燃锅炉、吸收式制冷机、电制冷机、冰蓄冷机组、光伏、风电机组再加上与电网的交易变量总量不大在YALMIP里也就是几行代码的事。目标函数的写法是把所有成本项线性叠加燃气轮机和补燃锅炉的燃料成本与出力相关购电成本按分时电价计算如果允许售电则在目标函数里减掉售电收益。代码里每一行最好加上注释标明对应的是哪条公式的哪一项项目后期改模型的时候这种注释能省下大量时间。5.3 核心约束的YALMIP写法约束条件在YALMIP里其实比在纸面公式上更好处理因为矩阵化非常直观。功率平衡约束相当于把所有设备的变量加起来等于负荷值蓄冰槽的状态更新方程是跨时段的递推写循环嵌套就行。燃气轮机的启停逻辑要引入二元变量和连续出力变量的绑定。比如某时段机组关了出力必须为零开着才能出力。这个约束用两条不等式实现还要配合最小运行时间和最小停机时间的约束这个通常用状态变量的历史窗口约束来实现虽然会增加不少整数变量但对实际工程来说非常必要。冰蓄冷的同时制冰和融冰互斥约束也类似。整套代码写下来最核心的部分是一段数据输入脚本把所有设备参数、负荷曲线、预测数据、电价数据读入工作区然后调用求解函数。我习惯把模型写成独立函数输入是各类数据和参数输出是最优决策变量和目标函数值这样无论改参数还是改场景主脚本都不用动。5.4 日内滚动调度主循环逻辑日内滚动调度在代码实现上和日前调度的最大区别是外层多了一个滚动循环。主脚本按时间步推进每个步依次做三件事更新当前时刻的实测数据和超短期预测数据把上一时刻的状态变量传入当前优化模型调用YALMIP求解当前窗口的优化问题取第一个时段的决策结果下发执行。执行之后系统把该时段的实际状态反馈回来进入下一个时刻。这里有一个实现细节很重要每个窗口求解出来的结果只有第一个时段是真正下发执行的后面几个时段的结果只是预览到下一个滚动时刻会被新数据覆盖。这种“边走边算”的模式才是滚动调度的正确实现方式不是把每个窗口的结果全部存下来当作最终计划。滚动窗口的长度也会影响效果。窗口太短设备来不及调整调度优势体现不出来窗口太长预测误差又会被放大。我这边的经验是取4小时比较合适既能覆盖一个完整的负荷变化过程又不会让超短期预测误差积累得太大。6. 常见问题与排错技巧实录6.1 求解器报不可行怎么排查MILP模型报不可行几乎每个做过优化调度的人都遇见过而且第一反应基本都是怀疑求解器出了问题。实际上绝大多数不可行都是约束自相矛盾导致的排查顺序非常固定。第一步把目标函数改成常数0只求一个可行解。如果仍然不可行说明约束本身有冲突这时候就可以用YALMIP的check功能逐条查看约束残差哪个约束不满足就锁定哪条。第二步给能量平衡约束加一个松弛变量把严格的等式约束改成软约束加上足够大的惩罚系数重新求解。如果加松弛以后能解出来说明大部分矛盾集中在这个平衡约束上如果解出来的松弛变量很大说明设备容量根本无法满足该时段的负荷需求。另外蓄冰槽的周期约束和状态更新方程也是矛盾重灾区建议单独检查特别是调度周期结束时刻的蓄冰量目标值设置得太高会导致融冰计划受限设置太低又会失去削峰效果。6.2 蓄冰状态漂移问题状态漂移问题在日内滚动调度中特别常见。蓄冰槽状态每15分钟更新一次虽然单步误差很小但经过几十次滚动蓄冰量的累积偏差会越来越大。偏差主要来自两个地方一是散热损耗系数估计不准二是融冰功率的实际输出和模型假设不一致冰槽末端冰水混合物的温度升高会导致融冰能力衰减。我处理这类问题的方法是引入状态反馈校正。每次滚动调度开始之前用实测的蓄冰液位修正状态方程里的初始蓄冰量同时根据过去几个时段的实际融冰量在线辨识散热系数的偏差再代入下一个窗口的优化计算。这一步看起来不起眼但实测下来对冷负荷高峰时段的供应可靠性提升非常大否则模型算出来有冰可用实际系统里却融不出足够的冷量。6.3 求解速度优化当设备数量增加到十几台、日内滚动频次提高到每15分钟一次的时候求解速度就开始变得敏感了。这里有几个实用技巧。第一减少不必要的整数变量。比如电制冷机的启停变量如果它没有最小启停时间要求完全可以用连续变量加分段限幅来代替避免把纯线性问题变成MIP问题。第二给求解器设定合适的MIP gap一般设置1%到2%就能满足调度精度要求没必要追求0.01%精确解能省下一大半求解时间。第三要善用热启动把日前调度的解作为日内滚动第一轮窗口的初始可行解传给求解器能显著减少分支定界的搜索时间。6.4 数据单位与量纲的统一这个坑极容易被忽略但后果极严重。冷热量单位可能是kW、kWh、kWth也可能直接拿制冷吨来算电量的单位又牵扯到MW和kW的比例换算燃料热值单位又有kJ/Nm³和kWh/m³的区别。我曾见过一个项目把制冷吨直接当作kW使用结果冷负荷平衡约束偏差了1.1倍调度结果全部失真的情况。我的习惯是在数据输入阶段就把所有物理量统一转换成标幺值体系或者至少统一到同一个单位制下。比如电功率统一用kW冷热量统一用kWth能量统一用kWh时间统一用15分钟或1小时。代码里写一个单位转换脚本把所有原始数据全部转成模型内部的标准单位再验证几个典型时段的量纲一致性比如电功率总和除以总负荷误差不超过1%。这一步做好后面所有调试工作都会顺利很多。7. 实际项目中的一点心得体会这个项目我从建模到代码落地花了大半个月真正让我觉得有价值的不是最后那一版能跑的代码而是中间反复推翻重来的那些约束设计。冰蓄冷空调加入微网之后最直观的效果是把高峰冷负荷从电价高峰时段剥离了出去但要想真正跑出经济性光有设备还不够必须靠多时间尺度的协同。日前计划负责把大框架定下来日内滚动负责应对各种突发两层配合好设备的价值才能真正被释放出来。最后分享一个小技巧在Matlab里做这类调度模型建议从一开始就把所有数据、所有约束、所有求解结果都封装成结构体千万不要写一堆脚本文件互相乱调用。我见过太多代码模型参数散落在十几个文件里改一个电价数据要找半天。把数据和模型函数彻底分开用一套统一的输入输出结构会让项目后期的迭代速度和调参效率都上一个台阶。这个项目后续如果想继续扩展往里面加入电动汽车充放电、多元储能协调、需求侧响应这些模块整体框架不用大改扩展性非常好。