iOS傳感器:利用磁力計完成一個AR場景應用

前面我們用了陀螺儀、加速傳感器作了一些好玩的效果,今天我們就用用第三個傳感器--磁力計--來作一個AR的場景。說到AR這個詞,請你們不要噴我哈,並無用到WWDC剛出的ARKit。並且今天這個例子重點是學習使用磁力計,本質上來說和AR關係並不大。噱頭,就是個噱頭。。。。html

image.png

磁力計跟前面的加速計、陀螺儀,都是用到了上次說的iOS當中的那個核心運動框架CoreMotion, 也都用了CMMotionManagergit

完成後的效果,能看到在視頻輸出的下面會有一個隨着屏幕移動的天空星辰背景圖,同時屏幕左上角會實時打印當前的方向信息、地理信息。bash

磁力計.gif

1. 磁力計的介紹

磁強計指的是各類用於測量磁場的儀器,也稱磁力儀、高斯計。它能夠感應地球的磁場,得到方向信息。網絡

1.1 應用場景

那顯而易見,典型的應用場景就是用在電子羅盤和導航上面。多線程

以前看到過某個大神用磁力計簡直玩出了花兒,隔空抓牛的感受。利用iPhone上磁力計、加速計和麥克風實現平面和三維上的磁鐵追蹤,並能實時的反饋在iPhone 屏幕上。框架

image.png

看上去屌炸了,有沒有?宅胖還專門找到了這篇文章的報道,有興趣的能夠進去看看,裏面有實現後的視頻。mobile.163.com/14/1127/09/…ide

1.2 須要瞭解的基本概念

要用到磁力計,常常會聽到有人說到「磁北」、「真北」這兩個高頻詞,CoreMotion也會給咱們返回這兩個數值。是什麼意思吶?函數

  • 真北:指的是地理的北極
  • 磁北:指的是磁場北極

納尼?這是什麼鬼?來來來,我們科普一下。學習

  1. 磁北 磁北是以大地磁場爲基準的,經過各類傳感器傳送的方位都是以磁北爲基準的。BUT!!!!敲黑板!!!!!磁北的具體位置是隨着時間而改變的。 也就是說我們隨着地球的旋轉,我們除了有一年四季的變化、時間的變化,連磁場都會發生改變。嗯,是這樣的。ui

  2. 真北 因爲磁北是會變化的,那咱們怎麼用?不可能還要計算地球自轉軸、考慮時間因素吧。因此纔有了真北這個概念。

真北是地球自轉的地理北極,這個就是考慮到了各類因素,是一個固定的位置。因此我們電子羅盤神馬的所指的北,說的是這個真北。一般狀況下,方位都是須要矯正到真北的。蘋果很貼心啊,真北就不用本身算了,直接也會有返回的數值。

剩下的還有磁偏角校訂、網絡北、網絡北校訂、收斂角等等學術概念。

那豈不是電子羅盤上面的北和指南針上面的北不一致啊?

問這個問題的童鞋那是至關的聰明呀,那確定是不一致的。不過偏差也是在可感官接受的範圍內。在等會兒的例子裏面,我們把這兩個數值都打印出來,本身看看。

2 . 磁力計的使用

2.1 使用步驟

磁力計一樣也是經過CoreMotion 這個框架來管理的,因此和前面兩個傳感器同樣,四個標準步驟:

  • 初始化CMMotionManager管理對象;
  • 調用管理對象的對象方法獲取數據;
  • 處理數據;
  • 當不須要使用的時候,中止獲取數據。

2.2 磁力計數據獲取的兩種方法

CoreMotion中有2種獲取數據方式,一種叫作PUSH的方式,一種叫作PULL的方式。顧名思義,PUSH就是被動的獲取。設定完了以後,線程定時把獲取到的數據推送回來。可想而知,對於資源的消耗是會稍微大一點的。PULL,就是要去索取。拉一下才會獲取到數據。不要不給。上一次代碼是Swift的,這一次我們就使用OC啦。

2.2.1 PULL的方式

//PULL的方法獲取數據
- (void)pullMagnetometer {
    //    判斷磁力計是否可用
    if (self.manager.magnetometerAvailable) {
        //        設置磁力計採樣頻率
        self.manager.magnetometerUpdateInterval = 0.1;
        
        //開始更新,後臺線程開始運行。這是Pull方式。
        [self.manager startMagnetometerUpdates];
        NSLog(@"X = %f,Y = %f,Z = %f",self.manager.magnetometerData.magneticField.x,self.manager.magnetometerData.magneticField.y,self.manager.magnetometerData.magneticField.z);
        
    } else {
        NSLog(@"It cannot be used!");
    }
}
複製代碼

2.2.2 PUSH的方式

//PUSH的方法獲取數據
- (void)pushMagnetometer {
    //    判斷磁力計是否可用
    if (self.manager.magnetometerAvailable) {
        //        設置磁力計採樣頻率
        self.manager.magnetometerUpdateInterval = 0.1;
        
        //Push方式獲取和處理數據,這裏咱們同樣只是作了簡單的打印。把採樣的工做放在了主線程中。
        [self.manager startMagnetometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMMagnetometerData * _Nullable magnetometerData, NSError * _Nullable error) {
            NSLog(@"X = %f,Y = %f,Z = %f",magnetometerData.magneticField.x,magnetometerData.magneticField.y,magnetometerData.magneticField.z);
        }];
    } else {
        NSLog(@"It cannot be used!");
    }
}
複製代碼

3. 開始咱們的小案例

寫這個案例的時候發現要實現一個比較逼真的AR,我們有好多東東都沒有分享過。因此例子寫完以後,寫這篇文章的時候又對這個例子作了一些調整。大幅簡化刪減了了好多需求。可是,最後仍是使用了相機、百度地圖,若是這兩個都不用,那真的一點都不能算是AR了。

完成後的效果,能看到在視頻輸出的下面會有一個隨着屏幕移動的天空星辰背景圖,同時屏幕左上角會實時打印當前的方向信息、地理信息。

磁力計.gif

小案例裏面的相機不用緊張,我們後面也仍是會分享的。還有一個以前說過的,多線程也記得的哈,下一個系列就來補。

3.1 思惟導圖

image.png

3.2 準備工做

3.2.1 SDK的導入

這個例子裏面我們用了百度地圖,因此須要導入百度地圖的SDK。由於我們沒有分享過如何使用第三方庫,能夠看看這篇文章iOS·採用第三方(百度地圖SDK)實現定位等功能開發

3.2.2 相機、定位權限的索取

iPhone對於APP使用用戶的隱私權限作了很嚴格的規定,每一個APP使用用戶隱私以前必需要讓用戶知道而且贊成。大概也正是由於這點,本宅胖才這麼愛iPhone吧。雖然開發的時候就面臨着不少問題,但至少產品始終是站在用戶的角度考慮問題的。

在Info.plist中向用戶索取相機和地理位置信息的權限。

  • Privacy - Camera Usage Description
  • Privacy - Location When In Use Usage Description

image.png
麥克風、媒體庫的權限就不須要了。以前沒有刪減的那個案例裏面用到了這個。提及來好心疼~~~

3.2.3 相機使用

相機在這個案例裏面,使用的是AVFoundation框架。也是很心痛,這部分以前沒有分享過。因此若是等不及俺的分享,能夠先看看這個。Objc的第21期內容:iOS上的相機捕捉

別忘了在頭文件<AVFoundation/AVFoundation.h>,同時遵照代理協議AVCaptureVideoDataOutputSampleBufferDelegate

3.3 建立動態活動的星空背景

從網上找到的星空圖是4000*2800的大小,要讓它徹底超出屏幕。這樣才能根據手機的移動進行活動。

一樣的,爲了可以明顯的看到效果,在從陀螺儀獲取到的數值以後,添加了一個放大倍數。這個小例子裏面我們使用的是5。

3.3.1 使用陀螺儀進行防抖

若是陀螺儀返回的數據在某個特定小範圍內,咱們就是視同只是手抖,不對圖片自己進行處理。這樣就看不到背景圖片明顯抖動的感受了。

  • 注意:陀螺儀返回的各軸旋轉角度是有可能爲負數的,因此別忘了用絕對值進行判斷。
//            作一下防抖動的處理,若是手機旋轉的不太大,就不執行操做
if (fabs(gyroData.rotationRate.x) * multiplier < 0.2 && fabs(gyroData.rotationRate.y) * multiplier < 0.2) {
    return ;
}

複製代碼

3.3.2 讓背景星空圖隨着屏幕進行運行

直接修改背景圖的center就行了,讓原center添加上須要進行的位移量就能夠實現了。 這裏須要注意的是,須要對邊界值進行處理。若是屏幕旋轉的亂七八糟,咱們要讓視頻輸出層下面始終有一個背景存在。

            //            由於背景圖的大小事屏幕寬度的三倍,高度的兩倍。爲了防止超出邊界,進行限制
            if (imageRotationX > self.view.frame.size.width * 1.5) {
                imageRotationX = self.view.frame.size.width * 1.5;
            }
            
            if(imageRotationX < (- self.view.frame.size.width * 0.5)){
                imageRotationX=(- self.view.frame.size.width * 0.5);
            }
            
            if (imageRotationY > self.view.frame.size.height) {
                imageRotationY = self.view.frame.size.height;
            }
            if (imageRotationY < 0) {
                imageRotationY = 0;
            }

複製代碼

3.4 利用百度地圖輸出磁力計信息、經緯度、高度

3.4.1 輸出磁力計信息

根據百度地圖SDK的文檔,在用戶的方向信息放生變化以後,會調用如下的方法。 這裏咱們沒有作任何特殊的處理,就只是簡單的打印出來了磁北、真北、三軸的偏移量。

等會兒運行的時候你們就能看到以前的問題,到底磁北、真北之間相差多少。

/**
 *用戶方向更新後,會調用此函數
 *@param userLocation 新的用戶位置
 */
- (void)didUpdateUserHeading:(BMKUserLocation *)userLocation {
    self.magnetometerInfo.numberOfLines = 0;
    self.magnetometerInfo.text = [NSString stringWithFormat:@"磁北:%.0f,真北:%.0f \n偏移:%.0f \nx:%.1f y:%.1f z:%.1f",
                                  userLocation.heading.magneticHeading,userLocation.heading.trueHeading,userLocation.heading.headingAccuracy,userLocation.heading.x,userLocation.heading.y,userLocation.heading.z];
}

複製代碼

3.4.2 輸出用戶位置信息:經緯度、高度

/**
 *用戶位置更新後,會調用此函數
 *@param userLocation 新的用戶位置
 */
- (void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation
{
    self.physicalLocation.numberOfLines = 0;
    self.physicalLocation.text = [NSString stringWithFormat:@"經度:%f \n緯度:%f \n高度:%f",userLocation.location.coordinate.longitude,userLocation.location.coordinate.latitude,userLocation.location.altitude];
}

複製代碼

源代碼下載地址:OC下載地址

相關文章
相關標籤/搜索