轨迹数据基础:从原始坐标到可用数据

大家好,我是你们的老朋友。今天我们来聊聊轨迹数据的基础处理。

说实话,很多刚入行的同学,一上来就急着调模型、跑网络。结果呢?数据预处理没做好,模型怎么调都不收敛。我当年就吃过这个亏,后来才明白——数据质量决定了模型的天花板

轨迹数据的表示方法

轨迹数据说白了,就是物体在空间中的运动记录。最常见的表示方式有三种:

1. 坐标表示

这是最直观的方式。每个时间点记录一个位置。

  • 二维坐标:(x, y) 或 (经度, 纬度)
  • 三维坐标:(x, y, z) 或 (经度, 纬度, 高度)

举个例子,一个行人的轨迹可能是这样的:

t=0s: (2.3, 5.1)
t=0.1s: (2.5, 5.3)
t=0.2s: (2.8, 5.4)
...

嗯,这里要注意:坐标系的选取直接影响后续计算。我在项目中遇到过用经纬度直接算距离的,结果误差大得离谱。为什么?因为地球是球面,经纬度不是等距的。

2. 速度表示

速度是位置对时间的一阶导数。我们通常用两种方式:

  • 瞬时速度:当前时刻的速度大小和方向
  • 平均速度:一段时间内的位移除以时间

实际项目中,我更喜欢用速度向量 (vx, vy) 来表示。为什么呢?因为它保留了方向信息,对轨迹预测特别有用。

3. 加速度表示

加速度是速度对时间的一阶导数,也是位置的二阶导数。它反映了运动状态的变化。

你想想看,一个急刹车和匀速行驶,加速度完全不同。这对预测车辆行为非常关键。

我的经验:在轨迹预测中,我通常会把位置、速度、加速度都作为输入特征。三者的组合能提供更完整的运动信息。但要注意,加速度对噪声特别敏感,需要先做平滑处理。

坐标系转换

坐标系转换,说白了就是把数据从一个坐标系搬到另一个坐标系。常见的场景有:

1. 地理坐标系 vs 平面坐标系

GPS采集的数据通常是经纬度(WGS84坐标系),但模型训练时我们更习惯用平面坐标(比如UTM坐标系)。

转换公式大致是这样的:

# 伪代码示例
def wgs84_to_utm(lat, lon):
    # 这里省略了复杂的投影计算
    # 实际项目中可以用 pyproj 库
    x = lon * 111320 * cos(lat)
    y = lat * 110540
    return x, y

避坑指南:我曾经直接用这个简化公式处理高纬度数据,结果误差达到了几十米。后来改用专业的投影库才解决。记住:简化公式只适合小范围区域

2. 世界坐标系 vs 车辆坐标系

自动驾驶中经常需要把世界坐标转换到车辆坐标系。这样模型看到的数据就是「相对位置」,而不是「绝对位置」。

转换步骤:

  1. 平移:减去车辆当前位置
  2. 旋转:乘以车辆朝向的旋转矩阵
import numpy as np

def world_to_vehicle(world_pos, vehicle_pos, vehicle_yaw):
    # 平移
    relative_pos = world_pos - vehicle_pos
    # 旋转
    cos_yaw = np.cos(vehicle_yaw)
    sin_yaw = np.sin(vehicle_yaw)
    rotation = np.array([[cos_yaw, -sin_yaw],
                         [sin_yaw, cos_yaw]])
    vehicle_pos = rotation @ relative_pos
    return vehicle_pos

轨迹数据预处理

原始数据通常很「脏」,不能直接用。我总结了三个关键步骤:

1. 去噪

传感器数据难免有噪声。GPS信号飘移、雷达误检,都会产生异常点。

常用的去噪方法:

  • 中值滤波:对窗口内的点取中值,能有效去除孤立噪声
  • 卡尔曼滤波:利用运动模型预测,再结合观测值修正
  • 阈值法:速度或加速度超过物理极限的点直接剔除

小技巧:我习惯先用阈值法粗筛,再用卡尔曼滤波精修。这样效率高,效果也好。阈值怎么定?看你的应用场景。行人一般速度不超过5m/s,车辆不超过30m/s。

2. 插值

传感器采样率不稳定,或者数据有缺失,就需要插值补全。

常见的插值方法:

方法 适用场景 优缺点
线性插值 匀速运动 简单快速,但不够平滑
三次样条插值 平滑运动 曲线光滑,但计算量大
多项式插值 已知运动规律 精度高,但容易过拟合
import numpy as np
from scipy import interpolate

# 原始数据(有缺失)
t = np.array([0, 1, 2, 4, 5])  # 缺少 t=3
x = np.array([0, 2, 4, 8, 10])

# 三次样条插值
f = interpolate.interp1d(t, x, kind='cubic')
t_new = np.array([0, 1, 2, 3, 4, 5])
x_new = f(t_new)

3. 重采样

不同传感器的采样率可能不同。比如摄像头30fps,雷达10fps。训练模型时,需要统一到相同的采样率。

重采样的步骤:

  1. 确定目标采样率(比如10Hz)
  2. 生成新的时间序列
  3. 用插值方法计算新时间点的位置

我的建议:重采样时要注意奈奎斯特采样定理。目标采样率至少是运动频率的两倍。比如行人步频约2Hz,那采样率至少4Hz。我一般取10Hz,留足余量。

实战中的注意事项

最后,分享几个我在项目中踩过的坑:

  • 数据对齐:多传感器数据的时间戳要对齐。我习惯用插值法把不同传感器的数据统一到同一时间轴。
  • 边界处理:轨迹的起点和终点数据往往不完整。我一般会截掉前后各5%的数据。
  • 数据增强:如果数据量不够,可以对轨迹做小幅旋转、平移、加噪声。但注意不要改变运动规律。

好了,这一章的内容就到这里。轨迹数据预处理看似基础,但做得好不好,直接决定了模型的上限。下一章我们会聊到时序特征工程,到时候见。

课后练习:找一份公开的轨迹数据集(比如nuScenes),尝试对其中一条轨迹做去噪、插值、重采样。看看处理前后的轨迹有什么变化。