2012-06-17 12 views
12

Come si ottiene un vettore di direzione che rappresenta la direzione verso cui è rivolto il retro del dispositivo rispetto alle coordinate della terra?Ottenere il vettore direzione in Android

Ad esempio, se posizionato su una scrivania (con lo schermo rivolto verso l'alto) dovrebbe leggere [0,0, -1] e se tenuto verticalmente rivolto verso nord dovrebbe leggere [1,0,0], ecc.

So come calcolarlo da titolo, intonazione e rotazione, purché siano relativi alle coordinate terrestri. Per essere chiari qui non sto cercando la velocità angolare, ma l'angolo attuale della corrente rispetto al piano tangente alla terra. Quindi se il dispositivo è tenuto verticalmente e rivolto verso nord, l'angolo "alpha" dovrebbe leggere 0 o 360, l'angolo "beta" dovrebbe leggere 90, e "gamma" dovrebbe leggere 0. Non riesco a capire come ottenere questi valori o.

Ho letto l'API tutto il giorno e ancora non riesco a trovare come ottenere una di queste cose.

public void onSensorChanged(SensorEvent event) { 
    // ?  
} 

Grazie per eventuali approfondimenti.

risposta

9

SensorManager.getRotationMatrix() fa ciò che è descritto di seguito, scritto prima di averlo scoperto. Lascerò la spiegazione aggiunta, perché se vuoi correggere la differenza tra il nord magnetico e il reale, ne avrai ancora bisogno.

L'algoritmo approssimativo è quello di ottenere la matrice di rotazione, moltiplicare il vettore [0,0,-1] da esso, quindi regolare questo al sistema di coordinate. Perché? Documenti per Android danno i sistemi di coordinate per i dispositivi e mondo

deviceworld

Nota [0,0,-1] in coords dispositivi Android punti perpendicolari all'indietro lontano dallo schermo. Se moltiplichi la matrice di rotazione R con questo vettore, otterrai [0,0,-1] nelle coordie mondiali quando il dispositivo è sul tavolo rivolto verso l'alto come desideri. Quando è rivolto verso nord, otterrai [0,-1,0], che indica che hai scelto un sistema di coordinate in cui i valori di x e vengono scambiati rispetto al sistema Android, ma si tratta semplicemente di un cambio di convenzioni.

Nota R * [0,0,-1]^T è solo la terza colonna di R negata. Da questo ottengo lo pseudocodice:

getRotationMatrix(R); 
Let v = first three elements of third column of R. 
swap v[0] and v[1] 

Questo dovrebbe ottenere quello che vuoi.

Ulteriori informazioni su cosa fa getRotationMatrix() come segue.


Sono necessari entrambi i dati dell'accerometro per stabilire la direzione "in basso" ei dati del magnetometro per determinare la direzione "nord". Dovrai supporre che gli accelerometri percepiscano solo la gravità (il dispositivo è fermo o si muove a velocità costante). Quindi è necessario proiettare il vettore del magnetometro sul piano perpendicolare al vettore di gravità (poiché il campo magnetico non è generalmente tangente alla superficie terrestre). Questo ti dà due assi. Il terzo è ortogonale, quindi può essere calcolato dal prodotto incrociato. Questo ti dà i vettori di coordinate terrestri nel sistema del dispositivo. Sembra che tu voglia l'inverso: coordinate del dispositivo in coordinate terrestri. Per questo basta costruire la matrice dei coseni di direzione e invertirli.

Aggiungo che la discussione precedente presuppone che il vettore del magnetometro punti a nord. Penso (dalla scienza del liceo!) è in realtà verso il sud magnetico, ma non ha alcun dispositivo a portata di mano, quindi non può provarlo. Ovviamente il nord/sud magnetico è diverso dal vero da zero a 180 gradi a seconda di dove sei sulla terra. È possibile recuperare le coordinate GPS e calcolare un offset reale.

Se non si ha familiarità con la matematica necessaria per fare queste, posso spiegare ulteriormente, ma dovrà essere in seguito.

+0

Grazie tanto, questo è davvero utile. Entrambi avete dato risposte così buone, vorrei che ci fosse un modo per selezionarle entrambe. Sembra che io possa ottenere il vettore gravitazionale con solo Sensor.TYPE_GRAVITY - hanno fatto quel lavoro per me. Vorrei che fosse altrettanto facile ottenere un vettore "nord". – Joey

+0

Ho finito per ottenerlo usando la tua spiegazione. Risulta che passo i valori restituiti da un sensore di gravità e un sensore di campo magnetico direttamente in getRotationMatrix() e funziona tutto fuori. Quindi ho semplicemente moltiplicato quella matrice per [0,0, -1] e ottenuto il dv. – Joey

8

Leggere questa pagina: http://developer.android.com/reference/android/hardware/Sensor.html

In API 8 e superiore, vi sono sensori "virtuali" che vengono generati combinando gli ingressi di tutti i sensori disponibili e filtri appropriati. Il sensore "TYPE_ORIENTATION" fornisce l'orientamento completo del dispositivo, ma questa interfaccia è deprecata a causa di stati di errore in alcuni orientamenti. Il nuovo sensore è TYPE_ROTATION_VECTOR (API 9 e successive) che fornisce l'orientamento del dispositivo come un quaternione. Questo è davvero il miglior sensore da usare, ma la matematica dietro è un po 'pesante.

In caso contrario, ciò che viene chiamato SensorManager.getRotationMatrix(), passando gli ultimi dati di gravità e magnetometro. Ciò restituirà una matrice di rotazione che potrebbe essere utilizzata per convertire un vettore dalle coordinate del dispositivo alle coordinate del mondo o viceversa (basta trasporre la matrice per invertirla).

La funzione getOrientation() può fornire intestazione, inclinazione e rotazione, ma questi hanno gli stessi stati di errore del sensore TYPE_ORIENTATION.

Examples: 

    Device flat on a table, top facing north: 
    1 0 0 
    0 1 0 
    0 0 1 

    Tilted up 30 degrees (rotated about X axis) 
    1 0  0 
    0 0.86 -0.5 
    0 0.5 0.86 

    Device vertical (rotated about X axis), facing north: 
    1 0 0 
    0 0 -1 
    0 1 0 

    Device flat on a table, top facing west: 
    0 -1 0 
    1 0 0 
    0 0 1 

    Device rotated about its Y axis, onto its left side, top 
    facing north: 
    0 0 -1 
    0 1 0 
    1 0 0 

Ecco qualche esempio di codice si possono trovare utili:

public void onSensorChanged(SensorEvent event) { 
    long now = event.timestamp;  // ns 

    switch(event.sensor.getType()) { 
     case Sensor.TYPE_ACCELEROMETER: 
     gData[0] = event.values[0]; 
     gData[1] = event.values[1]; 
     gData[2] = event.values[2]; 
     break; 
     case Sensor.TYPE_MAGNETIC_FIELD: 
     mData[0] = event.values[0]; 
     mData[1] = event.values[1]; 
     mData[2] = event.values[2]; 
     haveData = true; 
     break; 
    } 

    if(haveData) { 
     double dt = (now - last_time) * .000000001; 

     SensorManager.getRotationMatrix(R1, Imat, gData, mData); 
     getOrientation(R1, orientation); 
     pfdView.newOrientation(orientation[2], (float)dt); 


     Log.d(TAG, "yaw: " + (int)(orientation[0]*DEG)); 
     Log.d(TAG, "pitch: " + (int)(orientation[1]*DEG)); 
     Log.d(TAG, "roll: " + (int)(orientation[2]*DEG)); 

     acft.compass = orientation[0]; 

     last_time = now; 
    } 
} 
+0

Grazie per la tua risposta - Ho solo due domande, se uso TYPE_ROTATION_VECTOR, è il quanternion espresso in coordinate del mondo? È già integrato o devo integrarlo per ottenere il vettore corrente? – Joey

+0

Coordinate del mondo del dispositivo, credo. Già convertito in un quaternione di unità e talvolta il termine w è omesso. Ad essere onesti, non l'ho mai usato da solo. Il codice che mostro sopra è da un'app di navigazione di volo che ho scritto per 1.5, prima che fossero disponibili i nuovi tipi di sensori. –

Problemi correlati