La piattaforma Android fornisce due sensori che consentono di determinare la posizione di un dispositivo: il sensore del campo geomagnetico e l'accelerometro. La piattaforma Android fornisce anche un sensore che consente di determinare la vicinanza del lato di un dispositivo a un oggetto (noto come sensore di prossimità). Il sensore del campo geomagnetico e il sensore di prossimità sono basati sull'hardware. La maggior parte dei produttori di smartphone e tablet include un sensore del campo geomagnetico. Analogamente, i produttori di cellulari in genere includono un sensore di prossimità per determinare quando un cellulare viene tenuto vicino al viso di un utente (ad esempio durante una telefonata). Per determinare l'orientamento di un dispositivo, puoi utilizzare le letture dell'accelerometro e del sensore del campo geomagnetico del dispositivo.
Nota: il sensore di orientamento è stato ritirato in Android 2.2 (livello API 8) e il tipo di sensore di orientamento è stato ritirato in Android 4.4W (livello API 20).
I sensori di posizione sono utili per determinare la posizione fisica di un dispositivo nel sistema di riferimento mondiale. Ad esempio, puoi utilizzare il sensore del campo magnetico terrestre in combinazione con l'accelerometro per determinare la posizione di un dispositivo rispetto al polo nord magnetico. Puoi anche utilizzare questi sensori per determinare l'orientamento di un dispositivo nel sistema di riferimento dell'applicazione. I sensori di posizione in genere non vengono utilizzati per monitorare il movimento o il movimento del dispositivo, come scuotimento, inclinazione o spinta (per ulteriori informazioni, vedi Sensori di movimento).
Il sensore del campo geomagnetico e l'accelerometro restituiscono matrici multidimensionali
dei valori del sensore per ogni SensorEvent
. Ad esempio, il sensore del campo geomagnetico fornisce i valori dell'intensità del campo geomagnetico per ciascuno dei tre assi di coordinate durante un singolo evento del sensore. Analogamente, il
sensore di accelerometro misura l'accelerazione applicata al dispositivo durante un
evento del sensore. Per ulteriori informazioni sui sistemi di coordinate utilizzati
dai sensori, consulta
Sistemi di coordinate dei sensori. Il sensore di prossimità fornisce un singolo valore per ogni evento del sensore. La tabella 1 riassume i sensori di posizione supportati sulla piattaforma Android.
Sensore | Dati sugli eventi del sensore | Descrizione | Unità di misura |
---|---|---|---|
TYPE_GAME_ROTATION_VECTOR |
SensorEvent.values[0] |
Componente del vettore di rotazione lungo l'asse x (x * sin(θ/2)). | Senza unità |
SensorEvent.values[1] |
Componente del vettore di rotazione lungo l'asse y (y * sin(θ/2)). | ||
SensorEvent.values[2] |
Componente del vettore di rotazione lungo l'asse z (z * sin(θ/2)). | ||
TYPE_GEOMAGNETIC_ROTATION_VECTOR |
SensorEvent.values[0] |
Componente del vettore di rotazione lungo l'asse x (x * sin(θ/2)). | Senza unità |
SensorEvent.values[1] |
Componente del vettore di rotazione lungo l'asse y (y * sin(θ/2)). | ||
SensorEvent.values[2] |
Componente del vettore di rotazione lungo l'asse z (z * sin(θ/2)). | ||
TYPE_MAGNETIC_FIELD |
SensorEvent.values[0] |
Intensità del campo geomagnetico lungo l'asse x. | μT |
SensorEvent.values[1] |
Intensità del campo geomagnetico lungo l'asse Y. | ||
SensorEvent.values[2] |
Intensità del campo geomagnetico lungo l'asse z. | ||
TYPE_MAGNETIC_FIELD_UNCALIBRATED |
SensorEvent.values[0] |
Intensità del campo geomagnetico (senza calibrazione del ferro duro) lungo l'asse x. | μT |
SensorEvent.values[1] |
Intensità del campo geomagnetico (senza calibrazione del ferro duro) sull'asse y. | ||
SensorEvent.values[2] |
Intensità del campo geomagnetico (senza calibrazione del ferro duro) lungo l'asse z. | ||
SensorEvent.values[3] |
Stima del bias di ferro sull'asse x. | ||
SensorEvent.values[4] |
Stima del bias di ferro sull'asse Y. | ||
SensorEvent.values[5] |
Stima dell'errore sistematico dell'obiettivo lungo l'asse z. | ||
TYPE_ORIENTATION 1 |
SensorEvent.values[0] |
Azimut (angolo attorno all'asse z). | Gradi |
SensorEvent.values[1] |
Inclinazione (angolo attorno all'asse X). | ||
SensorEvent.values[2] |
Inclinazione (angolo attorno all'asse Y). | ||
TYPE_PROXIMITY |
SensorEvent.values[0] |
Distanza dall'oggetto.2 | cm |
1Questo sensore è stato ritirato in Android 2.2 (livello API 8) e questo tipo di sensore è stato ritirato in Android 4.4W (livello API 20). Il framework del sensore fornisce metodi alternativi per acquisire l'orientamento del dispositivo, descritti in Calcolo dell'orientamento del dispositivo.
2 Alcuni sensori di prossimità forniscono solo valori binari che rappresentano vicino e lontano.
Utilizzare il sensore del vettore di rotazione del gioco
Il sensore del vettore di rotazione del gioco è identico al sensore del vettore di rotazione, tranne per il fatto che non utilizza il campo geomagnetico. Pertanto, l'asse Y non indica il nord, ma un altro riferimento. Questo riferimento può avere un'incertezza dello stesso ordine di grandezza della deriva del giroscopio attorno all'asse Z.
Poiché il sensore del vettore di rotazione del gioco non utilizza il campo magnetico, le rotazioni relative sono più precise e non sono interessate dalle variazioni del campo magnetico. Utilizza questo sensore in un gioco se non ti interessa dove si trova il nord e il vettore di rotazione normale non soddisfa le tue esigenze a causa della sua dipendenza dal campo magnetico.
Il seguente codice mostra come ottenere un'istanza del sensore vettoriale di rotazione del gioco predefinito:
Kotlin
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);
Utilizzare il sensore del vettore di rotazione geomagnetica
Il sensore del vettore di rotazione geomagnetica è simile al sensore del vettore di rotazione, ma non utilizza il giroscopio. La precisione di questo sensore è inferiore a quella del normale sensore del vettore di rotazione, ma il consumo energetico è ridotto. Utilizza questo sensore solo se vuoi raccogliere informazioni sulla rotazione in background senza utilizzare troppa batteria. Questo sensore è più utile se utilizzato insieme al raggruppamento.
Il seguente codice mostra come ottenere un'istanza del sensore del vettore di rotazione geomagnetica predefinito:
Kotlin
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);
Calcola l'orientamento del dispositivo
Calcolando l'orientamento di un dispositivo, puoi monitorare la sua posizione rispetto al sistema di riferimento terrestre (in particolare, al polo nord magnetico). Il seguente codice mostra come calcolare l'orientamento di un dispositivo:
Kotlin
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);
Il sistema calcola gli angoli di orientamento utilizzando il sensore del campo magnetico geomagnetico di un dispositivo in combinazione con l'accelerometro del dispositivo. Utilizzando questi due sensori hardware, il sistema fornisce dati per i seguenti tre angoli di orientamento:
- Azimut (gradi di rotazione attorno all'asse -z). Si tratta dell'angolo tra la direzione attuale della bussola del dispositivo e il nord magnetico. Se il bordo superiore del dispositivo è rivolto verso il nord magnetico, l'azimut è di 0 gradi; se il bordo superiore è rivolto verso sud, l'azimut è di 180 gradi. Analogamente, se il bordo superiore è rivolto verso est, l'azimut è di 90 gradi e se è rivolto verso ovest, l'azimut è di 270 gradi.
- Inclinazione (gradi di rotazione attorno all'asse x). Si tratta dell'angolo tra un piano parallelo allo schermo del dispositivo e un piano parallelo al suolo. Se tieni il dispositivo parallelo al suolo con il bordo inferiore più vicino a te e inclini il bordo superiore del dispositivo verso il suolo, l'angolo di inclinazione diventa positivo. Se inclini il dispositivo nella direzione opposta, ovvero allontanando il bordo superiore dal suolo, l'angolo di inclinazione diventa negativo. L'intervallo di valori è compreso tra -90 e 90 gradi.
- Inclinazione (gradi di rotazione attorno all'asse Y). Si tratta dell'angolo tra un piano perpendicolare allo schermo del dispositivo e un piano perpendicolare al suolo. Se tieni il dispositivo parallelo al suolo con il bordo inferiore più vicino a te e inclini il bordo sinistro del dispositivo verso il suolo, l'angolo di inclinazione diventa positivo. Se inclini il dispositivo in direzione opposta, ovvero muovi il bordo destro del dispositivo verso il suolo, l'angolo di inclinazione diventa negativo. L'intervallo di valori va da -180 gradi a 180 gradi.
Nota: la definizione di inclinazione del sensore è stata modificata per riflettere la vasta maggioranza delle implementazioni nell'ecosistema dei geosensori.
Tieni presente che questi angoli si basano su un sistema di coordinate diverso da quello utilizzato nell'aviazione (per imbardata, beccheggio e rollio). Nel sistema aereo, l'asse x è lungo il lato lungo dell'aereo, dalla coda alla prua.
Il sensore di orientamento ricava i dati elaborando i dati non elaborati del sensore
dell'accelerometro e del sensore del campo geomagnetico. A causa dell'elevato livello di elaborazione richiesto, l'accuratezza e la precisione del sensore di orientamento diminuiscono. Nello specifico, questo sensore è affidabile solo quando l'angolo di roll è pari a 0. Di conseguenza, il sensore di orientamento è stato ritirato in Android 2.2 (livello API 8) e il tipo di sensore di orientamento è stato ritirato in Android 4.4W (livello API 20).
Invece di utilizzare i dati non elaborati del sensore di orientamento, ti consigliamo di
utilizzare il metodo getRotationMatrix()
in combinazione con il metodo
getOrientation()
per calcolare i valori di orientamento, come mostrato nell'esempio di codice seguente. Nell'ambito di questo processo, puoi utilizzare il metodo remapCoordinateSystem()
per tradurre i valori di orientamento nel frame di riferimento della tua applicazione.
Kotlin
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. } }
In genere, non è necessario eseguire l'elaborazione o il filtraggio dei dati degli angoli di orientamento non elaborati del dispositivo, tranne che per tradurre il sistema di coordinate del sensore nel sistema di riferimento dell'applicazione.
Utilizzare il sensore del campo geomagnetico
Il sensore del campo geomagnetico consente di monitorare le variazioni del campo magnetico terrestre. Il codice seguente mostra come ottenere un'istanza del sensore del campo geomagnetico predefinito:
Kotlin
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);
Nota: se la tua app ha come target Android 12 (livello API 31) o versioni successive, questo sensore è limitato in termini di frequenza.
Questo sensore fornisce dati non elaborati sull'intensità di campo (in μT) per ciascuno dei tre assi di coordinate.
In genere, non è necessario utilizzare questo sensore direttamente. In alternativa, puoi utilizzare il sensore del vettore di rotazione per determinare il movimento di rotazione non elaborato oppure puoi utilizzare l'accelerometro e il sensore del campo geomagnetico in combinazione con il metodo getRotationMatrix()
per ottenere la matrice di rotazione e la matrice di inclinazione. Puoi quindi
utilizzare queste matrici con i metodi getOrientation()
e getInclination()
per ottenere dati sull'azimut
e sull'inclinazione geomagnetica.
Nota: durante il test dell'app, puoi migliorare la precisione del sensore agitando il dispositivo a forma di 8.
Utilizzare il magnetometro non calibrato
Il magnetometro non calibrato è simile al sensore del campo magnetico geomagnetico, tranne per il fatto che al campo magnetico non viene applicata alcuna calibrazione del ferro duro. La calibrazione di fabbrica
e la compensazione della temperatura vengono comunque applicate al campo magnetico. Il magnetometro non calibrato è utile per gestire le stime errate del ferro. In genere, geomagneticsensor_event.values[0]
è vicino a uncalibrated_magnetometer_event.values[0] -
uncalibrated_magnetometer_event.values[3]
. ovvero
calibrated_x ~= uncalibrated_x - bias_estimate_x
Nota:i sensori non calibrati forniscono risultati più non elaborati e possono includere alcuni bias, ma le loro misurazioni contengono meno salti dovuti alle correzioni applicate durante la calibrazione. Alcune applicazioni potrebbero preferire questi risultati non calibrati perché più uniformi e affidabili. Ad esempio, se un'applicazione tenta di eseguire la propria fusione dei sensori, l'introduzione di calibrazioni può distorcere i risultati.
Oltre al campo magnetico, il magnetometro non calibrato fornisce anche la stima dell'errore di ferro duro in ogni asse. Il seguente codice mostra come ottenere un'istanza del magnetometro non calibrato predefinito:
Kotlin
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);
Usare il sensore di prossimità
Il sensore di prossimità ti consente di determinare la distanza di un oggetto da un dispositivo. Il seguente codice mostra come ottenere un'istanza del sensore di prossimità predefinito:
Kotlin
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);
Il sensore di prossimità viene solitamente utilizzato per determinare la distanza tra la testa di una persona e il volto di un dispositivo cellulare (ad esempio quando un utente effettua o riceve una chiamata). La maggior parte degli accelerometri di prossimità restituisce la distanza assoluta in cm, ma alcuni restituiscono solo i valori vicino e lontano.
Nota: su alcuni modelli di dispositivi, il sensore di prossimità si trova sotto lo schermo, il che può causare la visualizzazione di un punto lampeggiante sullo schermo se è attivo mentre lo schermo è acceso.
Il seguente codice mostra come utilizzare il sensore di prossimità:
Kotlin
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); } }
Nota: alcuni sensori di prossimità restituiscono valori binari che rappresentano "vicino" o "lontano". In questo caso, in genere il sensore riporta il valore massimo dell'intervallo nello stato lontano
e un valore inferiore nello stato vicino. In genere, il valore lontano è un valore maggiore di 5 cm, ma può variare da sensore a sensore. Puoi determinare l'intervallo massimo di un sensore utilizzando il metodo getMaximumRange()
.