如何在iOS 5中使用CMDeviceMotion获取设备的标题

时间:2012-02-18 12:41:45

标签: ios objective-c augmented-reality heading

我正在使用陀螺开发AR应用程序。我使用了一个苹果代码示例pARk。它使用旋转矩阵来计算坐标的位置并且它确实很好,但是现在我正在尝试实现“雷达”,我需要在设备标题的功能中旋转它。我正在使用CLLocationManager标题,但它不正确。

问题是,如何使用CMAttitude获取设备的标题以反映我在屏幕中得到的确切内容?

我是旋转矩阵的新手和那种东西。

这是用于计算AR坐标的代码的一部分。以态度更新cameraTransform:

CMDeviceMotion *d = motionManager.deviceMotion;
if (d != nil) {
    CMRotationMatrix r = d.attitude.rotationMatrix;
    transformFromCMRotationMatrix(cameraTransform, &r);
[self setNeedsDisplay];
}

然后在drawRect代码中:

mat4f_t projectionCameraTransform;
multiplyMatrixAndMatrix(projectionCameraTransform, projectionTransform, cameraTransform);

int i = 0;
for (PlaceOfInterest *poi in [placesOfInterest objectEnumerator]) {
    vec4f_t v;
    multiplyMatrixAndVector(v, projectionCameraTransform, placesOfInterestCoordinates[i]);

    float x = (v[0] / v[3] + 1.0f) * 0.5f;
    float y = (v[1] / v[3] + 1.0f) * 0.5f;

我还用俯仰角旋转视图。 运动更新使用北方开始:

[motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical];

所以我认为必须有可能在任何位置获得设备的“滚动”/标题(任何俯仰和偏航......)但我不知道如何。

1 个答案:

答案 0 :(得分:16)

有几种方法可以从CMDeviceMotion返回的旋转矩阵中计算出航向。这假设您使用与Apple指南针相同的定义,其中指向正北的+ y方向(iPhone顶部)返回标题0,向右旋转iPhone会增加标题,因此East为90,South为180等等。

首先,当您开始更新时,请务必检查以确保标题可用:

if (([CMMotionManager availableAttitudeReferenceFrames] & CMAttitudeReferenceFrameXTrueNorthZVertical) != 0) {
   ...
}

接下来,当你启动动作管理器时,要求态度为从X指向真北的旋转(或者如果你出于某种原因需要它,则为磁北):

[motionManager startDeviceMotionUpdatesUsingReferenceFrame: CMAttitudeReferenceFrameXTrueNorthZVertical
                                                   toQueue: self.motionQueue
                                               withHandler: dmHandler];

当运动管理器报告动作更新时,您想要了解设备在X-Y平面中的旋转程度。由于我们对iPhone的顶部感兴趣,我们将在该方向上选择一个点并使用返回的旋转矩阵旋转它以获得旋转后的点:

   [m11 m12 m13] [0]   [m12]
   [m21 m22 m23] [1] = [m22]
   [m31 m32 m33] [0]   [m32]

时髦的括号是矩阵;这是我用ASCII做的最好的。 :)

标题是旋转点与真北之间的角度。我们可以使用旋转点的X和Y坐标来提取反正切,这将给出点和X轴之间的角度。这实际上与我们想要的相差180度,因此我们必须相应地进行调整。结果代码如下所示:

CMDeviceMotionHandler dmHandler = ^(CMDeviceMotion *aMotion, NSError *error) {
    // Check for an error.
    if (error) {
        // Add error handling here.
    } else {
        // Get the rotation matrix.
        CMAttitude *attitude = self.motionManager.deviceMotion.attitude;
        CMRotationMatrix rm = attitude.rotationMatrix;

        // Get the heading.
        double heading = PI + atan2(rm.m22, rm.m12);
        heading = heading*180/PI;
        printf("Heading: %5.0f\n", heading);
    }
};

有一个问题:如果iPhone的顶部笔直向上或笔直向下,则方向未定义。结果是m21和m22为零或非常接近它。您需要确定这对您的应用程序意味着什么,并相应地处理条件。例如,当m12 * m12 + m22 * m22接近零时,您可能会切换到基于-Z轴(在iPhone后面)的标题。


这一切都假设你想围绕X-Y平面旋转,就像苹果通常为他们的指南针做的那样。它的工作原理是因为您正在使用运动管理器返回的旋转矩阵来旋转沿Y轴指向的矢量,即此矩阵:

[0]
[1]
[0]

要旋转另一个向量 - 比如,一个指向-Z的向量 - 使用不同的矩阵,如

[0]
[0]
[-1]

当然,您还必须将弧切线放在不同的平面上,而不是

double heading = PI + atan2(rm.m22, rm.m12);

你会用

double heading = PI + atan2(-rm.m33, -rm.m13);

获取X-Z平面的旋转。