我正在开发一款 iOS 应用,它将使用 CoreMotion 来计算运动范围。

由于万向节锁定,我很快就放弃了欧拉角。所以,现在我正在尝试使用四元数。

您可能知道,CMMotionManager通过CMDeviceMotion类报告设备运动数据。这里的相关属性(为了我的目的)是attitude属性,CMAttitude的一个实例。当然,您可以从中访问pitchrollyaw。在记录结果时我仍然这样做是为了更好地了解从设备中传出的数据(因为这些值非常直观地设想)。它还提供quaternion属性,它是CMQuaternion的一个实例。

基于这里和其他地方的许多小时的研究,我确信使用四元数是在任何方向上获得正确结果的正确方法。问题是我发现CMQuaternion class/structure 非常密集且难以理解。 Apple 的文档很少,我无法退出我认为是CMQuaternion的有效 axis-angle 表示。 (如果某人有从CMQuaternion计算 axis-angle 表示的数学计算,我全都耳朵!)

当我在他们的 GLKit 库中偶然发现 Apple 的GLKQuaternion结构时,我以为我已经解决了这个问题。 GLKit 具有从GLKQuaternion实例提供轴和角度的方法。甚至还有一个很好的构造方法:GLKQuaternionMake(x, y, z, w)

由于CMQuaternion有 x,y,z 和 w 属性,我推断我可以使用此方法在CMQuaternionGLKQuaternion的实例之间基本上“强制转换”。我仍然不确定这是否正确。

无论如何,当我遇到一些特别奇怪的结果时,我正在从我的 iPhone 记录结果。

我编写的代码具有**用于捕获初始姿态(当用户点击按钮时)然后对 CoreMotion 数据进行采样并确定起始位置和当前位置之间的差异的目的。

这是代码:

- (void)sampleDeviceMotion {
    // I've tested this, and "self.startingAttitude" is set/reset correctly
    if (self.startingAttitude == nil) {
        self.startingAttitude = self.motionManager.deviceMotion.attitude;
    }

    CMQuaternion quaternion1  = self.startingAttitude.quaternion;
    GLKQuaternion q1          = GLKQuaternionMake(quaternion1.x, quaternion1.y, quaternion1.z, quaternion1.w);
    GLKVector3 v1             = GLKQuaternionAxis(q1);
    float angle1              = GLKQuaternionAngle(q1);

    CMQuaternion quaternion2  = self.motionManager.deviceMotion.attitude.quaternion;
    GLKQuaternion q2          = GLKQuaternionMake(quaternion2.x, quaternion2.y, quaternion2.z, quaternion2.w);
    GLKVector3 v2             = GLKQuaternionAxis(q2);
    float angle2              = GLKQuaternionAngle(q2);

    float dotProduct          = GLKVector3DotProduct(v1, v2);
    float length1             = GLKVector3Length(v1);
    float length2             = GLKVector3Length(v2);
    float cosineOfTheta       = dotProduct / (length1 * length2);
    float theta               = acosf(cosineOfTheta);
    theta                     = radiansToDegrees(theta);

    float rotationDelta       = angle2 - angle1;
    rotationDelta             = radiansToDegrees(rotationDelta);

    printf("v1: (%.02f, %.02f, %.02f) v2: (%.02f, %.02f, %.02f) angle1: %.02f, angle2: %.02f - vectorDelta: %dº. rotationDelta: %dº (pitch: %.02f, roll: %.02f, yaw: %.02f)\n", v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, angle1, angle2, (int)theta, (int)rotationDelta, self.motionManager.deviceMotion.attitude.pitch, self.motionManager.deviceMotion.attitude.roll, self.motionManager.deviceMotion.attitude.yaw);

}

我已经看了 20 多次这个代码,我没有看到任何缺陷,除非我对CMQuaternionGLKQuaternion之间移动能力的假设是有缺陷的。

我希望当设备平放在桌子上时,点击按钮并保持设备仍然会给我结果,其中q1q2基本相同。我会理解数据中有少量“摆动”,但如果设备不移动,则不同四元数的轴和角度应该(几乎)相同。

有时我会得到它(当点击按钮并且设备保持静止时),但有时“初始”(self.startingAttitude)和后续值会大量关闭(40º-70º)。

此外,在初始值和后续值一致的一种情况下,当我尝试测量旋转时,我得到了一个奇怪的结果。

我“旋转”手机(围绕“Z”轴旋转),并得到以下结果:

之前:

v1:(-0.03,-0.03,-1.00)v2:(0.01,-0.04,-1.00)angle1:0.02,angle2:0.01 - vectorDelta:2º。 rotationDelta:0º(音高:0.00,roll:-0.00,yaw:-0.01)

后:

v1:(-0.03,-0.03,-1.00)v2:(-0.00,-0.01,1.00)angle1:0.02,angle2:0.14 - vectorDelta:176º。 rotationDelta:7º(音高:-0.00,roll:-0.00,yaw:0.14)

我相信 pitch/roll/yaw 数据两次都是正确的。也就是说,我向手机传递了少量的偏航旋转。那么,为什么 Z 轴完全翻转而只有很小的偏航变化?