26

Hey I'm trying to figure out. How to keep a simple node in place. As I walk around it in ARKit

Code:

 func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

    if let planeAnchor = anchor as? ARPlaneAnchor {

    if planeDetected == false { // Bool only allows 1 plane to be added
            planeDetected = true
        self.addPlane(node: node, anchor: planeAnchor)
    }

    }
}

This adds the SCNNode

    func addPlane(node: SCNNode, anchor: ARPlaneAnchor) {

    // We add the anchor plane here

    let showDebugVisuals = Bool()
    let plane = Plane(anchor, showDebugVisuals)
    planes[anchor] = plane
    node.addChildNode(plane)



    // We add our custom SCNNode here 

    let scene = SCNScene(named: "art.scnassets/PlayerModel.scn")!
    let Body = scene.rootNode.childNode(withName: "Body", recursively: true)!

    Body.position = SCNVector3.positionFromTransform(anchor.transform)
    Body.movabilityHint = .movable
    wrapperNode.position = SCNVector3.positionFromTransform(anchor.transform)

    wrapperNode.addChildNode(Body)

    scnView.scene.rootNode.addChildNode(wrapperNode)

Ive tried adding a Plane/Anchor Node and putting the "Body" node in that but it still moves. I thought maybe it has something to do with the update function.

 func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
 }

Or most likely the position setting

wrapperNode.position = SCNVector3.positionFromTransform(anchor.transform)

Iv'e looked through every source / project file / video on the internet and nobody has a simple solution to this simple problem.

Hunter
  • 1,321
  • 4
  • 18
  • 34

2 Answers2

31

There are two kinds of "moving around" that could be happening here.


One is that ARKit is continuously refining its estimate of how the device's position in the real world maps to the abstract coordinate space you're placing virtual content in. For example, suppose you put a virtual object at (0, 0, -0.5), and then move your device to the left by exactly 10 cm. The virtual object will appear to be anchored in physical space only if ARKit tracks the move precisely. But visual-inertial odometry isn't an exact science, so it's possible that ARKit thinks you moved to the left by 10.5 cm — in that case, your virtual object will appear to "slip" to the right by 5 mm, even though its position in the ARKit/SceneKit coordinate space remains constant.

You can't really do much about this, other than hope Apple makes devices with better sensors, better cameras, or better CPUs/GPUs and improves the science of world tracking. (In the fullness of time, that's probably a safe bet, though that probably doesn't help with your current project.)


Since you're also dealing with plane detection, there's another wrinkle. ARKit is continuously refining its estimates of where a detected plane is. So, even though the real-world position of the plane isn't changing, its position in ARKit/SceneKit coordinate space is.

This kind of movement is generally a good thing — if you want your virtual object to appear anchored to the real-world surface, you want to be sure of where that surface is. You'll see some movement as plane detection gets more sure of the surface's position, but after a short time, you should see less "slip" as you move the camera around for plan-anchored virtual objects than those that are just floating in world space.


In your code, though, you're not taking advantage of plane detection to make your custom content (from "PlayerModel.scn") stick to the plane anchor:

wrapperNode.position = SCNVector3.positionFromTransform(anchor.transform)
wrapperNode.addChildNode(Body)
scnView.scene.rootNode.addChildNode(wrapperNode)

This code uses the initial position of the plane anchor to position wrapperNode in world space (because you're making it a child of the root node). If you instead make wrapperNode a child of the plane anchor's node (the one you received in renderer(_:didAdd:for:)), it'll stay attached to the plane as ARKit refines its estimate of the plane's position. You'll get a little bit more movement initially, but as plane detection "settles", your virtual object will "slip" less.

(When you make the node a child of the plane, you don't need to set its position — a position of zero means it's right where the plane is. Inf anything, you need to set its position only relative to the plane — i.e. how far above/below/along it.)

DBD
  • 23,075
  • 12
  • 60
  • 84
rickster
  • 124,678
  • 26
  • 272
  • 326
  • 1
    Apple Dev provided more details example in this project https://github.com/gao0122/ARKit-Example-by-Apple Its explain everything properly and simple. – mychar Nov 25 '17 at 09:50
  • @rickster thank you very much for your insightful response. I'm also having the same issue as the OP, as my 3D models sometimes drift by 20cm whenever I move 1-3 meters away from it. Is that expected? Why don't IKEA Place's furniture items move that much? Thanks! – aviggiano Aug 27 '18 at 20:19
  • @aviggiano, I am also facing such issue in one of our AR project. Just wanted to confirm did you find any solution for the same? – a.palo Apr 04 '20 at 07:00
  • @a.palo no, I didn't find a solution but since I am not doing AR apps anymore I stopped trying – aviggiano Apr 05 '20 at 16:02
  • Hey @Rickster, thank you so much for your detailed answers! I and my team stuck on an issue for days ah :/ I posted an interesting question: https://stackoverflow.com/questions/63662318/arkit-apply-filter-cifilter-to-a-specific-part-vertex-of-an-arfaceanchor Would love your suggestions! – Roi Mulia Aug 31 '20 at 07:28
0

To keep an SCNNode in place you can disable sceneView plane detection once you get the result you desired.

let configuration = ARWorldTrackingConfiguration(); configuration.planeDetection = [] self.sceneView.session.run(configuration)

The reason for this is that ARKit constantly reestimates the position of the detected plane resulting in your SCNNode moving around.

Linh Ta
  • 593
  • 6
  • 11