移动传感器

Android 平台提供了多种传感器,以便您监控相关运动 设备的实际情况

传感器的可能的架构因传感器类型而异:

  • 重力、线性加速度、旋转矢量、大幅度动作、步进 计数器和步测器传感器基于硬件, 基于软件
  • 加速度计和陀螺仪传感器始终基于硬件。

大多数 Android 设备都配有加速度计,而现在许多设备都配有 陀螺仪。基于软件的传感器的可用性 变量,因为它们通常依赖一个或多个硬件传感器来获取其数据, 数据。根据设备的不同,这些基于软件的传感器可以推导出其 来自加速度计和磁力计或陀螺仪的数据。

移动传感器对于监控设备的移动情况(例如倾斜、摇晃、旋转或 挥杆。这种移动通常反映用户的直接输入内容(例如,用户操控 或用户在游戏中控制球),但这也可以反映 设备所在的物理环境(例如,在驾车时随您一起移动) 您的车上)。在第一种情况下,您要监控相对于设备参照系的运动 或应用的参照系;在第二种情况中,您需要监控 世界的参照系移动传感器本身通常不用于监控 但它们可以与其他传感器(例如地磁场传感器)一起使用, 确定设备相对于世界参照系的位置(如需了解详情,请参阅位置传感器) 信息)。

所有移动传感器都会为每个 SensorEvent 返回传感器值的多维数组。例如,在单个传感器事件期间,加速度计会返回 三个坐标轴的加速度数据,陀螺仪返回旋转速率 三个坐标轴的数据。这些数据值在 float 数组中返回 (values)以及其他SensorEvent 参数。表 1 总结了在 Android 平台上可用的运动传感器。

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

传感器 传感器事件数据 说明 度量单位
TYPE_ACCELEROMETER SensorEvent.values[0] 沿 x 轴的加速力(包括重力)。 米/秒2
SensorEvent.values[1] 沿 y 轴的加速力(包括重力)。
SensorEvent.values[2] 沿 z 轴的加速力(包括重力)。
TYPE_ACCELEROMETER_UNCALIBRATED SensorEvent.values[0] 沿 X 轴测量的加速度,没有任何偏差补偿。 米/秒2
SensorEvent.values[1] 沿 Y 轴测量的加速度,没有任何偏差补偿。
SensorEvent.values[2] 沿 Z 轴测量的加速度,没有任何偏差补偿。
SensorEvent.values[3] 沿 X 轴测量的加速度,并带有估算的偏差补偿。
SensorEvent.values[4] 沿 Y 轴测量的加速度,并带有估算的偏差补偿。
SensorEvent.values[5] 沿 Z 轴测量的加速度,并带有估算的偏差补偿。
TYPE_GRAVITY SensorEvent.values[0] 沿 x 轴的重力。 米/秒2
SensorEvent.values[1] 沿 y 轴的重力。
SensorEvent.values[2] 沿 z 轴的重力。
TYPE_GYROSCOPE SensorEvent.values[0] 绕 x 轴的旋转速率。 弧度/秒
SensorEvent.values[1] 绕 y 轴的旋转速率。
SensorEvent.values[2] 绕 z 轴的旋转速率。
TYPE_GYROSCOPE_UNCALIBRATED SensorEvent.values[0] 绕 x 轴的旋转速率(无漂移补偿)。 弧度/秒
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 轴的加速力(不包括重力)。 米/秒2
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(them/2))。1
TYPE_SIGNIFICANT_MOTION 不适用 不适用 不适用
TYPE_STEP_COUNTER SensorEvent.values[0] 传感器自上次重启以来用户行走的步数 已启用。 步骤
TYPE_STEP_DETECTOR 不适用 不适用 不适用

1 标量部分是可选值。

旋转矢量传感器和重力传感器是最常用的运动传感器 检测和监控。旋转矢量传感器用途特别多样,可用于 各种与运动相关的任务,如检测手势、监控角度变化以及 监控相对屏幕方向变化。例如,如果您 正在开发游戏、增强现实应用、二维或三维罗盘, 或相机防抖应用。在大多数情况下,使用这些传感器比 加速度计和地磁场传感器或方向传感器。

Android 开源项目传感器

Android 开源项目 (AOSP) 提供了三种基于软件的移动传感器:重力传感器 传感器、线性加速度传感器和旋转矢量传感器。这些传感器在 Android 4.0,现在使用设备的陀螺仪(以及其他传感器)来提升稳定性和 性能如果您想试用这些传感器,可以使用 getVendor() 方法和 getVersion() 方法识别它们 (供应商为 Google LLC;版本号为 3)。供应商识别这些传感器 版本号是必填项,因为 Android 系统会将这三个传感器视为次要传感器 传感器。例如,如果设备制造商提供自己的重力传感器,则 AOSP 重力传感器显示为辅助重力传感器。这三个传感器依赖于 陀螺仪:如果设备没有陀螺仪,这些传感器将不会显示并且不会 资源。

使用重力传感器

重力传感器提供三维矢量,指示 重力的方向和大小通常,该传感器用于确定 设备在空间中的相对屏幕方向。以下代码展示了如何 获取默认重力传感器的实例:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);

单位与加速使用的单位相同 传感器 (m/s2),坐标系与 加速度传感器。

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

使用线性加速度计

线性加速度传感器为您提供一个三维矢量, 表示沿每个设备轴的加速度(不包括重力)。您可以使用 该值以执行手势检测。该值还可以作为 使用航位推算的惯性导航系统。以下代码展示了 如何获取默认线性加速度传感器的实例:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);

从概念上讲,该传感器根据以下公式为您提供加速度数据: 关系:

linear acceleration = acceleration - acceleration due to gravity

如果您要获取加速度数据,而不受 重力。例如,您可以使用此传感器了解汽车的行驶速度。线性模型 加速度传感器始终都有一个偏移量,您需要将其移除。最简单的方法是 在应用中构建一个校准步骤。在校准期间,您可以让用户 放在桌子上,然后读取所有三个轴的偏移量。然后,您可以 加速度传感器直接读数的偏移量,以获取实际的线性 加速。

传感器 坐标 system 与加速度传感器使用的方法相同, (m/s2)。

使用旋转矢量传感器

旋转矢量通过角度和 轴上,设备围绕轴(x、y 或 z)旋转了一个角度 个。以下 代码展示了如何获取默认旋转矢量传感器的实例:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);

旋转矢量的三个元素表示如下:

x*sin(θ/2)、y*sin(θ/2)、z*sin(θ/2)

其中,旋转矢量的大小等于 sin(环聊二次调用),旋转矢量的方向为 旋转矢量等于旋转轴的方向。

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

旋转矢量的三个元素等于单元的最后三个分量 四元数 (cos(Twitter)/2, x*sin(账号的) 取值, y*sin(账号的二元数), z*sin(账号的可选)。旋转矢量的元素为 无单位。x 轴、y 轴和 z 轴的定义方式与加速传感器的定义方式相同。参考 坐标系定义为直接正交基础(参见图 1)。此坐标系 具有以下特征:

  • X 定义为矢量积 Y x Z。它与 设备当前位置的地面,并大致指向东。
  • Y 在设备当前位置与地面相切,并指向 地磁 北极。
  • Z 指向天空并与地平面垂直。

有关展示如何使用旋转矢量传感器的示例应用程序,请参阅 RotationVectorDemo.java

使用有效运动传感器

每次检测到有效动作时,有效移动传感器会触发事件,并且 那么它会自行停用有效动作是指可能导致 用户所在位置;例如步行、骑车或坐在行驶的汽车上以下代码展示了 如何获取默认有效移动传感器的实例以及如何注册事件 监听器:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val mSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION)
val triggerEventListener = object : TriggerEventListener() {
    override fun onTrigger(event: TriggerEvent?) {
        // Do work
    }
}
mSensor?.also { sensor ->
    sensorManager.requestTriggerSensor(triggerEventListener, sensor)
}

Java

private SensorManager sensorManager;
private Sensor sensor;
private TriggerEventListener triggerEventListener;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);

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

sensorManager.requestTriggerSensor(triggerEventListener, mSensor);

如需了解详情,请参阅 TriggerEventListener

使用计步器传感器

计步器传感器提供自上次重新启动以来用户行走的步数 当传感器被激活时。计步器的延迟时间较长(最长 10 秒),但延迟时间较长 比步测器传感器的准确度高。

注意 :您必须声明 ACTIVITY_RECOGNITION 权限,以便您的应用在运行 Android 10(API 级别 29)或更高版本。

以下代码展示如何获取默认步骤的实例 计数器传感器:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);

为了节省运行您应用的设备上的电量,您应该使用 JobScheduler 类,用于检索当前值 按特定间隔计时的计步器传感器。虽然不同类型的应用 需要不同的传感器读取间隔,您应将此间隔设为 尽可能长,除非您的应用需要来自传感器的实时数据。

使用步测器传感器

每当用户迈步时,步测器传感器就会触发一个事件。延迟时间 预计不到 2 秒

注意 :您必须声明 ACTIVITY_RECOGNITION 权限,以便您的应用在运行 Android 10(API 级别 29)或更高版本。

以下代码展示如何获取默认步骤的实例 检测器传感器:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);

使用原始数据

以下传感器可为您的应用提供有关线性和 施加在设备上的旋转力。要使用 您需要从环境中滤除各种因素, 例如重力您可能还需要对趋势应用平滑算法 以减少噪声。

使用加速度计

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

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)

Java

private SensorManager sensorManager;
private Sensor sensor;
  ...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

注意 :如果您的应用以 Android 12(API 级别 31)或 这个传感器就是 有速率限制

从概念上讲,加速度传感器确定施加的加速度, 通过测量施加到传感器的力来将传感器应用于设备 (Ad) 本身 (Fs) 定义如下关系:

A_D=-(1/mass)∑F_S

但是,重力始终都会影响测量的加速度, 以下关系:

A_D=-g-(1/mass)∑F_S

因此,当设备放在桌子上(没有加速)时, 加速度计的读数为 g = 9.81 m/s2。同样,当设备进入 因此会以 9.81 米/秒2的速度向地面快速加速 加速度计的读数为 g = 0 m/s2。因此,要衡量 设备的实际加速度,必须消除重力的作用, 加速度计数据。这可以通过应用高通滤波器来实现。相反,低通 过滤器可用于隔离重力以下示例展示了如何 :

Kotlin

override fun onSensorChanged(event: SensorEvent) {
    // 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.

    val alpha: Float = 0.8f

    // 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]
}

Java

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 值进行演示。如果使用此过滤方法 即可选择其他 Alpha 值

加速度计使用标准传感器 坐标 system。在实践中,这意味着当设备处于平铺状态时,以下条件适用 处于自然方向的桌子上:

  • 如果您向左推动设备(因此向右移动),则 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 次 但功耗却比其他移动传感器低。其中一个缺点是 低通和高通滤波器可以消除重力并减少噪音。

使用陀螺仪

陀螺仪测量围绕设备 x、y、 和 z 轴。以下代码展示如何获取默认陀螺仪的实例:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

注意 :如果您的应用以 Android 12(API 级别 31)或 这个传感器就是 有速率限制

传感器的坐标系 与加速度传感器使用的值相同。旋转为正值, 逆时针方向也就是说,观察者 从设备(原点定位的设备)上 x 轴、y 轴或 z 轴上的某个正位置测量 正向旋转。这是 正向旋转的标准数学定义,与 方向传感器使用的旋转角度。

通常,陀螺仪的输出会随着时间的推移而积分,以计算描述 角度随着时间步长的变化。例如:

Kotlin

// Create a constant to convert nanoseconds to seconds.
private val NS2S = 1.0f / 1000000000.0f
private val deltaRotationVector = FloatArray(4) { 0f }
private var timestamp: Float = 0f

override fun onSensorChanged(event: SensorEvent?) {
    // This timestep's delta rotation to be multiplied by the current rotation
    // after computing it from the gyro sample data.
    if (timestamp != 0f && event != null) {
        val dT = (event.timestamp - timestamp) * NS2S
        // Axis of the rotation sample, not normalized yet.
        var axisX: Float = event.values[0]
        var axisY: Float = event.values[1]
        var axisZ: Float = event.values[2]

        // Calculate the angular speed of the sample
        val omegaMagnitude: Float = 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.
        val thetaOverTwo: Float = omegaMagnitude * dT / 2.0f
        val sinThetaOverTwo: Float = sin(thetaOverTwo)
        val cosThetaOverTwo: Float = cos(thetaOverTwo)
        deltaRotationVector[0] = sinThetaOverTwo * axisX
        deltaRotationVector[1] = sinThetaOverTwo * axisY
        deltaRotationVector[2] = sinThetaOverTwo * axisZ
        deltaRotationVector[3] = cosThetaOverTwo
    }
    timestamp = event?.timestamp?.toFloat() ?: 0f
    val deltaRotationMatrix = FloatArray(9) { 0f }
    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;
}

Java

// 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

注意:未校准传感器可提供更多原始结果, 包含一些偏差,但它们的测量结果包含从应用校正到 校准。某些应用可能更喜欢这些未经校准的结果,因为它们更平滑、更流畅 可靠性。例如,如果应用尝试自行进行传感器融合, 引入校准可能会使结果失真。

除了旋转速率外,未经校准的陀螺仪还提供 会绕每个轴漂移以下代码展示了如何获取默认 未校准陀螺仪:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);

更多代码示例

通过 BatchStepSensor 示例进一步演示 如何使用本页介绍的 API。

另请阅读