7、位置质量:Accuracy精度概念、速度与方位计算、海拔与地磁场数据获取
各位同学,今天我们来聊聊位置质量这个话题。说实话,很多人拿到经纬度就觉得万事大吉了,但实际开发中,位置数据的质量才是真正决定应用体验的关键。我见过太多App因为忽略了精度问题,导致导航路线飘到河里去,或者运动轨迹画成了心电图。
7.1 精度(Accuracy)—— 别被数字骗了
先问大家一个问题:你手机显示“精度10米”,真的意味着你就在那个10米圆圈里吗?
嗯,不一定。Android的精度值是一个概率估计,通常表示有68%的概率你的真实位置在以该点为圆心、精度值为半径的圆内。说白了,这是一个统计学概念,不是绝对边界。
- getAccuracy() 返回水平精度(米),越小越好
- 精度值 ≤ 20米:适合导航、运动追踪
- 精度值 20~100米:适合签到、天气
- 精度值 > 100米:只适合粗略定位(城市级别)
我在项目中遇到过一个问题:某款户外App在山区经常出现精度值突然跳到500米的情况。排查后发现,是因为GPS信号被山谷遮挡,系统自动切换到了基站定位。所以,不要盲目信任精度值,要结合场景做过滤。
7.2 速度与方位计算 —— 别自己算,有现成的
很多新手喜欢自己算速度:拿两个位置的距离除以时间差。你想想看,这有多不靠谱?GPS信号抖动一下,算出来的速度能飙到200km/h。
Android Location 对象已经提供了两个现成的方法:
// 获取速度(米/秒)
float speed = location.getSpeed();
// 获取方位角(度,0=北,90=东,180=南,270=西)
float bearing = location.getBearing();
这两个值是由GPS芯片直接计算出来的,内部做了平滑滤波,比你自己算靠谱得多。我曾经在骑行App里对比过,自己算的速度波动极大,而 getSpeed() 返回的值非常稳定。
- getSpeed() 只在 GPS 定位时有效,网络定位通常返回 0
- getBearing() 需要设备在运动中才有意义,静止时返回 0
- 如果设备没有移动,bearing 值会乱跳,建议结合 speed 判断
那如果我想自己算方位呢?Android 也提供了工具类:
float[] results = new float[3];
Location.distanceBetween(
startLat, startLng, // 起点
endLat, endLng, // 终点
results
);
// results[0] = 距离(米)
// results[1] = 初始方位角(度)
// results[2] = 最终方位角(度)
这个方法用的是WGS84椭球模型,精度很高。我在做轨迹回放功能时就用它来计算每两个点之间的方向变化。
7.3 海拔高度 —— 别太当真
海拔数据是个坑。真的,我踩过。
Android 提供了 getAltitude() 方法,但它的精度……嗯,怎么说呢,看运气。
| 定位方式 | 海拔精度 | 说明 |
|---|---|---|
| GPS | ±10~50米 | 受卫星几何分布影响大 |
| GPS + 气压计 | ±1~3米 | 需要设备硬件支持 |
| 网络定位 | 不可用 | 通常返回0或海平面高度 |
我曾经做一个登山App,用户反馈海拔数据忽高忽低,明明在爬坡,海拔却往下掉。后来发现是GPS信号反射导致的误差。解决方案是:对海拔数据做滑动平均滤波,取最近5~10个点的平均值。
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure) 方法,配合校准后的海平面气压,精度可以做到1米以内。
7.4 地磁场数据 —— 指南针的真相
地磁场数据主要用于电子罗盘和方位校正。Android 通过 Sensor.TYPE_MAGNETIC_FIELD 提供原始磁场数据,单位是微特斯拉(μT)。
但这里有个问题:地磁场很弱,很容易被干扰。手机扬声器、马达、甚至你手上的金属手表都会影响读数。
// 获取地磁场传感器
SensorManager sensorManager = getSystemService(SensorManager.class);
Sensor magneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
// 在 onSensorChanged 中获取数据
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
float x = event.values[0]; // 地磁场X分量
float y = event.values[1]; // 地磁场Y分量
float z = event.values[2]; // 地磁场Z分量
float total = (float) Math.sqrt(x*x + y*y + z*z);
// total 正常范围:25~65 μT(视纬度而定)
}
}
要得到真正的方位角,需要结合加速度传感器做融合计算。Android 提供了 SensorManager.getRotationMatrix() 和 SensorManager.getOrientation() 方法,帮你完成这个复杂计算。
filteredValue = alpha * rawValue + (1 - alpha) * filteredValue;
// alpha 通常取 0.1~0.3
7.5 知识体系总览
下面这张图总结了本章的核心知识点,帮你理清思路:
7.6 实战建议
最后,给大家几个我在实际项目中总结的经验:
- 精度过滤是第一步:拿到位置后,先判断 accuracy 是否满足需求,不满足就丢弃或提示用户
- 速度数据用现成的:别自己算,getSpeed() 已经做了平滑处理
- 海拔数据要滤波:滑动平均或卡尔曼滤波都能有效减少抖动
- 地磁场要融合:单独用磁场传感器做指南针,结果会让你崩溃
- 测试要覆盖不同场景:室内、室外、高楼间、山谷里,精度表现天差地别
好了,关于位置质量的内容就讲到这里。记住一句话:拿到位置只是开始,理解质量才是关键。下一章我们会聊如何在不同场景下选择合适的定位策略,到时候见。
公众号:蓝海资料掘金营,微信deep3321