1. 程式人生 > >iOS開發-CoreMotion核心動作開發-陀螺儀CMRotationRate-加速計CMAcceleration-磁強計Magnetometer開發CoreMotion核心動作實踐

iOS開發-CoreMotion核心動作開發-陀螺儀CMRotationRate-加速計CMAcceleration-磁強計Magnetometer開發CoreMotion核心動作實踐

本期內容:

  • CoreMotion 核心動作
  • CMMotionManager 核心動作管理類
  • CMAcceleration 加速度計
  • CMRotationRate 陀螺儀
  • Magnetometer 磁強計
  • Device motion裝置運動
  • 使用DeviceMotion製作帶有方向操作的晃動功能
  • 使用Accelerometer製作平衡球遊戲

CoreMotion 核心動作

簡介:CoreMotion是iOS系統目前用於處理加速度計,陀螺儀,計步器和環境相關事件。 Core Motion的報告來自iOS裝置的板載硬體的運動和環境相關資料,包括加速度計和陀螺儀,以及計步器,磁力計和氣壓計。您可以使用此框架訪問硬體生成的資料,以便在應用程式中使用它。例如,遊戲可能使用加速度計和陀螺儀資料來控制螢幕上的遊戲行為。

提示: 在iOS 10.0上或之後連結的iOS應用程式必須在其檔案中包含用於所需資料型別的使用說明金鑰。未能包含這些金鑰將導致應用程式崩潰。要具體訪問運動和健身資料,必須包含NSMotionUsageDescription,可以在Info.plist中新增欄位來妥善解決。

在進行CoreMotion的具體操作之前我們需要詳細的瞭解一下Api的構成

CMMotionManager 核心動作管理類

簡介:CMMotionManager用於啟動和管理運動服務的物件。 使用物件啟動報告裝置的板載感測器檢測到的移動的服務。使用此物件可以接收四種類型的運動資料:CMMotionManager

  • 加速度計資料,指示裝置在三維空間中的瞬時加速度。
  • 陀螺儀資料,表示裝置三個主軸周圍的瞬時旋轉。
  • 磁強計資料,表明裝置相對於地球磁場的方向。

裝置運動資料,指示與運動相關的關鍵屬性,例如裝置的使用者啟動的加速度,其姿態,旋轉速率,相對於校準磁場的方向以及相對於重力的方向。這些資料由Core Motion的感測器融合演算法提供。經處理的裝置運動資料給出裝置的姿態,旋轉速率,校準磁場,重力方向以及使用者賦予裝置的加速度。

提示: 在一般情況下,一個App僅可以建立一個CMMotionManager物件,因為此類的多個例項化之後,可能會影響從加速度計和陀螺儀接收資料的速率。

可以在指定的更新間隔接收實時感測器資料,或者讓感測器收集資料並將其儲存起來以供以後檢索。對於這兩種方法,當不再需要資料時,呼叫適當的停止方法(stopAccelerometerUpdates, stopgyroupdate, stopMagnetometerUpdates,和stopDeviceMotionUpdates)。

在使用的時候,在指定的時間間隔處理動作更新

為了以特定的時間間隔接收運動資料,應用程式呼叫一個“start”方法,該方法接受一個操作佇列(NSOperationQueue的例項)和一個特定型別的塊處理程式來處理這些更新。運動資料被傳遞到塊處理程式。更新頻率由“interval”屬性的值決定。

  • Accelerometer加速度計使用方式:

    • 設定accelerometerUpdateInterval屬性以指定更新間隔。
    • 呼叫startAccelerometerUpdatesToQueue:withHandler:方法,傳入一個CMAccelerometerHandler型別的塊。
    • 加速度計資料作為CMAccelerometerData物件傳遞到塊。
  • Gyroscope陀螺儀使用方式:

    • 設定gyroUpdateInterval屬性以指定更新間隔。
    • 呼叫startGyroUpdatesToQueue:withHandler:方法,傳入一個型別ecmgyrohandler的塊。
    • 旋轉速率資料作為CMGyroData物件傳遞到塊中。
  • Magnetometer磁強計使用方式:

    • 設定magnetometerUpdateInterval屬性以指定一個更新間隔。
    • 呼叫startMagnetometerUpdatesToQueue:withHandler:方法,傳遞一個CMMagnetometerHandler型別的塊。
    • 磁場資料作為CMMagnetometerData物件傳遞到塊中。
  • Device motion裝置運動使用方式:

    • 設定deviceMotionUpdateInterval屬性以指定更新間隔。
    • 呼叫startdevicemotionupdatesingreferenceframe:或startdedevicemotionreferenceframe:toQueue:withHandler:或startDeviceMotionUpdatesToQueue:withHandler:方法,傳入一個CMDeviceMotionHandler型別的塊。

關於運動資料的週期性取樣

為了通過定期取樣處理運動資料,app呼叫不帶引數的“start”方法,並定期訪問屬性為給定型別的運動資料儲存的運動資料。這種方法是遊戲等應用程式的推薦方法。處理塊中的加速度計資料會帶來額外的開銷,並且大多數遊戲應用程式在渲染幀時僅對最新的運動資料樣本感興趣。

  • 加速度計:呼叫startAccelerometerUpdates來開始更新,並通過讀取accelerometerData屬性定期訪問CMAccelerometerData物件。

  • 陀螺儀:呼叫startgyroupdate開始更新,並通過讀取gyroData屬性定期訪問CMGyroData物件。

  • 磁強計:呼叫startMagnetometerUpdates來開始更新,並通過讀取magnetometerData屬性定期訪問CMMagnetometerData物件。

  • 裝置運動:呼叫startDeviceMotionUpdatesUsingReferenceFrame:或startdedevicemotionupdates方法開始更新,並通過讀取deviceMotion屬性定期訪問CMDeviceMotion物件。

檢查裝置的服務和功能

檢查服務的可用性:

屬性 型別 作用
accelerometerAvailable BOOL 裝置上是否有加速度計
gyroAvailable BOOL 裝置上是否有陀螺儀
magnetometerAvailable BOOL 裝置上是否有磁強計
deviceMotionAvailable BOOL 動作服務在裝置上是否可用

檢查功能的活躍狀態:

屬性 型別 作用
accelerometerActive BOOL 當前是否正在進行加速度計更新
gyroActive BOOL 確定當前是否正在進行陀螺儀更新
magnetometerActive BOOL 確定當前是否正在進行磁力計更新
deviceMotionActive BOOL 確定應用程式是否從裝置動作服務接收更新

使用樣例如下:

#import "ViewController.h"
#import <CoreMotion/CoreMotion.h>
@interface ViewController ()<UIAccelerometerDelegate>{
    CMMotionManager * motionManager;
}
@property(nonatomic,strong)UIDynamicAnimator *animator;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 建立一個管理者物件
    motionManager = [[CMMotionManager alloc] init];
    // 判斷裝置是否支援
    [self isActived];
}

// 判斷裝置是否支援
- (void)isActived{
    // 判斷裝置是否支援加速度計
    if(motionManager.accelerometerAvailable){
        [motionManager startAccelerometerUpdates];
        NSLog(@"該裝置支援加速度計");
    }else{
        NSLog(@"該裝置不支取加速度");
    }
    
    // 判斷裝置是否支援陀螺儀
    if (motionManager.gyroAvailable){
        [motionManager startGyroUpdates];
        NSLog(@"該裝置支援陀螺儀");
    }else{
        NSLog(@"該裝置不支援陀螺儀");
    }
    
    // 判斷裝置是否支援磁強計
    if (motionManager.magnetometerAvailable){
        [motionManager startMagnetometerUpdates];
        NSLog(@"該裝置支援磁強計");
    }else{
        NSLog(@"該裝置不支援磁強計");
    }
    
    // 判斷裝置是否支援DeviceMotion
    if (motionManager.deviceMotionAvailable) {
        [motionManager startDeviceMotionUpdates];
        NSLog(@"該裝置支援DeviceMotion");
    }else{
        NSLog(@"該裝置不支援DeviceMotion");
    }
}

@end

執行結果如下: 在這裡插入圖片描述 在做CoreMotion的測試的時候,我們必須使用真機來進行測試,模擬器沒法實現。接下來我們繼續詳細講解。

CMAcceleration 加速度計

加速度計的使用,首先要用我們的管理者啟用服務:

[motionManager startAccelerometerUpdates];

Tips:在這裡需要注意一點,在CoreMotion管理的iOS硬體的Api都有兩種方式來獲取資料 官方定義為:pull(主動獲取)、push(通過程式碼塊獲取) 接下來的程式碼中兩種方式都使用

在啟用服務之後,我們就來主動獲取資料(pull),程式碼如下:

// 主動獲取加速度資料
- (void)useAccelerometerPull{
    CMAccelerometerData* accelerometerData = motionManager.accelerometerData;
    NSLog(@"加速度X = %.04f",accelerometerData.acceleration.x);
    NSLog(@"加速度Y = %.04f",accelerometerData.acceleration.y);
    NSLog(@"加速度Z = %.04f",accelerometerData.acceleration.z);
}

這只是主動去獲取一次在當前時間的加速度計的值,如果我們需要一直獲取,那就使用定時器,在成員變數中宣告一個showTimer,使用showTimer呼叫useAccelerometerPull,來實現主動獲取資料,程式碼如下:

showTimer = [NSTimer timerWithTimeInterval:0.01f target:self selector:@selector(useAccelerometerPull) userInfo:nil repeats:YES];

我們也可以讓CoreMotion自帶的塊方法來獲取資料,程式碼如下,詳細內容在註釋裡面:

// 通過塊方法回撥加速度資料
- (void)useAccelerometerPush{
    // 設定獲取間隔時間
    motionManager.accelerometerUpdateInterval = 0.01f;
    // 新增執行緒(如果要重新整理UI,就使用主執行緒)
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 塊方法回撥
    [motionManager startAccelerometerUpdatesToQueue:queue
                                  withHandler:^(CMAccelerometerData *accelerometerData, NSError *error){
        // 列印資料
        NSLog(@"加速度X = %.04f",accelerometerData.acceleration.x);
        NSLog(@"加速度Y = %.04f",accelerometerData.acceleration.y);
        NSLog(@"加速度Z = %.04f",accelerometerData.acceleration.z);
    }];
}

執行效果: 在這裡插入圖片描述

CMRotationRate 陀螺儀

陀螺儀的使用,首先要用我們的管理者啟用服務:

[motionManager startGyroUpdates];

和加速度計一樣,使用兩個方法獲取資料:

// 主動獲取陀螺儀資料
- (void)useGyroAvailablePull{
    CMGyroData* gyroData = motionManager.gyroData;
    NSLog(@"陀螺儀X = %.04f",gyroData.rotationRate.x);
    NSLog(@"陀螺儀Y = %.04f",gyroData.rotationRate.y);
    NSLog(@"陀螺儀Z = %.04f",gyroData.rotationRate.z);
}

// 通過塊方法回撥陀螺儀資料
- (void)useGyroAvailablePush{
    // 設定獲取間隔時間
    motionManager.gyroUpdateInterval = 0.1f;
    // 新增執行緒
    NSOperationQueue * queue = [[NSOperationQueue alloc]init];
    // 塊方法回撥
    [motionManager startGyroUpdatesToQueue:queue withHandler:^(CMGyroData * _Nullable gyroData, NSError * _Nullable error) {
        // 列印資料
        NSLog(@"陀螺儀X = %.04f",gyroData.rotationRate.x);
        NSLog(@"陀螺儀Y = %.04f",gyroData.rotationRate.y);
        NSLog(@"陀螺儀Z = %.04f",gyroData.rotationRate.z);
    }];
}

執行效果: 在這裡插入圖片描述

Magnetometer 磁強計

磁強計的使用,首先要用我們的管理者啟用服務:

[motionManager startMagnetometerUpdates];

然後依舊是兩種方式獲取資料,程式碼如下:

// 主動獲取磁強計資料
- (void)useMagnetometerPull{
    CMMagnetometerData* magnetometerData = motionManager.magnetometerData;
    NSLog(@"磁強計X = %.04f",magnetometerData.magneticField.x);
    NSLog(@"磁強計Y = %.04f",magnetometerData.magneticField.y);
    NSLog(@"磁強計Z = %.04f",magnetometerData.magneticField.z);
}

// 通過塊方法回撥磁強計資料
- (void)useMagnetometerPush{
    // 設定獲取間隔時間
    motionManager.magnetometerUpdateInterval = 0.1f;
    // 新增執行緒
    NSOperationQueue * queue = [[NSOperationQueue alloc]init];
    // 塊方法回撥
    [motionManager startMagnetometerUpdatesToQueue:queue withHandler:^(CMMagnetometerData * _Nullable magnetometerData, NSError * _Nullable error) {
        NSLog(@"磁強計X = %.04f",magnetometerData.magneticField.x);
        NSLog(@"磁強計Y = %.04f",magnetometerData.magneticField.y);
        NSLog(@"磁強計Z = %.04f",magnetometerData.magneticField.z);
    }];
}

執行效果: 在這裡插入圖片描述 看到這裡大家彆著急啊,關於CoreMotion的東西馬上講解完畢,畢竟是科普和分享知識,細緻認真才是最重要的。一會兒會給大家看看我做的demo的效果。

Device motion裝置運動

在裝置運動這裡,我查了一下資料,一般所需要的功能就是獲取自身與水平的夾角,以及自身旋轉角度,在程式碼裡我做了介紹。

// 啟用服務
[motionManager startDeviceMotionUpdates];

// 主動獲取deviceMotion資料
- (void)useDeviceMotionUpdatePull {
    [motionManager startDeviceMotionUpdates];
    CMDeviceMotion *deviceMotion = motionManager.deviceMotion;
    double gravityX = deviceMotion.gravity.x;
    double gravityY = deviceMotion.gravity.y;
    double gravityZ = deviceMotion.gravity.z;
    NSLog(@"\n重力:\nX:%f\nY:%f\nZ:%f", gravityX, gravityY, gravityZ);
}

// 通過塊方法回撥deviceMotion資料
- (void)useDeviceMotionUpdatePush{
    // 設定獲取間隔時間
    motionManager.deviceMotionUpdateInterval = 0.1f;
    // 新增執行緒
    NSOperationQueue * queue = [[NSOperationQueue alloc]init];
    [motionManager startDeviceMotionUpdatesToQueue:queue withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
        // 重力加速度在各個方向的分量
        double gX = motion.gravity.x;
        double gY = motion.gravity.y;
        double gZ = motion.gravity.z;
        NSLog(@"重力X:%f -- Y:%f -- Z:%f", gX, gY, gZ);
        // 獲取手機的傾斜角度(zAngle是手機與水平面的夾角, xyAngle是手機繞自身旋轉的角度):
        double zAngle = atan2(gZ,sqrtf(gX*gX+gY*gY))/M_PI*180.0;
        double xAngle = atan2(gX,grY)/M_PI*180.0;
        NSLog(@"與水平夾角: %f----自身旋轉角度:%f", zAngle, xyAngle);
    }];
}

執行效果: 在這裡插入圖片描述 在這裡引述一下函式:ATAN2和sqrtf

ATAN2:

C 語言裡 double atan2(double y,double x) 返回的是原點至點(x,y)的方位角,即與 x 軸的夾角。也可以理解為複數 x+yi 的輻角。返回值的單位為弧度,取值範圍為(-π,π]; Excel 裡 ATAN2(x,y)返回的是原點至點(x,y)的方位角。返回值的單位為弧度,取值範圍為(-π,π]。 注意: 1、C 函式與 Excel 函式的引數順序正好相反; 2、C 函式允許 x、y 同時為零,Excel 不允許 x、y 同時為零。 與 atan 的不同,atan2 比 atan 穩定。 如:atan(y/x),當 y 遠遠大於 x 時,計算結果是不穩定的。 atan2(y,x)的做法:當 x 的絕對值比 y 的絕對值大時使用 atan(y/x);反之使用 atan(x/y)。這樣就保證了數值穩定性。

sqrtf:

用來計算引數的平方根

好了,關於這四個裝置動作就講到這裡,大家可以去coding一下,練練手。接下來繼續講解兩個例子

使用DeviceMotion製作帶有方向操作的晃動功能

其實程式碼很簡單,主要是根據acceleration值來判斷手機是否晃動。程式碼如下:

- (void)isShake{
    motionManager.deviceMotionUpdateInterval = 0.01f;
    [motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
                                 withHandler:^(CMDeviceMotion *data, NSError *error{
       double x = accelData.acceleration.x;
		double y = accelData.acceleration.y;
		double z = accelData.acceleration.z;
		if (fabs(x)>2.0 || fabs(y)>2.0 ||fabs(z)>2.0) {
 			NSLog(@"檢測到晃動");
		}
     }];
}

如果是帶有方向的晃動,那就是這樣:

- (void)shake{
    motionManager.deviceMotionUpdateInterval = 0.01f;
    [motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
                                 withHandler:^(CMDeviceMotion *data, NSError *error) {
         if (data.userAcceleration.x < -1.5f) {
             // 往左晃動
         }
         if (data.userAcceleration.x > 1.5f) {
             // 往右晃動
         }
         if (data.userAcceleration.y < -1.5f) {
             // 往上晃動
         }
         if (data.userAcceleration.y > 1.5f) {
             // 往下晃動
         }
     }];
}

演示效果如圖: 在這裡插入圖片描述 然後我們再來拓展一個功能:平衡球。大家都玩過平衡球遊戲吧?什麼?沒有?那我給大家做出來看看。

使用Accelerometer製作平衡球遊戲

直接上程式碼:

- (void)viewDidLoad {
    [super viewDidLoad];
    motionManager = [[CMMotionManager alloc] init];
    // 啟用服務
    [motionManager startAccelerometerUpdates];
    self.view.backgroundColor = [UIColor whiteColor];
    // 建立檢視
    [self createUI];
    // 新增加速度測試
    [self useAccelerometerPush];
}

-(void)createUI{
    yuan = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 80, 80)];
    yuan.center = self.view.center;
    yuan.layer.cornerRadius = 40;
    yuan.backgroundColor = [UIColor redColor];
    [self.view addSubview:yuan];
}

// 通過塊方法回撥加速度資料
- (void)useAccelerometerPush{
    // 設定獲取間隔時間
    motionManager.accelerometerUpdateInterval = 0.01f;
    // 塊方法回撥(重新整理檢視,必須在主執行緒)
    [motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue]
                                        withHandler:^(CMAccelerometerData *accelerometerData, NSError *error){
        // 獲得的加速度要考慮到加速度感測器的原點是物理重心,而不是螢幕右上角
        // x軸方向的速度加上x軸方向獲得的加速度
        self->speedX += accelerometerData.acceleration.x;
        // y軸方向的速度加上y軸方向獲得的加速度
        self->speedY += accelerometerData.acceleration.y;
        // 小球將要移動到的x軸座標
        CGFloat posX = self->yuan.center.x + self->speedX;
        // 小球將要移動到的y軸座標
        CGFloat posY = self->yuan.center.y - self->speedY;
        // 在碰到螢幕邊緣之後反彈
        if (posX < 0.0) {
            posX = 0.0;
            // 在碰到螢幕左邊以0.1倍的速度反彈
            self->speedX *= -0.1;
        }else if(posX > self.view.bounds.size.width){
            posX = self.view.bounds.size.width;
            // 在碰到螢幕右邊以0.1倍的速度反彈
            self->speedX *= -0.1;
        }
        if (posY < 0.0) {
            posY = 0.0;
            // 碰到螢幕頂部不反彈
            self->speedY = 0.0;
        }else if (posY > self.view.bounds.size.height){
            posY = self.view.bounds.size.height;
            // 碰到螢幕底部以0.1倍的速度反彈
            self->speedY *= -0.1;
        }
        // 小球檢視的位置改變
        self->yuan.center = CGPointMake(posX, posY);
        // 列印資料
        NSLog(@"加速度X = %.04f",accelerometerData.acceleration.x);
        NSLog(@"加速度Y = %.04f",accelerometerData.acceleration.y);
        NSLog(@"加速度Z = %.04f",accelerometerData.acceleration.z);
    }];
}

演示效果如下: 在這裡插入圖片描述

好了關於核心動作的所有知識點都已經梳理完畢,還是那句話希望大家多動手多實踐~