3、数据采集与预处理:传感器数据采集、数据清洗与滤波、时间同步与数据对齐、数据标注与分割
好,咱们进入第三个章节。说实话,数据采集与预处理这块,是整门课程里最「脏」最「累」的活,但也是决定模型校准成败的关键。我见过太多人,模型算法选得天花乱坠,结果数据一塌糊涂,最后全白干。所以,咱们得把地基打牢。
3.1 传感器数据采集:从物理世界到数字世界的「翻译」
飞控系统里,传感器就是我们的眼睛和耳朵。IMU(惯性测量单元)告诉你加速度和角速度,GPS告诉你位置,空速管告诉你速度。但这些东西输出的原始信号,可不是我们直接能用的。
采集的核心问题有两个:采样率和分辨率。
采样率,说白了就是每秒读多少次。IMU通常要100Hz到400Hz,GPS慢一些,10Hz左右。分辨率呢,就是传感器能感知的最小变化。比如一个12位的加速度计,能输出4096个不同的值。
我个人习惯,在采集之前先做一件事:确认传感器的量程。你想想看,一个量程±2g的加速度计,用在特技飞行的无人机上,一拉杆就超量程了,数据全被削顶。我在项目中遇到过,有人用手机级别的IMU去做高速固定翼的飞控校准,结果数据全是平的——因为早就超量程了。
采集的硬件链路也很关键。我建议用同步串行接口(SPI)或者I²C,尽量避免用USB转串口这种有不确定延迟的方式。嗯,这里要注意,如果实在要用USB,记得用带硬件时间戳的采集卡。
3.2 数据清洗与滤波:剔除「脏数据」
数据采回来了,但里面什么妖魔鬼怪都有。传感器本身有噪声,环境有干扰,通信链路有丢包。所以,清洗和滤波是必须的。
数据清洗,我一般分三步走:
- 去除异常值: 用3σ原则或者箱线图。比如加速度计突然跳到一个不可能的值,直接扔掉。
- 填补缺失值: 如果丢包了,用线性插值或者样条插值。但注意,如果连续丢包超过5个,我建议直接丢弃这段数据,别硬补。
- 去除趋势项: 传感器会有零偏漂移,比如静止时加速度计输出不是0,而是0.01。这个要去掉,不然积分后位置误差会越来越大。
滤波呢,我常用的有几种:
| 滤波器类型 | 适用场景 | 我的经验 |
|---|---|---|
| 低通滤波(LPF) | 去除高频噪声 | IMU数据必用,截止频率设为采样率的1/10左右 |
| 中值滤波 | 去除脉冲噪声 | GPS偶尔跳点,用中值滤波很管用 |
| 卡尔曼滤波 | 融合多传感器 | 这个后面章节会细讲,但预处理阶段别用,太复杂 |
3.3 时间同步与数据对齐:让所有传感器「说同一种语言」
这是最容易出问题的一步。IMU、GPS、空速管,它们各自有自己的时钟。IMU可能每10ms输出一次,GPS每100ms输出一次。如果不做时间同步,你拿到的数据就是「鸡同鸭讲」。
时间同步有两种主流方法:
- 硬件同步: 用PPS(秒脉冲)信号。GPS模块输出一个精确的秒脉冲,所有传感器都以此为基准。这是最准的,误差在微秒级。
- 软件同步: 如果没有硬件支持,就用插值法。比如GPS数据在t=0.1秒,IMU数据在t=0.105秒,那就把IMU数据插值到t=0.1秒。
我曾经在项目中犯过一个低级错误:IMU用的是UTC时间,GPS用的是GPS周秒,两者差了18秒。结果校准出来的模型,姿态和位置永远对不上。后来查了三天才发现是时间基准没统一。所以,我建议所有数据统一用Unix时间戳(毫秒级),省心。
数据对齐的代码示例,我一般这么写:
import pandas as pd
import numpy as np
# 假设imu_data和gps_data都是DataFrame,包含'timestamp'列
# 统一时间基准为毫秒级Unix时间戳
imu_data['timestamp'] = pd.to_datetime(imu_data['timestamp']).astype(np.int64) // 10**6
gps_data['timestamp'] = pd.to_datetime(gps_data['timestamp']).astype(np.int64) // 10**6
# 以GPS时间为基准,对IMU数据进行插值对齐
aligned_imu = pd.DataFrame()
for col in ['accel_x', 'accel_y', 'accel_z', 'gyro_x', 'gyro_y', 'gyro_z']:
aligned_imu[col] = np.interp(gps_data['timestamp'], imu_data['timestamp'], imu_data[col])
aligned_imu['timestamp'] = gps_data['timestamp']
3.4 数据标注与分割:给数据打上「标签」
数据清洗干净、时间对齐好了,接下来就是标注和分割。这一步是为了让模型知道:这段数据对应的是「平飞」,那段对应的是「转弯」。
标注的方式有两种:
- 人工标注: 用标注工具(比如LabelImg、CVAT)手动框选。准确率高,但费时费力。我一般只标注关键动作段。
- 自动标注: 根据飞控日志里的飞行模式(比如AUTO、STABILIZE)自动打标签。效率高,但要注意日志里的模式切换时间戳是否准确。
我个人的习惯是「先自动,后人工」。先用脚本根据飞行模式自动标注,然后人工抽检10%的数据,看看有没有标错的。曾经有一次,自动标注把「降落滑跑」标成了「平飞」,因为两者的姿态角很接近,但地速完全不同。所以,标注时一定要结合多个维度,比如姿态、速度、高度。
数据分割呢,要注意几点:
- 训练集、验证集、测试集: 一般按70%、15%、15%分割。但注意,不要随机分割。因为时间序列数据有连续性,随机分割会导致数据泄露。我习惯按时间段分割,比如前70%的时间段做训练,中间15%做验证,最后15%做测试。
- 每个分割段要包含完整的动作: 比如一个完整的「起飞-巡航-降落」过程,不要切一半。不然模型学到的就是不完整的动作模式。
- 保持数据分布一致: 训练集和测试集的飞行条件(风速、载荷)要尽量相似。如果训练集全是无风天气,测试集全是强风,那模型肯定崩。
本章知识体系总览
下面这张图,是我自己总结的数据采集与预处理流程。你把它打印出来贴在工位上,每次做数据前看一眼,能少踩很多坑。
好了,数据采集与预处理这部分,核心就是这些。说白了,就是「把原始数据变成干净、对齐、有标签的数据集」。这一步做扎实了,后面的模型校准才能事半功倍。别嫌麻烦,我见过太多人在这上面栽跟头了。