Android 传感器 II-运动传感器

概述:

Android平台提供了一些传感器让咱们能够监测设备的运动状况. 其中的两种老是基于硬件的(加速度计和陀螺仪), 其中的三个是能软能硬的(重力计, 线性加速计和旋转矢量传感器). 好比, 在一些设备上基于软件的传感器会从加速度计和磁力计来计算它们的数据, 可是在其它设备上它们可能也使用陀螺仪来做为它们的数据来源. 大多数Android设备都拥有一个加速度计, 不少都已经包含陀螺仪. 基于软件的传感器的可用性更加多样, 由于它们一般依赖于一个或一个以上的硬件传感器来做为数据来源. html

运动传感器顾名思义是用于监测设备移动的, 好比倾斜, 摇动, 旋转或摆动等. 这种运动一般来源于用户的输入(栗如, 用户在汽车类的游戏中转动设备或者控制球类游戏等), 可是它也能够来自于设备自己的位置(好比设备跟着汽车一块儿运动). 第一个栗子中, 监测的是相对于设备做为参考框架. 第二个例子中则是相对于全世界作参考框架. 运动传感器自己一般不用来监测设备位置, 可是能够同其它传感器一块儿使用, 好比地磁传感器, 来肯定设备相对于世界中的位置. android

全部的运动传感器都会在SensorEvent中返回传感器值的多维数组. 好比, 在一个传感器事件中, 加速计会为坐标系的三个方向返回加速度数据, 陀螺仪返回三维旋转速率数据. 这些数据值以float数组格式同其它SensorEvent参数一块儿返回. 下表总结了Android平台上可用的运动传感器:数组

传感器框架

SensorEvent数据ide

描述性能

测量单位ui

TYPE_ACCELEROMETERthis

SensorEvent.values[0]spa

沿x轴的加速度(包括重力)code

m/s2(平方)

SensorEvent.values[1]

沿y轴的加速度(包括重力)

SensorEvent.values[2]

沿z轴的加速度(包括重力)

TYPE_GRAVITY

SensorEvent.values[0]

沿x轴的重力

m/s2

SensorEvent.values[1]

沿y轴的重力

SensorEvent.values[2]

沿z轴的重力

TYPE_GYROSCOPE

SensorEvent.values[0]

绕x轴旋转的速率

rad/s

SensorEvent.values[1]

绕y轴旋转的速率

SensorEvent.values[2]

绕z轴旋转的速率

TYPE_GYROSCOPE_UNCALIBRATED

SensorEvent.values[0]

绕x轴的旋转速率(无偏移补偿)

rad/s

SensorEvent.values[1]

绕y轴的旋转速率(无偏移补偿)

SensorEvent.values[2]

绕z轴的旋转速率(无偏移补偿)

SensorEvent.values[3]

绕x轴的预计偏移

SensorEvent.values[4]

绕y轴的预计偏移

SensorEvent.values[5]

绕z轴的预计偏移

TYPE_LINEAR_ACCELERATION

SensorEvent.values[0]

沿x轴的加速度(不包括重力)

m/s2

SensorEvent.values[1]

沿y轴的加速度(不包括重力)

SensorEvent.values[2]

沿z轴的加速度(不包括重力)

TYPE_ROTATION_VECTOR

SensorEvent.values[0]

沿x轴旋转矢量份量(x*sin(θ/2)).

无单位

SensorEvent.values[1]

沿y轴旋转矢量份量(y*sin(θ/2)).

SensorEvent.values[2]

沿z轴旋转矢量份量(z*sin(θ/2)).

SensorEvent.values[3]

旋转向量的标量份量((cos(θ/2)).(标量份量是可选的)

TYPE_SIGNIFICANT_MOTION

N/A

N/A

N/A

TYPE_STEP_COUNTER

SensorEvent.values[0]

从传感器激活那一刻起的步数

步数

TYPE_STEP_DETECTOR

N/A

N/A

N/A

旋转矢量传感器和重力传感器是运动检测中使用频率最高的传感器. 旋转矢量传感器是多功能的, 能够普遍的用于运动相关的任务, 好比检测手势, 检测角度改变, 检测相关的方向变化. 在开发游戏的时候常常用到旋转矢量传感器, 还有加强现实的应用, 二维或者三维的罗盘, 或者一个相机稳定的APP. 在大多数的状况下, 使用这些传感器会比用加速度计和磁力计或者方向传感器更好.

Android开源工程传感器:

Android Open Source Project(AOSP)提供了三种基于软件的运动传感器: 一个重力传感器, 一个线性加速度传感器, 还有一个旋转矢量传感器. 这些传感器在Android 4.0中添加, 如今使用设备的陀螺仪来提升稳定性和性能. 若是想要尝试一下这些传感器, 可使用getVendor()和getVersion()方法(vendor是Google公司, 版本号是3). 使用vendor和version来识别这些传感器是必要的, 由于Android将这三个传感器做为辅助传感器. 好比若是一个设备供应商提供了它们的重力传感器, 那么AOSP重力传感器将会做为一个辅助备用的传感器来显示. 全部的这三个传感器都依赖于陀螺仪: 若是设备没有陀螺仪, 这些传感器将不会显示也不能用.

使用加速度计:

加速度传感器用来测量设备的加速度, 包括重力. 下面的代码演示了如何获取一个加速度传感器的实例:

private SensorManager mSensorManager;
private Sensor mSensor;
  ...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

理论上讲, 一个加速度传感器肯定加速度经过测量应用在传感器自己的力(Fs)来计算出应用在设备上的力(Ad), 而后使用这个公式一套:

Ad = - ∑Fs / mass

然而根据这个公式重力会老是影响到测量的结果:

Ad = -g - ∑F / mass

根据这个缘由, 当设备放在桌子上(没有被加速), 加速度传感器会读取到一个值为g=9.81m/s2. 一样, 当设备在自由落体的时候, 加速度读取到的值为g=0m/s2. 所以想要测量到真实的加速度, 必须将重力移除. 这能够经过一个high-pass过滤器实现. 相反, 一个low-pass过滤器能够被用来隔离出重力. 栗子:

public void onSensorChanged(SensorEvent event){
  // In this example, alpha is calculated as t / (t + dT),
  // where t is the low-pass filter's time-constant and
  // dT is the event delivery rate.

  final float alpha = 0.8;

  // Isolate the force of gravity with the low-pass filter.
  gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
  gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
  gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];

  // Remove the gravity contribution with the high-pass filter.
  linear_acceleration[0] = event.values[0] - gravity[0];
  linear_acceleration[1] = event.values[1] - gravity[1];
  linear_acceleration[2] = event.values[2] - gravity[2];
}

注意: 咱们可使用不少不一样的技术来过滤传感器数据. 代码中的栗子使用的是一个简单的过滤器常量(alpha)来建立一个low-pass过滤器. 过滤器常量是从一个时间常量衍生的(t). 若是想要使用该方法, 那么可能须要更换alpha.

加速度计使用标准的传感器坐标系统. 在实践中, 这意味着当设备被放在水平的桌子上并处于天然方向的时候下面的条件适用:

l  若是将设备往右边推, x加速度值为正.

l  若是从设备底部推(这样设备会远离你), y加速度为正.

l  若是将设备往天上推, 而且加速度为Am/s2,z加速度将会等于A+9.81, 也就是设备加速度加剧力加速度.

l  静止的设备拥有一个等于9.81的加速度.

一般若是想要监测设备的运动的话, 加速度计是一个很好的传感器. 几乎每一个Android的设备都带有一个加速度计, 它比其它的传感器消耗更少的能量(10倍). 缺陷则是咱们得本身实现对重力的过滤和降噪.

使用重力传感器:

重力传感器提供了三维矢量来描述重力的方向和大小. 下面代码可让咱们取得重力传感器的实例:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);

单位和坐标系跟加速度计同样. 设备静止的时候, 重力传感器和加速度传感器输出的结果应该相同.

陀螺仪:

陀螺仪测量的是设备x,y,z轴的旋转速度, 单位是rad/s. 一样, 获取陀螺仪的实例代码以下:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

陀螺仪的坐标系跟加速度计同样. 逆时针旋转为正; 这意味着若是观察者从x, y, 或者z轴的正方向看过去, 设备逆时针旋转, 则为正向. 这是正方向的标准定义, 不一样于方向传感器的定义. 一般陀螺仪的输出是整数, 随着时间的推移按照时间戳计算旋转的度数, 栗子:

// Create a constant to convert nanoseconds to seconds.
private static final float NS2S = 1.0f / 1000000000.0f;
private final float[] deltaRotationVector = new float[4]();
private float timestamp;

public void onSensorChanged(SensorEvent event) {
  // Thistimestep's delta rotation to be multiplied by the current rotation
  // aftercomputing it from the gyro sample data.
  if (timestamp != 0) {
    final float dT = (event.timestamp - timestamp) * NS2S;
    // Axis of therotation sample, not normalized yet.
    float axisX = event.values[0];
    float axisY = event.values[1];
    float axisZ = event.values[2];

    // Calculate theangular speed of the sample
    float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);

    // Normalize therotation vector if it's big enough to get the axis
    // (that is,EPSILON should represent your maximum allowable margin of error)
    if (omegaMagnitude > EPSILON) {
      axisX /= omegaMagnitude;
      axisY /= omegaMagnitude;
      axisZ /= omegaMagnitude;
    }

    // Integratearound this axis with the angular speed by the timestep
    // in order toget a delta rotation from this sample over the timestep
    // We willconvert this axis-angle representation of the delta rotation
    // into aquaternion before turning it into the rotation matrix.
    float thetaOverTwo = omegaMagnitude * dT / 2.0f;
    float sinThetaOverTwo = sin(thetaOverTwo);
    float cosThetaOverTwo = cos(thetaOverTwo);
    deltaRotationVector[0] = sinThetaOverTwo * axisX;
    deltaRotationVector[1] = sinThetaOverTwo * axisY;
    deltaRotationVector[2] = sinThetaOverTwo * axisZ;
    deltaRotationVector[3] = cosThetaOverTwo;
  }
  timestamp = event.timestamp;
  float[] deltaRotationMatrix = new float[9];
  SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    // User codeshould concatenate the delta rotation we computed with the current rotation
    // in order toget the updated rotation.
    //rotationCurrent = rotationCurrent * deltaRotationMatrix;
   }
}
 

标准陀螺仪提供原始旋转数据而不经任何过滤, 也不去纠正噪音和误差. 实践中, 陀螺仪噪音和误差将会引起错误, 因此须要纠正它们. 咱们常常检查经过检查其它的传感器来计算误差和噪音, 好比重力传感器或者加速度计.

使用未校准陀螺仪:

为校准的陀螺仪跟陀螺仪类似, 不一样的是它没有对旋转速率进行陀螺偏移补偿. 工厂校准和温度补偿依然应用于旋转速率. 为校准陀螺仪对后期处理和融合方向数据颇有用. 一般, gyroscope_event.value[0]将会跟uncalibrated_gyroscope_event.values[0]- uncalibrated_gyroscope_event.values[3]接近. 就是,

calibrated_x ~= uncalibrated_x -bias_estimate_x

注意: 为校准陀螺仪提供了更多的原始数据并存在一些误差, 可是它们的测量经过校准能够包含更少的跳变. 一些APP可能更喜欢这些为校准的结果, 由于更加的平滑和可靠. 栗如, 若是一个APP尝试实现本身的传感器合成, 引入校准实际上会扭曲结果.

除了旋转速率, 为校准陀螺仪还为各轴提供了预计的误差. 下面的代码演示了如何获取一个陀螺仪实例:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);

使用线性加速计:

线性加速传感器提供了一个表明加速度的三维矢量, 可是不包含重力. 下面代码演示了如何获取一个它的实例:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);

从概念上讲, 该传感器提供的加速数据是根据这个公式:

linear acceleration = acceleration - acceleration due to gravity

咱们一般在想要获取不包含重力的加速数据的时候使用这个传感器. 栗如, 可使用这个传感器来看汽车行驶的多快. 线性加速传感器老是有一个偏移, 咱们应该移除这个偏移. 最简单的实现这个操做的方法是在APP中建立一个校准步骤. 校准过程当中咱们能够请求用户将设备放置于一个桌子上, 而后读取三个轴的偏移. 而后减去这个偏移来得到真实的线性加速度. 它的坐标系和加速度传感器同样, 单位也是m/s2.

使用旋转矢量传感器:

旋转矢量(向量)用角度和数值的组合表示设备的方向, 设备绕轴旋转的角度为θ. 下面的代码展现了如何获取默认的旋转矢量传感器:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);

旋转矢量的三个元素表达以下:

x*sin(θ/2)

y*sin(θ/2)

z*sin(θ/2)

其中旋转矢量的大小为sin(θ/2), 方向为各轴的方向.


旋转矢量传感器使用的坐标器. 这个球是地球. 

旋转矢量的三个元素等于一个四元数的最后三个组成部分(cos(θ/2), x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)). 旋转矢量元素是无单位的. X,y和z轴的定义跟加速度计同样. 坐标系定义为一个标准正交基(上图). 该坐标系统有下面三个特性:

X定义为矢量积Y乘以Z. 它在设备当前所处的位置与地面相切, 并指向东边.

Y也在设备当前的位置与地面相切, 并指向地磁北极.

Z则朝向天空并垂直于地面.

Android sample中包含使用旋转矢量传感器的栗子.

使用重要(significant)运动传感器:

每次检测到重要运动的时候出发传感器而后关闭本身. 一个重要运动是指能够致使用户坐标改变的运动. 栗如, 走路, 骑自行车, 或者坐在一辆运行的汽车中. 下面的代码演示了如何得到一个该传感器的实例, 还有如何注册一个事件监听器:

private SensorManager mSensorManager;
private Sensor mSensor;
private TriggerEventListener mTriggerEventListener;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);

mTriggerEventListener = new TriggerEventListener() {
    @Override
    public void onTrigger(TriggerEvent event) {
        // Do work
    }
};

mSensorManager.requestTriggerSensor(mTriggerEventListener, mSensor);

使用步数传感器(stepcounter sensor):

步数传感器能够从上一次重启以后(步数传感器被激活)用户走过的总步数. 步数传感器有更多的延迟(最多能够达到10s)可是比步检测传感器(step detector sensor, 下面介绍)更加精确. 栗子:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);

使用步检测传感器(stepdetector sensor):

步检测传感器当每次用户迈出一步的时候触发一次. 延迟一般低于2s. 栗子:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);

 

参考: https://developer.android.com/guide/topics/sensors/sensors_motion.html