iOS 传感器CMMotionManager

1,382 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天。

在iOS开发中,经常会用到传感器做一些有意思的程序。 CMMotionManager 可以获取到很多类型的数据:

环境光传感器:感应光照强度
距离传感器:感应靠近设备屏幕的物体
磁力计算器:感应周边磁场(标识设备相对于地球磁场的方位)
内部温度传感器:感应设备内部温度(非公开)
湿度传感器:感应设备是否进水(非微电子传感器)
陀螺仪:感应持握方式(标识设备在三个主轴上的瞬时旋转)
加速计:感应设备运动(标识设备在三维空间中的瞬时加速度)

使用很简单,今天记录一下其中一种场景的使用。

介绍一下属性:

1. attitude
attitude 用于标识空间位置的欧拉角(roll、yaw、pitch)和四元数(quaternion)
其中绕 x 轴运动称作 pitch(俯仰),绕 y 轴运动称作 roll(滚转),绕 z 轴运动称作 yaw(偏航)。
当设备正面向上、顶部指向正北、水平放置时,pitch、yaw 和 roll 值均为 0,其他变化如下
设备顶部上扬,pitch 由 0 递增 pi/2,顶部下沉,由 0 递减 pi/2
设备顶部左偏 180 度范围内,yaw 由 0 递增 pi,右偏递减
设备左部上旋,roll 由 0 递增 pi,左部下旋,roll 由 0 递减

2. rotationRate
rotationRate 标识设备旋转速率,具体变化如下
pitch 增加,x > 0,pictch 减少,x < 0
roll 增加,y > 0,row 减少,y < 0
yaw 增加,z > 0,yaw 减少,z < 0

3. gravity
gravity 用于标识重力在设备各个方向的分量,具体值的变化遵循如下规律:重力方向始终指向地球,而在设备的三个方向上有不同分量,最大可达 1.0,最小是 0.04. userAcceleration
userAcceleration 用于标识设备各个方向上的加速度,注意是加速度值,可以标识当前设备正在当前方向上减速 or 加速。

5. magneticField & heading
magneticField 用于标识设备周围的磁场范围和精度,heading 用于标识北极方向。但是要注意,这两个值的检测需要指定 ReferenceFrame,它是一个 CMAttitudeReferenceFrame 的枚举,有四个值
CMAttitudeReferenceFrameXArbitraryZVertical
CMAttitudeReferenceFrameXArbitraryCorrectedZVertical
CMAttitudeReferenceFrameXMagneticNorthZVertical
CMAttitudeReferenceFrameXTrueNorthZVertical
其中前两个 frame 下磁性返回非法负值,只有选择了 CMAttitudeReferenceFrameXMagneticNorthZVerticalCMAttitudeReferenceFrameXTrueNorthZVertical 才有有效值,这两个枚举分别指代磁性北极和地理北极。

1、首先,懒加载创建对象。

- (CMMotionManager *)m_motionManager
{
    if (!_m_motionManager)
    {
        _m_motionManager = [[CMMotionManager alloc] init];
        _m_motionManager.accelerometerUpdateInterval = 0.1;
    }
    return _m_motionManager;
}

//获取数据

if (self.m_motionManager.deviceMotionAvailable) {
//TODO:Pull 方式,自己取
//[motionManager startDeviceMotionUpdates];
//double x = motionManager.deviceMotion.gravity.x;
//double y = motionManager.deviceMotion.gravity.y;
//double z = motionManager.deviceMotion.gravity.z;

//TODO:Push 方式,设置好时间后会自动推过来

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [self.m_motionManager startDeviceMotionUpdatesToQueue:queue withHandler:^(CMDeviceMotion *_Nullable motion, NSError * _Nullable error) {
        if (!m_objLiveView.m_enableVR)
            return;
    CMQuaternion quaternion = motion.attitude.quaternion;
    GLKMatrix4 matrix4 = [self calculateMatrixFromQuaternion:&quaternion];
    matrix4 = GLKMatrix4RotateX(matrix4, M_PI_2);
    float rotationMatrix[16] = {matrix4.m00,matrix4.m01,matrix4.m02,matrix4.m03,matrix4.m10,matrix4.m11,matrix4.m12,matrix4.m13,matrix4.m20,matrix4.m21,matrix4.m22,matrix4.m23,matrix4.m30,matrix4.m31,matrix4.m32,matrix4.m33};
     }];
}

//TODO:  重力坐标转换

- (GLKMatrix4)calculateMatrixFromQuaternion:(CMQuaternion*)quaternion{
    float xx = quaternion->x * quaternion->x;
    float yy = quaternion->y * quaternion->y;
    float zz = quaternion->z * quaternion->z;
    float xy = quaternion->x * quaternion->y;
    float wz = quaternion->w * quaternion->z;
    float wy = quaternion->w * quaternion->y;
    float xz = quaternion->x * quaternion->z;
    float yz = quaternion->y * quaternion->z;
    float wx = quaternion->w * quaternion->x;
    float r00 = 1 - 2 * (yy + zz);
    float r01 = 2 * (xy - wz);
    float r02 = 2 * (wy + xz);
    float r03 = 0;
    float r10 = 2 * (xy + wz);
    float r11 = 1 - 2 * (xx + zz);
    float r12 = 2 * (yz - wx);
    float r13 = 0;
    float r20 = 2 * (xz - wy);//xy - wy
    float r21 = 2 * (yz + wx);
    float r22 = 1 - 2 * (xx + yy);
    float r23 = 0;
    float r30 = 0;
    float r31 = 0;
    float r32 = 0;
    float r33 = 1;
    return GLKMatrix4Make(r00,r01,r02,r03,
                          r10,r11,r12,r13,
                          r20,r21,r22,r23,
                          r30,r31,r32,r33);
}

距离传感器

距离传感器可以检测有物理在靠近或者远离屏幕,使用如下

[UIDevice currentDevice].proximityMonitoringEnabled = YES;
    
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(proximityStateDidChange:) name:UIDeviceProximityStateDidChangeNotification object:nil];

- (void)proximityStateDidChange:(NSNotification *)note
{
    if ([UIDevice currentDevice].proximityState) {
        NSLog(@"Coming");
    } else {
        NSLog(@"Leaving");
    }
}

环境光传感器

目前没有找到相应的 API,可以采取的思路是通过摄像头获取每一帧,进行光线强度检测

NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];

CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate);

NSDictionary *metadata = [[NSDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict];

CFRelease(metadataDict);

NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];

float brightnessValue = [[exifMetadata  objectForKey:(NSString *)kCGImagePropertyExifBrightnessValue] floatValue];

NSLog(@"%f",brightnessValue);

其中主要是要注意这个坐标转换,在进行图形转换或者其他交互时,如果发现手机的动作方向和实际的显示的不对称,调整坐标转换。不同的交互场景坐标转换的方法可能不同。

内容参考