一聊到风光出力随机性,搞电力系统优化的同行第一反应往往是“头大”。风电和光伏的出力像过山车一样波动,今天凌晨的大风天和明天下午的晴空天,完全不是同一个运行工况。可问题在于,不管是做机组组合、经济调度,还是规划阶段的可靠性评估,需要的是一组能代表各种典型情况的确定性输入——这就是场景生成和场景消减要干的活。简单说,风光出力场景生成是把历史数据和概率特性变成一批“有可能发生的出力曲线”,场景消减则是把这批曲线压缩成少数几条最有代表性的“典型日”,让优化模型既能算得动,又不至于把极端风险漏掉。
这篇文章我从实际做模拟优化项目的角度,把场景生成、消减的原理、选型逻辑、具体实现和踩过的坑完整梳理一遍。适合正在做新能源并网仿真、电力系统随机优化、或者研究风光出力建模的研究生和工程师参考,也欢迎只想知道“这几个算法到底怎么选”的同行直接跳到自己需要的章节。
1. 场景生成与消减到底解决什么问题
1.1 为什么优化模型“怕”随机性
电力系统优化模型的核心是求解一个目标函数,比如发电成本最小、弃风弃光最小、碳排放最少,然后给出一组机组启停和出力安排。传统确定性模型里,负荷和出力都是固定数值,模型能直接求解。可一旦把风电、光伏装进模型,问题就变了——风光的出力不是确定值,而是随机变量。
如果只取期望值代入模型,算出来的方案往往在大多数情况下可行,但遇到极端低出力或高出力场景就会出问题:低出力时系统可能旋转备用不足,高出力时可能出现大规模弃电甚至潮流越限。更麻烦的是,电力系统调度是“先决策后看到随机实现”的过程——你要在知道明天实际风况之前决定机组的启停状态,这就是典型的两阶段随机规划问题。
场景法的基本思路就是把随机变量的连续分布离散化成有限个场景,每个场景代表一种可能的风光出力时序,并赋予一个发生概率。优化模型在这种场景集上求解,相当于同时考虑了“多种未来”,因此能得到相对稳健的决策方案。场景生成的任务就是“怎么合理地离散化”,场景消减的任务是“离散化之后怎么压缩规模”。
1.2 场景在电力系统模拟优化中的角色
场景在整个模拟优化流程里处于承上启下的位置。上游是风速、辐照度等原始气象数据,要经过数据清洗、归一化、时序建模才能变成出力场景;下游是机组组合、经济调度、可靠性评估等优化问题,场景的质量直接决定优化结果的工程可信度。
我在项目里最直观的感受是:优化模型本身再精致,如果场景集生成得稀烂,结果一样没法看。比如你生成10000个场景塞进机组组合模型,算三天三夜得到一个解,但场景消减做得太狠,把关键的晚高峰大风光场景删掉了,那么解虽然在场景集内最优,实际运行起来可能直接拉闸限电。场景不是优化模型的“配角”,它本身就是决策质量的地基。
2. 场景生成方法怎么选
2.1 基于统计采样的生成路线
统计采样是入门最常用的方法,核心思想是从历史出力数据或者概率分布模型中抽样,生成大量候选场景。
以风电出力为例,常用做法是先对历史风速数据拟合成威布尔分布或者经验分布,然后用蒙特卡洛抽样产生风速序列,再通过风机功率曲线把风速转换成出力。光伏则通常用Beta分布拟合辐照度,再叠加云遮挡模型来修正出力。如果只考虑单站且不考虑时序相关性,这种方法实现起来非常简单,写几十行Python就能跑。
但统计采样有一个明显短板:它默认各时段之间相互独立,生成出来的出力曲线前后波动会非常剧烈,和实际风电出力的平滑爬坡特性差得很远。实际风电场出力具有很强的自相关性——前一时刻出力高,下一时刻大概率也高;一片云飘过来,光伏出力下降后也需要一段时间恢复。直接用独立抽样生成的场景,时序上“碎”得离谱,下游优化模型看到的是现实中几乎不存在的出力曲线。
2.2 基于时序模型的生成路线
为了保住时序相关性,就需要上时间序列模型。风力发电出力场景生成里最经典的是ARIMA模型,它的优势在于可以直接刻画时间序列的自回归和滑动平均结构。光伏出力则常和晴雨状态挂钩,先用马尔可夫链模拟天气状态转移,再在每个状态内拟合出力分布。
还有一种思路是Copula方法:先用边缘分布分别描述风速和辐照度的分布特性,再用Copula函数把它们的相关性结构耦合起来。这个方法在考虑“风电场和光伏电站空间相关”时特别有用。比如在同一个风带上建了三个风电场,它们的出力不可能完全独立——一个风带的大风过程会同时影响三个场;Copula可以从数据里学到这种联合分布的尾部相关性,生成的场景自然带有空间联动特征。
实际工程里,我建议先对原始出力序列做归一化和去趋势处理,再进行时序建模。很多人直接拿原始数据拟合,结果模型把季节性趋势和随机波动混在一起,生成出来的场景要么整体偏高要么整体偏低,概率分布完全失真。
2.3 基于数据驱动的生成路线
这两年深度学习在场景生成领域也火起来了,主流代表是生成对抗网络(GAN)和变分自编码器(VAE)。
GAN做场景生成的基本逻辑是:训练一个生成器,输入随机噪声向量,输出一条完整的风光出力时序;同时训练一个判别器,让它分辨“真实历史出力曲线”和“生成器生成的假曲线”。两者互相对抗,生成器逐渐学会生成以假乱真的出力场景。VAE则走的是另一条路,把历史场景压缩进一个低维隐空间,再从隐空间采样解码出新场景。
这类方法最大的优势是能自动学习高层特征,比如出力曲线的爬坡段、夜间风电大发的形态、晴天光伏的倒U型曲线,而不需要手动建模相关性。但实际落地时要注意:GAN训练非常不稳定,很容易出现模式崩塌——生成器生成的100条曲线长得几乎一模一样,多样性完全丧失。VAE相对稳,但生成结果的细节可能偏模糊,尤其是出力突变点经常被平滑掉。
我自己的项目里,数据驱动方法一般用在“历史样本不足以覆盖所有运行情况”的场景下,作为数据增强手段。如果历史数据本身就有三五年、覆盖了各种极端气象,完全可以用更轻量的统计或时序模型搞定,没必要上大模型,毕竟电力行业的优化计算讲究可控、可解释。
3. 场景消减算法的核心与取舍
3.1 为什么生成完还要消减
假设你用蒙特卡洛抽样生成了一万个风光出力场景,每个场景都对应一条48时段或96时段的时序曲线。理论上场景越多越接近真实概率分布,但优化模型规模撑不住——机组组合里的启停变量本来就多,再乘以场景数,求解复杂度指数上升。
举个例子:一个中等规模的区域电网有30台机组,96时段机组组合的混合整数规划问题,单场景求解可能只要几十秒;但1000个场景的随机机组组合,变量数量暴增到数百万级,一般商用求解器也很难在可接受时间内收敛。这时候就必须做场景消减,用一小部分典型场景去近似原场景集的概率分布。
场景消减的目标可以概括成一句话:在保持概率分布和时空相关性损失最小的前提下,用尽量少的场景代表尽量多的信息。它不是简单“抽几条代表曲线”,而是有严格数学定义的问题——一般转化为概率距离最小化问题。
3.2 K-Means聚类的用法与坑
K-Means是场景消减里最容易上手的算法。做法是把每条出力曲线当作高维空间的一个点,维度就是时段数(比如96维),然后运行K-Means聚类,把相似曲线聚成K类,用每个类的质心作为典型场景,用类内样本占比作为该场景的概率。
我的实操经验是:K-Means适合场景数量大、且曲线形态差异明显的场景集。比如把“风电大发型”“光伏主导型”“早晚双峰型”这类形态差异大的曲线分开,效果一目了然。但有两个致命的细节必须注意。
第一个问题是K-Means用均值作为质心,这会抹平极端峰值。比如某类里有两条曲线,一条晚峰出力900MW,一条晚峰出力500MW,均值后变成700MW——这个代表场景既不代表高风况也不代表中风况,处在一种“中间态”。对调度优化来说,这种中间态是最坑的,因为它让系统看起来“压力不大”,实际运行时却可能扛不住真正的900MW大风。
第二个问题是K-Means对时间对齐极度敏感。如果两条爬坡曲线的峰值时段差了几个小时,欧氏距离会算得非常大,聚不到一类里,但其实它们代表的运行风险是相似的。如果有条件,可以考虑用动态时间规整(DTW)距离替代欧氏距离,或者先把曲线对齐到同一天气过程再聚类。
3.3 同步回代缩减法(SR)实操
比K-Means更贴合场景消减原始定义的是同步回代缩减法(Simultaneous Backward Reduction)。这个方法的思路不是聚类求均值,而是迭代地删掉一个“最不重要”的场景,同时把它的概率转移给距离它最近的另一个场景,直到剩下的场景数达到目标值。
每一步选择删除哪个场景的标准是“概率距离乘积最小”——即让删除场景与替代场景之间的概率距离增量最小。算法实现上需要维护场景之间的距离矩阵和概率向量,每删一个场景就更新一次矩阵。虽然计算复杂度比K-Means高,但结果的理论性质更好:它能保证消减前后场景集的概率分布距离最小化。
我用Python实现过一轮,核心逻辑大致是:
import numpy as np from scipy.spatial.distance import cdist def simultaneous_backward_reduction(scenarios, probabilities, target_count): # scenarios: shape (n_scenarios, time_steps) # probabilities: shape (n_scenarios,) n = len(scenarios) probs = probabilities.copy() idx = list(range(n)) dist_matrix = cdist(scenarios, scenarios, metric='euclidean') while len(idx) > target_count: min_cost = np.inf del_idx = -1 nearest_idx = -1 # 对每个场景,找到替代代价最小的删除方案 for i in range(len(idx)): for j in range(len(idx)): if i == j: continue # 删除场景i,替代场景j的代价 cost = probs[idx[i]] * dist_matrix[idx[i]][idx[j]] if cost < min_cost: min_cost = cost del_idx = i nearest_idx = j # 删除场景,概率转移给最近场景 probs[idx[nearest_idx]] += probs[idx[del_idx]] del idx[del_idx] return scenarios[idx], probs[idx]实际使用时要控制消减比例,通常建议从几百个原始场景消减到20~50个典型场景。再少的话,概率分布形状会走样,尤其是长尾的极端场景大概率会被删掉。另外,每次迭代都重新计算整个距离矩阵代价比较高,工程上一般只更新和删除场景相关的距离行,能快很多。
3.4 消减质量怎么评估
很多同行做完消减就直接丢给优化模型,这一步是不负责任的做法。场景消减后一定要做质量评估,否则你根本不知道信息损失了多少。
常用指标包括:消减前后场景集的Wasserstein距离或Kantorovich距离、各时段出力均值和方差的偏差、极端场景覆盖度(比如历史10%分位的出力是否还在场景集里)、时序自相关系数的保留程度。
我在项目中积累的经验是,先看均值和方差的偏差,偏差控制在5%以内才放心;再看消减后场景中是否保留了“最坏情况”场景——可以主动把历史出力最低的几条曲线单独挑出来,检查消减后它们是否仍然存在。如果极端场景被削掉了,即使整体统计指标很好看,调度结果也不可信,因为优化模型会把压力完全集中在剩下那些“好日子”上,系统性低估运行风险。
4. 把场景接入电力系统优化模型
4.1 两阶段随机机组组合的基本形态
生成和消减完场景之后,下一步就是把它接入优化模型。电力系统里最常见的场景应用场景是随机机组组合(Stochastic Unit Commitment)和两阶段随机经济调度。
两阶段模型的逻辑是:第一阶段在知道风光出力之前做机组启停决策(因为启停需要时间,必须提前决定),第二阶段看到每个场景的风光出力后,在给定启停状态下做经济分配。目标函数是所有场景下的期望运行成本,包括煤耗成本、启停成本、弃电惩罚和失负荷惩罚。
模型的标准形式可以写成:
- 决策变量:机组启停状态 u_it(第一阶段)、机组出力 p_it、弃风量、切负荷量(第二阶段)
- 目标函数:min Σ_t Σ_i (启动成本 + 运行成本) + Σ_s prob_s × Σ_t (弃风惩罚 + 切负荷惩罚)
- 约束条件:系统功率平衡、机组出力上下限、爬坡约束、最小启停时间、备用约束、潮流约束
4.2 约束条件怎么写
功率平衡约束是一切的底线。每个场景每个时段,系统中所有机组出力加上风光出力,等于负荷加上网损。这里要注意的是,风光出力在场景里已经是“最大可用出力”,实际出力可以向下调整——这就是弃风弃光的来源。
机组爬坡约束在随机模型中要特别小心。很多初学者把爬坡约束写成每个场景独立满足,这在物理上没错,但忽略了同一机组在相邻时段处于不同场景时的爬坡耦合关系。实际上,机组爬坡是随时间连续变化的物理过程,场景切换只发生在模型里,物理世界并不会瞬时跳变。严谨的做法是:对每个相邻时段对,校验所有场景组合之间的出力差是否满足爬坡限制——这一下子就让约束数量爆炸,但得到的解才是真实可行的。
备用约束也不能只按期望场景设置。我见过一个真实案例:场景消减后的平均出力看起来充足,备用也按最大单机容量设置,但消减遗漏了某条持续低出力场景,实际运行时备用全部耗尽,最终切了一部分负荷。后来在约束里增加了针对低出力场景的备用充足性校验,问题才解决。
4.3 从场景到调度决策的闭环流程
在实际项目中,场景生成和消减不是一锤子买卖,而是要和优化模型形成闭环。我的标准做法是这样:
第一步,用历史数据训练场景生成模型,生成500~1000个候选场景;第二步,用同步回代缩减法消减到20~50个典型场景,并记录每个场景的概率;第三步,把这些场景输入两阶段随机机组组合模型求解;第四步,拿到调度结果之后反查——哪些时段哪些场景出现了备用紧张或弃电严重,如果某个场景频繁触发约束边界,说明这个区域还需要补充场景;第五步,回到场景集,针对性补充该时段附近的样本,重新消减、重新求解。
这样做一轮下来,场景集和优化模型能够互相校验,比“生成一次就用到天荒地老”靠谱得多。尤其是调度结果在某个场景下异常激进或异常保守时,多半不是模型错了,而是场景集里缺少了平衡该风险的代表样本。
5. 常见问题与参数调整经验
5.1 消减后调度结果偏乐观怎么办
调度结果偏乐观的典型表现是:期望成本很低,但一拿到实际运行数据就对不上,实际弃风量和切负荷都比预期高。这个问题八成出在场景消减删掉了极端场景上。
我的排查思路是先画出消减前后场景集的出力包络线。如果消减后场景集的最大出力明显低于原始场景集,说明高峰场景被削了,弃风风险会被低估;反之最小出力抬高了,备用风险会被低估。解决办法不是简单增加场景数量,而是把极端场景单独保留下来,再对剩余场景做消减,最后合并。这样相当于人为保证了极端场景的覆盖率。
还有一种做法是在目标函数里加风险度量项,比如用条件风险价值(CVaR)替代期望成本,把尾部风险显式建模进优化模型。这样即使场景消减偶尔漏掉极端情况,CVaR项也会让调度方案更保守,防止系统性低估。
5.2 场景数量K怎么选
这是被问最多的问题。K值太小,场景代表性不足,优化结果偏乐观;K值太大,计算规模爆炸,求解时间指数增长。
我在实际项目里一般这样选:先用肘部法则看K-Means聚类误差的拐点,得到一个粗略范围;然后取K=10、20、30、50分别做消减,比较消减前后场景集的距离指标变化。当K增加到某个值后距离指标下降明显变缓,就说明信息损失已经接近饱和,没必要再加。经验上,区域电网做96时段机组组合,20~30个场景是比较平衡的选择;如果计算资源充足,可以取50个场景。
还有一点要注意:场景数量不能只看绝对数,还要看每个场景的概率分布。如果消减后某个场景概率超过了0.15,说明原始场景集里有大量样本长得很像,这个场景过于“主导”,会让优化结果偏向该种运行方式。需要检查原始场景是不是真的如此集中,还是生成模型多样性不足。
5.3 时序特征被消减破坏怎么办
消减后的场景有时会出现出力曲线“失真”——前一时刻还在爬坡,后一时刻突然掉到谷底,和原始历史曲线完全不是一个风格。原因是对场景做距离计算时用了逐点欧氏距离,它不包含时序形状信息,所以被合并到一起的场景在形态上可能并不相似。
解决办法有两个方向:一是距离计算换成DTW距离,它允许曲线在时间轴上轻微错位匹配,更符合出力爬坡过程的相似性判断;二是消减后做一次时序平滑修正,比如对质心应用滑动平均或者保持原始场景中形状最常见的曲线作为代表。我个人更倾向于用原始场景中距离质心最近的曲线作为代表场景,而不是直接用质心,这样时序特征不会因平均而衰减。
5.4 相关性问题怎么处理
单一风电场或光伏电站的场景生成相对简单,难的是多个场站之间的空间相关性。比如两个风电场处于同一风带,它们的出力变化是联动的;单独生成再拼接,相关性会彻底丢失。
处理相关性比较实用的做法是先做主成分分析(PCA)或者经验正交函数分解,把多个场站的出力分解成若干互相正交的空间模态和时间系数,对时间系数做场景生成,再重构出场站出力序列。这个方法保留空间相关性的同时,大大降低了生成难度。近两年Copula方法也很流行,它能显式建模场站间的非线性相关性,尾部相关性的刻画比PCA更准确,但计算和调参复杂度也更高。
我的建议是:如果项目周期紧,先用PCA构建降维空间再做场景生成,快速出结果;如果是要发论文或者做长期规划评估,值得花时间上Copula模型。
我在实际运行中体会最深的一点是:场景生成和消减这一套东西,看似是优化模型的“前置处理”,实际上直接决定优化结果的工程可信度。花在场景质量上的功夫,远比你想象的要值得。最后再分享一个小技巧:做完消减后,千万别急着把原始场景丢进回收站。保留一份原始场景集,每次调试完新模型,都可以拿它做对照——什么时候消减后场景和原始场景的结果差得不可接受了,你才知道算法哪一步走偏了。这套对照习惯,让我在不少项目里少走了大弯路。