简介:面向新能源汽车电池健康状态估计与剩余寿命预测研究者的数据集配套代码包,适用于机器学习、深度学习相关实验与算法验证。原始数据由智能汽车安全技术全国重点实验室基于300辆运营车辆的三元锂离子电池实测生成,覆盖0-50万公里里程与0.5-4年运行周期,采样频率10秒/帧,总量约8.5亿帧,包含充电、放电、静置等全工况信息;关键字段涵盖SOC、SOH、电压、电流等13项,可为电池老化规律挖掘和BMS算法优化提供高时间分辨率的数据支撑。压缩包共3个文件,以html预览页面、inscode在线运行配置和gitignore工程配置为主,体积仅6KB,轻量易用;其中html页面可辅助快速查看数据集说明,inscode配置便于在线环境复现。目前已有182人学习浏览,适合电池衰退分析、剩余寿命预测等领域的研究人员及学生。配套还提供100MB样例和72GB全量CSV下载入口,并附有标准引用格式,兼顾快速体验与规范引用。
1. 新能源汽车电池数据集:能做什么、适合谁、坑在开头就埋下了
刚拿到一份新能源汽车电池数据集的项目代码包时,最容易犯的错是急着把它喂进模型。这个方向的数据集通常不是一张干净表格,而是来自多辆车的 BMS 原始日志、充放电曲线,外加零散的 SOH(健康度)标定记录。它能解决的事情很集中:电池健康度估算、剩余寿命预测、异常充电段识别,如果带图像部分还能做缺陷检测。适合谁呢——做新能源算法落地的工程师、电池系统开发团队,还有想拿真实工况数据练手的从业者。我的建议是拿到手先做三件事:确认数据有哪几个来源、标签覆盖哪些时刻、自带的清洗脚本按什么约定工作。这三件事没确认之前,跑出来的任何指标都不可信。
2. 看懂数据集结构:BMS 时序、充放电曲线和图像三类数据怎么对齐
这类项目数据集下载后一般几十到几百 MB,解压开是一堆 CSV 和几个说明文件。别急着跑训练脚本,先花半个小时把 readme 和文件清单过一遍,搞清楚数据是怎么采集的。采集方式决定了你能做哪些特征,也决定了后面所有坑的位置。
2.1 三类数据分别长什么样、各解决什么问题
第一类是 BMS 采样时序,频率通常在 1Hz 到 10Hz,记录总电压、总电流、各单体电压、最高/最低温度和 SOC。这是最常用来做 SOH 估算和异常检测的数据。第二类是充放电曲线,可能来自台架标定,也可能来自随车工况,电压-容量曲线和增量容量曲线都能从这里提,老化分析主要靠它。第三类是图像数据,比如电芯 X 光、模组红外热成像,多用于缺陷检测。如果里面带标注框,常见做法是处理成 yolov8 能用的格式微调一个检测模型;想往多模态方向做的,也会把时序特征和图像特征拼起来用。
三类数据本质上是三个时间轴。BMS 数据按秒采样,台架数据可能到毫秒级,图像则是按事件触发,比如每次充电握手或每圈循环拍一张。建模前第一件事是统一时间轴基准,否则后面任何按时间差分的特征都会算出错误结果。
2.2 字段含义和单位:画图之前先对照这张表
| 字段 | 含义 | 单位/范围 | 常见坑 |
|---|---|---|---|
| timestamp | 采样时间戳 | 需解析时区 | 文件名里的日期和内容可能不一致 |
| pack_voltage | 电池包总压 | V | 台架和 BMS 的电压基准可能不同 |
| pack_current | 总电流 | A | 充电为正还是放电为正,各数据集不统一 |
| cell_voltage_min/max | 单体电压极值 | V | 不是所有车都记录全部单体 |
| cell_temp_max | 最高温度 | 摄氏度 | 缺失率高的通道要先剔除 |
| soc | 荷电状态 | 0~100 | 上位机补写会造成跳变 |
| soh | 健康度标签 | 0~1 | 只在标定时刻有真值,不能直接 merge |
拿到手之后,我会把 readme 里写的采集频率、电流方向和参考容量 rated_ah 抄到一张便签上,再打开 CSV 看一眼前几百行。单位不统一是家常便饭:有的电流字段单位是 A,有的实际标的是 mA;温度有的是单传感器读数,有的是多个传感器的均值。画图前先按这张表过一遍字段,能省掉后面一整轮的排查时间。
对齐时间轴时还有个细节:BMS 的 SOC 字段是估算值,不是真值。它用来切充电段没问题,但用来当标签会出大事。真正的 SOH 标签几乎总是单独存放在一个标定文件里,和主数据不在同一张表。先把这个对应关系理清,再谈建模。
2.3 项目代码的目录习惯:先看结构再改配置
这类项目的代码结构一般比较固定,常见做法是数据、标签、脚本分开:
battery_dataset/ ├── data/ │ ├── raw/ # 原始 CSV,一块电池一个文件 │ └── processed/ # 清洗后落盘的 parquet ├── labels/ │ └── soh_labels.csv ├── src/ │ ├── clean.py # 清洗和特征工程 │ └── train.py # 模型训练与评估 └── config.yaml # 全局参数config.yaml 里通常写着 rated_ah、采样率、电流方向约定、SOC 插值方式。我会先打开它,逐项和 readme 对照,再决定要不要改。最容易踩的坑是直接用默认配置跑完整条链路,结果把一边的充电段识别成了放电段。这个文件不复杂,但值得花十分钟逐行看,它决定了后续所有脚本的输入口径。
提示:先跑通最小链路再去看项目里那些花哨的可视化模块,能省一半排查时间。
3. 跑通项目代码:从原始 CSV 到 SOH 估算模型的最小链路
不管项目里自带多少功能模块,我会把它收敛成一条最小链路:清洗到特征再到基线模型。先把端到端打通,再往里加花活。下面三段代码是这类项目上常用的参考实现,参数可以直接抄,但要知道每个参数在控什么。
3.1 清洗:剔除关机段和补写数据,统一电流方向
import pandas as pd import numpy as np df = pd.read_csv("data/raw/pack_001_bms.csv", parse_dates=["timestamp"]) df = df.sort_values("timestamp") # 剔除关机段:电流和电压同时接近零,通常不是有效工况 mask_idle = (df["pack_current"].abs() < 0.01) & (df["pack_voltage"] < 10) df = df[~mask_idle].copy() # 同一秒内重复写入只保留最后一条,避免后续聚合重复计数 df = df.drop_duplicates(subset=["timestamp"], keep="last") # 电流方向自动校正:充电段电流应该有统一的正负号 # 约定:充电为正、放电为负;统计大电流的中位数符号 large_current = df.loc[df["pack_current"].abs() > 0.5, "pack_current"] if np.median(large_current) < 0: print("原始电流方向反了,统一翻转") df["pack_current"] = -df["pack_current"]逻辑说明:关机段电压和电流同时接近零,对容量估计没有贡献,还会让电压-容量曲线尾部异常长,所以先剔除。drop_duplicates 处理的是上位机或网关补写造成的同一时间戳多行,这种重复会直接干扰后面按时间差分的特征计算。电流方向自动校正这一步最关键:很多数据集来源混杂,充电正负约定不一致,人工改每个文件不现实,用一个中位数符号判断就能覆盖绝大多数情况。
参数说明:0.01A 和 10V 的阈值按乘用车电池包量级写的,如果数据来自大巴或储能柜,电流阈值要放到 0.1A 左右,电压阈值调整到额定电压的 5%。判方向时用 abs()>0.5A 的窗口,是为了避开静置时的电流噪声;高采样率台架数据可以把这个窗口缩到 0.2A。
3.2 特征工程:从恒流充电段提取增量容量特征
rated_ah = 180 # 从 config.yaml 读入,示例值 # 只保留恒流充电段:电流足够大且 SOC 单调上升 cc = df[(df["pack_current"] > 1.5) & (df["soc"].diff() > 0)].copy() if len(cc) < 100: raise ValueError("充电段识别异常,请检查电流方向和 SOC 字段") # 电量增量 dq:SOC 差折算成安时再乘 3600 换算成库仑量 cc["dq"] = cc["soc"].diff() / 100.0 * rated_ah * 3600.0 cc["dv"] = cc["pack_voltage"].diff().clip(lower=1e-6) cc["ic"] = cc["dq"] / cc["dv"] # 增量容量 dQ/dV # 按电压分箱取平均,得到平滑后的 IC 曲线 bins = pd.cut(cc["pack_voltage"], bins=200) ic_curve = cc.groupby(bins, observed=True)["ic"].mean().dropna() peak_v = ic_curve.index.left[np.argmax(ic_curve.values)] peak_area = ic_curve.sum()逻辑说明:增量容量分析是电池老化特征里最常用的方法之一。充电过程电压上升时,dQ/dV 曲线的峰位会随老化往低电压方向移动,峰值电压和峰面积就能作为 SOH 的强特征。这段代码的核心是先锁定恒流充电段——只有恒流段电压平稳上升,微分才有意义;恒压段电压几乎不动,dv 接近零,算出来全是噪声。按电压分箱平均是为了把原始差分的高频抖动压掉,让曲线可复现。
参数说明:1.5A 的恒流判断阈值要和额定容量匹配,按 0.02C 左右估;180Ah 的电池用 3.6A 以上更稳,小容量电池可以放到 0.5A。分箱数量 200 是把充电电压范围切成 200 格,如果整条充电只跨 0.5V,可以降到 100 让曲线更平滑。clip(lower=1e-6) 是防止电压差分出现零导致除零报错。
3.3 训练基线:用分组划分避免数据泄漏
上一节每次充电段聚合出一行特征,得到 df_feat,一行对应一次完整充电循环。下面直接训练 SOH 回归基线。
from sklearn.ensemble import GradientBoostingRegressor from sklearn.model_selection import GroupShuffleSplit from sklearn.metrics import mean_absolute_error import numpy as np features = ["ic_peak_v", "ic_peak_area", "cycle_count", "max_temp"] X = df_feat[features] y = df_feat["soh"] groups = df_feat["pack_id"] # 同一块电池的所有行归为一组 gss = GroupShuffleSplit(n_splits=1, test_size=0.25, random_state=42) train_idx, test_idx = next(gss.split(X, y, groups=groups)) model = GradientBoostingRegressor( n_estimators=400, learning_rate=0.05, max_depth=3, subsample=0.8, random_state=42 ) model.fit(X.iloc[train_idx], y.iloc[test_idx]) y_pred = model.predict(X.iloc[test_idx]) mae = mean_absolute_error(y.iloc[test_idx], y_pred) print(f"MAE = {mae:.2f}%")逻辑说明:这里最关键的不是模型选型,而是分组。电池数据同一块电池相邻时刻高度相关,如果按行随机划分,训练集和测试集里会混进同一块电池的数据,模型等于在记答案。GroupShuffleSplit 按 pack_id 保证同一块电池只落到一边,这时算出来的 MAE 才有意义。梯度提升树作为基线,是因为它对特征尺度不敏感,IC 特征和温度特征单位差别很大,换线性模型要先做标准化,树模型不用。
参数说明:test_size=0.25 意味着留 25% 的电池整组做验证,如果总共只有 20 块电池,建议提到 0.3 到 0.4,保证测试组数量足够。n_estimators=400 配 learning_rate=0.05 是防止单棵树太强导致过拟合,数据量小时把 max_depth 降到 2 更稳。random_state=42 固定下来,后面调参时才能保证每次对比口径一致。
4. 避坑:电池时序数据里最容易翻车的五个地方
这类项目我做过不止一轮,血泪经验是:翻车几乎都发生在数据划分、符号约定和标签对齐上,模型本身很少出问题。下面五条按出现频率排。
4.1 同一块电池同时进了训练集和测试集
现象:测试集 MAE 低得离谱,比如 0.3%,一换新电池预测就崩。原因:默认 train_test_split 按行随机切,同一块电池的相邻时刻跨进了两个集合,模型记住了电池本身的响应曲线。解决:用 GroupShuffleSplit 按 pack_id 分组,或按日历时间切片,前 70% 时间做训练,后 30% 做测试。时间切片更接近真实上线场景,因为上线时只能看到历史数据。
4.2 电流符号不统一,充电段识别反了
现象:画出来的充电曲线电流是负的,增量容量曲线整体为负,特征工程直接报废。原因:数据集来自多家供应商,充电正负约定不一致,readme 里没写或者写得含糊。解决:写一个自动校正函数,统计大电流段的中位数符号并统一翻转;把校正后的约定写进配置文件,重跑前断言一次。断言这步不能省,否则下次换一个 pack 文件又悄悄翻回去。
4.3 SOC 跳变和重复时间戳
现象:SOC 在几分钟内从 80 跳到 5,充电段特征里出现尖刺。原因:车载终端补传报文、BMS 重启后 SOC 重新估算、网关重复写入。解决:先 drop_duplicates 去重,再对 SOC 差分做突变检测,超过 5% 的记录打标记剔除或线性插值。注意只对静置段插值,充电段跳变直接丢数据,不要补。
4.4 温度通道缺失率高或传感器漂移
现象:特征矩阵里 max_temp 大量 NaN,或者某一辆车温度整体偏高 3 到 5 度。原因:部分车辆的传感器通道没有全部接进来,还有老车温度传感器老化产生直流漂移。解决:按列统计缺失率,超过 40% 直接丢列;对漂移,用充电结束后的静置段温度做基线,把整条温度序列减去静置中位数后再用。漂移不校正的话,温度特征会变成车号特征,模型学到的不是物理规律。
4.5 SOH 标签稀疏,直接 merge 得到一堆 NaN
现象:训练时 y 大量缺失,或某一行 y 值长期不变导致模型学不出东西。原因:SOH 真值来自满充满放标定,一般几个月才做一次;平时记录到的只是 BMS 估计值,不能当标签用。解决:标签表按 pack_id 和 cycle 对齐后 forward fill 到有效区间,训练只取标签点附近窗口;或者把问题改造成分段回归,只预测有标定记录的时刻。整条时序上求 loss 的做法在这里不成立。
提示:上面五条里任意一条没处理干净,后面的模型调参都是在给错误系统做优化。
5. 验证 SOH 估算模型的最后一公里:随机性控制和双阈值告警
模型在测试集上 MAE 到 2% 并不代表能上线。电池 SOH 估算最终是拿去做维护排程的,错判一次就是一次不必要的换电或一次漏判的事故风险。我习惯在上线前加两道检查。
第一道是随机性控制。固定 random_state 跑出来的 MAE 只是单次抽样,换一个划分种子结果可能差很多。我会用五组种子跑五轮分组验证,比较 MAE 均值和标准差;如果标准差超过均值的一半,说明特征不稳定或样本电池太少,这时加复杂模型只会放大方差,退回去用简单模型更稳。这步看起来像是调参数,其实是在验证数据能不能支撑这个精度。
第二道是业务侧的阈值设置。假设估算误差 MAE 为 2%,标准差 0.8%,那么告警阈值不能只设一个点,否则边界上的电池会被反复误判。我一般设成两张表里的三段:
| 阈值 | 取值建议 | 依据 |
|---|---|---|
| 关注线 | SOH 低于 86% | 留出 2 倍误差余量 |
| 行动线 | SOH 低于 80% | 接近质保和维保边界 |
| 复核线 | 估算区间跨过阈值 | 触发人工标定确认一次 |
具体数值要结合车型和质保条款调整,有的车型 SOH 到 80% 就要触发换电流程,有的到 75% 才需要动作。关键是把模型误差量化之后乘上 1.5 到 2 倍作为滞回带,避免 SOH 在阈值附近来回抖动触达误报。复核线靠前放,宁可多一次人工确认,也不要把决策完全交给模型。
我现在的习惯是:拿到这类项目代码先跑探路脚本,把时间轴、电流方向、标签覆盖三件事确认完再谈算法选型;模型再先进也救不了标签错的数据。训练完必须报告种子标准差,不报告就当没验证过。这套流程帮我挡掉过不少线上翻车,希望帮到你。
本文还有配套的精品资源,点击获取