当前位置: 首页 > news >正文

【iOS ARKit】协作 Session 实例

协作 Session 使用注意事项

      协作 Session 是在 ARWorldMap 基础上发展起来的技术,ARWorldMap 包含了一系列的地标、ARAnchor 及在观察这些地标和 ARAnchor 时摄像机的视场(View)。如果用户在某一个位置新创建了一个 ARAnchor,这时这个 ARAnchor位置并不是相对于公共世界坐标系的(实际上此时用户根本就不知道是否还有其他参与者),而是被存储成离这个 ARAnchor 最近视场的相对坐标,这些信息也会一并存入到用户的ARWorldMap 中并被发送到其他用户。

      由于 ARAnchor 是相对于 View 的坐标,而这些 View 会分组存储到 ARWorldMap 中,亦即是说,ARAnchor 与任何设备的世界坐标系都没有关系,不管这些ARAnchor 是被本机设备解析到本机场景中,还是通过网络发送到其他设备而被解析到其他用户的场景中,都不会改变 ARAnchor 与 View 之间的相互关系。因此,即使其他用户使用了不同的世界坐标系,他们也能在相同的真实环境位置中看到这个 ARAnchor。

       从以上原理可以看到,ARAnchor 对共享AR体验起到了非常关键的作用,所以为了更好地共享AR体金,开发人员应当在开发时注意以下几点:

     (1)跟踪 ARAnchor 的更新。在ARKit探索环境时,随着采集的特征点信息越来越多,对环境的理解会越来越精准,ARKit会通过对之前的摄像机视场(View)进行微调来优化与调整地标信息,因此,与某一摄像机视场(View)相关联的 ARAnchor 姿态也会随之发生调整,所以应当保持对 ARAnchor 的跟踪以确保ARAnchor 发生更新时能及时反映到当前用户场景中。

    (2)虚拟物体应靠近 ARAnchor。在 ARAnchor 发生更新时,连接到其上的虚拟物体也会发生更新,离ARAnchor 远的虚拟物体在更新时可能会出现误差而导致偏离真实位置。所以连接到ARAnchor 的虚拟对象应当靠近对应的 ARAnchor 以减少误差带来的影响。

    (3)处理好 ARAnchor 与虚拟物体的关系。独立的虚拟物体应当使用独立的ARAnchor,这样每一个独立虚拟物体都可以尽量靠近 ARAnchor,并且在存储时可以存储到 ARWorldMap 相同分组中。对若干个距离较近并且希望保持相互之间位置关系的虚拟物体应当使用同一个 ARAnchor,因为在 ARAnchor 更新时,这些虚拟物体会得到相同的更新矩阵,从而保持相互间的位置关系不发生任何变化。

    (4)使用协作 Session 必须要将 isCollaborationEnabled设置为true,只有设置为 true, ARKit 才会周期性的调用 session(_:didOutputCollaborationData:)方法,也才能将 Collaboration Data 数据发送给所有参与方。

    (5)为更高效可靠地传输 AR 进程数据,ARKit 对 Collaboration Data 数据进行了优先级区分,由ARSession. CollaborationData. Priority 枚举表示,分为两种类型:Critical(关键)和 Optional(可选)。Critical 数据定期更新,对同步 AR体验非常关键,应当被可靠地发送到所有参与设备;Optional 数据产生频率高,几乎每帧产生,重要性不及 Critical 数据,因此有所丢失也不会有太大影响。标记为 Optional 的数据包括设备位置数据。区分优先级可以允许我们对不同的Collaboration Data 数据采取不同的处理策略,提高同步的性能。

    (6) 在使用协作 Session 时,有时我们需要知道某个 ARAnchor 是不是由本机设备生成,ARAnchor 的创建者属于哪个设备,如在某个场合需要在某个参与者退出后清除所有该参与者创建的虚拟物体。

      在ARKit 中,每个 ARSession 在运行时会都会生成一个 UUID(Universally Unique Identifier,全局唯一 ID)用于唯一标识该 Session,同时,在协作 Session 中,每个 ARParticipantAnchor 也都有一个独立且唯一的 Identifier 值标识该参与者,ARParticipantAnchor 与 ARAnchor 都有一个 sessionldentifier 属性,这个sessionldentifier 值与所在设备的ARSession UUID 值相同。因此,利用这些信息我们就可以判断ARAnchor 的创建者,并依据结果进行后续处理,典型的示例代码所示。

   (7)协作 Session 同步从有参与者参与开始,但地图的真正融合开始于参与者物理特征值的匹配,即参与者探索过的物理环境有重叠的部分,一旦地图融合后,每个参与用户都会获得其他参与者探索过的地图,同时会同步所有ARAnchor,所以为了便于 ARKit 更快地融合地图,参与者应当在相同的物理环境中扫描相同的物理区域。

协作 Session 实例

      在 ARKit 中,使用协作 Session 主要利用 session(_:didOutputCollaborationData:)方法跟踪同步所有ARAnchor,其中通过网络收发 Collaboration Data 需要开发人员自行处理,完整代码如下所示。

//
//  CooperationSession.swift
//  ARKitDeamo
//
//  Created by zhaoquan du on 2024/2/27.
//import SwiftUI
import ARKit
import RealityKit
import MultipeerConnectivitystruct CooperationSession: View {static var arView:ARView?static var multipeerSession: MultipeerSession?var body: some View {CooperationSessionContent().onDisappear(perform: {CooperationSession.arView?.session.delegate = nilCooperationSession.arView?.session.pause()CooperationSession.arView = nilCooperationSession.multipeerSession?.endConnect()CooperationSession.multipeerSession = nilprint("CooperationSession onDisappear")}).edgesIgnoringSafeArea(.all).navigationTitle("协作Session")}
}struct CooperationSessionContent:UIViewRepresentable {func makeUIView(context: Context) -> some ARView {let arView = ARView(frame: .zero)let config = ARWorldTrackingConfiguration()config.isCollaborationEnabled = trueconfig.planeDetection = .horizontalarView.session.run(config,options: [.resetTracking,.removeExistingAnchors])arView.session.delegate = context.coordinatorCooperationSession.arView = arViewcontext.coordinator.createPlane()context.coordinator.addGesture()return arView}func updateUIView(_ uiView: UIViewType, context: Context) {}func makeCoordinator() -> Coordinator {Coordinator()}class Coordinator: NSObject, ARSessionDelegate {var multipeerSession: MultipeerSession?{return CooperationSession.multipeerSession}var planeEntity : ModelEntity? = nilvar raycastResult : ARRaycastResult?var arView: ARView? {return CooperationSession.arView}func createPlane(){CooperationSession.multipeerSession = MultipeerSession(serviceType: "cooper-session", receivedDataHandler: receiveData(data:from:), peerJoinedHandler: peerJoined(_:), peerLeftHandler: peerLeft(_:), peerDiscoveredHandler: peerDiscovered(_:))let planeMesh = MeshResource.generatePlane(width: 0.15, depth: 0.15)let planeMaterial = SimpleMaterial(color:.white,isMetallic: false)planeEntity = ModelEntity(mesh: planeMesh, materials: [planeMaterial])let planeAnchor = AnchorEntity(plane: .horizontal)do {let planeMesh = MeshResource.generatePlane(width: 0.15, depth: 0.15)var planeMaterial = SimpleMaterial(color: SimpleMaterial.Color.red, isMetallic: false)planeMaterial.color =  try SimpleMaterial.BaseColor(tint:UIColor.yellow.withAlphaComponent(0.9999), texture: MaterialParameters.Texture(TextureResource.load(named: "AR_Placement_Indicator")))planeEntity = ModelEntity(mesh: planeMesh, materials: [planeMaterial])planeAnchor.addChild(planeEntity!)arView?.scene.addAnchor(planeAnchor)} catch let error {print("加载文件失败:\(error)")}}func addGesture(){let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))arView?.addGestureRecognizer(tap)}@objc func handleTap(_ sender: UITapGestureRecognizer? = nil) {guard let raycastResult = raycastResult else {print("还未检测到平面")return}let anchor = ARAnchor(name: "objectAnchor", transform: raycastResult.worldTransform)arView?.session.add(anchor: anchor)}//ARSessionDelegatefunc session(_ session: ARSession, didUpdate frame: ARFrame) {guard let arView = arView,  let result = arView.raycast(from: arView.center, allowing: .estimatedPlane, alignment: .horizontal).first else{return}raycastResult = resultplaneEntity?.setTransformMatrix(result.worldTransform, relativeTo: nil)}func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {guard let arView = arView else {return}for anchor in anchors {if anchor.name == "objectAnchor"{let box = ModelEntity(mesh: MeshResource.generateBox(size: 0.1), materials: [SimpleMaterial.init(color: .green, isMetallic: false)])box.position = [0,0.05,0]let anchorEntity = AnchorEntity(anchor: anchor)anchorEntity.addChild(box)arView.scene.addAnchor(anchorEntity)}}}func session(_ session: ARSession, didOutputCollaborationData data: ARSession.CollaborationData) {guard let multipeerSession = multipeerSession else {return}if !multipeerSession.connectedPeers.isEmpty {do {let encodeData = try NSKeyedArchiver.archivedData(withRootObject: data, requiringSecureCoding: true)multipeerSession.sendToAllPeers(encodeData, reliably: data.priority == .critical)} catch  {print("encode data faile")}}}func receiveData(data:Data,from peer: MCPeerID){if let data = try? NSKeyedUnarchiver.unarchivedObject(ofClass: ARSession.CollaborationData.self, from: data){if data.priority == .critical {arView?.session.update(with: data)print(" data updated")}}}func peerDiscovered(_ peer: MCPeerID) -> Bool {guard let multipeerSession = multipeerSession else {return false}if multipeerSession.connectedPeers.count > 3 {return false}else{return true}}func peerJoined(_ peer: MCPeerID) {}func peerLeft(_ peer: MCPeerID) {}}
}#Preview {CooperationSession()
}

代码清单8-6 中代码实现的功能如下:

     (1)进行平面检测,在检测到可用平面时实例化一个指示图标用于指示放置位置。

     (2)添加屏幕单击手势,在平面可用时单击屏幕会在指示图标位置放置一个 ARAnchor,注意这个ARAnchor 的名字(name 属性),稍后会详细说明。

    (3)检查所有添加到场景中的 ARAnchor,当ARAnchor 名字(name 属性)为指定值时在 ARAnchor 位置生成一个立方体。

    (4)周期性地向所有参与设备发送本设备的 AR 进程数据(Collaboration Data)。

    (5)接收来自其他设备的Collaboration Data 数据并更新到本设备的 ARSession 中。

      在第(2)项功能中,即 handleTap()方法中的代码,利用命中点的坐标生成了一个 ARAnchor,并将其添加到 ARSession 中,这里 ARAnchor 的name 属性很重要,因为我们后续需要利用该 ARAnchor 的名字来恢复虚拟元素。

     在第(3)项功能中,即 session(:didAdd:)方法中代码,遍历所有添加的 ARAnchor,这里的 ARAnchor既包括本设备的ARAnchor,也包括从其他设备同步过来的 ARAnchor,当 ARAnchor 名字(name 属性)为功能2中指定值时在 ARAnchor 位置生成一个立方体。利用该方法既会生成本设备自身的立方体,也会生成其他设备共享的立方体,即实现了操作同步。

     在第(4)项功能中,即 session(_:didOutputCollaborationData:)方法中代码,首先确保通信可用且有参与者,然后利用 let collaborationData = try? NSKeyedUnarchiver. unarchivedObject (ofClass: ARSession.CollaborationData. self, from: data) 语句获取 Collaboration Data 数据并序列化之,设置数据通信优先级后将数据发送到所有参与者。

     在第(5)项功能中,即 receivedData()方法中代码,利用 let collaborationData = try?NSKeyedUnarchiver.unarchivedObject(ofClass: ARSession. CollaborationData. self, from: data) 语何获取 Collaboration Data数据并反序列化之,然后将其更新到本设备的 ARSession中。

     与 ARWorldMap一样,Collaboration Data 数据也不包含虚拟元素本身,因此,需要人工恢复虚拟元素,因为虚拟元素总是与ARAnchor 关联,利用 ARAnchor 的名字(name)属性我们就可以恢复关联的虚拟元素,通过这种方式,可以逐一地恢复所有的虚拟元素,从而恢复整个场景,达到所有参与方看到完全相同 AR场景的效果,即实现了 AR体验的同步。

      在两台设备A 和B上同时运行本案例(确保两台设备连接到同一个 WiFi网络或者都打开蓝牙),在A设备检测到的平面上单击添加立方体,在AB连接顺畅的情况下可以看到B设备也会同步出现该立方体,并且立方体所在物理世界中的位置与A设备中的一致,反之亦然,在B设备检测到的平面上单击添加立方体,A设备也会同步出现该立方体,并且立方体所在物理世界中的位置与B设备中的一致,效果如上图 所示。

具体代码地址:GitHub - duzhaoquan/ARkitDemo

相关文章:

【iOS ARKit】协作 Session 实例

协作 Session 使用注意事项 协作 Session 是在 ARWorldMap 基础上发展起来的技术,ARWorldMap 包含了一系列的地标、ARAnchor 及在观察这些地标和 ARAnchor 时摄像机的视场(View)。如果用户在某一个位置新创建了一个 ARAnchor,这时…...

云原生精品资料合集(附下载)

云计算是产业数字化转型的关键基础设施,以基础设施资源为中心的云搬迁时代接近尾声,以应用价值为中心的云原生时代已经到,所以IT人员学习云原生正当时!最近跟各位大神征集了云原生的教程,行业报告和最佳实践,总有一款适…...

JVM 第一部分 JVM两种解释器 类加载过程和类加载器

JVM是跨平台跨语言的虚拟机,不直接接触硬件,位于操作系统的上一层 跟字节码文件直接关联,和语言没有关系 一次编译成字节码文件,多次执行 虚拟机可以分成三部分:类加载器,运行时数据区,执行引…...

用Java语言创建的Spring Boot项目中,如何传递数组呢??

问题: 用Java语言创建的Spring Boot项目中,如何传递数组呢?? 在这个思路中,其实,Java作为一个后端开发的语言,没必要着重于如何传入,我们主要做的便是对传入的数组数据进行处理即可…...

[笔记] 使用 Java Swing 实现一个简单的窗口

Java Swing 是一个用于构建图形用户界面(GUI)的Java库,它提供了丰富的组件和工具,用于创建交互式的桌面应用程序。Swing 是 Java Foundation Classes(JFC)的一部分,它是 Java 平台的一种标准用户…...

2024.03.03蓝桥云课笔记——排序

sort简介 #include<algorithm> 使用的是快速排序 时间复杂度为O(nlogn) sort使用(默认是从小到大) 1.sort(起始地址&#xff0c;结束地址的下一位&#xff0c;*比较函数&#xff09;&#xff1b; #include<iostream> #include<algorithm> using namesp…...

Vue3和ElementPlus封装table组件

最近学习vue3.2并自己在写一个项目&#xff0c;然后发现好几个页面都是列表页&#xff0c;重复写table和column也是觉得累&#xff0c;学习的项目列表页不算多&#xff0c;要是公司项目就不一样了&#xff0c;所以就想着自己封装一个table组件&#xff0c;免去大量重复工作和co…...

第一篇:参考资料地址

javaGuide JavaGuide&#xff08;Java学习&面试指南&#xff09; | JavaGuide 清华学生总结的 小林coding labuladong labuladong 的算法笔记 | labuladong 的算法笔记 【华仔说技术】kafka的系列文章 https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg3MTcxMDgxNA…...

wordpress 开源主题

海外就医wordpress主题 出国看病、海外就医是越来越多中产家庭的选择&#xff0c;此wordpress主题适合做相关业务的公司官网。 https://www.jianzhanpress.com/?p5220 防护wordpress外贸主题 个人防护器具wordpress外贸主题&#xff0c;适合做劳动保护的外贸公司使用。 ht…...

【Linux网络命令系列】ping curl telnet三剑客

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

于月仙主动与赵本山握手表示欢迎,赵:怎么着要跟我第二次牵手啊?

于月仙主动与赵本山握手表示欢迎&#xff0c;赵&#xff1a;怎么着要跟我第二次牵手啊&#xff1f; --小品《乡村爱情》&#xff08;中1&#xff09;的台词 表演者&#xff1a;赵本山 于月仙 王小利 唐鉴军等 &#xff08;接上&#xff09; 咱们呢就给新人揭盖头 好 好长贵…...

Unity UGUI之Slider基本了解

在Unity中&#xff0c;Slider&#xff08;滑动条&#xff09;是一种常用的用户界面控件之一&#xff0c;允许用户通过拖动滑块来选择一个数值。常常应用于调节数值&#xff08;如调节音量、亮度、游戏难度等&#xff09;、设置选项等。 以下是Slider的基本信息和用法: 1、创建…...

【Linux】进程间通信之共享内存

文章目录 引入共享内存的原理共享内存的相关接口shmget()shmat()shmdt()shmctl() 共享内存的简单使用共享内存的特点 引入 进程间通信&#xff0c;顾名思义就是一个进程和另一个进程之间进行对话&#xff0c;以此完成数据传输、资源共享、通知事件或进程控制等。 众所周知&am…...

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于条件风险价值的虚拟电厂参与能量及备用市场的双层随机优化》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 这篇文章的标题涉及到以下几个关键点…...

前端架构: 脚手架通用框架封装之CommonJS和ESM混合开发兼容解决(教程五)

CommonJS 和 ESModule 混合开发 接上文&#xff0c;仍旧在 abc-cli 项目中参考&#xff1a;https://blog.csdn.net/Tyro_java/article/details/136433159现在要在脚手架项目中安装 chalk 依赖&#xff0c;因为在 abc-cli 项目几乎都是 CommonJS的实现而 chalk 这个依赖源码是基…...

基于主从模式的Reactor的仿muduo网络库

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…...

Linux服务器搭建超简易跳板机连接阿里云服务器

简介 想要规范内部连接阿里云云服务器的方式&#xff0c;但是最近懒病犯了&#xff0c;先搞一个简易式的跳板机过渡一下&#xff0c;顺便在出一个教程&#xff0c;其他以后再说&#xff01; 配置方法 创建密钥 登录阿里云&#xff0c;找到云服务器ECS控制台&#xff0c;点击…...

Windows Server 各版本搭建文件服务器实现共享文件(03~19)

一、Windows Server 2003 打开服务器&#xff0c;点击左下角开始➡管理工具➡管理您的服务器➡添加或删除角色 点击下一步等待测试 勾选自定义配置&#xff0c;点击下一步 选择文件服务器&#xff0c;点击下一步 勾选设置默认磁盘空间&#xff0c;数据自己更改&#xff0c;最…...

ARM总结and复习

安装交叉编译工具链 a. 为什么安装 因为arm公司的指令集在不断迭代升级&#xff0c;指令集日益增多,而架构是基于指令集研发的&#xff0c;所以架构不一样&#xff0c;指令集也不一样 eg:arm架构使用的是arm指令集 x86架构使用的是x86指令集 而我们日常开发环境中linux的架构…...

非功能测试的定义、类型和示例

软件已从推动者转变为不同行业企业成功的核心支柱。因此&#xff0c;非功能测试活动成为人们关注的焦点。然而&#xff0c;许多技术和质量保证专业人员并没有意识到非功能测试的必要性。 他们必须了解什么是非功能测试以及为什么必须鼓励将其作为企业应用程序开发项目的实践。…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...