当iPhone垂直时,CMDeviceMotion偏航值不稳定

时间:2012-05-21 20:43:42

标签: iphone ios cocoa-touch sensor cmmotionmanager

在iOS原型中,我使用CMDeviceMotion.deviceMotion.yaw和CLHeading.trueHeading的组合来制作响应准确且稳定的罗盘标题。当iPhone保持平坦时,这种方法很有效,我有一个指向稳定罗盘标题的图形箭头。

当iPhone在移植模式下保持垂直时,会出现问题。 UIDeviceOrientation不断从UIDeviceOrientationFaceDown更改为UIDeviceOrientationFaceUp并返回。这使得偏航值基于音调的微小变化来回跳跃+/- 180度。是否可以将设备锁定在一个方向上,该方向可以提供稳定的偏航值,预测变化时不会出现毛刺或以其他方式计算陀螺偏航(或以此方向滚动)?

这个可怜的家伙有同样的问题,没有答案。双点可能的人! :) https://stackoverflow.com/questions/10470938/euler-angle-yaw-not-working-when-iphone-orientation-changes

3 个答案:

答案 0 :(得分:14)

我只是在寻找这个问题的答案。它让我心碎了一下,看到你在一年前发布了这个,但我想也许你或其他人可以从解决方案中受益。

问题是万向节锁定。当俯仰大约90度时,偏航和滚动匹配并且陀螺仪失去一定的自由度。四元数是避免万向节锁定的一种方法,但老实说,我并不想绕过它。相反,我注意到偏航和滚动实际上是匹配的,可以简单地总结为解决问题(假设你只关心偏航)。

<强> SOLUTION:

    float yawDegrees = currentAttitude.yaw * (180.0 / M_PI);
    float pitchDegrees = currentAttitude.pitch  * (180.0 / M_PI);
    float rollDegrees = currentAttitude.roll * (180.0 / M_PI);

    double rotationDegrees;
    if(rollDegrees < 0 && yawDegrees < 0) // This is the condition where simply
                                          // summing yawDegrees with rollDegrees
                                          // wouldn't work.
                                          // Suppose yaw = -177 and pitch = -165. 
                                          // rotationDegrees would then be -342, 
                                          // making your rotation angle jump all
                                          // the way around the circle.
    {
        rotationDegrees = 360 - (-1 * (yawDegrees + rollDegrees));
    }
    else
    {
        rotationDegrees = yawDegrees + rollDegrees;
    }

    // Use rotationDegrees with range 0 - 360 to do whatever you want.

我希望这有助于其他人!

答案 1 :(得分:2)

如果有人对iOS Swift中的实现感兴趣,则代码如下:

    let queue = NSOperationQueue()
    motionManager.startDeviceMotionUpdatesToQueue(queue) {
        [weak self] (data: CMDeviceMotion!, error: NSError!) in
    var yawDegrees: Double = self!.motionManager.deviceMotion.attitude.yaw * (180.0 / M_PI)
        var pitchDegrees: Double = self!.motionManager.deviceMotion.attitude.pitch * (180.0 / M_PI)
        var rollDegrees: Double = self!.motionManager.deviceMotion.attitude.roll * (180.0 / M_PI)


        if(rollDegrees < 0 && yawDegrees < 0){
            self!.rotationDegrees = 360 - (-1 * (yawDegrees + rollDegrees))
        }
        else {
            self!.rotationDegrees = yawDegrees + rollDegrees
        }
   }

然而我遇到了一些问题,我希望@ blkhp19可以帮助我解决这个问题,因为在某些点上角度会变成负值,然后会影响整个计算,我无法弄清楚问题是什么。

答案 2 :(得分:0)

问题有点令人困惑,因为至少有两种不同的方式来考虑偏航。一种是从手机的角度来看,一种是从世界的角度来看。

我将使用此image from Apple进行进一步说明:

enter image description here

如果电话放在桌子上

  • 沿手机的偏航角度(或Z轴)旋转:更改指南针的方向。
  • 沿手机滚动方向(或Y轴)旋转:请勿更改指南针方向。

如果手机靠墙平坦

  • 沿手机的偏航角度(或Z轴)旋转:请勿更改指南针的航向。
  • 沿手机滚动方向(或Y轴)旋转:更改指南针的方向。

如果您希望手机的罗盘方向为垂直方向

使用blkhp19's code above来总结偏航。如果导入SwiftUI,则可以利用Angle结构简化弧度+度转换:

let yaw = Angle(radians: deviceMotion.attitude.yaw)
let roll = Angle(radians: deviceMotion.attitude.roll)

var compassHeading: Angle = yaw + roll
if roll.degrees < 0 && yaw.degrees < 0 {
  compassHeading = Angle(degrees: 360 - (-1 * compassHeading.degrees))
}

如果要在垂直时沿手机的偏航轴旋转

您将需要使用atan2并像this example一样检查重力。

let rotation = atan2(data.gravity.x,
                             data.gravity.y) - .pi

还要注意,如果您不需要实际角度,而只需要关系(例如isPhoneUpright),则可以简单地读取这些角度的重力值。