Notifications
Article
AR导航精灵开发复盘
Updated 4 months ago
190
0
AR导航精灵开发复盘
本文是app“AR导航精灵”的开发复盘总结,您可以在AppStore上搜索到;源代码请参看我的GitHub页面:https://github.com/734843327/AR-navigation/

2018年一月我进入国家动漫园实习,跟随刘扬老师学习AR技术。经过一周的摸索准备,我的工作目标定在了AR导航的实现。
为什么是AR导航?
一周里看了很多市场上的AR应用,感觉都有些不尽人意。最大的感受是,现阶段AR还不是特别成熟,没有什么场景是AR刚需,但用户也有尝鲜的需求,所以需要一款最能体现AR特性的应用来吸引用户。Mentor给我看了一款名为AR Penguin的应用——这是一个AR实景导航,打开app 后会出现一只企鹅,带用户去一家水族馆(这也是那家水族馆的营销手段)我看这个项目确实挺有意思,有技术铺垫,有改良空间。而且完全可以体现出AR特性,之后我会专门写一篇文章来讨论我对AR的理解,有一些场景很适合AR,但大多数目前来说并不适合。这里我们暂不展开。
用什么平台、什么技术?
手机的操作系统就Andriod、iOS两大阵营,而市面上流行的AR解决方案有ARKit、ARCore、Vuforia三种,ARkit对应iOS,后两个对应Andriod(这是2018年1月的情况,当然还有一些国产解决方案,但不太成熟,兼容性也不太理想,没有再深入了解;AR市场日新月异,每家的解决方案发展都非常快,后文的所有内容全都是基于当时情况,很有可能后来又发生了天翻地覆的变化,故请读者仅作参考)
另外,由于我一点都不懂安卓和苹果开发,java、object-c啥的也根本没学过(hhh真不是逗你玩)只能靠Unity勉强度日,所以需要用Unity做主要开发平台,而不能使用原生的。三种AR解决方案都推出了Unity插件,下面是一些测评:
ARCore:当时还是公测版,根本不支持我的华为机(即使后面正式版出来了,也只支持少量谷歌、三星机),果断放弃了
Vu foria:能用,一些功能做的还不错,业界领先,如3D物体识别。但最大的问题是不支持remote,即连上手机后在Unity中直接点play测试。这非常难受,因为尝试新技术,总是要一点一点尝试它的所有功能,总不能做一步打包一个apk吧,太没效率了,也只好放弃。
ARKit:安卓阵营全军覆没,只好选苹果爸爸的ARKit了。一开始特别抵触,因为文档奇缺,尤其是for Unity插件,甚至没有文档,只有一篇介绍性的博客,墙内外能参考的资料也很少。处境可以说十分艰难了。
技术路线
OK,既然是AR导航,主体工作就分为两部分,一是AR,二是导航。AR的难点就是资料少……即使把ARkit官网文档全过一遍,也并没啥卵用,因为ARKit和ARKit for Unity插件的差别实在太大了,只能稍微了解些概念性的东西(强烈建议官方出插件的文档orz)这是我第一次看文档时的想法,但后来发现,其实文档依然是克服所有困难的核心的核心,后面会谈到。

创建一个AR角色并实现基本移动并不困难,要注意的点有:1.实现扫描地面成功后触发事件,要使用MyARAnchorAdded委托,具体代码如下,这还是第一次用到委托这个概念hh。
void OnEnable() { UnityARSessionNativeInterface.ARAnchorAddedEvent += MyARAnchorAdded; }

2.实现人物站在平面上:
yourObj.transform.position = UnityARMatrixOps.GetPosition(anchorData.transform);
要注意anchorData的概念,一定要读透文档,尤其是里面涉及的坐标体系,非常乱,读完文档还要读相关代码,最后再动手试试,才能把那些坐标搞清楚。
到这步的时候,AR应该没有其他大问题,好像也没啥能做的了,于是想先做地图部分,看看导航这块最后有什么需求,AR这边再配合着改。
其实做到现在,对整个产品框架还是没有一点主意的,因为完全没做过这方面的东西,对怎样实现导航根本不懂。只知道别人已经做出来过,那我就肯定也能做出来,就靠着这点迷之自信,开始寻找地图的解决方案。
找了一圈百度/腾讯/高德地图的解决方案,妈嘞,全都是for Android/iOS的,根本没有for Unity的。我也问了一圈,这for iOS的SDK能不能想办法弄到Unity里,后来感觉应该是有解决方案的,但太难,需要自己写接口,我一窍不通,遂放弃。
这是我第一次陷入绝望,好在mentor给我推荐了Mapbox,说是一个对Unity友好的地图解决方案。花了两三天边摸索边看文档(我实在太菜了),最后终于找到了所有需要的接口,那么现在,终于形成了一个大致的产品&技术思路:
用户打开应用后,后台读取用户当前位置;用户输入目标地点,后台将输入的文本传给mapbox检索,返回若干个“结构化位置”(如下图,用户输入的文本并不一定能和后台的地点信息刚好匹配上,这时就需要用户进一步确认)

“结构化位置”包含的信息太多了,最基本如名字、经纬度,还有所属街道名、城市、以及大量POI信息。把这些选项再推给用户,让用户确认到底是去哪一个,这时后台也就拿到了目的地的经纬度坐标。有了起点终点的坐标,就可以调用路径规划接口,mapbox会返回一条“路径”,这“路径”也包含很多信息,我们需要什么呢?当然是路径折线上每个顶点的坐标,我们依次走向每个顶点,最后就能到达终点。当然,这里面也有坑……Mapbox默认返回的waypoint并不是严格的折线顶点,而是一定缩放比例下的“明显要拐弯的地方”,非常不精确,滨海新区到南开区一共只有20多个waypoint,需要改变Fields参数值为full,才能实现返回严格的几何顶点。
再用图表示一下整个流程:

现在导航部分大体搞定,难点在于右下角,要与AR部分联动。【如果说本项目有什么真正的难点,应该只有这里了】需要考虑的问题是:导航部分Mapbox所有的地理坐标格式都是它自己定义的Vector2d,即(纬度,经度),路径规划后,每两个途经点之间的路线必然是直线,可以简化成一个方向向量,也可用Vector2d表示。总之导航部分,所有坐标都是基于真实世界的经纬度坐标系表示的。但是!Unity可不吃你这套啊,人家有自己的xyz坐标系。那现在这两套坐标系,如何统一?不可能直接向精灵发送“向经度+45°,纬度+45°方向移动”吧,总要转成xyz坐标;另外,如何实现贴地行走?这里面可大有文章。

先说坐标转换。我开始时比较傻萌,想实验出来这俩坐标系有没有夹角度数关系,因为我不知道ARKit是怎样定义方向的,后来一想,可能和打开app时设备朝向有关,于是调用设备指南针信息看了看,目测确实是这样。Unity中Input.compass.trueHeading表示当前设备朝向。
如上图,我们有两个坐标系(竖直方向一样,我们就把三维简化成平面二维了)它们有一个夹角,和compass有关;那么任何一个方向向量AB,在两个坐标系下的表示肯定是不一样的,这种变换关系完全也可以算出来,于是我就傻乎乎地去算了……算完以后我一翻ARKit文档!!!md原来AR的坐标系是可以设置的!只需将ARConfiguration.WorldAlignment的参数调为gravityAndHeading,上面那俩坐标轴就能完全重合!心真累……这个我一个非常重要的启示:技术文档至少要看两遍,一遍是刚开始接触时,这时看不了那么细,只是大致了解有什么功能、怎样实现;第二遍是在项目做到山穷水尽时,这时你对sdk已经有了一定理解,但卡在细节上,这时就能带着思考再去领会。这遍阅读时,就要重点思考有哪些角落的东西对项目有用,一丁点都不能放过。技术文档常看常新,看多少遍都不为过,因为每次你带着不同的思考,就会有不同的关注点和收货。但是话说回来,这些几何思考也挺好玩的hhh
这样问题就解决了

吗?
理想很丰满
一个大坑是,Mapbox提供的数据源渣得一比,根本没法用。按它的说法,地图数据采集于一家开源地图数据库OpenStreetMap,并且自己有一些加工。你也知道,天朝有特色地图管制,这种境外的地图源信息缺失严重、并有大量错误。比如地名天津大学对应的坐标竟然是天津医科大学的,忍不了。如果最后导航确定用这个sdk做,怕是要导航导翻车。
留给我的时间已经不多了,虽然在Mapbox上倾注大量心血,但还是毅然决定抛弃。重新看了看国内的解决方案,发现除了sdk之外,还有一种叫“Web API”的东东。虽然从来没接触过,但隐约觉得能用,便现学了Unity网络通信、JSON的东西,直接上阵了,选了度娘的。
Web API和sdk插件没太大差别,就是所有函数接口都在人家Web服务器上,你得发送http命令才能调用。这时碰上了一个天坑,导致了我第二次绝望——Web API不支持实时定位。虽然手机能读取GPS坐标,但因为天朝特色管制,百度所有坐标使用的都是加过密的百度坐标系,GPS体系必须经过加密转换才能用,但这种转换只能发送到服务器上才能进行,完全不能实时。除非你用人家的sdk可以在本地转,但我也说了我不会用……你猜我怎么解决的?我竟然查到了一份加密算法并直接copy了过来……写代码真刺激,心情就像过山车。

后面就把mapbox的东西全换成了百度地图。这样问题就解决了,细节步骤不在赘述,剩下的问题就是如何【贴地与避障】
很遗憾,目前我的回答是,没有完美的解决方案。
避障是不可能避障的,这辈子都不可能避障的。AR导航不像人那么聪明——你开着百度地图,导航告诉你往前走,但前面有辆自行车,你肯定知道绕一下;但AR精灵不行啊,她收到的指令就是一直沿某方向走直到某点附近。所以,避障这事完全取决于地图源的靠谱度和你城市的整洁度……百度地图倒还好,有步行导航模式可以把你限制在人行道上不至于导到快车道上去,但满地的单车怎么解决……
“贴地”的意思是,精灵必须在地面上行走,不能走着走着飞到天上,你可能会想控制y轴值不变不就得了?但人家Y轴自己都不能保持精确竖直的……这跟陀螺仪有很大关系,你把手机晃一晃、甩一甩,就会惊喜地发现精灵已飞到天上。这问题和避障有相似之处,思来想去,问题的核心就是四个字,地面识别。
所有的移动,必须紧紧贴在已识别的平面上,而不能简单地就给一个(x,z)方向向量,就让她随便撒野。不是想避障吗?那我做更保守些,只要我没识别出是平面的区域,一律不走!这样总可以了吧,贴地更别说了,肯定是在地面上。
但想一想,这样牺牲了什么什么?
“导航”
导航的目的,是要引导用户走向终点,但如果航线规则设置的太保守,就根本无法达到导航本身的目的。就拿现在的情况来说,AR精灵走到了摆满单车的人行道,前方已无法识别出平面(即地面),那她会怎么办,只能选择原地不动,这样就丧失了导航的意义;或者另外一种算法思路是沿着 能走的&&与现有方向夹角最小的方向走,直到能识别出平面再返回原方向。这个项目确实就是这么做的。也就是说,人与精灵其实是在互相带路,当精灵走不下去的时候,需要人主动地调整位置,才能使精灵继续正常工作。
这样,真的就丧失了导航的意义了吗?
我看过一些同类项目,他们路径算法真的很是豪放,一条路走到黑什么都不顾,走到垃圾桶上也在所不惜;这样的导航当然十分准确,但是从另一个角度想,真实性完全丧失,难道不就是丧失了AR的意义了吗?
ok,那么现在问题就变成了——导航和AR哪个重要,我思考了很久,最终决定,选AR。也就是说,这款应用不会有很好的导航体验,但一定会有最真实的交互体验。做这个决定的原因很简单——用户如果想要更准确的导航,他直接去找百度地图就好了,但既然来到我这儿想尝鲜AR,那么AR体验就一定是重中之重,其它一切都必须让路。
还有没有其他修补方式?
我又增加了一个“跟随模式”即当精灵走位严重错误时,允许精灵完全离开导航模式,跟随用户行走(其实是跟随Camera.main.ScreenPointToRay),等到了环境合适的地方,后台再重新规划路径、重新回到导航模式。
(其实纠结这么多取舍,本质上还是因为现在技术不成熟,如果ARKit日后能把自行车识别出来(把无人驾驶那套搬到手机上,还有啥避不了的障),那一切就都不是问题了,希望能有这么一天吧)
为了增加导航可信度,我又把百度的路径提示信息摘了出来,显示在屏幕上方,如“过XXX米右转,进入XX路”
所有技术与产品思考,应该就是以上这些了。
UI思考
根据公司分工,美术部分给我配了一个小姐姐,不用我做;但在人员到位之前,我已经做了一版demo,有一些思考,也要写一下。
我思考的核心问题是,什么样的UI风格是适合AR的。
AR和一般软件相比,强调的是真实世界,是沉浸感、真实感,但是任何附在屏幕上的UI元素,都会打破这种氛围,下图就是一个不好的示例,太过冗杂感的画面,带来的用户体验是极糟糕的。
我想到了两个关键字,极简、半透。
半透指主要UI元素半透明,这样能不至太醒目、打扰用户;极简指尽量减少UI元素使用,但是说实话很困难,必要的提示、交互有很多,不可能全去掉。
还设想了另一种交互方式:把所有UI元素拆解到AR场景上,比如把一些按钮(如“About”、’’关闭”之类)也做成小精灵,让它们跟着导航精灵一块移动,这样就不会有元素会固定悬浮在屏幕上(这点我认为是AR应用应该着重考虑的)但这个想法看起来太奇怪了,便没有去实现。
我自己美术功底非常差,最后的版本也基本不忍直视:

右上角和左下角是两个永久按钮,右下方是临时命令按钮,上方显示导航信息。这个版本美术最后没有被采用。
图上蓝色的框是什么呢?是ARKit已识别出的地面区域的标注。要不要留这个框我纠结了很久,最后决定留(但换了另外一个样式)它很丑但很必要。前面已经说过,精灵的所有移动都必须在已识别出的地面上,那么我们就应该把这个已识别区域展现给用户,方便用户确认。当精灵走到边框不再移动时,用户就知道应该赶紧往那个方向走,以识别出更多平面。我们强调的是贴地和避障,就必须把问题的核心“安全区”展示出来。那些完全乱飞的AR应用不在乎这些,它们就不需要标注地面。
音效
寒假做的时候没有考虑声音部分,现在来看,必须要加,好处有以下几点:1.增加动感,卖萌。现在抖音很流行,大家能明显看出来运动和音乐结合起来会带来非常欢乐的用户体验。现在精灵是行走带路,希望之后改成尬舞带路,加上有节奏的音乐,完美。2.提示功能。现在很多提示信息必须呈现在屏幕上,这些是不是真的有必要?(如前所述,任何没有绝对必要呈现的UI元素都必须被干掉,只保留最核心的交互元素)其实他们中的很多都可以改成语音提示,这样就节省了屏幕上的空间。
TODO
离开实习岗位的两个月来,AR技术又发生了翻天覆地的革新,很多当时勉强应对的困难,现在有了新的解决方案,ARKit推出了支持iOS11.3的新版本。我想7月时再改进一版,因为那时我时间比较富裕,而且iOS11.3会更普及一些。想下一步做的事情有:
1. 改进美术
2. 添加音效、语音提示与音乐
3. 增加竖直面识别功能,以更好避障(AKKit新版本功能)
4. 增加不规则平面识别功能,以更好避障(AKKit新版本功能)
5. 改善产品逻辑,删去firstfollow环节(这个文中没有提,可以看产品和源代码)
6. 如果可能,优化导航算法
7. 如果ARCore那时能兼容我的手机,就做一下安卓版
如果你有什么其他建议,欢迎告诉我~


总结
emmmmm可能你看完这篇文章,发现我真的菜的一逼(没错就是这样),做的全都是“API Caller”的工作。但其实对我来说,也是有意义的,毕竟是第一次做app。总结一下收获:
1. 把充足的时间放在找工具上。刚开始我基本没怎么深入了解每一个候选工具,只要感觉一个工具看起来可以,就马上拿来用,越钻越深,才发现了各种各样的坑,比如开始用的是Vuforia和Mapbox,用了很久后换成ARKit和百度地图api,浪费了大量时间。所以,一定要先读文档了解功能细节,再做小规模实验验证可行性靠谱度,最后再往项目上移植。
2. 多读文档。多读文档。多读文档。
3. 做自己不熟的东西,要有快速学习的能力,不能怕。现用现学,不用不学。
4. MonoDevelop是傻逼,Xamarin 是傻逼,VS Code是傻逼。VS大法好。
5. 我自身基础知识太差,主要指语言基础和算法基础,日后可能还是要回归CS本身学习上,尽量少做这种应用性的API Caller。我不止一次感受到只能调接口而不知其原理导致bug无法解决的绝望。

欢迎大家批评指正,我还是太菜了,溜了溜了。

VinalWang
学生 - Student
2
Comments