实时跟踪中为检测脚部绑定3D物体:ARKit坐标转换求助
解决Vision脚部检测转ARKit 3D坐标的问题
我完全懂你现在的困扰——Vision给出的2D边界框只能告诉你脚在屏幕上的位置,但要在ARKit里放3D物体,必须把这个2D坐标映射到3D世界空间里。之前你的Hit Test没返回结果,大概率是坐标转换出了问题,或者没找对ARKit的正确用法,下面给你两种可行的解决方案:
方案一:把Vision的2D边界框转成ARKit 3D坐标(适配自定义模型)
首先得搞清楚坐标系差异:Vision的归一化边界框原点在图像左上角,而ARKit的屏幕坐标也是左上角原点,但视频帧的尺寸和屏幕实际尺寸可能不匹配,而且还要考虑设备方向的旋转,这是很多人踩坑的地方。
1. 正确转换Vision坐标到屏幕坐标
先把Vision的归一化框转成视频帧上的矩形,再取中心点,最后转换成屏幕上的点(这里假设你用了AVCaptureVideoPreviewLayer来显示视频):
func translateVisionToScreenPoint(observation: VNRecognizedObjectObservation, bufferSize: CGSize, previewLayer: AVCaptureVideoPreviewLayer) -> CGPoint? { // 把Vision的归一化boundingBox转成视频帧上的Rect let imageRect = VNImageRectForNormalizedRect(observation.boundingBox, Int(bufferSize.width), Int(bufferSize.height)) // 取边界框的中心点(视频帧坐标系) let imageCenter = CGPoint(x: imageRect.midX, y: imageRect.midY) // 把视频帧上的点转成屏幕坐标(处理设备方向旋转) return previewLayer.captureDevicePointConverted(fromLayerPoint: imageCenter) }
2. 用ARRaycast获取3D世界坐标(替代旧的Hit Test)
iOS 13之后,ARKit推荐用ARRaycastManager来做射线检测,比旧的hitTest方法更稳定,支持估计平面(不需要提前检测到平面)。
首先初始化射线管理器:
private let raycastManager = ARRaycastManager()
然后在Vision检测到脚部后,用转换后的屏幕点执行射线检测:
// 假设你已经拿到了转换后的screenPoint和ARView实例 guard let screenPoint = translateVisionToScreenPoint(observation: objectObservation, bufferSize: bufferSize, previewLayer: previewLayer), let frame = arView.session.currentFrame else { return } // 创建射线查询:从相机位置向屏幕点方向发射射线,检测估计平面 let query = ARRaycastQuery(origin: frame.camera.transform, direction: frame.camera.unprojectPoint(screenPoint), allowing: .estimatedPlane, alignment: .any) // 执行射线检测,取第一个结果 if let raycastResult = raycastManager.raycast(query).first { // 创建AR锚点,给锚点标记名称方便后续识别 let footAnchor = ARAnchor(transform: raycastResult.worldTransform) footAnchor.name = "FootAnchor" arView.session.add(anchor: footAnchor) }
最后在ARKit的代理方法里添加3D模型:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { // 只处理我们创建的脚部锚点 guard anchor.name == "FootAnchor" else { return } // 加载你的3D模型 guard let shoeScene = SCNScene(named: "ShoeModel.scn") else { return } let shoeNode = shoeScene.rootNode.clone() // 调整模型位置和大小,适配脚部 shoeNode.scale = SCNVector3(0.1, 0.1, 0.1) node.addChildNode(shoeNode) }
注意点
- 确保Vision检测的视频帧和ARKit的帧是同步的,最好在同一个回调里处理两者的结果,避免坐标错位。
- 如果脚部不在平面上(比如悬空),可以把
allowing参数改成.featurePoint,用ARKit检测到的特征点来估算3D位置。
方案二:直接用ARKit人体跟踪(更简单精准)
如果你不需要自定义Vision模型,ARKit自带的ARBodyTrackingConfiguration可以直接跟踪人体骨架,包括脚部的3D坐标,比从2D转3D靠谱得多:
1. 开启人体跟踪配置
let configuration = ARBodyTrackingConfiguration() // 配置只跟踪一个人体(默认就是单人体) configuration.isLightEstimationEnabled = true arView.session.run(configuration)
2. 获取脚部的3D坐标并添加模型
在ARKit的代理方法里,直接从人体锚点的骨架中获取脚部的变换矩阵:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { guard let bodyAnchor = anchor as? ARBodyAnchor else { return } // 获取左脚踝的3D变换矩阵 let leftAnkleTransform = bodyAnchor.skeleton.joint(.leftAnkle).modelTransform // 创建鞋子模型节点 let leftShoe = SCNScene(named: "LeftShoe.scn")!.rootNode.clone() leftShoe.transform = leftAnkleTransform node.addChildNode(leftShoe) // 同理处理右脚踝 let rightAnkleTransform = bodyAnchor.skeleton.joint(.rightAnkle).modelTransform let rightShoe = SCNScene(named: "RightShoe.scn")!.rootNode.clone() rightShoe.transform = rightAnkleTransform node.addChildNode(rightShoe) }
这种方法不需要自己处理坐标转换,ARKit会实时更新人体骨架的3D位置,精度和稳定性都更好,适合脚部跟踪的场景。
内容的提问来源于stack exchange,提问作者Saif
相关产品推荐
相关产品推荐

