1. 单体压差数据采集与基线建立
为什么先做“基线”,而不是先看告警
单体压差排障最容易犯的错误,是拿一个瞬时最大值直接判断“电池有问题”。压差本身随 SOC、温度、电流倍率、静置时长变化很大:同一组电池在 50% SOC 静置时可能只有 8 mV,在大倍率放电末端却可能到 60 mV。因此第一步不是找故障,而是用历史数据建立正常工况下的压差分布,后续任何异常值都要和对应工况的基线比,而不是和某个固定数字比。
本例的任务是:从 BMS 历史数据中提取各单体电压,逐帧计算最大压差,按工况分箱统计,得到可复用的基线表。核心指标定义如下(全文统一):
- 单体压差 ΔV = 同一时刻
max(V_cell) − min(V_cell),单位 mV; - 工况分箱键 = SOC 区间 + 温度区间 + 电流状态(静置/充电/放电);
- 基线取分箱内的 P50(典型值)与 P95(正常上限参考),样本数不足的分箱标记为“待积累”。
数据形态与提取要点
典型 BMS 历史数据是“一帧一行、单体电压多列”的宽表,或“一帧多行、每行一个单体”的长表。宽表更适合逐帧求极差,长表更适合做单体级趋势。本例按宽表处理,字段约定为:ts(时间戳)、soc(%)、temp_max(℃)、current(A,充电为正、放电为负)、v01…v96(单体电压,V)。
计算与分箱的实现
下面是一段可直接运行的 Python 示例,输入为上述宽表 CSV,输出分箱基线表。示例数据为教学构造,运行结果属于示例输出,不代表任何实船实测值。
import pandas as pd
import numpy as np
CELL_COLS = [f"v{i:02d}" for i in range(1, 97)]
def load_frames(path):
df = pd.read_csv(path, parse_dates=["ts"])
v = df[CELL_COLS]
# 有效性过滤:单体电压应在 2.0~4.5 V 之间
valid = (v > 2.0).all(axis=1) & (v < 4.5).all(axis=1)
df = df[valid].copy()
df["dv_mv"] = (df[CELL_COLS].max(axis=1) - df[CELL_COLS].min(axis=1)) * 1000
return df
def bin_key(df):
soc_bin = pd.cut(df["soc"], bins=[0, 20, 40, 60, 80, 100],
labels=["0-20", "20-40", "40-60", "60-80", "80-100"])
temp_bin = pd.cut(df["temp_max"], bins=[-20, 0, 15, 30, 45, 60],
labels=["<0", "0-15", "15-30", "30-45", ">45"])
mode = np.where(df["current"].abs() < 2, "静置",
np.where(df["current"] >= 2, "充电", "放电"))
return pd.DataFrame({"soc_bin": soc_bin, "temp_bin": temp_bin, "mode": mode})
def build_baseline(df, min_n=200):
key = bin_key(df)
g = df.groupby([key["soc_bin"], key["temp_bin"], key["mode"]])["dv_mv"]
base = g.agg(n="count", p50="median",
p95=lambda s: s.quantile(0.95)).reset_index()
base["status"] = np.where(base["n"] >= min_n, "可用", "待积累")
return base.sort_values(["mode", "soc_bin", "temp_bin"])
df = load_frames("bms_history.csv")
baseline = build_baseline(df)
print(baseline.head(10).to_string(index=False))
baseline.to_csv("dv_baseline.csv", index=False)
几个参数需要按船型调整:min_n=200 是分箱最少帧数,样本太少时 P95 不稳定;静置判定阈值 |I| < 2 A 要结合 CT 精度和实际待机电流设定;温度用 temp_max 而非平均温度,是因为高温单体更容易触发压差扩大,用最大值更保守。
基线结果怎么读
基线表不是一张“合格证”,而是一张对照尺。判读时抓住三点:同一工况下 P50 是否随 SOC 单调上升(放电末端压差扩大属正常);P95 与 P50 的差距是否过大(差距大说明该工况下压差离散,基线本身不稳);“待积累”分箱占比是否过高(说明历史数据覆盖不足,后续排障结论要降级)。
交付物:压差基线表
把上面的输出整理成可归档、可对照的基线表,字段固定,后续排障直接查表。示例行如下(数值为教学示例):
| 工况键 | SOC 区间 | 温度区间 | 电流状态 | 样本数 n | P50 (mV) | P95 (mV) | 状态 |
|---|---|---|---|---|---|---|---|
| B-03 | 40–60% | 15–30 ℃ | 静置 | 1280 | 7 | 13 | 可用 |
| B-11 | 80–100% | 30–45 ℃ | 放电 | 640 | 16 | 30 | 可用 |
| B-07 | 0–20% | <0 ℃ | 充电 | 85 | — | — | 待积累 |
归档时同时保存三样东西:基线表 CSV、生成脚本、以及数据覆盖说明(时间范围、航次类型、BMS 版本)。没有覆盖说明的基线,半年后没人敢用。
验证基线是否可信
基线建好后做一次自检,通过再进入排障环节:
- 单调性检查:同一温度、同一电流状态下,P50 应随 SOC 升高而上升(放电末端压差扩大)。若出现明显倒挂,多半是分箱边界或数据混入了不同电池组。
- 离散度检查:P95/P50 比值普遍超过 3,说明该工况下压差波动大,基线只能作粗参考,排障时需结合单体级趋势。
- 覆盖率检查:“待积累”分箱占比超过 30%,说明历史数据不足以支撑全工况判断,应补充数据或缩小分箱粒度。
- 回放验证:随机抽 20 帧正常工况数据,用基线表反查其 ΔV 是否落在 P95 以内,命中率应接近 95%。