FantasmoSDK 2.3.4

FantasmoSDK 2.3.4

Lucas KuzmaNick Jensen 维护。



  • 作者
  • Nick Jensen

Fantasmo-iOS-SDK

概览

使用摄像头仅通过相机即可加速应用程序的位置定位。Fantasmo SDK 是进入摄像头定位系统 (CPS) 的门户,为移动设备提供 6 个自由度(位置和方向)定位。

功能

基于摄像头的定位是从图像确定设备摄像头的全局位置的过程。从活动的 ARSession 获取图像帧并发送到服务器进行计算。服务器计算时间约为 900 毫秒。然后,往返时间由连接延迟决定。

由于在捕获图像帧的那一刻后,摄像头可能发生移动,因此有必要在定位过程中持续跟踪设备运动,以确定响应时的设备位置。跟踪由 ARSession 提供。方便的是,随后可以在跟踪会话中的任何时间点确定设备的全局位置,无论图像是在何时捕获的(尽管在过度运动后,可能有些偏移)。

安装

文件管理器(CocoaPods iOS 11及以上)

CocoaPods是一个用于Cocoa项目的依赖管理工具。有关使用和安装说明,请访问https://cocoapods.org.cn/。要在Xcode项目中使用CocoaPods集成Fantasmo SDK,请在Podfile中指定它

pod 'FantasmoSDK'

您的Podfile也应该在顶部包含一行use_frameworks!

Cartfile(iOS 8+, OS X 10.9+)

您可以使用Cartfile将Fantasmo SDK添加到您的项目中以使用Carthage进行安装

  • 通过运行brew install carthage来获取Carthage

  • 创建一个Cartfile,使用https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile中的链接,在您的.xcodeproj或.xcworkspace所在的相同目录下创建。例如

    github "fantasmoio/Fantasmo-iOS-SDK" ~> 2.0.0

  • 通过解压缩Carthage.sh.zip来安装Carthage.sh并将其放置在您的.xcodeproj或.xcworkspace相同的目录下。

  • 通过chmod +x Carthage.sh给Carthage.sh赋予可执行权限

  • 注意:您可能需要更新Carthage.sh文件中的XCode版本号。它应该能够与XCode 12直接配合使用,但对于XCode 13,请更新以下部分

... __XCODE__1200__BUILD_ ...

... __XCODE__1300__BUILD_ ...

  • 通过将以下命令粘贴到终端来运行Carthage

    ./Carthage.sh update --platform iOS

  • 通用选项卡中,滚动到页面底部,您会看到链接框架和库。在Xcode项目窗口依然打开的情况下,打开一个Finder窗口并导航到项目目录。在项目目录中,打开以下文件夹:Carthage/Build/iOS。在iOS文件夹中,你应该能看到< strong>FantasmoSDK.framework。将框架拖动到项目的< strong>链接框架和库部分,并从嵌入选项中选择< strong>不嵌入。

  • 在你的应用程序目标构建阶段设置选项卡中,点击+图标并选择New Run Script Phase。创建一个运行脚本,在shell下面指定您的shell(例如:/bin/sh),并将以下内容添加到脚本区域

    /usr/local/bin/carthage copy-frameworks

  • 在上述运行脚本的< strong>输入文件中添加以下文件
    $(SRCROOT)/Carthage/Build/iOS/FantasmoSDK.framework

  • 在上述运行脚本的< strong>输出文件中添加以下文件
    $(DERIVED_FILE_DIR)/$(FRAMEWORKS_FOLDER_PATH)/FantasmoSDK.framework

导入

短期内,Fantasmo SDK将以Cocoapod的形式提供。在此期间,可以直接将Fantasmo SDK目录导入到项目中。

访问令牌

通过将其添加到应用中的 Info.plist 文件来设置您的 Fantasmo 访问令牌。

<key>FM_ACCESS_TOKEN</key>
<string>a0fc7aa1e1144f1e81eaa2ad47794a9e</string>

您也可以通过 FMConfiguration 类在运行时设置访问令牌。

FMConfiguration.setAccessToken("a0fc7aa1e1144f1e81eaa2ad47794a9e")

注意:通过 FMConfiguration 设置的访问令牌优先于 Info.plist 中的访问令牌。

相机和位置权限

FantasmoSDK 需要访问设备的相机和位置信息。因此,您需要将以下键的用法描述添加到应用程序的 Info.plist 文件中。

NSCameraUsageDescription
NSLocationWhenInUseUsageDescription

要求

  • iOS 11.0+
  • Xcode 11.0+
  • ARKit
    • iPhone 6s, 6s Plus, SE 或更高版本
    • iPad/iPad Mini 第 5 代或更高版本
    • iPad Pro(9.7英寸、10.5英寸或12.9英寸)
    • iPod touch 第 7 代或更高版本

用法

快速开始

要使用 Fantasmo SDK 停车和定位,您首先应该检查用户当前位置是否有停车可用。您可以通过调用静态方法 FMParkingViewController.isParkingAvailable(near:) 并传递一个 CLLocation 来完成此操作。结果回调块用一个布尔值调用,指示用户是否靠近已映射的停车位。

import FantasmoSDK

FMParkingViewController.isParkingAvailable(near: userLocation) { isParkingAvailable in
    if !isParkingAvailable {
        print("No mapped parking spaces nearby.")
        return
    }
    // Safe to start parking flow
    self.startParkingFlow()
}

上述方法还检查设备是否支持 ARKit。如果不支持,则立即用 false 调用 completion 块。

// SomeViewController.swift

func startParkingFlow() {            
    // construct a new parking view controller with a sessionId
    let sessionId = UUID().uuidString
    let sessionTags = ["berlin", "e-scooter"]  // optionally add tags
    let parkingViewController = FMParkingViewController(sessionId: sessionId, sessionTags: sessionTags)

    // configure delegation
    parkingViewController.delegate = self

    // present modally to start
    parkingViewController.modalPresentationStyle = .fullScreen
    self.present(parkingViewController, animated: true)
}

提供 sessionIdsessionTags

参数 sessionId 允许您将本地化结果与您自己的会话标识符关联起来。通常这是一个UUID字符串,但也可以遵循您自己的格式。例如,一辆摩托车停车会话可能需要进行多次定位尝试。出于分析和计费的目的,此标识符允许您将多个尝试与单个停车会话关联起来。当前字符串长度限制为64个字符。

sessionId 类似,您还可以提供 sessionTags 列表。此可选参数可用于标记和分组具有共同特征的用户停车会话。例如,在同一天城市发生的事件可能会有以城市名称为标记的会话。这些仅用于分析目的,并将包含在您的使用报告中。每个标签必须是字符串,并且会话可以拥有的标签数量目前没有限制。

提供位置更新

默认情况下,在本地化过程中,FMParkingViewController 内部使用 CLLocationManager 以获取设备位置的自动更新。如果您想提供自己的 CLLocation 更新,可以将 usesInternalLocationManager 属性设置为 false,并手动调用 updateLocation(_ location: CLLocation) 以每个更新到位置的更新。

let parkingViewController = FMParkingViewController(sessionId: sessionId)

// disables the internal CLLocationManager
parkingViewController.usesInternalLocationManager = false
self.present(parkingViewController, animated: true)

// create your own CLLocationManager
let myLocationManager = CLLocationManager()
myLocationManager.delegate = self
myLocationManager.requestAlwaysAuthorization()
myLocationManager.startUpdatingLocation()

extension ViewController: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        // notify the parking view controller of the update
        parkingViewController.updateLocation(locations.last)
    }
}

如果SDK没有收到有效的 CLLocation 更新,无论是来自内部 CLLocationManager 还是手动通过 updateLocation(_ location: CLLocation),则您将收到定位错误。

func parkingViewController(_ parkingViewController: FMParkingViewController,
                               didReceiveLocalizationError error: FMError, errorMetadata: Any?) {
    if (error.type as? FMLocationError) == FMLocationError.invalidCoordinate {
        print("No location updates received.")
    }
}

如果发生这种情况,应检查您是否正确提供了位置更新,或者如果您正在使用内部位置管理器,则用户是否已允许访问设备的地理位置。

二维码

扫描二维码是本地化的第一步也是唯一一步。因为我们试图定位的是车辆而不是设备本身,我们需要一种方法来确定车辆相对于设备的位置。这是通过在 ARSession 中设置锚点来实现的,并在用户扫描二维码时自动完成。

SDK 不关心二维码的内容,默认情况下,任何二维码检测到后都将开始本地化。如果您的应用 确实 关心二维码的内容,可以在您的 FMParkingViewControllerDelegate 中实现以下方法进行验证。

func parkingViewController(_ parkingViewController: FMParkingViewController,
                           didScanQRCode qrCode: CIQRCodeFeature,
                           continueBlock: @escaping ((Bool) -> Void)) {
    // Validate the QR code
    let isValidCode = qrCode.messageString != nil
    
    // Call the continue block with the result
    continueBlock(isValidCode)
    
    // Alternatively, validation can be done asynchronously
    APIService.validateQRCode(qrCode) { isValidCode in
        continueBlock(isValidCode)
    }
}

手工输入QR码

如果在扫描过程中遇到问题,用户可以手动输入QR码。使用默认的QR码扫描UI时,此功能已为您实现。只需轻触“手动输入”按钮,并在提示中输入代码。如果您使用的是自定义UI,那么您应该提示用户输入代码,并将字符串传递给您的停车视图控制器的enterQRCode(string:)方法。

手动输入的QR码验证也是可选的,与扫描验证的工作方式相同。在您的FMParkingViewControllerDelegate中实现以下方法。

func parkingViewController(_ parkingViewController: FMParkingViewController,
                           didEnterQRCodeString qrCodeString: String,
                           continueBlock: @escaping ((Bool) -> Void)) {
    // Validate the entered QR code
    let isValidCode = qrCodeString.isEmpty == false
    
    // Call the continue block with the result
    continueBlock(isValidCode)
    
    // Alternatively, validation can be done asynchronously
    APIService.validateQRCode(qrCode) { isValidCode in
        continueBlock(isValidCode)
    }
}

重要: 如果您实现了任何QR码验证方法,您必须使用布尔值调用continueBlock。值为true表示QR码有效,应开始本地化。将false传递给此块表示代码无效,允许用户扫描或输入新的代码。此块可以是同步调用或异步调用,但必须在主队列上执行。

禁用QR码

虽然我们推荐使用QR码扫描器,但这不是必需的。如果您不希望使用QR码扫描器,可以将其禁用,方法是将qrCodeScannerEnabled属性设置为false。请注意,必须在向用户展示视图控制器之前执行此操作。

    // disable the QR code scanner
    parkingViewController.qrCodeScannerEnabled = false

通常在车辆上扫描QR码时,在AR会话中设置一个锚点。这个锚点允许Fantasmo即使用户走开也能定位车辆。但是,如果QR码扫描器被禁用,这个锚点会在本地化时尽可能早地设置。如果用户已经移动,这可能会导致锚点不是精确位于车辆的位置。

本地化

在本地化过程中,会持续捕获帧并发送至服务器。SDK中的过滤逻辑将自动选择最佳帧,并向用户发出行为请求以帮助改进接收到的图像。在连续更新中,位置结果的可信度会增加,客户端可以选择在达到所需可信度水平时通过关闭视图来停止本地化。

func parkingViewController(_ parkingViewController: FMParkingViewController,
                           didReceiveLocalizationResult result: FMLocationResult) {
    // Got a localization result
    if result.confidence == .low {
        return
    }
    let coordinate = result.location.coordinate
    print("Coordinate: \(coordinate.latitude), \(coordinate.longitude)")
    // Medium or high confidence, dismiss to stop localizing
    parkingViewController.dismiss(animated: true, completion: nil)
}

可能会出现本地化错误,但本地化过程不会停止,仍然有可能得到成功的本地化结果。您应该决定一个可接受的错误阈值,并在达到该阈值时停止本地化,再次通过关闭视图来完成。

func parkingViewController(_ parkingViewController: FMParkingViewController,
                           didReceiveLocalizationError error: FMError, errorMetadata: Any?) {
    // Got a localization error
    errorCount += 1
    if errorCount < 5 {
        return
    }
    // Too many errors, dismiss to stop localizing
    parkingViewController.dismiss(animated: true, completion: nil)
}

截至目前,行为请求仅提供英语版本。更多语言将很快推出。

定制UI

可以通过创建您自己的视图协议实现来完全自定义扫描二维码和本地化的UI。

public protocol FMQRScanningViewControllerProtocol: UIViewController {
    func didStartQRScanning()
    func didStopQRScanning()
    func didScanQRCode(_ qrCode: CIQRCodeFeature)
}

public protocol FMLocalizingViewControllerProtocol: UIViewController {
    func didStartLocalizing()
    func didRequestLocalizationBehavior(_ behavior: FMBehaviorRequest)
    func didReceiveLocalizationResult(_ result: FMLocationResult)
    func didReceiveLocalizationError(_ error: FMError, errorMetadata: Any?)
}

一旦创建了上述协议的视图控制器,请在显示之前将它们注册到您的FMParkingViewController实例中。

parkingViewController.registerQRScanningViewController(MyCustomQRScanningViewController.self)
parkingViewController.registerLocalizingViewController(MyCustomLocalizingViewController.self)

提示:您自定义的视图是FMParkingViewController的子视图,您可以在自定义视图中访问停车视图。

// MyCustomViewController.swift
let parkingViewController = self.parent as? FMParkingViewController

重要:自定义视图将置于显示实时相机流的视图之上。因此,自定义视图应该是半透明的,或者包含一些透明区域,以便用户可以看到下面的相机。

行为请求

为了帮助用户成功本地化并最大化结果质量,相机输入将针对常见问题进行过滤,并将行为请求显示给用户。这些是说明用户在本地化时应该如何操作设备的消息。例如,如果用户的设备对准了地面,您可能会收到一个"倾斜设备向上"的请求。

如果您使用的是默认的本地化UI,这些请求已经显示给了用户。如果您已注册了自己的自定义UI,您应使用FMLocalizingViewControllerProtocoldidRequestLocalizationBehavior(_ behavior: FMBehaviorRequest)方法来显示这些请求给用户。

class MyCustomLocalizingViewController: UIViewController, FMLocalizingViewControllerProtocol {
  
    @IBOutlet var label: UILabel!
  
    func didRequestLocalizationBehavior(_ behavior: FMBehaviorRequest) {
        // display the requested behavior to the user
        label.text = behavior.description
    }
}

模拟模式

由于并非总是在现场测试,因此提供了模拟模式。记录的模拟可以在FantasmoSDKTestHarness/Videos文件夹中找到,并设置为在停车视图中运行。

parkingViewController.simulation = FMSimulation(named: "parking-session-1")

调试

有时查看底层发生的情况很有用。要查看实时详细的调试信息,可以显示统计视图。

parkingViewController.showsStatistics = true

日志记录

默认情况下,只有错误和警告被记录,但还有其他日志级别可供选择:debuginfowarningerror

parkingViewController.logLevel = .debug

您还可以拦截日志调用,将日志消息发送到自己的分析服务。

parkingViewController.logIntercept = { message in
    sendToAnalytics(message)
}

覆盖

对于测试,设备位置可以在Info.plist中指定。

key: FM_GPS_LAT_LONG
value: 25.762586765198417,-80.19404801110545

对于Fantasmo内部开发和演示构建测试,API服务器URL可以在Info.plist中指定。它不应该包括URI方案。

key: FM_API_BASE_URL
value: 192:168:0:1:8090/v1/image.localize

测试

运行测试

要从命令行运行单元测试,请使用以下命令

xcodebuild test -project FantasmoSDK.xcodeproj -scheme FantasmoSDKTests -destination 'platform=iOS Simulator,OS=latest,name=iPhone 12 Pro Max'

如果您想查看类似于GitHub actions日志中出现的整洁格式版本,将输出通过重定向到xcpretty(您需要单独安装xcpretty)

xcodebuild test -project FantasmoSDK.xcodeproj -scheme FantasmoSDKTests -destination 'platform=iOS Simulator,OS=latest,name=iPhone 12 Pro Max' | xcpretty

如果您希望针对多个目标OS和设备名称运行测试,您可以指定多个。