Android 方向感測器與磁力計和加速度感測器之間的關係
一般情況下,在android系統中獲取手機的方位資訊azimuth似乎是很簡單的事情,在api中有TYPE_ORIENTATION常量,可以像得到加速度感測器那樣得到方向感測器sm.getDefaultSensor(Sensor.TYPE_ORIENTATION);然而我們這樣做的話在最新版的SDK中就會看到這麼一句話:“TYPE_ORIENTATION This
constant is deprecated. use SensorManager.getOrientation() instead. ”即這種方式也過期,不建議使用!Google建議我們在應用程式中使用SensorManager.getOrientation()來獲得原始資料。
其實,Android中的方向感測器也是是通過磁力計magnetometer和加速度感測器accelerometer抽象出來的。因此我們可以通過磁力計magnetometer和加速度感測器accelerometer來獲得方位資訊。由磁場和加速度如何得到方位資訊的演算法在api中已被封裝好了。通過這種方式比直接獲得方向感測器獲得的資訊更準確。
values[0] :azimuth 方向角,但用(磁場+加速度)得到的資料範圍是(-180~180),也就是說,0表示正北,90表示正東,180/-180表示正南,-90表示正西。而直接通過方向感應器資料範圍是(0~359)360/0表示正北,90表示正東,180表示正南,270
values[1] pitch 傾斜角即由靜止狀態開始,前後翻轉
values[2] roll 旋轉角即由靜止狀態開始,左右翻轉
//api中原始碼
public static float[] getOrientation(float[] R, float values[]) {
/*
* 4x4 (length=16) case:
* / R[ 0] R[ 1] R[ 2] 0 \
* | R[ 4] R[ 5] R[ 6] 0 |
* | R[ 8] R[ 9] R[10] 0 |
* \ 0 0 0 1 /
*
* 3x3 (length=9) case:
* / R[ 0] R[ 1] R[ 2] \
* | R[ 3] R[ 4] R[ 5] |
* \ R[ 6] R[ 7] R[ 8] /
*
*/
if (R.length == 9) {
values[0] = (float)Math.atan2(R[1], R[4]);
values[1] = (float)Math.asin(-R[7]);
values[2] = (float)Math.atan2(-R[6], R[8]);
} else {
values[0] = (float)Math.atan2(R[1], R[5]);
values[1] = (float)Math.asin(-R[9]);
values[2] = (float)Math.atan2(-R[8], R[10]);
}
return values;
}
//getRotaionMatrix原始碼
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;
}
其中R[]資料是旋轉陣列,用來存放磁場和加速度的資料。之後在通過getOrientation方法通過一定的演算法利用R[]得到方位資訊values[]陣列。
編寫的程式碼如下:
package xuan.android.orientation;
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;
public class OrientationActivity extends Activity {
/** Called when the activity is first created. */
TextView textview=null;
private SensorManager sm=null;
private Sensor aSensor=null;
private Sensor mSensor=null;
float[] accelerometerValues=new float[3];
float[] magneticFieldValues=new float[3];
float[] values=new float[3];
float[] rotate=new float[9];
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textview=(TextView)findViewById(R.id.view0);
sm=(SensorManager)getSystemService(Context.SENSOR_SERVICE);
aSensor=sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensor=sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
sm.registerListener(myListener, aSensor, SensorManager.SENSOR_DELAY_GAME);
sm.registerListener(myListener, mSensor, SensorManager.SENSOR_DELAY_GAME);
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
sm.unregisterListener(myListener);
}
final SensorEventListener myListener=new SensorEventListener(){
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
@Override
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
if(event.sensor.getType()==Sensor.TYPE_ACCELEROMETER){
accelerometerValues=event.values;
}
if(event.sensor.getType()==Sensor.TYPE_MAGNETIC_FIELD){
magneticFieldValues=event.values;
}
SensorManager.getRotationMatrix(rotate, null, accelerometerValues, magneticFieldValues);
SensorManager.getOrientation(rotate, values);
//經過SensorManager.getOrientation(rotate, values);得到的values值為弧度
//轉換為角度
values[0]=(float)Math.toDegrees(values[0]);
textview.setText("x="+values[0]);
}};
}