作者:Prithiv Dev Devendran
随着空间计算平台(VR和AR)的出现,计算的景观正在经历深刻的转变。当我们进入这个新时代,虚拟现实的交汇处,增强现实,在设备机器学习为开发人员创建的体验提供了空前的机会,这些体验将数字内容与物理世界无缝融合在一起。
引入 Visionos在这一演变中标志着一个重要的里程碑。苹果空间计算平台将复杂的硬件功能与强大的开发框架相结合,使开发人员能够构建可以实时理解和与物理环境进行交互的应用程序。空间意识和设备机器学习能力的这种融合为对象识别和跟踪应用程序提供了新的可能性,这些应用程序以前具有挑战性。
在本指南中,我们将构建一个应用程序,以展示Visionos中设备机器学习的力量。我们将创建一个可以实时识别和跟踪饮食苏打水的应用程序,直接在用户视野中叠加视觉指示器和信息。
我们的应用程序将利用Visionos生态系统中的几种关键技术。当用户运行该应用程序时,他们会使用一个窗口,其中包含目标对象的旋转3D模型以及使用说明。当他们环顾环境时,该应用程序不断扫描减肥苏打罐。在发现后,它在罐子周围显示动态边界线,并在其上方放置浮动文本标签,同时随着对象或用户在空间中移动时保持精确的跟踪。
在开始开发之前,让我们确保我们拥有必要的工具和理解。本教程需要:
从捕获目标对象的3D模型到实现实时跟踪和可视化,开发过程将使我们经历几个关键阶段。每个阶段都建立在上一个阶段,使您可以彻底了解开发功能,该功能由Visionos的设备机器学习提供动力。
创建我们的对象识别系统的第一步涉及捕获目标对象的详细3D模型。苹果为此提供了一个强大的应用程序: 真人秀,可通过App Store可用于iOS。
捕获3D模型时,环境条件在我们的结果质量中起着至关重要的作用。正确设置捕获环境可确保我们为机器学习模型获得最佳数据。光线充足的空间具有一致的照明,可帮助捕获系统准确检测对象的特征和尺寸。饮食苏打水应以良好的对比度放置在表面上,使系统更容易区分物体的边界。
捕获过程首先启动 真人秀应用程序并从可用选项中选择“对象捕获”。该应用程序指导我们在目标对象周围定位一个边界框。这个边界框至关重要,因为它定义了我们捕获量的空间边界。
一旦我们在应用程序内指南的帮助下捕获了苏打水的所有细节,然后处理了图像, .usdz将创建包含我们3D模型的文件。此文件格式是专门为AR/VR应用程序设计的,不仅包含我们对象的视觉表示,还包含将在培训过程中使用的重要信息。
借助3D模型,我们进入下一个关键阶段:使用 创建ML。苹果 创建ML应用程序为训练机学习模型提供了直接的界面,包括用于空间计算应用程序的专业模板。
为了开始培训过程,我们启动 创建ML并从空间类别中选择对象跟踪模板。该模板是专门为可以识别和跟踪三维空间中对象的训练模型而设计的。
创建一个新项目后,我们导入 .usdzâ 将其归档到创建ML。该系统会自动分析3D模型,并提取将用于识别的关键功能。该接口提供了配置应如何在空间中识别我们的对象的选项,包括查看角度和跟踪首选项。
一旦您进口了3D模型并以各种角度进行了分析,请继续单击火车。 创建ML将处理我们的模型并开始训练阶段。在此阶段,系统学会了从各个角度和不同条件下识别我们的对象。培训过程可能需要几个小时,因为系统对我们的对象的特征有了全面的了解。
这个训练过程的输出是 .ReferenceObject文件,其中包含针对Visionos中实时对象检测的训练有素的模型数据。该文件封装了所有学习的功能和识别参数,这些功能和识别参数将使我们的应用程序能够在用户环境中识别饮食苏打水。
我们的参考对象的成功创建标志着我们开发过程中的重要里程碑。现在,我们有一个训练有素的模型,能够实时识别我们的目标对象,为在我们的Visionos应用中实现实际检测和可视化功能奠定了基础。
现在,我们已经有了训练有素的参考对象,让我们建立Visionos项目。发射 XCode然后选择创建一个新的Xcode项目。在模板选择器中,在平台过滤器下选择Visionos,然后选择应用程序。该模板提供了Visionos应用所需的基本结构。
在“项目配置”对话框中,使用以下主要设置配置项目:
创建项目后,我们需要进行一些基本修改。首先,删除名为字的文件 toggleimmersivespacebutton.swift•我们将在实施中使用它。
接下来,我们将向项目添加我们先前创建的资产。在Xcode的项目导航器中,找到REALE KITCONTENT.RKASSET文件夹并添加3D对象文件(âsodamodel.usdz文件)。该3D模型将在我们的信息视图中使用。创建一个名为â的新组参考原理并添加Diet Soda.referenceobject我们使用创建ML生成的文件。
最后的设置步骤是配置对象跟踪的必要权限。打开您的项目 info.plist文件并添加一个新密钥: nsworldsensingusagedescription。将其价值设置为用于跟踪饮食苏打水的价值。该应用程序在用户环境中检测和跟踪对象需要此权限。
完成这些设置步骤后,我们准备了一个正确配置的Visionos项目,可以实现我们的对象跟踪功能。
让我们从 sodatrackerapp.swift,当我们设置Visionos项目时,它会自动创建。我们需要修改此文件以支持我们的对象跟踪功能。用以下代码替换默认实现:
进口Swiftui/**SodatrackerApp是应用程序的主要入口点。它配置了应用程序的窗口和沉浸式空间,并管理对象检测功能的初始化。该应用程序自动启动到沉浸式体验用户可以在哪里看到节食苏打水罐,并突出显示在他们的环境中。*/@主要的struct sodatrackerapp:app {///管理对象检测状态的共享模型@StateObject private var appmodel = appmodel()///推出沉浸体验的系统环境价值@environment(\。openimmersivespace)var openimmersivespacevar身体:某些场景{WindowGroup {contentview()。。任务 {//加载并准备对象检测功能等待AppModel.InitializedEtector()}.onappear {任务 {//直接进入身临其境的体验等待openimmersivespace(id:appmodel.immersives -paceid)}}}.Windowstyle(.plain)。//配置沉浸空间以进行对象检测inmersivespace(id:appmodel.immersivespaceid){imsiveview()环境(AppModel)}//使用混合沉浸式将虚拟内容与现实融合.immersionStyle(选择:.constant(.mixed),in:.mixed)//隐藏系统UI具有更严肃的体验。}}
该实现的关键方面是我们对象检测系统的初始化和管理。当应用启动时,我们初始化 AppModel处理的 arkit会话和对象跟踪设置。初始化序列至关重要:
。任务 {等待AppModel.InitializedEtector()}
这种异步初始化加载了我们训练的参考对象,并准备了 arkit•用于对象跟踪的会话。我们确保在打开实际检测的沉浸式空间之前发生这种情况。
沉浸空间配置对于对象跟踪特别重要:
.immersionStyle(选择:.constant(.mixed),in:.mixed)
混合沉浸式样式对于我们的对象跟踪实现至关重要,因为它允许 RealityKit`将我们的视觉指示器(边界框和标签)与我们重新检测对象的现实环境混合在一起。这创建了一种无缝的体验,其中数字内容可以准确地与用户空间中的物理对象保持一致。
进行这些修改 sodatrackerapp.swift,我们的应用程序可以使用ARKIT,REALEKIT和我们训练有素的模型在混合现实环境中共同工作。在下一节中,我们将检查核心对象检测功能 AppModel.Swift,在项目设置期间创建的另一个文件。
AppModel.Swift在项目设置期间创建的,作为我们的核心检测系统。该文件管理 arkit会话,加载我们的训练有素的模型,并协调对象跟踪过程。让我们研究其实施:
进口Swiftui导入RealityKit导入arkit/**AppModel用作苏打可以检测应用程序的核心模型。它管理ARKIT会话,处理对象跟踪初始化,并在整个应用程序的生命周期中保持对象检测状态。该模型旨在与Visionos的对象跟踪功能合作,专门针对在用户环境中检测饮食苏打水罐的优化。*/@mainactor@observable类AppModel:ObservableObject {///对物体检测发生的沉浸空间的唯一标识符让ImmersivSpaceId =“ sodatracking”///管理核心跟踪功能的ARKIT会话实例///本会话与Visionos协调以处理空间数据私人var Arsession = Arkitsession()///专门处理苏打罐的实时跟踪的提供商///这维护当前跟踪对象的状态私人var sodatracker:objectTrackingProvider?///收集用于检测的参考对象///这些对象包含用于识别苏打罐的训练有素的模型数据私有var targetObjects:[referentObject] = []/**通过加载和准备来初始化对象检测系统来自App捆绑包的参考物体(Diet Soda Can)。此方法加载了包含空间和的预训练模型有关苏打水的视觉信息我们可以检测到。*/func InitializedEtector()async {Guard Let Objecturl = Bundle.Main.URL(前面:“ Diet Soda”,withextension:“ ReferenceObject”)else {打印(“错误:无法在捆绑中找到参考对象 - 确保苏打水。返回}做 {让ReferentaBject =尝试等待ReferenceObject(来自:Objecturl)self.targetObjects = [ReferentoBject]} 抓住 {打印(“错误:无法初始化参考对象:\(error)”)}}/**使用ARKIT启动活动对象检测过程。此方法用加载的参考对象初始化跟踪提供商并开始在用户环境中的实时检测过程。返回:如果成功初始化的ObjectTrackingProvider,则无需*/func begintection()async-> objectTrackingProvider?{Guard!targetObject.isempty else {return nil}令tracker = objectTrackingProvider(ReferenceObjects:TargetObjects)做 {尝试等待Arsession.run([Tracker])self.sodatracker =跟踪器返回跟踪器} 抓住 {打印(“错误:无法初始化跟踪:\(error)”)返回无}}/**终止对象检测过程。此方法安全停止了ARKIT会话并清理当不再需要对象检测时跟踪资源。*/func endDetection(){Arsession.stop()}}
我们实施的核心是 arkitsession,Visionos的空间计算功能的门户。。 @mainactor属性确保我们的对象检测操作在主线程上运行,这对于与渲染管道同步至关重要。
私人var Arsession = Arkitsession()私人var sodatracker:objectTrackingProvider?私有var targetObjects:[referentObject] = []
。 ObjectTrackingProvider是Visionos中的专门组件,可处理实时对象检测。它与 ReferenceObject实例,其中包含来自我们训练的模型中的空间和视觉信息。我们将其视为私人特性,以确保适当的生命周期管理。
初始化过程尤其重要:
让ReferentaBject =尝试等待ReferenceObject(来自:Objecturl)self.targetObjects = [ReferentoBject]
在这里,我们将经过训练的模型(我们在Create ML中创建的.ReferenceObject文件)加载到A中 ReferenceObject实例。此过程是异步的,因为系统需要解析并准备模型数据以进行实时检测。
开始的探测方法设置了实际的跟踪过程:
令tracker = objectTrackingProvider(ReferenceObjects:TargetObjects)尝试等待Arsession.run([Tracker])
当我们创建 ObjectTrackingProvider,我们传递参考对象。提供商使用这些来建立检测参数 - 寻找什么,匹配的功能以及如何在3D空间中跟踪对象。。 arkitsession.run调用激活跟踪系统,开始对用户环境的实时分析。
Immersiveview.swift在我们最初的项目设置中提供的,管理用户空间中的实时对象检测可视化。此视图处理连续的检测数据流并创建检测对象的视觉表示。这是实施:
进口Swiftui导入RealityKit导入arkit/**ImmerSiveView负责创建和管理增强现实发生对象检测的经验。此视图处理实时在用户环境中可视化检测的苏打罐。它维护每个检测到的对象的视觉表示集合并在检测,移动或删除对象时实时更新它们从视图。*/struct ImmerSiveView:查看{///访问该应用程序共享模型以进行对象检测功能@environment(appmodel.self)私人var appmodel///作为所有AR内容的父级实体///该实体为所有可视化提供了一致的坐标空间@State Private var saceeroot = entity()///将唯一对象标识符映射到其视觉表示///启用特定对象可视化的有效更新@State私有VAR ActiveVisalization:[UUID:ObjectVisualization] = [:]var身体:某些视图{ealleview {content in//用我们的根实体初始化AR场景content.Add(sceneroot)任务 {//开始对象检测并跟踪更改令检测器=等待AppModel.begindetection()守卫探测器其他{return}//处理对象检测的实时更新在detector.anchorupdates {令锚= update.andor令ID = adnawor.id切换update.event {案件。//对象新检测到 - 创建并添加可视化让可视化=对象视觉化(用于:锚定)ActiveVisualization [ID] =可视化sceneroot.addchild(可视化。实用性)案例。//对象移动 - 更新其位置和方向ActiveVisualization [id]?。刷新(使用:锚)案例。//对象不再可见 - 删除其可视化ActiveVisualization [id]?entity.removefromparent()ActiveVisualization.removeValue(forkey:id)}}}}.disappear {//清除视图时清理AR资源清理visualization()}}/**删除所有主动可视化并停止对象检测。当视图不再活动时,确保正确清理AR资源。*/私人弹性清理visalizations(){for(_,可视化)在ActiveVisualization中{可视化。}ActiveVisualization.RemoveAll()AppModel.EndDetection()}}
我们对象跟踪可视化的核心在于检测器 锚定流。这 arkit功能提供了对象检测事件的连续流:
在detector.anchorupdates {令锚= update.andor令ID = adnawor.id切换update.event {案件。//对象首先检测到案例。//对象位置更改案例。//对象不再可见}}
每个 objectanchor 包含有关检测到的苏打罐的关键空间数据,包括其位置,方向和边界框中的3D空间。当检测到一个新对象(.DADED事件)时,我们创建一个可视化,以 RealityKit 相对于物理对象,将以正确的位置呈现。随着对象或用户的移动,任意事件确保我们的虚拟内容与现实世界完全保持一致。
创建一个名为字的新文件 objectvisalization.swift用于处理检测到的对象的视觉表示。该组件负责创建和管理围绕苏打罐出现的边界框和文本覆盖:
导入RealityKit导入arkit导入Uikit进口Swiftui/**对象视觉管理可以检测到苏打水时出现的视觉元素。此类处理出现在对象上方的3D文本标签和在空间中概述所检测到的对象的边界框。*/@mainactor类对象visalization {///包含所有视觉元素的根实体VARENTITY:实体///专门用于边界框可视化的实体私人var边界箱:实体///边界盒线的宽度-0.003提供最佳的可见性,而不会太侵入私人出租宽度:float = 0.003init(用于锚点:objectanchor){实体= entity()boundingbox = entity()//根据检测到的对象的位置设置主实体的转换entity.transform = transform(矩阵:anchor.originfromanchortransform)Entity.iseNabled = Anchor.StrackedCreateFloatingLabel(用于:锚)setupBoundingBox(用于:锚)刷新Boxgeemementry(带有:锚)}/**创建一个悬停在检测对象上方的浮动文本标签。文本使用Avenir下一个字体,以在AR空间和位于稍微上方的物体上方,以供清晰可见度。*/私有弹性func createFloatingLabel(用于锚点:objectanchor){// 0.06单元提供最佳的文本大小,用于在典型距离上查看让标记:float = 0.06//接下来使用Avenir在AR中的清晰度和现代外观令字体= meshresource.font(名称:“ avenir next”,size:cgfloat(labelsize))!令textMesh = meshresource.generateText(“ Diet Soda”,挤出:标记 * 0.15,字体:字体)//创建一种使文本在任何背景中都可以清晰可见的材料var texmaterial = unlitMaterial()textmaterial.color = .init(tint:.erange)令textentity = modelentity(网格:textMesh,材料:[textmaterial])//将文本放置在对象上方,并带有足够的间隙以避免交叉textentity.transform.translation = simd3(Anchor.boundingbox.center.x -TextMesh.bounds.max.x / 2,Anchor.boundingbox.extent.y + labelsize * 1.5,0)实体。}/**创建一个边界框可视化,概述了检测到的对象。使用洋红色的颜色透明度可提供清晰的但是检测到的汽水周围的非分布视觉边界可以。*/私有弹药设置框架(用于锚点:objectancane){令boxmesh = meshresource.generetybox(尺寸:[1.0,1.0,1.0])//为所有边缘创建一个带有洋红色颜色的材料让bundsmaterial = unlitMaterial(颜色:.magenta.withalphacomponent(0.4))//创建具有均匀外观的所有边缘对于_ in 0 .. <12 {LET EDGE = Modelentity(网格:BoxMesh,材料:[boundsmaterial])boundingbox.addchild(边缘)}Entity.Addchild(边界箱)}/**当跟踪对象移动时,会更新可视化。这样可以确保边界框和文本保持准确的定位相对于要跟踪的物理对象。*/func刷新导线(带有锚点:objectanchor){Entity.iseNabled = Anchor.Stracked后卫锚点。其他{return}entity.transform = transform(矩阵:anchor.originfromanchortransform)刷新Boxgeemementry(带有:锚)}/**更新边界框的几何形状以匹配检测到的对象的尺寸。创建一个与物理对象边界完全匹配的精确轮廓同时保持梯度视觉效果。*/私有弹药函数重新启动BoxGeometry(带有锚:Objectancane){LET LEND = Anchor.boundingbox.extentboundingbox.transform.translation = Anchor.BoundingBox.Centerfor(index,edge)boundingbox.children.enumerated(){后卫LET EDGE =边缘?模型性else {继续}开关索引{情况0 ... 3://沿宽度的水平边缘edge.scale = simd3(范围x,utlinewidth,utlineWidth)edge.position = [0,lenty.y / 2 *(索引%2 == 0?-1:1),,范围z / 2 *(索引<2?-1:1)这是给出的案例4 ... 7://沿高度垂直边缘edge.scale = simd3(uterlineWidth,latten.y,utlineWidth)edge.position = [latend.x / 2 *(索引%2 == 0?-1:1),,0,范围z / 2 *(索引<6?-1:1)这是给出的案例8 ... 11://深度边缘edge.scale = simd3(uterlineWidth,uterlineWidth,latend.z)edge.position = [latend.x / 2 *(索引%2 == 0?-1:1),,lenty.y / 2 *(索引<10?-1:1),0这是给出的默认:休息}}}}
边界框创建是我们可视化的关键方面。我们没有使用单个盒网眼,而是构造了形成线框轮廓的12个单独的边缘。这种方法提供了更好的视觉清晰度,并可以更精确地控制外观。边缘使用SIMD3向量定位,以进行有效的空间计算:
edge.position = [latend.x / 2 *(索引%2 == 0?-1:1),,lenty.y / 2 *(索引<10?-1:1),0这是给出的
这种数学定位可确保每个边缘与检测到的对象的维度完美对齐。该计算使用对象的范围(宽度,高度,深度),并在其中心点周围创建对称排列。
该可视化系统与我们的 浸入式观察创建实时视觉反馈。当ImmersiveView从ARKIT接收到位置更新时,它调用了我们的可视化刷新刷新,这会更新转换矩阵,以保持虚拟叠加层和物理对象之间的精确对齐。
contentview.swift,在我们的项目模板中提供的内容处理我们的应用程序的信息界面。这是实施:
进口Swiftui导入RealityKit导入Reality KitContent/**ContentView提供了应用程序的主窗口接口。显示目标物体的旋转3D模型(饮食苏打罐)除了为用户提供有关如何使用检测功能的明确说明。*/struct contentview:查看{//状态控制连续旋转动画@State Private var旋转:double = 0var身体:某些视图{VSTACK(间距:30){//带有旋转动画的3D模型显示model3d(命名:“ sodamodel”,捆绑包:RealeyKitContentBundle)。.frame(宽度:200,身高:200).Rotation3Deffect(.Degrees(旋转),轴:( x:0,y:1,z:0)).onappear {//创建连续旋转动画持续(.linear(持续时间:5.0).repeatForever(Autorevers:true)){旋转= 180}}//用户说明vstack(间距:15){文本(“饮食汽水检测”).font(.title)。文字(“将您的饮食苏打水塞在您面前,以便在您的空间中自动检测并突出显示。”).font(.body)。。.padding(.horizontal)}}。填充().Frame(MaxWidth:400)}}
此实现将显示我们的3D扫描苏打型(Sodamodel.usdz),并带有旋转动画,为用户提供了对系统所需的内容的清晰参考。旋转有助于用户了解如何显示对象以进行最佳检测。
有了这些组件,我们的应用程序现在提供了完整的对象检测体验。该系统使用我们的训练有素的模型来识别饮食苏打水罐,实时创建精确的视觉指标,并通过信息界面提供明确的用户指导。
In this tutorial, weâve built a complete object detection system for visionOS that showcases the integration of several powerful technologies. Starting from 3D object capture, through ML model training in Create ML, to real-time detection using ARKit and RealityKit, weâve created an app that seamlessly detects and tracks objects in the userâs space.
This implementation represents just the beginning of whatâs possible with on-device machine learning in spatial computing. As hardware continues to evolve with more powerful Neural Engines and dedicated ML accelerators and frameworks like Core ML mature, weâll see increasingly sophisticated applications that can understand and interact with our physical world in real-time. The combination of spatial computing and on-device ML opens up possibilities for applications ranging from advanced AR experiences to intelligent environmental understanding, all while maintaining user privacy and low latency.