2013-03-26 132 views
34

假設您以縱向方向垂直握着iphone/ipad,屏幕正對着您。您將設備傾斜到一邊,讓屏幕朝向您。如何使用CMMotionManager測量靜態傾斜角度?這似乎是一個簡單的問題,應該有一個簡單的答案,但我找不到任何不會消失成四元數和旋轉矩陣的方法。使用CMMotionManager測量傾斜角度

任何人都可以指給我一個工作的例子嗎?

+0

「鑄造廠」以最好和唯一的方式回答你的問題。你應該將他的答案標記爲遺憾接受了。 – 2017-01-18 05:52:15

+0

這裏是我自己的實現,它比大多數更好:https://bitbucket.org/snippets/theoknock/74XxB/untitled-snippet – 2017-01-23 18:55:12

回答

49

gravity

self.deviceQueue = [[NSOperationQueue alloc] init]; 
self.motionManager = [[CMMotionManager alloc] init]; 
self.motionManager.deviceMotionUpdateInterval = 5.0/60.0; 

// UIDevice *device = [UIDevice currentDevice]; 

[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical 
                 toQueue:self.deviceQueue 
                withHandler:^(CMDeviceMotion *motion, NSError *error) 
{ 
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
     CGFloat x = motion.gravity.x; 
     CGFloat y = motion.gravity.y; 
     CGFloat z = motion.gravity.z; 
    }]; 
}]; 

有了這個參考幀(CMAttitudeReferenceFrameXArbitraryZVertical),如果z接近零,你在垂直與地面保持它在一個平面上(例如,如果你是拿着它反對一面牆),當你在那架飛機上旋轉它時,xy值會改變。垂直是其中x接近零並且y接近-1。


望着這post,我注意到,如果你想這個向量轉換成的角度,你可以使用下面的算法。

如果要計算有多少度垂直旋轉設備(其中正面是順時針,負是逆時針),你可以計算出這一點:

// how much is it rotated around the z axis 

CGFloat angle = atan2(y, x) + M_PI_2;   // in radians 
CGFloat angleDegrees = angle * 180.0f/M_PI; // in degrees 

您可以使用此圖出多少經由石英2D transform屬性來旋轉視圖:

self.view.layer.transform = CATransform3DRotate(CATransform3DIdentity, -rotateRadians, 0, 0, 1); 

(個人而言,我在一個CADisplayLink更新所述startDeviceMotionUpdates方法的旋轉角度,並更新此transform,其德從夫妻的角度更新屏幕更新)

你可以看到你多遠通過其傾斜向後/向前:

// how far it it tilted forward and backward 

CGFloat r = sqrtf(x*x + y*y + z*z); 
CGFloat tiltForwardBackward = acosf(z/r) * 180.0f/M_PI - 90.0f; 
+1

感謝羅布。這正是我需要的應用程序。 – 2014-01-31 05:39:59

+0

工程像魅力,謝謝你 – 2014-03-05 10:46:02

+0

完美答案! – alones 2014-12-02 06:01:14

7

這裏是轉動一個UIView self.horizon,以保持它的水平用一個例子當你傾斜設備時的地平線。

- (void)startDeviceMotionUpdates 
{ 
    CMMotionManager* coreMotionManager = [[CMMotionManager alloc] init]; 
    NSOperationQueue* motionQueue = [[NSOperationQueue alloc] init] 
    CGFloat updateInterval = 1/60.0; 
    CMAttitudeReferenceFrame frame = CMAttitudeReferenceFrameXArbitraryCorrectedZVertical; 
    [coreMotionManager setDeviceMotionUpdateInterval:updateInterval]; 
    [coreMotionManager startDeviceMotionUpdatesUsingReferenceFrame:frame 
          toQueue:motionQueue 
          withHandler: 
    ^(CMDeviceMotion* motion, NSError* error){ 
     CGFloat angle = atan2(motion.gravity.x, motion.gravity.y); 
     CGAffineTransform transform = CGAffineTransformMakeRotation(angle); 
     self.horizon.transform = transform; 
    }]; 
} 

這是一個有點過於簡單 - 讓你想通過屬性預先初始化這個和訪問它,你應該確保只有一個CMMotionManager的情況下在你的應用程序。

+0

非常感謝,這是我需要的atan2函數(儘管我質疑需要將M_PI添加到角度?)。當z值太高或太低時,我已經使用先前的回答來指導我警告用戶,以確保他/她垂直持有設備。我也很感激提醒在應用程序中只創建一個CMMotionManager。 – millport 2013-03-27 14:45:17

+1

@millport,你說得對,沒有M_PI就可以正常工作 - 我正在使用這個功能,我希望我的數字保持在零以上。我已經解決了答案。 – foundry 2013-03-27 15:18:01

+0

@foundry Bravo!這真的是它應該完成的唯一方式(即,atan2 ...);該公式是Apple用於將首選旋轉變換應用於OpenGL着色器輸出的座標位置的公式。由GPU傳遞給OpenGL着色器的任何其他數據生成的數值必須與GPU爲生成預期結果(關閉不會削減它)而生成的數值一樣精確。所有上述的唯一公式就是爲您提供的OpenGL完美精度。 – 2017-01-18 05:48:57

8

這是一個很晚的答案,但你可以找到一個working example on githubblog article隨它而來。

總結上面提到的文章,可以使用四元數來避免在垂直拿着iPhone時可能面臨的gimbal lock problem

這裏是計算傾斜(或偏航)的編碼部分:

CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion; 
double yaw = asin(2*(quat.x*quat.z - quat.w*quat.y)); 

// use the yaw value 
// ... 

你甚至可以添加一個簡單的Kalman filter緩解偏航:

CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion; 
double yaw = asin(2*(quat.x*quat.z - quat.w*quat.y)); 

if (self.motionLastYaw == 0) { 
    self.motionLastYaw = yaw; 
} 

// kalman filtering 
static float q = 0.1; // process noise 
static float r = 0.1; // sensor noise 
static float p = 0.1; // estimated error 
static float k = 0.5; // kalman filter gain 

float x = self.motionLastYaw; 
p = p + q; 
k = p/(p + r); 
x = x + k*(yaw - x); 
p = (1 - k)*p; 
self.motionLastYaw = x; 

// use the x value as the "updated and smooth" yaw 
// ... 
+1

爲什麼要加倍努力,並使用過於複雜的卡爾曼濾波器?爲什麼不是一個簡單的低通濾波器? – openfrog 2013-11-29 16:27:19

+0

這是一個很好的觀點,我受到信號處理文章的影響,並牢記KF是一個非常好的工具。我顯然不是這個領域的專家。基本上我試圖配置卡爾曼濾波器的行爲就像一個低通濾波器,所以是的,爲什麼不簡單地使用低通濾波器:) 如果你有一個更好的實現,去爲它,我會很高興更新答案接着就,隨即。 – dulaccc 2013-11-30 13:22:10

+4

只是想提一下,所有這些與四元數的解決方案,是唯一一個可靠的工作,並避免萬向節鎖問題 - 謝謝你親切先生! – hajn 2014-02-26 19:06:12

1

由於iOS8上CoreMotion也將返回一個CMAttitude對象,其中包含pitch,rollyaw屬性,以及四元數。使用這將意味着您不必執行手動數學將加速度轉換爲方向。