星期二,一条bug报告弹了出来。就一行:“你们的应用说我开到了400公里/小时。我当时在等红灯。”
用户没撒谎。应用也没撒谎——它真的相信自己记录的数据。这才是最要命的地方。
我在做一个里程追踪工具,就是那种悄悄在后台记录你开了多少公里、方便报账或者扣税的应用。在这个品类里,行程准不准不是锦上添花,而是产品的命门。刚起步的时候,我们的准确率大概只有50%,每个翻车的行程几乎都能追溯到同一个源头:手机理直气壮地给出一堆假数据,而且表现得毫无怀疑。
后来我们把准确率拉到了95%。下面就是这中间干的两件事,以及藏在所有事情底下的那一个想法。
GPS就像一个刚撞到头的目击者
我们习惯把GPS当成真相的来源。但在真实世界里开过车的人都知道,它的表现更像一个脑震荡还没好的目击者:自信、配合、频繁出错。
有三个场景能把它搞崩得特别惨。
隧道。接收器突然看不到天空了。它不吭声,不告诉你“信号丢了”,而是继续死死拽着最后记住的那个位置,有时候能硬撑三四秒。等你从隧道另一头出来,信号一恢复,它猛地窜到真实位置——一次跳跃。把那段距离除以毫秒级的时间差,你就能看到一辆静止在红灯前的车跑出400公里/小时。
城市峡谷。高楼大厦的玻璃幕墙把信号弹来弹去,术语叫“多径效应”。手机把一堆反射信号求了个平均,然后一脸笃定地把你钉在隔了一条街的位置上。
停车场。什么都没了,只剩噪音,然后又没了。这种场景不在边缘地带,它们就是日常。于是我们的问题不再是“怎么获得更好的GPS信号”,而是“怎么不去相信那个信口开河的GPS”。
修法一:抓住说谎的那个点
最便宜也最快见效的一步。任何一种出行方式,都有一个物理上可能的运动包络。走路的人不会一秒横跨200米,汽车加速也不会像导弹点火。所以在任何一个坐标点被录入行程之前,先问一句:它暗示了一件物理上根本不可能发生的事吗?
计算旧坐标和新坐标的半正矢距离,除以时间戳的差值(至少要取1秒,免得除零捣乱),算出当时的速度。如果这个速度超过了当前模式下的合理上限——步行、骑行、驾车的阈值各自不同——那就直接把这一点丢掉。下面这个guard函数本身没什么花哨逻辑,但它单独杀死了我们手里最社死的bug,包括那个400公里/小时的报告。它不聪明,它只是拒绝被手机PUA。
fun GpsFix.isImpossible(prev: GpsFix, mode: Mode): Boolean { val meters = haversine(prev, this) val seconds = (timestamp - prev.timestamp).coerceAtLeast(1) val speed = meters / seconds return speed > mode.maxPlausibleSpeed}修法二:没有星图的时候,就靠“猜”把坏点一脚踢开之后,你会得到一堆空洞,而隧道就是一个巨大的空洞。如果傻等在原地直到GPS信号恢复,行程就会变成一条横穿三个街区的直线。
所以当GPS一掉,就别再等它了。拿好最后已知的朝向和速度,把加速度计的读数吃进去,自己把位置往前推。几百年前的水手就是这么干的——没有星星,没有陆地标志,只有方向、时间和一个不见得准但总比没有好的估算。
这种方法当然会漂移。加速度和方向的小误差会在连续的积分里积累,所以不能长时间靠它撑着。但一条隧道也就是几十秒的事,而“关键40秒里的粗估”远胜于“一脸自信的胡扯”和“地图上一个黑洞”。不需要完整的导航方程,一个简单的航位推算式子就够用了:新位置≈旧位置+上一帧的速度×时间+加速度计捕捉到的速度变化。漂移?有。短时间把车钉在隧道墙上?几乎没有。
所以最后那一个想法是什么?
不是追求更好的GPS,而是学会让你的手机不要再相信它自己的GPS。给它一道物理定律铸成的门槛,把明显违背现实的数据挡在门外;再给它一段惯性的记忆,让它在看不见天空的那几十秒里不至于彻底失明。
就是这两件事,把准确率从一半出头推到了95%。而那个核心心态的转变其实只有一个:把GPS从“真理源”降格成“提供线索的证人”——还是一个随时可能胡说的证人。一旦你开始用这个眼光看每一个坐标点,很多以前“诡异但没法解释”的bug突然就变得很清晰了。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.