在做轨迹处理(GPS / 车辆 / 无人机 / 人员定位)时,

  • 经纬度算距离
  • 用时间算速度
  • 小距离丢弃
  • 大速度丢弃

一、核心目的

判断一个定位点是否“符合物理常识”:
如果几乎没动 → 抖动点
如果快得不可能 → 异常点
都直接丢掉


二、完整代码片段

        // 1) 时间阈值:太密集就不写 buffer(减少下游点数)
        Long le = lastEmitTs.value();
        if (le != null && p.ts - le < cfg.minEmitIntervalMs) {
            return;
        }

        // 2) 距离/速度阈值:基于 lastPoint 计算两点间距离与速度
        Double derivedSpeed = null;
        if (lp != null) {
            long dtMs = max(1, p.ts - lp.ts); // 避免除 0
            double distM = haversineMeters(lp.lat, lp.lon, p.lat, p.lon);

            // 抖动点:位移过小(常见于 GPS 漂移/静止抖动)
            if (distM < cfg.minMoveMeters) {
                incJitterDrop();
                return;
            }

            // 推导速度:米/秒
            derivedSpeed = distM / (dtMs / 1000.0);

            // 异常速度:大概率是定位跳点或异常数据
            // 当前策略:直接丢弃(也可以改成“切段”或“保留但打标”)
            if (derivedSpeed > cfg.maxSpeedMps) {
                incSpeedDrop();
                return;
            }
        }
    /**
     * Haversine 公式计算球面两点距离(单位:米)
     * - R=6371000m:地球平均半径
     * - 适合轨迹点距离计算(GPS)
     */
    private static double haversineMeters(double lat1, double lon1, double lat2, double lon2) {
        double R = 6371000.0;
        double dLat = toRadians(lat2 - lat1);
        double dLon = toRadians(lon2 - lon1);
        double a = sin(dLat/2)*sin(dLat/2) +
                cos(toRadians(lat1))*cos(toRadians(lat2)) *
                        sin(dLon/2)*sin(dLon/2);
        double c = 2 * atan2(sqrt(a), sqrt(1-a));
        return R * c;
    }

三、先搞清楚:这两个点是谁?

LastPoint lp = lastPoint.value(); // 上一个“已接受”的点
RawPoint p = 当前新来的点;

你可以把它理解成:

lp(上一个点) ─────────▶ p(当前点)

所有计算,都是围绕 “这两个点之间发生了什么”


四、第一步:算时间差(dtMs)

long dtMs = max(1, p.ts - lp.ts);

这一步在干什么?

  • p.ts - lp.ts:两个点之间隔了多久(毫秒)
  • max(1, …):防止除 0(非常重要)

举例

lp.ts = 10000 ms
p.ts  = 10050 ms
dtMs  = 50 ms

如果两个点时间戳相同:

dtMs = 1 ms(强行兜底)

五、第二步:算距离(distM)——为什么要用 Haversine?

double distM = haversineMeters(lp.lat, lp.lon, p.lat, p.lon);

这一步的本质

根据两个经纬度,计算它们在“地球表面”的真实距离(米)


Haversine 是什么?(不用怕)

private static double haversineMeters(...)

这是 GPS / 地图 / 轨迹领域的标准算法,特点是:

  • ✅ 考虑地球是球体
  • ✅ 精度足够(米级)
  • ✅ 被广泛使用

你可以把它当成一个“官方认证的算距离工具”,
不用关心数学推导,只管用。


举个能理解的例子

lp: (39.900000, 116.400000)
p : (39.900010, 116.400010)

大约相当于:

→ 移动了 1~2 米

那么:

distM ≈ 1.5

六、第三步:抖动点判断(为什么距离太小要丢?)

if (distM < cfg.minMoveMeters) {
    incJitterDrop();
    return;
}

什么是抖动点?

现实中的 GPS:

  • 人 / 车 / 无人机 静止
  • 经纬度却会在 ±1~3 米 范围内乱跳

如果不处理,轨迹会变成:

原地抖成一团毛线

举例说明

minMoveMeters = 2 米
distM = 0.6 米

👉 判断为 GPS 漂移,不是真实移动 → 丢弃


七、第四步:算推导速度(derivedSpeed)

derivedSpeed = distM / (dtMs / 1000.0);

拆开看:

dtMs / 1000.0 → 秒
distM / 秒     → 米/秒

举例(非常直观)

distM = 10 米
dtMs = 1000 ms = 1 秒

derivedSpeed = 10 m/s

八、第五步:异常速度判断(为什么速度太大要丢?)

if (derivedSpeed > cfg.maxSpeedMps) {
    incSpeedDrop();
    return;
}

这一步在防什么?

防这种情况:

上一点:北京
下一点:上海
时间差:1 秒

算出来的速度是:

1000+ 公里 / 秒 ❌

这种情况通常是:

  • GPS 跳点
  • 数据错位
  • 经纬度异常
  • 设备故障

maxSpeedMps 一般怎么设?

场景 合理范围(m/s)
行人 3 ~ 5
自行车 8 ~ 12
汽车 30 ~ 60
无人机 50 ~ 100

九、总结

如果有上一个点:
    1. 算这两个点隔了多久
    2. 算这两个点隔了多远
    3. 如果几乎没动 → GPS 抖动 → 丢
    4. 算实际速度
    5. 如果快得不可能 → 异常 → 丢
否则:
    这个点是正常的

如果你 不做这层过滤,结果通常是:

  • 轨迹乱抖
  • 偶尔飞线几百公里
  • 地图前端缩放直接炸
  • 下游分析严重失真

本质原因是:

真实世界 ≠ 传感器世界
传感器数据必须经过“物理常识过滤”

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐