Swift: ARKit Save ARPlaneAnchor for next session

ARKit

is brand new and I am brand new in fast ... So I am having problems ...

I want to keep the ones ARPlaneAnchor

discovered during the session and reload them when I restart my application. My phone will always be in one place and I would like to scan the number once. And I remember the Anchor I found in the room every time I launch the app.

I've tried several solutions:

Solution1: Save ARPlaneAnchor using:NSKeyedArchiver.archiveRootObject(plane, toFile: filePath)

I got this error:

Application terminated due to uncaught "NSInvalidArgumentException", reason: '- [ARPlaneAnchor encodeWithCoder:]: unrecognized selector posted to instance

I think that maybe I cannot save data like this locally

Solution2: Save the data ARPlaneAnchor

, then intrigue it when the application starts. the data is mostly floating. I could create ARAnchor

easily, I could use them like ARPlaneAnchor

, but I could not change the parameter "center" and "extend" ARPlaneAnchor

because they only have a getter and not a setter. Therefore, I cannot create good anchors.

I am open to any solution. I think I need to keep the ARAnchor object, but so far I haven't been able to find a way to do it without crashing! So if someone can help me I would be very grateful.

+3


source to share


2 answers


First ... if your app is limited to a situation where the device is permanently installed and the user cannot move or rotate it, using ARKit to display the overlay content on the camera feed is a "killer mosquito with a gun" kind of situation. You could just as well work out during development what camera projection your 3D engine needs, use a dumb camera with your 3D engine running on top, and don't need iOS 11 or an ARKit-enabled device.

This way, you can think about your use case or technology stack even before embarking on specific solutions and workarounds.


As for your more specific problem ...

ARPlaneAnchor

is a completely read-only class because its use case is completely read-only. It exists for the sole purpose of providing ARKit with a way to provide you with information about detected aircraft. However, once you get this information, you can do whatever you want with it. And from there, you no longer need to keep ARPlaneAnchor

in equation.

You might be confused by the typical use case for plane detection (and SceneKit based display):

  • Enable Plane Definition
  • Answer renderer(_:didAdd:for:)

    for getting objectsARPlaneAnchor

  • In this method, return the virtual content to link to the plane anchor
  • Let it ARSCNView

    automatically position this content for you to match the plane's location

If your position in the plane is fixed relative to the camera, you do not need this.



You only need ARKit to handle the placement of your content within the scene if that location requires constant control, as in the case when planes are detected in real time (ARKit refines estimates of the position and volume of the plane and updates the anchor accordingly). If you get everything up and running, you won't receive updates, so you don't need ARKit to manage updates.

Instead, your steps might look something like this:

  • Know where the plane is (position in world space).
  • Set the position of your virtual content to a plane position.
  • Add content directly to the stage.

In other words, your "Solution 2" is a step in the right direction, but not far enough. You don't want to archive the instance itself ARPlaneAnchor

, but the information it contains, and then when unpacking, you don't need to re-create the instance ARPlaneAnchor

, you just need to use that information.

So, if this is what you are doing to host content with live plane detection:

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
    let extent = planeAnchor.extent
    let center = planeAnchor.center
    // planeAnchor.transform not used, because ARSCNView automatically applies it
    // to the container node, and we make a child of the container node

    let plane = SCNPlane(width: CGFloat(extent.x), height: CGFloat(extent.z))
    let planeNode = SCNNode(geometry: plane)
    planeNode.eulerAngles.x = .pi / 2
    planeNode.simdPosition = center
    node.addChildNode(planeNode)
}

      

Then you can do something like this to host static content:

struct PlaneInfo { // something to save and restore ARPlaneAnchor data
    let transform: float4x4
    let center: float3
    let extent: float3
}
func makePlane(from planeInfo: PlaneInfo) { // call this when you place content
    let extent = planeInfo.extent
    let center = float4(planeInfo.center, 1) * planeInfo.transform
    // we're positioning content in world space, so center is now
    // an offset relative to transform

    let plane = SCNPlane(width: CGFloat(extent.x), height: CGFloat(extent.z))
    let planeNode = SCNNode(geometry: plane)
    planeNode.eulerAngles.x = .pi / 2
    planeNode.simdPosition = center.xyz
    view.scene.rootNode.addChildNode(planeNode)
}

// convenience vector-width conversions used above
extension float4 {
    init(_ xyz: float3, _ w: Float) {
        self.init(xyz.x, xyz.y, xyz.z, 1)
    }
    var xyz: float3 {
        return float3(self.x, self.y, self.z)
    }
}

      

+7


source


Along with the aforementioned method of actually saving the plane anchors as objects to reload, a more reliable way of reloading the planes in the correct positions the second time you open the app is to use some kind of precise localization system that can place your planes constantly in the desired positions. ...



This article is a good reference for creating "persistent" scenes by storing any SCNNode in physical space.

+1


source







All Articles