去年接手一个商业综合体项目的冷热源改造,空调系统用的是地源热泵(GSHP),运营方反馈很典型:室内温度能稳住,但总感觉末端忽冷忽热,而且主机电费比同类型项目高一截。最初我们以为是PID参数没调干净,后来把地埋管侧的换热过程拉出来分析才发现,问题的根源不在控制器本身,而是这套系统的热惯性太大了——地埋管与土壤之间的换热响应慢到以小时计,常规闭环控制只能等偏差出现才去调,等主机频率跟上负荷变化,房间早就过温或欠温了。这个项目最后的方案,就是标题里写的基于深度学习LSTM模型的预测控制:用LSTM对系统热状态做多步超前预测,再把预测结果接入一个滚动优化控制器,让主机、水泵和阀门的动作比实际负荷变化提前一拍。这篇博文就围绕这个项目完整拆一遍技术路径,包括LSTM负荷预测模型怎么搭、预测控制策略怎么设计、Matlab工程代码怎么组织,以及我们实际跑数据时踩过的坑。适合做暖通自控、能源管理、建筑节能算法或者机器学习预测控制的朋友参考。
1. 为什么地源热泵温控系统非要上预测控制
1.1 传统控制方式的天花板
地源热泵系统与传统风冷热泵最大的区别,在于它多了一个巨大的地下换热体。夏季制冷时,冷凝热通过地埋管排入土壤,冬季制热时从土壤取热。地下土壤本身热容量极大,温度变化非常缓慢,这既是地源热泵能效高的根本原因,也是控制难点所在。
常规控制系统里用得最多的是PID加机组群控逻辑。PID的思路是"看到误差再动手":回水温度偏高,就加大主机频率或开大电磁阀;温度偏低,就降频关阀。问题是,建筑负荷是不断变化的——早上九点上班人群涌入、午后太阳辐射增强、傍晚商场人流骤增,这些负荷跃迁发生得很快,而地埋管侧的水温和土壤温度变化却很慢。PID看到误差时才响应,等调节量真正通过地埋管换热体现到末端供水温度上,往往已经过去几十分钟甚至一两个小时。这个时间差直接表现为室内温度的周期性过冲和欠冲,而机组为了纠正过冲又会频繁加减载,能耗自然就上去了。
我在项目里做过一个对照测试:把某天的运行数据把PID控制下的主机启停次数拉出来看,一天之内频繁波动的次数是预测控制方案的两倍以上。频率波动大意味着压缩机频繁在高低速之间切换,既费电又伤机器。
1.2 LSTM在这个场景里到底干什么用
预测控制的核心是给控制器一双"预知未来的眼睛"。不需要精确到下一秒,只需要能提前几小时判断出负荷趋势、回水温度走势、室内温度走势,控制器就可以提前调整设定值和执行机构,让整个系统的动作走在负荷前面。
为什么选LSTM而不是传统时间序列模型或者别的机器学习模型?这个项目里我是做过对比的。ARIMA对新工况的适应能力太差,遇到节假日、极端天气、大型活动这类突发负荷,残差会迅速变大;普通的BP神经网络或者支持向量回归处理非线性问题没问题,但它们没有对时序依赖关系做显式建模,很难把"前24小时的热状态如何影响下一小时出水温度"这种记忆关系学出来。LSTM的结构里有遗忘门、输入门和输出门,本质上是在学习"哪些历史信息要记住、哪些要丢掉",这跟建筑热系统"今天中午的蓄热量会影响傍晚的供水温度"这种物理逻辑天然吻合。
这个项目最终选型就是:多步LSTM预测模型 + 滚动优化控制(模型预测控制思想)。LSTM模型负责预测未来4到6小时的室内温度、回水温度和负荷趋势,优化器基于这些预测值去计算未来1到2小时的最优控制设定值,每个控制周期滚动刷新一次。这么做的直接收益是:机组和阀门的动作变平滑了,室内温度波动从正负1.5摄氏度收窄到正负0.5摄氏度以内,主机频繁启停的问题也基本消失。
2. 项目起步:数据清洗与特征工程才是重头戏
2.1 测点梳理与输入变量选择
很多人一说到深度学习的项目就觉得重点是搭网络,但在这个领域,真正决定模型上限的是数据质量和特征工程。我们当时从现场BA系统里导出了将近三个月的运行数据,采样频率大部分是1分钟,有些老表是15分钟。第一步不是急着喂给网络,而是先理清测点。
我最后保留的输入变量分四类,给表放在下面:
| 变量类型 | 具体变量 | 说明 |
|---|---|---|
| 外部气象 | 室外干球温度、室外相对湿度、水平太阳辐射 | 建筑负荷最强的影响因素 |
| 内部状态 | 室内平均温度、送回风温度、CO2浓度 | CO2浓度间接反映人员密度变化 |
| 系统运行 | 地埋管侧进出水温度、热泵主机功率、二次泵频率、末端阀门开度 | 描述当前热力系统所处状态 |
| 时间特征 | 时刻(0到23)、星期几、是否节假日 | LSTM不能自带时间概念,必须显式编码 |
有几个变量是我后来才意识到必须加的。一个是节假日标志位。商业综合体的负荷曲线在工作日和节假日是完全两个形状,如果没有这个特征,模型在假期预测会明显失准。另一个是时刻特征,我把一天24小时按sin/cos编码成两个维度喂进去,比直接用0到23的整数效果好很多——因为凌晨0点和深夜23点在数值上很接近,但实际上是一个时段的延续,sin/cos编码能保留这种周期性。
2.2 时间窗口与训练样本构建
LSTM训练数据需要把连续时间序列切成一个个样本对。每个样本包含两部分:一段长度为N的历史窗口作为输入,下一段长度为M的未来数值作为预测目标。这里的N和M需要结合实际控制周期来定。
系统的控制周期我定为15分钟。历史窗口取了24步,也就是过去6小时的数据;预测目标取了未来8步,对应未来两小时的温度与负荷趋势。这个配置不是拍脑袋定的:太短的历史窗口模型记不住热惯性,太长了则会混入过多噪声且训练数据量减少;预测长度如果只有几十分钟,提前量不够控制器用,预测两小时以上误差又会大幅上升。试过12步、36步窗口之后,24步在验证集上综合表现最好。
样本构建还有一个容易被忽略的细节:滑窗步长。如果按1分钟采样、窗口24步,那滑动步长设为1会严重浪费算力,而且相邻样本高度相似,训练出来的模型泛化性反而差。我在15分钟重采样后再滑窗,滑动步长取3到4步,相当于每45分钟到1小时生成一个训练样本,这样既保证了样本数量,又降低了冗余。
2.3 Matlab中的数据预处理与数据集划分
Matlab里做这套流程很简单,核心是先重采样再归一化再滑窗。重采样用retime函数,归一化我用了两种方式对比,最后选了z-score标准化。这里有一个需要特别注意的安全边界问题:标准化用的均值和标准差必须只用训练集计算,再把同样的参数应用到验证集和测试集上。如果先对整个时间序列做标准化再切分,等于把未来数据的信息偷偷泄露给了训练过程,验证集和测试集的评估结果会虚高,这在预测控制落地时是致命的——你会带着一种"模型很准"的错觉上线运行,然后被真实误差打得措手不及。
数据集切分上,我建议完全按时间顺序切,不要随机打乱。因为时间序列有自相关性,随机打乱会把未来信息混入训练集。我习惯按7:1.5:1.5切分,而且验证集和测试集不能紧贴着训练集末尾,最好在中间留一段gap,防止模型把"连续性"误认成"因果关系"。代码示意如下:
% 假设 dataTable 包含重采样后的全部测点,均值为meanVec,标准差stdVec [Xtrain, Ytrain, Xval, Yval, Xtest, Ytest] = ... buildSlidingWindows(dataTable, histSteps, predSteps, gapDays); function [Xtrain, Ytrain, Xval, Yval, Xtest, Ytest] = buildSlidingWindows(... rawTable, histSteps, predSteps, gapDays) % 1. 按时间排序,去掉缺失段 rawTable = sortrows(rawTable, 'Timestamp'); % 2. z-score标准化参数仅基于训练段计算 trainRows = rawTable(1:round(height(rawTable)*0.7), :); mu = mean(trainRows{:, varNames}); sigma = std(trainRows{:, varNames}); normTable = rawTable; normTable{:, varNames} = (rawTable{:, varNames} - mu) ./ sigma; % 3. 按时间顺序滑窗生成样本 ... end3. LSTM温度预测模型搭建细节
3.1 网络结构与超参数设定
Matlab深度学习工具箱对LSTM的支持已经很成熟,不需要自己写前向传播。我最终的模型结构是:一个序列输入层,接两层LSTM,中间夹两个Dropout层,最后是全连接加回归输出。隐藏单元数第一层取了128,第二层取了64。两层设计比单层效果好一些,因为第一层能学习基本的热响应模式,第二层在抽象特征上进一步提炼负荷变化的组合关系。
Dropout比例在这里很重要。建筑运行数据本身噪声很大,末端有人开关门窗、设备偶尔巡检停机,这些都会在数据里留下毛刺。如果不用Dropout,训练集损失能压得很低,但验证集误差回升特别明显,典型的过拟合。我对比过0.1、0.2、0.3三档,最后定格在0.2,在保证拟合能力的同时把过拟合控制在了可接受范围。
训练轮数上,我用了早停机制,最大训练轮数200,但验证损失连续10轮不下降就停止。学习率初始值0.005,用了自适应矩估计优化器。这里有个经验:LSTM的训练对学习率很敏感,试过0.01,loss曲线振荡到几乎没法收敛,降到0.005之后就稳定了。
网络定义代码大致是这个样子:
numFeatures = size(Xtrain{1}, 1); % 特征维度 numResponses = size(Ytrain{1}, 1); % 预测目标维度 layers = [ sequenceInputLayer(numFeatures, 'Normalization', 'none') lstmLayer(128, 'OutputMode', 'sequence') dropoutLayer(0.2) lstmLayer(64, 'OutputMode', 'last') dropoutLayer(0.2) fullyConnectedLayer(32) reluLayer fullyConnectedLayer(numResponses) regressionLayer ]; options = trainingOptions('adam', ... 'MaxEpochs', 200, ... 'InitialLearnRate', 0.005, ... 'MiniBatchSize', 64, ... 'ValidationData', {Xval, Yval}, ... 'ValidationFrequency', 50, ... 'OutputNetwork', 'best-validation', ... 'Plots', 'training-progress', ... 'Verbose', false);3.2 训练过程中的关键指标怎么看
回归模型不能只看一个损失函数。我在项目里重点盯三个指标:均方根误差、平均绝对百分比误差和方向准确率。前两个大家都很熟,方向准确率是预测控制项目需要额外关注的——它衡量的是"预测的上升/下降趋势是否跟实际一致"。原因很简单:控制器需要的核心信息是趋势,不是数值。只要方向判断对了,哪怕幅值有点误差,优化器采取的增减策略也是对的;反过来,如果数值预测很准,但趋势判断错了,相当于该降温的时候反而升温,整个控制动作就是反的。
三组数据的测评结果大概是这样(因为数据涉及项目方保密要求,这里只给量级参考):
| 指标 | 验证集 | 测试集 |
|---|---|---|
| 回水温度RMSE | 0.32℃ | 0.41℃ |
| 室内温度RMSE | 0.28℃ | 0.35℃ |
| 回水温度MAPE | 2.1% | 2.8% |
| 方向准确率 | 87.3% | 83.6% |
测试集结果比验证集略差是正常的,也说明模型没有明显过拟合。如果测试集比验证集还好,反而要怀疑数据切分环节出了泄露问题。
3.3 预测到底准不准:直观验证方法
数值指标是不够的,我习惯把预测曲线和真实曲线画在一张图上逐段检查。重点看三种场景:平稳段、突变段、拐点段。平稳段误差应该很小,突变段误差肯定会放大,但只要拐点的时间位置偏差不超过一到两个控制周期,控制器就能接受。如果模型在每个突变点上都晚了一个小时才反应过来,那这种模型就算RMSE好看也不能用,因为它没有学到热惯性规律,只是学会了延迟复读。
我还做了一件事来验证模型是否真的学到了物理规律:把室外温度人为抬高5度,重新输入模型,观察预测的供水温度是否随之下调。好的模型应该有符合物理直觉的反应——室外变热,负荷上升,供回水温度预测值上升。如果模型对这种扰动没有反应,说明它学到的主要是历史自回归,而不是气象与热力系统之间的因果关系。这个测试帮我们筛掉了一个看起来很准、实际只是记忆历史数据的候选模型。
4. 预测控制策略:让模型真正去驱动温控系统
4.1 从预测到控制的闭环框架
LSTM模型预测做得再好,如果不接到控制回路里,它只是一个昂贵的趋势图。这个项目里我采用的是标准模型预测控制框架,只是把预测模型从机理模型换成了LSTM黑箱模型。每个控制周期执行四步:读取当前状态、用LSTM预测未来两小时状态、用优化器求解未来两小时的最优控制序列、只执行序列中的第一步。15分钟后重复这个过程。因为每一步都在滚动刷新,所以即使预测有误差,也不会被累积放大。
控制变量选择上,我没有直接去控制压缩机频率,因为现场主机自带PID调节逻辑,外部干预频率反而容易打架。最终定下来的控制变量有两个:一个是二次侧供水温度设定值,另一个是地埋管侧循环泵的转速比例。这两个变量既能直接影响末端换热量,又被现场设备允许外部接管,改动成本最低。决策周期同样是15分钟,预测未来两小时,控制仅执行第一步,也就是每个周期重新优化一次。
4.2 目标函数与约束怎么设计
预测控制的核心是目标函数设计。我用的目标函数分三项:舒适性偏差、能耗惩罚、动作平稳惩罚。舒适性偏差是未来预测步数内室内温度与设定温度之差的平方和;能耗项用未来主机功率预测作惩罚,功率本身也可以用一个简单回归模型去估计;动作平稳项限制相邻两个控制周期设定值的差不要太大,避免阀门和泵频繁波动。
表达式可以写成下面的形式:
function J = costFunction(uSeq, state, model) % uSeq: [供水温度设定序列; 泵转速序列] % state: 当前系统状态 % 内部调用LSTM模型 predictTemperature 得到未来室内温度预测 tempPred = predictIndoorTemp(model, state, uSeq); powerPred = estimatePower(model, state, uSeq); comfort = sum((tempPred - refTemp).^2); energy = lambda * sum(powerPred); smooth = beta * sum(diff(uSeq(1,:)).^2 + diff(uSeq(2,:)).^2); J = comfort + energy + smooth; end涉及约束条件时,现场工程比理论要复杂很多。供水温度有硬上下限,低于某个值末端风机盘管可能结露,高于某个值采暖效果不达标;泵转速有范围,太低会触发最小流量保护;设定值还不能变化太快,否则主机跟不上。求解时我用fmincon处理连续变量,加上边界约束和线性不等式约束。为了让在线求解稳定,我把决策序列自由度压到了4步——也就是只优化未来1小时内的4个控制决策点,而不是完整预测时域的8步,这样求解速度和稳定性都好很多。
4.3 Matlab工程代码的整体结构
预测控制项目写下来不算短,但模块化以后逻辑很清晰。我的代码分成四个文件:数据加载与预处理脚本、LSTM训练脚本、温度预测函数、滚动优化主程序。main.m里是主循环,每15分钟调用一次优化器,把最优设定值下发到现场控制器。
% main.m - 滚动优化主循环(仿真模式) for k = 1:numSteps % 1. 读取当前状态 state = plantSnapshot(k); % 2. 调用优化器 [uOpt, fval] = fmincon(@(u)costFunction(u, state, net), ... u0, A, b, Aeq, beq, lb, ub, @nonlcon, options); % 3. 只执行第一步 plantSetpoint(k+1, :) = uOpt(1, :); % 4. 用仿真器或真实系统推进一个控制周期 state = advancePlant(state, uOpt(1, :)); end特别说一下优化器求解时间。刚开始我用完整预测时域做优化,单次求解要20多秒,完全无法满足15分钟控制周期后面隐含的实时性要求。后来把决策序列压缩到4步,并且给fmincon设置了最大迭代次数和步长容差,单次求解压到了3秒以内。如果你在真实系统上部署,还要考虑通信延时,强烈建议把优化器放到现场工控机里跑,上位机只负责下发和监控。
5. 实测运行中的坑与解决思路
5.1 第一个坑:看似精确的数据其实藏着大漏洞
项目上线前做历史回测,模型在测试集上表现非常好,回水温度RMSE只有0.2摄氏度出头。后来我把预测结果按小时分桶统计,发现凌晨4点到6点的误差远小于白天,而且所有预测曲线都异常平滑,像是提前知道了答案。排查之后确认问题出在归一化——之前那份代码是在完整数据集上先做标准化再切的训练集,均值里包含了未来数据的信息。以室外温度这一列为例,测试段的标准差比训练段小,标准化后测试段的数值整体被"压缩"了,模型输出自然更接近真实值。
解决方式就是前面提到的:严格只用训练集计算标准化参数。另外还加了一步检查:每次训练完,把预测值和真实值的时间差做互相关分析。如果互相关峰值出现在零延迟附近且异常尖锐,说明数据可能存在时间错位或泄露,需要回到数据管道里排查。这一步后来帮我发现了一个隐藏更深的错误:BA系统里供水温度测点的时间戳比实际采集慢了两条记录,导致模型有机会"偷看"未来15分钟的真实温度。
5.2 预测滞后问题与针对性调整
LSTM在拐点处天生容易产生一个周期的滞后,这一点在做控制时特别明显。具体表现是:早上室外温度快速上升,预测的冷负荷也上升,但上升的时间点比实际晚了一到两个周期。控制器跟着预测动作,就变成了"总是慢半拍"。
我试过两种调整思路。第一种是把输入窗口加长,从6小时加到12小时,让模型有更多历史上下文去判断"当前升温是阶段性波动还是趋势性变化",但这个方案效果有限,只把滞后减少了半个周期。第二种是在目标函数层面做补偿,在计算舒适度偏差时,不仅惩罚当前温度偏差,还额外惩罚温度的上升速率,相当于逼控制器"看到趋势就要提前动作"。第二种方案效果更明显,室内温度峰谷差进一步收窄了约0.3摄氏度。这个经验很重要:预测模型的滞后可以通过控制目标的软约束来补偿,不必一味追求预测模型本身精度。
5.3 极端天气与节假日工况的外推风险
LSTM本质上是个数据驱动模型,冷启动和极端工况是天然的短板。夏天极端高温时段,室外温度超过历史训练数据上限时,模型预测的冷负荷会明显偏保守。我们的处理办法分三层:第一层是气象前馈,把未来几小时的短期天气预报接入LSTM输入,而不是只用过去实测气象数据;第二层是设置工况隶属度检测,如果当前室外温度超过训练集95%分位数,就自动切换为保守控制模式,不再完全信任预测值;第三层是保留一套传统的规则控制作为兜底,预测控制一旦输出异常就自动切回。这套三层机制在项目里被证明非常必要,可靠性的优先级永远高于节能率。
5.4 节能效果评估必须做的工作
预测控制上线的目标是节能,但节能数据不能靠嘴说。我的评估方法是选择改造前的同期数据做基准对比,同时用度日数修正天气差异。具体做法是计算评估时段内的制冷度日数,对比基准期和运行期的度日数差异,按比例修正基准能耗,再计算节能量。此外,还统计了室内温度平均值和波动标准差,避免"节能靠牺牲舒适度"的假象。
最终结果方面:供热季运行了两周对比测试,在室内温度平均值基本不变、波动标准差从0.9摄氏度降到0.4摄氏度的前提下,主机侧耗电量下降了约11%,水泵能耗下降了约7%。我没有把这两个数字包装成一个总的"节能18%",因为主机和水泵的能耗结构不同,整体的说法容易误导。
6. 如果要复现这个项目,我建议你注意这几件事
做LSTM预测控制项目,很多人一开始就想把网络结构弄得又大又复杂,我的经验是反过来:先把数据管道和验证流程做得滴水不漏,再去考虑网络多么高端。这个项目里真正花时间的不是在调LSTM层数,而是在清洗BA系统里错标的时间戳、缺失的传感器数据和各种设备手自动切换产生的异常点上。
另外想提醒的是,LSTM预测控制不是用来替代现有系统里已有的保护逻辑的。像压缩机最小运行时间保护、地埋管侧进出口最低温差保护、防冻保护这些,必须原样保留在控制链路上,预测控制输出只能做设定值层面的调整,不能越权去直接启停压缩机。这样即使模型哪天出现误判,也不至于造成设备损坏。
最后聊聊扩展方向。这个框架本身是不限领域的,只要是有热惯性、有滞后特性的被控对象,都可以套用:集中供热换热站、冷站蓄冷系统、数据中心精密空调,甚至部分工业炉温控制。模型结构不用大改,主要是把特征变量和控制变量换成对应系统的物理量。后续如果要进一步优化,可以尝试把LSTM和机理模型结合,用机理模型提供物理约束,LSTM负责修正机理误差,这种做法在控制稳定性上通常比纯黑箱更稳。