简体   繁体   中英

Core Motion: how to tell which way is “up”?

I'm trying to duplicate the functionality in the Compass app - and I'm stuck on a particular bit: how do I figure out which way is "up" in the interface?

I've got a label onscreen, and I've got the following code that orients it to remain horizontal as the device moves around:

self.motionManager = CMMotionManager()
self.motionManager?.gyroUpdateInterval = 1/100
self.motionManager?.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue(), withHandler: { (deviceMotion, error) -> Void in
  let roll = -deviceMotion.attitude.roll
  self.tiltLabel?.transform = CGAffineTransformRotate(CGAffineTransformIdentity, CGFloat(roll))
})

This effect is pretty good, but it's got a few states where it's wrong - for example, the label flips erratically when the iPhone's lightning connector is pointed up.

How do I consistently tell which direction is up using CoreMotion?

UPDATE: Apparently, roll/pitch/yaw are Euler angles , which suffer from gimbal lock - so I think the correct solution might involve using quaternions , which don't suffer from this issue, or perhaps the rotationMatrix on CMAttitude might help: https://developer.apple.com/library/ios/documentation/CoreMotion/Reference/CMAttitude_Class/index.html

It doesn't need to be quite so complicated for the 2D case. "Up" means "opposite gravity", so:

motionManager.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue()) { (motion, error) in
    // Gravity as a counterclockwise angle from the horizontal.
    let gravityAngle = atan2(Double(motion.gravity.y), Double(motion.gravity.x))

    // Negate and subtract π/2, because we want -π/2 ↦ 0 (home button down) and 0 ↦ -π/2 (home button left).
    self.tiltLabel.transform = CGAffineTransformMakeRotation(CGFloat(-gravityAngle - M_PI_2))
}

But simply "opposite gravity" has less meaning if you're trying to do this in all 3 dimensions: the direction of gravity doesn't tell you anything about the phone's angle around the gravity vector (if your phone is face-up, this is the yaw angle). To correct in three dimensions, we can use the roll, pitch, and yaw measurements instead:

// Add some perspective so the label looks (roughly) the same,
// no matter what angle the device is held at.
var t = self.view.layer.sublayerTransform
t.m34 = 1/300
self.view.layer.sublayerTransform = t

motionManager.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue()) { (motion, error) in
    let a = motion.attitude
    self.tiltLabel.layer.transform =
        CATransform3DRotate(
            CATransform3DRotate(
                CATransform3DRotate(
                    CATransform3DMakeRotation(CGFloat(a.roll), 0, -1, 0),
                    CGFloat(a.pitch), 1, 0, 0),
                CGFloat(a.yaw), 0, 0, 1),
            CGFloat(-M_PI_2), 1, 0, 0) // Extra pitch to make the label point "up" away from gravity
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM