2013-07-31 16 views
8

Sto avendo un problema davvero fastidioso con una visualizzazione AR che agisce come una bussola. Quindi quando tengo il telefono in verticale (in modo che lo schermo sia puntato al mio viso), chiamo il remapCoordinateSystem che il pitch è 0 quando lo reggi ritratto. Quindi l'azimut (funzionalità bussola) è perfetto, ma non appena inclino il telefono l'azimut viene rovinato, se mi piego in avanti l'azimut aumenta e se mi piego all'indietro diminuisce.Android getOrientation Azimuth viene inquinato quando il telefono è inclinato

Io uso 2 sensori per ottenere le letture, Sensor.TYPE_MAGNETIC_FIELD e Sensor.TYPE_GRAVITY.

Io uso un filtro passa basso che è piuttosto semplice, è implementato con una costante alpha e viene utilizzato direttamente sui valori letti dai sensori.

Ecco il mio codice:

float[] rotationMatrix = new float[9]; 
SensorManager.getRotationMatrix(rotationMatrix, null, gravitymeterValues, 
    magnetometerValues); 

float[] remappedRotationMatrix = new float[9]; 

SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_X, 
    SensorManager.AXIS_Z, remappedRotationMatrix); 

float results[] = new float[3]; 
SensorManager.getOrientation(remappedRotationMatrix, results); 

float azimuth = (float) (results[0] * 180/Math.PI); 
if (azimuth < 0) { 
    azimuth += 360; 
} 

float pitch = (float) (results[1] * 180/Math.PI); 
float roll = (float) (results[2] * 180/Math.PI); 

Come si vede non c'è nessuna magia qui. Chiamo questo pezzo di codice quando GravitymeterValues ​​e il magnetometroValues ​​sono pronti per essere usati.

La mia domanda è: come posso impedire all'azimut di impazzire quando inclino il telefono?

Ho controllato un'applicazione gratuita su Google Play Store, Compass e non ha risolto questo problema, ma spero che ci sia una soluzione.

ho 2 soluzioni in mente:

  1. rendere la visualizzazione AR funzionano solo in angoli di beccheggio molto constrainted, in questo momento ho qualcosa di simile pitch >= -5 && pitch <= 30. Se questo non viene completato, all'utente viene visualizzata una schermata che gli chiede di ruotare il telefono in verticale.

  2. In qualche modo utilizzare il pitch per sopprimere l'azimuth, questa però sembra una soluzione piuttosto specifica per il dispositivo, ma ovviamente sono aperto a suggerimenti.

Posso anche aggiungere che ho cercato per un paio d'ore per una soluzione decente e non ho trovato alcun che mi ha dato tutte le soluzioni migliori rispetto a 2) qui.

Grazie in anticipo!

+0

ciò che si fa filtro passa-basso? Non è necessario filtrare quando si utilizza TYPE_GRAVITY. –

+0

@HoanNguyen Devo passare in basso il 'Sensor.TYPE_MAGNETIC_FIELD'? Ora ho passato sia "Sensor.TYPE_MAGNETIC_FIELD' che" Sensor.TYPE_GRAVITY ". –

+1

No, l'uso del filtro passa-basso serve a rimuovere l'accelerazione nella direzione x an. GetRotationMatrix richiedeva che il terzo parametro fosse approssimativamente l'accelerazione nella sola direzione z.Se utilizzi TYPE_GRAVItY non è necessario il filtro passa-basso, ma molti dispositivi non hanno TYPE_GRAVITY, quindi è necessario utilizzare TYPE_ACCELEROMETER e il filtro passa-basso per i dispositivi che non dispongono di TYPE_GRAVITY. –

risposta

14

Per il codice completo vedere https://github.com/hoananguyen/dsensor
mantenere una cronologia e media fuori, non so la corretta interpretazione di beccheggio e rollio in modo che il codice riportato di seguito è solo per azimut.

I membri della classe

private List<float[]> mRotHist = new ArrayList<float[]>(); 
private int mRotHistIndex; 
// Change the value so that the azimuth is stable and fit your requirement 
private int mHistoryMaxLength = 40; 
float[] mGravity; 
float[] mMagnetic; 
float[] mRotationMatrix = new float[9]; 
// the direction of the back camera, only valid if the device is tilted up by 
// at least 25 degrees. 
private float mFacing = Float.NAN; 

public static final float TWENTY_FIVE_DEGREE_IN_RADIAN = 0.436332313f; 
public static final float ONE_FIFTY_FIVE_DEGREE_IN_RADIAN = 2.7052603f; 

onSensorChanged

@Override 
public void onSensorChanged(SensorEvent event) 
{ 
    if (event.sensor.getType() == Sensor.TYPE_GRAVITY) 
    { 
     mGravity = event.values.clone(); 
    } 
    else 
    { 
     mMagnetic = event.values.clone(); 
    } 

    if (mGravity != null && mMagnetic != null) 
    { 
      if (SensorManager.getRotationMatrix(mRotationMatrix, null, mGravity, mMagnetic)) 
      { 
       // inclination is the degree of tilt by the device independent of orientation (portrait or landscape) 
       // if less than 25 or more than 155 degrees the device is considered lying flat 
       float inclination = (float) Math.acos(mRotationMatrix[8]); 
       if (inclination < TWENTY_FIVE_DEGREE_IN_RADIAN 
         || inclination > ONE_FIFTY_FIVE_DEGREE_IN_RADIAN) 
       { 
        // mFacing is undefined, so we need to clear the history 
        clearRotHist(); 
        mFacing = Float.NaN; 
       } 
       else 
       { 
        setRotHist(); 
        // mFacing = azimuth is in radian 
        mFacing = findFacing(); 
       } 
      } 
    } 
} 

private void clearRotHist() 
{ 
    if (DEBUG) {Log.d(TAG, "clearRotHist()");} 
    mRotHist.clear(); 
    mRotHistIndex = 0; 
} 

private void setRotHist() 
{ 
    if (DEBUG) {Log.d(TAG, "setRotHist()");} 
    float[] hist = mRotationMatrix.clone(); 
    if (mRotHist.size() == mHistoryMaxLength) 
    { 
     mRotHist.remove(mRotHistIndex); 
    } 
    mRotHist.add(mRotHistIndex++, hist); 
    mRotHistIndex %= mHistoryMaxLength; 
} 

private float findFacing() 
{ 
    if (DEBUG) {Log.d(TAG, "findFacing()");} 
    float[] averageRotHist = average(mRotHist); 
    return (float) Math.atan2(-averageRotHist[2], -averageRotHist[5]); 
} 

public float[] average(List<float[]> values) 
{ 
    float[] result = new float[9]; 
    for (float[] value : values) 
    { 
     for (int i = 0; i < 9; i++) 
     { 
      result[i] += value[i]; 
     } 
    } 

    for (int i = 0; i < 9; i++) 
    { 
     result[i] = result[i]/values.size(); 
    } 

    return result; 
} 
+0

Grandi cose! Ora il mio azimut è molto meglio, solo il problema ora è nei movimenti rapidi che salta molto, ma suppongo che tu debba convivere con quello. Grazie mille per il tuo codice. Ancora una domanda: quale frequenza di aggiornamento sono i tuoi sensori? UI/Gioco/quale? Grazie –

+2

Io uso Normale ed è abbastanza buono per il mio scopo. –

+0

Stai usando questo codice in un'app bussola? Uso l'interfaccia utente ed è molto frequente, ho ottimi risultati con il tuo codice! –

Problemi correlati