实验数据采集:数据采集系统搭建、采样频率与精度选择、工况循环设计、数据预处理
大家好,我是你们的BMS算法讲师。今天咱们聊聊实验数据采集这块。
说实话,很多做SOC算法的同行,把精力全扑在算法模型上,却忽略了数据采集这个基础环节。我见过太多人,模型调得天花乱坠,一上实车数据就崩盘。为什么?因为数据本身就有问题。你想想看,垃圾进,垃圾出,再牛的算法也白搭。
数据采集系统搭建
先说说硬件层面的东西。我个人习惯把采集系统分成三层:传感器层、信号调理层、数据记录层。
- 传感器层:电流传感器(霍尔或分流器)、电压采样芯片、温度传感器(NTC或PT1000)。
- 信号调理层:滤波电路、隔离放大器、ADC转换模块。
- 数据记录层:工控机或嵌入式板卡,配合CAN总线或以太网传输。
我在项目中遇到过一个问题:某次用霍尔传感器测电流,低频噪声特别大。后来发现是供电电源纹波没处理好。嗯,这里要注意,传感器的供电质量直接影响数据精度,别省那点钱。
采样频率与精度选择
采样频率怎么选?说白了,得看你的工况变化有多快。
对于UDC工况,电流变化相对平缓,1Hz采样就够了。但NEDC工况里有些急加速段,电流变化率可能超过100A/s。这时候,我建议采样频率至少10Hz,最好20Hz以上。
精度方面,我踩过坑。曾经用12位ADC采集电流,分辨率只有0.1A左右。结果做SOC估算时,静置阶段的微小电流根本测不准,导致自放电估算偏差很大。后来换成16位ADC,分辨率到了0.01A,问题才解决。
| 工况类型 | 推荐采样频率 | 推荐ADC位数 | 典型电流变化率 |
|---|---|---|---|
| UDDS | 1-5 Hz | 12-14 bit | ~20 A/s |
| NEDC | 10-20 Hz | 14-16 bit | ~100 A/s |
| 自定义急加速工况 | 50-100 Hz | 16 bit | ~300 A/s |
工况循环设计
工况循环,说白了就是让电池“跑”起来的一套速度-时间曲线。标准的有UDDS和NEDC,但实际项目中,我更推荐自定义工况。
为什么?因为标准工况是给整车测试用的,对电池SOC估算来说,有些关键特征没覆盖到。比如:
- 长时间静置(模拟停车自放电)
- 小电流充放电(模拟BMS待机功耗)
- 脉冲充放电(模拟能量回收)
我记得有一次,客户反馈SOC在低电量时跳变严重。我一看他们的测试数据,全是NEDC工况,根本没有低SOC下的静置段。后来我设计了一个自定义工况,在SOC 10%以下加入了30分钟的静置和微电流放电,果然复现了问题。
- 覆盖SOC全范围(0%-100%)
- 包含静置段(至少30分钟)
- 加入脉冲电流(模拟真实驾驶中的急加速/急减速)
- 温度变化(从-20°C到55°C,每10°C一个测试点)
数据预处理
数据采集完了,别急着用。原始数据里全是坑。我总结了三步预处理流程:滤波、去噪、异常值处理。
滤波
滤波的目的是去掉高频噪声。我常用的是滑动平均滤波和低通滤波。
# 滑动平均滤波示例
import numpy as np
def moving_average(data, window_size=5):
return np.convolve(data, np.ones(window_size)/window_size, mode='same')
# 低通滤波(一阶RC)
def low_pass_filter(data, alpha=0.1):
filtered = [data[0]]
for i in range(1, len(data)):
filtered.append(alpha * data[i] + (1-alpha) * filtered[-1])
return filtered
我个人习惯先用滑动平均粗滤,再用低通精调。alpha值一般取0.05到0.2之间,具体看噪声水平。
去噪
去噪和滤波不太一样。滤波是平滑,去噪是剔除明显错误的数据点。比如:
- 电流值突然跳变超过100A(传感器故障)
- 电压值低于0V或高于4.5V(单节电芯)
- 温度值超过-40°C到85°C范围
我曾经遇到过一批数据,电流在某个时间段全是0。查了半天,发现是CAN总线丢帧了。这种数据如果不剔除,SOC估算会直接飘走。
异常值处理
异常值处理,我推荐用3σ原则或IQR方法。
# 3σ原则剔除异常值
def remove_outliers_3sigma(data):
mean = np.mean(data)
std = np.std(data)
return [x for x in data if abs(x - mean) <= 3 * std]
# IQR方法
def remove_outliers_iqr(data):
q1 = np.percentile(data, 25)
q3 = np.percentile(data, 75)
iqr = q3 - q1
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr
return [x for x in data if lower_bound <= x <= upper_bound]
好了,数据预处理完,才算真正拿到了“干净”的数据。下一章,咱们聊聊如何用这些数据来标定OCV曲线。到时候见。