Платформа 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 градусов.
- Roll (degrees of rotation about the y axis). This is the angle between a plane perpendicular to the device's screen and a plane perpendicular to the ground. If you hold the device parallel to the ground with the bottom edge closest to you and tilt the left edge of the device toward the ground, the roll angle becomes positive. Tilting in the opposite direction—moving the right edge of the device toward the ground— causes the roll angle to become negative. The range of values is -180 degrees to 180 degrees.
Примечание: определение крена датчика было изменено, чтобы отразить подавляющее большинство реализаций в экосистеме геосенсоров.
Обратите внимание, что эти углы рассчитываются в системе координат, отличной от той, которая используется в авиации (для рыскания, тангажа и крена). В авиационной системе ось 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()
.