1. 为什么要把风电和光伏放在同一个模型里研究
做新能源电力系统的人应该都有过这种体验:风电和光伏的出力看着都"随机",但随机的脾气完全不一样。风电靠的是风速,一天24小时可能忽大忽小,遇到低风速时段整个风场可能瘫在那里;光伏靠的是光照,白天有、晚上没有,中午强、早晚弱,规律性强得多。如果只是粗略地把它们当成"不稳定的电源",那建模精度根本不够,后面做容量配比、储能调度、可靠性评估都会跟着出问题。
所以就有了这个课题的方向——把风电出力和光伏出力分别用合适的概率分布来描述,再放到同一个框架里做组合分析。这里有一个行业共识:风速通常用两参数Weibull分布拟合,光伏出力用Beta分布拟合。注意,这里说的是"风速"用Weibull,但风电功率和风速之间存在非线性转换关系,实际工作中有时直接对功率做Weibull拟合,有时先拟合风速再换算,两种思路都有人用。光伏那边更直接,因为辐照度和出力在归一化后往往呈现双峰或偏态特征,Beta分布凭借其定义在(0,1)区间、形态灵活的特点,成了标准选择。
这个课题对三类人特别有参考价值:一是做电力系统规划的研究生,需要生成风-光联合场景;二是做调频调峰策略的工程师,需要刻画出力不确定性;三是刚入门概率建模、想把统计模型落地成Matlab代码的初学者。标题里提到的"组合研究",核心目标就是解决一个问题——如何把两种不同分布形态的随机变量放在一个可计算的框架里,让后续的蒙特卡洛模拟、场景削减、风险分析都能跑起来。
2. 风电的Weibull分布:从风速到功率的关键一步
2.1 两参数Weibull的数学原理,以及为什么它适合风速
两参数Weibull分布的概率密度函数长这样:
[ f(v) = \frac{k}{\lambda} \left( \frac{v}{\lambda} \right)^{k-1} \exp \left[ -\left( \frac{v}{\lambda} \right)^k \right] ]
其中 ( v ) 是风速,( \lambda ) 是尺度参数(scale parameter),( k ) 是形状参数(shape parameter)。( k ) 决定了分布的形状——等于1时退化为指数分布,等于2时是瑞利分布,大于3时越来越接近正态分布。实际风速数据的 ( k ) 值大多落在1.5到3之间,意味着风速分布是明显的右偏形态:大部分时间风速不高,偶尔有强风过程。
为什么行业里普遍选Weibull而不选正态分布?原因很实在:风速没有负数,最低就是0,而且不会对称分布在均值两侧。正态分布假设对称性,物理上就不对。Weibull分布的取值非负,又能通过参数调整任意适配偏态形态,加上累积分布函数有显式表达式:
[ F(v) = 1 - \exp \left[ -\left( \frac{v}{\lambda} \right)^k \right] ]
这在后续做逆变换采样时极其方便——直接对 ( F(v) ) 取反函数就能生成风速随机数。如果用正态分布,还得处理截断问题,凭空多出很多麻烦。
2.2 参数估计:极大似然估计的两大坑
Weibull参数估计最常用的是极大似然估计(MLE)。对数似然函数是:
[ \ln L = \sum_{i=1}^{n} \left[ \ln k + (k-1)\ln v_i - k \ln \lambda - \left( \frac{v_i}{\lambda} \right)^k \right] ]
对 ( k ) 求导并令其等于零,得到:
[ \frac{1}{k} + \frac{1}{n} \sum_{i=1}^{n} \ln v_i - \frac{\sum_{i=1}^{n} v_i^k \ln v_i}{\sum_{i=1}^{n} v_i^k} = 0 ]
这个方程没有解析解,只能在Matlab里用fzero或fsolve做数值求解。我在这块踩过几个坑,必须提醒你:
第一个坑:初始值选择。直接用fzero跑一元方程时,如果初始值给得太离谱(比如给了5以上),迭代经常发散。我的做法是先用经验公式估算初始值:
[ k \approx \left( \frac{\sigma}{\mu} \right)^{-1.086} ]
其中 ( \mu ) 是风速均值,( \sigma ) 是标准差。这个公式来自文献,准确率相当高,基本能让MLE在几次迭代内收敛。算完 ( k ) 后再用闭式公式算 ( \lambda ):
[ \lambda = \left( \frac{1}{n} \sum_{i=1}^{n} v_i^k \right)^{1/k} ]
第二个坑:风速数据里有零值或极小值。实际测风塔数据经常出现0 m/s或0.01这种值,取对数之后直接导致MLE崩溃。处理方式是二选一:要么把零值剔除(但要注意样本代表性),要么给零值加一个极小偏移量(比如0.001)。我实测下来,如果零值占比低于5%,直接剔除对拟合结果影响不大;占比高的要先确认测风设备是否故障,别急着建模。
2.3 从风速到功率:功率曲线转换的取舍
拟合出风速分布后,要得到风电出力分布,必须经过功率曲线转换。典型的风机功率曲线可用分段函数近似:
[ P(v) = \begin{cases} 0 & v < v_{cut_in} \text{ 或 } v > v_{cut_out} \ \frac{P_r (v^2 - v_{cut_in}^2)}{v_r^2 - v_{cut_in}^2} & v_{cut_in} \leq v < v_r \ P_r & v_r \leq v \leq v_{cut_out} \end{cases} ]
其中 ( v_{cut_in} ) 是切入风速(通常3~4 m/s),( v_{cut_out} ) 是切出风速(通常25 m/s),( v_r ) 是额定风速(通常12~15 m/s),( P_r ) 是额定功率。
这条转换关系对最终出力分布的影响远远被低估。我见过不少人辛辛苦苦把风速Weibull拟合得很漂亮,却忽略了功率曲线的非线性效应,导致最终的出力分布形态完全不对。原因在于:额定风速以下的区间,功率随风速平方增长;但额定风速和切出风速之间,功率被截断在额定值。这会在出力分布的高端引入一个概率堆积(mass point),出力为0的区间也因为有切入风速而出现概率堆积。这些"堆积"在PDF图上看得清清楚楚,但它不是拟合错误,而是物理约束带来的天然特征。
实操中如果手头没有具体的风机功率曲线,可以用简化模型近似。但要做高精度研究,建议拿实际风机厂商的功率曲线数据做插值,别用简化公式硬套。我遇到过的情况是,用简化模型估出来的出力均值偏差能到8%以上,这在容量规划里是很要命的误差。
3. 光电的Beta分布:归一化处理是绕不开的前置步骤
3.1 Beta分布的数学形态与物理直觉
Beta分布的概率密度函数定义为:
[ f(x) = \frac{\Gamma(\alpha + \beta)}{\Gamma(\alpha)\Gamma(\beta)} x^{\alpha-1} (1-x)^{\beta-1}, \quad 0 \leq x \leq 1 ]
其中 ( \alpha > 0 ) 和 ( \beta > 0 ) 是两个形状参数,( \Gamma(\cdot) ) 是伽马函数。这个定义天然限定了取值区间在(0,1),所以应用在光伏出力建模前必须先把实际出力归一到[0,1]区间——用实际出力除以装机容量(或最大出力)。
Beta分布为什么灵活?因为 ( \alpha ) 和 ( \beta ) 的相对大小能产生形态完全不同的PDF:二者相等时分布对称,( \alpha > \beta ) 时左偏,( \alpha < \beta ) 时右偏,二者都小于1时呈现U型(两头高中间低)。光伏出力数据经过归一化后经常表现出强烈的偏态,甚至双峰,Beta分布能覆盖这些形态,这是正态分布和均匀分布完全做不到的。
3.2 参数估计:从矩估计到MLE的完整流程
Beta分布的参数估计有两种常用路径。矩估计法简单直接,利用均值和方差反解参数:
[ \alpha = \mu \left( \frac{\mu(1-\mu)}{\sigma^2} - 1 \right) ] [ \beta = (1-\mu) \left( \frac{\mu(1-\mu)}{\sigma^2} - 1 \right) ]
其中 ( \mu ) 是样本均值,( \sigma^2 ) 是样本方差。这个方法的优点是计算量小,几行代码就能跑完;缺点是遇到极端数据(比如某段时间光伏出力几乎恒定为额定值)时,方差趋近于零,公式直接除零崩溃。
MLE方法需要数值优化。Beta分布的似然函数是:
[ \ln L = (\alpha-1)\sum \ln x_i + (\beta-1)\sum \ln(1-x_i) - n\ln B(\alpha, \beta) ]
其中 ( B(\alpha, \beta) = \Gamma(\alpha)\Gamma(\beta)/\Gamma(\alpha+\beta) ) 是贝塔函数。Matlab的betafit函数是现成的MLE解法,底层用的是fminsearch。我实测下来,当样本量大于200时,betafit表现稳定;样本量小的时候建议回到矩估计,因为MLE在小样本下可能出现边界位置的病态解。
3.3 归一化边界处理:0和1的"洁癖"
光伏数据处理中有一个常见的头痛问题:归一化后出现大量恰为0或1的数据点。比如阴天的时候出力就是0,中午云层薄的时候出力可能短暂达到额定值。Beta分布的支撑区间是开区间(0,1),严格来说0和1不在定义域内,直接塞给betafit会报错。
处理方案有三个:
- 压缩变换:把所有数据点向中间压缩一个小偏移量,比如 ( x' = (x \cdot (n-1) + 0.5) / n ),其中 ( n ) 是样本量。这是从经验分布函数的分位数变换引申来的技巧,能保持数据整体形态几乎不变。
- 剔除边界点:直接丢掉0和1,只对开区间内的数据拟合。这种做法的缺点是可能丢掉信息——如果数据里0占比很高,剔除后拟合出的分布会高估整体出力水平。
- 零膨胀Beta模型:把0和1的概率单独建模,中间连续部分用Beta分布描述。这是统计学里比较精细的路线,但Matlab实现起来要手写EM算法或者MCMC,对多数人来说过重了。
我的建议是:做工程评估用压缩变换,简单且效果足够;做学术研究且数据边界点占比超过10%,考虑零膨胀模型,否则你的分布拟合得再漂亮,积分出来的期望出力也会偏。
4. Matlab组合实现:完整代码架构与核心逻辑拆解
4.1 程序整体设计思路
两个分布分别建模不复杂,真正的难点是把它们组合在一个框架里,后续能支持场景生成和统计分析。我建议的代码架构分四层:
- 数据层:读入风速时序数据和光伏出力时序数据,统一清洗、归一化、按时间对齐。
- 拟合层:分别估计Weibull参数和Beta参数,输出参数到工作区同时画拟合诊断图。
- 组合层:基于两个分布的独立采样生成风-光联合场景,这里用Copula或者简单联立都不难,关键是要有清晰的中间数据结构。
- 分析层:对联合场景做Monte Carlo模拟,输出出力均值、方差、极端场景概率等指标。
4.2 主程序核心代码
%% 主程序:风-光概率组合建模 % 清空环境 clear; clc; close all; % 加载数据(假设风速矩阵wind_speed和光伏出力矩阵pv_power) load('wind_pv_data.mat'); wind_speed = wind_speed(:); % 整理成一维 pv_power = pv_power(:); % 整理成一维 %% 1. Weibull分布拟合 % 初步估算形状参数(基于变异系数法) mu_w = mean(wind_speed); std_w = std(wind_speed); cv = std_w / mu_w; k_init = cv^(-1.086); % 经验公式初始值 lambda_init = mu_w / gamma(1 + 1/k_init); % 极大似然估计(MLE) pdf_weibull = @(x, k, lambda) (k/lambda) .* (x/lambda).^(k-1) .* exp(-(x/lambda).^k); neg_log_lik = @(params) -sum(log(pdf_weibull(wind_speed, params(1), params(2)))); options = optimset('Display', 'off', 'TolX', 1e-6); params_w = fminsearch(neg_log_lik, [k_init, lambda_init], options); k_w = params_w(1); lambda_w = params_w(2); % 输出拟合结果 fprintf('Weibull拟合参数: k = %.4f, lambda = %.4f\n', k_w, lambda_w); %% 2. Beta分布拟合(使用betafit) pv_norm = pv_power / max(pv_power); % 归一化到(0,1)附近 % 边界压缩:避免0和1导致betafit报错 n_pv = length(pv_norm); pv_norm_adj = (pv_norm * (n_pv - 1) + 0.5) / n_pv; [alpha_b, beta_b] = betafit(pv_norm_adj); fprintf('Beta拟合参数: alpha = %.4f, beta = %.4f\n', alpha_b, beta_b); %% 3. 组合场景生成(基于独立采样) N_scen = 5000; % 场景数量 wind_scen = wblrand(N_scen, k_w, lambda_w); % Matlab内置Weibull采样 pv_scen = betarnd(alpha_b, beta_b, N_scen, 1) * max(pv_power); % 注:betarnd生成的是(0,1)区间,要乘回装机容量 %% 4. 统计分析 wind_mean = mean(wind_scen); pv_mean = mean(pv_scen); wind_p95 = wblinv(0.95, k_w, lambda_w); pv_p95 = betainv(0.95, alpha_b, beta_b) * max(pv_power); fprintf('风速均值: %.2f m/s, 95%%分位风速: %.2f m/s\n', wind_mean, wind_p95); fprintf('光伏出力均值: %.2f kW, 95%%分位出力: %.2f kW\n', pv_mean, pv_p95); %% 5. 拟合诊断图 figure('Color', 'w'); subplot(2,1,1); histogram(wind_speed, 50, 'Normalization', 'pdf', 'FaceAlpha', 0.6); hold on; v_plot = linspace(0, max(wind_speed)*1.1, 200); plot(v_plot, wblpdf(v_plot, k_w, lambda_w), 'r-', 'LineWidth', 2); xlabel('风速 (m/s)'); ylabel('概率密度'); title('风速Weibull分布拟合效果'); legend('实际数据', 'Weibull拟合'); subplot(2,1,2); histogram(pv_norm_adj, 50, 'Normalization', 'pdf', 'FaceAlpha', 0.6); hold on; x_plot = linspace(0, 1, 200); plot(x_plot, betapdf(x_plot, alpha_b, beta_b), 'r-', 'LineWidth', 2); xlabel('归一化光伏出力'); ylabel('概率密度'); title('光伏出力Beta分布拟合效果'); legend('实际数据', 'Beta拟合');4.3 各段代码的意图与易踩的细节
这段代码看起来不长,但每一段都有"设计意图"在里面。拿初始值的确定来说,k_init用的变异系数经验公式不是随便来的——它是实测风速数据统计规律总结出来的,比瞎猜收敛速度快得多,能减少fminsearch陷入局部极值的概率。
betafit前的边界压缩是很多教程没提但实务中几乎必须做的一步。我在处理一个真实光伏电站数据时就碰到过,整整有17%的出力数据落在0或1上,如果不压缩,Matlab直接抛"betafit: data must be in (0,1)"错误。压缩之后拟合照跑,参数也合理。
场景数量N_scen = 5000是我试出来的经验值。少于1000时统计矩不稳定,均值波动幅度能到3%以上;超过10000时计算时间大幅上升,但统计精度提升有限。5000是个平衡点,既够用又跑得快。另外注意betarnd返回的是(0,1)区间,成量纲必须乘回装机容量,否则后面对比出力水平时会错一个数量级。
5. 常见问题与排查技巧实录
5.1 参数估计发散或不收敛
这是遇到最多的问题。表现是fminsearch跑了半天,最后参数变成NaN或者无穷大。原因基本逃不开两种:一是数据里有负数或零(风速测风设备故障、光伏夜间数据没剔除);二是初始值给得太差,目标函数曲面太陡峭,数值优化直接溢出。
排查步骤我总结为三步:先做数据清洗,风速里出现负数直接删或者置零,光伏出力出现过大于装机容量的值基本是数据采集错误,建议核对原始记录。再检查归一化,确认所有数据都在合理区间内。最后调整优化器,如果数据集上万条,fminsearch的单纯形法收敛太慢,改用fminunc配合梯度信息,或者写一个简单的梯度下降循环,反而更快更稳。
5.2 拟合结果和直方图明显不符
模型参数给了,画出来却不贴合数据,通常是混合分布问题。比如风速数据来自两个不同季节——冬季大风和夏季小风混合在一起,整体分布会出现双峰,单条Weibull曲线根本拟合不了。
这种情况不要硬拗Weibull,正确做法是分层建模:按月份切分、按昼夜切分,或者用混合Weibull模型(两个Weibull加权叠加)。我在实际项目里就遇到过海上风电数据,冬天和夏天的风速分布差异很大,合并拟合出的模型在冬季场景下会系统性低估高风速概率,直接影响极端天气下出力评估的准确性。
光伏那边类似,晴天和阴天的出力分布形态天差地别。一个Beta分布想把这两种天气都包住,拟合出来会是一个"高不成低不就"的妥协解。实务做法是先用天气聚类把历史数据分成晴天样本集和阴天样本集,分别拟合两个Beta分布,再按气象概率做加权组合。这一步虽然增加工作量,但对后续可靠性评估的精度提升非常明显。
5.3 Matlab环境相关的排查
代码跑不动还有环境层面的原因。老版本Matlab(2019之前)对betafit的输入校验更严格,边界压缩稍微没做好直接报错。新版本(2023之后)对数组维度的兼容更友好,但工具箱必须要装Statistics and Machine Learning Toolbox,否则连betafit都不认识。
有一个省事的技巧:如果目标机器上没有统计工具箱,用矩估计自己手写Beta参数的闭合解,代码不超过10行,虽然精度略低,但至少能跑通主流程。定位完问题再装工具箱回跑MLE,比卡死在环境问题上好得多。
还有一个值得注意的坑是随机数种子。如果你跑Monte Carlo模拟,不设置rng(SEED)的话,每次运行结果都不同,不稳定的话没法复现自己的结果。代码开头加一行rng(42),后续所有测试对比才有可比性。
6. 实操经验:组合模型的三个隐藏价值
6.1 相关性建模才是"组合"研究的精髓
独立采样只做了一半。实际风电场和光伏电站如果地理位置接近,风速和辐照度往往存在负相关关系——大风天云层薄,光伏出力高;静风天阳光好,但风电量低。忽略这种相关性直接独立采样,产生的联合场景会高估"风-光同时出力"的概率,低估"风-光双低"的场景,这直接影响储能的容量配置结论。
进阶做法是引入Copula。常见的选择是Gaussian Copula或t-Copula,思路是先分别把风速和光伏出力变换成标准正态空间,算相关系数矩阵,再采样回来。Matlab里实现这个需要copularnd函数,配合上面拟合好的边缘分布,代码量比独立采样多不了多少,但场景质量完全不同。我建议拿到基础代码后,第一优先级就是加这一层。
6.2 "组合"不只是概率分布,还是系统指标的计算框架
两个分布组合的意义在于,你有了一个能生成任意长度风-光出力时序的概率引擎。基于这个引擎,你可以算很多系统级的东西:
- 风-光联合出力的概率密度,直接看出力在全时间尺度上的波动范围。
- 风-光互补性指标——比如出力波动相关性,如果相关系数为负,说明两种能源在时间尺度上确实有互补潜力,这对制定"风光互补"的调度策略有直接指导价值。
- 极端事件概率——比如连续三天风速低于切入风速且辐照度低的场景,这种"风-光双枯"事件虽然概率低,但对电力平衡的影响可能是灾难性的,需要在规划阶段就量化清楚。
6.3 求解速度的优化心得
基础代码用fminsearch和betafit就能跑,但数据量变大时(比如分钟级数据,一年52万条),Matlab跑起来明显吃力。用parfor而不是for,可以把场景生成的耗时压缩到原来的三分之一;用矢量化运算,避免循环里单个采样,也能快不少。实测过一组数据:5万条历史数据在普通笔记本上,完整流程从加载到输出所有指标,矢量化版本大约30秒跑完,循环版本需要三分钟以上。别小看这两分钟的差距,你后面要跑敏感性分析至少几百次实验的时候,差距就是几个小时和两天的区别。
7. 写在最后的几条进展方向
做风-光组合建模,这个题目看着基础,其实延伸空间很大。模型从独立采样到Copula相关采样,是已经被验证的必要升级;从静态参数到动态滚动更新参数,能适配出力统计特性随季节变化的现实;从单风场单电站扩展到区域级多场站联合建模,是实际工程项目的必经之路。
我个人在跑完这套流程后最大的体会是:Matlab代码实现本身虽然重要,但它只是"包装盒"。真正值钱的认知在于理解每个分布假设背后的物理前提——Weibull对应湍流风速的统计特性,Beta对应归一化辐照度的边界约束,把这些物理逻辑搞清楚,代码只是最后一步的转录工作。如果只是把参数估计出来画个图发篇论文,那真的浪费了这套组合模型的潜力。能把这套概率引擎接进你后续的调度优化、可靠性评估、容量规划框架里,才算是真正吃透了这个题目。