使用CMMotionManager测量倾斜角度

mil*_*ort 36 ios cmmotionmanager

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

谁能指点我一个有效的例子?

Rob*_*Rob 55

看看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;
    }];
}];
Run Code Online (Sandbox Code Playgroud)

有了这个参考帧(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
Run Code Online (Sandbox Code Playgroud)

您可以使用它来计算通过Quartz 2D transform属性旋转视图的程度:

self.view.layer.transform = CATransform3DRotate(CATransform3DIdentity, -rotateRadians, 0, 0, 1);
Run Code Online (Sandbox Code Playgroud)

(就个人而言,我更新了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;
Run Code Online (Sandbox Code Playgroud)

  • 我们如何计算绕y轴的旋转? (2认同)

dul*_*ccc 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
// ...
Run Code Online (Sandbox Code Playgroud)

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

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
// ...
Run Code Online (Sandbox Code Playgroud)

  • 只是想提一下,那个带有四元数的上述所有解决方案中唯一能够可靠地工作并避免万向锁定问题的解决方案 - 谢谢你,先生! (5认同)

fou*_*dry 7

下面是一个示例,self.horizon当您倾斜设备时,旋转UIView 以使其与地平线保持水平.

- (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;
     }];
}
Run Code Online (Sandbox Code Playgroud)

这有点过于简单 - 您应该确保在您的应用中只有一个CMMotionManager实例,因此您需要预先初始化它并通过属性访问它.