ARKit:利用摄像头和运动数据在你移动时绘制出周围的世界。
CoreLocation:利用 Wi-Fi 和 GPS 数据确定你的全球位置,精度较低。
ARKit + CoreLocation:结合了 AR 的高精度和 GPS 数据的规模。
结合这些技术的潜力巨大,可以在许多不同领域产生许多潜在的应用。这个库包含两个主要功能
- 允许使用现实世界的坐标在 AR 世界中放置项目。
- 大幅度提升位置精度,通过结合最新的位置数据点以及通过 AR 世界运动的知识。
改进的位置精度目前处于“实验”阶段,但可能是最重要的组件。
因为在这方面以及其他方面仍有工作要做,所以这个项目最好由一个开放的社区来维护,而不仅仅是 GitHub Issues 允许我们的。所以,我正在开设一个任何人都可以加入的 Slack 群组,以讨论这个库及其改进,以及他们自己的工作。
要求
ARKit 需要 iOS 11,并支持以下设备
- iPhone 6S 及以上
- iPhone SE
- iPad(2017)
- 所有 iPad Pro 型号
iOS 11 可从苹果开发者网站下载。
用法
这个库包含了 ARKit + CoreLocation 框架,以及一个类似于 Demo 1 的演示应用程序。
使用 Swift 构建
swift build \
-Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" \
-Xswiftc "-target" -Xswiftc "x86_64-apple-ios12.1-simulator"
使用 Swift Package Manager 设置
使用 CocoaPods 设置
- 将以下内容添加到 podfile 中
pod 'ARCL'
- 在终端中,切换到您的项目文件夹,然后
pod update
pod install
- 将
NSCameraUsageDescription
和NSLocationWhenInUseUsageDescription
添加到 plist 中,并简要说明(请参考示例项目)
手动设置
- 将
ARKit+CoreLocation/Source
目录下的所有文件添加到您的项目中。 - 导入 ARKit、SceneKit、CoreLocation 和 MapKit。
- 将
NSCameraUsageDescription
和NSLocationWhenInUseUsageDescription
添加到 plist 中,并简要说明(请参考示例项目)
快速入门指南
例如,要在伦敦金丝雀码头这样的建筑上放置一个标记,我们将使用 ARCL 构建的类 - SceneLocationView
。
首先,导入 ARCL 和 CoreLocation,然后声明 SceneLocationView 作为属性
import ARCL
import CoreLocation
class ViewController: UIViewController {
var sceneLocationView = SceneLocationView()
}
当它处于焦点时,应该调用 sceneLocationView.run()
,如果它被中断(例如移动到不同的视图或离开应用程序),则应调用 sceneLocationView.pause()
。
override func viewDidLoad() {
super.viewDidLoad()
sceneLocationView.run()
view.addSubview(sceneLocationView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sceneLocationView.frame = view.bounds
}
在调用run()
之后,我们可以添加我们的坐标。ARCL SDK中有一个名为LocationNode
的类——3D场景中的一个对象,它具有真正的地理位置和其他一些属性,使得它可以在世界中适当地显示。LocationNode
是SceneKit库中的SCNNode
的子类,还可以进一步进行子类化。在这个例子中我们将使用一个名为LocationAnnotationNode
的子类,我们使用它来在世界中显示一张2D图像,它总是面向我们
let coordinate = CLLocationCoordinate2D(latitude: 51.504571, longitude: -0.019717)
let location = CLLocation(coordinate: coordinate, altitude: 300)
let image = UIImage(named: "pin")!
let annotationNode = LocationAnnotationNode(location: location, image: image)
LocationAnnotationNode
也可以使用UIView进行初始化。内部,UIView被转换成了UIImage,因此你无法动态更新内容。然而,此方法允许你轻松地以POI的形式显示复杂的布局。
let coordinate = CLLocationCoordinate2D(latitude: 51.504571, longitude: -0.019717)
let location = CLLocation(coordinate: coordinate, altitude: 300)
let view = UIView() // or a custom UIView subclass
let annotationNode = LocationAnnotationNode(location: location, view: view)
它还可以用CALayer进行初始化。当你想要实时更新内容时可以使用这个方法。
let coordinate = CLLocationCoordinate2D(latitude: 51.504571, longitude: -0.019717)
let location = CLLocation(coordinate: coordinate, altitude: 300)
let layer = CALayer() // or a custom CALayer subclass
let annotationNode = LocationAnnotationNode(location: location, layer: layer)
默认情况下,你设置的图片应该始终显示为其给定的尺寸,例如如果给你一个100x100的图片,它将显示为100x100。这意味着远程标注节点总是以与近距离节点相同的大小显示。如果你希望它们按距离进行缩放,可以将LocationAnnotationNode的scaleRelativeToDistance
设置为true
。
sceneLocationView.addLocationNodeWithConfirmedLocation(locationNode: annotationNode)
有两种方式可以向场景中添加位置节点——使用addLocationNodeWithConfirmedLocation
,或者使用addLocationNodeForCurrentPosition
,后者将其定位在与世界内设备相同的位置,然后给它一个坐标。
所以,这就完成了。如果你设置了sceneLocationView的边框,现在你应该能看到而是在Canary Wharf上方的针标。
为了在sceneLocationView
中被触发的节点时获取通知,你需要在ViewController类中遵守LNTouchDelegate
。
annotationNodeTouched(node: AnnotationNode)
让你可以访问在屏幕上被触摸的节点。AnnotationNode
是SCNNode的一个子类,有两个额外的属性:image: UIImage?
和view: UIView?
。这两个属性将根据如何初始化LocationAnnotationNode
(使用接受UIImage或UIView的构造函数)来填充。
locationNodeTouched(node: LocationNode)
则提供从PolyNode
(例如,MKRoute
的渲染方向)创建的节点的访问。
class ViewController: UIViewController, LNTouchDelegate {
override func viewDidLoad() {
super.viewDidLoad()
//...
self.sceneLocationView.locationNodeTouchDelegate = self
//...
}
func annotationNodeTouched(node: AnnotationNode) {
// Do stuffs with the node instance
// node could have either node.view or node.image
if let nodeView = node.view{
// Do stuffs with the nodeView
// ...
}
if let nodeImage = node.image{
// Do stuffs with the nodeImage
// ...
}
}
func locationNodeTouched(node: LocationNode) {
guard let name = node.tag else { return }
guard let selectedNode = node.childNodes.first(where: { $0.geometry is SCNBox }) else { return }
// Interact with the selected node
}
}
附加功能
库和示例都附带了一组额外的配置功能。它们都有充分的文档说明,一定要看看。
SceneLocationView是ARSCNView的子类。请注意,尽管这让你完全能够在其他方式中使用ARSCNView,你不应该将其委托设置到另一个类。如果你需要使用委托功能,应该子类化SceneLocationView
。
真北校准
我个人尚未解决的一个问题是,iPhone的真正北方校准在最佳情况下只能达到15度的精度。这对于地图导航来说还可以,但是当在AR世界中放置东西时,它就变成了一个问题。
我相信可以通过使用各种AR技术来解决这个难题——我认为这是一个可以从共同努力中真正受益的领域。
为了提高这个问题,我在库中添加了一些功能,可以调整北方。
sceneLocationView.moveSceneHeadingClockwise
sceneLocationView.moveSceneHeadingAntiClockwise
sceneLocationView.resetSceneHeading
您应该使用这些功能,通过设置sceneLocationView.useTrueNorth
为false
,然后在开始之前将设备指向大致的北方,以便它足够接近。当useTrueNorth
设置为true(默认值)时,它会不断调整,直到对北方有一个更好的感觉。
在演示应用中,有一个名为adjustNorthByTappingSidesOfScreen
的可禁用属性,它访问这些功能,并在启用后,允许用户轻击屏幕的左右两边来调整场景航向。
我的建议是找到附近的标志性建筑,从您的位置直接指向真正的北方,使用坐标在那里放置一个物体,然后使用moveSceneHeading
函数调整场景,直到它们对齐。
改进的定位精度
CoreLocation可以在每1-15秒之间提供位置更新,精度从150米到4米不等。有时,您会收到更精确的读数,如4米或8米,然后再返回到更不精确的读数。同时,AR使用运动和相机数据创建当地世界的地图。
用户可能会收到一个精确度为4米的位置读数,然后他们向北走10米,并接收到另一个精确度为65米的位置读数。这个65米精确度读数是CoreLocation能提供的最佳读数,但如果我们知道用户在收到4米读数时的AR场景中的位置,并且他们已经通过场景向北移动了10米,我们可以将这份数据转换为提供一个新坐标,精度约为4米。这大约精确到100米。
问题
我提到这是一项实验性的技术——目前,ARKit在用户在场景中行走时可能会偶尔混乱,并且可能会不准确地更改其位置。这个问题似乎还影响了设备的“欧拉角”,或方向信息,因此经过短暂的距离后,它可能认为您正走在不同的方向。
尽管苹果可以通过时间改进ARKit,但我认为我们可以通过识别这种情况并努力更正它来避免这些问题,例如,通过与我们的预计位置比较位置数据,以确定我们是否已经移出可能的边界。
位置算法改进
还可以进一步优化确定用户位置的方法。
例如,一种技术可以是查看最近的位置数据,使用用户自那时以来的旅行将每个数据点进行转换,并使用数据点之间的重叠来更精确地确定用户的可能位置。
未来方向
我们有与它们相关的一些里程碑和问题 - 欢迎任何人讨论和贡献。欢迎 Pull 请求。您可以通过添加新问题或通过以下链接加入 Slack 社区来讨论新的特性、增强或错误。
感谢
由 @AndrewProjDent 创建的库,但此后成为社区努力的结果。
根据MIT许可协议开源。