ARKit和CoreLocation:第一部分

image.png

演示代码
ARKit和CoreLocation:第一部分
ARKit和CoreLocation:第二部分
ARKit和CoreLocation:第三部分

背景

自从我写了一篇新的博客帖子以来,已经有一段时间了,这有所不同。这篇文章和下一篇文章是关于我使用ARKitCoreLocation进行实验的两部分系列!第一部分将介绍ARKit的基础知识,从MapKit获取方向以及触摸矩阵变换的基础知识。在第二部分将讨论计算两个位置,以及如何利用位置数据,并变换成在ARKit场景的位置之间的轴承。

介绍

image.png

提到“增强现实”,跳入大多数人头脑的第一件事是PokemonGO。如果你像大多数人一样,你可能已经玩了一两次(或者说是痴迷。)PokemonGO证明了在设置时,没有什么能比我们的世界更好。像PokemonGO一样令人敬畏,它只是对增强现实体验的深度和潜力的一瞥。

Apple文档

增强现实(AR)描述了用户体验,这些体验将2D或3D元素从设备的相机添加到实时视图中,使得这些元素看起来居住在现实世界中。ARKit结合了设备运动跟踪,摄像机场景捕捉,高级场景处理和显示便利性,简化了构建AR体验的任务。

iOS 11中,Apple已经将ARKit的强大功能释放到了iOS开发社区。我们还有几个星期的iOS 11上线,但我们已经看到的可能会重新定义移动用户体验的可能性。

首先,一些基础知识

个人项目 - 8月20日

那么,它真的很棒吗?我不想成为那个说这个的人,但不是,这只是数学。所以,如果它不是魔术,他们怎么把它拉下来?视觉惯性测距!(快说十倍。)

定义

视觉惯性测距(VIO):ARKit分析手机摄像头和运动数据,以便跟踪周围的世界。计算机视觉记录了环境中的显着特征,无论iPhone的移动如何,都能够保持对现实世界中位置的了解。

Apple非常喜欢围绕会话组织代码。会话是一种封装应用程序活动的定义时段内包含的逻辑和数据的方法。使用URLSession时,这是应用程序发送网络请求并接收数据作为回报时的逻辑和数据。

ARSession在ARKit中,ARSession协调创建增强现实体验所需的逻辑和数据。这包括摄像机和运动数据以及在周围移动时跟踪世界所需的计算。

ARFrame:** ARFrame**包含视频帧数据和位置跟踪数据,这些数据将传递到currentFrame属性中的ARSession。ARKit将图像数据与运动跟踪数据结合起来,以计算iPhone的位置。

[ARAncho - R ****:一种ARAnchor是在保持了不管相机(理论上)的运动或位置的真实世界的位置。它固定在一个特定的位置,并且大部分将保留在那里。

ARConfiguration

image.png

ARWorldTrackingConfiguration用于跟踪设备方向,位置和检测相机记录的特征点(如相机记录的表面)的配置。ARConfigurations根据摄像机和运动数据,将您和手机所在的物理世界与手机生成的虚拟坐标空间相连接。

worldAlignment:****ARSession上的worldAlignment属性定义ARSession如何在3D坐标映射系统上解释ARFrame的运动数据,该系统用于跟踪世界并构建增强现实体验。

image.png

worldAlignment - Apple Docs

创建AR体验取决于能够构建用于将对象放置在虚拟3D世界中的坐标系,该虚拟3D世界映射到设备的真实位置和运动。运行会话配置时,ARKit会根据设备的位置和方向创建场景坐标系; ARAnchor您创建的任何对象或AR会话检测到的对象都是相对于该坐标系定位的

重力:通过将对齐设置为重力, ARKit将y轴与重力平行对齐,z轴和x轴沿着设备的原始方向定向
!

image.png

worldAlignment.gravity - Apple Docs

首次运行会​​话配置时设备的位置和方向决定了坐标系的其余部分:对于z轴,ARKit选择(0,0,-1)指向设备摄像机面向和垂直于重力轴的方向的基矢量。ARKit使用右手规则选择基于z轴和y轴的x轴 - 也就是说,基矢量(1,0,0)与其他两个轴正交,并且(对于在负z方向上看的观察者)指向正确的。
gravityAndHeading:通过将对齐设置为**gravityAndHeading ** ARKit将y轴与重力平行对齐,z轴和x轴朝向罗盘方向。原点位于设备的初始位置。虽然这在大多数情况下都是准确的,但它的精确度并不高得多,因此创建沉浸式增强现实体验同时完全依赖于这些数据可能会非常棘手。

资源

worldAlignment.gravityAndHeading - Apple Docs

虽然此选项固定方向的三个坐标轴,以真实世界的方向,该位置的坐标系的原点仍是相对于设备,匹配当会话配置是第一次运行的设备的位置。

SceneKit

关于ARKit最酷的事情之一是它与Apple现有的图形渲染引擎很好地集成:SpriteKit,Metal和SceneKit。我最常用的是SceneKit,它用于渲染3D对象。

个人项目 - 8月11日

定义

ARSCNView ARSCNView是SCNView的子类,它是用于渲染3D内容的标准SceneKit视图。因为它专门用于ARKit,它具有一些非常酷的功能。例如,它可以无缝访问手机的相机。甚至更酷,视图的SceneKit场景的世界坐标系统直接响应由会话配置建立的AR世界坐标系。它还会自动移动SceneKit相机以匹配iPhone的实际移动。

image.png

ARSCN查看文档

因为ARKit自动将SceneKit空间与现实世界匹配,所以放置虚拟对象以使其看起来保持真实世界位置只需要适当地设置该对象的SceneKit位置。

您不一定需要使用ARAnchor该类来跟踪添加到场景中的对象的位置,但通过实现ARSCNViewDelegate方法,您可以将SceneKit内容添加到ARKit自动检测到的任何锚点。

将节点添加到场景

https://developer.apple.com/documentation/scenekit/scnsphere

在我们继续之前,让我们先做一些基本的事情。让我们构建我们的第一个增强现实体验!为此,我们将在相机前放置1米蓝色球。

定义

SCNSphere一个球体定义一个表面,其每个点与其中心等距离,该中心位于其局部坐标空间的原点。您可以使用其 radius 属性在所有三个维度中定义球体的大小。

SCNGeometry可以在场景中显示的三维形状(也称为模型或网格),附加材料定义其外观。

SphereNode球形码

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {
    
   @IBOutlet var sceneView: ARSCNView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // setup
    }
    
    // viewWillAppear & viewWillDisappear
    
    func createSphereNode(with radius: CGFloat, color: UIColor) -> SCNNode {
        let geometry = SCNSphere(radius: radius)
        geometry.firstMaterial?.diffuse.contents = color
        let sphereNode = SCNNode(geometry: geometry)
        return sphereNode
    }
}
sceneView.scene = SCNScene()        
// Add Scene
let circleNode = createSphereNode(with: 0.2, color: .blue)  
// Add Sphere
     
circleNode.position = SCNVector3(0, 0, -1) // 1 meter in front 
// Give sphere position    
   
sceneView.scene.rootNode.addChildNode(circleNode)
// Add to scene as childNode of rootNode

把它放在一起

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        sceneView.delegate = self
        sceneView.showsStatistics = true
        sceneView.scene = SCNScene()
        let circleNode = createSphereNode(with: 0.2, color: .blue)
        circleNode.position = SCNVector3(0, 0, -1) // 1 meter in front of camera 
        sceneView.scene.rootNode.addChildNode(circleNode)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let configuration = ARWorldTrackingConfiguration()
        sceneView.session.run(configuration)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        sceneView.session.pause()
    }
    
    func createSphereNode(with radius: CGFloat, color: UIColor) -> SCNNode {
        let geometry = SCNSphere(radius: radius)
        geometry.firstMaterial?.diffuse.contents = color
        let sphereNode = SCNNode(geometry: geometry)
        return sphereNode
    }
}

SceneKit和ARKit坐标以米为单位进行量化。当我们将SCNVector3上的最后一个属性设置为-1时,我们将z轴设置为摄像机前面的一米。如果一切按计划进行(应该),屏幕将显示如下内容:

image.png

目前这种方法很好。我们的球体将自动显示为跟踪真实世界的位置,因为ARKit将SceneKit空间与真实世界空间相匹配。如果我们想要使用坐标,我们可能需要找到一些持久的东西来锚定提示我们的节点将来。

向量,矩阵和线性代数,哦不!

一个二乘四的矩阵

如果你还记得回到数学课,那么矢量有一个幅度和方向。

在数学,物理和工程中,欧几里德矢量(有时称为几何或空间矢量,或者 - 在这里 - 简称矢量)是具有幅度(或长度)和方向的几何对象。

在编程时,矢量只是一个数字数组。每个数字是向量的“维度”。

简单地说,我们的向量使用2乘1矩阵。让我们给它一个x = 1的值。矢量(1,0)的图形看起来像:

image.png

我们可以在一个非常简单的矩阵中表达相同的向量(1,0):

![X超过Y(https://upload-images.jianshu.io/upload_images/910914-131876c1ac3bc24b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

如上所述:

向量只是一个数字数组

如您所见,矩阵看起来类似于数字数组。虽然它们看起来很吓人,但是在你练习之后,矩阵是一个非常简单的概念并且很容易使用。

OpenGL的定义

简而言之,矩阵是一个数字数组,具有预定义的行数和列数

矩阵用于变换3D坐标。这些包括:

  • 旋转(改变方向)
  • 缩放(大小更改)
  • 变换(移动位置)

转换

在大多数情况下,转换点可以用以下等式表示:

Transformed Point = Transformation Matrix × Original Point

转换点=转换矩阵×原点

如果您在看过CGAffineTransform之前就已经使用过CoreGraphics了。它制作了一些非常酷的动画。实际上,CGAffineTransform只是一种不同类型的矩阵变换。

仿射变换是一种保留点,直线和平面的线性映射方法。

资源

旋转太空船

image.png

让我们试试转型吧!虽然这与它们用于位置节点的方式不同,但它们足够接近,您可以开始考虑实际应用的原则。为此,使用SceneKit创建一个新的ARKit项目。当你运行它时,屏幕前应该有一个漂浮在你面前的屏幕截图。

环-T-循环

在viewDidLoad下面添加以下行:

class ViewController: UIViewController, ARSCNViewDelegate {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        sceneView.scene.rootNode.childNodes[0].transform = SCNMatrix4Mult(sceneView.scene.rootNode.childNodes[0].transform, SCNMatrix4MakeRotation(Float(Double.pi) / 2, 1, 0, 0))
        sceneView.scene.rootNode.childNodes[0].transform = SCNMatrix4Mult(sceneView.scene.rootNode.childNodes[0].transform, SCNMatrix4MakeTranslation(0, 0, -2))
    }
}

现在当你重新运行它时,宇宙飞船应该仍然出现在你的屏幕上,但是,当你点击它时,它应该循环。继续敲击它直到它回到它开始的位置(粗略地)。

image.png

这是完整的ViewController代码:

class ViewController: UIViewController, ARSCNViewDelegate {
    
    @IBOutlet var sceneView: ARSCNView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Set the view's delegate
        sceneView.delegate = self
        
        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true
        
        // Create a new scene
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
        sceneView.scene = scene
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        // Create a session configuration
        let configuration = ARWorldTrackingConfiguration()
        
        // Run the view's session
        sceneView.session.run(configuration)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        sceneView.scene.rootNode.childNodes[0].transform = SCNMatrix4Mult(sceneView.scene.rootNode.childNodes[0].transform, SCNMatrix4MakeRotation(Float(Double.pi) / 2, 1, 0, 0))
        sceneView.scene.rootNode.childNodes[0].transform = SCNMatrix4Mult(sceneView.scene.rootNode.childNodes[0].transform, SCNMatrix4MakeTranslation(0, 0, -2))
    }
}

如果一切按照计划进行,经过几次触摸后,您的宇宙飞船看起来就会变得光彩夺目:

`

导航

现在我们已经对ARKit的基础知识进行了一些处理,让我们继续进行导航和定位服务。如果我们希望被引导到目的地,我们需要导航服务的一些帮助。

MapKit带有方便的转弯指示API。使用CoreLocation目标和MKDirectionsRequest,我们可以获得一系列导航步骤,将我们引导到特定位置。

import MapKit
import CoreLocation

struct NavigationService {
    
   func getDirections(destinationLocation: CLLocationCoordinate2D, request: MKDirectionsRequest, completion: @escaping ([MKRouteStep]) -> Void) {
        var steps: [MKRouteStep] = []
       
        let placeMark = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: destinationLocation.coordinate.latitude, longitude: destinationLocation.coordinate.longitude))
       
        request.destination = MKMapItem.init(placemark: placeMark)
        request.source = MKMapItem.forCurrentLocation()
        request.requestsAlternateRoutes = false
        request.transportType = .walking

        let directions = MKDirections(request: request)
        
        directions.calculate { response, error in
            if error != nil {
                print("Error getting directions")
            } else {
                guard let response = response else { return }
                for route in response.routes {
                    steps.append(contentsOf: route.steps)
                }
                completion(steps)
            }
        }
    }
}

定义

MKPlacemark** s:**包含与特定坐标相关联的城市,州,县或街道地址等信息。

MKRoute请求的起点和终点之间的单一路线。MKRoute对象定义路线的几何形状 - 即,它包含与特定地图坐标相关联的线段。路线对象还可以包括其他信息,例如路线的名称,距离和预期的行驶时间。

print(route.name)
// Broadway
print(route.advisoryNotices)
// []
print(route.expectedTravelTime)
// 2500.0

MKRouteStep是路线的一个部分。每个步骤都包含一条指令,应由用户在两点之间导航完成,以便他们成功完成路线。

print(step.distance)
// 1.0
print(step.instructions)
// Proceed to 7th Ave

MKMapItem地图上的兴趣点。地图项目包括地理位置和可能适用于该位置的任何有趣数据,例如该位置的地址和该地址的企业名称。

MKDirections一个实用程序对象,它根据您提供的路径信息计算方向和行程时间信息。

import SceneKit
import ARKit
import CoreLocation
import MapKit

class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate, LocationServiceDelegate {
    
    @IBOutlet weak var sceneView: ARSCNView!
    
    var steps: [MKRouteStep] = []
    var destinationLocation: CLLocationCoordinate2D!
    var locationService = LocationService()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        locationService.delegate = self
        var navService = NavigationService()
        
        self.destinationLocation = CLLocationCoordinate2D(latitude: 40.737512, longitude: -73.980767)
        var request = MKDirectionsRequest()
        
        if destinationLocation != nil {
            navService.getDirections(destinationLocation: destinationLocation, request: request) { steps in
                for step in steps {
                    self.steps.append(step)
                }
            }
        }
    }
}

资料来源:

medium.com - Yat Choi

aviation.stackexchange.com

github.com/ProjectDent/ARKit-CoreLocation

movable-type.co.uk/scripts/latlong.html

gis.stackexchange.com

opengl-tutorial.org

math.stackexchange.com

原文:https://medium.com/journey-of-one-thousand-apps/arkit-and-corelocation-part-one-fc7cb2fa0150
Christopher Webb-Orenstein

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

推荐阅读更多精彩内容

  • 引言ARKit 为开发 iPhone 和 iPad 增强现实(AR)app 提供了一个前沿平台。本文为你介绍 AR...
    蚂蚁安然阅读 8,926评论 0 14
  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    X先生_未知数的X阅读 15,937评论 3 118
  • 那个人明明嘴里不文明, 却能时时刻刻有人维护。 你被人打压着说丑小鸭, 而无人能理解你的痛苦。 上学上班坏事永远是...
    陶陶丫头阅读 182评论 0 6
  • 很久不留心情了,朋友圈碎片化的表达几乎快让我丧失了写文能力,今天捡起心情,是觉得有些路还是要记的,有些人还是要谢的...
    未红的骆驼刺阅读 281评论 0 0
  • 学习 昨天读成甲的好好学习,学习到彩虹笔记法,用三种颜色标记,红色代表有启示重要的句子,绿色代表案例...
    Lisa的小世界阅读 161评论 0 0