ARKit multi-machine picture synchronization solution, principle analysis, technical explanation

Keywords: Mobile network

Learning Tasks in this Section

How to Synchronize Multiple Mobile AR Pictures in ARKit LAN

Requirement Description

A user opens the AR application, places an object in the room, and other users join the game to find it

Rule of the game: Players can only discover this object if they are within 1 m of it

Technical difficulties:

When a user enters an AR game, the location and angle of the mobile phone cannot be constant, so the resulting object is not found in the same place

Solution 1:

That is to say, let two mobile phones start in the same position and direction at the same time, because entering the AR scene coordinate system needs to move the original coordinate system of the mobile phone to be accurately positioned, so it causes a lot of error

Solution 2

Different mobile phones can start AR scenes anywhere, and when a player hides an object, the coordinate system of his or her mobile phone and that of other users can be synchronized to achieve the same coordinate values.

Let's start with a schematic diagram


3BB791B7-27AA-4C67-874B-1853A88DA5E7.png

The operation is divided into two steps

The first step starts AR (regardless of location and angle)
Step 2 Converts coordinates (converts the coordinates of Player 1's hidden object to the same location in reality in other Player's coordinate system)

Exploration of ARKit Rule

1. The Y-axis of the AR scene coordinate system is always perpendicular to the horizontal plane regardless of the angle and location of the phone.
2. Calibrating the mobile phone is to make the surface of the mobile phone parallel to the same direction, which is equivalent to the coordinate system of the cameras of the two mobile phones themselves in the same coordinate system. At this time, the coordinates of player 1, the objects placed (x1,y1,z1) are converted to the camera coordinate system first, then the coordinates are converted to (x2,y2,z2), and then the coordinates are converted to (x3,y3,z3) in the world coordinate system.
3. After the above conversion, simply place the object at coordinates x3,y3,z3

Deep analysis at the bottom

Since the y-axis of the coordinate system of all players is perpendicular to the water level, we consider the position of the coordinate system to be relative to the location of the calibration point. It is obtained by rotating an angle along the y-axis and then shifting a value. As long as we calculate how much rotation is relative between the two coordinate systems and how much increment is shifted, as long as the coordinates of the object are also in accordance with this law, we can calculate by rotating + shifting.Position of object in other player's coordinate system

First look at the underlying algorithm implementation

The main functions of this class are to find the offset and rotation angle theta, x, y, z

Core class TARSceneConverter

import SceneKit
import Foundation

class TARSceneConverter{
var Δθ: Float = 0.0
var Δx: Float = 0.0
var Δy: Float = 0.0
var Δz: Float = 0.0

init(referBeginRef1:SCNVector3, referEndRef1: SCNVector3,
     referBeginRef2: SCNVector3,referEndRef2: SCNVector3,
     collisionPosition1: SCNVector3,
     collisionPosition2: SCNVector3) {
     calculateΔθByBeginPosition(referBeginRef1, endPosition1: referEndRef1, beginPosition2: referBeginRef2, endPosition2: referEndRef2)
     calaulteFactor(position1: collisionPosition1, position2: collisionPosition2)
}
init() {
    
}

// Calculating the rotation angle of a vector on an xz surface
// Theta=-1 Identity
 func     calculateRotationY(beginPosition:SCNVector3,endPosition:SCNVector3) -> Float{
    var θ:Float = 0.0
    let x = endPosition.x 
    let z = endPosition.z
    if z > 0 && x > 0{
        θ = atan(z/x)
    }else if z > 0 && x < 0  {
        θ = atan(z/x) + Float.pi
    }else if z < 0 && x > 0{
        θ = 2*Float.pi + atan(z/x)
    }else if z < 0 && x < 0{
        θ =  Float.pi + atan(z/x)
    }else if x == 0 {
        if z > 0 {
            θ = 0
        }else if z < 0 {
            θ = Float.pi
        }else if z == 0 {
            θ = 0
        }
    }
    return θ
}
  private func calculateΔθByBeginPosition(_ beginPostion1: SCNVector3,
                                     endPosition1: SCNVector3,
                                     beginPosition2: SCNVector3,
                                     endPosition2: SCNVector3){
    self.Δθ = Float( calculateRotationY(beginPosition: beginPostion1, endPosition: endPosition1) - calculateRotationY(beginPosition: beginPosition2, endPosition: endPosition2))
}
  // Calculate the required factor
 private func calaulteFactor(position1:SCNVector3,position2:SCNVector3){
     let θ2 = calculateRotationY(beginPosition: SCNVector3Zero, endPosition: position2)
     let θ21 = θ2 + self.Δθ

     let x1 = position1.x
     let y1 = position1.y
     let z1 = position1.z
     let x2 = position2.x
     let y2 = position2.y
     let z2 = position2.z
     let r2 = sqrt(pow(x2, 2)+pow(z2, 2))

     let x21 = Float(cos(θ21)) * r2
     let z21 = Float(sin(θ21)) * r2
     let y21 = y2
     self.Δx = x21 - x1
     self.Δy = y21 - y1
     self.Δz = z21 - z1
}

// Convert values from airport scene coordinates
func convertPositionToMachine(position:SCNVector3)->SCNVector3{
    let x1 = position.x
    let y1 = position.y
    let z1 = position.z
    let x2 = x1 + self.Δx
    let y2 = y1 + self.Δy
    let z2 = z1 + self.Δz
    print("Δx:\(self.Δx)-Δy\(self.Δy)-Δz\(self.Δz)")
    print("x2:\(x2)-y2:\(y2)-z2:\(z2)")
    let θ2 = calculateRotationY(beginPosition: SCNVector3Zero, endPosition: SCNVector3Make(x2, y2, z2))
    print("angle\(θ2)")
    let r2 = sqrt(pow(x2, 2)+pow(z2, 2))
    print(r2)
    let θ21 =  θ2 - self.Δθ
    let x21 = cos(θ21) * r2
    let y21 = y2
    let z21 = sin(θ21) * r2
    return SCNVector3Make(x21, y21, z21)
}
// Shift the target coordinate system first
func translatePositionToMachine(position:SCNVector3)->SCNVector3{
     let x1 = position.x
    let y1 = position.y
    let z1 = position.z
    let x2 = x1 + self.Δx
    let y2 = y1 + self.Δy
    let z2 = z1 + self.Δz
    return SCNVector3Make(x2, y2, z2)
}
// Rotate to target coordinate system
func rotationByYToMachine(position:SCNVector3)->SCNVector3{
             let x1 = position.x
    let y1 = position.y
    let z1 = position.z
     let θ2 = calculateRotationY(beginPosition: SCNVector3Zero, endPosition: SCNVector3Make(x1, y1 ,z1))
    print("angle\(θ2)")
    let r2 = sqrt(pow(x1, 2)+pow(z1, 2))
    print(r2)
    let θ21 =  θ2 - self.Δθ
    let x21 = cos(θ21) * r2
    let y21 = y1
    let z21 = sin(θ21) * r2
    return SCNVector3Make(x21, y21, z21)
}

func getFactor()->(Float,Float,Float,Float){
    return (self.Δx,self.Δy,self.Δz,self.Δθ)
}
}

TRSceneConvert.switf

import Foundation
import SceneKit
private let sceneManager = TARSceneManager()
class TARSceneManager{
   var referBeginPosition1: SCNVector3!
   var referEndPosition1: SCNVector3!
   var referBeginPosition2: SCNVector3!
  var referEndPosition2: SCNVector3!
  var collisionPosition1: SCNVector3!
  var collisionPosition2:SCNVector3!
  var converter: TARSceneConverter!
  class func share() -> TARSceneManager{
      return sceneManager
  }
  // Start execution tag
  func handleMark(){
      converter = TARSceneConverter(referBeginRef1: referBeginPosition1, referEndRef1: referEndPosition1, referBeginRef2: referBeginPosition2, referEndRef2: referEndPosition2, collisionPosition1:collisionPosition1, collisionPosition2: collisionPosition2)
  }
  // Convert coordinates to the scene below
  func convertPositionToFollowScene(position:SCNVector3)->SCNVector3{
      if converter != nil {
         return converter.convertPositionToMachine(position: position)
      }else{
         fatalError("converter is nil")
      }
  }
}

Parameter Interpretation

var referBeginPosition1: SCNVector3! // Host, initialized at a point in the host camera's coordinate system, in the world coordinate system
var referEndPosition1: SCNVector3! //Host, when calibrating, the position of the above node in the world coordinate system
var referBeginPosition2: SCNVector3! // Host, initialized at a point in the host camera's coordinate system, in the world coordinate system
var referEndPosition2: SCNVector3! //Host, when calibrating, the position of the above node in the world coordinate system
var collisionPosition1: SCNVector3! // World coordinate position of camera 1 during calibration
var collisionPosition2:SCNVector3! // World coordinate location of camera 2 during calibration

This algorithm is a bit complex, actually there is a better algorithm, it can be done in about ten sentences. Next we will demonstrate that this section is here first. The code will be sent to the group (530957835), check ToyAR, pay attention to preparing two 6S mobile phones to connect to the same local area network, pay attention to modifying the host ip address of TGameRoom file to change to the ip address of your mobile phone host.

 self.client.connectHost(host: "192.168.8.108", port: 10001, success: {
       self.client.writeData(user, success: {
        })
    }) { (err) in
        print(err!)
    }

Posted by kendhal on Tue, 21 May 2019 11:25:04 -0700