Платформа 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)
Java
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)
Java
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)
Java
private SensorManager sensorManager; ... // Rotation matrix based on current readings from accelerometer and magnetometer. final float[] rotationMatrix = new float[9]; SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading); // Express the updated rotation matrix as three orientation angles. final float[] orientationAngles = new float[3]; SensorManager.getOrientation(rotationMatrix, orientationAngles);
Система вычисляет углы ориентации, используя датчик геомагнитного поля устройства в сочетании с акселерометром устройства. С помощью этих двух аппаратных датчиков система предоставляет данные для следующих трех углов ориентации:
- Азимут (градусы поворота вокруг оси -z). Это угол между текущим направлением компаса устройства и магнитным севером. Если верхний край устройства обращен к магнитному северу, азимут равен 0 градусов; если верхний край обращен к югу, азимут равен 180 градусам. Аналогично, если верхний край обращен к востоку, азимут равен 90 градусам, а если верхний край обращен к западу, азимут равен 270 градусам.
- Угол наклона (градусы вращения вокруг оси x). Это угол между плоскостью, параллельной экрану устройства, и плоскостью, параллельной земле. Если держать устройство параллельно земле так, чтобы нижний край был ближе к вам, и наклонить верхний край устройства к земле, угол наклона станет положительным. Наклон в противоположном направлении — перемещение верхнего края устройства от земли — приведет к тому, что угол наклона станет отрицательным. Диапазон значений составляет от -90 до 90 градусов.
- Угол крена (градусы вращения вокруг оси Y). Это угол между плоскостью, перпендикулярной экрану устройства, и плоскостью, перпендикулярной земле. Если держать устройство параллельно земле так, чтобы нижний край был ближе к вам, и наклонить левый край устройства к земле, угол крена станет положительным. Наклон в противоположном направлении — перемещение правого края устройства к земле — приведет к тому, что угол крена станет отрицательным. Диапазон значений составляет от -180 до 180 градусов.
Примечание: Определение крена датчика изменено с учетом особенностей подавляющего большинства реализаций в экосистеме геосенсоров.
Следует отметить, что эти углы рассчитываются по другой системе координат, нежели та, которая используется в авиации (для рыскания, тангажа и крена). В авиационной системе координат ось x проходит вдоль длинной стороны самолета, от хвоста до носа.
Датчик ориентации получает данные путем обработки необработанных данных с акселерометра и датчика геомагнитного поля. Из-за трудоемкой обработки точность и прецизионность датчика ориентации снижаются. В частности, этот датчик надежен только при угле крена, равном 0. В результате датчик ориентации был объявлен устаревшим в Android 2.2 (уровень API 8), а тип датчика ориентации был объявлен устаревшим в Android 4.4W (уровень API 20). Вместо использования необработанных данных с датчика ориентации мы рекомендуем использовать метод getRotationMatrix() совместно с методом getOrientation() для вычисления значений ориентации, как показано в следующем примере кода. В рамках этого процесса вы можете использовать метод remapCoordinateSystem() для преобразования значений ориентации в систему координат вашего приложения.
Котлин
class SensorActivity : Activity(), SensorEventListener { private lateinit var sensorManager: SensorManager private val accelerometerReading = FloatArray(3) private val magnetometerReading = FloatArray(3) private val rotationMatrix = FloatArray(9) private val orientationAngles = FloatArray(3) public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { // Do something here if sensor accuracy changes. // You must implement this callback in your code. } override fun onResume() { super.onResume() // Get updates from the accelerometer and magnetometer at a constant rate. // To make batch operations more efficient and reduce power consumption, // provide support for delaying updates to the application. // // In this example, the sensor reporting delay is small enough such that // the application receives an update before the system checks the sensor // readings again. sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also { accelerometer -> sensorManager.registerListener( this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI ) } sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also { magneticField -> sensorManager.registerListener( this, magneticField, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI ) } } override fun onPause() { super.onPause() // Don't receive any more updates from either sensor. sensorManager.unregisterListener(this) } // Get readings from accelerometer and magnetometer. To simplify calculations, // consider storing these readings as unit vectors. override fun onSensorChanged(event: SensorEvent) { if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) { System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size) } else if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) { System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size) } } // Compute the three orientation angles based on the most recent readings from // the device's accelerometer and magnetometer. fun updateOrientationAngles() { // Update rotation matrix, which is needed to update orientation angles. SensorManager.getRotationMatrix( rotationMatrix, null, accelerometerReading, magnetometerReading ) // "rotationMatrix" now has up-to-date information. SensorManager.getOrientation(rotationMatrix, orientationAngles) // "orientationAngles" now has up-to-date information. } }
Java
public class SensorActivity extends Activity implements SensorEventListener { private SensorManager sensorManager; private final float[] accelerometerReading = new float[3]; private final float[] magnetometerReading = new float[3]; private final float[] rotationMatrix = new float[9]; private final float[] orientationAngles = new float[3]; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // Do something here if sensor accuracy changes. // You must implement this callback in your code. } @Override protected void onResume() { super.onResume(); // Get updates from the accelerometer and magnetometer at a constant rate. // To make batch operations more efficient and reduce power consumption, // provide support for delaying updates to the application. // // In this example, the sensor reporting delay is small enough such that // the application receives an update before the system checks the sensor // readings again. Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); if (accelerometer != null) { sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI); } Sensor magneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); if (magneticField != null) { sensorManager.registerListener(this, magneticField, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI); } } @Override protected void onPause() { super.onPause(); // Don't receive any more updates from either sensor. sensorManager.unregisterListener(this); } // Get readings from accelerometer and magnetometer. To simplify calculations, // consider storing these readings as unit vectors. @Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.length); } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.length); } } // Compute the three orientation angles based on the most recent readings from // the device's accelerometer and magnetometer. public void updateOrientationAngles() { // Update rotation matrix, which is needed to update orientation angles. SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading); // "rotationMatrix" now has up-to-date information. SensorManager.getOrientation(rotationMatrix, orientationAngles); // "orientationAngles" now has up-to-date information. } }
Обычно нет необходимости обрабатывать или фильтровать исходные данные об углах ориентации устройства, за исключением преобразования системы координат датчика в систему координат вашего приложения.
Используйте датчик геомагнитного поля.
Датчик геомагнитного поля позволяет отслеживать изменения магнитного поля Земли. Следующий код показывает, как получить экземпляр стандартного датчика геомагнитного поля:
Котлин
private lateinit var sensorManager: SensorManager private var sensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
Примечание: Если ваше приложение ориентировано на Android 12 (уровень API 31) или выше, использование этого датчика будет ограничено по частоте запросов .
Этот датчик предоставляет необработанные данные о напряженности поля (в мкТл) для каждой из трех координатных осей. Обычно нет необходимости использовать этот датчик напрямую. Вместо этого можно использовать датчик вектора вращения для определения необработанного вращательного движения, или же можно использовать акселерометр и датчик геомагнитного поля совместно с методом 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)
Java
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)
Java
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) } }
Java
public class SensorActivity extends Activity implements SensorEventListener { private SensorManager sensorManager; private Sensor proximity; @Override public final void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Get an instance of the sensor service, and use that to get an instance of // a particular sensor. sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); } @Override public final void onAccuracyChanged(Sensor sensor, int accuracy) { // Do something here if sensor accuracy changes. } @Override public final void onSensorChanged(SensorEvent event) { float distance = event.values[0]; // Do something with this sensor data. } @Override protected void onResume() { // Register a listener for the sensor. super.onResume(); sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL); } @Override protected void onPause() { // Be sure to unregister the sensor when the activity pauses. super.onPause(); sensorManager.unregisterListener(this); } }
Примечание: Некоторые датчики приближения возвращают двоичные значения, обозначающие «близко» или «далеко». В этом случае датчик обычно сообщает максимальное значение дальности в состоянии «далеко» и меньшее значение в состоянии «близко». Как правило, значение «далеко» составляет > 5 см, но это может варьироваться от датчика к датчику. Вы можете определить максимальную дальность действия датчика, используя метод getMaximumRange() .