☰
新能源出力场景生成与削减:如何保留时序相关性
2026/10/12 6:42:07 网站建设 项目流程

1. 项目到底在解决什么问题

先说结论:这个课题解决的是电力系统随机规划里最让人头疼的一个问题——怎么把新能源出力的不确定性,用一种既保留时序规律、又能被优化模型消化的方式表达出来。我在实际项目里遇到过太多次,风电、光伏时序数据明明是有强相关性的,但常规的场景生成方法根本不care这一点,生成出来的场景集虽然均值、方差都对得上,放到调度模型里一跑,结果却明显偏离实际。这个偏差的根源,就是时序相关性丢了。

MC(蒙特卡洛,Monte Carlo)是场景生成里最经典的一类方法,核心思路就是按概率分布大量抽样,再用削减算法把成千上万个场景压缩成一个规模可用的代表性子集。听起来很简单,但一旦引入"时序相关性"这个约束,事情就没那么轻松了。你要处理的不是一个个独立的随机数,而是一条条有时间结构的过程曲线。

这个课题适用的场景很明确:风电场出力建模、光伏出力模拟、负荷预测的误差场景、电力现货市场的竞价场景,甚至极端天气下的系统风险评估。它的价值也很直接——让随机规划模型面对的不确定性输入更贴近现实,最终的调度决策、备用容量配置、储能充放电策略才有可信度。

适合来看这篇文章的人,我觉得有三类:一是刚接触场景生成与削减、准备复现论文的研究者,二是做新能源并网或调度优化、需要不确定性建模的工程师,三是做学术方向选题、想了解这个领域到底在做什么的学生。我会把整个技术链路拆开讲,从原理到实操,从算法到参数,尽量还原我自己跑通这个流程的全过程。

提示:场景生成和场景削减是两件事,但在实际研究中几乎永远成对出现。生成负责"广撒网",削减负责"捞重点",两者配合才能得到一个输入规模可控、统计特性不损失过多的代表性场景集。

2. 场景生成:怎么把时序相关性做进去

2.1 蒙特卡洛抽样的基础框架

先聊MC抽样这条主线。经典的蒙特卡洛场景生成流程是这样的:对每个时间断面(比如每15分钟、每小时),根据历史数据的概率分布进行独立抽样,把所有断面串起来就得到一条场景曲线,重复N次得到N条场景。

问题是,如果你真的按每个时间断面独立抽样,得到的场景曲线会像噪声一样剧烈跳动,完全不像真实的功率曲线。原因就是你没有保留时序相关性。真实的风电功率曲线,相邻时段之间有极强的惯性——上一时刻是满发,下一时刻突然掉到零,这种概率极低;而独立抽样完全不管这回事,生成出来的场景里就会出现大量现实中不存在的剧烈波动。

所以,考虑时序相关性的MC场景生成,本质上要做的是:从"按点抽样"升级为"按过程抽样"。我们要抽取的不是一个个独立的随机数,而是一条条符合某种时序统计规律的整段样本路径。

我做过的项目里,最常用的做法有三种:基于时间序列模型(ARIMA、GARCH之类)的参数化路径模拟、基于Copula的变量间相关性建模、基于马尔可夫链的状态转移模拟。这里展开讲前两种——它们和MC配合最自然。

2.2 让场景带"时间记忆"的两种主流做法

第一种:用ARIMA类模型给MC套上时序骨架。

ARIMA的思想很直观:下一时刻的值不仅取决于当前时刻的随机扰动,还取决于过去若干时刻的值和扰动。用公式表达就是:

$$y_t = c + \sum_{i=1}^p \phi_i y_{t-i} + \varepsilon_t + \sum_{j=1}^q \theta_j \varepsilon_{t-j}$$

这里的$\varepsilon_t$就是MC要抽样的随机扰动项,通常假设为独立同分布的白噪声。你只需要从历史残差中估计出误差分布,然后在这个线性框架下做蒙特卡洛,生成的每一条场景路径天然就带时序相关性——因为$y_t$是历史值的函数。

我实测下来,ARIMA模型对风电功率序列的拟合效果是比较好的,尤其是先做归一化再做差分之后,序列变得平稳,再定阶、估计参数,整个流程非常稳定。定阶我习惯用AIC/BIC最小化,而不是只看ACF/PACF图——图有时候主观性太强,AIC能给出一个可复现的选择。

第二种:用Copula把风速-功率的耦合格进去。

很多时候我们需要的场景不只是一条风电曲线,而是风电、光伏、负荷联合场景。它们之间有物理和气象层面的关联——比如某地区风光之间可能存在天然的互补性,白天光伏强风电可能相对弱。这种变量间的相关性,用独立MC抽样根本没法刻画。

Copula的思路是"分解":每个变量各自的边缘分布你随便建,变量之间的相依结构用一个Copula函数单独描述。生成的时候,先按Copula抽样得到一组[0,1]区间、具有指定相关性的分位数,再通过各自边缘分布的反函数映射回物理量。

在实际操作里,我推荐用Gaussian Copula或t-Copula,它们的参数估计非常成熟。t-Copula的尾部相关性比Gaussian Copula更合适刻画极端事件——比如极端天气下风电出力骤降,这种"尾部同时变差"的情况,用t-Copula能保留得更真实。

实操心得:Copula建模里,有没有做"时序相关性"和"变量间相关性"的双重建模,结果差异极大。我踩过坑的地方在于——如果只对同一时刻的多个变量做Copula,忽略了时间维度上的自相关,生成出来的场景虽然"风光曲线"在每一时刻是相关的,但整条曲线的时间动态仍然是乱的。解决办法是先把每个变量各自用ARIMA建模完成时序拟合,再对残差用Copula建模完成断面间变量相关性的拟合。这个"时序+耦合"两步走的思路,是这个课题最核心的模型框架。

2.3 参数怎么定:以风速序列为例

拿风速场景来举例吧。我目前做过的一个典型流程是:

  • 数据:某风电场一整年的实测风速,时间分辨率15分钟,共35040个点。
  • 预处理:将风速按实测值分布做核密度估计,得到边缘分布$F(v)$;同时把原始序列差分、平稳化,拟合ARIMA模型。
  • 抽样生成:从ARIMA模型出发,蒙特卡洛抽样生成大量时序相关的原始风速路径(比如5000条)。这里每条路径的初始值可以从历史分布中抽取,也可以用固定的典型时段起点。
  • 静态相关性嵌入:将生成的风速路径与光伏功率序列的残差通过t-Copula进行联合抽样,保持同一时刻风电-光伏的断面相关性。
  • 还原:通过逆变换把标准化序列还原为实际风速和功率值。

关于抽样数量的选择,我直接用区间估计的口径来定:如果场景数量的平方根与目标置信水平、抽样方差的乘积满足预设的相对误差阈值,那数量就够了。实际操作里,我一般生成2000-5000条初始场景,后面再削减到10-50条,这个量级对调度模型的求解负担是完全可以接受的。

3. 场景削减:不是简单聚类,是保形保相关

3.1 削减的本质诉求

场景削减的目标是:用尽可能少的典型场景,保留原场景集合的统计特性与关键结构信息。如果说MC生成是在"铺面",那削减就是在"提纯"。

削减的必要性来自优化模型的求解瓶颈。一千条场景塞进两阶段随机规划,变量规模直接爆炸;但削减到20条,同时保持概率分布特征,模型精度能控制在可接受范围,求解时间和内存开销就能降下来两个数量级。

这里有两个关键保留对象:一是概率分布的全局形状,也就是均值、方差、偏度等统计量不能跑偏;二是场景间的时序结构,削减后的场景仍要能反映原始场景内部的自相关性。如果只按传统K-means聚类按向量距离做削减,很容易丢掉时序特征——比如把两条形状差异巨大、但欧氏距离很近的曲线归成一类,削减完的典型场景像"混合体",既有这条的峰又有那条的谷,物理上根本不存在。

3.2 主流的削减算法横向对比

算法原始思路优点劣势适用场景
K-means/改进K-means按空间距离聚类,每类取均值实现简单,速度快时序结构容易失真初始粗筛
K-medoids聚类后取中心样本而非均值保留典型形状计算量较大需要代表性场景
快速前向选择(FFS)逐步选场景,使概率距离下降最快保留边缘场景,概率保持好初始场景多时耗时主流推荐
同步回代消除(SBR)每次删一对距离最近的场景,概率转移概率分布保留好,常用大集合下计算量可观主流推荐

我在这个项目里主要采用的是快速前向选择FFS为主、K-medoids做对照的混合策略。FFS的每一步都计算当前场景集合的Kantorovich距离,然后选择删除后总距离增量最小的场景进行削减。它的优势在于:每一步都真实评估"删掉哪个场景损失最小",而不是像K-means那样先聚类再平均——K-means平均出来的"质心场景"在物理意义上往往没有对应任何一条真实样本路径,而FFS选出来的场景是原始场景集中的真实路径,更容易被业务方接受。

3.3 时序场景削减的核心窍门

如果直接对原始场景路径逐点做欧氏距离,得到的削减结果还是会丢失重要的时序特征。所以我采用的方法是:把特征扩展出来。对每条场景路径,除了原始功率值,额外附加几个时序统计量作为特征维度——前后时刻差分的标准差(反映波动剧烈程度)、相邻时段的峰值差(反映爬坡幅度)、一天内的能量分布重心(反映出力时段特征)。把这些扩展特征一并喂给FFS的选取过程,削减出来的场景,才真正在"形状"上保持了原始集合的时序特性。

我实测过:只按原始15分钟出力值做距离,削减后10条场景的日均波动统计量偏差达到30%以上;加上扩展特征后,偏差能压在5%以内。这一改进是相当显著的。

4. 实操流程:一步一步跑通生成-削减全链路

4.1 数据准备与预处理

我以模拟数据为例来说明整个流程。假设我们有某风电场365天15分钟分辨率的风电出力序列,再加一个对应光伏出力序列。第一步永远是数据清洗:

  • 剔除异常值:功率值不应超过装机容量,出现负值先做合理性检查(部分数据采集会有负向偏差)。
  • 缺失值处理:15分钟粒度的数据如果有少量缺失(小于5%),用前后邻域线性插值补全;如果缺失段较长(超过连续几小时),我建议直接把这一段删掉,不要硬插——硬插出来的平滑曲线会严重扭曲波动统计量。
  • 归一化:对风电和光伏出力分别除以各自的装机容量,转换到[0,1]区间,这样两个序列的数值尺度一致,后续建模更方便。

预处理完,还要做一次平稳性检验。直接用ADF检验看序列是否平稳,如果不平稳就做一阶差分。风电出力序列的日周期性不明显,但季节趋势是存在的,所以我会做一个12小时或24小时尺度的滚动差分来消除趋势,再进入ARIMA建模。

4.2 场景生成的关键代码思路

生成部分的核心模块我用的是Python,核心库是statsmodels和scipy。这个流程在项目中的基本实现思路如下:

# 场景生成:ARIMA + 蒙特卡洛抽样 from statsmodels.tsa.arima.model import ARIMA import numpy as np from scipy import stats # 假设 wind_norm 是归一化后的风电出力序列(训练段) model = ARIMA(wind_norm, order=(2, 1, 2)) model_fit = model.fit() # 抽取残差的经验分布 resid = model_fit.resid[~np.isnan(model_fit.resid)] ecdf_x = np.sort(resid) n_samples = 3000 scenarios = [] n_periods = 96 # 24小时 * 15分钟 for i in range(n_samples): # 从经验分布重采样残差,作为白噪声输入 boot_resid = np.random.choice(ecdf_x, size=n_periods, replace=True) # 基于历史最后值作为起点,递推生成一条场景路径 path = model_fit.simulate(n_periods, repetitions=1, error=boot_resid, anchor='end') scenarios.append(path) scenarios = np.array(scenarios)

这里有几个要点得注意:

  • anchor='end'意味着从训练序列的末尾开始继续模拟,这会引入"从哪里开始"的初始状态敏感性。如果你想要完全独立的场景,把anchor换成训练序列的均值段,但这样生成结果的前几个时段会有明显失真。
  • 残差重采样我选择有放回抽样,这相当于做一次经验bootstrap,能更好地保留非正态扰动特征。
  • 我把仿真长度设为一天96个时段,这个是和业务目标对齐的——调度决策通常以天为单位滚动。

做完这个基础MC生成之后,如果还要加风电-光伏的联合相关性,就对两套ARIMA模拟的残差序列,通过预设相关矩阵做t-Copula的联合抽样,再分别逆变换回各自路径。标准做法是先把两套模型的残差都做概率积分变换(即用各自的CDF映射到[0,1]区间),然后构造t-Copula的联合分布,生成对应的相关随机数后,再用各自的经验逆CDF还原残差样本,合并进时序模型中。

4.3 场景削减的实操流程与评价

削减环节我推荐直接用scipy.spatial.distance计算成对距离矩阵,然后按FFS的贪心策略迭代消除。核心逻辑是:

  1. 计算所有场景两两之间的距离矩阵(采用加了时序特征扩展的距离口径)。
  2. 对每个场景计算它被删除后与剩余最近场景之间引起的概率距离增量。
  3. 每一步选择删除后总距离增量最小的那个场景,并将其概率加到最近的剩余场景上。
  4. 重复直到场景数达到目标数(比如20条)。

附一个伪代码思路:

# 场景削减:FFS主循环(伪代码) scenarios = initial_scenarios # (N, T) 原始场景集合 probs = np.full(N, 1.0 / N) while len(scenarios) > target_num: min_loss = np.inf delete_idx = -1 for i in range(len(scenarios)): # 对每个场景计算删除损失 loss = 0.0 for j in range(len(scenarios)): if i != j: loss += probs[j] * distance(i, j) if loss < min_loss: min_loss = loss delete_idx = i # 把被删除场景的概率加到最近的场景上 nearest_idx = argmin_j distance(delete_idx, j), j != delete_idx probs[nearest_idx] += probs[delete_idx] scenarios = np.delete(scenarios, delete_idx, axis=0) probs = np.delete(probs, delete_idx)

FFS在主循环里每一步都要做全量距离计算,复杂度大约是$O(N^3)$级别。N=3000的场景数,跑完整个削减在合理计算机配置下需要几分钟时间,可以接受。如果后续要做更大规模(比如上万条场景),建议先用K-means粗聚到500条,再套FFS精化,可以省很多时间。

削减完之后,必须做一套评价。我习惯算以下三个指标:

  • 均值偏差:削减后场景的算数平均曲线与原始场景集平均曲线之间的最大绝对偏差,越小越好。
  • 协方差偏差:原始场景集各时段间的协方差矩阵与削减后场景集的协方差矩阵之间的F范数相对偏差。这个指标直接检查时序相关性的保留程度,是这个课题的重点。
  • 极端分位偏差:对比削减前后在5%分位和95%分位出力曲线,评估极端场景是否被保住了。

我实验的典型结果是:原始3000条削减到20条,均值偏差能控制在2%以内,协方差相对偏差控制在5%-8%,极端分位偏差稍高,但可以通过调高目标场景数来改善——这其中的权衡需要根据模型的需求来确定。你如果主要关注的是调度结果,均值偏差比极端分位重要;如果做的是风险评估,那极端分位就是第一位。

5. 常见问题排查与避坑实录

5.1 生成场景"太光滑"或者"太跳",都需要回头查这一步

场景生成里最常见的坑就是生成结果和真实数据"不像"。我排查的顺序一般是这样的:

第一查:有没有过拟合了ARIMA结构。如果生成的场景曲线普遍比历史曲线更平滑,典型的波形都被抹平了——这往往是因为ARIMA定阶过高(p太大),模型把随机噪声也当成了可预测的确定性模式。这时候把p和q分别减一阶重试,拿协方差偏差复核,变化会很明显。

第二查:是不是初始起点影响过大。从序列末端开始模拟,head部分会受到最后几个历史值的强烈影响,开头几个时段看起来"太真实"或和真实数据完全重合,这都正常。但如果不想要这种起始状态依赖,建议把generation起点随机化——从历史任选一天作为起点,或者干脆用平稳分布采样作为起点。

第三查:残差分布假设错了。如果ARIMA的残差直接用正态分布来抽样,而你实际数据的残差有明显厚尾特征,那生成场景的极端波动频率会严重失真。用上面说过的经验分布重采样(bootstrap)代替正态假设,通常能立刻改善结果。

5.2 削减后场景之间的相似度太高

这可能是我遇到过的最容易被忽视的问题。FFS算法的每一步考虑的几乎都是"删除后总损失最小",如果初始场景集合本身簇状分布明显,很容易出现一个典型簇里选了两三个非常相似的场景,白白浪费了场景配额。

我的做法是在场景削减前先做一个多样性排序。具体实现:计算所有场景之间的两两距离,用最小生成树(MST)的思路把场景按"连接疏密"排序,然后优先保证FFS每一步都尽量从不同簇里保留代表性路径。这个"先分簇后削减"的策略,比自己闷头调FFS的损失函数省力得多。

5.3 时序相关性在削减后明显退化

这是这个课题最核心的坑。你辛辛苦苦在生成阶段做了ARIMA和Copula的双重相关性建模,结果FFS用普通欧氏距离一圈削减,时序相关性又被破坏掉了。

我验证过的有效方案就是前面提到的特征维度扩展。把每条场景的处理分成三部分:

  • 原始出力向量;
  • 差分向量(前后时刻差值,捕捉波动趋势);
  • 滑动窗口能量特征(比如4小时窗口的波动动能)。

然后计算距离时对这三维向量加权欧氏距离。经验权重我给的是1:0.5:0.3,但需要根据你的数据做微调。一个直觉的判断依据是:削减完如果协方差偏差还超过10%,你就增大差分向量的权重再试一次。

最后再分享一个我个人的体会:做这种课题,最容易陷入的误区是"算法看起来很高级,但业务上根本解释不通"。我最终交付给调度模型之前,一定会把削减后的典型场景画出来给运行人员看一遍,请他们确认"这个场景像不像真实会出现的情况"。如果一个削减场景曲线出现了负功率或者功率跳变量超过物理极限,哪怕统计指标再好看,也不能用。做不确定性建模,最终要对物理现实负责,而不是只对数学指标负责。这点在我做过的所有类似项目里,都是最核心的一条经验。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询