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

Платформа 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 градусов.
  • Pitch (градусы поворота вокруг оси 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() .

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

,

Платформа 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 градусов.
  • Pitch (градусы поворота вокруг оси 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() .

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