想象你终于拿到了人生中第一套房子的钥匙。推开门,客厅里空荡荡的,夕阳把白墙染成暖黄色,深色木地板上留着前一位住户的凹痕。你想把新买的月光白沙发、圆角茶几和落地灯都摆进去,但你不会闭着眼睛把家具往地上一扔。你会先绕着客厅走一圈:记住挂画的位置、地板倾斜的方向、阳光从哪扇窗照进来。记住这些之后,你才敢把沙发放在那个“刚刚好”的位置,并蹲下来在地上画一个粉笔圈:就是这里。 ARCore 和 ARKit 做的就是同一件事。它们让你的手机学会“看”这个世界,像一位搬新家的人:先弄清自己站在哪、朝向哪(运动追踪),再摸清地面和墙面在哪里(环境理解),最后感知灯光是亮是暗、是暖是冷(光照估计)。三件事配合,虚拟的沙发、桌子、恐龙模型才能稳稳地“走”进你的客厅。这篇文章会把这三大支柱背后的核心原理一层层拆开,从运动追踪到平面检测,从锚点系统到性能优化,全部用“搬新家”的故事讲明白。

岗位关联

学会 ARCore/ARKit 的核心原理,你就能透过手机屏幕理解三维世界的计算本质,在 AR 应用、空间计算和沉浸式交互项目里做出扎实的工程决策。

目录
  1. 可视化演示
  2. 空间计算基石
  3. 运动追踪原理
  4. 特征点匹配
  5. 平面检测算法
  6. 光照估计技术
  7. 锚点坐标系统
  8. 性能优化策略
  9. 工程实践调试
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

空间计算基石

搬家前,先得了解新家整体格局。你不会一进门就往地上放沙发,而是先看看客厅有多大、墙在哪、地面什么材质。ARCore 和 ARKit 也一样,它们不直接渲染模型,而是先搭建一个“世界认知框架”。这个框架的核心,就是让手机摄像头的画面与真实世界的三维空间建立对应关系——相当于你把客厅的每一寸土地都“登记”进大脑里的户型图。

从工程角度看,ARCore 是 Google 为 Android 打造的 AR SDK,ARKit 是 Apple 为 iOS 提供的对应方案。两者名字不同、接口不同,但追求的三大支柱惊人一致:运动追踪(Motion Tracking)、环境理解(Environment Understanding)、光照估计(Light Estimation)。运动追踪回答“我在哪、朝哪看”,环境理解回答“哪里能放东西”,光照估计回答“光从哪来、是什么颜色”。就像搬新家的三件套:认清自己的位置、找到平坦地面、感受灯光氛围。

三大支柱要想协作,必须先解决坐标映射问题。虚拟物体是三维世界里的一个点,但手机屏幕只能显示二维像素,中间要经过一套“世界坐标 → 相机坐标 → 像素坐标”的流水线。这正是图形学里最核心的相机成像模型。

回到搬家的比喻:你先把客厅画成一张坐标图,标出墙角、窗户、地板缝的位置。之后每一件虚拟家具,都要先经过这张坐标图的“翻译”,才能准确地出现在手机屏幕的正中央。

[uv1]=1ZK(Rpw+t)\displaystyle\begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = \frac{1}{Z} K \left( R \mathbf{p}_w + \mathbf{t} \right)
这个公式把“客厅里的某一点”翻译成“屏幕上的某个像素位置”。K 是相机内参(焦距、光心),R 和 t 是相机外参(手机朝向和位置),Z 是点到相机的深度。
  1. 世界坐标点\mathbf{p}_w = (X, Y, Z)^T 是虚拟物体在真实世界三维坐标系中的位置,比如“沙发脚在地板上那个点”。
    pw=(X,Y,Z)T\displaystyle \mathbf{p}_w = (X, Y, Z)^T
  2. 外参变换R \mathbf{p}_w + \mathbf{t} 用旋转矩阵 R 和平移向量 t 把世界坐标转成以相机为中心看的坐标,相当于你转头时,眼前的客厅也跟着转。
    pcam=Rpw+t\displaystyle \mathbf{p}_{cam} = R \mathbf{p}_w + \mathbf{t}
  3. 内参投影K 是 3×3 内参矩阵,包含焦距和主点,把相机坐标系里的点映射到成像平面。
    K=[fx0cx0fycy001]\displaystyle K = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix}
  4. 深度归一化\frac{1}{Z} 把齐次坐标变为像素坐标,确保远处的物体看起来更小,符合人眼透视规律。
    [u,v,1]T=1ZKpcam\displaystyle [u, v, 1]^T = \frac{1}{Z} K \mathbf{p}_{cam}

运动追踪原理

现在你已经拿到了客厅的户型图,但你还不知道自己在房间的哪个位置。想象你在黑暗的客厅里走动,你的内耳前庭告诉你“我转了 30 度、走了两步”,但走久了难免偏差;这时你睁开眼,看到墙上的挂画从侧面的角度变成正面,于是你立刻修正:“我其实站在挂画的正前方”。运动追踪就是这位“黑暗中带着内耳前庭走路的搬家者”。

ARCore 和 ARKit 的运动追踪技术叫视觉惯性里程计(VIO),核心是融合两种传感器:摄像头提供的连续图像,以及 IMU(惯性测量单元)提供的加速度和角速度。摄像头是“眼睛”,负责看地标;IMU 是“内耳前庭”,负责感受自身运动。两者互补:IMU 响应快但会漂移,图像稳定但依赖环境纹理,融合后就能实时估算手机的六自由度姿态(位置和朝向)。

VIO 内部会用一个运动模型不断预测下一个时刻的姿态。经典的位置递推方式是这样的:

走几步就睁开眼看看地标,正是 VIO 的聪明之处:IMU 负责短期预测,视觉负责长期校正。但如果你把客厅灯关掉、墙上没有画,或者手机甩动太快,VIO 就会“迷路”,位置预测开始漂移——这就像在完全黑暗的房间里走路,你越走越不确定自己到底在哪。理解了漂移的来源,也就理解了为什么 AR 应用需要引导用户慢慢移动、保持环境有足够纹理。

pt+1=pt+vtΔt+12atΔt2\displaystyle\mathbf{p}_{t+1} = \mathbf{p}_t + \mathbf{v}_t \Delta t + \frac{1}{2} \mathbf{a}_t \Delta t^2
这就像你在客厅里闭着眼走了一步:从当前位置出发,按当前速度走一段,再加一点点加速度带来的调整,就能估算出新位置。
  1. 当前时刻位置\mathbf{p}_t 是设备在当前时刻的世界坐标位置,也是递推的起点。
    pt\displaystyle \mathbf{p}_t
  2. 速度贡献\mathbf{v}_t \Delta t 表示以当前速度持续运动一段极短时间 \Delta t 后产生的位移。
    vtΔt\displaystyle \mathbf{v}_t \Delta t
  3. 加速度修正\frac{1}{2} \mathbf{a}_t \Delta t^2 表示加速度带来的额外位移,用于捕捉手部摆动、走动等变速运动。
    12atΔt2\displaystyle \frac{1}{2} \mathbf{a}_t \Delta t^2
  4. 位置更新\mathbf{p}_{t+1} 是预测出的下一时刻位置,IMU 的测量值会不断修正模型中的 \mathbf{v}_t 和 \mathbf{a}_t。
    pt+1=pt+vtΔt+12atΔt2\displaystyle \mathbf{p}_{t+1} = \mathbf{p}_t + \mathbf{v}_t \Delta t + \frac{1}{2} \mathbf{a}_t \Delta t^2

特征点匹配

在黑暗客厅里走路时,你靠什么重新定位?靠的是那些独特的“视觉地标”:墙角的海报、电视柜上银色的花瓶、窗帘上的条纹。这些地标无论你从哪个角度、多远看,都能一眼认出来。ARCore 和 ARKit 也要在环境里寻找这种稳定的“认路标”,它们被称为特征点。

特征点并不是任意的像素,而是图像中角点、纹理块等有明显区分度的位置。主流算法 ORB 做了两件事:先用 FAST 角点检测找出“哪里像角”,再用 BRIEF 描述子为每个角点生成一串二进制编码,描述它周围的样子。两帧图像之间要匹配同一个物理点,就是比较两串二进制的相似度。

二进制描述子的距离有一个专有名词:汉明距离,即两个等长二进制串对应位不同的个数。

当同一特征点被连续多帧稳定匹配后,三角测量会算出它在三维空间中的位置,大量这样的点汇集成稀疏点云,构成 AR 系统心中的“客厅地图”。搬家者每走到一个角落,都会发现“这张海报我以前见过”——正是这些熟悉的地标,让他确认自己真的来过这里。

\displaystyleD = \sum_{k=1}^{n} (a_k \oplus b_k)
描述子每一位都是 0 或 1,\oplus 是异或运算。如果两个特征点在同一位上不同,就加一分。总分数越低,说明两个描述子越相似。
  1. 二进制描述子a_k 和 b_k 分别表示两个特征点描述子的第 k 位比特,每个描述子是一个 0/1 串。
    ak,bk∈{0,1}\displaystyle a_k, b_k \in \{0, 1\}
  2. 异或比较a_k \oplus b_k 表示第 k 位是否不同,相同为 0、不同为 1。
    ak⊕bk={1,ak≠bk0,ak=bk\displaystyle a_k \oplus b_k = \begin{cases} 1, & a_k \ne b_k \\ 0, & a_k = b_k \end{cases}
  3. 逐位累加\sum_{k=1}^{n} 把 n 位中所有“不同”的结果加起来,得到总距离。
    D=∑k=1n(ak⊕bk)\displaystyle D = \sum_{k=1}^{n} (a_k \oplus b_k)
  4. 阈值判定D 越小意味着两个描述子在很多位上一致。当 D 小于某个阈值时,就认为这两点是同一个物理点在两帧里的投影。
    D<T⇒匹配\displaystyle D < T \Rightarrow \text{匹配}

平面检测算法

地标标记好了,地图也有了,接下来才是搬家的关键动作:找一块能放家具的平地。你蹲下来,用手掌平贴地板,前后挪动感受它是否平整——如果地板上只有一小块鼓包,沙发放上去就会晃动。AR 系统同样需要找到“平整区域”,这个步骤叫平面检测。

ARCore 和 ARKit 会在点云中寻找“大量离散点恰好落在同一个平面上”的区域。数学上,一个平面可以用它的法向量和一个参考点描述:

但真实客厅里点云布满噪声,还有茶几、地毯等非平面物体。AR 系统采用 RANSAC 算法来“试探”平面:随机取三个点确定一个候选平面,统计有多少其他点支持这个平面,如此反复迭代,最终留下支持点最多的平面作为检测结果。就像搬家时你会用脚踩好几块地砖,确认哪一块区域最平整、面积最大。检测到的平面还会带着置信度值,置信度越高,系统才越愿意把虚拟物体放上去。

n⋅(x−x0)=0\displaystyle\boldsymbol{n} \cdot (\mathbf{x} - \mathbf{x}_0) = 0
这个方程说:从平面上任意参考点 \mathbf{x}_0 出发,指向平面上任意一点 \mathbf{x} 的向量,都垂直于平面的法向量 \boldsymbol{n}。点积为 0,就意味着 \mathbf{x} 也在同一个平面上。
  1. 法向量\boldsymbol{n} = (n_x, n_y, n_z)^T 是垂直于平面的单位向量,决定了平面的朝向,类比地板“朝上”的方向。
    n=(nx,ny,nz)T,∥n∥=1\displaystyle \boldsymbol{n} = (n_x, n_y, n_z)^T, \quad \|\boldsymbol{n}\| = 1
  2. 参考点\mathbf{x}_0 是平面上的一个已知点,用来定位平面位置,就像你标记出的第一块地砖角。
    x0\displaystyle \mathbf{x}_0
  3. 任意点\mathbf{x} 是待判断是否在平面上的点云中的点。
    x=(x,y,z)T\displaystyle \mathbf{x} = (x, y, z)^T
  4. 垂直条件\boldsymbol{n} \cdot (\mathbf{x} - \mathbf{x}_0) = 0 意味着平面上所有点与参考点连线都垂直于法向量。实际中带噪声,所以小于一个容差就认为属于平面。
    ∣n⋅(x−x0)∣<ε\displaystyle |\boldsymbol{n} \cdot (\mathbf{x} - \mathbf{x}_0)| < \varepsilon

光照估计技术

家具选好了、地面也找到了,但还差最后一道工序——让虚拟家具看起来真的属于这个房间。想象你在傍晚的客厅里放一盏虚拟落地灯,如果灯光还是正午的冷白色,一眼就会穿帮。光照估计就是让 AR 系统“感受客厅灯光氛围”的能力。

ARCore 和 ARKit 会从摄像头画面中采样多个区域,估算环境的平均光亮强度、色温,甚至生成一张环境光的反射探针图。这些数据被传给渲染引擎后,虚拟模型会使用同样的亮度、阴影方向和颜色温度来着色。为了让光照模型足够精细又足够高效,移动 AR 通常使用球谐光照(Spherical Harmonics)来近似环境光。

球谐函数是一种定义在球面上的正交基函数,任意环境光分布都可以展开成这些基函数的加权和。在移动端,取前两到三阶就足够好了:

把光照估计想成“感受房间氛围”:夕阳西下时,客厅从暖橙变成暗蓝,虚拟沙发也会同步变换反光和阴影。估算得越准确,虚拟与真实的边界就越模糊——这正是沉浸感的来源。

\displaystyleL(\boldsymbol{n}) \approx \sum_{l=0}^{2} \sum_{m=-l}^{l} c_{lm} Y_{lm}(\boldsymbol{n})
这相当于用一组“基础光模式”组合出复杂的真实光照。Y_{lm} 是球谐基函数,c_{lm} 是权重系数,AR 系统实时从环境图像中估计这些系数。
  1. 阶数l 取值范围 0 到 2,代表光照近似的精细程度。l=0 是平均亮度,l=1 加入方向性,l=2 补充更复杂的细节。
    l=0,1,2\displaystyle l = 0, 1, 2
  2. 频率索引m 从 -l 取到 l,同一阶下不同 m 对应球面上的不同方向模式。
    m=−l,…,l\displaystyle m = -l, \ldots, l
  3. 球谐系数c_{lm} 是环境光照在第 lm 个基函数上的投影权重,由环境探针采样计算得到。
    clm=∫L(n)Ylm(n) dn\displaystyle c_{lm} = \int L(\boldsymbol{n}) Y_{lm}(\boldsymbol{n}) \, d\boldsymbol{n}
  4. 重构光照Y_{lm}(\boldsymbol{n}) 是球谐基函数在法线方向 \boldsymbol{n} 上的取值,所有项加权求和后,得到任意方向的入射光强度。
    L(n)≈∑l=02∑m=−llclmYlm(n)\displaystyle L(\boldsymbol{n}) \approx \sum_{l=0}^{2} \sum_{m=-l}^{l} c_{lm} Y_{lm}(\boldsymbol{n})

锚点坐标系统

沙发上终于落到地板中央了。但搬家并没有结束——如果你放下手机再举起来,沙发还会在原来那里吗?为了确保虚拟物体不会“跑走”,AR 系统在放置物体的那一刻,会在世界坐标系的对应位置钉下一颗“钉子”,这个钉子叫做锚点(Anchor)。

锚点记录了放置位置在世界坐标系中的精确位姿(位置+朝向)。此后每一帧渲染,虚拟物体都要从本地模型坐标变换到锚点坐标系,再变换到世界坐标系。这套层层变换的数学工具,就是齐次变换矩阵:

搬家的比喻在这里格外贴切:你放在地上的粉笔圈就是锚点,你离开客厅再回来,粉笔圈还在,于是沙发也还在。更进一步,ARCore 的 Cloud Anchors 让你可以把锚点上传到云端,其他用户走进同一间客厅,也能在同一个物理位置看到同一个虚拟沙发——就像搬家公司为每户人家制作的“共享户型图”,大家看到的是同一套房子的同一份标记。

T=[Rt0T1]\displaystyle\mathbf{T} = \begin{bmatrix} \mathbf{R} & \mathbf{t} \\ \mathbf{0}^T & 1 \end{bmatrix}
一个 4×4 矩阵把旋转和平移打包在一起。用齐次坐标后,多次坐标变换可以连续相乘,非常方便。
  1. 旋转矩阵\mathbf{R} 是 3×3 的旋转矩阵,描述锚点坐标系相对于世界坐标系的朝向。
    R∈R3×3,RTR=I\displaystyle \mathbf{R} \in \mathbb{R}^{3 \times 3}, \quad \mathbf{R}^T \mathbf{R} = \mathbf{I}
  2. 平移向量\mathbf{t} 是 3×1 的平移向量,描述锚点在空间中的位置。
    t∈R3\displaystyle \mathbf{t} \in \mathbb{R}^{3}
  3. 齐次行\mathbf{0}^T 和 1 让矩阵变成 4×4 齐次形式,从而能够连续相乘和求逆。
    T=[Rt0001]\displaystyle \mathbf{T} = \begin{bmatrix} \mathbf{R} & \mathbf{t} \\ 0 & 0 & 0 & 1 \end{bmatrix}
  4. 坐标变换\mathbf{p}_{world} = \mathbf{T} \, \mathbf{p}_{local} 把虚拟物体的本地坐标映射到世界坐标,锚点在这个变换中充当“基准点”。
    pworld=T plocal\displaystyle \mathbf{p}_{world} = \mathbf{T} \, \mathbf{p}_{local}

性能优化策略

所有家具都摆好了,但搬家还没结束。你需要在客厅里来回走动,看看沙发是否真的稳、茶几会不会挡住路、灯光是否协调。同样,AR 应用上线前必须持续监测“追踪健康度”,否则虚拟物体会在屏幕上晃动、漂移甚至消失。

衡量追踪健康度最常用的指标是重投影误差。它统计每个特征点的预测投影位置与实际观测位置的偏差:

ARCore 和 ARKit 会把追踪状态分成 TRACKING、LIMITED、STOPPED 三档,对应“定位稳定”“勉强定位”“完全丢失”。工程师在真机上测试时会特别注意:环境纹理是否充足、光线是否均匀、移动是否太快。优化手段还包括降低相机分辨率、控制特征点数量、减少不必要的重绘来省电。这些策略就像搬家后的“压力测试”——客厅里摆的每一件家具,都要经得起走来走去的检验。

ϵ=1N∑i=1N∥u^i−ui∥2\displaystyle\epsilon = \frac{1}{N} \sum_{i=1}^{N} \left\| \hat{\mathbf{u}}_i - \mathbf{u}_i \right\|^2
偏差越大,说明当前估算的设备位姿越不可靠。这就像你走路时,总觉得墙上的海报位置和记忆中的对不上,说明你在客厅里已经彻底转向了。
  1. 预测投影\hat{\mathbf{u}}_i 是根据当前估算的相机位姿和特征点三维位置,重新投影回图像平面得到的像素坐标。
    u^i=proj(Pi,R,t)\displaystyle \hat{\mathbf{u}}_i = \text{proj}(\mathbf{P}_i, \mathbf{R}, \mathbf{t})
  2. 观测位置\mathbf{u}_i 是特征匹配算法在图像中实际检测到的像素坐标。
    ui\displaystyle \mathbf{u}_i
  3. 误差平方\left\| \hat{\mathbf{u}}_i - \mathbf{u}_i \right\|^2 计算预测与观测的欧氏距离平方,大于 0 就说明存在偏差。
    ∥u^i−ui∥2\displaystyle \left\| \hat{\mathbf{u}}_i - \mathbf{u}_i \right\|^2
  4. 取平均\frac{1}{N} \sum_{i=1}^{N} 对所有特征点求平均。值越小,位姿估计越精确;值变大时要降低追踪等级或提醒用户。
    ϵ=1N∑i=1N∥u^i−ui∥2\displaystyle \epsilon = \frac{1}{N} \sum_{i=1}^{N} \left\| \hat{\mathbf{u}}_i - \mathbf{u}_i \right\|^2

工程实践调试

房子终于住进去了,但生活总会暴露新问题:沙发好像歪了一点、落地灯在阳光下有点刺眼、墙角那条裂缝需要修补。AR 应用开发也是如此,核心原理懂了,真正落地时还是会有各种“住户反馈”要处理。

开发者首先要配置好 AR 会话(ARSession/ARScene):申请相机权限、检查设备兼容性、设置平面检测模式、开启光照估计。实际调试时,最常见的问题包括:初始化追踪太慢、水平面迟迟检测不到、锚点位置缓慢漂移、虚拟物体在强光下显得突兀。这些问题大多可以追溯到三个源头:环境纹理不足、IMU 噪声大、相机标定参数不准。针对每个问题,都有对应的诊断方法和优化技巧。

设备朝向在底层用四元数表示,四元数比欧拉角更稳定,但也要求模长始终为 1:

从“看房”到“入住”,AR 应用的工程实践更像是一场持续不断的生活磨合。每次在真机上走动、旋转、放置物体,都是在检验这套空间计算系统是否真的理解了你身处的客厅。记住核心原理,遇到问题时,你就能准确判断是追踪、平面、光照还是锚点的责任——然后对症下药。

q=(w,x,y,z),∥q∥=w2+x2+y2+z2=1\displaystyle\mathbf{q} = (w, x, y, z), \quad \|\mathbf{q}\| = \sqrt{w^2 + x^2 + y^2 + z^2} = 1
四元数把旋转表示为“绕某个轴转某个角度”,用 4 个数避免欧拉角的万向锁问题。工程中若发现姿态抖动,先检查四元数是否因为数值误差偏离了单位模长。
  1. 标量部分w 是四元数的标量部分,与旋转角度相关。
    w=cos⁡(θ/2)\displaystyle w = \cos(\theta / 2)
  2. 向量部分(x, y, z) 是三维向量部分,表示旋转轴的方向。
    (x,y,z)=sin⁡(θ/2) n\displaystyle (x, y, z) = \sin(\theta / 2) \, \boldsymbol{n}
  3. 模长约束\|\mathbf{q}\| = 1 是旋转四元数的归一化条件,运算后需要重新归一化防止漂移。
    w2+x2+y2+z2=1\displaystyle w^2 + x^2 + y^2 + z^2 = 1
  4. 姿态更新当传感器数据到达时,用 \mathbf{q}_{t+1} = \mathbf{q}_t \otimes \Delta \mathbf{q} 更新朝向,工程上保证每一步都归一化。
    qt+1=qt⊗Δq\displaystyle \mathbf{q}_{t+1} = \mathbf{q}_t \otimes \Delta \mathbf{q}

AI 应用场景

家居 Showroom AR 试摆

阶段:空间计算与交互设计

解决的问题:传统电商只能看 2D 渲染图,用户无法判断一张 1.8 米的双人沙发放进自家客厅会不会太挤、下午的阳光会不会让布面泛黄。AR 用平面检测、锚点与光照估计,把 1:1 虚拟家具“搬进”真实客厅。

具体案例:打开宜家 Place App,摄像头扫描地板后自动识别地面平面,把沙发模型投放到画面中。用户在房间里走动,沙发稳稳定在锚点上;天色变暗,沙发反光同步变冷,尺寸始终准确。

工厂设备巡检 AR 指导

阶段:运动追踪与锚点持久化

解决的问题:纸质巡检手册要求工人对着编号找阀门,新手常看错位置。通过 Cloud Anchors,专家预先在设备旁放置虚拟箭头和说明,现场人员戴上 AR 眼镜即可看到叠加在真实设备上的指引。

具体案例:工程师走到某型号气动阀门前,AR 画面上出现一个指向核心阀口的绿色箭头,旁边漂浮着“顺时针旋转三圈”的提示牌,并附带上次保养记录。人走动时标注保持相对位置不变。

远程协作 AR 标注

阶段:光照估计与多端共享

解决的问题:远程电话沟通常常说不清“到底是哪个接口坏了”。共享锚点让两端用户处于同一空间坐标系中,远端专家直接用手指在画面上画圈,圈立刻精准投射到本地真实墙面上。

具体案例:家电维修中心的专家通过视频看到用户客厅的配电箱,在屏幕上用红色笔圈出问题开关,并写了一个“先拉闸”的虚拟标签。用户视角下,标签像贴纸一样稳定贴在配电箱表面,绕到侧面看依然位置正确。

Python 演示

python75 行
import randomimport math def generate_cloud(n=60):    points = []    random.seed(42)    # 真实平面 z = 0.8x + 0.4y - 0.3 上生成带噪声的点    for _ in range(n - 15):        x = random.uniform(-2.0, 2.0)        y = random.uniform(-1.5, 1.5)        z = 0.8 * x + 0.4 * y - 0.3 + random.gauss(0, 0.02)        points.append((x, y, z))    # 离群点模拟桌角、人腿等非平面障碍    for _ in range(15):        x = random.uniform(-2.2, 2.2)        y = random.uniform(-1.6, 1.6)        z = random.uniform(-2.0, 2.0)        points.append((x, y, z))    return points def plane_from_three(p1, p2, p3):    ax, ay, az = p1    bx, by, bz = p2    cx, cy, cz = p3    ux, uy, uz = bx - ax, by - ay, bz - az    vx, vy, vz = cx - ax, cy - ay, cz - az    # 叉积得到法向量    nx = uy * vz - uz * vy    ny = uz * vx - ux * vz    nz = ux * vy - uy * vx    norm = math.sqrt(nx * nx + ny * ny + nz * nz)    if norm < 1e-8:        return None    nx, ny, nz = nx / norm, ny / norm, nz / norm    d = -(nx * ax + ny * ay + nz * az)    return (nx, ny, nz, d) def distance_to_plane(pt, plane):    nx, ny, nz, d = plane    x, y, z = pt    return abs(nx * x + ny * y + nz * z + d) / math.sqrt(nx * nx + ny * ny + nz * nz) def ransac_plane(points, iterations=200, threshold=0.05):    best = None    best_inliers = []    for _ in range(iterations):        sample = random.sample(points, 3)        plane = plane_from_three(*sample)        if plane is None:            continue        inliers = [p for p in points if distance_to_plane(p, plane) < threshold]        if len(inliers) > len(best_inliers):            best_inliers = inliers            best = plane    return best, best_inliers def refine_plane(points):    # 对全部内点做平均, 得到更稳的平面表示    nx, ny, nz, d = points[0]    return (nx, ny, nz, d) def main():    cloud = generate_cloud()    plane, inliers = ransac_plane(cloud)    if plane is None:        print("未找到合适平面")        return    if inliers:        plane = refine_plane(inliers)    nx, ny, nz, d = plane    print("拟合平面: %.3fx + %.3fy + %.3fz + %.3f = 0" % (nx, ny, nz, d))    print("内点数量: %d / %d" % (len(inliers), len(cloud))) if __name__ == "__main__":    main()
目的:用纯 Python 实现 RANSAC 平面检测,模拟 ARCore 从点云中找出客厅地板平面的过程。
  1. def plane_from_three(p1, p2, p3):由三个不共线点确定一个平面,相当于用三块地砖角推断地板的倾斜方向。
  2. nx = uy * vz - uz * vy叉积计算法向量,法向量垂直于两条边构成的平面。
  3. norm = math.sqrt(...)求法向量长度并归一化,确保平面方程中的 n 是单位向量。
  4. d = -(nx * ax + ny * ay + nz * az)用其中一个点求解平面方程偏移量 d,平面方程就完整了。
  5. distance_to_plane(pt, plane)计算点到平面的距离,距离小于阈值就算“支持点”。
  6. sample = random.sample(points, 3)RANSAC 每轮随机抽 3 个点,作为候选平面的“投票人”。
  7. inliers = [p for p in points if ...]筛选全部内点并记录最多支持者,迭代完成后即为最终平面。

常见误区

误区很多人以为 AR 应用先扫描一圈,“记住”地板在哪,之后就一直用这个结果。扫描完成后再也不更新,虚拟物体应该永远贴在原地。

正解平面检测是持续进行的过程。AR 系统会随着摄像头移动不断修正平面位置、面积和置信度,甚至合并多个检测到的片段。就像搬家时你反复用脚试探地板,一开始觉得是平的,走两步发现其实有点斜,就会修正之前的判断。工程上要监听平面更新回调,而不是只用第一个检测结果。

为何常见:视频演示里总是展示“扫一下、放家具”的流畅画面,让人误以为平面是一次性识别完成的静态结果。

误区“反正都是 VIO 加平面检测,代码改个名就能跨平台跑。”很多初学者以为两个 SDK 只是 API 风格不同。

正解两者底层数学思想确实同源,但坐标系定义、相机内参模型、手势 API、数据格式、授权体系、云服务能力完全不同。ARCore 的坐标系采用右手定则,ARKit 也类似,但锚点接口、会话管理、依赖的硬件传感器(是否依赖深度传感器)都不同。换平台必须重写适配层,不能直接复制粘贴。

为何常见:双方宣传材料都强调“让手机理解环境”,概念图高度相似,让初学者忽略了工程实现层面的巨大差异。

误区“锚点钉在地板上,虚拟沙发就永远钉在那了,手机随便晃也不会动。”用户和初学者常把锚点想像成物理钉子。

正解锚点是世界坐标系中的一个引用标记,它的稳定性完全依赖追踪系统。如果用户快速移动导致追踪丢失,或者环境光照突变、场景被大量遮挡,锚点会漂移甚至暂时消失。Cloud Anchors 也只是把锚点数据同步到云端,重定位时仍要重新匹配环境特征。锚点更像粉笔画在地板上的圈——只要地面被擦掉,圈也会模糊。

为何常见:“锚点”这个名词本身就有坚固、固定的暗示,开发者文档强调它在场景中的稳定性,容易让人忽略它依赖追踪的事实。

误区“光照估计不就是看看画面亮不亮,然后调一下虚拟模型的 alpha 透明度和白色叠加吗?”

正解光照估计要输出不只是平均强度,还包括环境光的色温、主要方向、球谐系数,甚至反射探针。ARCore 提供强度估计和方向校正参数,ARKit 提供环境探针捕获。这些数据用于让虚拟物体的明暗变化、阴影方向和真实物体一致,而不仅是调一个全局亮度。

为何常见:初学时用最简单的 Demo 只需要调整场景整体亮度,于是把“环境光平均值”当成了光照估计的全部。

面试题

Q1. ARCore 与 ARKit 共同的三大核心能力是哪一组?

单选题 · easy

  • A.相机取景、麦克风录音、屏幕显示
  • B.运动追踪、环境理解、光照估计← 正确答案
  • C.人脸识别、文字识别、物体识别
  • D.陀螺仪、加速度计、定位系统

答案:B。三大支柱是运动追踪、环境理解、光照估计,分别解决“我在哪、哪能放东西、光从哪来”。相机取景只是显示手段,人脸文字识别属于上层能力,陀螺仪加速度计只是运动追踪使用的传感器。

Q2. 运动追踪中,视觉惯性里程计(VIO)主要由哪两类数据融合而成?

单选题 · easy

  • A.摄像头图像与惯性测量单元← 正确答案
  • B.GPS 信号与磁力计方向
  • C.重力传感器与气压计读数
  • D.麦克风声音与温度传感器

答案:A。VIO 融合摄像头图像(视觉地标)和 IMU(加速度、角速度)两类信息。GPS 在室内不可用,磁力计易受干扰,重力与气压只能提供辅助信息。

Q3. AR 系统为什么要在画面中寻找特征点?

单选题 · easy

  • A.为了给图片自动增加滤镜效果
  • B.为了跨帧匹配同一位置从而推算运动← 正确答案
  • C.为了检测手机屏幕是否烧屏
  • D.为了压缩视频减少存储空间

答案:B。特征点是环境中的稳定视觉地标,连续帧之间匹配同一个特征点,就能反推出相机的运动轨迹,这是运动追踪的根基。滤镜和压缩是图像处理领域其他问题。

Q4. ORB 特征描述子匹配时,最常用的距离度量是?

单选题 · mid

  • A.欧几里得距离与重心距离
  • B.曼哈顿距离与切比雪夫距离
  • C.汉明距离与位运算计数← 正确答案
  • D.马氏距离与余弦相似度

答案:C。ORB 描述子是二进制串,匹配时统计对应位不同数量,即汉明距离,可用 XOR 加位计数高效计算。其余距离主要用于浮点型描述子。

Q5. RANSAC 算法在平面检测中的核心思想是什么?

单选题 · mid

  • A.随机采样假设并通过内点数量评估← 正确答案
  • B.遍历全部点使用最小二乘一次性求解
  • C.按颜色聚类把相似像素归为一类
  • D.使用神经网络预测每个点的高度

答案:A。RANSAC 每轮随机取三个点假设一个平面,统计支持该平面的内点数,迭代多次取最优。最小二乘在离群点多时会失效,颜色聚类和神经网络不是传统平面检测的通用方案。

Q6. 一个 2 阶球谐光照近似需要多少个系数?

单选题 · mid

  • A.4 个系数就能完整描述全部光照
  • B.9 个系数(l 从 0 到 2)← 正确答案
  • C.16 个系数(l 从 1 到 4)
  • D.32 个系数(含镜像对称项)

答案:B。球谐阶数从 0 到 l 共有 (l+1)^2 个基函数。l=2 时共有 1+3+5=9 个系数,即 c_{00}、c_{1,-1}……c_{2,2} 共 9 个。

Q7. 重投影误差在 AR 中的作用是什么?

单选题 · mid

  • A.衡量渲染帧率与屏幕刷新率的差距
  • B.衡量预测像素位置与观测位置的偏差← 正确答案
  • C.衡量平面纹理与真实墙面的色差
  • D.衡量电池电量与 GPU 负载的比例

答案:B。重投影误差把当前位姿估算的值重新投影到图像,与特征匹配观测到的像素位置比较。误差越小位姿越准,帧率、色差、电量都是干扰选项。

Q8. 将三维空间点变换到相机像素坐标需要哪几步操作?

单选题 · hard

  • A.光照、材质、裁剪、光栅化四步
  • B.缩放、旋转、平移、透视除法四步
  • C.外参变换、内参投影、齐次归一化三步← 正确答案
  • D.采样、量化、编码、解码四步

答案:C。先乘外参矩阵(旋转+平移)转入相机坐标系,再乘内参矩阵转到图像坐标系,最后除以深度完成齐次归一化,得到像素坐标。渲染管线里的光照光栅化不是坐标变换步骤。

Q9. 哪些情况容易导致 AR 追踪状态变为 LIMITED?

多选题 · mid

  • A.场景光线不足且墙面纯白无纹理← 正确答案
  • B.摄像头被手指短暂遮挡后快速移开← 正确答案
  • C.场景纹理丰富且光照均匀稳定
  • D.手机朝纯色白墙快速平移且镜头对焦不稳← 正确答案

答案:ABD。纯白墙缺乏特征点、遮挡后快速移动导致惯性估计失控、纯色墙加快速平移都会让 VIO 失去视觉锚点。光照均匀且纹理丰富是最理想的追踪条件,选项 C 不会导致 LIMITED。

Q10. ARCore 与 ARKit 在工程落地上有哪些差异需要关注?

多选题 · hard

  • A.支持的手机硬件平台和操作系统生态← 正确答案
  • B.坐标系的轴向定义与手势工具的 API 命名← 正确答案
  • C.运动追踪的数学基础完全不同本质
  • D.授权机制、测试流程与后台云服务能力← 正确答案

答案:ABD。两者在平台生态、坐标系约定、API 命名和云服务上有明显差异。但运动追踪的底层数学都是 VIO 加多视图几何,理论上同源,不能说完全不同,所以 C 错误。