第3章:数据采集与预处理
各位工程师朋友,大家好。今天我们来聊聊电池管理系统里最基础、也最容易被忽视的一环——数据采集与预处理。
你可能觉得,不就是测个电压、采个温度吗?有什么难的?
嗯,我当年也是这么想的。直到有一次,我在实验室里盯着一条剧烈抖动的电流曲线,死活找不到原因。后来才发现,是ADC采样时序和滤波参数没配好。那一次,我整整熬了两个通宵。
所以,这一章我会把踩过的坑、积累的经验,都摊开来跟你讲。
3.1 信号调理:让传感器信号“说人话”
传感器输出的信号,往往不是我们直接能用的。比如热电偶输出的是毫伏级电压,电流传感器输出的是4-20mA的电流。这些信号需要先“调理”一下。
信号调理主要做三件事:
- 放大:把微弱的信号放大到ADC能识别的范围(通常是0-3.3V或0-5V)
- 滤波:用硬件低通滤波器滤掉高频噪声。我习惯在运放后面加一个RC滤波器,截止频率设在100Hz左右
- 电平转换:把双极性信号(比如±10V)转换成单极性信号
3.2 ADC采样:精度与速度的博弈
ADC采样,说白了就是把模拟信号变成数字信号。这里有两个关键参数:分辨率和采样率。
| 参数 | 说明 | 我的建议 |
|---|---|---|
| 分辨率 | 12位、14位、16位等 | 电池电压检测建议用16位以上 |
| 采样率 | 每秒采样次数(SPS) | 温度信号10Hz足够,电流信号需要100Hz以上 |
| 参考电压 | 内部参考或外部参考 | 我强烈建议用外部精密参考源 |
为什么会这样?因为电池电压的变化其实很慢,但电流变化很快(尤其是发生短路时)。你想想看,如果采样率太低,短路电流的峰值可能就漏掉了。
3.3 滤波算法:把噪声“洗”干净
硬件滤波之后,信号里还是会有残留噪声。这时候就需要软件滤波上场了。我常用的有三种方法。
3.3.1 均值滤波
最简单,也最常用。就是把N次采样的值加起来,再除以N。
// 均值滤波示例
#define FILTER_N 10
uint16_t samples[FILTER_N];
uint8_t index = 0;
uint16_t mean_filter(uint16_t new_sample) {
uint32_t sum = 0;
samples[index++] = new_sample;
if (index >= FILTER_N) index = 0;
for (int i = 0; i < FILTER_N; i++) {
sum += samples[i];
}
return (uint16_t)(sum / FILTER_N);
}
嗯,这里要注意:N值越大,滤波效果越好,但响应速度越慢。我个人习惯在温度检测上用N=20,电流检测上用N=5。
3.3.2 中值滤波
对付脉冲噪声特别有效。比如传感器偶尔受到电磁干扰,出现一个离谱的尖峰,均值滤波会被拉偏,但中值滤波完全不受影响。
// 中值滤波示例(冒泡排序法)
uint16_t median_filter(uint16_t *data, uint8_t len) {
uint8_t i, j;
uint16_t temp;
// 复制数据
uint16_t buf[len];
for (i = 0; i < len; i++) buf[i] = data[i];
// 冒泡排序
for (i = 0; i < len-1; i++) {
for (j = 0; j < len-1-i; j++) {
if (buf[j] > buf[j+1]) {
temp = buf[j];
buf[j] = buf[j+1];
buf[j+1] = temp;
}
}
}
return buf[len/2];
}
3.3.3 卡尔曼滤波
这个就有点高级了。卡尔曼滤波能根据系统的动态模型,预测下一个值,然后用测量值来修正预测值。说白了,它是在“猜”真实值是多少。
我一般在SOC估算和电池内阻在线监测中用卡尔曼滤波。效果确实好,但调参是个技术活。
// 一维卡尔曼滤波简化版
typedef struct {
float Q; // 过程噪声协方差
float R; // 测量噪声协方差
float P; // 估计误差协方差
float K; // 卡尔曼增益
float X; // 状态估计值
} Kalman_t;
void Kalman_Init(Kalman_t *kf, float init_val) {
kf->Q = 0.01; // 根据经验调整
kf->R = 0.1; // 根据传感器精度调整
kf->P = 1.0;
kf->X = init_val;
}
float Kalman_Update(Kalman_t *kf, float measurement) {
// 预测
kf->P = kf->P + kf->Q;
// 更新
kf->K = kf->P / (kf->P + kf->R);
kf->X = kf->X + kf->K * (measurement - kf->X);
kf->P = (1 - kf->K) * kf->P;
return kf->X;
}
3.4 数据归一化:让数据“站在同一起跑线”
归一化,就是把不同量纲的数据映射到同一个区间,通常是[0,1]或[-1,1]。
为什么要做归一化?你想想看,电压是3-4V,温度是-20到60℃,电流是-200到200A。如果不归一化,神经网络或者机器学习模型会天然地“偏爱”数值大的特征。
常用的归一化方法有两种:
- Min-Max归一化:X_norm = (X - X_min) / (X_max - X_min)
- Z-score标准化:X_norm = (X - μ) / σ
我个人习惯在热失控预警系统中用Min-Max归一化。因为电池的电压、温度范围是已知的,用Min-Max更直观。
3.5 本章知识体系
下面这张图,是我自己梳理的数据采集与预处理流程。你可以把它当作一个检查清单。
好了,这一章的内容就到这里。数据采集与预处理,看似基础,但往往是整个系统成败的关键。希望我的这些经验,能帮你少走一些弯路。