ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Copula的风光联合场景生成:原理、实现与工程实践

基于Copula的风光联合场景生成:原理、实现与工程实践 最近在做新能源并网规划的项目碰到一个绕不开的问题风电场和光伏电站的出力曲线到底该怎么模拟才靠谱。单看风电韦布尔分布拟合一下或者直接用时序生产模拟跑几百个场景都不难单看光伏Beta分布加云层系数也能凑合。但问题是实际电网里风光往往是打捆外送、共享一条输电通道的你单独建模的时候风和光在时间上、空间上的耦合关系是割裂的。这就导致规划算出来的结果要么过于乐观要么过于保守根本没法指导实际决策。后来我把目光转向了基于copula的风光联合场景生成方法——通过copula函数把风电、光伏各自的边缘分布“粘”在一起同时把站点之间的空间相关性也塞进联合分布里这样生成出来的风光联合场景既保留了单场站的出力统计特性又能体现“这边起风那边出太阳”或者“整片区域同时阴天”这类空间关联现象。今天就把这套方法的原理、实现步骤和我在实际项目里踩过的坑一次性说清楚。1. 内容整体设计与思路拆解1.1 为什么不能“各建模各的”先说一个直观的例子。假设某个区域有一个风电场和一个光伏电站共用一条220kV外送通道通道容量有限。如果你分别对风电和光伏做单机建模生成各自独立的大量场景那在随机优化的时候程序会把“大风天大晴天”的场景和“无风天阴雨天”的场景都按一定的概率抽样出来。但现实里“大风天大晴天”往往出现在春季冷空气过境后的高压控制区而“无风天阴雨天”常见于夏季副热带高压边缘的闷热天气。两者并不是均匀随机组合的。如果你忽略这种相关性外送通道的容量配置就会偏大——因为你把那些现实中很少出现的“风光双高”场景当成了常见场景来处理。还有一个更隐蔽的问题空间相关性。同一个风电场内的多台风机或者同一区域内不同位置的风电场它们的出力不是独立的。冷锋过境时整条狭长地带的风速都会同时上升这时候所有风电场的出力会同向波动。如果模型里假设它们独立那区域总出力的波动会被严重低估调峰容量、备用容量的计算结果都会偏小。所以核心思路其实就一句话用copula把单变量的边缘分布和变量间的相依结构拆开建模再把它们合起来生成保持相关性的联合场景。这样做的好处是灵活——你可以随便给风电、光伏配不同的边缘分布甚至是非参数的核密度估计相关性结构则单独交给copula来刻画互不干扰。1.2 copula在这套方法里的角色Copula这个词来源于拉丁语原意是“连接”。它的数学定义不复杂对于一个多维随机变量联合分布函数可以被拆成一个copula函数和各个变量的边缘分布函数的复合。用公式表达就是F(x₁, x₂, ..., xₙ) C(F₁(x₁), F₂(x₂), ..., Fₙ(xₙ))也就是说你先分别把风电出力X₁、光伏出力X₂各自的分布函数F₁、F₂求出来然后通过copula函数C把它们的“概率值”关联起来。因为F(X)本身是服从[0,1]均匀分布的所以copula本质上是在均匀分布的空间里研究变量之间的“步调一致性”——也就是相关性。这比直接用皮尔逊相关系数描述关系要强得多。皮尔逊系数只能刻画线性相关而且对边缘分布的类型很敏感而copula不仅能刻画非线性相关还能捕捉“尾部相关性”——比如极端大风天气下多个风电场会同时达到出力上限这种“尾部齐涨齐跌”的现象用普通相关系数是描述不了的。在实际建模中我常用的是阿基米德copula族里的Clayton、Gumbel、Frank以及椭圆族里的Gaussian copula和t-copula。选型的逻辑后面细说你先记住一个结论风电之间的空间相关性通常用Gumbel或者t-copula比较合适因为它们在右上尾高出力端有较强的相关性风光之间的互补性相关用Frank或者Gaussian copula更稳。2. 核心细节解析与实操要点2.1 边缘分布的拟合别一上来就套标准分布很多人做风光建模第一反应就是“风电用韦布尔分布光伏用Beta分布”然后拿历史数据去fit参数。这个做法在样本量充足、数据质量好的时候勉强能用但实际项目里光伏出力受云层影响出力序列经常出现大量0值和满发值分布形态是双峰甚至多峰的单纯的Beta分布根本拟合不好。我在项目里更推荐的做法是先做数据清洗再用核密度估计作为边缘分布的主力必要时辅以混合分布。核密度估计的好处是不需要对分布形态做先验假设数据长什么样它就拟合什么样。但要注意带宽的选择——带宽太大分布被过度平滑极值被抹掉带宽太小分布会出现很多毛刺影响后续抽样。我一般用Silverman规则自动选带宽然后盯着尾部看效果如果尾部抖动厉害就手动把带宽乘一个1.2到1.5的系数。还有一个数据预处理的关键点剔除异常数据但不破坏相关性结构。风电数据里经常有停机检修导致的出力骤降为0的记录光伏数据里有逆变器故障导致的出力异常跳变。如果你简单粗暴地把这些点全删掉会导致两个变量的样本对数量不一致copula拟合时会出现严重的估计偏差。我建议的做法是以“时间戳对齐”为原则检测到某个时间点数据异常时把该时间点所有变量的数据一起剔除保证样本对完整。2.2 copula函数选型结合场景用途做决定选copula不能只看拟合优度还得看场景生成出来之后拿来干什么。这里我把常见场景分类和推荐的copula列一下场景类型典型需求推荐copula选择理由风电多点空间相关性区域风电场群出力模拟Gumbel / t-copula高出力尾部相关性强能刻画“同时满发”风光互补性分析打捆外送通道容量配置Frank / Gaussian能刻画正负相关切换尾部相关性弱更符合互补特征极端天气事件模拟电力系统可靠性评估Clayton / t-copulaClayton左下尾相关性强适合模拟“同时低出力”多变量复杂依赖含风光水多种能源vine copula高维灵活但计算量大需谨慎使用你可能会问为什么不直接用t-copula打天下t-copula的自由度参数可以控制尾部相关性强度理论上比Gumbel更灵活。但实际拟合时t-copula的极大似然估计收敛速度慢而且自由度参数趋近于无穷时数值稳定性差对于5000个样本以内的数据集容易陷入局部最优。所以我的实践经验是二维问题优先用阿基米德copula三维以上才考虑t-copula或vine copula。2.3 空间相关性的引入策略空间相关性这个词听起来高大上其实落到实操层面就是两个思路一是基于历史数据的经验相关性二是基于地理距离的模型相关性。基于历史数据的做法很直接统计多个站点之间出力的秩相关系数Kendalls tau或者Spearmans rho然后把这些系数直接喂给copula的参数估计。这个思路的好处是真实反映当前气候条件下的依赖关系缺点是无法外推到没有历史数据的站点。基于地理距离的做法是用变差函数或者相关长度来建模“距离越近相关性越强”。这种方法在风电场选址阶段特别有用因为新场站没有历史出力数据只能用气象再分析数据或者相邻场站的实测数据推算。但要注意地形对风的相关性影响非常大——两座山脊上的风电场虽然直线距离只有10公里但相关性可能远低于平原上距离30公里的两个场站。所以纯距离建模只能作为初值最终还是要用实际运行数据修正。3. 实操过程与核心环节实现3.1 数据准备与预处理我用一个实际案例来走一遍完整流程。假设我们手上有某地区两个风电场和一个光伏电站逐小时出力数据时间跨度三年单位是MW。第一步是数据清洗把出力大于装机容量的点剔除数据记录错误把连续24小时出力恒定为0的点标记为停机状态该时段数据不参与建模。对于光伏还要剔除夜间数据——光伏夜间出力为0是正常现象如果把这些0值算进分布拟合里会在0处堆一个巨大的概率质量严重影响copula的拟合质量。第二步是归一化处理把出力除以各自的装机容量转换成标幺值这样不同规模的场站才能放在同一个框架下比较。第三步是趋势分离如果数据里存在明显的季节性趋势比如夏季光伏出力峰值明显高于冬季建议先做季节分解对每个季节分别建模。我试过不分离趋势直接建模结果生成的场景在冬季会出现不合理的“高光伏出力”样本就是因为季节性差异被混进了随机波动里。3.2 copula参数估计与拟合这块我用Python的copulas库做过一版后来发现这个库的文档贫瘠、接口变动大踩了不少坑。目前我比较推荐的方式是边缘分布自己拟合copula部分用scipy加上少量手写代码实现。这样每一步都在掌控之中。边缘分布拟合的示例代码如下import numpy as np import pandas as pd from scipy import stats # 读取数据 df pd.read_csv(wind_solar_data.csv, parse_dates[time]) wind1 df[wind1_pu].values # 风电场1标幺值出力 wind2 df[wind2_pu].values # 风电场2标幺值出力 solar df[solar_pu].values # 光伏标幺值出力 # 核密度估计边缘分布 kde_w1 stats.gaussian_kde(wind1, bw_methodsilverman) kde_w2 stats.gaussian_kde(wind2, bw_methodsilverman) kde_s stats.gaussian_kde(solar, bw_methodsilverman) # 计算每个样本对应的CDF值概率积分变换 u_w1 np.array([kde_w1.integrate_box_1d(-np.inf, x) for x in wind1]) u_w2 np.array([kde_w2.integrate_box_1d(-np.inf, x) for x in wind2]) u_s np.array([kde_s.integrate_box_1d(-np.inf, x) for x in solar])注意这里有个小坑gaussian_kde的integrate_box_1d在大样本下非常慢每算一个点都要做一次数值积分。三万个样本、三个变量跑一次要几分钟。我后来改成用stats.kde.gaussian_kde的cdf方法或者直接用scipy.integrate配合向量化运算速度快了一个数量级。接下来是copula参数拟合。以Gumbel copula为例Gumbel copula的累积分布函数是C(u, v) exp(-[(-ln u)^θ (-ln v)^θ]^(1/θ))其中θ≥1是相关参数θ1时表示独立θ越大相关性越强。Gumbel copula的密度函数比较复杂我一般用极大似然估计MLE求参数。但更省事的做法是利用Kendalls tau和θ的关系式τ 1 - 1/θ也就是说你先算两个变量之间的Kendalls tau然后直接反解出θ。这个方法的计算量小而且对异常值不敏感实际效果和MLE差别不大。代码如下from scipy.stats import kendalltau # 计算两个风电场出力的Kendalls tau tau_w1w2, _ kendalltau(u_w1, u_w2) theta_w1w2 1 / (1 - tau_w1w2) # 反解Gumbel参数 print(f风电场1和风电场2的Kendall tau: {tau_w1w2:.4f}) print(f对应的Gumbel copula参数theta: {theta_w1w2:.4f})3.3 联合场景生成抽样与逆变换参数定好之后生成场景就是标准的“三步走”先从copula抽样得到[0,1]均匀分布的样本对再通过边缘分布的反函数分位数函数变换回出力值。Gumbel copula的抽样不像Gaussian copula那么简单需要用条件分布法。具体来说生成两个独立的均匀分布随机数u和t。令v -[(-ln u)^θ (-ln t)^(θ/(θ-1))]^(1/θ)。得到的(u, v)就是服从Gumbel copula的样本对。代码实现def sample_gumbel_copula(theta, n_samples, seed42): 从Gumbel copula中抽样 rng np.random.default_rng(seed) u rng.random(n_samples) t rng.random(n_samples) v np.exp(-((-np.log(u))**theta (-np.log(t))**(theta/(theta-1)))**(1/theta)) return np.column_stack([u, v]) # 生成1000个相关性样本对 copula_samples sample_gumbel_copula(theta_w1w2, 1000) # 逆变换回出力值 wind1_scenarios kde_w1.ppf(copula_samples[:, 0]) # 这里需要核密度的PPF函数 wind2_scenarios kde_w2.ppf(copula_samples[:, 1])这里又有一个实操上的坑scipy.stats.gaussian_kde并没有直接提供ppf分位数函数方法。你需要自己用数值方法求逆。我一般是这样做的先在出力范围内生成一个密集的网格点算出每个点的CDF值然后用插值法反推分位数。代码如下def kde_ppf(kde, quantile, grid_min-0.1, grid_max1.1, grid_size10000): 通过网格插值法求核密度估计的分位数 grid np.linspace(grid_min, grid_max, grid_size) cdf_grid np.array([kde.integrate_box_1d(-np.inf, x) for x in grid]) return np.interp(quantile, cdf_grid, grid) # 使用 wind1_scenarios np.array([kde_ppf(kde_w1, q) for q in copula_samples[:, 0]])注意网格范围要稍微超出数据范围因为KDE在边界外会有延拓如果你截断在[min, max]里面生成的极端场景会被截尾后面的可靠性分析就会偏乐观。3.4 场景缩减从上千条曲线到可计算的典型场景生成1000组场景看着很爽但真正的电网随机优化不可能把1000个场景全塞进去算——计算量太大了。实际项目中通常需要用场景缩减技术把样本数量降到10到30个同时尽量保留原始场景集的概率分布特征。我常用的方法是基于K-means聚类的场景缩减把所有场景当作高维空间中的点用K-means聚成K类每类的质心作为一个典型场景该类的样本数占比作为场景概率。这个过程有一个细节要注意聚类前要不要做归一化。风电和光伏的出力范围不同如果你直接对原始值聚类光伏场站的标幺出力普遍在0到1之间而风电场可能经常在0到0.3之间徘徊距离度量会被出力幅值较大的变量主导。我建议先对每个变量做标准化聚完类之后再还原回去。还有一点K-means对初值敏感直接跑一遍很可能陷入局部最优。我一般跑50次随机初始化选总畸变最小的一次作为最终结果。sklearn.cluster.KMeans提供了n_init参数直接设成50就行。4. 常见问题与排查技巧实录4.1 问题一生成的场景里风电出力出现负值这是我第一次跑通整套流程后最先遇到的问题。用KDE拟合边缘分布时因为带宽设置的关系分布的左尾会延伸到负值区域。虽然理论上出力不可能是负数但逆变换还原时分位数在极小值时确实会把负值映射出来。排查思路其实很简单检查KDE在0附近的密度估计值。如果0处密度显著高于数据最小值处的密度说明KDE把0附近的概率质量“抹”到了负区间。我的解决办法有两个一是用截断核密度估计在计算CDF时把负值区间的概率质量强制累积到0处相当于把0点的概率变成连续分布加一个离散概率质量二是直接用混合分布比如用50%概率的0点离散分布加50%概率的正值连续分布。第二个方法在工程上更常用因为光伏夜间出力为0本身就是离散事件混合分布更符合物理实际。4.2 问题二Gumbel copula参数估计值大于10抽样出来的场景相关性过强有一次对两个距离很近的风电场建模时Kendalls tau算出来是0.91反解出来的θ高达11.1。抽样之后两个场的出力几乎完全同步区域总出力的波动范围反而比实际历史数据还小——因为历史数据里还有不少“一个场大一个场小”的样本但Gumbel copula在上尾处把相关性“锁死”了。这其实是Gumbel copula的固有特性它只有上尾相关性没有下尾相关性而且当你把θ推到比较大的值时整个分布几乎退化成完全单调的函数。这种情况我建议换用t-copula试试。t-copula同时有上下尾相关性自由度参数可以独立控制尾部的厚度拟合这类“中度偏强相关”的数据通常更稳。如果你非要用Gumbel另一个补救措施是抽完样之后做一个“相关性校正”用Cholesky分解或者Schur乘积方法把样本的相关矩阵调整到目标值。但这个操作会破坏copula的边际均匀性需要再配合排名变换处理比较繁琐不如直接换copula省事。4.3 问题三copula拟合的分布与经验分布偏差大评判copula拟合好坏我一般用两个指标一是Cramér-von Mises统计量二是概率积分变换后变量的独立性检验。后者更直观如果copula拟合得好把原始数据代入copula的条件分布函数做变换得到的新变量应该近似服从均匀分布且相互独立。如果检验不通过先别急着换copula先回头看看边缘分布有没有问题。我遇到的大多数情况都是边缘分布KDE带宽没调好导致概率积分变换后的变量偏离均匀分布copula拟合自然一塌糊涂。4.4 避坑清单汇总坑点现象解决方案数据未做季节分离冬季出现不合理高光伏出力按月或按季分别建模KDE带宽过小场景尾部毛刺多、样本失真带宽乘1.2~1.5或改用混合分布包含夜间0值光伏分布0值处概率过大建模时剔除夜间数据Gumbel copula强制拟合强相关场景退化为单调函数换t-copula或增加自由度参数场景缩减未归一化聚类结果被量纲大的变量主导聚类前标准化聚完后再还原直接删异常数据样本不对齐导致copula失稳按时间戳删除整行多变量数据5. 应用场景与扩展思路5.1 在电网规划中的应用风光联合场景生成最直接的应用场景就是输电网规划中的随机生产模拟。传统的确定性生产模拟用一条典型的负荷曲线加一条典型的风光出力曲线跑出结果之后人为加一个安全裕度。这个方法在风光渗透率比较低的时候勉强可用但现在很多地区的新能源装机占比已经超过50%确定性方法的裕度设置根本没有依据。用场景法之后你可以给规划方案做一个“概率化的N-1校验”——把所有可能的风光出力场景都跑一遍统计每个方案下支路潮流越限的概率。算出来的结果可以直接作为投资决策的依据如果方案A的越限概率是2%方案B是0.5%但方案B要多花3个亿你就可以算一笔经济账到底值不值。另外在储能容量配置里面copula场景生成也特别有用。我曾经遇到过一个项目某工业园区既要消纳屋顶光伏又要给储能定容量。用独立模型算出来的储能容量是20MWh用copula联合模型算出来的是14MWh差了30%。原因就是光伏和负荷空调用电在实际中呈正相关——天越热光伏出力越大、空调负荷也越大这个相关性用独立模型完全捕捉不到。5.2 怎么扩展到包含更多变量的情况二维copula一个风电场加一个光伏电站只是入门。实际电网往往是几十个风电场、几十个光伏电站同时存在这时候二维copula就力不从心了。一个朴素的扩展方法是建一个多维Gaussian copula先求所有变量的相关系数矩阵然后在高斯空间里做条件抽样。这个方法实现简单但Gaussian copula没有尾部相关性极端场景刻画能力差。如果需要刻画多维变量的尾部相关性就得用C-vine copulaC藤copula。C-vine的基本思想是把高维分布拆解成多个二维条件copula的乘积然后用树结构逐层建模。这个方法灵活但对数据量要求高、计算复杂度直线上升样本太少的时候参数估计方差很大。我的建议是分阶段来先判断问题关键维度是不是就是2到3个比如一个区域的风、光、负荷如果是就别硬上高维方法只有当变量数量确实很多且两两之间都有显著相关性时再考虑C-vine。模型不是越复杂越好能解决问题还能解释清楚的模型才是好模型。这里顺便提一句有人会用深度学习方法如生成对抗网络GAN来生成场景我也试过。GAN的优势是不需要显式建模分布从数据里直接学生成出来的样本在视觉上和真实数据几乎没区别劣势是可解释性差、训练不稳定而且生成结果的统计特性不能保证——有时候概率分布的高阶矩会漂移。在工程评审的时候“你的边际分布为什么和实测不一样”这个问题copula方法能拿公式说话GAN基本答不上来。所以至少到目前为止copula在电力系统的实际工程应用中还是更稳妥的选择。我在实际项目中还有一个体会copula模型不是一次建好就一劳永逸的。气候变化、场站扩容、调度策略调整都会改变风光的出力特性。我建议每半年用最新数据重新拟合一次参数同时监控模型分位数预测的偏差。如果偏差超限就触发重新训练。这个机制虽然简单但能避免很多因模型老化导致的决策偏差。最后再说一个实用的小技巧生成场景的时候不要只生成一套然后拿去算而是生成多套比如五套每套用不同随机种子把所有结果放在一起做统计。这样可以直观地看出哪些结果是稳定出现的、哪些结果只是随机波动。我见过不少项目把一套场景的结果当成铁板钉钉的结论最后评审的时候被专家问两句就站不住了。多跑几套哪怕只是简单的敏感性分析也能让你的结论扎实很多。
返回列表