如何获取ARImageAnchor相对于会话启动后添加节点的姿态(pitch、yaw、roll)
如何获取ARImageAnchor相对于会话启动后添加节点的姿态(pitch、yaw、roll)
嘿,我来帮你搞定这个问题!你现在已经能拿到ARImageAnchor对应节点在世界坐标系下的欧拉角,但想要的是它相对于之后添加的圆柱节点的姿态对吧?其实核心就是利用SceneKit自带的节点坐标转换能力来实现,下面给你一步步拆解方案:
关键思路
首先得明确两个节点的关系:
- ARImageAnchor绑定的
node是锚点在世界空间中的根节点 - 你添加的
cylinderNode默认是这个锚点节点的子节点,它的坐标系是相对父节点的。但如果要计算锚点节点相对于圆柱节点的姿态,就需要用坐标转换API把锚点的姿态映射到圆柱节点的坐标系中。
具体实现步骤
先保存目标节点的引用
你需要把后续要作为参考的cylinderNode存为实例变量,这样后续随时能访问它。在你的类里添加:var targetReferenceNode: SCNNode?在锚点添加时初始化并计算相对姿态
修改你原来的didAdd node方法,保存节点并进行转换计算:func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { guard let _ = anchor as? ARImageAnchor else { return } DispatchQueue.main.async { let cylinderNode = SCNNode() cylinderNode.geometry = SCNCylinder(radius: 0.05, height: 0.4) node.addChildNode(cylinderNode) self.targetReferenceNode = cylinderNode // 保存参考节点 // 计算锚点节点相对于圆柱节点的姿态 if let targetNode = self.targetReferenceNode { // 将锚点节点的世界变换转换到圆柱节点的坐标系下 let relativeTransform = node.convertTransform(SCNMatrix4Identity, to: targetNode) // 从变换矩阵中提取欧拉角(对应pitch/yaw/roll) let relativeEuler = SCNVector3( atan2(relativeTransform.m32, relativeTransform.m33), atan2(-relativeTransform.m31, sqrt(relativeTransform.m32*relativeTransform.m32 + relativeTransform.m33*relativeTransform.m33)), atan2(relativeTransform.m21, relativeTransform.m11) ) // 转换为你需要的格式(比如弧度转角度) print(self.convert(relativeEuler.x), self.convert(relativeEuler.y), self.convert(relativeEuler.z)) } } }(可选)持续跟踪更新相对姿态
因为AR会话中锚点的姿态可能会随着跟踪优化而更新,如果你需要实时获取相对姿态,可以在didUpdate node方法里重复计算:func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { guard anchor is ARImageAnchor, let targetNode = self.targetReferenceNode else { return } DispatchQueue.main.async { let relativeTransform = node.convertTransform(SCNMatrix4Identity, to: targetNode) let relativeEuler = SCNVector3( atan2(relativeTransform.m32, relativeTransform.m33), atan2(-relativeTransform.m31, sqrt(relativeTransform.m32*relativeTransform.m32 + relativeTransform.m33*relativeTransform.m33)), atan2(relativeTransform.m21, relativeTransform.m11) ) print("实时相对姿态:\(self.convert(relativeEuler.x)), \(self.convert(relativeEuler.y)), \(self.convert(relativeEuler.z))") } }
补充说明
- 为什么用变换矩阵而不是直接相减欧拉角?直接相减
node.eulerAngles - targetNode.eulerAngles虽然简单,但欧拉角存在万向锁问题,在某些姿态下会出现异常值,用变换矩阵转换是更稳妥的方案。 - 如果你的
convert方法是把弧度转换为角度,那直接用上面的代码就行;如果是其他转换逻辑,按需调整即可。
备注:内容来源于stack exchange,提问作者Sachinthana Aluvihare
相关产品推荐
相关产品推荐

