简介:面向分布式光伏发电功率预测研究与应用的技术资料包,主要服务于电力系统调度、新能源并网及光伏电站运维等方向的工程师、研究人员和高校学生。资源提出一种结合经验模态分解(EMD)、主成分分析(PCA)与长短期记忆神经网络(LSTM)的光伏出力预测方法,充分考虑环境温度、风速、风向、辐照度等气象因子对输出功率的影响,并采用山西省某电站8个月实测数据进行验证,能够帮助读者较快理解多变量气象序列降维与动态时间建模的完整思路。压缩包共8个文件、约116KB,以Matlab源程序(.m)和测试数据集(.mat)为主,辅以结果评估脚本,覆盖数据输入、模型预测、指标计算等主要环节,结构清晰便于直接运行与二次开发。已有200余人下载学习,适合对EMD分解、PCA特征提取及LSTM时序预测有一定基础、希望借助完整案例快速搭建光伏功率预测流程的读者。
1. 分布式光伏出力预测:为什么气象因子才是真正的黑匣子
接手分布式光伏电站的功率预测任务时,大部分人的第一反应是去翻逆变器历史数据、找组件铭牌参数、搭一个时间序列模型。但真正让预测结果翻车的,往往不是模型不够先进,而是气象因子没有处理好。分布式光伏与集中式地面电站最大的区别在于:装机容量小、布局分散、往往没有完整的气象站实测数据,甚至一个园区内不同屋顶的辐照度都能差出 30%。在这种数据条件下做出力预测,气象因子的选择、清洗、对齐方式直接决定了模型是能用还是废掉。这篇文章讨论的是一套以气象因子为输入的分布式光伏出力预测落地路径:从物理机制出发梳理特征体系,再到模型选型和最小可行实现,最后把实测中反复踩到的坑摊开来讲。适合正在做光伏功率预测、微电网能量管理或电站评估的工程师与算法人员,新手可以照着一步步复现,熟手可以对照检查自己的特征工程和验证流程有没有遗漏。
2. 光伏出力预测的物理基础:气象因子如何影响发电功率
2.1 出力预测的核心物理链条:辐照度、温度、风速的作用次序
光伏组件输出功率的直接物理来源是太阳辐照,但辐照只是起点。组件实际出力要经过三层损耗和修正:到达组件表面的有效辐照要扣除云层吸收、大气散射和灰尘遮挡;组件吸收辐照后升温,而晶硅组件的功率温度系数约为 -0.4%/℃,温度每升高 25℃,组件输出功率相对标称值降低约 10%;风速则通过散热效应反向调节组件工作温度,间接影响出力。
所以在构建出力预测模型时,特征不能只选辐照度和气温两个基本量。我一般会再加两个特征:风速用于修正组件温度,湿度用于判断云层类型和大气水汽含量。风速超过 2m/s 时组件的散热效率有明显提升,而高湿度往往伴随低云和雾霾,直接削减有效辐照。这四类气象因子加上时间变量,是分布式光伏出力预测的最小特征集。
辐照度本身还要区分水平面总辐照(GHI)和组件斜面辐照(POA)。多数公开气象数据只提供水平面辐照,而分布式光伏组件通常以固定倾角安装,辐照修正需要经过倾角与方位角的投影变换,这部分误差在非正午时段尤其显著,建模时如果直接用 GHI 替代 POA,午后出力预测容易出现系统性偏低。
2.2 气象因子特征集设计:原始数据之外还需要哪些衍生特征
原始气象因子只能描述当前状态,而光伏出力是一个强时序依赖过程。云层移动是连续过程,上一小时的辐照状态对当前出力有惯性影响,因此时间滞后特征是提升预测精度的关键一环。实际操作中,我一般构建三个时间尺度的滞后特征:前 15 分钟出力值、前一小时平均辐照、前三小时辐照变化趋势(一阶差分)。
除了滞后特征,还需要加入辐照的日周期位置特征。光伏出力预测天然带有强周期性,但单纯用时间戳不能让模型理解日升日落的规律。常见做法是把时间拆成小时、月份、以及全年日落日升相位角,或者直接用太阳高度角与方位角作为特征,这两个角可以通过经纬度和时间精确计算,也是让模型区分晴雨天的关键信号。
气象因子的衍生组合特征同样不可忽视。辐照度与出力的比值可以反映系统效率;温度与风速的交互项可以近似组件背板温度;辐照一阶差分可以捕捉云层快速遮蔽状态。这些组合特征在数据量不大时,比让模型自己从原始特征中隐式学习要稳定得多,尤其是使用树模型时,人工组合特征往往能显著减少树深度需求。
2.3 预测时间尺度分档:超短期、短期、中期建模思路完全不同
气象因子对出力预测的影响,在不同时间尺度上权重不一样。超短期预测(0~4 小时)主要依赖历史出力序列和实时辐照变化趋势,数值天气预报(NWP)在这个窗口内的空间分辨率太粗,参考价值有限。短期预测(未来 1~3 天)则是气象因子的主战场,此时 NWP 的辐照预报、云量预报直接决定预测精度。中期预测(一周以上)更多依赖气候统计值,这时候气象因子反而退居次位,主要看季节与年周期。
时间尺度的划分决定了你选什么气象数据源。做超短期预测,用本地气象站实测数据加上地基云图观测是常见做法;做短期预测,NWP 数据的更新频率和时空分辨率才是关键;而中期预测则可能需要参考历史同期气象统计。我见过不少团队用一个模型打天下,结果超短期精度尚可、短期一塌糊涂,原因不是模型不行,而是输入特征的时间尺度与预测目标不匹配。
3. 出力预测模型选型:从物理模型到深度学习的边界在哪里
3.1 五类常见预测模型的适用场景对比
光伏出力预测模型大致可归为物理模型、统计回归、机器学习、深度学习和混合模型五类。物理模型从组件电气参数与气象条件出发,通过等效电路方程计算出力,不依赖历史数据,但需要精确的组件参数与安装倾角信息,实测中往往因为组件衰减、脏污程度不一致而出现偏差。统计回归(如自回归移动平均模型类)擅长处理平稳序列,但对辐照突变的响应能力差。
机器学习模型(随机森林、梯度提升树)是目前工程落地的首选。它们对特征尺度不敏感,能处理非线性关系,并且在气象因子缺失时仍有不错的鲁棒性。深度学习模型(长短期记忆网络、时序卷积网络、Transformer)在数据量大、特征维度高的场景下精度上限更高,但训练成本和对数据质量的要求也更高。混合模型则是一种务实路线:用物理模型计算结果作为特征喂给机器学习模型,或者用多个模型输出的加权集成。
3.2 模型选型的判断依据:数据量、业务目标与计算约束
选型不是看哪个模型论文指标好看,而是看你的数据条件和业务目标。如果是新投运电站,历史数据不足三个月,物理模型或简单回归会是起步阶段的选择,机器学习模型在数据量过少时反而容易过拟合。如果历史数据超过一年且包含完整四季,梯度提升树类模型往往能获得不错的精度,而且对特征工程友好。
业务目标同样关键。做电站内部能量调度,关注 RMSE 和峰值误差;做电网考核申报,关注的是精度达标率;做投资收益测算,则需要概率预测输出,而不是单一的点预测。深度学习模型在概率预测上更容易扩展(通过分位数损失或贝叶斯近似),但物理模型与机器学习模型的组合反而更容易在生产环境中稳定运行。
另一个容易忽略的约束是推理速度与部署环境。分布式光伏电站往往在边缘侧部署预测服务,硬件资源有限。一套轻量级梯度提升树模型的推理耗时在毫秒级,而深度学习模型需要依赖框架运行时,内存占用可能高出两个数量级。我一般建议先跑通一个特征完整的树模型作为基线,再看精度瓶颈是否值得引入深度学习。
3.3 气象因子的时间对齐:频率不同、时延不同带来的建模陷阱
气象数据与出力数据的频率往往不一致。气象站数据可能是逐小时记录,逆变器出力是 5 分钟级采集,数值天气预报的更新频率可能是 3 小时或 6 小时一次。直接合并会造成时间戳错位,模型学到的是“错误的对应关系”。
标准做法是构建统一的时间栅格,例如以 15 分钟为基准步长,将高频出力数据重采样为 15 分钟均值,将低频气象数据向前填充,但要注意填充方向。预测时只能使用当前时刻已知的信息,所以气象数据只能“后向填充”到当前时间点,绝对不能向后填充或中心填充,否则会造成数据泄漏,表现为验证集指标极好、实盘表现崩盘。
NWP 数据的时延问题更隐蔽。数值天气预报的发布时刻与数据有效时刻之间存在预报时效,例如 08:00 发布的 12:00 辐照预报,实际代表的是 4 小时预报。在使用这类特征时,要把“预报发布时刻”和“预报有效时刻”同时编码进特征,否则模型无法区分不同时效的预报精度差异,预测结果会包含预报本身的系统性偏差。
4. 用气象因子训练光伏出力预测模型:最小可行流程
4.1 数据清洗与气象因子补齐的正确顺序
数据清洗的顺序比清洗本身更重要。我踩过的坑是:先补气象缺失值再清洗出力异常数据,结果被异常出力污染后的均值填充反过来污染了气象序列。正确顺序是先处理出力数据中的异常值,再对气象因子做缺失值处理。
出力数据的异常主要有三类:夜间非零出力(传感器零漂)、限电导致的平台期、通信中断导致的长时间零值。夜间非零出力可以通过太阳高度角阈值判别;限电平台期要用出力与辐照的比值识别,当比值长期低于理论效率的 20% 时大概率是限电;通信中断导致的零值段需要用时间连续性检查。
气象因子缺失的补齐顺序也有讲究。风速和湿度是空间相关性较强的变量,可以用邻近电站插值;辐照度的缺口如果超过一小时,单纯插值会引入较大误差,此时更合理的做法是利用当日该时段的晴空辐照模型做比值修正。以下是清洗流程的代码示意:
import pandas as pd import numpy as np def clean_pv_data(df, solar_elevation_threshold=5): """ df: 包含出力、辐照度、温度、风速、湿度、时间戳的DataFrame 返回清洗后的数据框 """ # 1. 计算太阳高度角,用于识别夜间异常出力 df['solar_elevation'] = calculate_solar_elevation(df['timestamp'], lat, lon) night_mask = df['solar_elevation'] < solar_elevation_threshold # 2. 夜间出力应接近零,超过阈值视为传感器漂移 df.loc[night_mask & (df['power'] > 0.01 * df['capacity']), 'power'] = 0 # 3. 限电检测:出力/辐照度比值低于理论效率20%持续超过2小时 df['efficiency'] = df['power'] / (df['irradiance'] * df['capacity'] / 1000) low_eff_mask = df['efficiency'] < 0.2 # 连续限电段的识别需要滚动窗口,此处用分组标记简化 df['is_curtailment'] = ( low_eff_mask.rolling(window=12, min_periods=1).mean() > 0.8 ) # 4. 风速与湿度采用前向填充,辐照度缺口超过1小时则用晴空模型估算 df[['wind_speed', 'humidity']] = df[['wind_speed', 'humidity']].ffill() df['irradiance'] = df['irradiance'].mask( df['irradiance'].isna() & (df['irradiance'].isna().groupby( df['timestamp'].dt.date).cumsum() > 4), estimate_clear_sky_irradiance(df['timestamp'], lat, lon) ) return df这段代码的顺序逻辑是:先清洗出力,再补气象。步骤 2 用太阳高度角筛掉夜间非零出力,避免零值污染后续效率计算;步骤 3 检测限电段,这些段的出力数据在训练时要降权或剔除,因为它们不代表真实发电能力;步骤 4 对短缺口用前向填充,因为气象变化是连续过程,但对超过 1 小时的辐照缺口改用晴空模型估算,避免长时间插值带来的平滑偏差。
4.2 特征工程落地:从气象因子到模型输入的完整管道
特征工程的核心目标是让模型“看到”物理规律。以辐照度为例,原始辐照只有数值,但模型需要知道当前辐照对应的是晴天、多云还是阴天。解法是引入晴空指数(Clear Sky Index),即实测辐照与理论晴空辐照的比值。晴空指数接近 1 表示晴天,接近 0 表示厚云遮蔽,它比原始辐照更能反映云层状态。
温度特征也要做变换。组件背板温度比环境温度对出力的影响更直接,但背板温度不是每个电站都有传感器。常见做法是用环境温度、风速和辐照度估算背板温度:背板温度约等于环境温度加辐照度乘以一个热系数(通常在 0.02~0.03℃·m²/W),风速会降低这个温升。这个估算值作为特征输入,比直接用环境温度更能捕捉高温降额效应。
以下是一个从原始气象数据到训练特征矩阵的完整处理流程:
def build_feature_matrix(df, capacity_kw): """ df: 清洗后的15分钟粒度气象与出力数据 返回特征矩阵与目标变量 """ # 1. 出力的归一化目标:用装机容量做标幺化 df['power_pu'] = df['power'] / capacity_kw # 2. 晴空指数:需要预先计算理论晴空辐照 df['clear_sky_irr'] = calculate_clear_sky_irradiance(df['timestamp'], lat, lon) df['csi'] = df['irradiance'] / df['clear_sky_irr'].clip(lower=1) # 3. 背板温度估算 df['cell_temp'] = df['temperature'] + df['irradiance'] * 0.026 * ( 1 - df['wind_speed'] / 10 ).clip(lower=0.5) # 4. 时间特征:太阳高度角、小时、季度 df['solar_elevation'] = calculate_solar_elevation(df['timestamp'], lat, lon) df['hour'] = df['timestamp'].dt.hour df['month'] = df['timestamp'].dt.month # 5. 滞后特征:前1个点出力、前4个点平均辐照 df['lag_power_1'] = df['power_pu'].shift(1) df['lag_irr_mean_4'] = df['irradiance'].rolling(4, min_periods=1).mean().shift(1) # 6. 剔除目标泄漏与无效特征后返回 valid_df = df.dropna(subset=['power_pu', 'lag_power_1']) features = ['irradiance', 'csi', 'cell_temp', 'wind_speed', 'humidity', 'solar_elevation', 'hour', 'month', 'lag_power_1', 'lag_irr_mean_4'] return valid_df[features], valid_df['power_pu']这段代码的关键参数有两个:背板温度估算的热系数 0.026 和滞后窗口的选择。热系数需要根据电站所在地的气候做微调,干燥地区偏低、潮湿地区偏高,调参时可以对比验证集误差。滞后窗口的选择依据是云层移动的时间尺度——15 分钟粒度下前 4 个点的辐照均值能捕捉约 1 小时内的云层变化趋势,如果数据粒度变成 5 分钟,这个窗口要相应放大到 12。
4.3 模型训练与评估:以 LightGBM 为例跑通第一版
特征矩阵构建完成后,选择 LightGBM 作为基线模型有三个理由:对缺失值有内部处理、对特征尺度不敏感、训练速度快便于反复迭代。但要注意 LightGBM 的随机性来自特征抽样和数据抽样,训练时需要固定随机种子,以保证实验结果可复现。
训练集与验证集的划分不能随机打散,光伏出力是强时间序列,随机划分会让模型看到未来数据,导致验证指标虚高。我按时间顺序切分:前 70% 做训练、后 30% 做验证,并且在验证集中保证跨季节覆盖。评估指标方面,RMSE 反映整体误差水平,MAE 更适合反映业务考核的绝对值偏差,而 R² 用于衡量模型对出力波动的解释能力。
import lightgbm as lgb from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score # X_train, X_valid, y_train, y_valid 按时间顺序切分 split_idx = int(len(X) * 0.7) X_train, X_valid = X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_valid = y.iloc[:split_idx], y.iloc[split_idx:] # 训练参数:树数量先给够,靠早停控制过拟合 model = lgb.LGBMRegressor( n_estimators=2000, learning_rate=0.05, max_depth=5, num_leaves=31, subsample=0.8, colsample_bytree=0.8, random_state=42 ) # 早停:观察验证集RMSE,连续50轮不下降则停止 model.fit( X_train, y_train, eval_set=[(X_valid, y_valid)], eval_metric='rmse', callbacks=[lgb.early_stopping(50), lgb.log_evaluation(100)] ) # 评估 y_pred = model.predict(X_valid) rmse = mean_squared_error(y_valid, y_pred, squared=False) mae = mean_absolute_error(y_valid, y_pred) r2 = r2_score(y_valid, y_pred) print(f'RMSE: {rmse:.4f}, MAE: {mae:.4f}, R2: {r2:.4f}')上面的参数中,subsample=0.8 表示每棵树随机使用 80% 的样本,colsample_bytree=0.8 表示每棵树随机使用 80% 的特征,这两个参数是 LightGBM 抗过拟合的主力。max_depth=5 与 num_leaves=31 的组合限制树复杂度,适合中小规模数据集。实际运行中,如果验证集 RMSE 不降反升,优先检查是否引入未来信息,其次是特征是否包含了过多冗余项,最后才调模型参数。
4.4 模型调优方向:哪些参数值得调、哪些是玄学
LightGBM 最值得调的参数是学习率、树的复杂度和正则化系数。学习率降低到 0.03 配合更大的 n_estimators 通常能带来小幅但稳定的精度提升;max_depth 和 num_leaves 决定模型能捕捉多复杂的交互效应;lambda_l2 正则项在特征数较多时有效抑制过拟合。早停轮数不要设太短,光伏数据有周期性,验证集误差曲线呈现锯齿状波动,50~80 轮的早停窗口比较稳妥。
不要过度调参。光伏出力预测的误差上界受限于辐照预报本身的精度,当验证集 RMSE 已经降到辐照预报误差对应出力范围时,继续调参只是让模型拟合训练集的噪声。此时更值得投入的方向是改进特征,例如加入地基云图信息或周边电站的辐照空间插值。
5. 气象因子与出力预测中的 6 个高频踩坑点
5.1 把实测辐照当预报辐照用,验证集完美、实盘全废
现象:模型在历史数据验证集上 RMSE 很低,切换成实时运行后预测误差明显变大,尤其是上午和下午时段。
原因:训练时用了当天实测辐照作为特征,而预测时只能拿到数值天气预报的辐照预报。实测辐照包含了已经发生的天气信息,预报辐照是对未来的估计,两者的误差分布完全不是一个量级。模型学到的映射关系在实盘输入分布变化时失效。
解决:训练与预测必须使用同分布输入。如果用 NWP 辐照做预测,训练时也只用 NWP 的辐照预报值(可以按发布时刻对齐),或者退一步,用实测辐照加噪声模拟预报误差。这样模型才能学到在预报条件下的条件期望。
5.2 温度取日平均,正午出力被系统性低估
现象:预测出力在中午时段整体偏低,上午下午相对准确,检查辐照特征无异常,但组件温度特征与出力的关系散点图出现明显分层。
原因:使用日平均温度或只取单一温度值作为特征,丢失了日内温度变化的信息。正午辐照最强时组件温度也最高,温度损失最大,平均温度无法反映这个时刻的降额状态。
解决:改用逐时刻温度序列,并加入背板温度估算特征。如果只有日平均温度数据,必须为模型提供辐照与温度的交互项特征,让模型有机会区分“高温高辐照”与“低温高辐照”两种状态的出力差异。
5.3 云量用离散等级编码,晴天预测出现阶梯状跳变
现象:预测出力曲线在上午出现台阶状跳变,不是平滑过渡,与实测曲线的连续变化不一致。
原因:云量特征使用人工观测等级(晴/少云/多云/阴),模型把云量当成了分类变量,输出在不同等级间切换时产生不连续的出力变化。
解决:改用连续气象变量替代离散等级,比如用总云量的百分比数值,或者更直接地使用辐照预报值。如果只能拿到离散云量,则不要把云量直接作为特征,而是把云量映射到晴空指数的预期衰减系数上,再做平滑处理。
5.4 训练集和验证集随机分割,指标好看到不敢信
现象:验证集 R² 在 0.95 以上,RMSE 极低,但部署到新电站或新时间段后精度显著下降,误差增大一倍以上。
原因:随机分割把同一天的前后时刻分别放进了训练集和验证集,模型看到了未来信息。光伏出力具有强自相关性,相邻时刻的出力高度相似,随机分割下的高指标是数据泄漏的假象。
解决:必须按时间顺序切分,并保证验证集在时间上完全晚于训练集。更严格的做法是采用滚动窗口回测,每次用过去 N 天训练、预测未来 M 天,多个窗口的平均误差才是可信的模型精度。
5.5 单面组件模型直接套到双面组件电站
现象:模型在其他电站表现不错,迁移到双面组件电站后整体预测偏低,阴天时偏低更明显。
原因:双面组件背面的散射辐照贡献了额外出力,通常占正面出力的 5%~15%,而模型的训练数据中没有背面辐照特征。阴天时散射辐照占比升高,偏差放大。
解决:特征工程中加入反照率特征(地面反射系数)或背面辐照估算值,将组件安装高度、地面类型(水泥地面/草地/沙地)编码为特征。至少要在模型中增加一个“双面增益”的修正项,把预测结果向上调整。
5.6 静稳天气下 NWP 辐照误差放大,预测系统性偏高
现象:在持续阴雨或雾霾天气中,模型预测出力持续偏高,且误差随着预报时效延长而增大。
原因:数值天气预报对静稳天气下的云层演变描述能力有限,云量预报往往偏乐观。模型以 NWP 辐照为关键输入,当输入系统性偏高时,输出必然跟着偏高。
解决:引入辐照预报的历史误差修正项。对 NWP 辐照预报值,按其历史预报偏差分布做分位数映射,把预报值校准到实测分布的对应分位。另一种做法是训练一个独立的误差修正模型,以 NWP 预报辐照、预报时效、季节为输入,预测实际辐照与预报辐照的偏差。
6. 进阶:从点预测到区间预测,再做滚动修正
当点预测模型稳定后,下一步值得做的是输出概率区间。电网考核和储能策略都希望知道“明天某个时刻出力大概率落在什么范围”,而不是一个单点值。分位数回归是工程上最直接的做法:把 LightGBM 的目标函数换成分位数损失,分别训练 10%、50%、90% 三个分位模型。训练分位模型时,目标函数为 pinball loss,它不像 MSE 那样对称惩罚高低误差,而是对低于分位和高于分位的预测赋予不对称权重,最终输出的三个分位数就构成了预测区间。
滚动修正则是另一个高性价比的进阶手段。光伏出力预测的误差在时间上是相关的——如果上午 10 点预测偏高,上午 11 点的预测大概率仍然偏高。利用这一特性,可以训练一个简单的误差修正模型:把当前时刻的预测误差作为特征,预测下一时刻的误差走向。实际落地时,我常用的做法是保留最近 4 小时的预测误差序列,用线性回归或小型树模型预测下个时刻的误差修正量,然后叠加到基线预测上。这个方法在超短期预测上效果明显,能把 RMSE 再压下去 5%~10%。
验证预测区间质量的方法与点预测不同,除了 RMSE 还要看区间覆盖率和区间宽度。90% 置信区间应当在实际出力值落在区间内的比例接近 90%,同时区间平均宽度不能过宽。我习惯用可靠性图检查:把预测样本按区间宽度分桶,统计每个桶内的实际覆盖率,如果覆盖率远低于预期,说明模型过度自信;如果区间宽得离谱,模型的概率预测就没有业务价值。
做这部分工作时的个人教训是:概率预测和滚动修正不是平行的两条路,而是先有可靠的基线与良好的特征体系,再做叠加才有意义。如果基线预测本身已经陷入了数据泄漏或特征错位的问题,任何进阶算法都只是放大了错误。先把时间切分、特征对齐、数据清洗这三件事做对,再考虑花哨的模型——这个顺序,我至少用三次翻车换来了。希望帮到你。
本文还有配套的精品资源,点击获取