13

在 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

4

3 回答 3

16

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

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

解决方案:

    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.

我希望这对其他人有帮助!

于 2013-07-29T18:27:46.127 回答
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 可以帮助我解决这个问题,因为在某些时候角度会变成负值,这会打乱整个计算,我无法弄清楚问题是什么。

于 2015-05-22T08:39:26.357 回答
1

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

我将使用Apple 的这张图片来进一步解释:

在此处输入图像描述

如果手机平放在桌子上

  • 沿手机偏航(或 Z 轴)旋转:更改指南针航向。
  • 沿手机的滚动(或 Y 轴)旋转:不要更改指南针航向。
  • 沿手机的俯仰(或 X 轴)旋转:不要更改罗盘航向。

如果手机平贴在墙上

  • 沿手机偏航(或 Z 轴)旋转:更改指南针航向。
  • 沿手机滚动(或 Y 轴)旋转:更改指南针航向。
  • 沿手机的俯仰(或 X 轴)旋转:不要更改罗盘航向。

对于这个答案的其余部分,我将假设手机是直立的,并且偏航、俯仰和滚动指的是上面照片中的内容。

偏航

您需要像本例atan2中那样使用和检查重力。

let yaw = -Angle(radians: .pi - atan2(motion.gravity.x, motion.gravity.y))

沥青

与上面类似,我主要只是交换了 x 和 z ,它似乎返回了正确的值:

let pitch = Angle(radians: .pi - atan2(motion.gravity.z, motion.gravity.y))

滚动(又名指南针航向)

使用上面 blkhp19 的代码总结了偏航和滚动的姿态。如果您 import SwiftUI,您可以利用该Angle结构使弧度 + 度数转换更容易:

  func roll(motion: CMDeviceMotion) -> Angle {
    let attitudeYaw = Angle(radians: motion.attitude.yaw)
    let attitudeRoll = Angle(radians: motion.attitude.roll)

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

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

extension CMDeviceMotion {
  var yaw: Angle {
    -Angle(radians: .pi - atan2(gravity.x, gravity.y))
  }

  var pitch: Angle {
    Angle(radians: .pi - atan2(gravity.z, gravity.y))
  }

  var roll: Angle {
    let attitudeYaw = Angle(radians: attitude.yaw)
    let attitudeRoll = Angle(radians: attitude.roll)

    var compassHeading: Angle = attitudeYaw + attitudeRoll
    if attitudeRoll.degrees < 0 && attitudeYaw.degrees < 0 {
      compassHeading = Angle(degrees: 360 - (-1 * compassHeading.degrees))
    }
    return compassHeading
  }
}
于 2020-12-09T17:59:16.373 回答