Core Motion 的那些事

獲取設備的移動事件

當我們移動铛绰,晃動岩喷,或者傾斜手機的時候视事,這些動作都會被設備的硬件捕獲匈勋。其實每一個動都都會在 X, Y, Z 三個方向上產(chǎn)生速度上的變化礼旅。根據(jù)不同的變化我們可以檢測出來設備的朝向和移動。要檢測設備的朝向和移動洽洁,我們有三種方法:

  • 利用 UIDevice痘系,我們可以獲取設備的大致方向,例如屏幕朝上還是朝下饿自。
  • 利用 UIKit 中的 UIEvent汰翠,我們可以獲取設備的晃動
  • 利用 Core Motion 來精確的獲取設備在 X Y Z 三個軸上的變化。這個是最精確的昭雌。

使用 UIDevice 來獲取設備的方向

首先我們應該告訴 UIDevice 來檢測設備方向變化复唤,然后我們就可以在 UIDevice 中的 orientation 屬性得到設備方向的變化。

typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
    UIDeviceOrientationUnknown,
    UIDeviceOrientationPortrait,            // Device oriented vertically, home button on the bottom
    UIDeviceOrientationPortraitUpsideDown,  // Device oriented vertically, home button on the top
    UIDeviceOrientationLandscapeLeft,       // Device oriented horizontally, home button on the right
    UIDeviceOrientationLandscapeRight,      // Device oriented horizontally, home button on the left
    UIDeviceOrientationFaceUp,              // Device oriented flat, face up
    UIDeviceOrientationFaceDown             // Device oriented flat, face down
} __TVOS_PROHIBITED;

如果你想檢測設備的方向變化烛卧,那么你應該監(jiān)聽 UIDeviceOrientationDidChangeNotification 通知佛纫,系統(tǒng)會在設備方向發(fā)生變化的時候發(fā)出該通知妓局。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    NSLog(@"%ld",[UIDevice currentDevice].orientation);
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cheange:) name:UIDeviceOrientationDidChangeNotification object:nil];
}

- (void)cheange:(NSNotification *)notification {
    UIDevice *device = notification.object;
    NSLog(@"%ld", device.orientation);
}

為了延長電池的使用壽命,我們應該在不使用的時候告訴 UIDevice 來關(guān)閉加速計呈宇。

- (void)viewDidDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}

使用 UIEvent 來獲取設備搖晃事件

當用戶晃動設備的時候好爬,iOS 系統(tǒng)會評估加速計的數(shù)據(jù),當這些數(shù)據(jù)特征符合搖晃的時候甥啄,系統(tǒng)會就會認為用戶在搖晃設備存炮,然后會創(chuàng)建一個 UIEvent 對象來發(fā)送個宿主 APP,讓其處理蜈漓。
移動事件和點擊事件很像穆桂。系統(tǒng)會告訴 APP 一個運動的開始和結(jié)束,但并不是每一個動作發(fā)生時都會這么做融虽。
檢測晃動超級簡單享完。晃動這個是可以在模擬器中試驗的衣形。

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
    NSLog(@"shake");
}

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
    NSLog(@"shake");
}
shake

設置并檢查硬件是否支持設備移動事件

如果你需要獲取加速計數(shù)據(jù)驼侠,你應該在 Info.plist 中添加相應的鍵值對,如果在一個沒有陀螺儀的設備上打開需要陀螺儀的應用谆吴,那么將會崩潰倒源。App Store 也會根據(jù)你所需要的硬件能力來提醒用戶來是否要下載一個打不開的 APP。

如果你只是獲取設備的大致方向句狼,可以不用添加

使用 Core Motion 來捕獲設備的移動

Core Motion 框架提供加速計和陀螺儀的原始數(shù)據(jù)給 APP 處理笋熬,它使用特別的算法來處理收集到的數(shù)據(jù),精確度更高腻菇,而且是在自己的線程中處理胳螟。

Core Motion 和 UIKit 是不同的,它和 UIEvent 無關(guān)筹吐,也不隨著響應鏈傳遞糖耸,它只是直接的交付數(shù)據(jù)。
Core Motion 所產(chǎn)生的數(shù)據(jù)有三類

  • CMAltimeter 高度計
  • CMPedometer 計步器
  • CMAccelerometerData 捕獲每一個軸上的加速度
  • CMGyroData 捕獲三個軸上的旋轉(zhuǎn)率
    *CMDeviceMotion 包含了幾個不同的測量丘薛,數(shù)據(jù)更加精確
    一般來說嘉竟,一個 APP 只需要創(chuàng)建一個 CMMotionManager 對象,設置其更新間隔洋侨,請求數(shù)據(jù)舍扰,然后處理,每一個數(shù)據(jù)類都是 CMLogItem 的子類希坚,所有的數(shù)據(jù)都會被打上時間的標記并且可以輸出到日志文件中边苹。
    CMMotionManager 有兩種獲取數(shù)據(jù)的方法,pull push
  • pull APP 主動去請求數(shù)據(jù)更新
  • push APP 調(diào)用加速計后裁僧,實現(xiàn)回調(diào)的 block个束,當加速計有數(shù)據(jù)更新的時候慕购,就回去調(diào)用這個 block,然后在其中處理相應的數(shù)據(jù)即可
    一般來說使用 pull 更為方便播急,因為它的代碼更少脓钾,用到了再去取相應的數(shù)據(jù)售睹。
    不用的時候桩警,記得關(guān)閉傳感器,這樣更加省電昌妹。

選擇一個合適的更新時間

當你使用 Core Motion 的時候捶枢,你需要指定一個更新間隔。這個時間應該是你的 APP 所能接收的最大間隔飞崖,因為這樣烂叔,可以提升電池的續(xù)航。下邊這個表格提供了常用的更新頻率固歪,很少有 APP 需要每秒鐘獲取 100 次的加速計數(shù)據(jù)蒜鸡。

事件頻率(HZ) 用途
10 - 20 確定設備的當前朝向
30 - 60 游戲或者以設備加速度作為實時的用戶輸入數(shù)據(jù)
70 - 100 適用于 APP 需要高頻率的運動事件,例如牢裳,可以根據(jù)這個來判斷用戶快速點擊和搖晃設備

使用高度計 CMAltimeter

在手機中逢防,一般使用的是氣壓高度計,根據(jù)當前位置的大氣壓蒲讯,獲取當前位置的高度忘朝。具體公式為

z=cT log(P0/P)
c 為常數(shù),T 為絕對溫度判帮,P 為在高度 z 處的氣壓局嘁,P0 為在海平面處的大氣壓,常數(shù) c 與重力加速度和空氣的摩爾質(zhì)量相關(guān)

我們知道晦墙,當高度上升12米悦昵,氣壓計中的汞柱下降1mm,標準大氣壓下是760mm的汞柱晌畅,一百帕相當于 3/4 mm的汞柱 我們可以得到簡單的計算公式

當前高度 =(760 - 當前大氣壓 * 3 / 4)*12

我們使用 + isRelativeAltitudeAvailable; 來判斷高度計是否可用但指,使用 - startRelativeAltitudeUpdatesToQueue: withHandler: 來獲得當前高度,在這個回調(diào) block 中踩麦,我們會得到一個 CMAltitudeData 對象枚赡,該對象有兩個屬性,一個是當前位置的大氣壓谓谦,單位是 kPa贫橙,一個是當前位置,相對于起始測量位置的高度反粥。例如卢肃,當你剛開始測量的時候疲迂,該值為 0 ,當你拿著設備運動的時候莫湘,這個值會返回你當前位置尤蒿,相對于起始位置的高度,具體高度幅垮,可以根據(jù)當前位置的壓強計算出來腰池,但如果遇到氣壓波動比較大的位置,高度誤差忙芒,還是相當大的示弓。

使用計步器 CMPedometer

當人攜帶者設備走動的時候,會產(chǎn)生一定的頻率呵萨,不同的運動產(chǎn)生的頻率是不一樣的奏属,或走,或坐潮峦,或跑囱皿,根據(jù)不同的頻率濾波,識別忱嘹,最終可以大致確定設備持有者的運動狀態(tài)嘱腥。
在 iOS 8 以后,我們使用 CMPedometer 德谅,而在 iOS 7 - iOS 8 我們使用 CMStepCounter 來獲取數(shù)據(jù)
在 CMPedometer爹橱, 我們可以獲取一下數(shù)據(jù)

  1. 步數(shù)
  2. 距離
  3. 樓層變化
  4. 速度
  5. 節(jié)奏
  6. 運動的開始暫停

當你開始調(diào)用的時候,即使應用被壓到后臺窄做,系統(tǒng)還是可以記錄步數(shù)愧驱,當你再次返回的時候,系統(tǒng)會把這段時間積累的步數(shù)全部返回給你椭盏。我們可以使用系統(tǒng)提供的 API 來查詢某一段時間的運動數(shù)據(jù)组砚,但是,系統(tǒng)最多只為我們保存 7 天掏颊。

//從某個時間開始記錄步數(shù)
-startPedometerUpdatesFromDate: withHandler             
//查詢某個時間的段的數(shù)據(jù)
-queryPedometerDataFromDate:toDate:withHandler
//停止記錄步數(shù)
-stopPedometerUpdates

使用 Core Motion 獲取加速計數(shù)據(jù)

加速計測量了三個軸的速度改變糟红。在 Core Motion 中,每一個運動都被捕獲進一個 CMAccelerometerData 對象乌叶,而這個對象包含了一個 CMAcceleration 結(jié)構(gòu)體盆偿。


acceleration_axes.png

首先創(chuàng)建一個 CMMotionManager 對象

  • 使用 pull 方式獲取數(shù)據(jù),當調(diào)用 startAccelerometerUpdates 方法后准浴,Core Motion 會用最新的測量數(shù)據(jù)更新 CMMotionManager 類的 accelerometerData 屬性事扭,之后你可以在一個循環(huán)中或者定時器中獲取 accelerometerData 來判斷設備的運動。
  • 使用 push 方式來獲取數(shù)據(jù)乐横,當調(diào)用 startAccelerometerUpdatesToQueue:withHandler: 當數(shù)據(jù)更新的時候求橄,就會自動調(diào)用 handler今野,在這個 block 中,你可以處理 accelerometerData
    self.mManager = [[CMMotionManager alloc] init];
    if (self.mManager.accelerometerAvailable) {
        NSLog(@"available");
    }
    self.mManager.accelerometerUpdateInterval = 1;
    // pull
    [self.mManager startAccelerometerUpdates];
    [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"%f", self.mManager.accelerometerData.acceleration.x);
    }];
    
    // push
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.name = @"accelerometer";
    [self.mManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
        NSLog(@"x : %f", accelerometerData.acceleration.x);
        NSLog(@"y : %f", accelerometerData.acceleration.y);
        NSLog(@"z : %f", accelerometerData.acceleration.z);
    }];
    // stop 
    - (void)stopUpdates {

     if ([self.mManager isAccelerometerActive] == YES) {
          [self.mManager stopAccelerometerUpdates];
     }
}

處理陀螺儀數(shù)據(jù)

角速度傳感器罐农,測量物體的偏轉(zhuǎn)条霜,角速度。在手機上涵亏,單憑加速器是無法構(gòu)建完整的 3D 動作宰睡,加上陀螺儀,可以讓我們清晰感測到手機的旋轉(zhuǎn)溯乒。 陀螺儀測量設備繞著 X Y Z 三個軸的自傳速率

陀螺儀

當你請求陀螺儀數(shù)據(jù)更新的時候夹厌,Core Motion 返回的數(shù)據(jù)是有誤差的。它會返回一個 CMGyroData 對象裆悄,該對象中有一個 rotationRate 屬性,它是一個 CMRotationRate 結(jié)構(gòu)體臂聋,里邊包含了三個方向上的自傳速率光稼,單位是弧度每秒。 用 CMGyroData 是有誤差的孩等,如果想獲取更為精確是自傳速率艾君,可以使用 CMDeviceMotion 。
其更新數(shù)據(jù)方式也是有兩種肄方,分為 startGyroUpdates pull 和 startGyroUpdatesToQueue:withHandler: push冰垄,具體方式和加速計獲取方式一樣,取的數(shù)據(jù)在 CMMotionManager 的 gyroData 屬性中权她,同樣虹茶,你也需要設置更新的時間間隔。
代碼就不貼了隅要,和上邊大同小異蝴罪。

旋轉(zhuǎn)方向

旋轉(zhuǎn)方向遵循右手定則,右手握拳步清,大拇指指向某一個軸要门,如果旋轉(zhuǎn)的方向和四個手指的方向一樣的話,即為正廓啊,否則就為負

處理設備數(shù)據(jù)

如果設備上既有加速計又有陀螺儀的話欢搜,Core Motion 提供了一個 device-motion 的服務用來處理兩個傳感器的原始數(shù)據(jù)。使用這個谴轮,可以得到最精確的數(shù)據(jù)炒瘟。
你可以訪問這個 CMDeviceMotion 對象的 attitude 屬性,每一個 CMAttitude 對象包含了下邊三種數(shù)據(jù)

  1. 一個四元組
  2. 一個旋轉(zhuǎn)率矩陣
  3. 歐拉角

調(diào)用方式和上邊測量加速度發(fā)方式類似书聚,代碼就不貼了唧领。

關(guān)于歐拉角

描述一個物體的旋轉(zhuǎn)藻雌,往往是最難的。歐拉角是表達旋轉(zhuǎn)的最簡單的一種方式斩个,形式上它是一個三維向量胯杭,其值分別代表物體繞坐標系三個軸(x,y,z軸)的旋轉(zhuǎn)角度。

pitch.gif
roll

yaw.gif

但是受啥,這么描述的話做个,會有一個問題,那就是萬向鎖滚局。

萬向鎖.jpg

歐拉旋轉(zhuǎn)可以靠這種順序讓一個物體指到任何一個想要的方向居暖,但如果在旋轉(zhuǎn)中不幸讓某些坐標軸重合了就會發(fā)生萬向節(jié)鎖,這時就會丟失一個方向上的旋轉(zhuǎn)能力藤肢,也就是說在這種狀態(tài)下我們無論怎么旋轉(zhuǎn)(當然還是要原先的順序)都不可能得到某些想要的旋轉(zhuǎn)效果太闺。具體可以點這里來了解。
由于大多數(shù)都是物理知識嘁圈,我這個農(nóng)民工省骂,了解的還不是很詳細,數(shù)據(jù)我們是得到了最住,具體怎么應用到現(xiàn)實中钞澳,還要慢慢摸索。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涨缚,一起剝皮案震驚了整個濱河市轧粟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脓魏,老刑警劉巖兰吟,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異轧拄,居然都是意外死亡揽祥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門檩电,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拄丰,“玉大人,你說我怎么就攤上這事俐末×习矗” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵卓箫,是天一觀的道長载矿。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么闷盔? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任弯洗,我火速辦了婚禮,結(jié)果婚禮上逢勾,老公的妹妹穿的比我還像新娘牡整。我一直安慰自己,他們只是感情好溺拱,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布逃贝。 她就那樣靜靜地躺著,像睡著了一般迫摔。 火紅的嫁衣襯著肌膚如雪沐扳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天句占,我揣著相機與錄音沪摄,去河邊找鬼。 笑死辖众,一個胖子當著我的面吹牛卓起,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凹炸,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼昼弟!你這毒婦竟也來了啤它?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤舱痘,失蹤者是張志新(化名)和其女友劉穎变骡,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芭逝,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡塌碌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了旬盯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片台妆。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖胖翰,靈堂內(nèi)的尸體忽然破棺而出接剩,到底是詐尸還是另有隱情,我是刑警寧澤萨咳,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布懊缺,位于F島的核電站,受9級特大地震影響培他,放射性物質(zhì)發(fā)生泄漏鹃两。R本人自食惡果不足惜遗座,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俊扳。 院中可真熱鬧途蒋,春花似錦、人聲如沸拣度。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抗果。三九已至筋帖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冤馏,已是汗流浹背日麸。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留逮光,地道東北人代箭。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像涕刚,于是被迫代替她去往敵國和親嗡综。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容