【问题标题】:DeviceMotion relative to world - multiplyByInverseOfAttitudeDeviceMotion 相对于世界 - multiplyByInverseOfAttitude
【发布时间】:2011-12-18 12:04:57
【问题描述】:

CMAttitude:multiplyByInverseOfAttitude 的正确使用方法是什么?

假设一个 iOS5 设备平放在桌子上,在启动 CMMotionManager 之后:

CMMotionManager *motionManager = [[CMMotionManager alloc]init];
[motionManager startDeviceMotionUpdatesUsingReferenceFrame:
    CMAttitudeReferenceFrameXTrueNorthZVertical];

之后,得到CMDeviceMotion对象:

CMDeviceMotion *deviceMotion = [motionManager deviceMotion];

我希望 [deviceMotion 姿态] 反映设备从正北方向的旋转。

通过观察,[deviceMotion userAcceleration] 报告设备参考系中的加速度。也就是说,将设备左右移动(使其平放在桌面上)会记录 x 轴上的加速度。将设备旋转 90°(仍然是平的)并左右移动设备仍会报告 x 加速度。

将 [deviceMotion userAcceleration] 转换为获得南北/东西加速度而不是左右/前进后退的正确方法是什么?

CMAttitude multiplyByInverseOfAttitude 似乎没有必要,因为已经指定了参考框架,并且从文档中不清楚如何将姿态应用于 CMAcceleration。

【问题讨论】:

    标签: ios core-motion


    【解决方案1】:

    如果 CMDeviceMotion 在参考框架的坐标中具有 userAcceleration 的访问器,则不会出现此问题。所以,我使用了一个类别来添加所需的方法:

    在 CMDeviceMotion+TransformToReferenceFrame.h 中:

    #import <CoreMotion/CoreMotion.h>
    
    @interface CMDeviceMotion (TransformToReferenceFrame)
    -(CMAcceleration)userAccelerationInReferenceFrame;
    @end
    

    在 CMDeviceMotion+TransformToReferenceFrame.m 中:

    #import "CMDeviceMotion+TransformToReferenceFrame.h"
    
    @implementation CMDeviceMotion (TransformToReferenceFrame)
    
    -(CMAcceleration)userAccelerationInReferenceFrame
    {
        CMAcceleration acc = [self userAcceleration];
        CMRotationMatrix rot = [self attitude].rotationMatrix;
    
        CMAcceleration accRef;
        accRef.x = acc.x*rot.m11 + acc.y*rot.m12 + acc.z*rot.m13;
        accRef.y = acc.x*rot.m21 + acc.y*rot.m22 + acc.z*rot.m23;
        accRef.z = acc.x*rot.m31 + acc.y*rot.m32 + acc.z*rot.m33;
    
        return accRef;
    }
    
    @end
    

    在 Swift 3 中

    extension CMDeviceMotion {
    
        var userAccelerationInReferenceFrame: CMAcceleration {
            let acc = self.userAcceleration
            let rot = self.attitude.rotationMatrix
    
            var accRef = CMAcceleration()
            accRef.x = acc.x*rot.m11 + acc.y*rot.m12 + acc.z*rot.m13;
            accRef.y = acc.x*rot.m21 + acc.y*rot.m22 + acc.z*rot.m23;
            accRef.z = acc.x*rot.m31 + acc.y*rot.m32 + acc.z*rot.m33;
    
            return accRef;
        }
    }
    

    现在,以前使用 [deviceMotion userAcceleration] 的代码可以改用 [deviceMotion userAccelerationInReferenceFrame]。

    【讨论】:

    • 这很有趣,很好。我假设您将它与指南针 (CLHeading) 结合使用。你在用你从加速中得到的数字做什么?
    • 您的答案不正确。为了将向量从参考系 A 移动到参考系 B,当给定从 B 到 A 的旋转矩阵时,该向量应该乘以逆旋转矩阵。这是计算逆矩阵的方法stackoverflow.com/a/984054/204533
    • @Vitaly,感谢您的反馈。该代码可操作且工作正常;你试过吗?您的链接提供了一种反转任意矩阵的方法,但“旋转矩阵的逆只是它的转置”(mathpages.com/home/kmath593/kmath593.htm)。这很微妙,但代码在计算中转置了旋转矩阵。
    • @Zaq,我收回我的话,你是对的。看看我的旋转矩阵求逆代码,这是一个任意矩阵求逆代码,它产生转置矩阵。显然,旋转矩阵逆是它的转置,因为有两个事实:a)行列式始终为 1,因为旋转时向量不缩放;b)旋转矩阵是正交矩阵。
    • @xmkevinchen,感谢您的关注。该代码完全按照您的建议进行。 VitalyStakhov 意识到,代码在计算中转置了旋转矩阵。
    【解决方案2】:

    根据 Apple 的文档,CMAttitude 指的是身体相对于给定参考系的方向。 userAccelerationgravity 是设备帧的值。所以为了得到参考帧的值。我们应该按照@Batti 说的做

    1. 每次更新时取姿态旋转矩阵。
    2. 计算逆矩阵。
    3. 将逆矩阵乘以 UserAcceleration 向量。

    这是 Swift 版本

    import CoreMotion
    import GLKit
    
    extension CMDeviceMotion {
    
        func userAccelerationInReferenceFrame() -> CMAcceleration {
    
            let origin = userAcceleration
            let rotation = attitude.rotationMatrix
            let matrix = rotation.inverse()
    
            var result = CMAcceleration()
            result.x = origin.x * matrix.m11 + origin.y * matrix.m12 + origin.z * matrix.m13;
            result.y = origin.x * matrix.m21 + origin.y * matrix.m22 + origin.z * matrix.m23;
            result.z = origin.x * matrix.m31 + origin.y * matrix.m32 + origin.z * matrix.m33;
    
            return result
        }
    
        func gravityInReferenceFrame() -> CMAcceleration {
    
            let origin = self.gravity
            let rotation = attitude.rotationMatrix
            let matrix = rotation.inverse()
    
            var result = CMAcceleration()
            result.x = origin.x * matrix.m11 + origin.y * matrix.m12 + origin.z * matrix.m13;
            result.y = origin.x * matrix.m21 + origin.y * matrix.m22 + origin.z * matrix.m23;
            result.z = origin.x * matrix.m31 + origin.y * matrix.m32 + origin.z * matrix.m33;
    
            return result
        }
    }
    
    extension CMRotationMatrix {
    
        func inverse() -> CMRotationMatrix {
    
            let matrix = GLKMatrix3Make(Float(m11), Float(m12), Float(m13), Float(m21), Float(m22), Float(m23), Float(m31), Float(m32), Float(m33))
            let invert = GLKMatrix3Invert(matrix, nil)
    
            return CMRotationMatrix(m11: Double(invert.m00), m12: Double(invert.m01), m13: Double(invert.m02),
                                m21: Double(invert.m10), m22: Double(invert.m11), m23: Double(invert.m12),
                                m31: Double(invert.m20), m32: Double(invert.m21), m33: Double(invert.m22))
    
        }
    
    }
    

    希望对你有所帮助

    【讨论】:

      【解决方案3】:

      在阅读了上面链接的论文后,我尝试实施解决方案。

      步骤如下:

      • 每次更新时取姿态旋转矩阵。
      • 计算逆矩阵。
      • 将逆矩阵乘以 UserAcceleration 向量。

      合成向量将是向量的投影。

      -x 北,+x 南

      -y 东,+y 西

      我的代码还不完美,我正在努力。

      【讨论】:

      • 我可以为俯仰和滚动也这样做>?
      • 可以分享代码吗?或解释上述步骤。我得到的只是存储您要创建的姿态作为参考,计算当前姿态的倒数
      【解决方案4】:

      参考系与姿态值有关,看偏航角的姿态值;如果您不使用参考框架,当您启动应用程序时,此值始终为零,相反,如果您使用参考框架 CMAttitudeReferenceFrameXTrueNorthZVertical,偏航值表示 x 轴和真北之间的角度。 通过这些信息,您可以识别手机在地球坐标中的姿态,从而识别加速度计轴相对于基点的位置。

      【讨论】:

      • 谢谢@batti,我了解几何,问题是关于编码的。 API 似乎不支持应用转换的简单方法。在 CMAttitude 中,欧拉角、四元数表示和旋转矩阵都很容易访问,但没有明显的方法可以将这些应用于 CMAcceleration。更糟糕的是,CMAcceleration 是只读的,所以我不能自己转换它并在发送之前更新对象。
      • 问题是加速度计的le轴与设备是固定的,所以加速度计可以测量设备框架内的加速度。如果传感器没有那么嘈杂,您应该只找到加速度的水平面,垂直于重力矢量的平面,查看该平面上的矢量并计算矢量与北之间的角度以了解加速度的方向在红雀坐标中。你的应用是做什么的?
      • 这应该会有所帮助,第 3.A 节 link
      猜你喜欢
      • 2018-06-04
      • 2015-04-08
      • 1970-01-01
      • 2015-06-07
      • 2020-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多