38

假设您将 iphone/ipad 垂直放在您的面前,屏幕朝向您,纵向放置。您将设备倾斜到一侧,使屏幕始终面向您。您如何使用 CMMotionManager 测量静态倾斜角?这似乎是一个简单的问题,应该有一个简单的答案,但我找不到任何不会消失在四元数和旋转矩阵中的方法。

谁能指出我一个可行的例子?

4

4 回答 4

57

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 的位置。


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

如果要计算设备从垂直方向旋转多少度(其中正为顺时针,负为逆时针),您可以将其计算为:

// 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;
于 2013-03-26T20:51:05.830 回答
10

这是一个迟到的答案,但您可以在 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
// ...
于 2013-11-17T10:14:21.430 回答
7

这是一个旋转 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 实例,因此您希望预先初始化它并通过属性访问它。

于 2013-03-27T03:15:20.280 回答
3

由于 iOS8 CoreMotion 还返回一个CMAttitude对象,其中包含pitch,rollyaw属性,以及四元数。使用它意味着您不必进行手动数学运算即可将加速度转换为方向。

于 2017-01-14T17:39:15.570 回答