ARKit多机画面同步解决方案,原理分析,技术讲解

本节学习任务

ARKit局域网内如何实现多个手机AR画面同步

需求描述

一个用户打开AR应用,在房间中放置一个物体,然后其他用户加入游戏去找这个物体

游戏规则:只有玩家靠近这个物体1m的范围内才能发现这个物体

技术难点:

用户进入AR游戏时,手机的位置和角度不可能一直,所以就造成的物体不在同一个地方被发现

解决方案1:

就是上述所说的,让两个手机在同一个位置,朝向同一个方向同时启动手机,由于进入AR场景坐标系需要移动一下手机原始坐标系才能被准确的定位,所以就造成了很大的误差

解决方案2

不同手机在任何位置,都可以启动AR场景,然后当一个玩家藏好物体后,将自己的手机坐标系和其它用户的手机坐标系进行同步转换即可完成坐标值的统一

下面先看一张原理图

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

操作分为两步

第一步 启动AR(无论在什么位置和角度都可以) 第二步 转换坐标 (将玩家1藏物体的坐标,转换的到其它玩家坐标系上,现实中的同一个位置)

ARKit 规律探究

1.无论手机在什么角度和位置开启AR场景坐标系的Y轴总是和水平面垂直 2.标定手机是让手机表面平行方向一致,这个时候相当于将两个手机的照相机的坐标自身的坐标系是同一个坐标系,这个时候,将玩家1,放置物体的坐标(x1,y1,z1)先转换到相机坐标系中,转换后的坐标为(x2,y2,z2),之后在将这个坐标,转换至世界坐标系中,转换后的坐标为(x3,y3,z3) 3.完成上面转换后,只要在坐标x3,y3,z3处放置物体即可

底层深入分析

由于所有玩家的坐标系的y轴都是和水平面垂直的,所以我们看做坐标系的位置相对标定点的位置,是有沿着y轴旋转了一个角度,然后平移一个值所得,只要计算出两个坐标系之间相对旋转了多少度,平移了多少增量,只要将物体坐标,也按照这个规律,旋转+平移,就可以计算物体在其他玩家坐标系中的位置

先看一下底层算法实现

下面这个类主要作用是求出,偏移量和旋转角度Δθ,Δx,Δy,Δz

核心类 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() {
    
}

// 计算xz面上向量的旋转角度
// θ = -1 标识
 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))
}
  // 计算需要的因子
 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
}

// 转换值从机场景坐标
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("角度\(θ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)
}
// 先平移目标坐标系
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)
}
// 旋转至目标坐标系
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("角度\(θ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
  }
  // 开始执行标记
  func handleMark(){
      converter = TARSceneConverter(referBeginRef1: referBeginPosition1, referEndRef1: referEndPosition1, referBeginRef2: referBeginPosition2, referEndRef2: referEndPosition2, collisionPosition1:collisionPosition1, collisionPosition2: collisionPosition2)
  }
  // 转换坐标到下面的场景
  func convertPositionToFollowScene(position:SCNVector3)->SCNVector3{
      if converter != nil {
         return converter.convertPositionToMachine(position: position)
      }else{
         fatalError("converter is nil")
      }
  }
}

参数解释

var referBeginPosition1: SCNVector3! // 主机,初始化时,放置在主机相机坐标系的一个点,在世界坐标系的位置 var referEndPosition1: SCNVector3! //主机,标定时 ,上面那个节点在世界坐标系的位置 var referBeginPosition2: SCNVector3! // 主机,初始化时,放置在主机相机坐标系的一个点,在世界坐标系的位置 var referEndPosition2: SCNVector3! //主机,标定时 ,上面那个节点在世界坐标系的位置 var collisionPosition1: SCNVector3! // 标定时,相机1的世界坐标位置 var collisionPosition2:SCNVector3! // 标定时,相机2的世界坐标位置

这个算法有点复杂,其实有更好的算法,十句左右代码就可以搞定,下一篇我们演示一下,本节就先到这里,代码会发送群里(530957835),查看ToyAR,注意准备两台6S以上手机,让两个手机连上同一个局域网,注意修改在TGameRoom文件的主机ip地址,改成你手机主机的ip地址

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏目标检测和深度学习

英伟达开源数据增强和数据解码库,解决计算机视觉性能瓶颈

【新智元导读】在CVPR 2018大会上,英伟达开源了数据增强库DALI和数据解码库nvJPEG。

14840
来自专栏机器之心

教程 | 如何使用TensorFlow实现音频分类任务

89870
来自专栏数说工作室

文本相似比较

大家好,我是数说君,这篇文章是想跟大家讨教一下。 如果有两段简单文本,如何比较它们的相似度?这里我们就假设是英文,不存在中文的分词问题,文本就类似于: text...

385140
来自专栏机器之心

资源 | 一个基于PyTorch的目标检测工具箱,商汤联合港中文开源mmdetection

项目地址:https://github.com/open-mmlab/mmdetection

84920
来自专栏机器之心

业界 | Facebook开源TTS神经网络VoiceLoop:基于室外声音的语音合成(附PyTorch实现)

选自GitHub 作者:Facebook Research 机器之心编译 参与:黄小天、路雪 近日,Facebook 在题为《Voice Synthesis f...

42860
来自专栏数据派THU

独家 | 教你用Q学习算法训练神经网络玩游戏(附源码)

原文标题:Teaching a NeuralNetwork to play a game using Q-learning 作者:Soren D 翻译:杨金鸿 ...

48980
来自专栏PaddlePaddle

【AI核心技术】课程八:卷积网络简介

UAI与PaddlePaddle联合推出的【AI核心技术掌握】系列课程持续更新中!

9530
来自专栏数据小魔方

堆叠柱图

今天你要跟大家分享的图表是——堆叠柱图! ▽▼▽ 首先还是来看堆叠柱图所用到的数据组织结构: ? 利用以上数据插入图表——柱形图(簇状)。 ? 然后为工资数据...

35380
来自专栏PPV课数据科学社区

【学习】R语言中的情感分析与机器学习

#玩转大数据#利用机器学习可以很方便的做情感分析。本篇文章将介绍在R语言中如何利用机器学习方法来做情感分析。在R语言中,由Timothy P.Jurka开发的情...

38480
来自专栏石瞳禅的互联网实验室

【TensorFlow实战——笔记】第2章:TensorFlow和其他深度学习框架的对比

可以看到各大主流框架基本都支持Python,目前Python在科学计算和数据挖掘领域可以说是独领风骚。虽然有来自R、Julia等语言的竞争压力,但是Python...

13310

扫码关注云+社区

领取腾讯云代金券