1

I'm trying to build a quality assurance app in Swift using ARKit.

I'd like to position a USDZ model on top of its real world counterpart similar to this video...

I have been able to successfully accomplish this by using both image tracking and plane detection but both methods have some issues regarding "drifting." My model tends to drift over time as the user moves around the object. I am trying to determine an approach that initially places the model using plane detection (since that seems to be the best way to keep the object anchored) and then use image anchors to "reset" the model's location as they are detected. I am having a hard time understanding how the playing cards shown in the video can be helpful given that they are arbitrarily placed on the surface. I can see how they could be helpful in re-anchoring in the Z direction, but can they really be helpful to realign the X and Y since they do not have known coordinates themselves?

I'd appreciate any advice or suggestions. Perhaps if I am overlooking a completely different approach that is better suited for this task.

UPDATE

I use a state based setup to wait for the user to...

  1. Select a Plane
  2. Select the first point on the plane
  3. Select the second point on the plane

The model is then added to the selected plane, and rotated to match the angle between point 1 and point 2.

The origin of the USDZ model is at the centerline, but my users typically want to align the corresponding surface to the selected plane so they are required to manually move the model up/down, left/right, etc... using GUI controls to get it into its final position.

Here is the code I am currently using to initially place my model...

Select desired plane...

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
    guard
        let updatedAnchor = anchor as? ARPlaneAnchor,
        let selectedAnchor = selectedPlaneAnchor,
        updatedAnchor.identifier == selectedAnchor.identifier
    else {
        return
    }
    
    self.selectedPlaneAnchor = updatedAnchor
}

Get first and second point to define rotation...

@objc func handleTap(_ gestureRecognizer: UITapGestureRecognizer) {
    // Get the location of the tap in the sceneView
    let tapLocation = gestureRecognizer.location(in: sceneView)
    
    // Perform a hit test to find the ARPlaneAnchor at the tap location
    let hitTestResults = sceneView.hitTest(tapLocation, types: .existingPlaneUsingExtent)
    
    if let result = hitTestResults.first {
        if let detectedPlaneAnchor = result.anchor as? ARPlaneAnchor {
            let p3 = result.worldTransform.columns.3
            
            switch currentState {
                case .initial, .loading, .ready:
                    break
                case .surface:
                    if selectedPlaneAnchor == nil {
                        selectedPlaneAnchor = detectedPlaneAnchor
                        currentState = .point1
                    }
                case .point1:
                    if detectedPlaneAnchor.identifier == selectedPlaneAnchor?.identifier {
                        firstPoint = SCNVector3(p3.x,p3.y,p3.z)
                        addPointNode(location: firstPoint!)
                        currentState = .point2
                    }
                case .point2:
                    if detectedPlaneAnchor.identifier == selectedPlaneAnchor?.identifier {
                        secondPoint = SCNVector3(p3.x,p3.y,p3.z)
                        addPointNode(location: secondPoint!)
                        addAssemblyNode()
                        currentState = .ready
                    }
            }
        }
    }
}

Add model to plane and rotate accordingly...

func addAssemblyNode() {
    guard
        let usdzURL = Bundle.main.url(forResource: "art.scnassets/assembly", withExtension: "usdz"),
        let p1 = firstPoint,
        let p2 = secondPoint
    else {
        return
    }
    
    let assemblyScene = try! SCNScene(url: usdzURL, options: nil)
    
    let directionVector = SCNVector3(p2.x - p1.x, p2.y - p1.y,p2.z - p1.z)
    let rotationAngle = atan2f(directionVector.z, directionVector.x)
    
    if let node = assemblyScene.rootNode.childNode(withName: "main", recursively: true) {
        node.name = "main"
        
        node.position = p1
        node.eulerAngles.y = -rotationAngle
        //node.scale = SCNVector3(x: 0.125, y: 0.125, z: 0.125)
        sceneView.scene.rootNode.addChildNode(node)
        
        assemblyNode = node
    }
}

Here is a video of the result where you can see the drift. You can see the large blue plate on the end move back into position when I move to my original location.

Joel S.
  • 41
  • 5
  • How much is the drifting over time? Do you have some screenshots? Can you also provide Code how you exactly place an object. (Do you update position Data over time?) – ZAY Aug 21 '23 at 13:52
  • I updated my post to include the sample code for loading a model at a specified plane and location. I will try to post some images soon. The "drifting" is roughly 1/2"-1" as I walk down the length of the model. – Joel S. Aug 21 '23 at 15:20
  • Just added a video as well. https://youtu.be/GFp0aKQbf30 – Joel S. Aug 21 '23 at 16:57
  • to me it seems, as if the blue grid part does not exactly match the distance to the bottom of that physical grid object you took on camera. otherwise I think, the tracking and placement works quite well, as your video demonstrates. – ZAY Aug 21 '23 at 17:07
  • How does your app behave, if you place (position) the object just once and do not make further node position updates on it? I made myself a couple of AR apps, and for me it was okay to do the positioning just once to set the object initially. – ZAY Aug 21 '23 at 17:31
  • That video was a test I ran this morning and it was actually better than the last time I tested prior to drafting this post. I'm pretty happy with the results. I do in fact only position the model one time at the beginning. I do not perform any additional resetting afterwards. – Joel S. Aug 21 '23 at 17:53
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/254990/discussion-between-joel-s-and-zay). – Joel S. Aug 21 '23 at 17:53

0 Answers0