3. 数据采集与预处理:传感器数据采集、缺失值处理、异常值检测、数据归一化与标准化

做SOC预测这些年,我踩过最大的坑是什么?不是模型选错了,而是数据预处理没做好。你想想看,传感器采集回来的原始数据,就像刚从泥里挖出来的土豆——不洗干净,谁敢直接下锅?

这一章,咱们就聊聊怎么把这堆“脏数据”收拾得服服帖帖。我保证,看完你就能上手干活。

3.1 传感器数据采集:从硬件到数字的“第一公里”

先说采集。SOC预测常用的传感器就那么几种:电压传感器、电流传感器、温度传感器。嗯,听起来简单,但实际采集时问题一堆。

采样频率怎么定?

我个人习惯是:先看电池的动态特性。锂电池的电压变化相对缓慢,1Hz的采样率基本够用。但如果你做的是动力电池,急加速时电流变化很快,建议提到10Hz以上。

我的经验: 别一味追求高频采样。数据量大了,存储和传输都是负担。我在一个项目中试过100Hz采样,结果一天下来几个GB的数据,处理起来卡得想砸电脑。

传感器标定

传感器出厂时都有误差,有的还很大。我建议你拿到新传感器后,先做个简单的标定:用已知电压源测一下,看看偏差多少。记录下来,后面预处理时直接补偿。

# 一个简单的标定补偿示例
def calibrate_voltage(raw_voltage, offset=0.02, scale=1.001):
    """
    raw_voltage: 传感器原始读数
    offset: 零点偏移(实测得到)
    scale: 增益系数(实测得到)
    """
    calibrated = (raw_voltage - offset) * scale
    return calibrated

3.2 缺失值处理:别让“空洞”毁了你的模型

采集过程中,数据丢失是家常便饭。通信中断、传感器故障、存储异常……原因多了去了。遇到缺失值怎么办?

三种常见策略:

  • 直接删除: 如果缺失比例很小(<5%),而且缺失是随机的,删了就删了。简单粗暴,但有效。
  • 前向填充: 用上一个有效值填充。适合电压、温度这种变化缓慢的参数。我在项目中经常用这个方法,效果不错。
  • 插值法: 线性插值或样条插值。适合电流这种波动较大的数据。但要注意,插值会引入人为误差,别过度使用。
注意: 我曾经犯过一个错误——对连续缺失超过10个点的数据用了插值。结果模型训练出来,预测值在缺失段完全偏离真实值。后来我定了个规矩:连续缺失超过5个点,直接丢弃这段数据。
# 缺失值处理示例
import pandas as pd
import numpy as np

def handle_missing(df, column, method='ffill', max_gap=5):
    """
    df: 数据框
    column: 要处理的列名
    method: 'ffill' 前向填充, 'linear' 线性插值
    max_gap: 最大连续缺失数,超过则丢弃
    """
    if method == 'ffill':
        df[column] = df[column].ffill()
    elif method == 'linear':
        df[column] = df[column].interpolate(method='linear')
    
    # 丢弃连续缺失过多的片段
    mask = df[column].isna().groupby(df[column].notna().cumsum()).transform('count') > max_gap
    df.loc[mask, column] = np.nan
    df = df.dropna(subset=[column])
    
    return df

3.3 异常值检测:揪出那些“捣乱分子”

传感器偶尔会抽风,蹦出个离谱的值。比如电压突然跳到100V,或者温度变成-200°C。这些异常值如果不处理,模型会被带偏。

怎么检测?

  • 3σ原则: 数据服从正态分布时,超过均值±3倍标准差的值视为异常。简单,但要求数据分布不能太偏。
  • IQR方法: 四分位距法。用Q1-1.5*IQR和Q3+1.5*IQR作为边界。对非正态分布更鲁棒。我个人更推荐这个。
  • 领域知识: 比如锂电池电压不会超过4.2V,低于2.5V基本就是过放了。直接设个硬阈值,比啥算法都管用。
避坑指南: 我曾经用3σ方法处理电流数据,结果把正常的脉冲放电当成了异常。为什么?因为电流分布不是正态的,有很多尖峰。后来改用IQR方法,问题就解决了。
# IQR异常值检测
def detect_outliers_iqr(data, factor=1.5):
    Q1 = np.percentile(data, 25)
    Q3 = np.percentile(data, 75)
    IQR = Q3 - Q1
    lower_bound = Q1 - factor * IQR
    upper_bound = Q3 + factor * IQR
    outliers = (data < lower_bound) | (data > upper_bound)
    return outliers

3.4 数据归一化与标准化:让所有特征“平起平坐”

电压、电流、温度,量纲完全不同。电压是伏特(3-4V),电流是安培(可能上百A),温度是摄氏度(-20到60°C)。如果不做归一化,模型会天然偏向数值大的特征。说白了,就是电流会把电压“欺负”得死死的。

两种主流方法:

方法 公式 适用场景 我的建议
Min-Max归一化 x' = (x - min) / (max - min) 数据有明确边界,如电压[2.5, 4.2] SOC预测中,电压和温度推荐用这个
Z-score标准化 x' = (x - μ) / σ 数据分布近似正态,无明确边界 电流数据常用,因为电流波动大,没有固定范围
小技巧: 归一化/标准化的参数(min, max, μ, σ)一定要用训练集计算,然后应用到验证集和测试集。千万别用全部数据算,否则会造成数据泄露,模型评估结果会虚高。这个坑我替你们踩过了。
# 归一化与标准化示例
from sklearn.preprocessing import MinMaxScaler, StandardScaler

# 假设 train_data, test_data 已经准备好
# 电压和温度用Min-Max
scaler_voltage = MinMaxScaler(feature_range=(0, 1))
train_data['voltage_norm'] = scaler_voltage.fit_transform(train_data[['voltage']])
test_data['voltage_norm'] = scaler_voltage.transform(test_data[['voltage']])

# 电流用Z-score
scaler_current = StandardScaler()
train_data['current_std'] = scaler_current.fit_transform(train_data[['current']])
test_data['current_std'] = scaler_current.transform(test_data[['current']])

3.5 本章知识体系:一张图看懂预处理流程

说了这么多,咱们用一张图把整个流程串起来。这是我做项目时自己画的,后来成了团队的标准流程。

SOC预测数据预处理流程 传感器数据采集 电压/电流/温度 缺失值处理 前向填充 / 插值 / 删除 异常值检测 3σ / IQR / 阈值 归一化 / 标准化 Min-Max / Z-score 特征工程 衍生特征 / 降维 数据集划分 训练集 / 验证集 / 测试集 关键注意事项 • 参数只用训练集计算 • 连续缺失>5点直接丢弃 • 异常值先分析原因再处理 • 保存scaler供推理时使用 • 不同特征用不同归一化方法 • 预处理顺序不要乱 图:SOC预测数据预处理完整流程

嗯,这张图基本概括了咱们这一章的核心内容。从传感器采集开始,到缺失值处理、异常值检测,再到归一化标准化,最后划分数据集。每一步都有讲究,顺序也不能乱。

我记得第一次做SOC项目时,图省事跳过了异常值检测,结果模型训练出来,预测误差大得离谱。后来排查了两天才发现,是传感器偶尔的毛刺在作怪。从那以后,我再也不敢偷懒了。

数据预处理这件事,说白了就是“磨刀不误砍柴工”。你花80%的时间把数据收拾干净,后面建模可能只需要20%的时间。反过来,数据一团糟,模型再先进也白搭。

核心要点回顾:
  • 传感器采集:注意采样频率和标定
  • 缺失值处理:前向填充最常用,连续缺失多则丢弃
  • 异常值检测:IQR方法比3σ更鲁棒,结合领域知识设阈值
  • 归一化/标准化:电压温度用Min-Max,电流用Z-score
  • 所有预处理参数只从训练集计算

公众号:蓝海资料掘金营,微信deep3321