Датчики положения

Платформа 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_ORIENTATION 1 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). Платформа датчиков предоставляет альтернативные методы определения ориентации устройства, которые обсуждаются в разделе Вычисление ориентации устройства .

2 Некоторые датчики приближения предоставляют только двоичные значения, представляющие ближнее и дальнее расстояние.

Используйте датчик вектора вращения игры

Датчик вектора вращения игры идентичен датчику вектора вращения , за исключением того, что он не использует геомагнитное поле. Следовательно, ось Y указывает не на север, а на какую-то другую точку отсчета. Эта опорная точка может отклоняться на тот же порядок, что и дрейф гироскопа вокруг оси Z.

Поскольку датчик вектора вращения игры не использует магнитное поле, относительные вращения являются более точными и на них не влияют изменения магнитного поля. Используйте этот датчик в игре, если вас не волнует, где находится север, а нормальный вектор вращения не соответствует вашим потребностям из-за его зависимости от магнитного поля.

Следующий код показывает, как получить экземпляр датчика вектора вращения игры по умолчанию:

Котлин

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)

Ява

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

Используйте датчик вектора геомагнитного вращения.

Датчик вектора геомагнитного вращения аналогичен датчику вектора вращения , но в нем не используется гироскоп. Точность этого датчика ниже, чем у обычного датчика вектора вращения, но энергопотребление снижается. Используйте этот датчик только в том случае, если вы хотите собирать информацию о вращении в фоновом режиме, не используя слишком много заряда батареи. Этот датчик наиболее полезен при использовании в сочетании с дозированием.

Следующий код показывает, как получить экземпляр датчика вектора геомагнитного вращения по умолчанию:

Котлин

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)

Ява

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

Вычислить ориентацию устройства

Вычислив ориентацию устройства, вы можете отслеживать положение устройства относительно земной системы отсчета (в частности, северного магнитного полюса). Следующий код показывает, как вычислить ориентацию устройства:

Котлин

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)

Ява

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() для перевода значений ориентации в систему отсчета вашего приложения.

Котлин

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.
    }
}

Ява

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.
    }
}

Обычно вам не требуется выполнять какую-либо обработку данных или фильтрацию необработанных углов ориентации устройства, кроме перевода системы координат датчика в систему отсчета вашего приложения.

Используйте датчик геомагнитного поля

Датчик геомагнитного поля позволяет отслеживать изменения магнитного поля Земли. Следующий код показывает, как получить экземпляр датчика геомагнитного поля по умолчанию:

Котлин

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

Ява

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

Примечание. Если ваше приложение предназначено для Android 12 (уровень API 31) или выше, этот датчик имеет ограничение по скорости .

Этот датчик предоставляет необработанные данные о напряженности поля (в мкТл) для каждой из трех координатных осей. Обычно вам не нужно использовать этот датчик напрямую. Вместо этого вы можете использовать датчик вектора вращения для определения необработанного вращательного движения или использовать акселерометр и датчик геомагнитного поля в сочетании с методом getRotationMatrix() для получения матрицы вращения и матрицы наклона. Затем вы можете использовать эти матрицы с методами getOrientation() и getInclination() для получения данных об азимуте и геомагнитном наклоне.

Примечание. При тестировании приложения вы можете повысить точность датчика, размахивая устройством в форме восьмерки.

Используйте некалиброванный магнитометр.

Некалиброванный магнитометр аналогичен датчику геомагнитного поля , за исключением того, что к магнитному полю не применяется твердая калибровка. Заводская калибровка и температурная компенсация по-прежнему применяются к магнитному полю. Некалиброванный магнитометр полезен для обработки плохих оценок твердого железа. В общем, geomagneticsensor_event.values[0] будет близко к uncalibrated_magnetometer_event.values[0] - uncalibrated_magnetometer_event.values[3] . То есть,

calibrated_x ~= uncalibrated_x - bias_estimate_x

Примечание. Некалиброванные датчики дают более необработанные результаты и могут иметь некоторую погрешность, но их измерения содержат меньше скачков по сравнению с поправками, внесенными при калибровке. Некоторые приложения могут предпочесть эти некалиброванные результаты как более плавные и надежные. Например, если приложение пытается выполнить собственное объединение датчиков, введение калибровок может фактически исказить результаты.

Помимо магнитного поля, некалиброванный магнитометр также обеспечивает расчетное смещение твердого железа по каждой оси. Следующий код показывает, как получить экземпляр некалиброванного магнитометра по умолчанию:

Котлин

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)

Ява

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

Используйте датчик приближения

Датчик приближения позволяет определить, насколько далеко объект находится от устройства. Следующий код показывает, как получить экземпляр датчика приближения по умолчанию:

Котлин

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

Ява

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

Датчик приближения обычно используется для определения того, насколько далеко голова человека находится от лица телефонной трубки (например, когда пользователь звонит или принимает телефонный звонок). Большинство датчиков приближения возвращают абсолютное расстояние в см, но некоторые возвращают только значения ближнего и дальнего расстояния.

Примечание. На некоторых моделях устройств датчик приближения расположен под экраном, поэтому на экране может появиться мигающая точка, если он включен при включенном экране.

Следующий код показывает, как использовать датчик приближения:

Котлин

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

Ява

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() .

Вам также следует прочитать