SensorManager是Android中的一個類,其有一個函數getRotationMatrix
,可以計算出旋轉矩陣,進而通過getOrientation
求得設備的方向(航向角、俯仰角、橫滾角)。函數getRotationMatrix的源碼如下所示,源碼中雖然對該函數整體進行了解釋,但是對代碼中各個參數的計算沒有說明,如為什么加速度的數值要和磁力計的數值做差乘。在網上各種搜索后,找到一段老外對這個問題的英文解釋,很好的回答了上述問題。大意翻譯(包括自己的理解)如下:加速度數值和磁力計數值均是向量,手機水平放置時,加速度讀數實際上就是重力向量,方向是豎直朝下的;磁力計表示本地的磁場,不考慮環境影響及磁偏角的話,認為磁場方向是水平南北朝向的。因此,代碼中首先對加速度和磁力計數據做了一個差乘,得出一個水平東西方向的向量(差乘的定義)。經過這個運算,本來只有一個平面的向量,變成了三個三維立體平面的向量,從而可以用來計算設備的方向。源碼中后面又做了一次差乘,是用計算出的水平東西方向的向量和重力向量做的差乘,這次運算重新得出一個水平南北方向的向量,最后旋轉矩陣中用這三個向量(兩個計算出的水平向量、一個重力向量)構成。
1、SensorManager.getRotationMatrix
public static boolean getRotationMatrix(float[] R, float[] I, float[] gravity, float[] geomagnetic) { // TODO: move this to native code for efficiency float Ax = gravity[0]; float Ay = gravity[1]; float Az = gravity[2]; final float Ex = geomagnetic[0]; final float Ey = geomagnetic[1]; final float Ez = geomagnetic[2]; float Hx = Ey*Az - Ez*Ay; float Hy = Ez*Ax - Ex*Az; float Hz = Ex*Ay - Ey*Ax; final float normH = (float)Math.sqrt(Hx*Hx + Hy*Hy + Hz*Hz); if (normH < 0.1f) { // device is close to free fall (or in space?), or close to // magnetic north pole. Typical values are > 100. return false; } final float invH = 1.0f / normH; Hx *= invH; Hy *= invH; Hz *= invH; final float invA = 1.0f / (float)Math.sqrt(Ax*Ax + Ay*Ay + Az*Az); Ax *= invA; Ay *= invA; Az *= invA; final float Mx = Ay*Hz - Az*Hy; final float My = Az*Hx - Ax*Hz; final float Mz = Ax*Hy - Ay*Hx; if (R != null) { if (R.length == 9) { R[0] = Hx; R[1] = Hy; R[2] = Hz; R[3] = Mx; R[4] = My; R[5] = Mz; R[6] = Ax; R[7] = Ay; R[8] = Az; } else if (R.length == 16) { R[0] = Hx; R[1] = Hy; R[2] = Hz; R[3] = 0; R[4] = Mx; R[5] = My; R[6] = Mz; R[7] = 0; R[8] = Ax; R[9] = Ay; R[10] = Az; R[11] = 0; R[12] = 0; R[13] = 0; R[14] = 0; R[15] = 1; } } if (I != null) { // compute the inclination matrix by projecting the geomagnetic // vector onto the Z (gravity) and X (horizontal component // of geomagnetic vector) axes. final float invE = 1.0f / (float)Math.sqrt(Ex*Ex + Ey*Ey + Ez*Ez); final float c = (Ex*Mx + Ey*My + Ez*Mz) * invE; final float s = (Ex*Ax + Ey*Ay + Ez*Az) * invE; if (I.length == 9) { I[0] = 1; I[1] = 0; I[2] = 0; I[3] = 0; I[4] = c; I[5] = s; I[6] = 0; I[7] =-s; I[8] = c; } else if (I.length == 16) { I[0] = 1; I[1] = 0; I[2] = 0; I[4] = 0; I[5] = c; I[6] = s; I[8] = 0; I[9] =-s; I[10]= c; I[3] = I[7] = I[11] = I[12] = I[13] = I[14] = 0; I[15] = 1; } } return true; }
2、英文原文解釋
If I understand your problem correctly you want your phone to know its 3D orientation. You need at least two vectors to do that. The xyz accelerometer produces a gravity vector that goes straight down. An xyz magnetometer can provide a second vector that is horizontally towards magnetic north and vertically downward in the northen hemisphere. The cross-product of these two vectors will be horizontal the magnetic east-west directions and the cross-product between the east-west vector and the gravity vector will be horizontal in the magnetic north-south directions. Formulas exist for converting magnetic to geographical north although I don't know them offhand. An xyz accelerometer by itself produces just the gravity vector, which could allow you to use your cell phone as an electronic bi-directional level (lateral and axial to the orientation of the accelerometer).
Read more: https://www.physicsforums.com