假设您将 iphone/ipad 垂直放在您的面前,屏幕朝向您,纵向放置。您将设备倾斜到一侧,使屏幕始终面向您。您如何使用 CMMotionManager 测量静态倾斜角?这似乎是一个简单的问题,应该有一个简单的答案,但我找不到任何不会消失在四元数和旋转矩阵中的方法。
谁能指出我一个可行的例子?
假设您将 iphone/ipad 垂直放在您的面前,屏幕朝向您,纵向放置。您将设备倾斜到一侧,使屏幕始终面向您。您如何使用 CMMotionManager 测量静态倾斜角?这似乎是一个简单的问题,应该有一个简单的答案,但我找不到任何不会消失在四元数和旋转矩阵中的方法。
谁能指出我一个可行的例子?
看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
接近于零,则您将其保持在与地面垂直的平面上(例如,就好像您将其靠在墙上一样)并且当您在该平面上旋转它时,x
值y
会发生变化。垂直是x
接近零且y
接近 -1 的位置。
看这篇文章,我注意到如果你想把这个向量转换成角度,你可以使用以下算法。
如果要计算设备从垂直方向旋转多少度(其中正为顺时针,负为逆时针),您可以将其计算为:
// 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 2Dtransform
属性旋转视图的程度:
self.view.layer.transform = CATransform3DRotate(CATransform3DIdentity, -rotateRadians, 0, 0, 1);
startDeviceMotionUpdates
(就个人而言,我在方法中更新旋转角度,并transform
在 a中更新它CADisplayLink
,这将屏幕更新与角度更新分离。)
您可以通过以下方式查看您向后/向前倾斜了多远:
// 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和随附的博客文章上找到一个工作示例。
总结上面提到的文章,你可以使用四元数来避免你在垂直握住 iPhone 时可能面临的云台锁定问题。
这是计算倾斜(或偏航)的编码部分:
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
// ...
这是一个旋转 UIViewself.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
对象,其中包含pitch
,roll
和yaw
属性,以及四元数。使用它意味着您不必进行手动数学运算即可将加速度转换为方向。