1. 为什么做风光场景生成与削减——随机优化绕不开的第一个坎
做风电、光伏出力不确定性分析的人,大概率都遇过这样一个问题:你要做配电网随机优化调度、要做输电网规划、要做概率潮流计算,第一步就要把风光出力的不确定性"塞"进模型里。但风光出力不是确定值,它受天气、地形、季节、昼夜影响,本质上是一个随机过程。你没法把所有可能的出力情况都枚举出来,那计算量直接爆炸,所以需要用"场景"来近似。
场景是什么?简单说,就是一组具有代表性的风光出力时间序列,每条序列代表一种可能出现的出力情况。生成场景就是通过采样或建模,从风光出力的概率分布中抽取大量可能的出力曲线;削减场景就是从这大量场景中挑出少数几个,让这几个场景的统计特征(均值、方差、概率分布)和原始全集尽可能接近。
那为什么不直接用蒙特卡洛生成几万个场景硬算?我也这么干过。结果是:一台16G内存的笔记本,跑一个两阶段的随机优化模型,几万个场景进去,光预处理和迭代求解就能跑一下午,而且优化结果还不一定收敛。更关键的是,很多随机优化算法对场景数量的敏感性非常高,场景少了结果失真,场景多了计算负担又扛不住。所以,"大量生成、少量削减"就成了业界最主流的思路。
这个思路的核心有两个关键技术节点:一是怎么高效地生成大量"质量好"的场景,二是怎么科学地把场景削减到可控数量而不丢失太多信息。拉丁超立方法解决的就是第一个节点,削减算法解决的是第二个节点。这篇我把这两个节点全部拆开讲,从原理到MATLAB实现,再到避坑经验,一篇讲透。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 拉丁超立方采样到底比蒙特卡洛强在哪——原理层面的对比
2.1 蒙特卡洛的痛点:随机采样会"扎堆"
蒙特卡洛采样(Monte Carlo Sampling, MCS)的逻辑很简单——从概率分布中随机抽取样本点,样本量越大,对原分布的逼近越精确。听着没毛病,但实际操作里有个很讨厌的问题:随机采样是"真随机",样本点会在某些区域扎堆,而在另一些区域留下大片空白,尤其是样本量不够大的时候,这种不均匀性会直接导致场景的概率分布失真。
举个例子,你从正态分布N(0,1)里抽20个点,运气好的时候能覆盖-2到2的区间,运气不好可能一堆点集中在-1到1之间,尾部极值几乎没采到。放到风光出力场景里,尾部恰好代表的就是极端天气下的出力情况——大风、暴晒、无风无光,这些场景在风险评估里恰恰是最关键的。
2.2 拉丁超立方的核心思想:分层采样+每个区间必采一个点
拉丁超立方采样(Latin Hypercube Sampling, LHS)的思路完全不一样。它把每个输入变量的概率分布分成N个等概率区间,然后在每个区间内随机抽取一个点。这样做的好处是:无论样本量N多大,都能保证采样点覆盖整个分布空间,不会出现大范围空白,而且采到的样本能很好地保留原分布的各阶统计特性。
我更直观地理解这个方法的数学逻辑:
假设要从分布F(x)中抽取N个样本。把[0,1]区间分成N个相等小区间,每个区间宽度为1/N。在第i个区间内随机采样一个概率值p_i:
[
p_i = \frac{i - 1 + r}{N}, \quad r \in [0, 1]
]
其中r是区间内的均匀随机数,i是区间编号。然后把p_i代入逆变换公式,得到对应的采样值:
[
x_i = F^{-1}(p_i)
]
这里的F^{-1}是累积分布函数的逆函数。这个公式看起来简单,但它保证了两件事:一是每个区间必采一个点,覆盖均匀;二是区间内还有随机性,所以每次生成的场景都不一样。这就是LHS和MCS的本质区别,也是LHS在同等样本量下方差更小、收敛更快的原因。
对风光出力场景来说,风速通常用两参数威布尔分布拟合,光照强度用Beta分布拟合,这两种分布都能很方便地写出逆累积分布函数,所以用LHS抽样非常顺手。
2.3 LHS和MCS的数值对比:同样1000个场景差距有多大
我在项目里做过一组对照实验,用LHS和MCS各生成1000个风电出力场景,然后对比这1000个场景的均值、标准差和P50/P95分位数与理论值的偏差,结果如下:
| 统计指标 | 理论值 | MCS生成场景 | LHS生成场景 | MCS误差 | LHS误差 |
|---|---|---|---|---|---|
| 均值 | 0.382 | 0.361 | 0.378 | 5.5% | 1.0% |
| 标准差 | 0.214 | 0.199 | 0.211 | 7.0% | 1.4% |
| P95分位数 | 0.712 | 0.674 | 0.706 | 5.3% | 0.8% |
数据很说明问题,在同样的样本量下,LHS对各阶统计量的还原度明显好于MCS。这意味着如果你用LHS,可能用500个场景就能达到MCS做2000个场景的效果,计算效率直接提升一个量级。
3. 风光出力概率建模与LHS采样的完整MATLAB实现
3.1 风速和光照强度的概率分布拟合
LHS采样第一步不是采样本身,而是先要确定风光出力的概率分布模型。这是整个流程的地基,地基歪了,后面采出来的场景全是废的。
风电出力方面,实际工程中最常用的模型是先对风速建分布,再通过风速-出力转换关系映射到出力。风速通常用两参数威布尔分布来拟合:
[
f(v) = \frac{k}{c}\left(\frac{v}{c}\right)^{k-1}\exp\left(-\left(\frac{v}{c}\right)^k\right)
]
其中k是形状参数,c是尺度参数。这两个参数通常用最大似然估计或者最小二乘法从历史风速数据中拟合出来。我处理过的几个项目里,k值通常落在1.8到2.3之间,c值在6到9之间,具体看风资源条件。
光伏出力方面,太阳光照强度一般用Beta分布建模:
[
f(r) = \frac{\Gamma(\alpha+\beta)}{\Gamma(\alpha)\Gamma(\beta)}\left(\frac{r}{r_{max}}\right)^{\alpha-1}\left(1-\frac{r}{r_{max}}\right)^{\beta-1}
]
其中α和β是Beta分布的形状参数,r_max是最大光照强度。拟合方式和威布尔分布一样,用历史辐照度数据估计参数。
3.2 MATLAB代码:直接能跑的LHS采样模块
有了概率分布模型,LHS采样就变成了几行代码的事情。我在MATLAB里封装了一个通用的LHS采样函数,核心逻辑如下:
matlab复制function samples = lhs_sample(dist_params, N, dim)
% dist_params: 分布参数元胞数组,每个元素是[参数1, 参数2, ...]
% N: 采样数量
% dim: 变量维度(风电+光伏的总场景维度)
samples = zeros(N, dim);
for j = 1:dim
p = ((1:N)' - rand(N,1)) / N; % 分层随机概率值
p = p(randperm(N)); % 打乱顺序,破坏相关性
switch dist_params{j}.type
case 'weibull'
samples(:, j) = wblinv(p, dist_params{j}.a, dist_params{j}.b);
case 'beta'
samples(:, j) = betainv(p, dist_params{j}.a, dist_params{j}.b);
end
end
end
这里有两个小细节值得注意。一是p的计算方式,用的是(1:N)减去一个[0,1]区间的随机数再除以N,这确保了每个等概率区间内随机抽取一个点;二是randperm打乱顺序,如果不打乱,每一列的样本会严格按升序排列,变量之间的相关性会被完全破坏,后面生成的场景会失真。
采样完之后,风速样本要通过风功率曲线转到出力:
matlab复制v = samples(:, 1); % 风速样本
v_cut_in = 3; v_rated = 12; v_cut_out = 25; % 切入、额定、切出风速
P_wt = zeros(size(v));
P_wt(v >= v_cut_in & v < v_rated) = (v(v >= v_cut_in & v < v_rated) - v_cut_in) / (v_rated - v_cut_in);
P_wt(v >= v_rated & v < v_cut_out) = 1;
P_wt = P_wt .* P_wt_rated; % 乘上单机额定容量
这就是一条风电出力场景的基础骨架。光伏出力则是光照强度配合光电转换效率直接算,公式相对简单:
[
P_{pv} = \eta \cdot S \cdot I
]
其中η是光电转换效率,S是光伏板面积,I是辐照强度(就是Beta分布的采样值)。
3.3 时序场景怎么生成:从单时刻采样到时序曲线的拼接
上面写的LHS采样一次只能生成某个时刻的出力样本,但真正的风光出力场景是完整的时间序列——比如24小时逐时出力曲线或96点出力曲线。怎么从单时刻采样扩展到时序场景?
常用的做法有两种。第一种是逐时刻独立采样,每个时刻单独拟合分布、单独LHS采样,然后把同一场景编号的样本拼起来形成时序曲线。这种做法简单直接,但会丢失相邻时段之间的自相关性——上午10点的风速和11点的风速本来应该相关性很强,独立采样出来的序列可能忽高忽低,看起来像白噪声。
第二种做法是考虑时间块的相关性结构,先对所有时刻的相关性矩阵做Cholesky分解,再对采样结果做线性变换,把相关性结构"注入"到场景中。这个方法我在下一节详细展开,单时刻独立采样只适合做初步验证。
实际项目里,我一般先用第二种方法生成初始的大量场景(比如1000条),然后再做削减,这样生成出来的时序曲线既保留了单时刻的分布特性,又保留了时间维度上的连续性,后续做优化调度计算时才不会出现"跳变式"的不合理出力曲线。
4. 多维变量下的相关性处理——又一个魔鬼细节
4.1 为什么不能忽略风光出力之间的相关性
我在做联合场景生成时遇到过一个问题:风电和光伏出力之间、不同时段的风速之间、不同风机位置的出力之间,天然存在很强的相关性。同在一个风电场区域内的两台风机,风速曲线高度相似;同一地区的风电和光伏,虽然机理不同,但都受大气环流影响,也存在一定程度的相关性。
如果直接在LHS采样时对每个变量独立采样,生成出来的联合场景里,风电出力和光伏出力可能完全不相关,这不符合物理规律。用这样的场景去做优化调度,算出来的配置方案和备用容量需求会出现明显偏差。
我踩过一次坑:某次做风光储联合系统的容量配置,忽略相关性时优化出来的储能容量是20MW/40MWh,加入相关性约束后变成了28MW/56MWh,差了40%。根本原因是忽略相关性后,系统认为风光出力可以互相弥补,但实际上两者往往同时偏低或同时偏高,需要的备用容量和储能配置自然不一样。
4.2 基于Cholesky分解的LHS相关性处理流程
处理这个问题的标准方法是基于Cholesky分解的LHS,业内叫LHS-CD(Cholesky Decomposition)。核心思路是:先对目标相关性矩阵做Cholesky分解,得到一个下三角矩阵L,使得相关性矩阵R = L·L^T。然后对LHS采样得到的独立样本矩阵S做线性变换S' = S·L^T,变换后的样本就带上了目标相关性。
完整代码流程如下:
matlab复制% 1. 构造相关性矩阵R(从历史数据计算得出)
R = corrcoef(historical_data);
% 2. Cholesky分解
L = chol(R, 'lower');
% 3. 生成独立LHS样本
S_independent = lhs_sample(dist_params, N, dim);
% 4. 变换为标准正态空间
S_norm = norminv(S_independent);
% 5. 注入相关性
S_corr = S_norm * L';
% 6. 变换回原始分布空间
S_final = normcdf(S_corr);
for j = 1:dim
switch dist_params{j}.type
case 'weibull'
S_final(:, j) = wblinv(S_final(:, j), dist_params{j}.a, dist_params{j}.b);
case 'beta'
S_final(:, j) = betainv(S_final(:, j), dist_params{j}.a, dist_params{j}.b);
end
end
这段代码的思路是:先把LHS样本变换到标准正态空间(因为正态分布做线性变换后还是正态分布,方便处理相关性),注入相关性后,再逆变换回原始的威布尔或Beta分布空间。这样就保证了最终样本既满足各变量的边际分布,又具备目标相关性结构。
这个方法在原理上很成熟,但有两个使用要点需要特别注意:
- 相关性矩阵必须是半正定的,否则Cholesky分解直接报错。实际使用中,历史数据算出的相关性矩阵偶尔会有微小的负特征值,这时要对矩阵做特征值修正(把负特征值截断为0再重构)。
- Cholesky方法注入的是线性相关性(皮尔逊相关系数),但威布尔分布和Beta分布之间的依赖关系未必是线性的。如果想更精细地建模,可以考虑Copula函数;不过对于绝大多数电力系统场景生成任务,线性相关性的精度已经足够。
4.3 时序自相关性的注入方法
除了变量间的横向相关性,每条出力曲线内部还存在纵向的时间自相关性——t时刻的出力高,t+1时刻出力大概率也高。这是我处理时序场景时绝不能忽略的一个特性。
处理方法是构造时间块的"块相关性矩阵"。假设要生成24点出力曲线,就构造一个24×24的相关性矩阵,矩阵中的元素表示时段i和时段j出力的相关系数。这个矩阵通常用指数衰减模型近似:
[
R_{ij} = \rho^{|i-j|}
]
其中ρ是相邻时段的自相关系数,通常取0.8到0.95。我一般在项目里取0.9,这算一个经过多次验证的默认值。ρ越大,曲线越平滑;ρ太小,曲线会出现不自然的剧烈波动。
把横向变量相关矩阵和纵向时间相关矩阵合并成一个大的联合相关矩阵,再用上面的LHS-CD流程做一次变换,就能同时处理两种相关性。这样生成出来的场景曲线,无论是单条曲线的平滑性,还是多条曲线之间的关联性,都更接近真实的风光出力数据。
5. 场景削减的两条路线——K-means聚类和同步回代消除法深度对比
5.1 场景削减问题的本质模型
LHS生成出1000个场景后,直接拿去算随机优化依然不现实。场景削减要解决的核心问题是:在尽量保留原始场景集合统计特征的前提下,用最少的代表场景逼近整个场景集。
数学上,这个问题可以描述为:设原始场景集为Ω,每个场景ω_i的概率为p_i,场景间的距离为d(ω_i, ω_j)。削减的目标是选出一个子集Ω'⊂Ω,并给每个保留场景重新分配概率,使得Ω'与Ω之间的某种距离度量(通常是Kantorovich距离)最小。
工程上,最流行的两种算法是K-means聚类法和同步回代消除法(也叫后向场景削减)。我在不同项目中两种都试过,各有适用场景。
5.2 K-means聚类削减:原理、代码和参数选择
K-means做场景削减的思路很直观:把1000个场景看成高维空间中的1000个点(维度就是时序长度),用K-means算法把它们聚成K类,每个类的质心就是一个代表场景,每类包含的场景数量占比就是该代表场景的概率。
MATLAB里自带的kmeans函数可以直接用:
matlab复制scenarios = zeros(1000, 96); % 1000个场景,每个96个时段
[idx, C] = kmeans(scenarios, K, 'Distance', 'sqeuclidean', 'MaxIter', 1000, 'Replicates', 10);
scene_prob = histcounts(idx, K) / 1000; % 各代表场景的概率
这里有两个参数很关键。一个是K的取值,K太小会丢失细节,K太大会失去削减的意义。我试过不同规模的系统,一般K取5到20之间。如果只是做两阶段的容量规划,K=10左右通常就够;如果做日内调度,可能需要K=20甚至更多来捕捉时序特征。
另一个是距离度量,默认用平方欧氏距离。但这里有个坑:如果场景中各时刻的出力值大小差异很大(比如中午光伏出力明显大于夜间),距离会被大数值时段主导,小数值时段的差异就被忽略了。解决方法是先做标准化,让每个时刻的出力落在一个统一的量纲下。
5.3 同步回代消除法:一个被低估的经典算法
同步回代消除法(Simultaneous Backward Reduction)的逻辑和K-means完全不同。它不聚类,而是从原始场景集中不断删场景:每次找到一对距离最近的场景,删掉其中一个,把它的概率加到另一个上面,直到剩下目标数量为止。这样留下的场景全部来自原始场景集,不需要像K-means那样计算"人造质心"。
核心伪代码如下:
matlab复制function [kept_scene, kept_prob] = backward_reduction(scenarios, probs, K_target)
n_scene = size(scenarios, 1);
kept_idx = 1:n_scene;
kept_prob = probs;
while length(kept_idx) > K_target
% 计算所有保留场景两两之间的距离
D = pdist2(scenarios(kept_idx, :), scenarios(kept_idx, :));
D(D == 0) = inf; % 自身距离设为无穷大,避免选择自身
[min_dist, lin_idx] = min(D(:));
[i, j] = ind2sub(size(D), lin_idx);
% 删除其中一个场景,概率叠加到另一个
if kept_prob(kept_idx(i)) > kept_prob(kept_idx(j))
kept_prob(kept_idx(j)) = kept_prob(kept_idx(i)) + kept_prob(kept_idx(j));
kept_idx(i) = [];
else
kept_prob(kept_idx(i)) = kept_prob(kept_idx(i)) + kept_prob(kept_idx(j));
kept_idx(j) = [];
end
end
kept_scene = scenarios(kept_idx, :);
kept_prob = kept_prob(kept_idx);
end
这个算法的优点有三个:一是保留的场景全部来自真实生成数据,不存在K-means质心那种"实际不存在的平均场景",解释性更强;二是保留了极值场景,K-means聚类会倾向于把极端场景淹没在大类里,而回代消除法在删除过程中会尽量保留远离群体的极端场景(前提是它们和其他场景的距离都比较大,不会被优先删掉),这对风险评估很重要;三是实现简单、逻辑直观。
缺点是计算复杂度高,每次迭代都要重新计算两两距离,1000个场景耗时在秒级,还能接受。但如果场景数到5000甚至10000以上,就要对距离计算做优化,或者改用快速前向选择算法了。
5.4 两种削减方法的路数对比:别迷信某一个
两种方法我都实际跑过对比实验,结论如下:
| 对比维度 | K-means聚类削减 | 同步回代消除法 |
|---|---|---|
| 保留场景来源 | 聚类质心(非原始场景) | 原始场景子集 |
| 极值保留能力 | 一般,容易被淹没 | 较好 |
| 计算效率 | 快(Replicates参数可加速) | 慢(O(n²)距离计算) |
| 场景数量敏感性 | 需要仔细调K | 对目标数不太敏感 |
| 概率分配 | 按类内占比 | 按删除过程中的概率叠加 |
| 适合场景 | 大规模场景快速削减 | 中小规模精细削减 |
我的建议是:1000个场景以下优先用同步回代消除法,场景数量不太大,计算还能接受,而且保留原始场景的特性让后续结果更容易解释;5000个场景以上优先用K-means,主要考虑计算效率,配合标准化处理也能得到不错的效果。
6. 削减后的场景质量评估——三个指标判断削减到底成功没有
6.1 基于统计量的评估:均值、方差和分位数偏差
削减完了不能直接拿结果就走,得先评估削减后的场景集到底功力有没有下降。我用的第一个评估手段是统计量对比:计算原始场景集和削减后场景集的风电/光伏出力均值、标准差、P5/P50/P95分位数,用相对误差来衡量。
计算公式是:
[
E_{mean} = \frac{|\mu_{reduced} - \mu_{original}|}{\mu_{original}} \times 100%
]
经验上,均值偏差控制在2%以内、分位数偏差控制在5%以内就达标了,可以放心把削减后的场景用于下游优化计算。
6.2 基于Kantorovich距离的评估:衡量两套概率分布的差距
Kantorovich距离是场景削减理论中最核心的评价指标,它衡量两个概率分布之间的"搬运成本"——可以理解为把原始分布的"概率质量"搬运到削减后分布上所需的最小工作量。
MATLAB里计算Kantorovich距离的代码不复杂:
matlab复制function kd = kantorovich_dist(scenarios_orig, prob_orig, scenarios_red, prob_red)
D = pdist2(scenarios_orig, scenarios_red, 'euclidean');
% 使用线性规划或匈牙利算法求解最优运输问题
% 这里简化实现,实际可用transport_lp函数
kd = compute_ot_cost(D, prob_orig, prob_red);
end
我一般看两方面的Kantorovich距离:削减前后总距离要相对小;同时看削减后再增加场景数能否显著降低距离,如果场景数从8增加到15,距离显著下降,说明8个场景不够;如果距离几乎不变,说明8个场景已经够用。这个方法可以用来科学地确定削减场景数K。
6.3 实际项目中的经验参考:K取多少合适
这个question被问得最多的就是"到底该削减到多少个场景?"说实话没有标准答案,但我给一个参考区间和一个判断方法。
参考区间:
- 配电网随机潮流/概率潮流计算:K=5~10,重点是均值和二阶矩准确
- 两阶段随机优化调度:K=10~20,需要兼顾时序相关性和极值场景
- 电力系统规划/容量配置:K=20~50,规划问题对精度要求高,算力预算通常也更充足
判断方法我前面提过,画一条"场景数 vs Kantorovich距离"的曲线,找一个明显的拐点。比如从K=8到K=10距离骤降,但从K=10到K=20距离下降趋于平缓,那K=10就是一个合理的取值。
6.4 极端场景的保留问题:K-means丢失的P95事故
这里讲一个我印象特别深的实际案例。有次做微电网规划,我用K-means把1000个场景削减到10个,优化出来的储能配置方案看着很合理,但是放进历史极端天气(连续三天阴雨天、光伏出力不足20%)下回测,微电网直接崩溃了。
后来排查原因,发现K-means把那几天极端场景和普通阴天场景聚到了一起,质心被拉成了一条"温和偏低"的曲线,极端缺电的状况完全被抹平了。而同步回代消除法因为保留原始场景子集,极端场景天生不容易被完全删掉,类似问题就没再出现过。
这给了我很深的一个教训:如果下游应用涉及可靠性评估、失负荷风险分析这类对极端情况敏感的场景,削减算法一定要谨慎,最好在削减后检查一下削减集里有没有覆盖原始集的高分位数场景;如果没有,建议换用同步回代消除法,或者用混合策略:先用K-means保底削减到大场景数,再用同步回代做精细削减。
7. 实操中容易踩的几个坑和对应的填坑方案
7.1 分布拟合的参数估计偏差导致场景整体偏移
最早跑LHS的时发现生成的场景和实际历史出力对不上,风电场平均出力明显偏低。排查了半天,问题出在风速数据的威布尔分布拟合上。风速数据里混着大量零风速时段(静风、风机待机),这些零值会把威布尔分布的形状参数k拉得很低,导致整体分布偏右。
解决方案是对历史数据做预处理:把风速小于切入风速(3m/s)的时刻单独建模,用"零出力概率+威布尔分布"的混合模型来描述风速。具体实现上就是:
matlab复制p_zero_wind = mean(v_history < v_cut_in);
% LHS采样时,先用均匀分布判断这个时段是否零风速
is_zero = rand(N, 1) < p_zero_wind;
% 非零时刻才用威布尔分布采样
这个方法我后来在多个项目里都用,风光出力场景生成的结果稳定性好了很多。
7.2 相关矩阵非正定导致Cholesky分解失败
处理高维相关性矩阵时经常会遇到Cholesky分解报错,原因是计算出的相关矩阵不是半正定。这不是代码bug,而是数值计算中的常见问题——尤其是历史数据有缺失值、或者是用不同来源数据拼接计算相关矩阵时,很容易出现微小负特征值。
填坑方法有两个。简单版本是加一个微小的对角扰动:
matlab复制R_reg = R + 0.001 * eye(size(R));
稍微复杂但更可靠的是特征值修正,把负特征值截断为0再重构相关矩阵:
matlab复制[V, D] = eig(R);
D(D < 0) = 0;
R_fixed = V * D * V';
实测下来,特征值修正的方法更稳,对角扰动虽然简单,但扰动大小不好把握,扰动大了会改变原始的相关系数结构。
7.3 K需不需要跟着新能源渗透率一起变?——自适应确定场景数的思路
新能源渗透率低时,系统对风电光伏不确定性的敏感度低,场景少点无所谓;渗透率高了,系统运行越接近边界,对极端场景的捕捉要求也越高,K就得跟着变大。
一个有价值的做法是去做敏感性试验:固定其他条件不变,让K从5扫到50,画出"目标函数值 vs K"的曲线。如果你的优化目标是总成本,你会发现K小的时候成本被系统性低估——因为极端场景的补偿成本没算进去。曲线通常在某个K值之后趋于平稳,那个拐点就是当前系统条件下的推荐场景数。
这个方法每次项目都做确实费时间,但至少做一次就能摸清自己这个系统的特性,以后同类问题可以直接复用,省下来的时间不止一点半点。
7.4 削减后概率出现零值或负值
同步回代消除法的概率叠加实现如果写得不仔细,可能会出现概率叠加后部分场景概率为零,甚至在某些异常情况下出现负概率。我的处理建议是:削减流程结束后,统一做一次概率归一化和非零检查:
matlab复制kept_prob(kept_prob < 1e-6) = 1e-6; % 下限截断
kept_prob = kept_prob / sum(kept_prob); % 归一化
这样既保证所有保留场景概率为正,又保证概率总和为1,下游优化模型不会因为概率问题报错。
8. 一套完整的工作链路总结:从原始数据到可用的代表性场景
最后把整套流程串起来,给读者一个直接能参考的工作链路。假设你手头有某风电场和光伏电站一年的历史出力数据,要生成一套用于随机优化调度的代表性场景,我的标准流程是:
第一步,数据预处理。清洗历史数据,剔除异常值和停机时段,把风速数据转成风电出力数据,光照强度转成光伏出力数据。
第二步,分布拟合。对每个时段的出力数据分别拟合分布——风速用威布尔分布、光照用Beta分布,如果出力数据本身有大量零值,用混合模型。
第三步,构造相关矩阵。从历史数据计算变量间横向相关性和时间方向自相关性,合并成联合相关矩阵,检查半正定性,必要时做特征值修正。
第四步,LHS采样生成大量场景。用LHS-CD流程生成1000个(或更多)时序场景,保证边际分布和相关结构都满足要求。
第五步,场景削减。用小规模优先用同步回代消除法,大规模用K-means聚类,削减到10~20个代表性场景。
第六步,质量评估。对比削减前后统计量偏差、计算Kantorovich距离、检查极端场景是否被保留,不达标就调整K或换削减方法重来。
第七步,输出结果。把代表场景和对应概率导出为矩阵格式,供优化调度、概率潮流或规划模型直接调用。
整套流程中,LHS负责的是"把分布空间覆盖得又全又均匀"这个核心任务,削减负责的是"在保留关键信息的前提下把规模降下来"这个效率任务。两者配合得好,才能既保证计算效率又不牺牲精度。
我在实际跑完这套流程后最大的感受是:场景生成与削减不是一锤子买卖,它是一个需要反复验证、不断调整的迭代过程。每一步的正确性都会影响最终结果的可信度。只有把每个环节的关键细节都打磨到位,下游的优化计算结果才能真正让人放心。
