我怎样才能得到iOS设备CMDeviceMotion的标题

我正在开发一个使用陀螺仪的AR应用程序。 我使用苹果代码示例pARk。 它使用旋转matrix来计算坐标的位置,它确实做得很好,但是现在我试图实现一个“雷达”,我需要在设备标题的函数中旋转它。 我正在使用CLLocationManager标题,但它不正确。

问题是,如何使用CMAttitude获取设备的标题以准确反映屏幕上显示的内容?

我是新的旋转matrix和那种东西。

这是用于计算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; 

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

 [motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical]; 

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

有几种方法可以从CMDeviceMotion返回的旋转matrix中计算标题。 这假设你使用苹果指南针的相同定义,其中指向正北的+ y方向(iPhone的顶部)返回标题0,并且向右旋转iPhone增加标题,所以东为90,南为180等等。

首先,当你开始更新时,一定要检查以确保标题是可用的:

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

接下来,当您启动运动pipe理器时,请求态度为从X指向真北(或者由于某种原因需要磁北)的旋转:

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

当运动pipe理器报告运动更新时,您需要了解设备在XY平面中旋转了多less。 因为我们对iPhone的顶部感兴趣,所以我们将在这个方向上select一个点,并使用返回的旋转matrix来旋转它以获得旋转后的点:

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

时髦的括号是matrix; 这是我能用ASCII做的最好的。 🙂

标题是旋转点和真北之间的angular度。 我们可以使用旋转点的X和Y坐标来提取反正切,从而得到该点和X轴之间的angular度。 这实际上与我们想要的相差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后面)的标题。


这一切都假设你想旋转XY平面,正如苹果通常为他们的指南针所做的那样。 这是因为您正在使用运动pipe理器返回的旋转matrix来旋转沿Y轴指向的向量,即此matrix:

 [0] [1] [0] 

要旋转一个不同的vector – 比方说,一个指向-Z的 – 使用一个不同的matrix,就像

 [0] [0] [-1] 

当然,你也必须在不同的平面上采取反正切,所以不是

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

你会用

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

在XZ平面上旋转。