手机如何定位:从时间差到地图上的“小蓝点”
手机如何定位:从时间差到地图上的“小蓝点”
这是一篇面向工程同学的“由浅入深”定位科普与落地指南。我们不从公式堆砌开始,而是从直觉与常识出发:卫星给出“时间”和“位置”,手机把“时间差”换成“距离”,再用几何把自己放在正确的位置上;最后,用IMU/GPS的动态融合,让轨迹更稳更顺。
1. 一分钟版结论(先知道全貌)
- 卫星在天上有“自己的位置”和“很准的时间”。手机接收到信号,知道“这条信号花了多长时间到我这里”。
- 距离=光速×时间差。每颗卫星就像一个“圆球”,以卫星为球心、距离为半径;手机在这个球面上。
- 三颗卫星给出三个球面,理论可交出一个点,但现实中还多了一个“手机时钟与卫星时钟的时间偏差”,所以通常需要四颗卫星:三颗定位置,一颗校时钟。
- 解算出来的位置/速度/时间叫 PVT。工程里常用两类工具:
- 加权最小二乘(WLS):一次性把“误差最小”的位置算出来,给更靠谱的观测更大的权重。
- 扩展卡尔曼(EKF):时间序列里,IMU负责“预测”,GPS负责“纠正”,不断迭代得到更稳的轨迹。
- 现代手机支持多星座(GPS/北斗/GLONASS/Galileo)和多频段,同步融合;你不用手动切换,系统在后台已经做了。
2. 时间怎么变成距离(最核心的一点)
- 卫星持续广播“导航电文”,里面有:卫星自己在某个时刻的空间位置、时钟信息、健康状态等。
- 手机接收时,能读到“这条电文是卫星在什么时间发的”;手机也知道“我是什么时间收到的”。
- 两者相减就是“飞行时间”。乘以光速(约 3×10^8 m/s),就是“到卫星的距离”。这就是“伪距”。
- 为什么叫“伪”距?因为里面混着各种误差:大气延迟、芯片时钟偏差、多路径反射……但这已经足够成为几何约束。
直觉小结:卫星给“自己的位置+很准的时间”,手机把时间差变成距离,每颗卫星都画出了一个以它为球心的球面,手机位置在这些球面的交点附近。
3. 为什么至少要四颗卫星(一个位置,四个未知)
- 要解的位置有三个维度:x、y、z。
- 还多一个“时钟偏差”未知量:手机的时间不可能和卫星完全一致,需要同时估计一个“δt”。
- 所以最少要四颗卫星,分别提供四条方程去约束这 4 个未知量。现实中我们会用更多卫星,让解更稳更准。
形象比喻:三颗卫星的三个球面理论上能交在一点,但当手机的时间不完全对齐时,这个“点”会漂。第四颗卫星就像“校表匠”,把这个时间偏差校正,让交点回到正确位置。
4. 误差从哪里来(知道噪声就知道该信谁)
- 几何条件:卫星全挤在一边,几何就不好(DOP 值变大),位置会放大误差。
- 大气与电磁环境:电离层/对流层延迟,城市峡谷的反射(多路径),会让“伪距”偏大或抖动。
- 接收机与运动状态:天线遮挡、低速时的航向不稳定、芯片时钟误差。
- 坐标与时间基线:WGS-84 与国内互联网地图(GCJ-02/BD-09)的差异;系统时钟和单调时钟混用会让轨迹断裂。
地图坐标差异(为什么你的点会“偏”)
- 一句话版:同一个物理位置,用不同“尺子”(坐标系)量,数值就不同;把 WGS-84 的点直接画在 GCJ-02/BD-09 地图上,肉眼会看到偏移。
- 三种常见坐标系:
WGS-84:GNSS 原始大地坐标,全球通用。GCJ-02(俗称“火星坐标”):国内互联网地图普遍使用的加偏坐标系(法律要求)。BD-09:百度在 GCJ-02 的基础上再次加偏处理。
- 为什么会偏:底图是 GCJ-02,你的点是 WGS-84,两把尺子不一样;直接叠加就会“错位”。
- 工程如何处理:
- 统一基准:解算与融合阶段用同一种坐标系(推荐全流程用 WGS-84 或统一 ENU 局部坐标),最后渲染到国内地图时做一次转换到 GCJ-02(或 BD-09)。
- 别“双重转换”:同一个点只转换一次;
WGS-84 → GCJ-02 → BD-09是两次转换,且只在确实要上百度底图时才做。 - SDK 返回值:多数国内定位 SDK(如高德)经纬度字段通常为 GCJ-02,在高德地图上直接画不会偏;若融合计算需要 WGS-84,要先把 GCJ-02 逆变换回 WGS-84 再参与计算(注意逆变换是近似的)。
- 常见坑:
- 混用坐标:用 GCJ-02 点计算轨迹,再和 WGS-84 的 IMU/ECEF 结合,轨迹会“扭”。
- UI 偏移:拿 WGS-84 直接画在 GCJ-02 底图上,城市中会肉眼偏移几十到数百米。
- 双重转换:同一个点被转两次,偏移翻倍。
- 快速自检清单:
- 你的底图是什么坐标系?(高德/腾讯→GCJ-02;百度→BD-09;OpenStreetMap/自绘底图→通常 WGS-84)
- 你的点的来源是什么坐标系?(AMapLocation 多数是 GCJ-02;外接 GNSS 接收机常给 WGS-84)
- 你在“计算 vs 展示”是否只做了一次、在“最后一步”做的坐标转换?
- 实战建议(与项目对应):
- 融合计算环节(
EKF2DHelper)尽量在统一坐标系下进行(建议 WGS-84 或统一 ENU),避免在计算途中做坐标系转换。 - UI 展示(
PerformanceTestActivity/ViewModel)在准备点位时,根据底图类型做一次坐标转换再渲染。 - 数据存储时标注坐标系元数据,避免后续混用。
- 融合计算环节(
- 直觉比喻:一把是“米尺”,一把是“英尺”。先用同一把尺子算清楚,再换成底图需要的尺子去画。
工程直觉:谁更靠谱,就给谁更大权重(WLS);时间上谁更稳定,就让它主导预测(EKF 的 IMU),再用 GPS 把方向拉回来。
5. 从观测到位置:一步步怎么做(不靠公式堆砌)
- 选择一个初始猜测位置(比如上一时刻的位置)。
- 用星历计算每颗可见卫星的空间位置(手机系统/SDK已做好)。
- 计算“几何距离”(猜测位置到每颗卫星的直线距离),与“观测伪距”做差,得到每颗卫星的“残差”。
- 这些残差就告诉我们“猜的位置不完美,往哪个方向、走多远能更好”。
- 根据每颗卫星的质量指标(比如信噪比 C/N0、仰角、SDK 的精度)给权重,越靠谱权重越大。
- 迭代更新:一次次把位置和“时钟偏差”往更好的方向推进,直到残差够小为止。
这套流程的名字叫“加权最小二乘(WLS)”,但你只要记住一句话:让所有卫星的误差总和尽可能小,可靠的卫星多说话, 不可靠的少说话。
6. 两种常用工具:WLS 与 EKF(通俗版)
6.1 WLS(一次性把误差压到最小)
- 本质:把“观测伪距 vs 几何距离”的误差统统拉到一起,给每条误差一个权重,求一个位置+时钟偏差,使加权误差最小。
- 为什么好用:不需要关心时间序列,只要这一刻的卫星足够多、质量够好,就能给出一个不错的位置。
- 用在什么时候:首次定位、短时段几何解算、给后续 EKF 一个好初值。
6.2 EKF(时间上的“推—拉”)
- 本质:把状态看成一辆“受力的推车”。
- IMU 像“发动机”,根据加速度/角速度在时间上“推”位置和速度(预测步)。
- GPS 像“拉回绳”,把预测的状态拉回更真实(更新步)。
- 两个旋钮:
Q(过程噪声):越大,越相信“状态在变”,预测更活跃,抗遮挡好但易抖。R(观测噪声):越大,越不信 GPS,更新幅度小,轨迹更平滑但可能偏离真实。
- 用在什么时候:需要连续时间的轨迹(车、人),低速航向不稳、短时遮挡的场景更鲁棒。
一句话:WLS管“这一刻的最小误差”,EKF管“时间上的稳定与纠偏”。工程里常是“WLS给初值,EKF持续融合”。
7. 手机上到底拿到什么字段(以及怎么用)
- 常见字段(来自 Android/高德等):
latitude/longitude、accuracy、speed、bearing、altitude、elapsedRealtimeNanos。 - 速度分解(把标量速度分成东西/南北):
val speed = amapLocation.speed // m/s
val bearingRad = Math.toRadians(amapLocation.bearing.toDouble())
val vx = speed * Math.sin(bearingRad) // 东向分量
val vy = speed * Math.cos(bearingRad) // 北向分量
- 小范围位移近似(经纬度到平面):
- 解释(通俗版):把两点的经纬度差换成“平面上的东西向/南北向位移(米)”。直觉是“地球半径×角度差≈弧长”;东西向每一度在不同纬度对应的米数不同,需要乘一个
cos(纬度)的缩放。 - 逐行含义:
R是地球平均半径(米),用来把角度换成长度(弧长)。dLat/dLon是纬度/经度的差,先用度数相减再转成弧度;三角函数都用弧度。meanLat是两点的平均纬度,用来修正经度方向的米数缩放(越往高纬,同样的经度差实际距离越短,因子是cos(纬度))。dx是东西向位移,近似公式:dx ≈ R × cos(meanLat) × dLon。dy是北向位移,近似公式:dy ≈ R × dLat。
- 使用范围与注意:
- 适合近距离(如几百米到几公里)近似;更远或更高精度建议用
WGS‑84 → ECEF → ENU转换。 - 忘了“度→弧度”,结果会差几个数量级。
- 统一坐标系后再计算位移(例如先把
GCJ‑02转到WGS‑84),否则轨迹会“扭”。
- 适合近距离(如几百米到几公里)近似;更远或更高精度建议用
val R = 6371000.0
val dLat = Math.toRadians(lat2 - lat1)
val dLon = Math.toRadians(lon2 - lon1)
val meanLat = Math.toRadians((lat1 + lat2) / 2.0)
val dx = dLon * Math.cos(meanLat) * R
val dy = dLat * R
- 时间基线统一:
val tMs: Long = amapLocation.elapsedRealtimeNanos / 1_000_000L
工程提示:低速时 bearing 不稳定,建议做门限或冻结;所有度数都要先转弧度再进三角函数。
8. 项目实战(数据流与代码位置)
- 数据来源与分发:
app/src/main/java/com/terminal/publictest/performance/model/PerformanceRepository.ktonGetGPSData(...):把AMapLocation分发到存储与融合(testingDataDealer/dataFusionHelper)。- 头部 import:
BleGpsLocationInfoProvider(支持外接高精度接收机)。
- 融合与输出:
app/src/main/java/com/dcd/dematrix/terminal/publictest/performance/helper/EKFilter.ktclass EKF2DHelper:IMU/GPS 融合控制、预测协程、fusionDataLiveData输出。onGetGpsData(...):未融合时直接预览,融合就绪时触发 GPS 更新。beginFusion():检查静态零偏与 RBV 校准、首帧 GPS。getGyroRateInCarZ()/getAccVector():IMU 均值与坐标变换。
- UI 消费:
PerformanceTestActivity.kt/PerformanceTestViewModel.kt:订阅融合数据,更新速度表、载荷圆、轨迹视图。
实战模式:未校准→GPS-only 预览;完成静态零偏与 RBV → 开启 EKF,IMU 预测、GPS 更新统一推送轨迹。
9. 常见坑与快速排查
- 航向错位:忘了“度→弧度”。
- 时间线抖动或断裂:系统时钟 vs 单调时钟混用,统一用
elapsedRealtime。 - 双重推送导致 UI 闪动:预测与更新重复发事件,统一交由预测协程推送。
- 精度异常与跳点:对
accuracy/速度设门限,低质量观测减权或剔除。
10. 进阶与演进建议
- 多星座/多频:GPS/北斗/GLONASS/Galileo 同收同算,双频能显著减小电离层误差。
- RTK 与差分:在有基站/网络差分的场景,伪距精度显著提升,轨迹更稳更准。
- 传感器增强:车轮速/CAN、地图匹配(MM)、视觉里程计(VO)与 GNSS/INS 组合,稳定性提升明显。
- 参数外置:把
Q/R、静止阈值、滤波参数放入配置(SP/JSON),按设备与赛道调参。 - 质量监控:打点航向漂移、加速度基线、RBV 矩阵、GPS/IMU 时间对齐,异常自动降级到 GPS-only。
附录 A:GNSS 星座中英文速查
- GPS(美国)
- GLONASS / 格洛纳斯(俄罗斯)
- Galileo / 伽利略(欧盟)
- 北斗(中国)
提示:手机大多数支持多星座并融合,用户无需手动切换;不少机型支持 L1/E1/B1 与 L5/E5a/B2a 等双频。
附录 B:坐标与展示
- 解算常用 WGS-84;国内互联网地图多用 GCJ-02(高德/腾讯)或 BD-09(百度)。
- 展示与存储时,注意坐标系转换和单位一致性。
总结(把话说短)
卫星给了“位置+很准的时间”,手机用“时间差→距离”搭建几何约束,四个未知量(x/y/z/δt)用四颗以上卫星求解出 PVT;这一刻用 WLS 把误差压到最小,时间上用 EKF 让轨迹更稳更顺。工程落地的关键不在公式多少,而在“坐标系正确、时间统一、权重合理、推送一致”。
