關(guān)于CoreMotion
Core Motion可以讓開發(fā)者從各個內(nèi)置傳感器那里獲取未經(jīng)修改的傳感數(shù)據(jù),并觀測或響應設(shè)備各種運動和角度變化翩概。這些傳感器包括陀螺儀、加速器和磁力儀(羅盤)返咱。
CoreMotion負責處理的數(shù)據(jù)
它包含幾種類型的數(shù)據(jù)钥庇,這些類都是CMLogItem類的子類:
- 加速度數(shù)據(jù) :CMAccelerometerData
typedef struct {
double x;
double y;
double z;
} CMAcceleration;
@interface CMAccelerometerData : CMLogItem
{
@private
id _internal;
}
//加速度的數(shù)據(jù)對象
@property(readonly, nonatomic) CMAcceleration acceleration;
@end
- 螺旋儀數(shù)據(jù) :CMGyroData
typedef struct {
double x;
double y;
double z;
} CMRotationRate;
@interface CMGyroData : CMLogItem
{
@private
id _internal;
}
//螺旋儀數(shù)據(jù)對象
@property(readonly, nonatomic) CMRotationRate rotationRate;
@end
- 磁感應數(shù)據(jù) :magnetometerData
typedef struct {
double x;
double y;
double z;
} CMMagneticField;
@interface CMMagnetometerData : CMLogItem
{
@private
id _internal;
}
//磁力對象
@property(readonly, nonatomic) CMMagneticField magneticField;
@end
- 前三種數(shù)據(jù)通過復雜運算得到的設(shè)備的運動數(shù)據(jù) :CMDeviceMotion
@interface CMDeviceMotion : CMLogItem
{
@private
id _internal;
}
//設(shè)備的狀態(tài)對象
@property(readonly, nonatomic) CMAttitude *attitude;
//設(shè)備的角速度
@property(readonly, nonatomic) CMRotationRate rotationRate;
//設(shè)備的重力加速度
@property(readonly, nonatomic) CMAcceleration gravity;
//用戶嫁給設(shè)備的加速度 設(shè)備的總加速度為重力加速度叫上用戶給的加速度
@property(readonly, nonatomic) CMAcceleration userAcceleration;
//設(shè)備的磁場矢量對象
@property(readonly, nonatomic) CMCalibratedMagneticField magneticField NS_AVAILABLE(NA,5_0);
我們來看下 attitude 這個對象, 它又封裝了許多設(shè)備的狀態(tài)屬性
@interface CMAttitude : NSObject <NSCopying, NSSecureCoding>
{
@private
id _internal;
}
//設(shè)備的歐拉角roll
@property(readonly, nonatomic) double roll;
//設(shè)備的歐拉角pitch
@property(readonly, nonatomic) double pitch;
//設(shè)備的歐拉角yaw
@property(readonly, nonatomic) double yaw;
//設(shè)備狀態(tài)的旋轉(zhuǎn)矩陣
@property(readonly, nonatomic) CMRotationMatrix rotationMatrix;
//設(shè)備狀態(tài)的四元數(shù)
@property(readonly, nonatomic) CMQuaternion quaternion;
@end
CoreMotion的使用
CoreMotion中獲取數(shù)據(jù)主要是兩種方式:
一種是Push咖摹,就是你提供一個線程管理器NSOperationQueue评姨,再提供一個Block,這樣萤晴,CoreMotion自動在每一個采樣數(shù)據(jù)到來的時候回調(diào)這個Block吐句,進行處理。在這中情況下店读,block中的操作會在你自己的主線程內(nèi)執(zhí)行嗦枢。
一種是 Pull,在這個方式里屯断,你必須主動去像CMMotionManager要數(shù)據(jù)文虏,這個數(shù)據(jù)就是最近一次的采樣數(shù)據(jù)侣诺。你不去要,CMMotionManager就不會給你氧秘。當然年鸳,在這種情況下,CoreMotion所有的操作都在自己的后臺線程中進行丸相,不會有任何干擾你當前線程的行為阻星。
有兩點需要注意:第一就是需要檢測是否可用,第二就是在不需要的時候已添,停止更新數(shù)據(jù)。蘋果建議在使用CoreMotionManager的時候采用單例模式滥酥。
我們以CMAccelerometerData為例更舞,其他方式都是一樣的
pull獲取方式
if ([_motionManager isAccelerometerAvailable]) {
// 設(shè)置加速計采樣頻率
[_motionManager setAccelerometerUpdateInterval:1 / 40.0];
[_motionManager startAccelerometerUpdates];
}else {
NSLog(@"設(shè)備不支持加速計");
}
//可以寫個定時器去定時主動獲取數(shù)據(jù)
//獲取數(shù)據(jù)
CMAcceleration acceleration=_motionManager.accelerometerData.acceleration;
NSLog(@"%f---%f---%f",acceleration.x,acceleration.y,acceleration.z);
push獲取方式
//判斷加速計是否可用
if([_motionManager isAccelerometerAvailable]) {
// 設(shè)置加速計頻率
[_motionManager setAccelerometerUpdateInterval:1 / 40.0];
//開始采樣數(shù)據(jù)
[_motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
NSLog(@"%f---%f",accelerometerData.acceleration.x,accelerometerData.acceleration.y);
}];
} else{
NSLog(@"設(shè)備不支持加速計");
}
我們再列舉幾個它的其他屬性和方法:
@interface CMMotionManager : NSObject
{
@private
id _internal;
}
//設(shè)置加速度傳感器更新幀率
@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;
//加速度傳感器數(shù)據(jù)對象
@property(readonly, nullable) CMAccelerometerData *accelerometerData __TVOS_PROHIBITED;
//pull方式開始更新加速度數(shù)據(jù)
- (void)startAccelerometerUpdates __TVOS_PROHIBITED;
//push方式更新加速度數(shù)據(jù)
- (void)startAccelerometerUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMAccelerometerHandler)handler __TVOS_PROHIBITED;
//停止更新加速度數(shù)據(jù)
- (void)stopAccelerometerUpdates __TVOS_PROHIBITED;
//設(shè)備狀態(tài)更新幀率
@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;
//設(shè)備運動信息是否可用
@property(readonly, nonatomic, getter=isDeviceMotionAvailable) BOOL deviceMotionAvailable __TVOS_PROHIBITED;
//設(shè)備運動信息是否激活
@property(readonly, nonatomic, getter=isDeviceMotionActive) BOOL deviceMotionActive __TVOS_PROHIBITED;
//設(shè)備運動信息對象
@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方式開始刷新設(shè)備運動信息
- (void)startDeviceMotionUpdatesUsingReferenceFrame:(CMAttitudeReferenceFrame)referenceFrame toQueue:(NSOperationQueue *)queue withHandler:(CMDeviceMotionHandler)handler NS_AVAILABLE(NA,5_0) __TVOS_PROHIBITED;
//停止刷新設(shè)備運動信息
- (void)stopDeviceMotionUpdates __TVOS_PROHIBITED;
關(guān)于CMDeviceMotion
我們都知道,設(shè)備靜止時受到的地球引力為1g坎吻,1g是物體在地球的海平面上受到的下拉力(9.8米/秒2)缆蝉。假如設(shè)備從高處掉落,其加速計測量到的加速度將為0g瘦真。假如設(shè)備水平放在桌面上刊头,則加速計測量出的加速度為1g,且方向朝上诸尽。
加速計測量3個軸(x原杂、y和z)上的值,如圖所示:
這個軸在方向上有些不同于傳統(tǒng)坐標軸您机,考慮以下實際情況:
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
僅當設(shè)備的朝向相對于重力的方向發(fā)生變化時际看,加速計才能檢測到咸产;要同時檢測設(shè)備的朝向和運動數(shù)據(jù),就需要用到陀螺儀了仲闽。當查詢設(shè)備的陀螺儀時脑溢,它將報告設(shè)備繞x, y, z軸的旋轉(zhuǎn)速度,單位為弧度/秒赖欣;2弧度相當于一整圈屑彻,因此陀螺儀返回讀數(shù)2表示設(shè)備繞相應的軸每秒轉(zhuǎn)一圈。
有兩種方式訪問設(shè)備的朝向和運動數(shù)據(jù)畏鼓,一種是通過UIDevice請求朝向通知酱酬,另一種是利用框架Core Motion定期地直接訪問加速計和陀螺儀數(shù)據(jù)。
- 通過UIDevice請求朝向通知
雖然可直接查詢加速計并使用它返回的值判斷設(shè)備的朝向云矫,但Apple為開發(fā)人員簡化了這項工作膳沽。單例UIDevice表示當前設(shè)備,它包含方法beginGeneratingDeviceOrientationNotifications,該方法命令iOS將朝向通知發(fā)送到通知中心(NSNotificationCenter)挑社。啟動通知后陨界,就可以注冊一個NSNotificationCenter實例,以便設(shè)備的朝向發(fā)生變化時自動調(diào)用指定的方法痛阻。
通過訪問UIDevice的屬性orientation來獲得設(shè)備當前朝向菌瘪,該屬性的類型為枚舉值
UIDeviceOrientation,有6個預定義值:
UIDeviceOrientationFaceUp — 設(shè)備正面朝上
UIDeviceOrientationFaceDown — 設(shè)備正面朝下
UIDeviceOrientationPortrait — 縱向(Home鍵在下)
UIDeviceOrientationPortraitUpsideDown — 縱向倒轉(zhuǎn)(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(@"當前朝向枚舉數(shù)字值:%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讀取加速計和陀螺儀數(shù)據(jù)
利用UIDevice只能判斷極端朝向阱当,應用程序經(jīng)常要獲悉這些朝向之間的過渡狀態(tài)俏扩,如設(shè)備處于某個傾斜位置。Core Motion運動管理器讓您能夠指定從加速計和陀螺儀那里接收更新的頻率(單位為秒)弊添,還讓您能夠直接指定一個處理程序塊(handle block)录淡,每當更新就緒時都將執(zhí)行該處理程序塊。
實際加速度在Core Motion里被分解成了兩部分:Gravity和UserAcceleration油坝。Gravity代表重力1g在設(shè)備的分布情況嫉戚,UserAcceleration代表設(shè)備運動中的加速度分布情況。將這兩者相加就等于實際加速度澈圈。Gravity的三個軸所受的重力加起來始終等于1g彬檀,而UserAcceleration取決于單位時間內(nèi)動作的幅度大小。
CMRotationRate的X瞬女,Y,Z分別代表三個軸上的旋轉(zhuǎn)速率窍帝,單位為弧度/秒。旋轉(zhuǎn)速度為1弧度/秒诽偷,意味著設(shè)備每秒旋轉(zhuǎn)半圈盯桦。這里復習一下弧度與角度的轉(zhuǎn)換:
1角度 = π/180 弧度
1弧度 = 180/π角度
360角度 = 360 * π/180 = 2π弧度 = 一整圈
CMAttitude的三個屬性Yaw,Pitch和Roll分別代表左右擺動、俯仰以及滾動渤刃∮德停可以將設(shè)備想象成一架飛機,下面的gif圖演示了各種運動狀態(tài):
Yaw的運動狀態(tài):
Pitch的運動狀態(tài):
Roll的運動狀態(tài):
實例
下面的代碼實現(xiàn)了這樣一個界面卖子,通過CMMotionManager返回了設(shè)備的各個狀態(tài)值:
#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