首页 > 代码库 > Android中SensorManager.getRotationMatrix函数原理解释
Android中SensorManager.getRotationMatrix函数原理解释
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
Android中SensorManager.getRotationMatrix函数原理解释