来说说CoreMotion

作者: Cheriez | 来源:发表于2017-08-15 16:52 被阅读143次

    关于CoreMotion

    Core Motion可以让开发者从各个内置传感器那里获取未经修改的传感数据,并观测或响应设备各种运动和角度变化。这些传感器包括陀螺仪、加速器和磁力仪(罗盘)。

    CoreMotion负责处理的数据

    它包含几种类型的数据,这些类都是CMLogItem类的子类:

    • 加速度数据 :CMAccelerometerData
    typedef struct {
       double x;
       double y;
       double z;
    } CMAcceleration;
    @interface CMAccelerometerData : CMLogItem
    {
    @private
       id _internal;
    }
    //加速度的数据对象
    @property(readonly, nonatomic) CMAcceleration acceleration;
    @end
    
    • 螺旋仪数据 :CMGyroData
    typedef struct {
       double x;
       double y;
       double z; 
    } CMRotationRate;
    @interface CMGyroData : CMLogItem
    {
    @private
       id _internal;
    }
    //螺旋仪数据对象
    @property(readonly, nonatomic) CMRotationRate rotationRate;
    @end
    
    • 磁感应数据 :magnetometerData
    typedef struct {
       double x;
       double y;
       double z;
    } CMMagneticField;
    @interface CMMagnetometerData : CMLogItem
    {
    @private
       id _internal;
    }
    //磁力对象
    @property(readonly, nonatomic) CMMagneticField magneticField;
    @end
    
    • 前三种数据通过复杂运算得到的设备的运动数据 :CMDeviceMotion
    @interface CMDeviceMotion : CMLogItem
    {
    @private
       id _internal;
    }
    //设备的状态对象
    @property(readonly, nonatomic) CMAttitude *attitude;
    //设备的角速度
    @property(readonly, nonatomic) CMRotationRate rotationRate;
    //设备的重力加速度
    @property(readonly, nonatomic) CMAcceleration gravity;
    //用户嫁给设备的加速度 设备的总加速度为重力加速度叫上用户给的加速度
    @property(readonly, nonatomic) CMAcceleration userAcceleration;
    //设备的磁场矢量对象
    @property(readonly, nonatomic) CMCalibratedMagneticField magneticField NS_AVAILABLE(NA,5_0);
    

    我们来看下 attitude 这个对象, 它又封装了许多设备的状态属性

    @interface CMAttitude : NSObject <NSCopying, NSSecureCoding>
    {
    @private
       id _internal;
    }
    //设备的欧拉角roll
    @property(readonly, nonatomic) double roll;
    //设备的欧拉角pitch
    @property(readonly, nonatomic) double pitch;
    //设备的欧拉角yaw
    @property(readonly, nonatomic) double yaw;
    //设备状态的旋转矩阵
    @property(readonly, nonatomic) CMRotationMatrix rotationMatrix;
    //设备状态的四元数
    @property(readonly, nonatomic) CMQuaternion quaternion;
    @end
    

    CoreMotion的使用

    CoreMotion中获取数据主要是两种方式:

    • 一种是Push,就是你提供一个线程管理器NSOperationQueue,再提供一个Block,这样,CoreMotion自动在每一个采样数据到来的时候回调这个Block,进行处理。在这中情况下,block中的操作会在你自己的主线程内执行。

    • 一种是 Pull,在这个方式里,你必须主动去像CMMotionManager要数据,这个数据就是最近一次的采样数据。你不去要,CMMotionManager就不会给你。当然,在这种情况下,CoreMotion所有的操作都在自己的后台线程中进行,不会有任何干扰你当前线程的行为。

    有两点需要注意:第一就是需要检测是否可用,第二就是在不需要的时候,停止更新数据。苹果建议在使用CoreMotionManager的时候采用单例模式。

    我们以CMAccelerometerData为例,其他方式都是一样的

    pull获取方式

    if ([_motionManager isAccelerometerAvailable]) {    
       // 设置加速计采样频率
       [_motionManager setAccelerometerUpdateInterval:1 / 40.0];
       [_motionManager startAccelerometerUpdates];
    }else {
       NSLog(@"设备不支持加速计");
    }
    
    //可以写个定时器去定时主动获取数据
    //获取数据
    CMAcceleration acceleration=_motionManager.accelerometerData.acceleration;
    NSLog(@"%f---%f---%f",acceleration.x,acceleration.y,acceleration.z);
    

    push获取方式

    //判断加速计是否可用
    if([_motionManager isAccelerometerAvailable]) {
       // 设置加速计频率
       [_motionManager setAccelerometerUpdateInterval:1 / 40.0];
       //开始采样数据
       [_motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
           NSLog(@"%f---%f",accelerometerData.acceleration.x,accelerometerData.acceleration.y);
       }];
    } else{
       NSLog(@"设备不支持加速计");
    }
    

    我们再列举几个它的其他属性和方法:

    @interface CMMotionManager : NSObject
    {
    @private
       id _internal;
    }
    //设置加速度传感器更新帧率
    @property(assign, nonatomic) NSTimeInterval accelerometerUpdateInterval __TVOS_PROHIBITED;
    //加速度传感器是否可用
    @property(readonly, nonatomic, getter=isAccelerometerAvailable) BOOL accelerometerAvailable __TVOS_PROHIBITED;
    //加速度传感器是否激活
    @property(readonly, nonatomic, getter=isAccelerometerActive) BOOL accelerometerActive __TVOS_PROHIBITED;
    //加速度传感器数据对象
    @property(readonly, nullable) CMAccelerometerData *accelerometerData __TVOS_PROHIBITED;
    //pull方式开始更新加速度数据
    - (void)startAccelerometerUpdates __TVOS_PROHIBITED;
    //push方式更新加速度数据
    - (void)startAccelerometerUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMAccelerometerHandler)handler __TVOS_PROHIBITED;
    //停止更新加速度数据
    - (void)stopAccelerometerUpdates __TVOS_PROHIBITED;
    
    //设备状态更新帧率
    @property(assign, nonatomic) NSTimeInterval deviceMotionUpdateInterval __TVOS_PROHIBITED;
    //参考器枚举
    + (CMAttitudeReferenceFrame)availableAttitudeReferenceFrames NS_AVAILABLE(NA,5_0) __TVOS_PROHIBITED;
    @property(readonly, nonatomic) CMAttitudeReferenceFrame attitudeReferenceFrame NS_AVAILABLE(NA,5_0) __TVOS_PROHIBITED;
    //设备运动信息是否可用
    @property(readonly, nonatomic, getter=isDeviceMotionAvailable) BOOL deviceMotionAvailable __TVOS_PROHIBITED;
    //设备运动信息是否激活
    @property(readonly, nonatomic, getter=isDeviceMotionActive) BOOL deviceMotionActive __TVOS_PROHIBITED;
    //设备运动信息对象
    @property(readonly, nullable) CMDeviceMotion *deviceMotion __TVOS_PROHIBITED;
    //pull方式开始刷新运动信息
    - (void)startDeviceMotionUpdates __TVOS_PROHIBITED;
    //push方式开始刷新运动信息
    - (void)startDeviceMotionUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMDeviceMotionHandler)handler __TVOS_PROHIBITED;
    //使用某个参考系
    - (void)startDeviceMotionUpdatesUsingReferenceFrame:(CMAttitudeReferenceFrame)referenceFrame NS_AVAILABLE(NA,5_0) __TVOS_PROHIBITED;
    //push方式开始刷新设备运动信息
    - (void)startDeviceMotionUpdatesUsingReferenceFrame:(CMAttitudeReferenceFrame)referenceFrame toQueue:(NSOperationQueue *)queue withHandler:(CMDeviceMotionHandler)handler NS_AVAILABLE(NA,5_0) __TVOS_PROHIBITED;
    //停止刷新设备运动信息
    - (void)stopDeviceMotionUpdates __TVOS_PROHIBITED;
    

    关于CMDeviceMotion

    我们都知道,设备静止时受到的地球引力为1g,1g是物体在地球的海平面上受到的下拉力(9.8米/秒²)。假如设备从高处掉落,其加速计测量到的加速度将为0g。假如设备水平放在桌面上,则加速计测量出的加速度为1g,且方向朝上。

    加速计测量3个轴(x、y和z)上的值,如图所示:


    这个轴在方向上有些不同于传统坐标轴,考虑以下实际情况:

    1g重力的分布情况是:y=-1.0


    1g重力的分布情况是:x=1.0

    1g重力的分布情况是:z=-1.0

    1g重力的分布情况是:x= 0.707,y=-0.707

    1g重力的分布情况是:y=-0.707,z= 0.707

    仅当设备的朝向相对于重力的方向发生变化时,加速计才能检测到;要同时检测设备的朝向和运动数据,就需要用到陀螺仪了。当查询设备的陀螺仪时,它将报告设备绕x, y, z轴的旋转速度,单位为弧度/秒;2弧度相当于一整圈,因此陀螺仪返回读数2表示设备绕相应的轴每秒转一圈。

    有两种方式访问设备的朝向和运动数据,一种是通过UIDevice请求朝向通知,另一种是利用框架Core Motion定期地直接访问加速计和陀螺仪数据。

    • 通过UIDevice请求朝向通知

    虽然可直接查询加速计并使用它返回的值判断设备的朝向,但Apple为开发人员简化了这项工作。单例UIDevice表示当前设备,它包含方法beginGeneratingDeviceOrientationNotifications,该方法命令iOS将朝向通知发送到通知中心(NSNotificationCenter)。启动通知后,就可以注册一个NSNotificationCenter实例,以便设备的朝向发生变化时自动调用指定的方法。

    通过访问UIDevice的属性orientation来获得设备当前朝向,该属性的类型为枚举值

    UIDeviceOrientation,有6个预定义值:
    UIDeviceOrientationFaceUp — 设备正面朝上
    UIDeviceOrientationFaceDown — 设备正面朝下
    UIDeviceOrientationPortrait — 纵向(Home键在下)
    UIDeviceOrientationPortraitUpsideDown — 纵向倒转(Home键在上)
    UIDeviceOrientationLandscapeLeft — Home键在左边的横向
    UIDeviceOrientationLandscapeRight — Home键在右边的横向

    - (void)viewDidLoad
    {
       [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    
       [[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(orientationChanged:) name:@"UIDeviceOrientationDidChangeNotification"object:nil];
    
       [super viewDidLoad];
    }
    
    - (void)orientationChanged:(NSNotification *)notification
    {
      UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
      NSLog(@"当前朝向枚举数字值:%d",orientation);
    
      switch (orientation) {
        case UIDeviceOrientationPortrait:
          self.lblOriention.text = @"Portrait";
          break;
        case UIDeviceOrientationPortraitUpsideDown:
          self.lblOriention.text = @"Portrait Upside Down";
          break;
        case UIDeviceOrientationLandscapeLeft:
          self.lblOriention.text = @"Landscape Left";
          break;
        case UIDeviceOrientationLandscapeRight:
          self.lblOriention.text = @"Landscape Right";
          break;
        case UIDeviceOrientationFaceUp:
          self.lblOriention.text = @"Face Up";
          break;
        case UIDeviceOrientationFaceDown:
          self.lblOriention.text = @"Face Down";
          break;
        default:
          self.lblOriention.text = @"Unknown";
          break;
       }
    }
    
    • 使用Core Motion读取加速计和陀螺仪数据

    利用UIDevice只能判断极端朝向,应用程序经常要获悉这些朝向之间的过渡状态,如设备处于某个倾斜位置。Core Motion运动管理器让您能够指定从加速计和陀螺仪那里接收更新的频率(单位为秒),还让您能够直接指定一个处理程序块(handle block),每当更新就绪时都将执行该处理程序块。

    实际加速度在Core Motion里被分解成了两部分:Gravity和UserAcceleration。Gravity代表重力1g在设备的分布情况,UserAcceleration代表设备运动中的加速度分布情况。将这两者相加就等于实际加速度。Gravity的三个轴所受的重力加起来始终等于1g,而UserAcceleration取决于单位时间内动作的幅度大小。

    CMRotationRate的X,Y,Z分别代表三个轴上的旋转速率,单位为弧度/秒。旋转速度为1弧度/秒,意味着设备每秒旋转半圈。这里复习一下弧度与角度的转换:

    1角度 = π/180 弧度

    1弧度 = 180/π角度

    360角度 = 360 * π/180 = 2π弧度 = 一整圈

    CMAttitude的三个属性Yaw,Pitch和Roll分别代表左右摆动、俯仰以及滚动。可以将设备想象成一架飞机,下面的gif图演示了各种运动状态:

    Yaw的运动状态:

    yaw.gif

    Pitch的运动状态:

    pitch.gif

    Roll的运动状态:

    roll.gif

    实例

    下面的代码实现了这样一个界面,通过CMMotionManager返回了设备的各个状态值:

    #import "ViewController.h"
    
    
    @interface ViewController ()
    
    @property (strong, nonatomic) IBOutlet UILabel *lblYaw;
    @property (strong, nonatomic) IBOutlet UILabel *lblPitch;
    @property (strong, nonatomic) IBOutlet UILabel *lblRoll;
    
    @property (strong, nonatomic) IBOutlet UILabel *lblAccelerometerX;
    @property (strong, nonatomic) IBOutlet UILabel *lblAccelerometerY;
    @property (strong, nonatomic) IBOutlet UILabel *lblAccelerometerZ;
    
    @property (strong, nonatomic) IBOutlet UILabel *lblGravityX;
    @property (strong, nonatomic) IBOutlet UILabel *lblGravityY;
    @property (strong, nonatomic) IBOutlet UILabel *lblGravityZ;
    
    @property (strong, nonatomic) IBOutlet UILabel *lblRotationRateX;
    @property (strong, nonatomic) IBOutlet UILabel *lblRotationRateY;
    @property (strong, nonatomic) IBOutlet UILabel *lblRotationRateZ;
    
    @property (strong, nonatomic) CMMotionManager *motionManager;
    
    - (IBAction)motionSwitchHandler:(id)sender;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad
    {
       [super viewDidLoad];
    
       self.motionManager = [[CMMotionManager alloc] init];
       self.motionManager.deviceMotionUpdateInterval = 1.0f/10.0f; //1秒10次
    }
    
    - (void)controlHardware
    {
       [self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
           //Acceleration
           if(fabs(motion.userAcceleration.x)>1.3f)
               self.lblAccelerometerX.text = [NSString stringWithFormat:@"%.2f",motion.userAcceleration.x];
           if(fabs(motion.userAcceleration.y)>1.3f)
               self.lblAccelerometerY.text = [NSString stringWithFormat:@"%.2f",motion.userAcceleration.y];
           if(fabs(motion.userAcceleration.z)>1.3f)
               self.lblAccelerometerZ.text = [NSString stringWithFormat:@"%.2f",motion.userAcceleration.z];
           //Gravity
           self.lblGravityX.text = [NSString stringWithFormat:@"%.2f",motion.gravity.x];
           self.lblGravityY.text = [NSString stringWithFormat:@"%.2f",motion.gravity.y];
           self.lblGravityZ.text = [NSString stringWithFormat:@"%.2f",motion.gravity.z];
           //yaw,pitch,roll
           self.lblYaw.text = [NSString stringWithFormat:@"%.2f",motion.attitude.yaw];
           self.lblPitch.text = [NSString stringWithFormat:@"%.2f",motion.attitude.pitch];
           self.lblRoll.text = [NSString stringWithFormat:@"%.2f",motion.attitude.roll];
           //Gyroscope's rotationRate(CMRotationRate)
           self.lblRotationRateX.text = [NSString stringWithFormat:@"%.2f",motion.rotationRate.x];
           self.lblRotationRateY.text = [NSString stringWithFormat:@"%.2f",motion.rotationRate.y];
           self.lblRotationRateZ.text = [NSString stringWithFormat:@"%.2f",motion.rotationRate.z];
       }];
    }
    
    - (IBAction)motionSwitchHandler:(id)sender
    {
       UISwitch *motionSwitch = (UISwitch *)sender;
       if(motionSwitch.on)
       {
           [self controlHardware];
       }
       else
       {
           [self.motionManager stopDeviceMotionUpdates];
       }
    }
    
    - (void)didReceiveMemoryWarning
    {
       [super didReceiveMemoryWarning];
       // Dispose of any resources that can be recreated.
    }
    @end
    

    相关文章

      网友评论

        本文标题:来说说CoreMotion

        本文链接:https://www.haomeiwen.com/subject/tvtvrxtx.html