使用CMMotionManager测量倾斜angular度

假设你正在拿着一个iphone / ipad,屏幕正对着你,纵向。 您将设备倾斜到一边,使屏幕朝向您。 如何使用CMMotionManager测量静态倾斜angular度? 这似乎是一个简单的问题,应该有一个简单的答案,但我找不到任何方法不会消失到四元数和旋转matrix。

任何人都可以指向我一个工作的例子吗?

看重gravity

 self.deviceQueue = [[NSOperationQueue alloc] init]; self.motionManager = [[CMMotionManager alloc] init]; self.motionManager.deviceMotionUpdateInterval = 5.0 / 60.0; // UIDevice *device = [UIDevice currentDevice]; [self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical toQueue:self.deviceQueue withHandler:^(CMDeviceMotion *motion, NSError *error) { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ CGFloat x = motion.gravity.x; CGFloat y = motion.gravity.y; CGFloat z = motion.gravity.z; }]; }]; 

用这个参考坐标系( CMAttitudeReferenceFrameXArbitraryZVertical ),如果z接近零,你就把它放在一个垂直于地面的平面上(例如,如果你把它靠在墙上),当你在该平面上旋转时, xy值改变。 垂直是x接近于零, y接近-1。


看这篇文章 ,我注意到如果你想把这个向量转换成angular度,你可以使用下面的algorithm。

如果您想计算垂直旋转设备的旋转angular度(正向为顺时针,负向为逆时针),则可以计算如下:

 // how much is it rotated around the z axis CGFloat angle = atan2(y, x) + M_PI_2; // in radians CGFloat angleDegrees = angle * 180.0f / M_PI; // in degrees 

你可以使用这个来计算出通过Quartz 2D transform属性旋转视图的次数:

 self.view.layer.transform = CATransform3DRotate(CATransform3DIdentity, -rotateRadians, 0, 0, 1); 

(就我个人而言,我更新了startDeviceMotionUpdates方法中的旋转angular度,并在一个CADisplayLink更新了这个transform ,它将angular度更新中的屏幕更新解耦。

你可以看到你通过以下方式向后/向后倾斜了多远?

 // how far it it tilted forward and backward CGFloat r = sqrtf(x*x + y*y + z*z); CGFloat tiltForwardBackward = acosf(z/r) * 180.0f / M_PI - 90.0f; 

这是一个迟到的答案,但你可以在github和它的博客文章 上find一个工作的例子 。

总结上面提到的文章,可以使用四元数来避免垂直拿着iPhone时可能面临的万向节locking问题 。

这是计算倾斜(或偏航)的编码部分:

 CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion; double yaw = asin(2*(quat.x*quat.z - quat.w*quat.y)); // use the yaw value // ... 

你甚至可以添加一个简单的卡尔曼滤波器来缓解偏航:

 CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion; double yaw = asin(2*(quat.x*quat.z - quat.w*quat.y)); if (self.motionLastYaw == 0) { self.motionLastYaw = yaw; } // kalman filtering static float q = 0.1; // process noise static float r = 0.1; // sensor noise static float p = 0.1; // estimated error static float k = 0.5; // kalman filter gain float x = self.motionLastYaw; p = p + q; k = p / (p + r); x = x + k*(yaw - x); p = (1 - k)*p; self.motionLastYaw = x; // use the x value as the "updated and smooth" yaw // ... 

下面是一个旋转UIView self.horizon的示例,以便在倾斜设备时保持水平。

 - (void)startDeviceMotionUpdates { CMMotionManager* coreMotionManager = [[CMMotionManager alloc] init]; NSOperationQueue* motionQueue = [[NSOperationQueue alloc] init] CGFloat updateInterval = 1/60.0; CMAttitudeReferenceFrame frame = CMAttitudeReferenceFrameXArbitraryCorrectedZVertical; [coreMotionManager setDeviceMotionUpdateInterval:updateInterval]; [coreMotionManager startDeviceMotionUpdatesUsingReferenceFrame:frame toQueue:motionQueue withHandler: ^(CMDeviceMotion* motion, NSError* error){ CGFloat angle = atan2( motion.gravity.x, motion.gravity.y ); CGAffineTransform transform = CGAffineTransformMakeRotation(angle); self.horizon.transform = transform; }]; } 

这有点过于简单了 – 您应该确保在您的应用程序中只有一个CMMotionManager实例,所以您要预先初始化并通过属性访问它。

由于iOS8 CoreMotion还会返回一个CMAttitude对象,其中包含pitchrollyaw属性以及四元数。 使用这将意味着你不必做手动math转换加速到方向。