Sensori di movimento

La piattaforma Android fornisce diversi sensori che ti consentono di monitorare il movimento di un dispositivo.

Le possibili architetture dei sensori variano in base al tipo di sensore:

  • I sensori di gravità, accelerazione lineare, vettore di rotazione, movimento significativo, contapassi e rilevatore di passi sono basati su hardware o software.
  • I sensori dell'accelerometro e del giroscopio sono sempre basati sull'hardware.

La maggior parte dei dispositivi Android ha un accelerometro e molti ora includono un giroscopio. La disponibilità dei sensori basati su software è più variabile perché spesso si basano su uno o più sensori hardware per derivare i dati. A seconda del dispositivo, questi sensori basati su software possono derivare i propri dati dall'accelerometro e dal magnetometro o dal giroscopio.

I sensori di movimento sono utili per monitorare il movimento del dispositivo, ad esempio inclinazione, scuotimento, rotazione o oscillazione. Il movimento è in genere il risultato dell'input diretto dell'utente (ad esempio, un utente che sterza un'auto in un gioco o che controlla una palla in un gioco), ma può anche essere il risultato dell'ambiente fisico in cui si trova il dispositivo (ad esempio, si muove con te mentre guidi l'auto). Nel primo caso, monitori il movimento rispetto al sistema di riferimento del dispositivo o della tua applicazione; nel secondo caso, monitori il movimento rispetto al sistema di riferimento del mondo. I sensori di movimento di per sé non vengono in genere utilizzati per monitorare la posizione del dispositivo, ma possono essere utilizzati con altri sensori, come il sensore del campo geomagnetico, per determinare la posizione di un dispositivo rispetto al sistema di riferimento del mondo (per ulteriori informazioni, vedi Sensori di posizione).

Tutti i sensori di movimento restituiscono array multidimensionali di valori del sensore per ogni SensorEvent. Ad esempio, durante un singolo evento del sensore, l'accelerometro restituisce dati sulla forza di accelerazione per i tre assi delle coordinate e il giroscopio restituisce dati sulla velocità di rotazione per i tre assi delle coordinate. Questi valori dei dati vengono restituiti in un array float (values) insieme ad altri parametri SensorEvent. La Tabella 1 riepiloga i sensori di movimento disponibili sulla piattaforma Android.

Tabella 1. Sensori di movimento supportati sulla piattaforma Android.

Sensore Dati degli eventi dei sensori Descrizione Unità di misura
TYPE_ACCELEROMETER SensorEvent.values[0] Forza di accelerazione lungo l'asse x (inclusa la gravità). m/s2
SensorEvent.values[1] Forza di accelerazione lungo l'asse y (inclusa la gravità).
SensorEvent.values[2] Forza di accelerazione lungo l'asse z (inclusa la gravità).
TYPE_ACCELEROMETER_UNCALIBRATED SensorEvent.values[0] Accelerazione misurata lungo l'asse X senza alcuna compensazione del bias. m/s2
SensorEvent.values[1] Accelerazione misurata lungo l'asse Y senza alcuna compensazione del bias.
SensorEvent.values[2] Accelerazione misurata lungo l'asse Z senza alcuna compensazione del bias.
SensorEvent.values[3] Accelerazione misurata lungo l'asse X con compensazione della distorsione stimata.
SensorEvent.values[4] Accelerazione misurata lungo l'asse Y con compensazione della distorsione stimata.
SensorEvent.values[5] Accelerazione misurata lungo l'asse Z con compensazione della distorsione stimata.
TYPE_GRAVITY SensorEvent.values[0] Forza di gravità lungo l'asse x. m/s2
SensorEvent.values[1] Forza di gravità lungo l'asse Y.
SensorEvent.values[2] Forza di gravità lungo l'asse z.
TYPE_GYROSCOPE SensorEvent.values[0] Tasso di rotazione attorno all'asse x. rad/s
SensorEvent.values[1] Velocità di rotazione attorno all'asse Y.
SensorEvent.values[2] Velocità di rotazione attorno all'asse Z.
TYPE_GYROSCOPE_UNCALIBRATED SensorEvent.values[0] Tasso di rotazione (senza compensazione della deriva) attorno all'asse x. rad/s
SensorEvent.values[1] Velocità di rotazione (senza compensazione della deriva) attorno all'asse Y.
SensorEvent.values[2] Tasso di rotazione (senza compensazione della deriva) attorno all'asse z.
SensorEvent.values[3] Deriva stimata intorno all'asse x.
SensorEvent.values[4] Deriva stimata intorno all'asse Y.
SensorEvent.values[5] Deriva stimata attorno all'asse z.
TYPE_LINEAR_ACCELERATION SensorEvent.values[0] Forza di accelerazione lungo l'asse x (esclusa la gravità). m/s2
SensorEvent.values[1] Forza di accelerazione lungo l'asse y (esclusa la gravità).
SensorEvent.values[2] Forza di accelerazione lungo l'asse z (esclusa la gravità).
TYPE_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)).
SensorEvent.values[3] Componente scalare del vettore di rotazione ((cos(θ/2)).1
TYPE_SIGNIFICANT_MOTION N/D N/D N/D
TYPE_STEP_COUNTER SensorEvent.values[0] Numero di passi compiuti dall'utente dall'ultimo riavvio mentre il sensore era attivo. Passi
TYPE_STEP_DETECTOR N/D N/D N/D

1 Il componente scalare è un valore facoltativo.

Il sensore del vettore di rotazione e il sensore di gravità sono i sensori più utilizzati per il rilevamento e il monitoraggio del movimento. Il sensore del vettore di rotazione è particolarmente versatile e può essere utilizzato per un'ampia gamma di attività correlate al movimento, come il rilevamento di gesti, il monitoraggio della variazione angolare e il monitoraggio delle variazioni di orientamento relative. Ad esempio, il sensore di vettore di rotazione è ideale se stai sviluppando un gioco, un'applicazione di realtà aumentata, una bussola bidimensionale o tridimensionale o un'app di stabilizzazione della videocamera. Nella maggior parte dei casi, l'utilizzo di questi sensori è una scelta migliore rispetto all'utilizzo dell'accelerometro e del sensore di campo geomagnetico o del sensore di orientamento.

Sensori Android Open Source Project

Android Open Source Project (AOSP) fornisce tre sensori di movimento basati su software: un sensore di gravità, un sensore di accelerazione lineare e un sensore di vettore di rotazione. Questi sensori sono stati aggiornati in Android 4.0 e ora utilizzano il giroscopio del dispositivo (oltre ad altri sensori) per migliorare la stabilità e le prestazioni. Se vuoi provare questi sensori, puoi identificarli utilizzando il metodo getVendor() e il metodo getVersion() (il fornitore è Google LLC; il numero di versione è 3). L'identificazione di questi sensori in base al fornitore e al numero di versione è necessaria perché il sistema Android considera questi tre sensori come secondari. Ad esempio, se un produttore di dispositivi fornisce il proprio sensore di gravità, il sensore di gravità AOSP viene visualizzato come sensore di gravità secondario. Tutti e tre questi sensori si basano su un giroscopio: se un dispositivo non ha un giroscopio, questi sensori non vengono visualizzati e non sono disponibili per l'uso.

Utilizzare il sensore di gravità

Il sensore di gravità fornisce un vettore tridimensionale che indica la direzione e l'intensità della gravità. In genere, questo sensore viene utilizzato per determinare l'orientamento relativo del dispositivo nello spazio. Il seguente codice mostra come ottenere un'istanza del sensore di gravità predefinito:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);

Le unità sono le stesse utilizzate dal sensore di accelerazione (m/s2) e il sistema di coordinate è lo stesso utilizzato dal sensore di accelerazione.

Nota:quando un dispositivo è a riposo, l'output del sensore di gravità deve essere identico a quello dell'accelerometro.

Utilizzare l'accelerometro lineare

Il sensore di accelerazione lineare fornisce un vettore tridimensionale che rappresenta l'accelerazione lungo ogni asse del dispositivo, escluso quello della gravità. Puoi utilizzare questo valore per eseguire il rilevamento dei gesti. Il valore può essere utilizzato anche come input per un sistema di navigazione inerziale, che utilizza la navigazione stimata. Il seguente codice mostra come ottenere un'istanza del sensore di accelerazione lineare predefinito:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);

Dal punto di vista concettuale, questo sensore fornisce dati di accelerazione in base alla seguente relazione:

linear acceleration = acceleration - acceleration due to gravity

In genere, questo sensore viene utilizzato quando vuoi ottenere dati di accelerazione senza l'influenza della gravità. Ad esempio, potresti utilizzare questo sensore per vedere a che velocità sta andando la tua auto. Il sensore di accelerazione lineare ha sempre un offset che devi rimuovere. Il modo più semplice per farlo è integrare un passaggio di calibrazione nell'applicazione. Durante la calibrazione puoi chiedere all'utente di posizionare il dispositivo su un tavolo e poi leggere gli offset per tutti e tre gli assi. Puoi quindi sottrarre questo offset dalle letture dirette del sensore di accelerazione per ottenere l'accelerazione lineare effettiva.

Il sistema di coordinate del sensore è lo stesso utilizzato dal sensore di accelerazione, così come le unità di misura (m/s2).

Utilizzare il sensore del vettore di rotazione

Il vettore di rotazione rappresenta l'orientamento del dispositivo come combinazione di un angolo e un asse, in cui il dispositivo è ruotato di un angolo θ attorno a un asse (x, y o z). Il seguente codice mostra come ottenere un'istanza del sensore del vettore di rotazione predefinito:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);

I tre elementi del vettore di rotazione sono espressi come segue:

x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)

dove la magnitudo del vettore di rotazione è uguale a sin(θ/2) e la direzione del vettore di rotazione è uguale alla direzione dell'asse di rotazione.

Figura 1. Sistema di coordinate utilizzato dal sensore del vettore di rotazione.

I tre elementi del vettore di rotazione sono uguali alle ultime tre componenti di un quaternione unitario (cos(θ/2), x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)). Gli elementi del vettore di rotazione sono senza unità. Gli assi x, y e z sono definiti nello stesso modo del sensore di accelerazione. Il sistema di coordinate di riferimento è definito come una base ortonormale diretta (vedi figura 1). Questo sistema di coordinate ha le seguenti caratteristiche:

  • X è definito come il prodotto vettoriale Y x Z. È tangente al terreno nella posizione attuale del dispositivo e punta approssimativamente a est.
  • Y è tangente al terreno nella posizione attuale del dispositivo e punta verso il polo nord geomagnetico.
  • L'asse Z è rivolto verso il cielo ed è perpendicolare al piano del suolo.

Per un'applicazione di esempio che mostra come utilizzare il sensore del vettore di rotazione, consulta RotationVectorDemo.java.

Utilizzare il sensore di movimento significativo

Il sensore di movimento significativo attiva un evento ogni volta che viene rilevato un movimento significativo e poi si disattiva. Un movimento significativo è un movimento che potrebbe comportare un cambiamento nella posizione dell'utente, ad esempio camminare, andare in bicicletta o sedersi in un'auto in movimento. Il seguente codice mostra come ottenere un'istanza del sensore di movimento significativo predefinito e come registrare un listener di eventi:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val mSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION)
val triggerEventListener = object : TriggerEventListener() {
    override fun onTrigger(event: TriggerEvent?) {
        // Do work
    }
}
mSensor?.also { sensor ->
    sensorManager.requestTriggerSensor(triggerEventListener, sensor)
}

Java

private SensorManager sensorManager;
private Sensor sensor;
private TriggerEventListener triggerEventListener;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);

triggerEventListener = new TriggerEventListener() {
    @Override
    public void onTrigger(TriggerEvent event) {
        // Do work
    }
};

sensorManager.requestTriggerSensor(triggerEventListener, mSensor);

Per ulteriori informazioni, vedi TriggerEventListener.

Utilizzare il sensore contapassi

Il sensore contapassi fornisce il numero di passi compiuti dall'utente dall'ultimo riavvio mentre il sensore era attivo. Il contapassi ha una latenza maggiore (fino a 10 secondi), ma una precisione superiore rispetto al sensore di rilevamento dei passi.

Nota: devi dichiarare l'autorizzazione ACTIVITY_RECOGNITION affinché la tua app possa utilizzare questo sensore sui dispositivi con Android 10 (livello API 29) o versioni successive.

Il seguente codice mostra come ottenere un'istanza del sensore contapassi predefinito:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);

Per preservare la batteria dei dispositivi che eseguono la tua app, devi utilizzare la classe JobScheduler per recuperare il valore corrente dal sensore contapassi a un intervallo specifico. Sebbene diversi tipi di app richiedano intervalli di lettura dei sensori diversi, devi impostare questo intervallo il più lungo possibile, a meno che la tua app non richieda dati in tempo reale dal sensore.

Utilizzare il sensore di rilevamento dei passi

Il sensore di rilevamento dei passi attiva un evento ogni volta che l'utente fa un passo. La latenza dovrebbe essere inferiore a 2 secondi.

Nota: devi dichiarare l'autorizzazione ACTIVITY_RECOGNITION affinché la tua app possa utilizzare questo sensore sui dispositivi con Android 10 (livello API 29) o versioni successive.

Il seguente codice mostra come ottenere un'istanza del sensore di rilevamento dei passi predefinito:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);

Utilizzare i dati non elaborati

I seguenti sensori forniscono alla tua app dati non elaborati sulle forze lineari e rotazionali applicate al dispositivo. Per utilizzare in modo efficace i valori di questi sensori, devi filtrare i fattori ambientali, come la gravità. Potresti anche dover applicare un algoritmo di smoothing alla tendenza dei valori per ridurre il rumore.

Utilizzare l'accelerometro

Un sensore di accelerazione misura l'accelerazione applicata al dispositivo, inclusa la forza di gravità. Il seguente codice mostra come ottenere un'istanza del sensore di accelerazione predefinito:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)

Java

private SensorManager sensorManager;
private Sensor sensor;
  ...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

Nota: se la tua app ha come target Android 12 (livello API 31) o versioni successive, questo sensore è soggetto a limiti di frequenza.

Dal punto di vista concettuale, un sensore di accelerazione determina l'accelerazione applicata a un dispositivo (Ad) misurando le forze applicate al sensore stesso (Fs) utilizzando la seguente relazione:

A_D=-(1/mass)∑F_S

Tuttavia, la forza di gravità influenza sempre l'accelerazione misurata in base al seguente rapporto:

A_D=-g-(1/mass)∑F_S

Per questo motivo, quando il dispositivo è appoggiato su un tavolo (e non accelera), l'accelerometro legge una magnitudo di g = 9,81 m/s2. Allo stesso modo, quando il dispositivo è in caduta libera e quindi accelera rapidamente verso il suolo a 9,81 m/s2, il suo accelerometro legge una magnitudo di g = 0 m/s2. Pertanto, per misurare l'accelerazione reale del dispositivo, il contributo della forza di gravità deve essere rimosso dai dati dell'accelerometro. Ciò può essere ottenuto applicando un filtro passa alto. Al contrario, un filtro passa-basso può essere utilizzato per isolare la forza di gravità. Il seguente esempio mostra come puoi farlo:

Kotlin

override fun onSensorChanged(event: SensorEvent) {
    // In this example, alpha is calculated as t / (t + dT),
    // where t is the low-pass filter's time-constant and
    // dT is the event delivery rate.

    val alpha: Float = 0.8f

    // Isolate the force of gravity with the low-pass filter.
    gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0]
    gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1]
    gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2]

    // Remove the gravity contribution with the high-pass filter.
    linear_acceleration[0] = event.values[0] - gravity[0]
    linear_acceleration[1] = event.values[1] - gravity[1]
    linear_acceleration[2] = event.values[2] - gravity[2]
}

Java

public void onSensorChanged(SensorEvent event){
    // In this example, alpha is calculated as t / (t + dT),
    // where t is the low-pass filter's time-constant and
    // dT is the event delivery rate.

    final float alpha = 0.8;

    // Isolate the force of gravity with the low-pass filter.
    gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
    gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
    gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];

    // Remove the gravity contribution with the high-pass filter.
    linear_acceleration[0] = event.values[0] - gravity[0];
    linear_acceleration[1] = event.values[1] - gravity[1];
    linear_acceleration[2] = event.values[2] - gravity[2];
}

Nota:puoi utilizzare molte tecniche diverse per filtrare i dati dei sensori. L'esempio di codice riportato sopra utilizza una semplice costante di filtro (alpha) per creare un filtro passa-basso. Questa costante del filtro deriva da una costante di tempo (t), che è una rappresentazione approssimativa della latenza che il filtro aggiunge agli eventi del sensore, e dalla frequenza di distribuzione degli eventi del sensore (dt). L'esempio di codice utilizza un valore alfa di 0,8 a scopo dimostrativo. Se utilizzi questo metodo di filtraggio, potresti dover scegliere un valore alfa diverso.

Gli accelerometri utilizzano il sistema di coordinate standard del sensore. In pratica, ciò significa che quando un dispositivo è appoggiato in orizzontale su un tavolo nel suo orientamento naturale, si applicano le seguenti condizioni:

  • Se spingi il dispositivo sul lato sinistro (in modo che si sposti verso destra), il valore dell'accelerazione x è positivo.
  • Se spingi il dispositivo verso il basso (allontanandolo da te), il valore dell'accelerazione sull'asse Y è positivo.
  • Se spingi il dispositivo verso il cielo con un'accelerazione di A m/s2, il valore dell'accelerazione z è uguale ad A + 9,81, che corrisponde all'accelerazione del dispositivo (+A m/s2) meno la forza di gravità (-9,81 m/s2).
  • Il dispositivo fermo avrà un valore di accelerazione di +9,81, che corrisponde all'accelerazione del dispositivo (0 m/s2 meno la forza di gravità, che è -9,81 m/s2).

In generale, l'accelerometro è un buon sensore da utilizzare se monitori il movimento del dispositivo. Quasi tutti gli smartphone e i tablet Android sono dotati di un accelerometro, che consuma circa 10 volte meno energia rispetto agli altri sensori di movimento. Uno svantaggio è che potresti dover implementare filtri passa-basso e passa-alto per eliminare le forze gravitazionali e ridurre il rumore.

Utilizzare il giroscopio

Il giroscopio misura la velocità di rotazione in rad/s attorno agli assi x, y e z di un dispositivo. Il seguente codice mostra come ottenere un'istanza del giroscopio predefinito:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

Nota: se la tua app ha come target Android 12 (livello API 31) o versioni successive, questo sensore è soggetto a limiti di frequenza.

Il sistema di coordinate del sensore è lo stesso utilizzato per il sensore di accelerazione. La rotazione è positiva in senso antiorario, ovvero un osservatore che guarda da una posizione positiva sull'asse x, y o z un dispositivo posizionato sull'origine segnalerebbe una rotazione positiva se il dispositivo sembra ruotare in senso antiorario. Questa è la definizione matematica standard della rotazione positiva e non è la stessa della definizione di rollio utilizzata dal sensore di orientamento.

In genere, l'output del giroscopio viene integrato nel tempo per calcolare una rotazione che descrive la variazione degli angoli nel timestep. Ad esempio:

Kotlin

// Create a constant to convert nanoseconds to seconds.
private val NS2S = 1.0f / 1000000000.0f
private val deltaRotationVector = FloatArray(4) { 0f }
private var timestamp: Float = 0f

override fun onSensorChanged(event: SensorEvent?) {
    // This timestep's delta rotation to be multiplied by the current rotation
    // after computing it from the gyro sample data.
    if (timestamp != 0f && event != null) {
        val dT = (event.timestamp - timestamp) * NS2S
        // Axis of the rotation sample, not normalized yet.
        var axisX: Float = event.values[0]
        var axisY: Float = event.values[1]
        var axisZ: Float = event.values[2]

        // Calculate the angular speed of the sample
        val omegaMagnitude: Float = sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ)

        // Normalize the rotation vector if it's big enough to get the axis
        // (that is, EPSILON should represent your maximum allowable margin of error)
        if (omegaMagnitude > EPSILON) {
            axisX /= omegaMagnitude
            axisY /= omegaMagnitude
            axisZ /= omegaMagnitude
        }

        // Integrate around this axis with the angular speed by the timestep
        // in order to get a delta rotation from this sample over the timestep
        // We will convert this axis-angle representation of the delta rotation
        // into a quaternion before turning it into the rotation matrix.
        val thetaOverTwo: Float = omegaMagnitude * dT / 2.0f
        val sinThetaOverTwo: Float = sin(thetaOverTwo)
        val cosThetaOverTwo: Float = cos(thetaOverTwo)
        deltaRotationVector[0] = sinThetaOverTwo * axisX
        deltaRotationVector[1] = sinThetaOverTwo * axisY
        deltaRotationVector[2] = sinThetaOverTwo * axisZ
        deltaRotationVector[3] = cosThetaOverTwo
    }
    timestamp = event?.timestamp?.toFloat() ?: 0f
    val deltaRotationMatrix = FloatArray(9) { 0f }
    SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    // User code should concatenate the delta rotation we computed with the current rotation
    // in order to get the updated rotation.
    // rotationCurrent = rotationCurrent * deltaRotationMatrix;
}

Java

// Create a constant to convert nanoseconds to seconds.
private static final float NS2S = 1.0f / 1000000000.0f;
private final float[] deltaRotationVector = new float[4]();
private float timestamp;

public void onSensorChanged(SensorEvent event) {
    // This timestep's delta rotation to be multiplied by the current rotation
    // after computing it from the gyro sample data.
    if (timestamp != 0) {
      final float dT = (event.timestamp - timestamp) * NS2S;
      // Axis of the rotation sample, not normalized yet.
      float axisX = event.values[0];
      float axisY = event.values[1];
      float axisZ = event.values[2];

      // Calculate the angular speed of the sample
      float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);

      // Normalize the rotation vector if it's big enough to get the axis
      // (that is, EPSILON should represent your maximum allowable margin of error)
      if (omegaMagnitude > EPSILON) {
        axisX /= omegaMagnitude;
        axisY /= omegaMagnitude;
        axisZ /= omegaMagnitude;
      }

      // Integrate around this axis with the angular speed by the timestep
      // in order to get a delta rotation from this sample over the timestep
      // We will convert this axis-angle representation of the delta rotation
      // into a quaternion before turning it into the rotation matrix.
      float thetaOverTwo = omegaMagnitude * dT / 2.0f;
      float sinThetaOverTwo = sin(thetaOverTwo);
      float cosThetaOverTwo = cos(thetaOverTwo);
      deltaRotationVector[0] = sinThetaOverTwo * axisX;
      deltaRotationVector[1] = sinThetaOverTwo * axisY;
      deltaRotationVector[2] = sinThetaOverTwo * axisZ;
      deltaRotationVector[3] = cosThetaOverTwo;
    }
    timestamp = event.timestamp;
    float[] deltaRotationMatrix = new float[9];
    SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    // User code should concatenate the delta rotation we computed with the current rotation
    // in order to get the updated rotation.
    // rotationCurrent = rotationCurrent * deltaRotationMatrix;
}

I giroscopi standard forniscono dati di rotazione non elaborati senza alcun filtro o correzione per il rumore e la deriva (bias). In pratica, il rumore e la deriva del giroscopio introducono errori che devono essere compensati. In genere, la deriva (bias) e il rumore vengono determinati monitorando altri sensori, ad esempio il sensore di gravità o l'accelerometro.

Utilizzare il giroscopio non calibrato

Il giroscopio non calibrato è simile al giroscopio, tranne per il fatto che non viene applicata alcuna compensazione della deriva giroscopica alla velocità di rotazione. La calibrazione di fabbrica e la compensazione della temperatura vengono comunque applicate alla velocità di rotazione. Il giroscopio non calibrato è utile per la post-elaborazione e la fusione dei dati di orientamento. In generale, gyroscope_event.values[0] sarà vicino a uncalibrated_gyroscope_event.values[0] - uncalibrated_gyroscope_event.values[3]. ovvero

calibrated_x ~= uncalibrated_x - bias_estimate_x

Nota:i sensori non calibrati forniscono risultati più grezzi e potrebbero includere alcuni bias, ma le loro misurazioni contengono meno salti dovuti alle correzioni applicate tramite la calibrazione. Alcune applicazioni potrebbero preferire questi risultati non calibrati perché più fluidi e affidabili. Ad esempio, se un'applicazione tenta di eseguire la propria fusione dei sensori, l'introduzione di calibrazioni può effettivamente distorcere i risultati.

Oltre alle velocità di rotazione, il giroscopio non calibrato fornisce anche la deriva stimata intorno a ogni asse. Il seguente codice mostra come ottenere un'istanza del giroscopio non calibrato predefinito:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);

Altri esempi di codice

L'esempio BatchStepSensor mostra ulteriormente l'utilizzo delle API trattate in questa pagina.

Ti consigliamo di leggere anche