Android 传感器学习(一)——指南针的实现

Always make sure to disable sensors you don't need, especially when your activity is paused. Failing to do so can drain the battery in just a few hours. Note that the system will not disable sensors automatically when the screen turns off.
确保在你不需要使用传感器的时候关掉它,特别是在 Activity 暂停的时候。如若不然,几个小时就会耗尽电池电量。请注意,系统在关闭屏幕的时候是不会自动停止传感器的。
Google APIs -- SensorManager

简介

智能手机经过近十年的发展,机身上集成了多种传感器(Sensor),一些传感器是基于硬件直接得出结果,另一些则是通过软件复合运算得出的。常见的重力计、陀螺仪以及距离感应计都属于硬件传感器,而方向计则是通过复合计算得出结果,属于软件传感器。

Sensor Type Description Common Uses
TYPE_ACCELEROMETER 硬件 测量设备在三个物理轴(x,y,z)上的加速度(m/s²),包括重力 运动检测(摇动,倾斜等)
TYPE_AMBIENT_TEMPERATURE 硬件 测量设备周围空间的摄氏温度(℃) 监测空气温度
TYPE_GRAVITY 硬件或软件 测量施加在设备三个物理轴向(x,y,z)的重力加速度(m/s²) 运动检测(摇动,倾斜等)
TYPE_GYROSCOPE 硬件 测量设备围绕三个物理轴(x,y,z)的旋转速率(rad/s) 旋转检测(旋转,转动等)
TYPE_LIGHT 硬件 测量环境光亮度等级流明(lx) 调节屏幕亮度
TYPE_LINEAR_ACCELERATION 硬件或软件 测量设备在三个物理轴(x,y,z)上的加速度(m/s²),包括重力 检测单个轴的加速度
TYPE_MAGNETIC_FIELD 硬件 检测沿三个物理轴(x,y,z)的磁场强度μT 创建指南针
TYPE_ORIENTATION 软件 测量设备围绕三个物理轴(x,y,z)的旋转度。API 等级大于3的可以通过调用重力计和磁场计获取倾斜矩阵和旋转矩阵,再结合 getRotationMatrix() 方法获取方向矩阵 确定设备位置
TYPE_PRESSURE 硬件 测量周围的空气压力(hPa or mbar) 检测气压变化
TYPE_PROXIMITY 硬件 测量一个物件到屏幕的距离(cm).该传感器通常用于确定手机是否靠近人耳 通话时的设备位置
TYPE_RELATIVE_HUMIDITY 硬件 测量周围的湿度(%) 检测露点,绝对和相对湿度
TYPE_ROTATION_VECTOR 软件或硬件 通过提供设备的三个旋转矢量元素确定设备的方向 运动和旋转检测

调用

获取传感器

要使用传感器,需要先获取手机的传感器服务。通过getSystemService创建一个SensorManager实例:

private SensorManager mSensorManager;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

接着,可以传入TYPE_ALL参数获取传感器列表:

List<Sensor> deviceSensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);

如果你想获取指定类型的传感器列表,可以通过传入其他的类型常量,如TYPE_GYROSCOPE或者TYPE_GRAVITY来取代TYPE_ALL.
所有支持的传感器,并不会在所有的手机上都存在,类似于气压计这样的传感器,目前也只在高端机型上存在。所以在调取传感器的时候,需要对其做代码检查,例如

private SensorManager mSensorManager;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if(mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){
  //Success!存在磁力计
}else{
  //Failure!不存在磁力计
}

监控传感器事件

要监控传感器事件,必须实现SensorEventListener接口的两个回调方法:onAccuracyChanged()onSensorChanged().

A sensor's accuracy changes.

传感器的精度变化会调用onAccuracyChanged()方法。精度有四个常量:SENSOR_STATUS_ACCURACY_LOW,SENSOR_STATUS_ACCURACY_MEDIUM,SENSOR_STATUS_ACCURACY_HIGHSENSOR_STATUS_UNRELIABLE

A sensor reports a new value.

传感器返回新的数据时候会调用onSensorChanged()方法,提供SensorEvent对象,该对象包含传感器的数据,包含数据精度,传感器,数据生成时间和传感器生成的数据。

接下来将通过一个指南针的例子来介绍传感器事件监听的运行机制。通过对 API 的查询了解,SENSOR_ORIENTATION已经在 API level 8废弃了,想要通过getDefaultSensor()方法直接获取方向参数已经不可取。官方给出的替代方法是一个静态(static)方法getOrientation(float[] R, float[] values).

getOrientation

基于旋转矩阵获取设备的方向。

请求参数
R float:旋转矩阵,通过getRotationMatrix(float[], float[], float[], float[])方法获取
values float:包含三个float的数组
返回参数
float[] 返回的数组

getRotationMatrix

getRotationMatrix(float[] R, float[] I, float[] gravity, float[] geomagnetic)
计算倾斜矩阵I和旋转矩阵R并将其通过矢量变换从设备的坐标系转换成真实世界正交坐标系:

  • X 定义为Y·Z的向量积(在设备所在的位置和地面相切,并大致指向东方)。
  • Y 在设备所在的位置与地面相切,并指向磁北极。
  • Z 指向天空并垂直于地面。


    正交坐标系

    其中

请求参数
R float:9个float参数的数组,保存旋转矩阵R的数据。R可以为空
I float:9个float参数的数组,保存倾斜矩阵I的数据。I可以为空
gravity float:3个float参数的数组,包含基于设备坐标系的重力矢量。使用类型为TYPE_ACCELEROMETER的传感器返回的数据
geomagnetic float:3个float参数的数组,包含基于设备坐标系的磁场矢量。使用类型为TYPE_MAGNETIC_FIELD的传感器返回的数据
返回参数
boolean true表示成功,false表示失败(当设备处于自由落体状态)。自由落体的界定,当设备承受的重力小于标称值的1/10时,视为自由落体。失败时,输出的矩阵值不会修改

构建一个指南针

在现实世界的正交坐标系中,磁北极是固定方向的。想要构造一个指南针应用,最直观的思考是获取到设备朝向和所处位置磁北极方向的夹角。这个时候,直观的思考就是正确的思考,如何获取这样一个夹角呢。通过上面介绍的 API 来看,getRotationMatrix()方法,能够获得设备的旋转矩阵和倾斜矩阵。旋转矩阵是一个33或者44的矩阵,通过getOrientation()方法就能直接获得设备关于正交系的三轴旋转数据。设备的指向必然是设备关于z轴的旋转角度。如何取值可以参考源码或者文档:

  • values[0]: Azimuth, angle of rotation about the -z axis. This value represents the angle between the device's y axis and the magnetic north pole. When facing north, this angle is 0, when facing south, this angle is π. Likewise, when facing east, this angle is π/2, and when facing west, this angle is -π/2. The range of values is -π to π.
  • values[1]: Pitch, angle of rotation about the x axis. This value represents the angle between a plane parallel to the device's screen and a plane parallel to the ground. Assuming that the bottom edge of the device faces the user and that the screen is face-up, tilting the top edge of the device toward the ground creates a positive pitch angle. The range of values is -π to π.
  • values[2]: Roll, angle of rotation about the y axis. This value represents the angle between a plane perpendicular to the device's screen and a plane perpendicular to the ground. Assuming that the bottom edge of the device faces the user and that the screen is face-up, tilting the left edge of the device toward the ground creates a positive roll angle. The range of values is -π/2 to π/2.

可知,此时此刻的values[0]正是我们想要的值。回归到代码的世界,是这样的:

public class SensorActivity extends Activity implements SensorEventListener{
    //sensor object
    private SensorManager mSensorManager;
    private Sensor mAccelerometer;
    private Sensor mMagneticField;
    //sensor data
    private float[] r = new float[9];   //rotation matrix
    private float[] values = new float[3]   //orientation values
    private float[] accelerometerValues = new float[3]  //data of acclerometer sensor
    private float[] magneticFieldValues = new fooat[3]  //data of magnetic field sensor

    @Ovrride
    protected void onCreate(Bundle savedInstanceState) {
        ...
        //init sensor
        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
            mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            mMagneticField = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        ...
    }

    
        @Override
        protected void onResume() {
            super.onResume();
        //regist listener
            mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
            mSensorManager.registerListener(this, mMagneticField, SensorManager.SENSOR_DELAY_NORMAL);
            ...
        }

    @Override
        protected void onPause() {
            super.onPause();
        //unregist listener
            mSensorManager.unregisterListener(this);
        }

    @Override
        public void onSensorChanged(SensorEvent sensorEvent) {
            if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {    
                accelerometerValues = sensorEvent.values;
            }
            if (sensorEvent.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { 
                magneticFieldValues = sensorEvent.values;
            }
            SensorManager.getRotationMatrix(r, null, accelerometerValues, magneticFieldValues);
            SensorManager.getOrientation(r, values);
            values[0] = (float) Math.toDegrees(values[0]);
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int i) {

        }
}

整个过程是很流畅的,由于传感器本身是不受Activity的生命周期控制,并且系统并不会在关闭屏幕时关闭传感器,所以,请注意手动控制好传感器的开闭,以节省电量。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容