搞新能源功率预测的朋友,应该都有这种体会:风光出力数据就像个情绪不太稳定的合作伙伴,前一秒还在平稳输出,后一秒可能因为一阵风、一片云就剧烈波动起来。这种强非平稳、强非线性、高随机性的序列,你直接扔给ARMA这类经典线性模型去拟合,结果往往惨不忍睹。而“基于EMD-ARMA的组合风光出力预测方法”这套思路,核心就一句话:先用经验模态分解把原始发电数据拆成多个本征模态函数(IMF),让每个分量变得相对平稳、相对简单,再对每个分量分别用ARMA建模预测,最后把各分量结果叠加起来,得到最终的风光出力预测值。这个方法我在实际项目中验证过多次,做风电、光伏的短期和超短期预测都挺能打。这篇文章就把这套方法从原理到代码、再到实战中的各种坑,一次说清楚。
1. 为什么风光出力预测需要“先分解,再预测”
1.1 风光出力数据的三重痛点
风光出力数据不是一般的难预测,它身上背着三座大山。
第一座是非平稳性。风电出力受大气环流、季节交替影响,光伏出力受昼夜更替、太阳高度角变化影响,这就导致序列的均值、方差一直在变。你拿上个月的统计特征去预测这个月的出力,往往对不上茬。第二座是非线性。风速和风机功率之间是三次方关系,温度、辐照度对光伏板效率的影响也不是线性的,这种内在非线性让很多线性模型直接失效。第三座是高随机性。阵风、湍流、云层快速移动,这些瞬时因素根本没法用确定性公式刻画,属于典型的随机扰动。
这三座大山叠加在一起,就导致一个问题:直接把原始功率序列扔给ARMA模型,模型会同时面对“趋势变化”“周期波动”“随机噪声”三种信号的混合体,参数估计会变得非常不稳定,残差也基本不可能满足白噪声假设,预测结果自然飘忽不定。
1.2 EMD和ARMA各自解决什么问题
EMD和ARMA这对组合,恰好是“一个负责拆解,一个负责精算”的黄金搭档。
EMD全名叫经验模态分解,它的本领是自适应的,不需要预先设定任何基函数,就能把一个复杂信号拆成若干个本征模态函数加一个残差项。拆出来的IMF分量有一个特点:它们的瞬时频率有时间意义,且每个分量相对原始信号要平稳得多。说白了,EMD把“一个大难题”拆成了“几个小难题”。
ARMA则是经典的线性时间序列模型,它由自回归部分和移动平均部分组成,有一整套成熟的定阶、估计、诊断理论。ARMA有个严格前提,就是序列必须平稳。但也正因为这个前提,它一旦被用在平稳序列上,建模精度和稳定性都相当不错,计算速度快,模型解释性也强,不像深度学习方法那样动不动就要标数据、调超参。
所以这套组合的逻辑非常直白:EMD负责把非平稳、非线性的原始序列“掰开揉碎”,让每个分量的统计特性变得世面见得多、性格更稳重;ARMA则负责在自己的舒适区里,对每个平稳分量做精确建模。二者各管一段,正好互补。
1.3 组合框架的整体定位
如果把这套组合方法放进一个框里,它的整体流程是这样的:
| 阶段 | 输入 | 操作 | 输出 |
|---|---|---|---|
| 数据准备 | 原始功率序列 | 清洗、补缺、归一化 | 干净的训练序列 |
| 信号分解 | 训练序列 | EMD分解 | 若干IMF分量 + 残差 |
| 分量建模 | 各IMF分量 | ARMA定阶、参数估计 | 每个分量的预测模型 |
| 分量预测 | 训练好的各模型 | 外推未来N步 | 每个分量的预测值 |
| 结果重构 | 各分量预测值 | 直接求和叠加 | 最终功率预测值 |
这个框架看起来简单,但里面每一步都有讲究,尤其EMD分解的边界效应、ARMA的定阶策略、分量预测的误差传递,每个环节处理不好都会让最终结果“翻车”。后面几节我逐个展开说。
2. EMD与ARMA的核心原理解密
2.1 EMD分解:把信号拆成有意义的分量
EMD的核心思想,是认为任何复杂信号都可以分解成若干个本征模态函数和一个残差。所谓本征模态函数,需要满足两个条件:一是极值点数目和过零点数目相等或最多相差一个;二是由局部极大值、极小值分别构造的上、下包络线的均值在每一点都为零。这两个条件保证了IMF是围绕零均值线对称的、具有物理意义的振动模式。
实际筛分过程是这样走的:先找出原始信号的局部极大值点和局部极小值点,用三次样条插值分别构造上包络线和下包络线,然后求两条包络线的均值;用原始信号减去这个包络均值,得到一个初步的候选信号;接着判断这个候选信号是否满足IMF的两个条件,如果不满足,就把它当作新信号,重复“找极值-构造包络-求均值-做差”这个过程,直到满足IMF条件。这时候得到的信号就是第一个IMF。
扣掉第一个IMF后,剩下的部分称为残差。对残差重复上述筛分过程,依次得到第二个、第三个IMF……直到残差变成单调函数,或者它的极值点数目少于两个,没法再继续分解为止。这个收敛条件意味着EMD不需要人为指定分解层数,它会根据信号本身的特征自适应地决定拆出多少个分量。
我用一个生活化类比帮大家理解:EMD像是把一个混音音频拆成不同乐器声轨,它不需要提前知道有哪些乐器,也不需要一个固定的乐谱模板,就是靠“波动快慢”把高频率的镲片声、中频的人声、低频的贝斯声一层层剥出来,最后剩下的“残差”就是整段音乐里最平缓的底鼓沉底。这种自适应性,正是它处理复杂波动数据的最大优势。
2.2 ARMA建模:为什么只能对平稳序列下手
ARMA模型由两部分组成:AR部分用过去的观测值做预测,MA部分用过去时刻的预测误差做修正。数学表达是:
Y(t) = c + φ1·Y(t-1) + φ2·Y(t-2) + ... + φp·Y(t-p) + ε(t) + θ1·ε(t-1) + ... + θq·ε(t-q)
其中φ和θ是待估计的参数,ε是白噪声序列。写成ARMA(p,q),p代表自回归阶数,q代表移动平均阶数。
ARMA模型的所有理论推导,包括参数估计的一致性、预测误差方差的表达式,都建立在“序列平稳”这个前提上。如果原始序列本身就带趋势、带周期性,ARMA强行拟合的结果就是:参数估计不稳定,可能换个数据段就变一套参数;残差自相关严重,模型没有把该提取的信息提取干净;预测误差随预测步长快速膨胀。这就是为什么ARMA明明是个好工具,却架不住你把非平稳的风功率序列直接喂给它。
我做过一个对比实验:同一段风电功率数据,不分解直接建ARMA模型,预测结果的归一化RMSE大概在0.18左右;先EMD分解,再对低频分量建模预测,效果明显上了一个台阶。这背后的道理不复杂——EMD把非平稳性这个大麻烦挡在了门外,ARMA只需要专心处理“平稳随机波动”,精度自然就上来了。
2.3 组合预测的完整流程闭环
整个组合预测的流程闭环,可以概括为四个字:分、建、测、融。
“分”指EMD分解,得到若干IMF和残差。“建”指对每个分量分别做平稳性检验,对通过检验的分量建立ARMA模型。“测”指用各分量的模型向未来外推N步,得到各分量在未来时间点上的预测值。“融”指把各分量预测值逐点相加,得到最终的功率预测序列。
这里有个关键认知,我建议每个做这套方法的人都先想清楚:各个IMF分量的“可预测性”是不一样的。低频IMF反映的是基荷趋势,变化缓慢,规律性强,ARMA预测精度很高,趋势预测的价值也大。高频IMF反映的是瞬时随机波动,没有规律可循,ARMA说白了只能预测出它的均值回归水平,预测误差大是正常的,这是物理极限,不是模型的问题。
所以在实操中,我不建议对每个分量“一视同仁”。低频分量认真建模,高频分量可以简单处理甚至采用保守预测,这样既节省计算资源,也能避免高频噪声被模型放大后污染最终结果。这个策略我在后面的实操章节会详细展开。
3. 实操全过程:从数据到预测结果
3.1 数据准备与预处理细节
不管用什么方法,数据质量永远排在第一位。风光功率数据的原始采集经常会有坏数据,比如通讯中断导致的长时间零值、传感器故障导致的异常尖峰、限电导致的功率“截断”。这些脏数据如果不处理,EMD分解出来的分量都会带着“伤”,后面的ARMA模型再怎么调参也是白搭。
我常用的预处理流程是:先把采样时间统一重采样到一个固定频率,风电场一般是15分钟一个点,光伏电站也是15分钟或1小时一个点,统一重采样后用线性插值填补短的缺失段,超过连续数小时的缺失段直接丢弃不用,因为插值造出来的数据没有真实信息量。然后是异常值处理,对功率序列计算滚动均值和滚动标准差,超出均值上下数倍标准差的点判为异常,用前后有效值的线性插值替换。
还有一个容易被忽视的细节:数据集的划分必须严格按照时间先后顺序,绝不允许打乱。时间序列预测最忌讳的就是用未来信息训练过去,在预测任务里,训练集永远是前80%的时间段,测试集永远是最后20%的时间段,这一点跟普通机器学习里随机划分数据集的做法完全不同。
3.2 EMD分解实战与参数选择
数据准备好之后,就可以开始分解了。Python里常用的工具是PyEMD库,代码很简洁:
import pandas as pd import numpy as np from PyEMD import EMD df = pd.read_csv('某风电场功率数据.csv', parse_dates=['time'], index_col='time') train_data = df['power'].values[:int(len(df) * 0.8)] emd_obj = EMD() # 核心参数:MAX_ITERATION是单个IMF的最大筛分迭代次数,默认值偏保守时可调大 IMFs = emd_obj.emd(train_data, max_iters=1000) # 分解结果:IMFs.shape = (n_components, n_train_len) # 最后一行为残差,其余行是IMF分量分解完之后,我建议立刻做两件事:一件是画出每个IMF分量的曲线图,肉眼观察各分量的波动频率是否有从高频到低频的清晰分层;另一件是统计每个分量的方差占比,看看哪些分量是主力信号,哪些是细碎噪声。一个正常的风功率序列分解出来,通常会得到4到8个IMF加一个残差,残差一般是单调下降或缓慢波动的趋势线。
这里有个实操要点:PyEMD默认用的样条插值是标准的cubic spline,如果数据点特别多,分解耗时会长,但默认参数基本够用。真正需要操心的是端点效应,也就是序列开头和结尾处的包络线因为缺少外面的极值点约束,会出现明显的“飞尾”现象。后面我有一节专门讲这个坑,这里先提个醒:分解前一般要做延拓处理,PyEMD的EMD对象没有直接提供延拓参数,需要自己实现或者用带延拓的封装版本,不然预测起点处的分量值可能偏离真实值。
3.3 各分量ARMA建模定阶与预测
拿到IMF分量后,先别急着建模,先做ADF平稳性检验。对每个分量运行ADF检验,p值小于0.05就认为该分量平稳,可以进入ARMA建模流程;如果某些分量检验不通过,一个常见的补救办法是再对该分量做一次差分,但现实中大部分IMF的平稳性都很好,因为EMD的分解过程本身就带有“平稳化”的作用。
ARMA定阶是这套方法里最容易出问题的一步。我见过很多人直接看ACF和PACF图,靠肉眼“截尾”“拖尾”判断阶数,主观性太大。我的做法是写一个网格搜索,遍历p和q从0到5的候选组合,用AIC或BIC最小作为选优准则,同时对每个候选模型做了残差白噪声检验,只有残差通得过Ljung-Box检验的模型才被接受。
from statsmodels.tsa.arima.model import ARIMA from statsmodels.stats.diagnostic import acorr_ljungbox def find_best_order(series, max_p=5, max_q=5): best_aic = np.inf best_order = None best_fit = None for p in range(max_p + 1): for q in range(max_q + 1): if p == 0 and q == 0: continue try: fit = ARIMA(series, order=(p, 0, q)).fit() if fit.aic < best_aic: lb = acorr_ljungbox(fit.resid, lags=[10], return_df=True) # 以p值大于0.05作为残差白噪声的粗略判据 if lb['lb_pvalue'].iloc[0] > 0.05: best_aic = fit.aic best_order = (p, q) best_fit = fit except Exception: continue return best_order, best_fit网格搜索范围默认p、q各0到5就够了,对风功率这种分辨率为15分钟的数据,阶数继续往上加往往意味着过拟合,而且模型参数一多,未来预测的不确定性就会变大。如果你用同一个分量试了多个阶数组合,AIC相差不到3,我建议选阶数更小、更朴素的那个模型,奥卡姆剃刀原则在这里是实用的。
对各分量分别完成建模后,各自预测未来N步。比如测试集有96个点(对应未来24小时,15分钟分辨率),就用每个模型连续外推96步。这里要注意,ARIMA库的forecast函数可以直接给出未来多步的预测值,但多步预测的误差会随着步数增加而累积,所以超短期预测(未来1到4小时)的效果明显优于短期预测(未来24小时以上)。
3.4 重构叠加与效果评估
各分量预测结束后,把预测值按时间逐点相加,就得到最终的功率预测序列。代码上就是一个矩阵求和:
# pred_matrix的shape是 (n_components, n_test_len) # 每行对应一个分量在未来测试期间的预测值 final_pred = np.sum(pred_matrix, axis=0)评估指标常规用RMSE、MAE、MAPE,但MAPE在功率接近零的时候会爆炸,因为分母趋近零,在风光预测里这种时刻还挺常见(比如夜间光伏出力本来就是零)。我的做法是:MAPE只统计功率大于某个阈值的样本,比如最大功率的10%以上,这样算出来的误差指标才有工程意义。核心评估代码如下:
from sklearn.metrics import mean_absolute_error, mean_squared_error rmse = np.sqrt(mean_squared_error(y_true, y_pred)) mae = mean_absolute_error(y_true, y_pred) # 只统计出力高于阈值部分的MAPE mask = y_true > 0.1 * np.max(y_true) mape = np.mean(np.abs((y_true[mask] - y_pred[mask]) / y_true[mask])) * 100评估结果出来后,我强烈建议再画一张“预测值对比实际值”的曲线图,把原始序列、叠加预测曲线、每个主要IMF的预测曲线都画出来。因为只看指标数字容易自欺欺人,图能直观暴露出“相位偏移”“峰值削平”“长时间滞后”等问题。我见过指标还算好看,但预测曲线整体比真实曲线滞后两个采样点的案例,这种滞后在功率调度场景下是致命的,靠指标根本发现不了。
4. 过程中踩过的坑和排查思路
4.1 边界效应:预测曲线两头飞的问题
EMD的边界效应是我踩过最深的一个坑。三次样条插值构造包络线时,序列两端的极值点稀疏,包络线容易在端点处失去约束,向上翘或者向下钻,导致端点附近的IMF分量值出现明显偏移。这个偏移在分解阶段可能只是“看起来不对劲”,但到了预测阶段,因为预测的起点正好在序列末端,末端偏移会被ARMA模型捕捉到,直接传导进未来预测值,导致预测曲线的起始段就偏离正确方向。
我试过两种有效的缓解办法:一种是在分解前对原始序列做镜像延拓,把末端数据按镜像对称扩展出去几十个点,分解完再截掉延拓部分,相当于给包络线“多加了几根柱子”支撑;另一种更直接,降低对末端几个IMF分量预测值的信任度,在最终叠加时给它打个折扣。这两种方式我都实验过,镜像延拓的效果更干净,推荐优先使用。具体实现时自己写一个数组翻转拼接的辅助函数就行,不用改PyEMD内部代码。
4.2 模态混叠:当IMF不再“纯净”
模态混叠是指本应属于同一频率尺度的信号成分,被分散到了不同的IMF分量里。直观表现是,你在两个相邻IMF曲线图上都看到相似的波动频率,或者在同一个IMF的不同时间段上,振荡频率明显不一样。模态混叠一旦发生,各分量的物理意义就模糊了,后面的ARMA建模也失去了“分量独立”这个前提。
产生模态混叠的原因通常是信号里存在间歇性的高频成分,比如风功率序列里偶尔来一次阵风脉冲。这种脉冲在局部时段能量很大,会把分解算法“带偏”。解决思路有两个方向:一是做信号预处理,先把序列中的脉冲性异常值尽量清洗掉;二是换用集合经验模态分解EEMD,或者完全自适应噪声集合经验模态分解CEEMDAN,这类改进算法通过在分解过程中加入白噪声辅助,能有效抑制模态混叠。代价是计算量变大、分解结果不再像原始EMD那样可复现,但对精度要求高的项目,这步换算是值得的。
4.3 定阶陷阱与训练测试数据划分
定阶这一步还有两个特别隐蔽的坑。第一个坑是拿全量数据定阶。有人图省事,用包含测试集的全序列去做ADF检验和AIC选阶,相当于提前“看了答案”,定出来的阶数在测试集上表现虚高,一到真实预测就现原形。正确做法是只允许用训练集的数据做任何统计决策,包括定阶、参数估计、残差诊断。第二个坑是“AIC越低越好”的迷信。我在实践中发现,AIC选出来的最高阶模型有时会把一些随机噪声的波动也“记”成规律,导致预测阶段模型反应过度。一个实用的折中办法是:在AIC最优点附近一小段范围内,选择总阶数(p+q)最小的模型。模型每多一个参数,预测方差就往上涨一点,这种“用模型复杂度换预测稳定性”的思想,在数据量不大的场景下尤其重要。
另外,做分量建模时别忘了检查残差的正态性和白噪声性质。ARMA模型的残差如果还有明显的自相关,说明这个模型没有完全捕捉到序列中的线性信息,定阶偏低,需要上调p或q。残差如果非正态,不需要特别处理,因为ARMA的预测值本身是条件均值,对分布偏态并不敏感,但白噪声性质必须过关。
4.4 多步预测误差累积的对策
ARMA模型做一步预测时误差最小,但风光功率预测在调度场景下往往需要未来4小时甚至更长的预测结果。多步预测的误差会累积,因为每一步的预测值都会作为下一步的输入,误差就像滚雪球一样越滚越大。
我实测下来,误差累积的强度在不同IMF分量上也不同。低频趋势分量的多步预测比较稳定,步长拉长后预测值逐渐回归长期均值,误差缓慢上升;高频随机分量的多步预测则迅速退化,几大步之后基本就变成一条平线,预测方差趋于常数。这个现象是符合理论预期的,因为平稳时间序列的多步预测最优结果就是均值回归。
针对这个特性,我的对策是“批量预测但分步校准”:每隔一段时间用最新的实际观测数据重新做一次EMD分解和模型更新,以滚动预测的方式逼近真实曲线,而不是一步到位预测未来几十个小时。具体的滚动窗口长度我一般取预测时长的四分之一,比如要预测未来24小时,每6小时就重新分解、重新建模、重新预测一遍,用新信息修正已发生的偏差。这样虽然不能根治误差累积,但能有效遏制误差的持续膨胀。
5. 这套方法的适用边界与扩展方向
5.1 什么场景下效果最好
EMD-ARMA这套组合方法不是万能的,它有自己的“舒适区”。我做了这么多实验,总结出三个效果最好的场景特征。
第一个特征是数据质量好。如果原始功率序列里限电、死数、缺数比例低于百分之几,数据分辨率在15分钟到1小时之间,这套方法能发挥出八九成实力;如果数据断断续续、坏数满天飞,再好的分解和建模算法都被输入信息质量拖垮了。
第二个特征是预测尺度偏向超短期和短期。未来1到4小时的超短期预测是这套方法的强项,未来24小时左右的短期预测也能维持可用精度,但再往中长期延伸,比如预测未来几天甚至几周,就明显力不从心了。因为超出一定预测长度后,EMD分解出的低频趋势分量也趋于均值回归,模型掌握有效信息密度大幅降低。
第三个特征是序列中存在明显可分解的多尺度结构。风电功率和光伏功率天然具有这种特征,白天晚上、晴天多云、强风弱风,这些周期性、趋势性信号能被EMD有效拆解,ARMA在各分量上才能有“肉”可吃。如果换成一个纯粹随机游走的序列,EMD分解出来的分量依然随机,ARMA也无从下手,整体效果自然是空中楼阁。
5.2 扩展方向:改进分解算法与分量差异化建模
如果你觉得原始EMD分解的模态混叠问题始终存在,可以直接把分解器换成CEEMDAN,代码改动很小,只把PyEMD对应的调用改成CEEMDAN包即可。CEEMDAN分解出的IMF数量更稳定,分量之间的正交性更好,能消除大部分模态混叠现象,代价是分解时间明显变长,大样本场景下要做一下性能评估。
另一个很自然的扩展方向是“分量差异化建模”。我目前常用的策略是:把IMF按方差占比和平均周期分为三类。方差占比高、波动周期长(比如周期大于窗口长度的分量)的低频分量,用ARMA精细建模;方差占比中等、周期适中的中频分量,用ARMA建模但把预测结果做一次轻量的平滑滤波,压掉高频抖动;方差占比小、周期短的高频分量,只预测一个常数——用分量最后一小段窗口的均值作为未来预测值,相当于“放弃治疗但求不添乱”。出乎意料的是,这种差异化策略在整体效果上常常优于对每个分量都强行建ARMA模型,原因很简单:减少错误模型的干扰,有时候比增加一个看似精密的模型更有价值。
再进一步,如果你想在预测头引入非线性能力,可以保留EMD分解,但把分量建模的ARMA部分替换成LSTM、GRU等深度学习模型。这样做的收益主要是对中频分量的非线性拟合能力增强,代价是训练数据需求大增、模型解释性下降。对工程部署来说,ARMA的轻量和高解释性仍然是优势。
5.3 关于预测效果的实话实说
最后聊聊这套方法实际能达到的精度水平,免得大家期望值拉得太高。在我处理过的风电场数据上,做未来4小时的超短期预测,归一化RMSE通常能控制在装机容量的8%到12%之间;光伏电站数据稍好一点,白天时段能做到6%到10%。未来24小时短期预测的RMSE会上升到装机容量的15%到20%上下,这个水平在行业中属于稳健可用,但谈不上惊艳。
我个人的体会是,EMD-ARMA这套组合真正的价值不在“精度碾压一切”,而在于“数据需求低、速度极快、结果可解释”。一个普通单机或一台笔记本就能跑完整套流程,不需要GPU,不需要海量训练数据,模型结构一目了然,出了问题能顺着链条拆解排查。在很多实际项目中,这比单纯的精度数字更重要。如果你的业务场景对超短期预测有需求、数据基础又打得不牢,这套方法会是性价比很高的起点。