A plataforma Android oferece dois sensores que permitem determinar a posição de um dispositivo: o sensor de campo geomagnético e o acelerômetro. O Android também oferece um sensor que permite determinar o quão perto é um dispositivo é para um objeto (conhecido como sensor de proximidade). O sensor de campo geomagnético e o sensor de proximidade são baseados em hardware. Mais frequentes os fabricantes de celulares e tablets incluem um sensor de campo geomagnético. Da mesma forma, os fabricantes de celulares geralmente incluem um sensor de proximidade para determinar quando o celular está próximo ao rosto do usuário (por exemplo, durante um ). Para determinar a orientação de um dispositivo, você pode usar as leituras do acelerômetro e do sensor de campo geomagnético.
Observação:o sensor de orientação foi descontinuado no Android 2.2. (nível 8 da API), e o tipo de sensor de orientação foi descontinuado no Android 4.4W. (nível 20 da API).
Os sensores de posição são úteis para determinar a posição física de um dispositivo em referência do mundo. Por exemplo, você pode usar o sensor de campo geomagnético junto com o acelerômetro para determinar a posição de um dispositivo em relação ao polo norte magnético. Você também pode usar esses sensores para determinar a orientação de um dispositivo no frame de referência do seu aplicativo. Em geral, os sensores de posição não são usados para monitorar o movimento do dispositivo, como trepidação, inclinação ou impulso. Para saber mais, consulte Sensores de movimento.
O sensor de campo geomagnético e o acelerômetro retornam matrizes multidimensionais
dos valores do sensor para cada SensorEvent
. Por exemplo:
o sensor de campo geomagnético fornece valores de intensidade do campo geomagnético para
cada um dos três eixos de coordenadas durante um único evento do sensor. Da mesma forma,
o sensor do acelerômetro mede a aceleração aplicada ao dispositivo durante uma
evento do sensor. Para mais informações sobre os sistemas de coordenadas usados
por sensores, consulte
Sistemas de coordenadas do sensor. O sensor de proximidade fornece um valor único
para cada evento de sensor. A Tabela 1 resume os sensores de posição
compatíveis com a plataforma Android.
Tabela 1. Sensores de posição compatíveis com a plataforma Android.
Sensor | Dados de eventos do sensor | Descrição | Unidades de medida |
---|---|---|---|
TYPE_GAME_ROTATION_VECTOR |
SensorEvent.values[0] |
Componente do vetor de rotação ao longo do eixo X (X * sen (θ / 2)). | Sem unidade |
SensorEvent.values[1] |
Componente do vetor de rotação ao longo do eixo Y (Y * sen (θ / 2)). | ||
SensorEvent.values[2] |
Componente do vetor de rotação ao longo do eixo Z (Z * sen (θ / 2)). | ||
TYPE_GEOMAGNETIC_ROTATION_VECTOR |
SensorEvent.values[0] |
Componente do vetor de rotação ao longo do eixo X (X * sen (θ / 2)). | Sem unidade |
SensorEvent.values[1] |
Componente do vetor de rotação ao longo do eixo Y (Y * sen (θ / 2)). | ||
SensorEvent.values[2] |
Componente do vetor de rotação ao longo do eixo Z (Z * sen (θ / 2)). | ||
TYPE_MAGNETIC_FIELD |
SensorEvent.values[0] |
Intensidade do campo geomagnético ao longo do eixo X. | μT |
SensorEvent.values[1] |
Intensidade do campo geomagnético ao longo do eixo Y. | ||
SensorEvent.values[2] |
Intensidade do campo geomagnético ao longo do eixo Z. | ||
TYPE_MAGNETIC_FIELD_UNCALIBRATED |
SensorEvent.values[0] |
Intensidade do campo geomagnético (sem calibração do ferro duro) ao longo do eixo X. | μT |
SensorEvent.values[1] |
Intensidade do campo geomagnético (sem calibração do ferro duro) ao longo do eixo Y. | ||
SensorEvent.values[2] |
Intensidade do campo geomagnético (sem calibração do ferro duro) ao longo do eixo Z. | ||
SensorEvent.values[3] |
Estimativa da polarização do ferro ao longo do eixo X. | ||
SensorEvent.values[4] |
Estimativa da polarização do ferro ao longo do eixo Y. | ||
SensorEvent.values[5] |
Estimativa da polarização do ferro ao longo do eixo Z. | ||
TYPE_ORIENTATION 1 |
SensorEvent.values[0] |
Azimute (ângulo ao redor do eixo Z). | Graus |
SensorEvent.values[1] |
Inclinação (ângulo ao redor do eixo X). | ||
SensorEvent.values[2] |
Rolagem (ângulo ao redor do eixo Y). | ||
TYPE_PROXIMITY |
SensorEvent.values[0] |
Distância do objeto.2 | cm |
1O uso desse sensor foi suspenso no Android 2.2 (API nível 8), e o tipo dele foi descontinuado no Android 4.4W (API nível 20). O framework do sensor oferece métodos alternativos para adquirir dispositivos orientação, que são discutidas em Computação orientação do dispositivo.
2 Alguns sensores de proximidade fornecem apenas valores binários que representam posições de "perto" e "longe".
Usar o sensor vetorial de rotação para jogos
O sensor vetorial de rotação para jogos é idêntico ao sensor vetorial de rotação, mas não usa o campo geomagnético. Portanto, o eixo Y não apontar para o norte, mas para alguma outra referência. É permitido que essa referência se desloque na mesma ordem de magnitude que o giroscópio em torno do eixo Z.
Como o sensor vetorial de rotação do jogo não usa o campo magnético, as rotações relativas são mais precisas e não são afetadas por alterações no campo magnético. Use esse sensor em um jogo se: não se importa a localização do norte, e o vetor de rotação normal não atende às suas necessidades por depender do campo magnético.
O código a seguir mostra como ter uma instância do sensor vetorial de rotação padrão para jogos:
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);
Usar o sensor vetorial de rotação geomagnética
O sensor vetorial de rotação geomagnética é semelhante ao sensor de vetor de rotação, mas ele não usa o giroscópio. A precisão desse sensor é menor que a do sensor vetorial de rotação normal, mas há menos consumo de energia. Use esse sensor apenas se você quiser coletar informações de rotação em segundo plano sem usar muita bateria. Esse sensor é mais útil quando usado com lotes.
O código a seguir mostra como ter uma instância da rotação geomagnética padrão. sensor vetorial:
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);
Calcular a orientação do dispositivo
Calculando a orientação de um dispositivo, você pode monitorar a posição do dispositivo em relação ao referencial terrestre (especificamente, o sistema magnético polo norte). O código a seguir mostra como computar o volume orientação:
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);
O sistema calcula os ângulos de orientação usando o sensor de campo geomagnético do dispositivo em combinação com o acelerômetro. O uso desses dois recursos sensores de hardware, o sistema fornece dados para os três ângulos de orientação:
- Azimute (graus de rotação em torno do eixo -z). Isso é o ângulo entre a direção atual da bússola do dispositivo e o norte magnético. Se a borda superior do dispositivo estiver voltada para o norte magnético, o azimute será 0 graus; se a borda superior estiver voltada para o sul, o azimute será de 180 graus. Da mesma forma, se o canto superior estiver voltado para o leste, o azimute será de 90 graus. Já se ele estiver voltado para o oeste, o azimute será de 270 graus.
- Inclinação (graus de rotação em torno do eixo X). Este é o ângulo entre um plano paralelo à tela do dispositivo e um plano paralelo ao solo. Se você segurar o dispositivo paralelamente ao chão com a parte de baixo na borda mais próxima de você e incline a borda superior do dispositivo em direção ao chão, o ângulo de inclinação se torna positivo. Inclinar o dispositivo na direção oposta, afastando o canto superior do solo, fará com que o ângulo de inclinação seja negativo. O intervalo de valores é de -90 a 90 graus.
- Rolagem (graus de rotação em torno do eixo Y). Esta é a ângulo entre um plano perpendicular à tela do dispositivo e um plano perpendicular ao solo. Se você segurar o dispositivo paralelo ao solo com o canto inferior mais próximo de você e incline o lado esquerdo do dispositivo em direção ao solo, o ângulo de rolagem será positivo. Inclinação na direção oposta direção, movendo a borda direita do dispositivo em direção ao solo, faz com que o ângulo de rolagem se torne negativo. O intervalo de valores é de -180 a 180 graus.
Observação: a definição de rolagem do sensor mudou para refletir a grande maioria das implementações no ecossistema do geosensor.
Esses ângulos funcionam com base em um sistema de coordenadas diferente do usada na aviação (para guinada, inclinação e rolagem). No sistema de aviação, o eixo x está ao longo do lado longo do plano, da cauda ao nariz.
O sensor de orientação gera informações processando os dados brutos do sensor
do acelerômetro e do sensor de campo geomagnético. Devido ao processamento
pesado envolvido, a exatidão e a precisão do sensor de
orientação são reduzidas. Especificamente, esse sensor só é confiável quando o ângulo de
rolagem é 0. Como resultado, o uso do sensor de orientação foi suspenso no Android
2.2 (nível 8 da API), e o tipo dele foi descontinuado no Android
4.4W (nível 20 da API).
Em vez de usar dados brutos do sensor de orientação, recomendamos que você
use a propriedade getRotationMatrix()
em conjunto com o método
Método getOrientation()
para calcular os valores de orientação, conforme mostrado no exemplo de código a seguir. Como parte
desse processo, você pode usar o método
remapCoordinateSystem()
para converter os valores de orientação no frame de referência do
aplicativo.
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. } }
Normalmente, você não precisa executar nenhum processamento, nem filtrar dados dos ângulos de orientação brutos do dispositivo, além de converter o sistema de coordenadas do sensor no frame de referência do seu aplicativo.
Usar o sensor de campo geomagnético
O sensor do campo geomagnético permite monitorar as alterações no campo magnético da Terra. O o código a seguir mostra como ter uma instância do sensor de campo geomagnético padrão:
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);
Observação: se o app for destinado ao Android 12 (nível 31 da API) ou mais recente, esse sensor será limitado a uma taxa.
Este sensor fornece dados brutos de intensidade do campo (em μT) para cada um dos três eixos de coordenadas.
Normalmente, não é preciso usar esse sensor diretamente. Em vez disso, você pode usar o sensor vetorial
de rotação para determinar o movimento rotacional bruto ou o acelerômetro e o sensor de campo
geomagnético com o método getRotationMatrix()
para acessar a matriz de rotação e inclinação. É possível
use essas matrizes com o getOrientation()
e getInclination()
para obter o azimute
e inclinação geomagnética.
Observação : ao testar o app, é possível melhorar a precisão do sensor, balançando o dispositivo em um padrão em forma de 8.
Usar o magnetômetro sem calibração
O magnetômetro não calibrado é semelhante ao campo geomagnético
sensor, exceto que nenhuma calibração de ferro duro é aplicada ao campo magnético. Calibração de fábrica
e a compensação de temperatura ainda são aplicadas ao campo magnético. O magnetômetro sem calibração
é útil para gerenciar estimativas ruins de ferro duro. Em geral, geomagneticsensor_event.values[0]
será próximo de uncalibrated_magnetometer_event.values[0] -
uncalibrated_magnetometer_event.values[3]
. Ou seja,
calibrated_x ~= uncalibrated_x - bias_estimate_x
Observação: os sensores não calibrados fornecem resultados mais brutos e podem incluir algumas tendências, mas as medições deles contêm menos saltos de correções aplicadas por meio da calibração. Alguns aplicativos podem preferir esses resultados não calibrados como mais suaves e mais confiáveis. Por exemplo, se um aplicativo está tentando conduzir a própria fusão de sensores, introduzir calibragens pode realmente distorcer os resultados.
Além do campo magnético, o magnetômetro sem calibração também fornece a polarização estimada de ferro duro em cada eixo. O código a seguir mostra como receber uma instância do magnetômetro não calibrado padrão:
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);
Usar o sensor de proximidade
O sensor de proximidade permite determinar a que distância um objeto está de um dispositivo. O seguinte mostra como ter uma instância do sensor de proximidade padrão:
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);
O sensor de proximidade geralmente é usado para determinar a que distância a cabeça de uma pessoa está do rosto de um celular (por exemplo, quando um usuário está fazendo ou recebendo uma chamada telefônica). Mais frequentes os sensores de proximidade retornam a distância absoluta, em cm, mas alguns retornam apenas "long" em valores distantes.
Observação:em alguns modelos de dispositivos, o sensor de proximidade está localizado embaixo da tela, podendo fazer com que um ponto piscando apareça na tela se ativado enquanto a tela está
O código a seguir mostra como usar o sensor de proximidade:
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); } }
Observação:alguns sensores de proximidade retornam valores binários que representam
"perto" ou "longe". Nesse caso, o sensor geralmente informa o valor máximo do intervalo no estado "longe"
e um valor menor no estado próximo. Normalmente, o valor "longe" é um valor > 5 cm, mas isso pode variar
de sensor para sensor. É possível determinar o alcance máximo de um sensor usando o método getMaximumRange()
.