前面我們們用了陀螺儀、加速感測器做了一些好玩的效果,今天我們們就用用第三個感測器--磁力計--來做一個AR的場景。說到AR這個詞,請大家不要噴我哈,並沒有用到WWDC剛出的ARKit。而且今天這個例子重點是學習使用磁力計,本質上來講和AR關係並不大。噱頭,就是個噱頭。。。。
磁力計跟前面的加速計、陀螺儀,都是用到了上次說的iOS當中的那個核心運動框架CoreMotion
, 也都用了CMMotionManager
。
完成後的效果,能看到在視訊輸出的下面會有一個隨著螢幕移動的天空星辰背景圖,同時螢幕左上角會實時列印當前的方向資訊、地理資訊。
1. 磁力計的介紹
磁強計指的是各種用於測量磁場的儀器,也稱磁力儀、高斯計。它可以感應地球的磁場,獲得方向資訊。
1.1 應用場景
那顯而易見,典型的應用場景就是用在電子羅盤和導航上面。
之前看到過某個大神用磁力計簡直玩出了花兒,隔空抓牛的感覺。利用iPhone上磁力計、加速計和麥克風實現平面和三維上的磁鐵追蹤,並能實時的反饋在iPhone 螢幕上。
看上去屌炸了,有沒有?宅胖還專門找到了這篇文章的報導,有興趣的可以進去看看,裡面有實現後的視訊。mobile.163.com/14/1127/09/…
1.2 需要了解的基本概念
要用到磁力計,經常會聽到有人說到“磁北”、“真北”這兩個高頻詞,CoreMotion也會給我們返回這兩個數值。是什麼意思吶?
- 真北:指的是地理的北極
- 磁北:指的是磁場北極
納尼?這是什麼鬼?來來來,我們們科普一下。
-
磁北 磁北是以大地磁場為基準的,通過各種感測器傳送的方位都是以磁北為基準的。BUT!!!!敲黑板!!!!!磁北的具體位置是隨著時間而改變的。 也就是說我們們隨著地球的旋轉,我們們除了有一年四季的變化、時間的變化,連磁場都會發生改變。嗯,是這樣的。
-
真北 由於磁北是會變化的,那我們怎麼用?不可能還要計算地球自轉軸、考慮時間因素吧。所以才有了真北這個概念。
真北是地球自轉的地理北極,這個就是考慮到了各種因素,是一個固定的位置。所以我們們電子羅盤神馬的所指的北,說的是這個真北。通常情況下,方位都是需要矯正到真北的。蘋果很貼心啊,真北就不用自己算了,直接也會有返回的數值。
剩下的還有磁偏角校正、網路北、網路北校正、收斂角等等學術概念。
那豈不是電子羅盤上面的北和指南針上面的北不一致啊?
問這個問題的童鞋那是相當的聰明呀,那肯定是不一致的。不過誤差也是在可感官接受的範圍內。在等會兒的例子裡面,我們們把這兩個數值都列印出來,自己看看。
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了。
完成後的效果,能看到在視訊輸出的下面會有一個隨著螢幕移動的天空星辰背景圖,同時螢幕左上角會實時列印當前的方向資訊、地理資訊。
小案例裡面的相機不用緊張,我們們後面也還是會分享的。還有一個之前說過的,多執行緒也記得的哈,下一個系列就來補。
3.1 思維導圖
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
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下載地址