GPS轨迹抽稀:降频、滑动窗口、RDP

一般来说,GPS轨迹的采样间隔越小,轨迹点越密集,越能够反映车辆行驶的实际情况。但是过度密集的轨迹点会增加计算和存储的负担,因此必要时需要进行抽稀处理。例如轨迹采样间隔为3秒时(即每隔3秒采集一个轨迹点),可以过滤掉部分轨迹点,使采样间隔变为10秒或其他满足需求的值,同时保留路径的形状特征和轨迹信息。

本文会介绍3种常用的抽稀方法:降频、滑动窗口、RDP(Ramer-Douglas-Peucker) ,已集成在AutoTrack库的【轨迹抽稀模块】

1、降频

降频即降低采样频率:例如每3个轨迹点保留1个(即跳过2个轨迹点)

python 复制代码
# 若存在10个轨迹点points
remained_list = list(range(0, len(points), 3))	# [0, 3, 6, 9]

2、滑动窗口

2.1、算法说明

将理想的轨迹点平均采样间隔作为滑动窗口,窗口内最多保留1个轨迹点

【关键步骤】

  • 初始化:设置理想的平均采样间隔IdealTimeInterval,初始化累计时间误差CumulativeTimeBias为0
  • 从第一个点开始依次判断,默认保留第一个点
  • 对于其他点,计算与上一个点的时间差(时间戳相减),并将该时间差加到CumulativeTimeBias
  • 若 C u m u l a t i v e T i m e B i a s ≥ I d e a l T i m e I n t e r v a l CumulativeTimeBias \ge IdealTimeInterval CumulativeTimeBias≥IdealTimeInterval,则保留该点,并更新CumulativeTimeBias
    • C u m u l a t i v e T i m e B i a s = C u m u l a t i v e T i m e B i a s % I d e a l T i m e I n t e r v a l CumulativeTimeBias = CumulativeTimeBias \% IdealTimeInterval CumulativeTimeBias=CumulativeTimeBias%IdealTimeInterval
    • CumulativeTimeBias为与IdealTimeInterval相除的余数
  • 否则,不保留该点

【优点】

  • 核心逻辑是保证每个理想平均采样间隔内至多只有一个点,使实际平均采样间隔接近理想值
  • 对轨迹的实际采样间隔无要求:若实际值大于理想值,则大多数点得以保留,仅剔除少量点

【举例说明】

  • 初始化:IdealTimeInterval=10CumulativeTimeBias为0,初始点保留
  • 第2个点间隔8秒:CumulativeTimeBias=8+0<10,不保留
  • 第3个点间隔6秒:CumulativeTimeBias=6+8≥10,保留,CumulativeTimeBias=14%10=4
  • 第4个点间隔12秒:CumulativeTimeBias=12+4≥10,保留,CumulativeTimeBias=16%10=6
  • 第5个点间隔3秒:CumulativeTimeBias=3+6<10,不保留
  • ...

2.2、代码示例

python 复制代码
# 初始化累计误差为0,若大于期望值则更新
ideal_time_interval = 10
cumulative_time_bias = 0
# 保留第一个轨迹点
remained_list = [0]
# 轨迹点的时间戳(精确到为毫秒)列表timestamps
for i in range(len(timestamps) - 1):
    interval = (timestamps[i + 1] - timestamps[i]) / 1000
    # 计算cumulative_time_bias,若cumulative_time_bias >= 期望值,则保留轨迹点,并更新cumulative_time_bias
	cumulative_time_bias += interval
	if cumulative_time_bias >= ideal_time_interval:
	    remained_list.append(i + 1)
	    cumulative_time_bias %= ideal_time_interval

3、RDP(Ramer-Douglas-Peucker)

Ramer-Douglas-Peucker算法(简称RDP算法)是一种用于减少由线连接的点集的点数的算法。

该算法通过保留数据的基本形状来简化曲线或多边形。例如在地理信息系统(GIS)中简化地图数据:在保留车辆行驶GPS轨迹特征的前提下,尽量减少轨迹点的数量

3.1、算法说明

【关键步骤】

  • 对于起终点之间的所有点,计算点到直线的距离并确定最大距离
    • 可根据需要选择合适的距离计算方式:直线距离或者球面距离等
  • 若最大距离大于距离阈值,则以这个点为界将原路径分为两部分,递归进行此操作;
  • 否则,删除中间点


    【算法缺陷】
  • 距离阈值依赖经验,可以根据数据的局部特征(如密度)动态调整阈值(可行但是过于复杂)
  • 受离群点影响大:仅考虑距离指标,会把离群点当做特征点保留下来(切记:先剔除异常点

3.2、用于轨迹抽稀的注意事项

【距离计算】

  • 基于几何投影(方法1):使用shapely库的PointLineString对象的属性或方法计算点到线的距离
    • 问题:若点不在线的范围内(比如噪点,见下图),则得到的距离是点到线起点或终点的距离
  • 基于三角形(方法2):基于海伦公式(根据三条边长计算面积)确定点到线的距离(已知边长求高)
    • 需先判断是否构成三角形,若三点共线,则距离为0
  • 当点不在线的范围内时,方法1计算的距离一般比方法2计算的距离大( d 3 1 > d 3 2 d_3^1 > d_3^2 d31>d32),对于确定间距最大的点一般无影响(轨迹点3到起终点连线的距离最大);当点在线的范围内时,两种计算方式的结果基本相等

【重投影】

将要删除的点进行重投影(投影到该点两侧最近的要保留的点构成的连线上),以投影点的坐标替代原坐标(轨迹点数量不变)

  • 确定投影点:使用shapely库的PointLineString对象的属性或方法确定点在线上的投影点
  • 投影点的时间戳、速度可直接使用原有轨迹点的值,但是航向角需更新
  • 计算投影点航向角:可以将连线的角度与正北方向的夹角作为投影点的航向角

3.3、代码示例

完整代码详见:AutoTrack库【轨迹抽稀模块】

python 复制代码
# 轨迹抽稀
def cal_projection_distance(left_p, right_p, other_p):
    # 实现方式1:
    # 构造shapely库的Point、LineString对象,使用函数得到点到线的距离
    # Point.distance获取点到线的最小距离:若点在线的范围内,返回投影距离;若不在范围内,则大概率返回点到线的端点的距离(受线形影响)
    # points_utm = [self.trans_4326.transform(lng, lat) for lng, lat in [left_p, right_p, other_p]]
    # line_utm = LineString(points_utm[:-1])
    # h_1 = Point(points_utm[-1]).distance(line_utm)

    # 实现方式2:
    # 计算点之间的距离,根据海伦公式(Heron's formula)计算三角形的面积(若共线则为0),根据面积计算某条边的高
    a = cal_haversine_dis(left_p, other_p)
    b = cal_haversine_dis(right_p, other_p)
    c = cal_haversine_dis(left_p, right_p)
    if a + b > c and b + c > a and c + a > b:
        s = (a + b + c) / 2
        h_2 = 2 * np.sqrt(s * (s - a) * (s - b) * (s - c)) / c
    else:
        h_2 = 0

    # 【说明】当点不在线的范围内时(比如噪点),根据投影计算的距离比根据三角形计算的数值大,对于确定间距最大的点无影响;
    #        当点在线的范围内时,两种计算方式的结果基本相等
    # print(f'根据投影计算的距离为{round(h_1,2)},根据三角形计算的距离为{round(h_2,2)}')
    return h_2

def rdp_core(points, left, right):
    if right - left < 2:
        return [left, right]

    if np.all(points[left] == points[right]):
        return [left, right]

    max_dis = 0.0
    max_i = 0
    for i in range(left + 1, right):
        distance = cal_projection_distance(
            points[left], points[right], points[i]
        )
        if distance > max_dis:
            max_i = i
            max_dis = distance

    if max_dis > self.core_param:
        results1 = rdp_core(points, left, max_i)
        results2 = rdp_core(points, max_i, right)
        return results1[:-1] + results2
    else:
        return [left, right]

# 轨迹点points(经纬度坐标)
remained_list = rdp_core(points, 0, len(points) - 1)

# 重投影:找到被剔除点,使用投影坐标替换原坐标
simplified = []
updated_info = []
for left, right in zip(remained_list, remained_list[1:]):
    if left + 1 == right:
        continue
    # 找到被剔除点,使用投影坐标替换原坐标
    for other in range(left + 1, right):
        # 投影坐标系转换为平面坐标系
        points_utm = [trans_4326.transform(lng, lat) for lng, lat in [points[left], points[right], points[other]]]
        line_utm = LineString(points_utm[:-1])
        # 对于line.project(point),若点的投影点在线的起点之前,则返回0;若在线的终点之后,则返回线的长度;若在线上,则返回起点到投影点之间的长度
        # line.interpolate(line.project(point)),若点的投影点在线的起点之前,则返回起点
        point_pro = line_utm.interpolate(
            line_utm.project(Point(points_utm[-1]))
        )
        x, y = point_pro.x, point_pro.y
        # 平面坐标系转换为投影坐标系
        lng, lat = trans_32648.transform(x, y)
        # 记录要重投影的点的索引
        simplified.append(other)
        # 记录投影点坐标
        updated_info.append([lng, lat])
        # 计算航向角(根据两点的坐标计算)
        bearing = cal_bearing(*points[left], *points[right])
        updated_info[-1].append(bearing)

# 轨迹点数量不变
remained_list = list(range(len(points)))
相关推荐
Jacob程序员44 分钟前
欧几里得距离算法-相似度
开发语言·python·算法
网安老伯1 小时前
什么是网络安全?网络安全包括哪几个方面?学完能做一名黑客吗?
linux·数据库·python·web安全·网络安全·php·xss
天才测试猿1 小时前
Postman接口测试:如何导入swagger接口文档?
自动化测试·软件测试·python·测试工具·职场和发展·接口测试·postman
计算机学姐2 小时前
基于Python的校园美食推荐系统【2026最新】
开发语言·vue.js·后端·python·mysql·django·推荐算法
天天进步20152 小时前
Python全栈实战:基于机器学习的用户行为分析系统
python
linzeyang2 小时前
Advent of Code 2025 挑战全手写代码 Day 5 - 餐厅
后端·python
祝余Eleanor2 小时前
Day 29 类的定义及参数
人工智能·python·机器学习
码界奇点2 小时前
基于Dash+FastAPI的通用中后台管理系统设计与实现
python·车载系统·毕业设计·fastapi·源代码管理·dash
white-persist2 小时前
【攻防世界】reverse | Mysterious 详细题解 WP
c语言·开发语言·网络·汇编·c++·python·安全