1. CAMS到底是什么:一份能直接用于业务分析的大气成分预报数据
做大气环境分析、空气质量预测或者新能源功率预测的朋友,一定绕不开一个问题:上哪儿弄一套覆盖范围广、更新及时、又不需要自己搭WRF-Chem去跑的大气成分数据。我最早接触CAMS纯属被项目逼的——当时要做某地区的颗粒物浓度变化趋势分析,地面监测站稀疏,卫星反演又只有柱浓度,怎么都凑不齐近地面PM2.5的时间序列。最后是从ECMWF的哥白尼大气监测服务(Copernicus Atmosphere Monitoring Service,简称CAMS)里提取了分析和预报字段,才把数据链条补全。
CAMS数据集从本质上看,是一套由欧洲中期天气预报中心(ECMWF)运行的全球大气成分再分析和预报产品。它跟气象领域熟知的ERA5是同一个体系下的产物,只不过侧重点从“天气”转向了“大气成分”。数据集涵盖气溶胶(如PM2.5、PM10、沙尘、海盐、硫酸盐)、痕量气体(如NO2、O3、SO2、CO、HCHO)以及温室气体(CO2、CH4)等几十种变量,空间覆盖全球,时间上既有过去几十年的再分析资料,也有面向未来几天的业务预报。简单来说,它就是一个不用你自己跑模式、不用自己处理排放源清单、下载就能用的“全球大气成分标准答案库”。
这篇文章适合谁?一类是做空气质量监测与污染溯源的研究人员,另一类是把大气数据当特征喂进算法的算法工程师,还有一类是搞光伏、风电功率预测的新能源从业者。我会从数据机制、下载实操、GRIB文件的读取处理,再到实际项目里的应用踩坑,完整走一遍。你会发现,这套数据集真正用起来,门槛并不在“下载”,而在“理解它的时空坐标体系”。
2. 下载前必须清楚的三件事:时空分辨率、变量体系和许可规则
2.1 时空分辨率:0.4度网格与3小时步长是怎么设计出来的
CAMS全球预报数据的时间设计是这样的:每天有00时和12时两个起报时次,每个起报时刻向后预报120小时(5天),时间步长3小时。也就是说,无论你什么时候去取数,一天最多能拿到两个起报批次,每个批次40个预报时次(包含起报时刻本身)。这在业务预测里是很标准的配置,上午起报的结果通常下午就能拿到,刚好接上当天做次日空气质量预报的节奏。
空间分辨率方面,CAMS全球预报是0.4度乘0.4度的等经纬网格,换算到赤道附近大约40公里。这个分辨率跟ERA5的0.25度相比略粗一些,但它不是气象场,是大气成分场,化学传输模式本身对排放清单和化学反应机制的敏感性远大于网格分辨率,所以40公里级别对区域污染过程的刻画已经够用。你可能还会看到CAMS区域预报产品,比如欧洲区域有0.1度的高分辨率版本,但全球产品只有这一个档位。做中国区域分析时,40公里网格看起来粗糙,实际用双线性插值把数据取到站点位置后,误差不会比你用WRF-Chem跑一个36公里嵌套网格更大,反而胜在稳。
2.2 变量体系:近地面浓度和气溶胶光学厚度怎么选
CAMS的变量体系非常庞杂,但对普通用户来说,主力变量集中在两个层面。近地面浓度层面,最常用的是PM2.5和PM10质量浓度,分别对应变量名pm2p5和pm10,单位是μg/m³。这是直接可以跟国控站点数据对比的字段,也是大多数空气质量分析的核心输入。再往上是气溶胶光学厚度(AOD),变量名aod550,代表550纳米波段整层大气气溶胶的光学衰减,对应卫星反演产品常见的AOD概念。
痕量气体方面,近地面的二氧化氮(no2)、臭氧(o3)、一氧化碳(co)、二氧化硫(so2)都以体积混合比单位(mol/mol或ppb/ppm)给出,使用时需要按温度和气压换算成质量浓度,或者直接用体积比跟监测站换算。甲醛(hcho)常用于示踪VOCs光化学反应,在大气氧化性分析里也有价值。气溶胶组分变量也值得关注,比如沙尘(dust)、海盐(sea_salt)、黑碳(black_carbon)、有机气溶胶(organic_matter)等,它们分别以质量浓度给出。这些组分变量在做污染来源解析时特别有用,比如判断一次PM10超标事件是沙尘主导还是人为扬尘主导,直接对比沙尘质量浓度贡献就能看个大概。
2.3 许可规则与版本选择
CAMS数据在EU的哥白尼计划框架下免费开放,个人和商业使用都不需要付费。需要做的只是在CDS(Climate Data Store)官网注册账号,同意数据许可协议后获取API密钥。这里有个容易被忽略的点:免费不等于随便用,发布成果时要求注明数据来源,具体引用格式在CDS的文档里都有。
版本方面,CAMS全球大气成分产品目前有再分析(EAC4)和预报两套主产品线。EAC4再分析从2003年至今,是经过观测同化的长期数据集,做历史趋势分析和气候尺度研究更可靠;预报产品则是实时滚动更新的,适合业务预报和时效性要求高的应用。两者变量结构和单位基本一致,但网格略有差异,EAC4是0.75度,预报是0.4度,混合使用时建议做统一重采样。
3. 下载实操:从CDS注册到API批量取数
3.1 第一步:完成账户注册并拿到API密钥
访问CDS官网(cds.climate.copernicus.eu),注册流程和普通网站差不多,邮箱验证后登录。登录后在用户中心的“Personal access token”页面能看到一串密钥,这串密钥是后面API调用的凭证。
拿到密钥后,在家目录下创建一个.cdsapirc文件,内容如下:
url: https://cds.climate.copernicus.eu/api key:你的UID_你的密钥UID在同一个页面也能看到,通常是一串数字。.cdsapirc文件在Windows下的位置是C:\Users\你的用户名\.cdsapirc,在Linux和macOS下是~/.cdsapirc。配置好这个文件后,Python调用cdsapi时会自动读取,省去每次在脚本里写凭证的麻烦。
3.2 第二步:用cdsapi发起数据请求
安装CDS的官方Python客户端,一行命令:
pip install cdsapi接下来是最关键的请求脚本。以下是我常用的下载模板,可以按需修改:
import cdsapi c = cdsapi.Client() c.retrieve( "cams-global-atmospheric-composition-forecasts", { "type": "forecast", "format": "grib", "variable": [ "particulate_matter_d_2_5um", "particulate_matter_d_10um", "nitrogen_dioxide", "ozone" ], "date": "2024-03-15/2024-03-17", "time": "00:00", "leadtime_hour": "0/3/6/9/12/15/18/21/24/27/30/33/36/39/42/45/48", "area": [40, 110, 20, 125], }, "cams_forecast_20240315.grib" )这个脚本里最关键的是area参数,格式是[北界, 西界, 南界, 东界]。我设置的是北纬40度到20度、东经110度到125度,大致覆盖华东地区。这样能避免下载全球文件,既节省磁盘空间也大幅缩短排队时间。下载完成前,CDS会在服务端预处理数据,然后才返回结果,文件后缀是.grib。
3.3 第三步:批量下载和时间窗口的处理技巧
碰到需要按整年或整月下载的情况,不要一次性提交一个大请求。CDS后端对单个请求的数据量有隐式限制,请求太大要么排队时间长得离谱,要么直接报错。我的做法是按月循环请求,每个月一个文件,最后再合并。示例:
import cdsapi from datetime import date, timedelta start = date(2024, 1, 1) end = date(2024, 1, 31) step = timedelta(days=1) c = cdsapi.Client() current = start while current <= end: date_str = current.strftime("%Y-%m-%d") c.retrieve( "cams-global-atmospheric-composition-forecasts", { "type": "forecast", "format": "grib", "variable": [ "particulate_matter_d_2_5um", "particulate_matter_d_10um", "nitrogen_dioxide" ], "date": date_str, "time": "00:00", "leadtime_hour": "0/3/6/9/12/15/18/21/24", "area": [40, 110, 20, 125], }, f"cams_{date_str}.grib" ) current += step实测下来,一天的数据量在裁剪区域后通常几十MB,下载速度取决于CDS服务端的排队情况。如果下载中途断了,重新运行脚本时会重新排队,但已经下载完成的文件不会被覆盖(可以直接跳过)。
4. 读数据:GRIB文件解析的正确姿势
4.1 为什么是GRIB,以及用什么工具打开
CAMS预报数据的默认格式是GRIB,这是一种由WMO(世界气象组织)定义的气象数据二进制格式。GRIB比NetCDF更节省存储空间,但可读性相对差,不能直接双击打开。在Python生态里,处理GRIB的主流方案有两个:pygrib和cfgrib。
我的建议是直接用cfgrib配合xarray。原因有三点:一是xarray的数据结构对多维气象数据友好,能直接按维度切片;二是cfgrib把GRIB的每条消息映射为xarray的DataArray,维度自动识别成时间、经度、纬度;三是后续可视化、重采样、插值流程都能继续用xarray生态。安装时需注意,cfgrib依赖eccodes库,在Linux和macOS下通常能自动装好,Windows下偶尔需要手动安装eccodes的二进制包。
pip install cfgrib xarray4.2 完整示例:读取并提取目标区域的PM2.5序列
import xarray as xr # 读取GRIB文件,engine指定为cfgrib ds = xr.open_dataset( "cams_20240315.grib", engine="cfgrib", backend_kwargs={"filter_by_keys": {"dataType": "fc"}} ) # 查看文件里的变量 print(ds.data_vars)读取后有两点需要马上确认,否则后续分析极易踩坑。
第一,时间坐标存在三个维度:time是起报时间,step是预报步长(以小时为单位),valid_time是实际有效时间(等于time加上step)。如果只想要“某个具体时刻”的数据,一定要按valid_time筛选,而不是按time。比如从2024年3月15日00时起报的数据里取起报后24小时的预报,对应的是3月16日00时的有效时间。
第二,变量单位不是完全统一的。PM2.5和PM10在CAMS预报产品里直接以μg/m³给定,可以直接使用。但NO2、O3这些痕量气体是体积混合比,单位是mol/mol。转成常见的μg/m³需要乘上分子量和空气密度的换算系数。以NO2为例,标准状态下(0℃、1013hPa)1ppb NO2约等于2.05μg/m³,实际计算时还需按环境温度和气压做修正。
下面是我常用的提取站点序列的处理代码,加入了双线性插值:
import numpy as np import xarray as xr from scipy.interpolate import RegularGridInterpolator def extract_point_time_series(ds, var_name, lon, lat): """ 从CAMS数据集中提取单点变量的时间序列 """ da = ds[var_name] # 取valid_time作为时间维度,重命名方便使用 da = da.assign_coords(valid_time=da.valid_time) # 插值到目标经纬度 lons = da.longitude.values lats = da.latitude.values # 需要广播经纬度网格 lon2d, lat2d = np.meshgrid(lons, lats) # 将数据整理成(时间, 纬度, 经度)形状 data = da.transpose("valid_time", "latitude", "longitude").values series = [] for t in range(data.shape[0]): # 对每个时间切片做双线性插值 interp = RegularGridInterpolator( (lats, lons), data[t], method="linear", bounds_error=False, fill_value=np.nan ) series.append(interp([lat, lon])[0]) return da.valid_time.values, np.array(series)注意,CAMS的经纬度网格是规则等距网格,直接用scipy的RegularGridInterpolator即可,不需要griddata那种散点插值方案。如果目标区域之外全是缺测,返回的fill_value设成NaN,后续处理时要记得过滤。
4.3 文件合并与变量单位换算经验
批量下载的GRIB文件通常按天命名,如果要做连续时间分析,建议先合并再处理,避免频繁打开文件。xarray的open_mfdataset可以直接合并,但要注意GRIB文件的维度合并有时会跟NetCDF不一样,需要显式指定合并维度:
import xarray as xr ds_all = xr.open_mfdataset( "cams_2024*.grib", engine="cfgrib", combine="nested", concat_dim="valid_time", backend_kwargs={"filter_by_keys": {"dataType": "fc"}} )这里有个坑:如果每天的GRIB文件里含两个起报时次(00时和12时),直接用concat_dim="valid_time"会产生重复索引。更稳妥的做法是先对每个文件单独处理成以valid_time为唯一时间维度的DataArray,再合并。我一般会写一个清洗函数,把time、step、valid_time三个坐标处理干净后再合并。
NO2体积比转质量浓度的实用公式(近地面近似):
# 假设近地面温度25摄氏度,气压1013hPa # 单位换算:1 mol/mol NO2 = 1e9 ppb # 质量浓度(ug/m3) = 体积混合比(mol/mol) * 1e9 * (分子量 / 摩尔体积) * (273.15 / T) * (P / 1013) molecular_weight_no2 = 46.005 # g/mol R = 8.314 # J/(mol*K) T_kelvin = 298.15 # 25摄氏度 P_hpa = 1013.0 # 标准状况下气体摩尔体积 Vm = R * 273.15 / 101325 * 1e3 # 约22.4 L/mol # 实际温度气压下的摩尔体积 Vm_real = R * T_kelvin / (P_hpa * 100) * 1e3 # m3/mol -> L/mol # 1e9 * (g/mol) / (L/mol) = ug/m3 近似系数 coef = 1e9 * molecular_weight_no2 / (Vm_real * 1e3) # 约等于1.88 ug/m3 per ppb no2_ugm3 = no2_molmol * 1e9 * coef公式不重要,重要的是记着NO2的单位坑。初次使用CAMS时,我因为没有做体积比到质量浓度换算,拿结果跟监测站对比时差了一个量级,排查了很久才发现是单位问题。
5. 典型应用场景:三个亲测过的实战案例
5.1 案例一:利用CAMS预报做区域PM2.5短期趋势分析
某次项目需要评估华东地区未来三天PM2.5浓度的空间演变,目标是识别一次污染过程的移动路径。我直接用CAMS预报的PM2.5近地面浓度字段,按6小时间隔截取序列,然后用matplotlib+cartopy批量出图。核心操作是:
批量下载华东区域未来48小时的PM2.5预报。
对每个预报时次插值到固定经纬度网格。
用
xarray的sel方法取出近地面层(CAMS的model_level变量可能涉及气层选择,但我用的forecast表面浓度产品直接给出10米高度或表面的成分浓度,没有垂直分层的问题)。将浓度场按时间排列,做成逐帧图或动图,观察污染中心移动方向和强度变化。
一次沙尘天气过程尤其适合用CAMS分析。在2024年3月中旬的一次沙尘事件中,CAMS不仅合理捕捉了PM10浓度从西北向东南的输送过程,还直观显示了沙尘组分对PM10浓度贡献的占比变化。对比如地面监测站数据,CAMS预报的PM10峰值出现时间偏差在3到6小时以内,对区域趋势分析来说误差可接受。
5.2 案例二:结合站点数据做模式评估,用CAMS诊断污染来源
单纯拿CAMS预报跟站点数据做对比,可以快速判断一次污染过程是本地生成还是区域传输。我做过一个很直接的分析:把CAMS的NO2和PM2.5时间序列与目标城市国控站数据画在同一张图上,如果CAMS的浓度变化趋势和站点一致,但绝对值系统性偏低,通常说明排放源清单对该区域的排放量低估;如果趋势都反了,可能说明气象场没有捕捉到污染输送过程。
要特别留意CAMS和观测数据之间的“时间对应”问题。地面监测站的时间是北京时间,CAMS的valid_time是UTC时间。如果不做转换,直接对比会造成8小时错位,在日变化上产生严重混淆。正确做法:
# 将UTC时间转为北京时间 beijing_time = utc_time + pd.Timedelta(hours=8) # 按北京时间的日期进行日平均 daily_mean = df.resample("1D", on="beijing_time").mean()5.3 案例三:新能源功率预测的辅助特征输入
在光伏功率预测模型里,云量、气溶胶对地表辐射的影响不可忽视。CAMS的气溶胶光学厚度(AOD550)字段可以作为辐射预报的补充特征,尤其是沙尘天气下,AOD的激增往往对应光伏出力骤降。风电预测则更关注低层风速和风向,CAMS也提供风场变量(10米U、V分量),虽然分辨率不如专门的数值天气预报,但胜在同时包含气溶胶信息,在沙尘导致风机叶片污染、发电效率下降的场景里有额外参考价值。
具体做法是:将CAMS的AOD和PM2.5/PM10浓度作为特征,与数值天气预报的辐照度变量一起输入LSTM或XGBoost模型。实测下来,加入AOD特征后在沙尘日的预测误差降低了约15%到20%,因为仅靠辐照度预报很难体现气溶胶对直射光的削弱。
6. 常见问题与排查速查表:把踩过的坑一次说清楚
6.1 问题速查表
下面是CAMS使用过程中最常见的几类问题,以及我的排查经验。
| 问题 | 常见原因 | 解决方法 |
|---|---|---|
| API请求报401错误 | .cdsapirc配置错误或密钥失效 | 检查uid和key是否用下划线连接,是否放在用户主目录,重新复制密钥 |
| 请求提交后长时间排队 | 单次请求数据量过大,或CDS服务端繁忙 | 缩小区域范围、按月分批请求、避开欧洲工作日上午高峰期 |
| 下载速度极慢(几十KB/s) | CDS全球用户共享带宽 | 调整time为单个起报时次,减少变量数量,尽量裁剪区域 |
| cfgrib报“eccodes cannot open file” | GRIB文件下载不完整,或eccodes版本不支持 | 删除重新下载;升级eccodes、cfgrib到最新版 |
变量只有unknown | 某些GRIB字段的短名映射不全 | 用pygrib的messages查看具体变量短名,手动映射 |
读取后时间维度是step而不是具体日期 | 没有使用valid_time坐标 | 用ds.valid_time替代ds.time做时间切片 |
| NO2/CO体积比单位转不准确 | 忘记做温压修正 | 按实际近地面气象条件计算换算系数 |
| PM2.5出现负值或异常大值 | 可能是沙尘暴等极端事件的模式极值 | 结合AOD和气象场判断,必要时做阈值截断 |
6.2 一个容易忽略但很关键的问题:起报批次的选择
CAMS每天有00时和12时两个起报批次。00时起报的预报用了前一天的全球观测同化资料,12时起报则多用当天的部分观测。同一有效时间点,两个批次的数值会有差异,这是正常现象。做业务预报时,我习惯固定用00时起报的批次,保证时间序列的同源性;如果想对比起报时间对预报技巧的影响,则可以用两个批次做集合分析。
6.3 GRIB读取时的一处硬坑:多维度变量
CAMS的部分变量,比如气溶胶光学厚度和沙尘浓度,在GRIB文件里有多个垂直层或扰动成员。读取时如果不加过滤条件,xarray会报维度冲突。解决办法是在backend_kwargs里增加filter_by_keys条件,例如只看分析数据(dataType: an)或只看控制预报(type: fc)。CAMS全球预报通常有0到9共10个集合成员,普通业务分析只取控制预报成员(通常编码为number: 0)即可,集合统计另说。
7. 数据集的进阶玩法与构建训练数据的经验
7.1 把CAMS变量用作机器学习特征时的数据泄露风险
用CAMS预报数据训练机器学习模型时,最容易犯的错误是时间泄露。CAMS预报产品里的forecast字段是真正意义上的“未来预报”,而analysis字段是同化后的“最优估计”。如果你拿analysis字段做特征去预测未来某时刻的浓度,这还算合理,因为分析场只用到了过去和当前观测;但如果把forecast里某个valid_time的数据放到特征里,同时标签也是同一时刻,就会导致模型学到的是“用预报作为答案去预测同一时刻”,实际部署时因预报误差存在,效果会断崖式下跌。
正确做法是:特征只能使用起报时间之前或起报时刻的信息。比如想预测有效时间T的PM2.5,可以用CAMS在起报时间T-24小时、T-12小时的预报结果,但绝不能用有效时间T本身的预报或分析数据作为特征。
7.2 构建区域数据集时的处理流程
如果你打算构建一个CAMS驱动的区域空气污染数据集,我建议按以下流水线处理:
确定研究区域和空间分辨率,统一用双线性插值到目标经纬度网格或站点。
将CAMS的
valid_time统一转换为目标时区,与地面监测数据对齐。对变量做单位标准化、缺失值标记。
按时间窗口切分样本,训练集、验证集、测试集按时间顺序划分,避免随机打乱导致时间泄露。
与站点监测数据求偏差,记录每个格点的系统性偏差,后续可以做简单偏差订正。
CAMS在做完偏差订正后的数据质量会明显提升。平均偏差订正的方法也不复杂:用训练集时间段内CAMS与站点观测的差值,生成一张空间分布的偏差场,然后在预测时段把偏差场加到CAMS预报上。这种方法在CAMS上的实测效果,比直接用原始预报作为输入能减少约30%到40%的均方根误差,而且成本极低。
7.3 进一步扩展方向
CAMS的预报产品本身也可以跟其他数据源交叉验证。比如跟卫星反演的AOD产品对比,评估气溶胶空间分布的合理性;跟地基激光雷达的数据对比,评估垂直分布的可信度;跟地面监测站的PM2.5对比,评估近地面浓度的准确性。这类对比不需要特别复杂的模型,几行代码加绘图就能看出问题,但它决定了你后续分析结论的可靠性。
我自己在使用中养成了一个习惯:每次拿到一批新下载的CAMS数据,第一件事不是立刻跑分析,而是先打印变量的units、long_name、valid_time的范围,再随机抽一个格点画一条时间序列目检一下。数据质量检查花5分钟,能省下来之后排查数据异常浪费的半天。CAMS整体质量在同类全球大气成分预报产品里属于第一梯队,但不代表每次下载都完美无缺,偶尔会出现片段缺失、极端异常值等问题。把“先检查再分析”写入流程,是跟这套数据集打交道最值得养成的习惯。