位置传感器

Android 平台提供了两个传感器,可用于确定 设备:地磁场传感器和加速度计。Android 平台还提供一款传感器,可让您确定 设备与物体接触(称为近程传感器)。通过 地磁场传感器和近程传感器均基于硬件。大多数人 手机和平板电脑制造商配备了地磁场传感器。同样, 手机制造商通常包含一个近程传感器,用于确定 手机靠近用户的脸部(例如,在手机屏幕上) 调用)。要确定设备的屏幕方向,您可以使用 设备的加速度计和地磁场传感器。

注意:Android 2.2 中已废弃方向传感器 (API 级别 8),并且 Android 4.4W 中已废弃屏幕方向传感器类型 (API 级别 20)。

位置传感器对于确定设备在 世界的参照系例如,您可以使用地磁场 传感器结合加速度计,以确定设备的位置 相对于磁性北极您还可以使用这些传感器 在应用的参照系中确定设备的屏幕方向。 位置传感器通常不用于监控设备的移动情况, 例如摇晃、倾斜或推力(有关详情,请参阅移动传感器)。

地磁场传感器和加速度计返回多维数组 每个 SensorEvent 的传感器值。例如: 地磁场传感器可提供 三个坐标轴中的每个坐标轴。同样, 加速度计传感器测量在运动期间施加到设备的加速度 传感器事件。如需详细了解所使用的坐标系 传感器,请参见 传感器坐标系。近程传感器提供单个值 每个传感器事件触发表 1 总结了 Android 平台支持这一功能。

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

传感器 传感器事件数据 说明 度量单位
TYPE_GAME_ROTATION_VECTOR SensorEvent.values[0] 沿 x 轴的旋转矢量分量 (x * sin(θ/2))。 无单位
SensorEvent.values[1] 沿 y 轴的旋转矢量分量 (y * sin(θ/2))。
SensorEvent.values[2] 沿 z 轴的旋转矢量分量 (z * sin(θ/2))。
TYPE_GEOMAGNETIC_ROTATION_VECTOR SensorEvent.values[0] 沿 x 轴的旋转矢量分量 (x * sin(θ/2))。 无单位
SensorEvent.values[1] 沿 y 轴的旋转矢量分量 (y * sin(θ/2))。
SensorEvent.values[2] 沿 z 轴的旋转矢量分量 (z * sin(θ/2))。
TYPE_MAGNETIC_FIELD SensorEvent.values[0] 沿 x 轴的地磁场强度。 微秒
SensorEvent.values[1] 沿 y 轴的地磁场强度。
SensorEvent.values[2] 沿 z 轴的地磁场强度。
TYPE_MAGNETIC_FIELD_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_ORIENTATION1 SensorEvent.values[0] 方位角(绕 z 轴的角度)。 度数
SensorEvent.values[1] 俯仰角(绕 x 轴的角度)。
SensorEvent.values[2] 倾侧角(绕 y 轴的角度)。
TYPE_PROXIMITY SensorEvent.values[0] 与物体的距离。2 厘米

1此传感器在 Android 2.2 (API) 中已废弃 级别 8),且此传感器类型在 Android 4.4W(API 级别 20)中已废弃。 传感器框架提供了用于获取设备的替代方法 Compute Engine 中的 设备的屏幕方向

2 有些近程传感器仅提供二进制值 分别代表近远和远近

使用游戏旋转矢量传感器

游戏旋转矢量传感器与 轮播 矢量传感器,但它不使用地磁场。因此,Y 轴 而是指向其他参考该引用可按 与陀螺仪绕 Z 轴漂移的位置相同数量级。

由于游戏旋转矢量传感器不使用磁场,因此相对旋转角度 更加准确,不受磁场变化的影响。在以下情况下,请在游戏中使用此传感器: 您并不在意北方是什么,而且正常旋转矢量不符合您的需求 因为它依赖于磁场。

以下代码展示了如何获取默认游戏旋转矢量的实例 传感器:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)

Java

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

使用地磁旋转矢量传感器

地磁旋转矢量传感器类似于 旋转矢量传感器,而 它不使用陀螺仪。此传感器的准确度低于正常旋转矢量传感器 但功耗有所降低。仅在需要收集旋转信息时使用此传感器 在后台获取信息,同时又不会消耗太多电量。使用此传感器时最有用 结合使用。

以下代码展示如何获取默认地磁旋转的实例 矢量传感器:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR)

Java

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

计算设备的屏幕方向

通过计算设备的屏幕方向,您可以监控 相对于地球参照系(具体来讲, 北极)。以下代码展示了如何计算设备的 屏幕方向:

Kotlin

private lateinit var sensorManager: SensorManager
...
// Rotation matrix based on current readings from accelerometer and magnetometer.
val rotationMatrix = FloatArray(9)
SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading)

// Express the updated rotation matrix as three orientation angles.
val orientationAngles = FloatArray(3)
SensorManager.getOrientation(rotationMatrix, orientationAngles)

Java

private SensorManager sensorManager;
...
// Rotation matrix based on current readings from accelerometer and magnetometer.
final float[] rotationMatrix = new float[9];
SensorManager.getRotationMatrix(rotationMatrix, null,
    accelerometerReading, magnetometerReading);

// Express the updated rotation matrix as three orientation angles.
final float[] orientationAngles = new float[3];
SensorManager.getOrientation(rotationMatrix, orientationAngles);

系统使用设备的地磁学来计算屏幕方向角 现场传感器与设备的加速度计结合使用。使用这两种 系统会为以下三大平台提供数据: 屏幕方向角度:

  • 方位角(绕 -z 轴的旋转角度)。这是 设备当前罗盘方向与磁北方向之间的角度。 如果设备的顶部边缘朝向磁北方向,则方位角为 0 学位;如果上边缘朝南,则方位角为 180 度。同样, 如果上边缘朝东,则方位角为 90 度;如果上边缘 朝西,方位角为 270 度。
  • 俯仰角(绕 x 轴旋转的角度)。这是 平行于设备屏幕的平面与平行平面之间的夹角 如果将设备放在与地面平行的位置, 并将设备的顶部边缘向地面倾斜, 俯仰角将变为正。反方向倾斜 - 使设备的顶部边缘远离地面— 俯仰角变为负值。值的范围是 -90 度到 90 度。
  • 滚转(绕 y 轴旋转的角度)。这是 垂直于设备屏幕的平面与平面之间的角度 与地面垂直如果将设备与地面平行放置 使底部边缘最靠近您,并倾斜设备的左侧边缘 旋转角度将变为正值。向相反方向倾斜 方向—即设备的右边缘向地面移动— 导致侧倾角变为负值。值的范围为 -180 度 设为 180 度。

注意:传感器的滚转定义已发生变化,以反映 地理传感器生态系统中的绝大多数实现。

请注意,这些角度使用的坐标系与 一种用于航空航天飞机(用于偏航、俯仰和翻滚)。在航空系统中, X 轴是沿着飞机长边(从机尾到机头)的。

方向传感器通过处理原始传感器数据来获取其数据 来自加速度计和地磁场传感器的传感器。由于云量较大, 屏幕方向的精确度和精确度、 传感器就变小了。具体来说,该传感器只有在发生滚转时, 角度为 0。因此,Android 中已废弃方向传感器 2.2(API 级别 8),Android 中废弃了屏幕方向传感器类型 4.4W(API 级别 20)。 我们建议您不要使用来自方向传感器的原始数据, 请使用 getRotationMatrix() 方法与 getOrientation() 方法 计算屏幕方向值,如以下代码示例所示。作为 可以使用 remapCoordinateSystem() 方法将屏幕方向值转换为应用的 参考。

Kotlin

class SensorActivity : Activity(), SensorEventListener {

    private lateinit var sensorManager: SensorManager
    private val accelerometerReading = FloatArray(3)
    private val magnetometerReading = FloatArray(3)

    private val rotationMatrix = FloatArray(9)
    private val orientationAngles = FloatArray(3)

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        // Do something here if sensor accuracy changes.
        // You must implement this callback in your code.
    }

    override fun onResume() {
        super.onResume()

        // Get updates from the accelerometer and magnetometer at a constant rate.
        // To make batch operations more efficient and reduce power consumption,
        // provide support for delaying updates to the application.
        //
        // In this example, the sensor reporting delay is small enough such that
        // the application receives an update before the system checks the sensor
        // readings again.
        sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also { accelerometer ->
            sensorManager.registerListener(
                    this,
                    accelerometer,
                    SensorManager.SENSOR_DELAY_NORMAL,
                    SensorManager.SENSOR_DELAY_UI
            )
        }
        sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also { magneticField ->
            sensorManager.registerListener(
                    this,
                    magneticField,
                    SensorManager.SENSOR_DELAY_NORMAL,
                    SensorManager.SENSOR_DELAY_UI
            )
        }
    }

    override fun onPause() {
        super.onPause()

        // Don't receive any more updates from either sensor.
        sensorManager.unregisterListener(this)
    }

    // Get readings from accelerometer and magnetometer. To simplify calculations,
    // consider storing these readings as unit vectors.
    override fun onSensorChanged(event: SensorEvent) {
        if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
            System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size)
        } else if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) {
            System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size)
        }
    }

    // Compute the three orientation angles based on the most recent readings from
    // the device's accelerometer and magnetometer.
    fun updateOrientationAngles() {
        // Update rotation matrix, which is needed to update orientation angles.
        SensorManager.getRotationMatrix(
                rotationMatrix,
                null,
                accelerometerReading,
                magnetometerReading
        )

        // "rotationMatrix" now has up-to-date information.

        SensorManager.getOrientation(rotationMatrix, orientationAngles)

        // "orientationAngles" now has up-to-date information.
    }
}

Java

public class SensorActivity extends Activity implements SensorEventListener {

    private SensorManager sensorManager;
    private final float[] accelerometerReading = new float[3];
    private final float[] magnetometerReading = new float[3];

    private final float[] rotationMatrix = new float[9];
    private final float[] orientationAngles = new float[3];

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Do something here if sensor accuracy changes.
        // You must implement this callback in your code.
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Get updates from the accelerometer and magnetometer at a constant rate.
        // To make batch operations more efficient and reduce power consumption,
        // provide support for delaying updates to the application.
        //
        // In this example, the sensor reporting delay is small enough such that
        // the application receives an update before the system checks the sensor
        // readings again.
        Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        if (accelerometer != null) {
            sensorManager.registerListener(this, accelerometer,
                SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);
        }
        Sensor magneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        if (magneticField != null) {
            sensorManager.registerListener(this, magneticField,
                SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        // Don't receive any more updates from either sensor.
        sensorManager.unregisterListener(this);
    }

    // Get readings from accelerometer and magnetometer. To simplify calculations,
    // consider storing these readings as unit vectors.
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
          System.arraycopy(event.values, 0, accelerometerReading,
              0, accelerometerReading.length);
        } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            System.arraycopy(event.values, 0, magnetometerReading,
                0, magnetometerReading.length);
        }
    }

    // Compute the three orientation angles based on the most recent readings from
    // the device's accelerometer and magnetometer.
    public void updateOrientationAngles() {
        // Update rotation matrix, which is needed to update orientation angles.
        SensorManager.getRotationMatrix(rotationMatrix, null,
            accelerometerReading, magnetometerReading);

        // "rotationMatrix" now has up-to-date information.

        SensorManager.getOrientation(rotationMatrix, orientationAngles);

        // "orientationAngles" now has up-to-date information.
    }
}

您通常不需要对 设备原始方向角,而不是平移传感器的方向角 坐标系。

使用地磁场传感器

借助地磁场传感器,您可以监测地球磁场的变化。通过 以下代码展示如何获取默认地磁场传感器的实例:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)

Java

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

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

该传感器提供三个坐标轴中每个坐标轴的原始场强数据(以 μT 为单位)。 通常,您无需直接使用此传感器。您可以改用旋转矢量 传感器来确定原始旋转运动,也可以使用加速度计和地磁场 传感器与 getRotationMatrix() 方法结合使用,以获取旋转矩阵和倾斜矩阵。然后,您可以 将这些矩阵与 getOrientation() 搭配使用 和 getInclination() 方法以获取方位角 以及地磁倾角数据

注意 :在测试应用时,您可以改进 以 8 字形图案挥动设备,了解传感器的准确度。

使用未经校准的磁力计

未经校准的磁力计类似于地磁场 传感器,只不过没有对磁场应用硬铁校准。出厂校准 仍会对磁场施加温度补偿。未经校准的磁力计 有助于处理不良的硬铁估算。一般来说,geomagneticsensor_event.values[0] 将接近 uncalibrated_magnetometer_event.values[0] - uncalibrated_magnetometer_event.values[3]。即,

calibrated_x ~= uncalibrated_x - bias_estimate_x

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

除了磁场以外,未经校准的磁力计还提供 估算硬铁偏差。以下代码展示了如何获取 默认的未校准磁力计:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED)

Java

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

使用近程传感器

借助近程传感器,您可以确定物体与设备之间的距离。以下 代码展示了如何获取默认近程传感器的实例:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)

Java

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

近程传感器通常用于确定头部与面部之间的距离 (例如,当用户拨打或接听电话时)。大多数人 近程传感器返回绝对距离(以厘米为单位),但有些传感器只返回 远距离值。

注意:在某些设备型号上,近程传感器 隐藏屏幕时,屏幕下方可能会出现闪烁的点 。

以下代码展示如何使用近程传感器:

Kotlin

class SensorActivity : Activity(), SensorEventListener {

    private lateinit var sensorManager: SensorManager
    private var proximity: Sensor? = null

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)

        // Get an instance of the sensor service, and use that to get an instance of
        // a particular sensor.
        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        // Do something here if sensor accuracy changes.
    }

    override fun onSensorChanged(event: SensorEvent) {
        val distance = event.values[0]
        // Do something with this sensor data.
    }

    override fun onResume() {
        // Register a listener for the sensor.
        super.onResume()

        proximity?.also { proximity ->
            sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL)
        }
    }

    override fun onPause() {
        // Be sure to unregister the sensor when the activity pauses.
        super.onPause()
        sensorManager.unregisterListener(this)
    }
}

Java

public class SensorActivity extends Activity implements SensorEventListener {
    private SensorManager sensorManager;
    private Sensor proximity;

    @Override
    public final void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Get an instance of the sensor service, and use that to get an instance of
        // a particular sensor.
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
    }

    @Override
    public final void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Do something here if sensor accuracy changes.
    }

    @Override
    public final void onSensorChanged(SensorEvent event) {
        float distance = event.values[0];
        // Do something with this sensor data.
    }

    @Override
    protected void onResume() {
        // Register a listener for the sensor.
        super.onResume();
        sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL);
      }

    @Override
    protected void onPause() {
        // Be sure to unregister the sensor when the activity pauses.
        super.onPause();
        sensorManager.unregisterListener(this);
    }
}

注意:有些近程传感器会返回代表 “附近”还是“远”?在这种情况下,传感器通常会在远距离状态下报告其最大范围值 在接近状态下的较小值。通常,远距离值为 >5 厘米,但这可能有所不同 从传感器到传感器。您可以使用 getMaximumRange() 方法确定传感器的最大范围。

另请阅读