风光资源评估实战:ERA5 多年逐时数据做代表年与 P50/P90 发电量估计

风电、光伏项目从踏勘到投决,绕不开三个数字:代表年(典型气象年)的资源水平、年际变率有多大、以及在不同保证率下的发电量到底能压到多少。融资方关心 P90、设计方关心 P50、运营方关心实际会不会被"两个细则"考核扣分。这些数字的底座,越来越多地从单点测风塔/辐射站,转向了长序列再分析数据。ERA5 提供了 1940 年至今、逐小时、0.25° 网格的全球场,正好补上"测站只有一两年、空间只有一个点"的短板。本文把一套可落地的风光资源评估流程拆开讲:怎么下长序列、怎么挑代表年、怎么算年际变率与 P50/P90、风速廓线怎么外推到轮毂高度、辐照怎么换算成发电量,并在最后给出在运梦气象 API 上一段可直接跑的下载代码。
关键要点
- 序列长度:资源评估至少取 10 年、最好 20 年以上的逐时序列,再分析不替代实测,它替代的是实测年数不足这个缺口。
- 下载策略:20 年逐时叠加多字段是 17 万行量级,必须按自然年切片下载,每片单独落盘失败只重试该片,天然形成幂等缓存并可并发。
- 风速外推:用 ERA5 的 10m 与 100m 两个高度逐时反算风切变指数 α(α = ln(ws100/ws10) / ln(100/10)),再外推到轮毂高度,比固定 1/7 律可靠。
- 辐照换算:组件进光是倾斜面总辐照 GTI 而非 GHI,最佳倾角下 GTI 年累计通常比 GHI 高 10%~25%,漏掉这步会系统性低估发电量。
- 保证率合成:P90 = P50 ×(1 − 1.282 × CV_total),CV_total 须把气象年际波动与模型不确定度按平方和合成,诚实叠加才能过融资尽调。
为什么资源评估要用多年长序列
单年数据最大的问题是"赶上了好年还是坏年"完全靠运气。风资源的年际波动通常在 ±6%~10%(以年均风速计,换算到发电量会被风机功率曲线的近立方关系放大到 ±15% 以上);光伏的年际辐照波动相对小一些,多在 ±3%~5%,但遇到火山气溶胶、强厄尔尼诺年也会明显偏离。只用一年数据做投决,相当于拿一次抽样去估计整个分布的均值,置信区间宽到没有意义。
行业通行做法是取至少 10 年、最好 20 年以上的逐时序列,先得到长期均值与逐年分布,再用这个分布去刻画代表年和保证率。ERA5 的优势正在于此:时间足够长、采样足够密、空间连续,能把测风塔短期实测通过 MCP(Measure-Correlate-Predict,实测-相关-外推)订正到长期水平。需要强调,再分析不替代实测——它替代的是实测年数不足这个缺口。ERA5 作为 ECMWF 第五代再分析产品,同化了海量地面、探空、卫星观测,时空一致性远好于把零散站点硬拼起来的拼盘数据。
长序列下载:为什么必须切片
拉 20 年逐时、再叠加 100m 风分量、辐照三分量,单点也是 17 万行量级。直接一个请求拉全程,几乎必然踩坑:网关超时、内存峰值、单次失败要从头重来。务实的工程做法是按年(或按季)切片,每片单独成功落盘,失败只重试那一片。
切片还有三个附带好处:一是天然形成幂等的缓存单元,重跑时已落盘的年份直接跳过;二是可以并发,多个年份同时拉,吞吐上得去;三是便于做数据质检——每片落地后先检查时间轴连续性、缺测率、字段量纲是否合理,再进入下一步,避免脏数据污染整条链路。切片粒度上,逐时数据按"自然年"切是最省心的,既对齐代表年的统计口径,又让缺测补齐、闰年处理都落在清晰边界内。注意时区:ERA5 原生是 UTC,运梦气象 API 用 timezone 参数统一换算,国内项目固定填 "8"(东八区),避免后面做日累计电量时把跨日的小时切错。
风速廓线:从 10m 到 100m 再到轮毂高度
风机轮毂高度普遍在 90~140m,而最经典的气象观测在 10m。ERA5 直接给了 u100/v100(100m 风分量),先用它合成 100m 风速:ws100 = sqrt(u100² + v100²)。但很多轮毂不在 100m 整,需要在两个高度间做廓线外推。
工程上两套常用方法。一是对数律:v(z) = v(zr)·ln(z/z0)/ln(zr/z0),其中 z0 是地表粗糙度,开阔农田约 0.03m、灌木约 0.1m、城郊约 0.5m。二是幂律(Hellmann 公式):v(z) = v(zr)·(z/zr)^α,风切变指数 α 是关键——开阔平原昼间约 0.14(经典 1/7 律),夜间稳定边界层可升到 0.3 以上,海上低至 0.1。最稳妥的做法不是套经验 α,而是用 ERA5 自带的 10m 与 100m 两个高度反算当地、当时的 α:α = ln(ws100/ws10) / ln(100/10),再用这个逐时 α 外推到真实轮毂高度。这样把昼夜、季节、稳定度的变化都吃进去了,比固定一个 1/7 律可靠得多。注意一点:ERA5 的 100m 风在复杂地形、近海岸带会有系统偏差,复杂地形项目仍需用测风塔做 MCP 订正,不能只信再分析。运梦气象 API 同时提供 uas/vas(10m 分量)与 ws/wd(派生风速、风向),合成与反算 α 所需的字段都能一次取齐。
辐照到发电量:别只盯着 GHI
光伏侧 ERA5 给三个辐照量:rsds(地表水平面总辐照 GHI)、dni(直接法向)、dhi(散射水平)。组件实际进光是倾斜面总辐照 GTI/POA,不是 GHI。从 GHI 到 GTI 要做三件事:用太阳几何把 DNI 投到组件平面、加上各向异性天空散射模型(Perez 或 HayDavies)处理 DHI、再补上地面反射(反照率默认 0.2,雪地可到 0.6)。最佳倾角下 GTI 年累计通常比 GHI 高 10%~25%,这一步漏掉就会系统性低估发电量。
拿到 GTI 后,发电量不是简单乘效率。要扣组件温度损失(晶硅温度系数约 -0.35%/°C,可用 tas 加 NOCT 模型估组件温度)、逆变器效率、线损、失配、灰尘遮挡、可用率。粗算可用 PR(系统性能比,典型 0.78~0.83)一把扣完:年发电量 ≈ GTI 年累计 × 装机容量 / 1000 × PR。精算建议上 pvlib,把单二极管模型、逆变器削顶、跟踪支架都建进去。运梦气象 API 的 rsds 已按 W·m⁻² 返回逐时平均功率密度,逐时累加再除 1000 即得 kWh,量纲上不用再做 J→W 的 /3600 换算,省掉一类常见错误。
代表年、年际变率与 P50/P90
有了多年逐时序列与逐年发电量估计,最后一步是统计。代表年(TMY,典型气象年):经典做法是 Sandia 法,对每个月分别从历史各年的同月里挑一个最接近长期分布的月份(用辐照、气温等的累积分布函数 FS 统计量打分),12 个月拼成一个虚拟的代表年。它的用途是给逐时仿真提供一条典型的输入曲线,而不是用来算保证率。
年际变率:对逐年发电量序列求标准差与变异系数 CV(CV = σ/μ)。风电年发电量 CV 常在 4%~8%,光伏在 2%~4%。P50/P90:P50 就是长期均值(中位水平),P90 指有 90% 的概率超过该值的保守发电量。常用做法是把年际变率视为正态,P90 = P50 ×(1 − 1.282 × CV_total),这里 1.282 是标准正态分布 90% 分位对应的 z 值。注意 CV_total 不只有年际气象波动,还要把测量/模型不确定度(再分析偏差、廓线外推误差、功率曲线误差、可用率波动)按平方和合成进去:CV_total = sqrt(CV_气象² + CV_模型² + ...)。融资方看的 P90 往往比只算气象波动的版本低好几个点,差别就在这些不确定度有没有诚实地加进去。
在运梦气象 API 上手
下面用历史数据源(dataSourceId="era5")按年切片拉一个站点 20 年的 100m 风分量与 GHI。响应是统一 JSON envelope,用 resp.json() 解析后取 data"timeList" 与各字段数组,按下标一一对应。
import os, time, requests
import numpy as np
API = "https://console.yun-meng.top/api/energy-weather/search/weather/action/downloadSync"
TOKEN = os.environ["YUNMENG_TOKEN"]
def fetch_year(year, lat, lon):
payload = {
"dataSourceId": "era5", # 历史用 ERA5;预报请用 "ger"(德国气象局,未来约 7 天)
"lat": lat, "lon": lon,
"stime": f"{year}-01-01 00:00",
"etime": f"{year}-12-31 23:00",
"timezone": "8", # 必填,时区偏移,东八区
"fields": ["u100", "v100", "ws", "rsds", "tas"],
}
r = requests.post(API, headers={"Authorization": f"Bearer {TOKEN}"},
json=payload, timeout=600)
r.raise_for_status()
result = r.json() # 统一 envelope,无 CSV/NetCDF、无 format 参数
if not result.get("success"):
raise RuntimeError(result.get("msg", "查询失败"))
return result["data"]
lat, lon = 31.5, 118.5
annual_mwh = []
for y in range(2004, 2024): # 20 年切片,失败只重试该年
data = fetch_year(y, lat, lon)
times = data["timeList"] # 时间轴,与各字段按下标对齐
u100 = np.asarray(data["u100"], dtype=float)
v100 = np.asarray(data["v100"], dtype=float)
ghi = np.asarray(data["rsds"], dtype=float)
ws100 = np.hypot(u100, v100) # 100m 风速
# 光伏粗算:GTI≈GHI 简化,PR 一把扣(精算请上 pvlib)
pr, cap_kw = 0.80, 1000.0
e_kwh = np.clip(ghi, 0, None).sum() / 1000.0 * cap_kw * pr
annual_mwh.append(e_kwh / 1000.0)
print(f"{y}: 小时数={len(times)}, 年均100m风速={ws100.mean():.2f} m/s, 估算电量={e_kwh/1000:.0f} MWh")
time.sleep(0.5) # 友好限速
a = np.asarray(annual_mwh)
p50 = a.mean()
cv = a.std(ddof=1) / p50
p90 = p50 * (1 - 1.282 * cv) # 仅气象年际波动,未叠加模型不确定度
print(f"P50={p50:.0f} MWh, CV={cv*100:.1f}%, P90(气象)={p90:.0f} MWh")
预报场景(如做短期功率预测训练对齐)把 dataSourceId 换成 "ger" 即可,由德国气象局预报数据覆盖未来约 7 天,字段口径与 ERA5 一致,业务侧不用维护两套换名逻辑。
收尾:把数字背后的假设写清楚
资源评估最容易出问题的不是公式,而是假设没写清楚:用了几年序列、廓线 α 怎么来的、GTI 转换用了哪个散射模型、PR 取了多少、P90 里到底叠了哪些不确定度。把这些列成一张可复核的表,比给一个漂亮的 P90 数字更有价值。ERA5 长序列负责把年际波动这部分讲明白,测风塔/辐射站负责把绝对水平锚准,两者结合、诚实合成不确定度,才是能过融资尽调的资源评估。先用运梦气象 API 把 20 年逐时序列拉下来,剩下的就是把每个假设逐一坐实。
常见问题
风光资源评估到底需要多少年的数据?
行业通行做法是至少 10 年、最好 20 年以上的逐时序列。单年数据相当于拿一次抽样去估计整个分布的均值,置信区间宽到没有意义;只有足够长的序列才能刻画风资源 ±6%~10%、光伏 ±3%~5% 的年际波动。
P50 和 P90 有什么区别,P90 怎么算?
P50 是长期均值(中位水平),P90 指有 90% 的概率超过该值的保守发电量。常用做法把年际变率视为正态:P90 = P50 ×(1 − 1.282 × CV_total),其中 1.282 是标准正态分布 90% 分位的 z 值,CV_total 须把气象波动与模型不确定度按平方和合成。
ERA5 的 100m 风速能直接当作轮毂高度风速用吗?
不能直接套用。ERA5 给的是 100m 风分量,而轮毂普遍在 90~140m,需要外推。最稳妥的做法是用 ERA5 的 10m 与 100m 反算逐时风切变指数 α 再外推到真实轮毂高度;复杂地形、近海岸带还存在系统偏差,仍需用测风塔做 MCP 订正,不能只信再分析。
算光伏发电量为什么不能只用 GHI?
因为组件实际进光是倾斜面总辐照 GTI/POA,不是地表水平面总辐照 GHI。从 GHI 到 GTI 要做太阳几何投影、各向异性天空散射模型和地面反射三步,最佳倾角下 GTI 年累计通常比 GHI 高 10%~25%,只盯 GHI 会系统性低估发电量。
用运梦气象 API 做预报场景该用哪个数据源?
历史评估用 dataSourceId="era5"(ERA5);预报场景把它换成 "ger" 即可,由德国气象局预报数据覆盖未来约 7 天,字段口径与 ERA5 一致,业务侧不用维护两套换名逻辑。
参考与延伸阅读
- Hersbach, H., Bell, B., Berrisford, P., et al. (2020). "The ERA5 global reanalysis." Quarterly Journal of the Royal Meteorological Society, 146(730), 1999–2049. https://doi.org/10.1002/qj.3803
- International Electrotechnical Commission (2025). IEC 61400-15-1:2025, Wind energy generation systems – Part 15-1: Site suitability input conditions for wind power plants, Edition 1.0. Geneva: IEC.
- Perez, R., Ineichen, P., Seals, R., Michalsky, J., & Stewart, R. (1990). "Modeling daylight availability and irradiance components from direct and global irradiance." Solar Energy, 44(5), 271–289. https://doi.org/10.1016/0038-092X(90)90055-H
- Wilcox, S., & Marion, W. (2008). Users Manual for TMY3 Data Sets (Revised). Technical Report NREL/TP-581-43156. Golden, CO: National Renewable Energy Laboratory. https://docs.nrel.gov/docs/fy08osti/43156.pdf