
简介面向光伏电站运维、电力调度与新能源预测领域的研究人员这份资料围绕“光伏电站短期发电功率预测”提供基于长短期记忆网络LSTM与支持向量回归SVR组合模型的完整实现方案适用于处理发电功率的随机波动与时间序列依赖关系解决功率预测精度不足的问题。压缩包共78个文件约1.97MB包含MATLAB源码(.m)、Python脚本(.py)、C/C辅助代码、示例数据(.mat/.xls)、可执行文件以及模型说明文档和程序说明覆盖从气象数据预处理、长短期记忆网络序列建模到支持向量回归非线性拟合与误差分析的完整流程。目前已有1662人学习下载。资源中附有实际电站数据可开展多步预测、正则化防过拟合、交叉验证调参等实践并配套模型说明书与程序注释便于快速复现预测模型、观察中间结果并调整参数适合有机器学习基础、希望将算法落地到光伏场景的工程师学习参考。1. 光伏电站短期发电功率预测先摸清预测边界与精度上限很多人一听到“光伏电站短期发电功率预测”第一反应是“不就是拿历史功率喂给神经网络吗”。实际接手一个电站之后你会发现模型精度的上限往往不由网络结构决定而是由数据质量和预测口径决定——辐照计上一坨鸟粪就能让整天的预测曲线系统性漂移这比调参要命得多。光伏电站短期发电功率预测核心是回答“未来0到72小时内并网点功率怎么走”直接服务于发电计划上报、储能充放电策略和电力交易决策。对做电站运维、功率预测系统开发、微网能量管理的工程师来说这是一项一旦跑偏就产生真金白银考核损失的工作。这篇文章只讲一件事怎么用一套可靠的数据管道和可复现的模型流程把短期功率预测从“论文里的方法”变成“电站里能跑的方案”。2. 数据基础决定预测上限分钟级气象与功率数据管道搭建2.1 数据来源与采样口径对齐短期预测的数据源一般有四类SCADA系统里的并网有功功率、电站气象站的地表辐照度与温湿度、数值天气预报NWP产品以及有条件的电站会部署的地基云图。很多团队一上来就堆特征把几十个变量塞进模型结果预测精度没有提升数据对齐反而变成噩梦。问题不在于数据量而在于时间口径不一致。SCADA功率通常是1分钟或5分钟一个点气象站辐照度往往是1分钟瞬时值NWP则是逐小时或者逐15分钟输出且有不同的起报时间。把这三种数据直接合并成一张表不做时效对齐模型学到的“规律”很可能只是不同数据源的延迟差。我一般会把训练数据统一重采样到15分钟粒度。原因很实际调度考核通常按15分钟或1小时统计15分钟粒度既能保留云团过境带来的功率波动细节又不至于让数据量太大、训练太慢。对短期预测来说1分钟粒度看起来美好但NWP和云图外推都达不到这个精度白白增加了计算开销。数据合并时还要为每个样本标记“预测时效”。NWP的输入是起报时刻的气象预报值地面观测是当前时刻的实测值两者代表的信息性质完全不同。做特征工程时我会把“当前时刻距NWP起报时刻的小时数”作为一个显式特征让模型有机会学到预报值随时效衰退的规律。这是很多人忽略的细节但对短期预测精度影响明显。2.2 清洗、填补与特征加工一段可直接跑的Python流程电站数据脏到什么程度没接过真实数据的人很难想象。通信中断导致功率整段缺失、逆变器停机时功率跳零、气象站辐照度在阴天出现超过1200W/m²的毛刺这类问题几乎每周都会遇到。以下代码是我在项目里常用的清洗与特征加工流程按实际电站情况改一下文件路径就能跑。import pandas as pd import numpy as np # 读取功率与气象数据时间列统一为UTC8 power pd.read_csv(scada_power.csv, parse_dates[time], index_coltime) weather pd.read_csv(station_weather.csv, parse_dates[time], index_coltime) # 统一重采样到15分钟 power_15 power[active_power].resample(15min).mean() weather_15 weather.resample(15min).mean() # 合并成一张表左连接以功率为准 df pd.concat([power_15, weather_15], axis1, joininner) # 基础合理性校验辐照度超过1200且功率接近0的组合直接剔除 valid ~((df[ghi] 1200) (df[active_power] 0.01 * 50000)) df df[valid] # 功率超过装机容量120%的点视为通信或采集异常 df.loc[df[active_power] 50000 * 1.2, active_power] np.nan # 夜间辐照为0但有功率抖动用前后2小时中位数平滑 night_mask (df[ghi] 5) df.loc[night_mask, active_power] df.loc[night_mask, active_power].rolling( window8, centerTrue, min_periods1 ).median() # 缺失值功率用线性插值辐照缺口超过1小时则标记为-1 df[active_power] df[active_power].interpolate(limit4) df[ghi_fill_flag] df[ghi].isna().astype(int) df[ghi] df[ghi].fillna(-1) # 构造基础特征太阳高度角需要经纬度和时间 from solar_position import get_solar_elevation # 站内自研工具函数 df[solar_elevation] df.index.map(lambda t: get_solar_elevation(t, lat39.9, lon116.4)) df[hour_sin] np.sin(2 * np.pi * df.index.hour / 24) df[hour_cos] np.cos(2 * np.pi * df.index.hour / 24)这段代码有几个关键参数需要按电站实际情况调整。resample(15min)对应预测的时间分辨率如果你想做小时级预测改成1h即可。装机容量50000是我假设的50MW电站你要替换成目标电站的并网容量。interpolate(limit4)表示最多填补连续1小时的功率缺失超过这个长度宁可保留缺失也不要硬填因为长段插值会制造“假平滑数据”让模型在真实波动场景下过度自信。太阳高度角这个特征对光伏预测至关重要。它比时间戳本身更直接地刻画出太阳轨迹让模型在阴雨天也能大致判断理论辐照上限。如果你的环境里没有现成的太阳位置计算库用pvlib的get_solar_position也可以但注意时区要和你数据的时区一致。2.3 标签构造短期预测到底在预测什么很多人做预测时只构造一个目标未来某个时刻的功率值。但实际调度需要的往往是“未来一段时间内的平均功率”因为考核和结算都按积分电量算。如果把目标设成未来1小时的15分钟瞬时功率点模型会去拟合每个时刻的波动细节而这部分恰恰是无法准确预测的。我的做法是预测未来1小时就把未来4个15分钟点的功率均值作为标签预测未来4小时就构造4个递进的均值标签对应未来1小时、2小时、3小时、4小时的平均出力。这里还有一个边界问题训练标签的构造必须严格使用“未来数据”。我在实际项目里吃过亏有一次把当天全天的功率均值混入标签导致验证集精度极高上线后预测效果惨不忍睹。做标签时要用shift(-n)取未来值并且保证预测目标时间点不能出现在输入特征中这是必须守住的底线。3. 从基线到GRU短期预测模型选型与可复现训练3.1 三类方法的代价与精度对比模型选型之前先明确一个事实短期预测不存在“万能模型”。不同预测时效对应的最优方法差别很大。0到4小时的超短期预测核心难点是云团运动无法提前精确建模数值天气预报在这个尺度上空间分辨率不够地面辐照计又只能代表单点。4到72小时的短期预测则更多依赖NWP的预报质量模型的任务变成修正系统误差。下表是我在多个电站项目里对比得到的大致结论具体数值会随电站纬度、气候类型变化方法适用时效输入依赖精度水平落地成本物理法晴空模型云遮系数0~4小时云图/辐照计晴好天较好波动天误差大中统计法ARIMA、马尔可夫链0~2小时历史功率短时平稳段尚可低机器学习SVR、GBDT0~24小时历史NWP稳定但难提升低深度时序模型LSTM/GRU0~8小时历史气象多变量波动场景下限更高中高实际落地时我建议先花一周时间把物理基线和统计基线跑通再决定要不要上深度学习。如果你的电站处于云量少的地区比如西北戈壁一个带云遮修正的物理模型可能就够用了。但如果是华东、华南这种多云多雨地区深度学习模型对多变量非线性关系的拟合优势会体现出来值得投入。理解“精度水平”时也要看场景。光伏预测的误差分为两部分慢变的天气过程误差比如对锋面过境时间判断偏晚两小时和快变的云团扰动误差比如突然飘来一朵云导致出力骤降。前者靠NWP改善后者靠云图外推和分钟级历史数据。深度学习模型在这两类误差上都有一定的学习能力但不可能消除这是物理世界的上限。3.2 用PyTorch训练一个15分钟尺度GRU预测模型选定GRU而不是LSTM原因很朴素短期功率预测序列不算太长GRU参数更少在小数据集上不容易过拟合训练速度也更快。代码核心是构造滑窗样本和训练循环。模型结构上做两个关键设计一是多步预测用“直接多输出”而不是递归预测避免误差在时间步之间累积放大二是把气象特征在时刻维度和功率历史拼在一起让模型自己学习辐照对功率的延迟效应。import torch import torch.nn as nn import numpy as np from torch.utils.data import DataLoader, TensorDataset class PVForecasterGRU(nn.Module): def __init__(self, n_features8, hidden_size64, n_layers2, horizon4): super().__init__() self.gru nn.GRU( input_sizen_features, hidden_sizehidden_size, num_layersn_layers, batch_firstTrue, dropout0.2 if n_layers 1 else 0 ) self.head nn.Sequential( nn.Linear(hidden_size, 32), nn.ReLU(), nn.Linear(32, horizon) # 直接输出未来4个15分钟点的功率均值 ) def forward(self, x): # x shape: (batch, seq_len, n_features) out, _ self.gru(x) last_hidden out[:, -1, :] return self.head(last_hidden) def make_samples(df, lookback96, horizon4, feature_colsNone): lookback96 表示用过去24小时96个15分钟点 horizon4 表示预测未来1小时的平均功率 if feature_cols is None: feature_cols [active_power, ghi, temp, wind_speed, solar_elevation, hour_sin, hour_cos, ghi_fill_flag] values df[feature_cols].values X, y [], [] for i in range(lookback, len(values) - horizon 1): X.append(values[i - lookback:i]) # 标签取未来horizon个点的功率均值即未来1小时平均功率 y.append(values[i:i horizon, 0].mean()) return np.array(X), np.array(y) # 数据归一化按特征顺序做minmax from sklearn.preprocessing import MinMaxScaler feature_scaler MinMaxScaler() df_scaled df.copy() df_scaled[feature_cols] feature_scaler.fit_transform(df[feature_cols]) X, y make_samples(df_scaled, lookback96, horizon4) # 按时间顺序切分严格禁止随机打乱后切分 train_cut int(len(X) * 0.7) val_cut int(len(X) * 0.85) X_train, y_train X[:train_cut], y[:train_cut] X_val, y_val X[train_cut:val_cut], y[train_cut:val_cut] X_test, y_test X[val_cut:], y[val_cut:] train_ds TensorDataset(torch.tensor(X_train, dtypetorch.float32), torch.tensor(y_train, dtypetorch.float32)) train_loader DataLoader(train_ds, batch_size256, shuffleTrue) model PVForecasterGRU(n_features8, hidden_size64, n_layers2, horizon4) optimizer torch.optim.Adam(model.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size30, gamma0.5) criterion nn.MSELoss() for epoch in range(60): model.train() total_loss 0 for xb, yb in train_loader: optimizer.zero_grad() pred model(xb).squeeze(-1) loss criterion(pred, yb) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() total_loss loss.item() * len(xb) scheduler.step() if epoch % 10 0: print(fepoch {epoch}, loss {total_loss / len(X_train):.6f})代码里的几个参数组值得具体说明。lookback96对应24小时的历史窗口这是我在多个电站上试出来的平衡点窗口太短比如只给过去4小时无法捕捉前一天的天气过程演变趋势窗口太长到48小时时训练时间明显增加而精度提升极其有限因为光伏序列非平稳远古信息对当前预测的帮助不大。hidden_size64对应中等规模电站10~100MW的数据量如果你的电站只有5MW且历史数据不满半年建议降到32。训练细节上梯度裁剪max_norm1.0很重要。光伏功率序列在云团遮挡时会出现断崖式下跌这种突变样本会造成梯度爆炸不裁剪的话训练loss会周期性跳到天文数字。学习率调度用StepLR每30个epoch降一半配合60个epoch的循环基本能让loss稳定收敛。如果发现验证集loss在20个epoch内持续上升就果断早停不要硬跑完。4. 短期预测模型在真实电站的五个常见翻车点4.1 辐照计污染导致预测系统性漂移现象模型在晴天预测功率整体偏高或偏低误差像是有个固定的偏置而且这个偏置持续好几天。原因电站气象站的辐照计长期暴露在户外灰尘、鸟粪、积雪会直接遮挡感光面。辐照计读数低于真实辐照模型在训练时学到“低辐照对应低功率”的映射关系但并网功率是真实的预测自然就偏低了。这类问题在数据清洗阶段极难发现因为辐照度数值看起来合理只是整体偏小。解决每周做一次辐照计清洁并在数据管道里加一道“晴空校验”。用太阳位置计算理论晴空辐照对比实测辐照在晴天的偏差。如果连续3个晴天实测辐照低于理论值超过10%就把该时段数据标记为可疑并在重训时将这部分样本降权。我现在的电站运维流程里辐照计清洁已经列入了每周例行巡检项比模型调参带来的收益大得多。4.2 NWP更新时间与预测时效错位现象预测曲线在每天固定时间点出现一个“台阶式”跳变比如每天早上8点发布的预测突然比7点版本高出一截。原因NWP产品每天按固定起报时间更新比如8点和20点。如果你的预测服务在NWP更新之后立刻切换输入数据而新旧两个起报批次的系统误差不一致预测就会突变。这不是模型问题是数据接口时序问题。解决在数据管道里为NWP输入增加“起报时间”标签模型预测时只使用最近的完整起报批次而不是在更新瞬间混用新旧数据。另外对NWP不同起报批次做滚动偏差修正维护一个“当前预报批次误差均值”的缓存对下一版本预报做实时校正。这个修正值要用滑动窗口计算窗口取过去7天比较合适。4.3 云图提前量与地表辐照的时空盲区现象模型在晴天转多云时误差不大但“多云转晴”场景下经常预测偏低导致功率实际高于预测考核产生偏差。原因地基云图外推能捕捉云团移动趋势但云图看到的是云层底部不等于地面辐照变化。云层变薄、消散的过程从云图上看不出明显变化而地面辐照可能已经在快速上升。这种时空盲区导致模型对“转晴”过程的响应滞后。解决在特征中加入“最近15分钟辐照变化率”和“功率变化率”。云团边缘过境时这两种变化率会先于绝对辐照值出现规律性信号。GRU模型能隐式利用这种变化趋势但前提是你把变化率作为显式特征喂进去而不是只给它原始值。我在实际项目中加入一阶差分特征后多云转晴场景的RMSE下降了约15%。4.4 随机划分数据带来的虚假高精度现象模型在测试集上RMSE非常漂亮但部署到线上后误差暴增30%以上。原因做数据切分时用了随机打乱而不是按时间顺序切分。光伏数据里相邻时间点高度相关随机划分会让训练集里混入测试集时间点附近的信息模型相当于“见过”测试答案的近似版本。这是短期预测领域最常见的评估错误。解决严格按照时间顺序划分训练集、验证集、测试集并且中间留出至少3天的隔离带。如果要做更严格的验证用滚动回测训练窗口随时间推进滑动每滑动一次就预测下一周把多次预测结果拼起来评估。这个方法会多几次训练成本但得到的误差估计才是真实上线水平。4.5 重训节奏没跟上组件衰减现象模型上线第一个月精度正常三个月后晴天预测逐渐偏高而且偏高的幅度每个月都微微加大。原因光伏组件存在衰减和积灰效应电站实际发电效率在持续下降。模型是从历史数据学习的它记忆中“同样的辐照对应更高的功率”但这个映射关系在悄悄变化。如果不重训模型会越来越乐观。解决建立定期重训机制常见做法是每两周重训一次基座模型同时用滚动数据自动更新归一化参数。另外监控“模型预测偏差的趋势”如果连续7天的平均预测偏差向同一方向偏移就触发一次模型重新校准。校准不是完全重训而是用最近30天数据对模型输出做线性纠偏这个操作成本低效果立竿见影。5. 部署后的自我验证滚动评估与自动重训5.1 分桶评估别被平均误差骗了只看整体RMSE会掩盖很多问题。光伏功率日间高、夜间低夜间误差天然趋近于零整体RMSE会被夜间数据稀释。我做评估时习惯按天气类型分桶晴空、多云、阴天、雨天各算RMSE并且只统计辐照度大于50W/m²的时段。更重要的是把预测误差按“辐照变化率”分层把“辐照剧烈波动时段”单独拿出来看。电站考核时最关心的是变化时段的预测能力平稳段的低误差没有实际意义。5.2 滚动回测与自动重训脚本滚动回测的代码不复杂但要注意不要把滚动窗口设置过小。我用8个月数据做训练窗口预测未来1周然后窗口后移1周。对短期预测模型来说训练窗口低于3个月时模型很难学到跨季节的辐照特征。滚动回测跑完一次大约需要几个小时我一般放服务器上夜间执行早上看结果。每天的自检流程我固定做三件事看当天预测曲线与实测曲线的偏差方向、检查辐照计清洁度记录、确认NWP数据接口是否在起报时间后正常刷新。这个习惯帮我提前发现过两次数据源切换导致的预测异常。每个季度再看一次滚动回测报告决定要不要调整隐藏层大小或lookback长度。做预测系统这些年我最大的体会是模型只占一半工作量另一半是数据质量和验证流程。每次模型效果变差先查数据再怀疑模型参数。先跑通一套严格的时间顺序评估再谈调参。希望帮到你。本文还有配套的精品资源点击获取