Find me - 用 ARKit 找人

姓名 连嘉玮 学号 16040120089

转自:画渣程序猿mmoaay 有删节

【嵌牛导读】:世界上七十多亿人,有的时候却只需要找到那一个。

【嵌牛鼻子】:ARKit

【嵌牛提问】:找一个人需要多久?

【嵌牛正文】:

引言

从 Apple 发布 ARKit 框架起,我就一直想学习并做点好玩的东西,后来就勾搭了滑滑鸡大佬来上海 Code<T> 沙龙第八次活动讲他做的 ARGitHubCommits,学习了一些 ARKit 的基础知识,后来持续跟进了一波,看了很多张嘉夫大佬的 ARKit 文章,也看了一些 ARKit 开源的项目。那会微博上有一个 ARKit 不停的弹 Windows 告警对话框的动态图特别火,看到的时候我就想,弹出来的这一堆对话框好像一条路径啊,这也是 Find me 这个 App 最初的灵感来源。

探索

后来就想利用这个特性做一些寻路的方面的探索,最开始是想做在家里找东西,脑洞如下:

在家里找一个位置固定不变的物品(比如电视机)做为参考点。

为每件物品录制一条路径到这个参考点。

想找某件物品的时候先找到这个参考点,然后就可以通过之前录制的路径找到这件物品了。

但是发现有两个特别大的痛点:

东西位置经常会变,路径也要跟着变。

找不到的东西往往是乱放的东西,所以当然也没有路径记录一说。

所以这个脑洞就被我 Pass 了,项目也搁置了一段时间。

然后有一天,我约了朋友去商场吃饭。他先到了店里,但是我确始终找不到这个店,问了半天才在一个很隐蔽的角落找到了这个店。约完回家之后灵感突发,这个场景完全可以用之前找东西的思路来做啊:

先到商场的人找一个好找的位置,比如商场北门。

从这个位置开始记录一条到门店的路径,并分享给后来的朋友。

后来的朋友找到这条路径的起点,也就是之前说的商场北门,打开先到的人分享的路径即可沿着路径找到约好见面的门店。

关键问题:从使用场景来说,没有痛点。

这也是我今天文章内容的主角 - Find me 实现的功能。

目前这个 App 已经发布上架:Find me,而且代码已经开源了,地址在:mmoaay/Findme(喜欢的话记得点个 Star),为什么选择开源呢?因为只是一个创意,并没有太多的技术壁垒,而且目前在技术上确实存在两个问题:

路径记录和寻找过程中 ARKit 的 Session 不能被打断,因为恢复之后的虚拟坐标会产生极大偏差,导致虚拟路径进入不可控状态。

ARKit 目前不稳定,虚拟坐标系会抖动,这样就会导致虚拟路径有偏移,而且距离越远,偏差越大。

产生这两个问题的原因主要都在虚拟世界坐标上,我们都知道,ARKit 初始化的时候,会基于你当前位置为世界原点,建立了一个虚拟世界坐标系:

所以,Find me 记录并分享出来的路径,对 ARKit 虚拟世界的坐标系有两个基本要求:

记录路径和根据路径找人时虚拟世界的原点一定要映射到现实世界中的同一个点,也就是分享和寻找的起点位置要是同一个。

虚拟世界的水平垂直方向要和现实世界一样。

优化

对于这两个问题,我也做了一些优化。

目前失败的两个优化

基于定位优化

思路很简单:根据定位将路径分段记录,并修正虚拟路径。相当于给虚拟路径加一个现实世界的坐标修正。

实践之后发现:定位比 ARKit 还不准,尤其在商场内,基于 WiFi 的定位基本上能让你的位置到处跳。Pass!

基于距离优化

这个方案的思路是:根据你在虚拟世界移动的距离分段记录路径。

实践之后问题也来了:ARKit 的初始化太慢了,在我的 iPhone 7 上需要的时间足足有 3 秒…而且 ARKit 的 Seesion 还不能并发,因为摄像头只有一个,只有上一个结束了,下一个才能开始,最后发现路径根本没法记录…

成功优化

ARKit 使用优化

设置 ARWorldTrackingConfiguration 的 worldAlignment 为 .gravityAndHeading。

首先我们来看一下 WorldAlignment 类型:

/**

Enum constants for indicating the world alignment.

*/

@available(iOS 11.0, *)

public enum WorldAlignment : Int {

       

    /** Aligns the world with gravity that is defined by vector (0, -1, 0). */

    case gravity

       

    /** Aligns the world with gravity that is defined by the vector (0, -1, 0)

    and heading (w.r.t. True North) that is given by the vector (0, 0, -1). */

    case gravityAndHeading

       

    /** Aligns the world with the camera’s orientation. */

    case camera

}

.gravity:只有重力,也就是坐标系的垂直方向和真实世界一致,但是水平方向不定。

.gravityAndHeading:重力和指北,垂直和水平方向都和真实世界一致。

.camera:摄像头方向,也就是坐标系和手机保持一致。

所以这就是我们选择 .gravityAndHeading 的原因。这样一来,根据路径找人的那个人就只需要找到路径起始点的真实位置即可,手机的方向就不重要了,极大降低的使用门槛。

提供路径起始点图片

这个优化的内容是:分享路径的时候提供一张起始点的照片。这样拿到路径的人就拿图片和自己所在的场景做一个大致的比对来确定分享路径的人当时的位置,看一下使用效果:

这张照片我们直接用 ARKit 的 sceneView.session.currentFrame 属性获取,如下:

if let currentFrame = sceneView.session.currentFrame, let image = UIImage(pixelBuffer: currentFrame.capturedImage, context:CIContext()) {

}

使用建议

基于上面说的情况,如果你在日常生活中确实想使用 Find me,下面是一些非常重要的使用建议:

如果在记录或者寻路过程中,有新消息,千万不要切出去看,毕竟商场内导航也就是 10 分钟左右的事情,正常晚 10 分钟回复消息也没事。

寻路的人一定要找准路径初始点!非常重要!这个直接决定了路径终点位置的准确度。

其他一些比较有趣的技术点

路径中的箭头实现

先看一下效果:

这个技术点包含两个部分:

怎么绘制箭头?

如果让箭头指向下一个点的位置?

怎么绘制箭头?

首先,我们生成一个如下图形状的 UIBezierPath:

代码如下:

private static func vertexCoordinates() -> [CGPoint] {

    return [CGPoint(x: 0, y: 0),

            CGPoint(x: 20, y: 0),

            CGPoint(x: 20, y: 10),

            CGPoint(x: 10, y: 10),

            CGPoint(x: 10, y: 20),

            CGPoint(x: 0, y: 20)

    ]

}

   

private static func arrowPath() -> UIBezierPath {

    let path = UIBezierPath()

    let points = NodeUtil.vertexCoordinates()

       

    var count = 0

    for point in points {

        if 0 == count {

            path.move(to: point)

        } else {

            path.addLine(to: point)

        }

        count += 1

    }

       

    path.close()

       

    return path

}

然后用下面的代码生成 SCNNode:

let path = NodeUtil.arrowPath()

let shape = SCNShape(path: path, extrusionDepth: 2)

let node = SCNNode(geometry: shape)

这样一个箭头节点就生成了。

如果让箭头指向下一个点的位置?

其实就是要把这个箭头旋转一定的角度。这里涉及到一个数学知识:根据两个点算它们的连线与某个坐标轴的角度。我数学不好,原理就不讲了,主要讲一下 SceneKit 中如何旋转 SCNNode:

首先我们需要两个点,当前点和上一次的点,所以我们通过一个 SCNVector3 类型的 last 变量来记录上一次的点,SCNVector3 包含 x、y、z 三个属性,分别对应了 x、y、z 轴的值。

然后根据当前点和上一次点的位置得到角度,用 SCNAction.rotateBy 来生成一个旋转动作,再使用 SCNNode 的 runAction 方法来执行这个动作即可。

最终代码如下:

node.runAction(SCNAction.rotateBy(x: CGFloat(Float.pi/2.0*3.0), y:CGFloat(Float.pi/4.0+atan2(current.x-last.x, current.z-last.z)), z: 0.0, duration: 0.0))

文件分享的一个坑

Find me 目前分享路径采用的方式是文件分享,分享出去采用的是 UIDocumentInteractionController,接受别人分享的路径主要通过 func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool 回调获取,这里它会通过 url 参数返回分享文件的路径,这里有个点,如果你直接打开这个文件,会发现这个文件并不存在…

解决方法比较有趣:通过这个路径把文件拷贝到另外一个路径,在打开这个文件就可以了。

总结

ARKit 做为 Apple 新发布的框架,后期一定会进行更深入的优化,所以 Find me 路径的准确度未来还是很值得期待的,当然我也会对 Find me 做持续的改进,欢迎大家关注。

另外个人感觉 ARKit 框架的学习门槛确实不高,主要门槛反而在 SceneKit 或者 SpriteKit 上,大家有兴趣也可以看看张嘉夫大佬的一些教程,质量很高。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 因为不懂社会的现实和残酷 因为经验不足、想法不完善 我们总被说太不成熟了 一个人成熟的标志是什么? 成熟的标准在哪...
    樂鈫阅读 246评论 0 0
  • 每一次见面 你都会用健康的那只手 攥着我的手 把温暖传递给我 在外奔走的寒凉啊 在那一刻不知所终 每一次见面 都殷...
    明沙水阅读 97评论 1 2
  • 继续接着前几坐火车的遇到的一位回民和一位农民工的经历,今天来谈农名工的感慨。 这位农民工是湖南岳阳人,家住洞庭湖边...
    山人Shan阅读 461评论 0 0
  • 通过 while 循环的方式,简单实现 C 语言的二分查找。 运行验证如下: key 在左侧 key 超出左侧 k...
    Jiubao阅读 623评论 0 0