SLAM 运动跟踪工作原理
SLAM 是什么
SLAM(Simultaneous Localization and Mapping,同时定位与地图构建)是 ARCore 所有能力的基石。它要解决的核心问题看似简单,但技术难度极高:
手机在未知空间中移动时,如何一边理解"我在哪"(定位),一边理解"周围有什么"(建图),两件事同时进行。
如果手机只做定位,它需要一张已知的地图才能知道自己在地图的什么位置;如果只做建图,它需要知道自己在空间中的移动轨迹才能把不同视角看到的画面拼起来。SLAM 就是把这两个依赖解耦,让它们在没有任何先验知识的情况下同时求解。
ARCore 的 SLAM 三件套
视觉侧:特征点提取
ARCore 在每一帧摄像头画面中寻找特征点——那些视觉上独特的局部区域,比如:
- 木纹的纹理变化
- 地砖的缝隙
- 海报的图案
- 物体边缘的角点
特征点有几个要求:可重复检测(换个角度看还是特征点)、具有区分度(和周围的特征点长得不一样)、足够密集(每帧通常检测数千个)。
ARCore 提取的是Oriented FAST(FAST 角点的改进版),然后用 BRIEF 描述子编码。BRIEF 是一种轻量级的二进制描述子,匹配速度非常快,适合实时场景。
惯性侧:IMU 融合
手机内置的 IMU(Inertial Measurement Unit) 包含:
- 加速度计:输出三轴加速度(包含重力分量)
- 陀螺仪:输出三轴角速度(设备的旋转速度)
IMU 的优势是采样率高——通常 100Hz ~ 500Hz,比摄像头的 30fps 高得多。在摄像头的两帧之间,IMU 可以提供更细致的姿态变化数据,避免了视觉 SLAM 在快速运动时的模糊和丢失。
融合算法使用扩展卡尔曼滤波(EKF)或因子图优化来同时估计:
- 当前设备的姿态(位置 + 朝向)
- 所有已观测特征点在 3D 空间中的位置
- IMU 的偏差(陀螺仪和加速度计都有固定偏差需要估计)
地图维护:关键帧管理
ARCore 不会把每一帧画面都存进地图——太费内存。它用关键帧(Keyframe)机制:
- 当设备移动足够远或旋转足够多时,当前帧被标记为关键帧
- 每个关键帧记录:当时的设备姿态、该帧检测到的特征点、特征点的 BRIEF 描述子
- 新的帧和历史关键帧做特征点匹配,计算新帧相对于关键帧的姿态变化
- 当历史关键帧的特征点与当前帧高度重叠时,ARCore 会回环检测(Loop Closure)——它发现自己回到了之前来过的地方,会修正整个地图的累积误差
姿态(Pose)的表达方式
ARCore 中的姿态是一个 Pose 对象,用平移向量(Vector3)+ 四元数(Quaternion)表示:
val pose: Pose = frame.camera.pose
val translation = pose.translation // 设备位置(x, y, z)
val rotation = pose.rotation // 设备朝向(四元数)
坐标系约定:
- 世界坐标系:ARCore 启动时确定的初始坐标系,后续所有姿态都相对于它
- 相机坐标系:摄像头的本地坐标系,通常 z 轴指向前方
- 右手坐标系:+x 向右,+y 向上,+z 指向设备前方
跟踪质量的三个阶段
ARCore 会持续评估当前的跟踪状态,通过 TrackingState 暴露:
| 状态 | 含义 | 用户体验 |
|---|---|---|
| TRACKING | 正常跟踪 | 虚拟物体稳定在原位 |
| PAUSED | 暂时丢失但正在恢复 | 虚拟物体可能短暂漂移 |
| STOPPED | 彻底丢失需要重新初始化 | 虚拟物体消失,需要用户扫一眼周围环境 |
开发者通常在 STOPPED 状态时重置 Session 或引导用户缓慢扫过房间。
影响 SLAM 效果的环境因素
| 因素 | 好 | 坏 | 原因 |
|---|---|---|---|
| 纹理丰富度 | 有图案的墙壁、木纹地板 | 纯白墙面、光滑金属桌面 | 特征点密度 |
| 光照 | 明亮均匀 | 黑暗、强逆光、频闪灯光 | 摄像头信噪比 |
| 运动速度 | 缓慢平稳 | 快速挥舞 | 图像模糊导致特征点丢失 |
| 视野范围 | 看到丰富的远景和近景 | 只盯着一个近距离物体 | 视差不足,深度估计困难 |
| 动态物体 | 静止环境 | 人来回走动、电视播放 | 静态世界假设被破坏 |
给用户的引导(ARCore.ARCoreSceneViewer 或自定义)应该鼓励他们在开始 AR 之前先缓慢扫过周围环境。