位移传感器

Android官方文档


Android平台提供了一些传感器让你能监测设备的移动。它们中的两个传感器总是基于硬件的(加速度和陀螺仪),另外的这类这些传感器中的3个即能使用基于硬件的也能使用基于软件的(重力,线性加速度和旋转矢量传感器)。例如,一些设备从基于原件的传感器中获取加速度和磁强的数据,但另外一些设备也用陀螺仪获取这些数据。大多数Android设备都有加速度传感器,很多设备现在也包含陀螺仪传感器。那些基于软件的传感期大多也是可变的,因为它们常常也要依赖于一类或更多类的基于硬件的传感器去获得它们需要的数据。

位移传感器对检测设备的移动(如倾斜、摇动、旋转、摇摆等)是有用的。这些动作通常直接反映了用户的输入(如用户游戏中操控汽车,或是用户在控制游戏中的球),但它也反映了设备所处的物理环境的变化(例如设备随着你正在驾驶的汽车运动)。在第一种情况下,你监控的运动是相对于设备或应用为参考坐标系的;在第二种情况下,所监控的应用是相对于地球为参考坐标系系的。位移传感器本身不是主要用来监控设备的位置的,但是他们结合其他传感器(如地磁传感器),可用于检测设备相对于地球参考系的位置。

所有的位移传感器都会在SensorEvent中返回用多维数组表示的传感数据。如,在一个加速度传感器事件中,会返回三维坐标轴上的加速度数据,陀螺仪会返回三维坐标轴上的旋转速率数据。 这些数据都是随着SensorEvent参数中以 float 数组的方式返回的。表1总结了Android平台中可用的位移传感器:

表1. Android平台支持的位移传感器。

SensorSensor event数据描述计量单位
TYPE_ACCELEROMETERSensorEvent.values[0]沿X轴的加速度值(包括重力加速度)。m/s2
SensorEvent.values[1]沿Y轴的加速度值(包括重力加速度)。
SensorEvent.values[2]沿Z轴的加速度值(包括重力加速度)。
TYPE_GRAVITYSensorEvent.values[0]沿X轴的重力加速度值。m/s2
SensorEvent.values[1]沿Y轴的重力加速度值。
SensorEvent.values[2]沿Z轴的重力加速度值。
TYPE_GYROSCOPESensorEvent.values[0]围绕X轴的旋转角速度。rad/s
SensorEvent.values[1]围绕Y轴的旋转角速度
SensorEvent.values[2]围绕Z轴的旋转角速度
TYPE_GYROSCOPE_UNCALIBRATEDSensorEvent.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_ACCELERATIONSensorEvent.values[0]沿X轴的加速度值(不包括重力加速度)。m/s2
SensorEvent.values[1]沿Y轴的加速度值(不包括重力加速度)。
SensorEvent.values[2]沿Z轴的加速度值(不包括重力加速度)。
TYPE_ROTATION_VECTORSensorEvent.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)1
TYPE_SIGNIFICANT_MOTIONN/AN/AN/A
TYPE_STEP_COUNTERSensorEvent.values[0]自从上次传感器被重启激活后携带设备的用户的所走的步数.
TYPE_STEP_DETECTORN/AN/AN/A

1度量元素是可选值。

旋转矢量和重力传感器是最常用的运动检测传感器。在相关运动的任务中,如检测收拾、角度变化、相对方位的变化等信息,旋转矢量加速度是是尤其强大的。例如,如果你正在开发游戏、AR应用、二维或三维罗盘、相机防抖应用等,旋转矢量传感器对你来说是非常理想的。在大多数情况下,用这些传感器是比选择使用加速度和地磁场传感器或是方向传感器更理想。

Android开源项目传感器


Android 开源项目(AOSP)提供了三种基于软件的位移传感器:重力、线性加速度和旋转矢量传感器。 Android 4.0 中对这些传感器进行了升级,目前利用陀螺仪来提高稳定性和性能。 如果你想尝试这些传感器,你可以用 getVendor() 和 getVersion() 方法来识别它们(制造商为 Google 公司;版本号为3)。通过vendor和version识别这些传感器是必须的,因为Android系统把这三种传感器作为备选传感器。例如,如果设备厂商提供了它们自己的的重力传感器,那么AOSP重力传感器则会显示它们为备选的重力传感器。所有这三个传感器都依赖于陀螺仪:如果设备上没有提供陀螺仪,这些传感器都不会显示出来,用户也无法使用。

加速度传感器的使用


加速度传感器测量设备的加速度,包括重力加速度。以下代码展示了如何获取默认加速度传感器的实例:

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

从概念上来说,加速度传感器通过测量施于传感器上的作用力,并按以下关系来检测设备的加速度(Ad)。
加速度传感器通过测量施加于传感器自身的作用力(Fs)来检测到设备加速度(Ad),公式如下:
Ad= - ∑Fs/ mass

然而,重力总是会影响到测量的精度,其公式如下:
Ad= -g - ∑F / mass

基于这个理由,如果设备是平放在桌子上的(没有加速度),加速度计会读到g = 9.81 m/s2。 同理,设备在自由落体或以 9.81 m/s2 的加速度坠向地面时,加速度计会读到 g = 0 m/s2。 因此,要测量设备的真实加速度,必须从加速度数据中移除掉重力加速度的贡献。可以通过高通过滤器实现它。 反之,低通过滤器则可以用于分离出重力加速度值。下面的例子展示了如果做:

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)来创建一个低通过滤器。 这个过滤器常量是由时间常量(t)(过滤器事件间隔的粗略表示)和传感器事件传送频率(dt)推导出来的。 使用的0.8仅仅是为了示例的演示,如果你要用这个过滤方法,你可能需要选用另外的 alpha 值。

加速度传感器使用标准的传感器坐标系统,事实上,这意味着当设备以自然方向平放在桌子上时,将会适用以下情况:

  • 如果从左侧平推设备(向右移动),X轴方向加速度为正值。
  • 如果从下侧平推设备(远离你的方向移动),Y轴方向加速度为正值。
  • 如果以 A m/s2的加速度向空中移动设备,Z方向加速度等于 A + 9.81,即设备加速度(+A m/s2)减去重力加速度(-9.81 m/s2)。
  • 静止设备的加速度值为 +9.81,即设备加速度(0 m/s2)减去重力加速度(-9.81 m/s2)。

总之,加速度传感器已足以检测设备的移动。几乎所有 Android 手机和平板都带有加速度传感器,它的能耗比其它运动传感器要少10倍。 不过缺点是,你不得不实现低通和高通过滤器去消除重力加速度的影响和减噪。

Android SDK 提供了一个应用示例,展示如何使用加速度传感器( Accelerometer Play )

重力加速度传感器的使用


重力传感器提供了方向和数值上三维矢量。下面的代码展示了如何获取默认的重力传感器的实例:

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

和加速度传感器具有相同的单位(m/s2)和坐标系。

Note: When a device is at rest, the output of the gravity sensor should be identical to that of the accelerometer.

注意: 当设备静止时,重力传感器的输出应该与加速度传感器相同。

陀螺仪的使用


陀螺仪以rad/s为单位测量设备围绕 X, Y, Z轴的旋转速度。下面的代码展示了如何获取默认陀螺仪的实例:

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) {
  // This timestep's delta rotation to be multiplied by the current rotation
  // after computing it from the gyro sample data.
  if (timestamp != 0) {
    final float dT = (event.timestamp - timestamp) * NS2S;
    // Axis of the rotation sample, not normalized yet.
    float axisX = event.values[0];
    float axisY = event.values[1];
    float axisZ = event.values[2];

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

    // Normalize the rotation 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;
    }

    // Integrate around this axis with the angular speed by the timestep
    // in order to get a delta rotation from this sample over the timestep
    // We will convert this axis-angle representation of the delta rotation
    // into a quaternion 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 code should concatenate the delta rotation we computed with the current rotation
    // in order to get the updated rotation.
    // rotationCurrent = rotationCurrent * deltaRotationMatrix;
   }
}

标准的陀螺仪能够提供未经过滤的原始旋转数据,或是经过噪声及漂移修正的数据。 实际上,噪点和漂移会带来误差,这些需要进行相应的补偿。 通常你要利用其它传感器来确定漂移和噪点值,如重力传感器或加速计。

未校准陀螺仪的使用


除了旋转速率没有使用陀螺仪漂移补偿外,未校准的陀螺仪和陀螺仪是相似的。工厂校准和温度补偿仍然应用到旋转速率中。对于后处理和数据融合定位,未校准陀螺仪是有用的 。通常,gyroscope_event.values[0] 的值接近于uncalibrated_gyroscope_event.values[0] - uncalibrated_gyroscope_event.values[3],也就是说:

calibrated_x ~= uncalibrated_x - bias_estimate_x

注意:未校准传感器提供更多的原始结果,这些值可能包含一些偏差,但是通过校准,他们的测量结果包含更少的跳变。为了数据的更平滑和更加可靠,一些应用可能更喜欢这些未校准的结果。例如,如果应用使用它自己的传感器融合,使用校准的结果可能会歪曲结果。

除了旋转速率,未校准的陀螺仪也提供每个轴的漂移估值。下面的代码展示了如何获得一个默认未校准陀螺仪的实例:

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);

概念上,这种传感器根据下列关系提供加速度数据:

线性加速度 = 加速度 - 重力加速度

这种传感器的典型用法是你想获得不受重力加速度影响的加速度数据。
例如,你能够使用这种传感器来测量你的汽车运行速度。线性加速度传感器始终有一个偏移量,你需要移除掉这个偏移量。最简单的方法就是在你的应用程序中设立一个校准步骤。在校准期间,你可以要求用户把设备放到桌面上,读取所有三轴的偏移量,然后从加速度传感器中读取的数据中直接减去对应的偏移量,从而获取真实的线性加速度。

该传感器的坐标系与加速度传感器的坐标系相同,其测量单位是(m/s2)。

旋转矢量传感器的使用


旋转矢量用角和轴的组合来代表设备的方向,即设备绕着轴(X、Y或Z)旋转角度θ。下列代码显示了如何获取默认旋转矢量传感器的一个实例:

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)

其中sin(θ/2)是旋转矢量的数据,旋转矢量的方向等于旋转轴的方向。

这三个旋转矢量元素等于一个四元数(cos(θ/2),X*sin(θ/2),Y*sin(θ/2),Z*sin(θ/2)的最后三部分。旋转矢量元素是无单位的。X、Y和Z轴的定义与加速度传感器相同。该参照坐标系统是用直接正交基来定义的(如图1)。该坐标系有下面几个元素:

这里写图片描述

图1. 旋转矢量传感器的坐标系

  • X轴是用Y x Z的矢量积定义的,它在设备的当前位置与地面相切,并指向东方;
  • Y轴在设备当前位置与地面相切,并指向地磁场的北极。
  • Z指向天空,并与地面垂直。

Android SDK有一个sample应用,该应用展示了如何使用旋转矢量传感器,该sample位于API Demos code ( OS - RotationVectorDemo)。

场景转移传感器


每次场景转移被检测到后,场景转移传感器会触发一个事件,接着它会禁止掉它自己。场景转移运动是可能会导致用户位置改变的一个运动;例如走路,骑自行车,或坐在一个移动的汽车中。下面的代码展示了如何获取默认场景转移传感器的实例和怎样注册一个时间listener:

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);

更多的信息,参阅TriggerEventListener

步伐计数传感器的使用


步伐计数传感器返回自开机以来活动状态下用户的总的运动步数。步计数有更多的延迟(高达10秒),但是比步检测传感器更精确。下面的代码展示了如何获得默认的步伐计数传感器的实例:

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

步伐检测传感器的使用


步伐检测传感器在用户每走一步时都会触发一个事件。其延迟理想值在2秒已下。下面的代码展示了如何获得默认的步伐检测传感器的实例:

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