数据预处理:数据清洗与对齐

数据预处理,说白了就是给原始数据「洗澡」。

我做了这么多年嵌入式,见过太多「垃圾进,垃圾出」的案例。你想想看,回放的数据如果本身就有问题,那定位问题就是缘木求鱼。所以,这一章我们聊聊怎么把数据洗干净、对齐好。

数据清洗:去重、补全、异常值处理

数据清洗是预处理的第一步。我个人习惯把它分成三个动作:去重、补全、处理异常值。

1. 数据去重

为什么会重复?最常见的原因是日志采集程序崩溃重启,或者网络重传。重复数据会干扰统计,甚至导致回放逻辑出错。

我的做法:

  • 基于时间戳去重:同一毫秒内出现两条相同ID的日志,只保留第一条。
  • 基于哈希值去重:对数据内容做MD5,相同哈希的直接丢弃。

避坑指南

我曾经遇到过一个问题:两条数据时间戳完全一样,但内容不同。这种情况不能简单去重,需要保留。所以去重时一定要加「内容相同」这个条件。

// 伪代码:基于时间戳+内容去重
if (data.timestamp == last.timestamp && data.content == last.content) {
    skip(); // 重复,丢弃
} else {
    process(data); // 正常处理
}

2. 数据补全

嵌入式系统采集数据,经常丢包。比如传感器每10ms上报一次,但中间丢了3个点。这时候就需要补全。

我常用的补全策略:

  • 线性插值:适用于连续变化的数据,如温度、电压。
  • 前值填充:适用于状态量,如开关状态、模式切换。
  • 后值填充:适用于延迟上报的场景。

个人经验

我在做车载CAN总线数据回放时,经常遇到丢帧。我的习惯是:如果连续丢帧超过5个,就不补了,直接标记为「数据缺失」。因为补太多反而会引入假数据,干扰问题定位。

3. 异常值处理

异常值,说白了就是「离谱」的数据。比如温度传感器突然读到200℃,或者速度值变成负数。

怎么判断异常?

  • 阈值法:设定合理范围,超出就标记。
  • 3σ原则:超出均值±3倍标准差的数据,视为异常。
  • 变化率法:相邻两个点的差值超过阈值,视为异常。

注意

异常值不一定要删除。有时候异常值本身就是问题的线索。比如电机电流突然飙升,那可能是堵转了。我建议先标记,再决定是剔除还是保留分析。

数据对齐:时间戳对齐、坐标系对齐

数据对齐,是回放前的关键一步。不同传感器、不同模块的数据,时间基准和坐标系可能都不一样。

1. 时间戳对齐

为什么会时间不对齐?因为每个模块都有自己的时钟。GPS用UTC,IMU用本地晶振,CAN总线用总线时间。这些时钟的精度和漂移都不一样。

我的对齐方法:

  • 统一基准:把所有时间戳都转成同一个基准,比如系统启动后的毫秒数。
  • 插值对齐:对于不同频率的数据,用插值法对齐到同一时间轴。
  • 最近邻对齐:找到时间上最接近的数据点进行匹配。
// 伪代码:时间戳对齐
// 将CAN数据对齐到GPS时间轴
for each gps_time in gps_timeline:
    can_data = find_nearest(can_timeline, gps_time)
    aligned_data.append(can_data)

避坑指南

我曾经遇到过一个坑:两个模块的时间戳差了整整1秒,但看起来又很接近。后来发现是一个模块用了UTC时间,另一个用了本地时间(+8时区)。所以对齐前一定要确认时区。

2. 坐标系对齐

坐标系对齐,在自动驾驶和机器人领域特别常见。比如激光雷达的坐标系和摄像头的坐标系不一样,需要做外参标定。

常见的坐标系:

  • 车体坐标系:以车辆中心为原点,前为X,左为Y,上为Z。
  • 传感器坐标系:以传感器自身为原点。
  • 世界坐标系:以地球上的某个点为原点。

对齐方法:

  • 旋转+平移:通过标定得到的旋转矩阵和平移向量,把数据从一个坐标系变换到另一个。
  • 欧拉角/四元数:用四元数表示旋转,避免万向锁问题。

个人经验

我建议在数据采集时就记录好每个传感器的安装位置和姿态。这样回放时可以直接用标定参数做变换,省去很多麻烦。

数据脱敏

数据脱敏,说白了就是「遮住敏感信息」。比如日志里可能包含用户手机号、车牌号、位置坐标等隐私数据。

脱敏的常用方法:

  • 替换:用固定字符替换敏感字段,比如手机号变成138****1234。
  • 哈希:对敏感数据做哈希,不可逆。
  • 截断:只保留部分信息,比如只保留城市名,不保留精确坐标。
  • 加噪:在原始数据上添加随机噪声,保留统计特性但无法还原。

注意

脱敏不是简单的「删掉」。如果只是删掉敏感字段,可能会破坏数据结构。我建议用占位符或者哈希值替代,保持数据格式完整。

// 伪代码:手机号脱敏
string phone = "13812345678";
string masked = phone.substr(0, 3) + "****" + phone.substr(7, 4);
// 输出:138****5678

知识体系总览

下面这张图,概括了数据预处理的完整流程。你可以把它当作一个检查清单。

数据预处理知识体系 数据清洗 数据对齐 数据脱敏 去重 补全 异常值处理 时间戳对齐 坐标系对齐 替换 哈希 截断 加噪 数据预处理的核心目标 让回放数据「干净、对齐、安全」

嗯,数据预处理这部分,说白了就是「磨刀不误砍柴工」。花点时间把数据洗干净、对齐好,后面回放定位问题会顺畅很多。

总结一下我的习惯:

  • 先做去重,再做补全,最后处理异常值。
  • 时间戳对齐时,统一基准,确认时区。
  • 坐标系对齐,提前标定,记录参数。
  • 数据脱敏,用占位符保持结构完整。

可可精品资料库