Android 推荐使用SensorManager的加速度传感器和磁场传感器配合使用,实现指南针。
垂直手机屏幕那一面向上为Z轴,向右为x,垂直于XZ面的即为Y轴
具体步骤如下:
1.注册SensorManager,获取当前设备的传感器列表,查看是否含有加速度传感器和磁场强度传感器。如果确认设备中一定含有加速度传感器和磁场强度传感器可以不获取sensorList。
SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor sensor : sensorList) {
Log.w(TAG, "SensorHelper: ===" + sensor.getStringType());
}
2.获取地磁传感器和加速度传感器并订阅监听,并在onDestroy()中取消监听。
//地磁感应器
Sensor magneticSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
//加速度感应器
Sensor accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, magneticSensor, SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(this, accelerometerSensor, SensorManager.SENSOR_DELAY_GAME);
@Override
protected void onDestroy() {
super.onDestroy();
if (sensorManager != null) {
sensorManager.unregisterListener(this);
}
}
3.自定义监听实现接口:SensorEventListener。在onSensorChanged中增加了低通滤波,可以降低抖动。
@Override
public void onSensorChanged(SensorEvent event) {
final float alpha = 0.97f;
// 判断当前是加速度感应器还是地磁感应器
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
//赋值调用clone方法
// accelerometerValues = event.values.clone();
accelerometerValues[0]=alpha * accelerometerValues[0] + (1 - alpha) * event.values[0];
accelerometerValues[1]=alpha * accelerometerValues[1] + (1 - alpha) * event.values[1];
accelerometerValues[2]=alpha * accelerometerValues[2] + (1 - alpha) * event.values[2];
} else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
//赋值调用clone方法
// magneticValues = event.values.clone();
magneticValues[0]=alpha * magneticValues[0] + (1 - alpha) * event.values[0];
magneticValues[1]=alpha * magneticValues[1] + (1 - alpha) * event.values[1];
magneticValues[2]=alpha * magneticValues[2] + (1 - alpha) * event.values[2];
}
float[] R = new float[9];
float[] I = new float[9];
float[] values = new float[3];
boolean success=SensorManager.getRotationMatrix(R,I,accelerometerValues,magneticValues);
sensorManager.getOrientation(R, values);
//【2.2】根据inclination matrix计算磁仰角,地球表面任一点的地磁场总强度的矢量方向与水平面的夹角。
double mInclination = SensorManager.getInclination(I);
// Log.i(TAG, "onSensorChanged: -----磁场强度:"+henceValue);
// Log.d("Main","values[0] :"+Math.toDegrees(values[0]));
//values[0]的取值范围是-180到180度。
//将计算出的旋转角度取反,用于旋转指南针背景图
//+-180表示正南方向,0度表示正北,-90表示正西,+90表示正东
int mAzimuth = (int) Math.toDegrees(values[0]);
int valueNew,valueNew1;
if (mAzimuth < 0) {
valueNew = mAzimuth + 360;
} else {
valueNew = mAzimuth;
}
float SmoothFactorCompass = 0.95f;
float SmoothThresholdCompass = 10.0f;
if(Math.abs(compassNumber-valueNew)<180){
if(Math.abs(compassNumber-valueNew)>SmoothThresholdCompass){
valueNew1=valueNew;
}else {
valueNew1 = (int) (compassNumber + SmoothFactorCompass * (valueNew - compassNumber));
}
}else{
if (360.0 - Math.abs(valueNew - compassNumber) > SmoothThresholdCompass) {
valueNew1 = valueNew;
}else {
if (compassNumber > valueNew) {
valueNew1 = (int) ((compassNumber + SmoothFactorCompass * ((360 + valueNew - compassNumber) % 360) + 360) % 360);
} else {
valueNew1 = (int) ((compassNumber - SmoothFactorCompass * ((360 - valueNew + compassNumber) % 360) + 360) % 360);
}
}
}
if (Math.abs(compassNumber-valueNew1)>=1 && success) {
// if ( success) {
compassNumber = valueNew1;
pitchNumber= (int) Math.toDegrees(values[1]);//俯仰角
rollNumber= (int) Math.toDegrees(values[2]);//横滚角
elevationNumber= (int) Math.toDegrees(mInclination);
henceValue= (int)((I[3]*R[0]+I[4]*R[3]+I[5]*R[6])*magneticValues[0]+
(I[3]*R[1]+I[4]*R[4]+I[5]*R[7])*magneticValues[1]+
(I[3]*R[2]+I[4]*R[5]+I[5]*R[8])*magneticValues[2]);
Log.i(TAG, "onSensorChanged: magneticValues:"+ Arrays.toString(magneticValues)+"_____accelerometerValues:"+ Arrays.toString(accelerometerValues));
mHandler.removeMessages(MESSAGE_UPDATE_COMPASSNUMBER);
mHandler.sendEmptyMessage(MESSAGE_UPDATE_COMPASSNUMBER);//更新指南针
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Log.i(TAG, "onAccuracyChanged: accuracy0:"+accuracy+"____sensor0:"+sensor.getStringType());
if(sensor.getType()== Sensor.TYPE_MAGNETIC_FIELD){
accuracyValue=accuracy;
sensorType=sensor.getStringType();
Log.i(TAG, "onAccuracyChanged: accuracy:"+accuracy+"____sensor:"+sensor.getStringType());
mHandler.removeMessages(MESSAGE_UPDATE_COMPASSNUMBER_ACCURACY);
mHandler.sendEmptyMessage(MESSAGE_UPDATE_COMPASSNUMBER_ACCURACY);//更新指南针精度
}
}
4. 踩坑介绍:
开发板上默认都是竖屏,陀螺仪(G-Sensor)和地磁传感器(E-compass)的方向设置也是默认竖屏。项目中要求是横屏使用,硬件工程师将地磁传感器横屏摆放,陀螺仪也按照高通推荐设置,但是最后使用时,发现水平状态测出的指南针数据正常。当时开发板竖直过程中,数据明显异常。
最后发现是加速度传感器的x,y轴方向的系统设置问题。设备的陀螺仪1pin对着主板的右上角,开发板的陀螺仪的1pin对着主板的左上角,查看俯仰角和横滚角,发现当前设备的横纵坐标需要逆时针旋转90°才能和原本开发板的方向一致。
有两种方案,方案一,改apk的加速度传感器的角度,原本代码如下:
public void onSensorChanged(SensorEvent event) {
final float alpha = 0.97f;
// 判断当前是加速度感应器还是地磁感应器
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
//赋值调用clone方法
accelerometerValues = event.values.clone();
}
修改后代码如下:
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
//赋值调用clone方法
// accelerometerValues = event.values.clone();
accelerometerValues[0]=event.values[1];
accelerometerValues[1]=-event.values[0];
accelerometerValues[2]= event.values[2];
}
方案2,系统修改x轴和y轴,在/vendor/qcom/proprietary/sensors-see/registry/config/lito目录下,
修改 lito_bmi160_0-2.json 中orient部分的代码如下:
".orient":{
"owner": "sns_bmi160",
"x":{ "type": "str", "ver": "0",
"data": "x"
},
"y":{ "type": "str", "ver": "0",
"data": "y"
},
"z":{ "type": "str", "ver": "0",
"data": "+z"
}
},
最终效果: