You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS中基于CMDeviceMotion获取世界坐标系下设备绝对旋转速率的方法

嘿,这个问题我之前做运动传感相关功能时刚好研究过,其实核心就是利用CMAttitude里的旋转矩阵或四元数做坐标变换,把设备本地坐标系的旋转速率映射到你设定的世界坐标系上,我给你一步步拆解下:

先明确两个坐标系

首先得搞清楚我们要转换的两个空间:

  • 设备本地坐标系:iOS标准的右手坐标系——x轴向右,y轴向上,z轴垂直屏幕向外
  • 世界坐标系(以xMagneticNorthZVertical为例):X轴指向磁北,Z轴垂直地面向上,Y轴则垂直于X-Z平面(同样是右手坐标系)

CMRotationRate默认给出的是设备相对于自身本地坐标系的旋转速率,我们要做的就是把这个“相对速率”转换成世界坐标系下的“绝对速率”。

具体实现步骤(代码示例)

1. 初始化运动管理器,设置目标参考帧

首先得让CMMotionManager以你需要的世界参考帧来输出数据:

import CoreMotion

let motionManager = CMMotionManager()
motionManager.deviceMotionUpdateInterval = 0.01 // 100Hz更新,根据需求调整
// 设置参考帧为磁北+垂直地面的世界坐标系
motionManager.startDeviceMotionUpdates(using: .xMagneticNorthZVertical)

2. 利用姿态的逆旋转矩阵做坐标变换

CMAttitude的rotationMatrix是世界坐标系→设备本地坐标系的转换矩阵,那反过来,要把本地的旋转速率转到世界坐标系,就需要用它的逆矩阵(对于正交矩阵来说,逆矩阵等于转置矩阵,用转置计算更高效)。

在更新回调里做转换:

motionManager.startDeviceMotionUpdates(to: .main) { motion, error in
    guard let motion = motion else {
        print("获取设备运动数据失败:\(error?.localizedDescription ?? "未知错误")")
        return
    }
    
    // 本地坐标系下的旋转速率(x/y/z对应设备自身的三个轴)
    let localRate = motion.rotationRate
    // 当前设备的姿态信息
    let attitude = motion.attitude
    
    // 取旋转矩阵的转置(等价于逆矩阵,正交矩阵特性)
    let rotationMatrix = attitude.rotationMatrix
    let transposedMatrix = CMRotationMatrix(
        m11: rotationMatrix.m11, m12: rotationMatrix.m21, m13: rotationMatrix.m31,
        m21: rotationMatrix.m12, m22: rotationMatrix.m22, m23: rotationMatrix.m32,
        m31: rotationMatrix.m13, m32: rotationMatrix.m23, m33: rotationMatrix.m33
    )
    
    // 矩阵乘法:将本地旋转速率向量转换为世界坐标系下的向量
    let worldRateX = transposedMatrix.m11 * localRate.x + transposedMatrix.m12 * localRate.y + transposedMatrix.m13 * localRate.z
    let worldRateY = transposedMatrix.m21 * localRate.x + transposedMatrix.m22 * localRate.y + transposedMatrix.m23 * localRate.z
    let worldRateZ = transposedMatrix.m31 * localRate.x + transposedMatrix.m32 * localRate.y + transposedMatrix.m33 * localRate.z
    
    // 现在worldRateX就是绕世界X轴(磁北)的旋转速率,worldRateZ是绕世界Z轴(垂直地面)的速率
    print("绕磁北轴旋转速率:\(worldRateX) rad/s,绕垂直轴旋转速率:\(worldRateZ) rad/s")
}

替代方案:用四元数变换(更高效)

如果你的场景对性能要求更高,也可以用四元数的共轭来做向量变换,避免矩阵运算的开销:

// 在回调里获取姿态四元数
let quat = attitude.quaternion
// 四元数的共轭(w不变,x/y/z取反)
let conjugateQuat = CMQuaternion(x: -quat.x, y: -quat.y, z: -quat.z, w: quat.w)

// 本地旋转速率向量
let vx = localRate.x
let vy = localRate.y
let vz = localRate.z

// 四元数旋转向量的计算逻辑
let tempX = conjugateQuat.w * vx + conjugateQuat.y * vz - conjugateQuat.z * vy
let tempY = conjugateQuat.w * vy + conjugateQuat.z * vx - conjugateQuat.x * vz
let tempZ = conjugateQuat.w * vz + conjugateQuat.x * vy - conjugateQuat.y * vx
let tempW = -conjugateQuat.x * vx - conjugateQuat.y * vy - conjugateQuat.z * vz

// 最终世界坐标系下的旋转速率
let worldRateX = tempX * quat.w + tempW * (-quat.x) + tempY * (-quat.z) - tempZ * (-quat.y)
let worldRateY = tempY * quat.w + tempW * (-quat.y) + tempZ * (-quat.x) - tempX * (-quat.z)
let worldRateZ = tempZ * quat.w + tempW * (-quat.z) + tempX * (-quat.y) - tempY * (-quat.x)

几个注意点

  • 确保设备的磁力计正常工作(xMagneticNorthZVertical依赖磁力计),如果出现数据异常,可能需要提示用户校准设备
  • 旋转速率的单位是弧度每秒(rad/s),如果需要转换成角度每秒,乘以180 / .pi即可
  • 正交矩阵的逆等于转置,所以用转置矩阵代替逆矩阵计算,结果完全一致,且计算成本更低

内容的提问来源于stack exchange,提问作者Ze Jibe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:50:10