从零实现AR室内导航Demo:基于ARCore与视觉SLAM的完整链路

发布时间:2026/9/7 7:54:11
从零实现AR室内导航Demo:基于ARCore与视觉SLAM的完整链路 简介AR室内导航Demo是一套基于Unity3D与Vuforia SDK开发的室内AR导航示例工程适合Unity开发者、AR初学者及LBS方向研究者参考学习。压缩包共856个文件大小62.46MB主要涵盖场景与预设unity/prefab、C#逻辑脚本cs、材质和着色器mat/shader、模型资源fbx、Android/iOS相关库与配置java/so/dll/plist以及文档md/txt等目录结构清晰便于按模块拆解学习。项目通过图像识别锚定室内平面图或地标并接入定位信息实现路径指引源码包含Vuforia配置、场景设置及导航算法示例。已有5707人学习下载学习者可获得从目标识别、场景搭建到导航逻辑的完整实现思路也可基于此扩展定制是一份能帮助打通AR与LBS结合开发流程的实用Demo。 前阵子我花了大概两个周末把一直想做的一个 AR 室内导航 Demo 从零到一跑通了。今天把它拆开讲讲。这个 Demo 解决的是个很典型的痛点在商场、写字楼、地下停车场这种 GPS 完全失效的室内环境里人很容易找不到方向。手机地图在室外能把人导到楼底下进了楼就抓瞎。AR 室内导航的思路就是让手机摄像头充当你的眼睛在真实场景画面上叠加指引箭头、距离数字和目的地标记直接把“往哪走”“还有多远”怼到屏幕上。做这个 Demo 之前我也查过不少资料发现网上大多数教程要么停留在“调用一下 AR SDK 放置一个虚拟物品”的玩具层面要么直接甩给你一套商业 SDK 的接入文档中间缺了很大一块从地图怎么来、定位怎么算、路径怎么画到最终怎么渲染到屏幕上这条完整链路很少有人讲清楚。这篇文章就按我实际做 Demo 的路线把技术选型、算法思路、踩坑过程和能直接用的代码片段都展开说一遍。如果你是刚接触 AR 开发或者想在 Unity / ARCore / ARKit 这套体系里做个室内导航原型这篇文章应该能帮你省掉不少试错时间。1. 项目拆解AR室内导航Demo到底在做什么1.1 核心需求与常见误区我给自己定了个目标打开 App摄像头对着走廊拍一圈系统自动识别当前所在位置然后在地面上渲染出一条指向目标房间的半透明光带和悬浮箭头跟着箭头走到门口就算成功。听着不复杂但一拆开就发现问题不少。最核心的三个子问题设备怎么知道自己在室内什么位置、朝向哪里目的地的路径是怎么算出来的如何让虚拟指引稳定地“粘”在真实地面上而不是跟着画面抖动我第一次做的时候想当然以为 ARCore 或者 ARKit 里自带的定位已经够了结果在走廊里走了几步虚拟箭头就开始漂移指向完全跑偏。后来才搞明白手机 AR 的定位是相对定位它只能告诉你“相对于刚才初始化位置的位移”并不知道你在整个建筑里的绝对坐标。要想让导航有意义就必须先建立一张“地图”让当前相对位置能对应到地图坐标上。1.2 技术选型为什么选视觉 SLAM 而不是其他方案室内定位目前有几条路线蓝牙 Beacon 指纹、UWB 超宽带、WiFi RTT、视觉 SLAM 以及二维码标记定位。我在 Demo 里最终采用了视觉 SLAM 为主、二维码标记辅助的方案原因很直接蓝牙和 WiFi 指纹定位精度在 1 到 3 米左右而且需要提前采集全场的信号指纹数据Demo 阶段没法搞UWB 精度高但需要部署基站硬件成本直接劝退视觉 SLAM 只需要摄像头靠图像特征点估计位姿精度在厘米到分米级虽然会有累计漂移但短距离导航绰绰有余二维码标记定位可以在关键位置做一次“绝对坐标修正”把漂移拉回来至于为什么不用 GPS室内环境卫星信号衰减严重偶尔能收到几颗星但误差能到几十米完全没法用。这里顺便提一下行业里最近比较热的 3DGS 三维重建。如果要做的是装修级别的精确室内数字孪生那 NeRF 或 3DGS 重建整个场景是正路。但对于导航 Demo 来说实时性和计算量都不允许跑这种重建模型所以选择轻量级的平面识别加锚点方案更务实。真要做商品化产品可以考虑预构建点云地图配合 VPS视觉定位服务那是另一个量级的工程了。1.3 行业现状从 Demo 到产品的差距现在市面上的 AR 室内导航要么是商场的定制导览系统要么是机场、医院的室内导航应用。它们大多基于视觉 SLAM 加二维码布点或者基于预先扫描的点云地图。华为的 AR Engine、Google 的 ARCore、苹果的 ARKit 都能做到实时平面检测和跟踪。此外鸿蒙生态里现在也支持把 AR 能力打包成 HAP、HSP、HAR 这种模块形式方便多设备复用这点我在后面打包环节会再提。从技术层面看Demo 用到的方法和生产级方案是一致的差别主要在工程化地图生产流程、多楼层切换、长时间漂移修正、多人共享位姿、后台语音提示、服务端路径规划、数据统计。如果只是做一个可行性验证或毕业设计Demo 的关系链路已经够用了。2. 核心实现拆解导航链路是如何跑通的2.1 地图构建从扫描房间到生成可导航地图室内导航的第一步不是写代码而是“建地图”。地图在这里分两层理解视觉层和逻辑层。视觉层地图由特征点和平面组成。ARCore 或者 ARKit 在启动时会自动检测摄像头画面中的角点、纹理边缘这些特征点并通过多帧三角化计算它们的空间位置。同时SDK 会识别出水平平面地面、桌面和垂直平面墙面。这些平面和特征点共同构成了 AR 会话的“空间锚点”基础。逻辑层地图是导航算法要用的简化模型。我这里是手动在 Unity 编辑器里放置了地板面片和导航路径点再把真实走廊的位置用坐标换算到模型坐标。说白了就是用一组带坐标的节点和边描述“哪里能走”“哪两个点连在一起”。有一个关键坐标转换问题AR 坐标系的0,0,0在启动会话时的相机位置坐标系的方向以手机姿态为基准。而导航地图有自己的原点和朝向。两者必须通过一个变换矩阵对齐。我在场景初始化时让用户站在一个已知点比如电梯口对准一个二维码标记用这个标记的位姿作为坐标对齐的桥梁。注意这个手动对齐过程是 Demo 里最容易出问题的地方。如果手机拿的角度不对或者二维码识别时距离太远后面所有叠加的箭头都会整体偏移。我的做法是在界面上显示实时偏移距离让用户调整站位让误差小于 0.1 米再锁定。2.2 定位与姿态解算设备如何知道自己在哪定位部分我没有自己写视觉里程计直接用 ARCore 的 Session 获取相机位姿。ARCore 底层跑的是基于视觉惯性里程计VIO的 SLAM融合了摄像头图像特征点追踪和 IMU 惯性数据。简单来说摄像头告诉它“相对于上次位置我移动了多少像素”陀螺仪和加速度计告诉它“设备转了多少度、加速度是多少”两者融合估算出六自由度位姿。但 VIO 有一个绕不开的毛病累计漂移。走 20 米之后误差可能到 20 到 50 厘米。对导航来说这个误差还在可接受范围毕竟引导箭头是视觉指示不是厘米级操作。但我还是加了两个辅助修正手段扫描识别墙上的位置二维码检测到之后把当前位姿直接对齐到二维码对应的预设坐标在走廊尽头的拐角处放置一个虚拟锚点当用户走到锚点附近时把路径起点重新绑定到锚点坐标另外设备朝向的初始角很重要。ARCore 依赖重力和磁力计做初始姿态对齐。在室内钢筋结构多、磁场干扰大的地方指南针方向容易偏。我的经验是初始化时让用户把手机竖起来朝一个笔直的方向比如走廊轴线方向停留两三秒能明显减小初始朝向误差。这里要补充一个不经常被提到的点ARCore 的 VIO 对纯旋转非常敏感。很多新手在原地转圈看环境时位姿漂移就会快速累计。调试阶段尽量让设备做平移运动别在原地转着玩。2.3 路径规划与 AR 引导光柱、箭头与文字提示地图场景搭建好之后路径规划就直接调用 A* 算法。我把走廊抽象成一张拓扑图节点是走廊拐角和目的地边是两节点之间的可通行路径。A* 的代价函数里加入了走廊宽度惩罚避免规划出的路径贴墙太近。计算得到路径是一串世界坐标点。接下来要做的是把这条路径“贴”到真实地面上对每个路径点做一次向下射线检测Raycast打到地面平面后在命中点生成一个小方块锚点。锚点之间用 LineRenderer 串起来渲染成一条发光的路径带。同时每个路径点上方生成一个 3D 箭头箭头朝向路径方向。渲染层有个很实用的细节路径带和箭头始终朝 Y 轴上方偏移 5 到 10 厘米。这样做的目的是防止虚拟物体和真实地面在视觉上“打架”出现闪烁和穿插感同时能让用户更清晰地看到引导线。距离提示我用的是 Canvas 上的文字 UI 加一个 3D 浮动图标。每隔 0.5 秒计算一次用户当前位置到终点的实际距离更新到 UI 上。3. 一个可复现的Demo流程3.1 环境准备与工程搭建我当时的开发环境是系统Windows 11引擎Unity 2021.3 LTSAR 框架Google ARCore SDK for Unity1.37 版本左右测试机一台支持 ARCore 的 Android 手机带 IMU摄像头别太差如果你想跑 iOS用 ARKit 的 Unity SDK 也是等价的核心 API 的命名都很接近。鸿蒙方向的 AR Engine 也支持 Unity打包方式略有不同后面会提。Unity 工程建好后先导入 ARCore SDK 包然后做三个设置Project Settings - Player - Other Settings 中把 Minimum API Level 设为 Android 7.0 以上勾选 ARCore Supported让应用在支持 ARCore 的设备上自动申请相机权限把渲染模式调整为 OpenGLES3 或者 Vulkan不要用 OpenGLES23.2 场景配置与锚点放置在 Unity 里创建空场景挂一个 ARCoreSession 对象用来启动和保持 AR 会话。再挂一个 ARCoreSessionOrigin用来承载相机和锚点。这两个是 ARCore Unity SDK 的标配。接着在地面上手动创建路径点。我从建模软件里导入了一层楼房平面图用 CAD 导出的轮廓再拉伸成一个薄立方体放在地平面附近作为视觉参考。然后按真实走廊结构在 Unity 里用空物体放置节点节点之间用直线连接。这一步是在编辑器里做的“离线地图构建”。节点属性大概是这样public class NavNode : MonoBehaviour { public string nodeId; public Vector3 worldPosition; public Liststring neighborIds; public bool isDestination; }3.3 核心代码与关键参数定位获取的代码很简单核心是拿到相机位姿再绑定到场景中的玩家对象上using Google.XR.ARCore; void Update() { // 从 ARCore 会话中获取当前相机姿态 Pose cameraPose Session.Status SessionStatus.Tracking ? Frame.Pose : Pose.identity; // 把相机位姿同步到 Unity 主相机 Camera.main.transform.SetPositionAndRotation(cameraPose.position, cameraPose.rotation); }路径生成和渲染的核心逻辑是把 A* 算出的路径坐标点转成世界坐标再逐个做射线检测贴地最后串成 LineRenderer 和箭头。void RenderPath(ListVector3 pathPoints) { lineRenderer.positionCount pathPoints.Count; for (int i 0; i pathPoints.Count; i) { Vector3 groundPoint GetGroundPoint(pathPoints[i]); groundPoint.y 0.05f; // 离地 5 厘米避免穿插 lineRenderer.SetPosition(i, groundPoint); // 每个路径点生成方向箭头 GameObject arrow Instantiate(arrowPrefab, groundPoint, Quaternion.identity); Vector3 dir (i pathPoints.Count - 1) ? pathPoints[i 1] - pathPoints[i] : Vector3.zero; arrow.transform.forward dir.normalized; } } Vector3 GetGroundPoint(Vector3 origin) { RaycastHit hit; if (Physics.Raycast(origin Vector3.up * 2f, Vector3.down, out hit, 5f)) return hit.point; return origin; }这段代码里有两个容易踩的坑一是射线检测的层级要过滤好只检测地面层不然碰到桌子椅子这些临时物体路径会贴到桌面上二是路径点之间如果距离太大LineRenderer 会拉出一条穿墙的直线。所以我在 A* 输出之后会对路径做插值每隔 0.5 米插入一个中间点。路径规划用的是简化版 A*public ListNavNode FindPath(string startId, string endId) { // 用优先队列 G/H 代价启发式搜索 // G 为起点到当前节点实际代价 // H 为当前节点到终点的曼哈顿距离估算 // 最终返回从 startId 到 endId 的节点序列 }关于 A* 的参数我用的启发式函数是欧几里得距离因为走廊是直线距离曼哈顿距离在这种结构下反而会绕远路。代价权重里我额外加了一项“转角惩罚”如果路径从上一个节点到当前节点再到下一个节点时转角大于 45 度就额外增加 1.5 倍距离权重的代价。这样跑出来的路径会优先走直线转弯次数少体验更顺。3.4 打包与真机调试Android 打包时要注意 ARCore 的清单文件配置。Unity 的 ARCore SDK 会自动注入需要的权限。如果打成 APK 后运行时黑屏优先检查两件事手机是否支持 ARCore在应用商店搜索 ARCore 服务能搜到就支持相机权限是否已授权Android 6.0 以上运行时权限需要单独弹窗鸿蒙的环境我后来也简单试了一下。华为的 AR Engine 提供了类似 ARCore 的会话能力打包成 HAP 的时候可以把 AR 业务拆成独立的 HSP 或 HAR 模块这样同一个 AR 引擎能力可以给多个鸿蒙应用复用。做 Demo 的话用 HAR 就够开发调试时改动 AR 代码只需要重新编译那个模块不用每次都全量打包。这个模块化的思路比 Android 的纯 APK 开发更清爽是鸿蒙生态的一个优势。4. 常见问题与排查技巧实录4.1 虚拟物体漂移、抖动、锁不住地面怎么办漂移是 AR 导航最致命的问题。表现就是箭头明明朝向目标走了两步就开始横移甚至悬在空中。排查顺序我建议是先看初始化时手机是否做了平移运动。ARCore 刚启动时需要用户左右晃动手机积累视差才能建立稳定的空间感知。如果启动后立刻站着不懂特征点不够位姿就会抖其次看环境纹理白墙、玻璃墙、无反光地砖区域特征点极少跟踪质量很差。我的调试环境选了瓷砖地面加墙面海报的走廊特征是足够的再看运行时是否有遮挡。手遮挡摄像头、镜头进灰、逆光强光都会让跟踪质量下降我在测试中发现一个很好用的小技巧在导航的全过程中把 ARCore 的可追踪特征点可视化出来实时观察周围特征点数量和分布。如果特征点数量少于 20 个就该提醒用户换个位置或姿势了。4.2 电磁干扰导致朝向错误前面提到过室内磁场干扰会让初始朝向偏。ARCore 的朝向融合并不会完全依赖磁场但如果刚启动的瞬间磁场严重异常累计朝向误差会在后续步态中放大。我第一次测试时站在一个变电箱旁边初始化箭头方向直接偏了将近 90 度往左边指人得往右走。解决方法是两步优化初始化交互让用户走到走廊中间位置屏幕中心对准远处目标点保持静止 2 秒确认每次检测到二维码标记时强制校正一次朝向把相机前方对齐到标记的 Z 轴方向4.3 性能与发热问题这一代手机跑 ARCore 加 Unity 渲染功耗不小。我在测试机上跑了 20 分钟机身温热帧率从 60 掉到 40 左右。优化经验有三点开启 ARCore 的背景渲染缓存不重复处理相机纹理路径带和箭头的 Mesh 面片数尽量少用最简单的圆柱和箭头模型不要上高模UI 层的 Canvas 不要频繁重建顶点距离文本的 Text 组件每帧更新时设置一个 0.5 秒的节流简单列了一个速查表方便对照排查现象可能原因解决办法偏移漂移特征点不足 / 快速旋转回归平移扫描少原地转圈箭头悬空Raycast 层级错乱过滤检测层级单独设 Ground 层方向偏 90 度磁场干扰严重二维码对齐 / 手动初始化校正路径穿墙节点间无插值A* 后自动插值加密画面卡顿渲染面数过高精简模型启用背景纹理缓存鸿蒙无法安装HAP/HAR 依赖配置错误单独编译 HAR 模块后重新集成4.4 多平台迁移心得如果后续想让这个 Demo 跑在 ARKit 或鸿蒙 AR Engine 上建议把定位、路径规划、渲染做成三个独立模块。定位模块封装成接口只暴露一个GetDevicePose()方法路径规划模块不依赖任何 AR SDK渲染模块只接收世界坐标路径点。这样换底层 SDK 时只需要写一个新的定位模块实现类路径和渲染代码完全不用动。我在 Demo 里就是这么设计的后面切到鸿蒙测试时确实省了很多事。写在最后的几句大实话AR 室内导航 Demo 做起来并不难难的是把“准”“稳”“快”这三个字做透。我这个版本也只做到了“能走通”离产品级还有不少距离。但把这个链路跑通之后再去看商用的导航 SDK 文档很多概念都能对得上了像是点云地图、共享位姿、云端 VPS 这些本质都是在解决我前面提到的那些痛点。如果你也想做类似的 Demo我的建议是从一个很小的场景开始比如一条 30 米长的走廊两三个目标点先把视觉定位和路径渲染跑顺再慢慢加楼层、加多路径、加手势交互。别一上来就想着做整个商场规模一上来坐标系管理和漂移修正的复杂度会指数级上升很容易把自己劝退。测试的时候也记得多备几台不同型号的手机。有的手机对单目视觉追踪的支持差异非常大至少我测试下来旗舰机和高性价比机型的跟踪稳定性差距能到一倍以上。最后祝各位能顺利跑通自己的第一个 AR 导航 Demo有问题可在评论区交流我尽量回复。本文还有配套的精品资源点击获取