第一步:数据预处理与滤波

说实话,我见过太多人在轨迹优化上栽跟头,最后发现根本不是算法的问题——原始数据本身就是一团糟。你想想看,拿一堆带毛刺、有跳点、甚至趋势都没对齐的数据去跑优化,结果能好才怪。

我个人习惯把预处理分成三个台阶:先管好采集规范,再干掉异常值,最后做滤波。每一步都有坑,咱们一个一个说。

核心原则:预处理不是可有可无的「清洁工」,它是整个优化流程的地基。地基歪了,上面盖多高的楼都没用。

数据采集规范——源头决定上限

我在项目里遇到过最离谱的事:客户给了三个月的轨迹数据,结果一问,采样率一会儿 50Hz 一会儿 100Hz,时间戳还有重复的。你说这怎么处理?

所以第一步,先把采集规范定死:

  • 采样率统一:所有传感器必须用同一个时钟源,或者事后重采样到固定频率。我个人习惯用 100Hz,够用又不至于数据量爆炸。
  • 时间戳对齐:多传感器融合时,时间戳偏差超过 1ms 就会引入相位误差。我一般用插值法把各通道对齐到同一时间轴。
  • 量纲归一:位置用米,速度用米/秒,角度用弧度。别混着用,否则滤波器的参数根本调不通。

小技巧:采集时顺手记一份日志,把传感器型号、采样率、环境温度都写进去。排查问题时,这些信息能救命。

异常值检测与剔除——别让一个坏点毁掉整条曲线

异常值这东西,说白了就是「一眼假」的数据点。比如无人机定位突然跳了 10 米,或者机械臂关节角度瞬间变了 30 度。这种点不干掉,后面的滤波和优化全白干。

我常用的方法有三种:

方法 适用场景 缺点
3σ 准则 数据近似正态分布 对非高斯分布效果差
IQR 四分位距 有离群点的稳健估计 计算稍复杂
局部离群因子 (LOF) 密度不均匀的数据 参数敏感

我个人最常用的是 IQR 法,因为它对数据分布不敏感,而且实现简单。具体做法:

  1. 计算数据的 Q1(25% 分位)和 Q3(75% 分位)
  2. IQR = Q3 - Q1
  3. 正常范围:[Q1 - 1.5×IQR, Q3 + 1.5×IQR]
  4. 超出范围的点直接剔除,用前后点的均值插补
def remove_outliers_iqr(data, factor=1.5):
    q1 = np.percentile(data, 25)
    q3 = np.percentile(data, 75)
    iqr = q3 - q1
    lower = q1 - factor * iqr
    upper = q3 + factor * iqr
    mask = (data >= lower) & (data <= upper)
    return data[mask]

注意:剔除异常值后一定要做插补,否则时间轴会断掉。我一般用线性插值,简单够用。如果数据变化剧烈,可以用样条插值。

低通滤波——把高频噪声滤干净

传感器数据里最烦人的就是高频抖动。比如 IMU 的加速度计,静止时都能读出 ±0.1g 的噪声。这时候就需要低通滤波。

我常用的两种低通滤波:

移动平均滤波

这是最朴素的方法,说白了就是取窗口内的平均值。窗口越大,曲线越平滑,但延迟也越大。

def moving_average(data, window_size=5):
    return np.convolve(data, np.ones(window_size)/window_size, mode='same')

我在项目中遇到过一个问题:窗口选 3 太平滑不够,选 10 又太滞后。后来我用了自适应窗口——根据当前数据的方差动态调整窗口大小。方差大就加大窗口,方差小就缩小窗口。效果还不错。

卡尔曼滤波

卡尔曼滤波比移动平均高级不少。它不光滤波,还能做状态估计和预测。说白了,它知道系统的运动模型,然后根据模型预测和观测值做加权平均。

我刚开始用卡尔曼滤波时也犯过傻——直接把所有参数设成 1,结果滤波效果还不如移动平均。后来才明白,调参才是关键:

  • 过程噪声协方差 Q:代表你对模型的信任程度。Q 越小,越相信模型;Q 越大,越相信观测。
  • 观测噪声协方差 R:代表传感器的噪声水平。R 可以从传感器手册里查,也可以自己标定。

经验之谈:如果你不确定 Q 和 R 怎么设,先跑一次离线标定。把传感器静止时的数据录下来,方差就是 R;然后根据运动模型算一下理论误差,那就是 Q 的起点。

高通滤波——把趋势项去掉

低通滤波搞定了高频噪声,但数据里可能还有低频趋势项。比如无人机长时间飞行,GPS 定位会慢慢漂移;或者机械臂长时间运行,关节编码器会有温度漂移。

趋势项不干掉,优化算法会误以为这是真实运动,结果就是轨迹越优化越偏。

高通滤波的做法很简单:先低通滤波提取趋势,然后用原始数据减去趋势。

def highpass_filter(data, window_size=100):
    trend = moving_average(data, window_size)
    return data - trend

窗口大小怎么选?我一般看趋势项的周期。比如 GPS 漂移的周期是几分钟,那窗口就设成几分钟的数据量。你想想看,窗口太小会把真实运动也滤掉,窗口太大又滤不干净趋势。

避坑指南:高通滤波会引入相位偏移。如果你后续要做时间对齐或者多传感器融合,一定要做零相位滤波(比如用 scipy 的 filtfilt 函数)。

本章知识体系

下面这张图把数据预处理的流程串起来了。你可以把它当作操作手册,每一步做完检查一下,再往下走。

数据预处理与滤波流程 原始轨迹数据 数据采集规范 异常值检测与剔除 低通滤波(去高频) 高通滤波(去趋势) 干净轨迹数据 两条路径可并行处理

嗯,到这里数据预处理就做完了。你拿到的应该是一条干净、平滑、没有趋势漂移的轨迹。别小看这一步,我见过太多人急着跑优化算法,结果花在调参上的时间比预处理还多——得不偿失。