2015-01-08 14 views
9

Ich habe eine App, die Orientierungsdaten verwendet, die sehr gut mit der pre API-8-Methode der Verwendung eines Sensor.TYPE_ORIENTAITON funktioniert. Das Glätten dieser Daten war relativ einfach.Wie bekomme ich glatte Orientierung Daten in Android

Ich versuche, den Code zu aktualisieren, um die Verwendung dieses veralteten Ansatzes zu vermeiden. Der neue Standardansatz besteht darin, die einzige Sensor.TYPE_ORIENTATION durch eine Sensor.TYPE_ACCELEROMETER und Sensor.TYPE_MAGENTIC_FIELD Kombination zu ersetzen. Wenn diese Daten empfangen werden, wird sie (über SensorManager.getRotationMatrix()) an SensorManager.getOrientation() gesendet. Dies liefert (theoretisch) die gleichen Informationen wie Sensor.TYPE_ORIENTATION (abgesehen von verschiedenen Einheiten und Achsenausrichtung).

Dieser Ansatz scheint jedoch Daten zu erzeugen, die viel nervöser sind als die veraltete Methode (die immer noch funktioniert). Wenn Sie also dieselben Informationen auf demselben Gerät vergleichen, bietet die veraltete Methode wesentlich weniger verrauschte Daten als die aktuelle Methode.

Wie bekomme ich die gleichen (weniger lauten) Daten, die die veraltete Methode verwendet?

Um meine Frage ein wenig klarer zu machen: Ich habe verschiedene Antworten zu diesem Thema gelesen, und ich habe alle Arten von Filter ausprobiert: einfache KF/IIR Tiefpass wie Sie vorschlagen; Median-Filter zwischen 5 und 19 Punkten, aber bis jetzt habe ich noch nie annähernd die Glätte der Daten erreicht, die das Telefon über TYPE_ORIENTATION liefert.

+1

Sie benötigen Tiefpass-Filter sowie Durchschnitt eine Geschichte.Wenn Sie TYPE_ORIENTATION und meine Antwort unter http://stackoverflow.com/questions/17979238/android-getorientation-azimuth-gets-polluted-when-phone-is-tilt/17981374#17981374 vergleichen, werden Sie sehen, dass sie ziemlich gleich sind . –

Antwort

6

Wenden Sie einen Tiefpassfilter auf Ihren Sensorausgang an.

Das ist meine Tiefpassfiltermethode:

private static final float ALPHA = 0.5f; 
//lower alpha should equal smoother movement 
... 
private float[] applyLowPassFilter(float[] input, float[] output) { 
    if (output == null) return input; 

    for (int i=0; i<input.length; i++) { 
     output[i] = output[i] + ALPHA * (input[i] - output[i]); 
    } 
    return output; 
} 

es Nehmen wie so:

float[] mGravity; 
float[] mGeomagnetic; 
@Override 
public void onSensorChanged(SensorEvent event) { 
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) 
     mGravity = applyLowPassFilter(event.values.clone(), mGravity); 
    if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) 
     mGeomagnetic = applyLowPassFilter(event.values.clone(), mGeomagnetic); 
    if (mGravity != null && mGeomagnetic != null) { 
     float R[] = new float[9]; 
     float I[] = new float[9]; 

     boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic); 
     if (success) { 
      float orientation[] = new float[3]; 
      SensorManager.getOrientation(R, orientation); 
      azimuth = -orientation[0]; 
      invalidate(); 
     } 
    } 
} 

Dieser für einen Kompass offensichtlich Code ist, entfernen Sie, was Sie nicht brauchen.

Werfen Sie auch einen Blick auf diese Frage SE How to implement low pass filter using java

+0

Sorry, ich hätte meine Frage ein wenig klarer machen sollen. Ich habe die anderen verschiedenen Antworten zu diesem Thema gelesen, und ich habe alle Arten von Filtern ausprobiert: einfacher KF/IIR Tiefpass, wie Sie vorschlagen; Median-Filter zwischen 5 und 19 Punkten, aber bis jetzt habe ich noch nie annähernd die Glätte der Daten erreicht, die das Telefon über TYPE_ORIENTATION liefert. –

+0

@NeilTownsend, hast du versucht, mit der 'ALPHA'-Variable in meiner Tiefpass-Filter-Methode zu verfahren? Vielleicht suchen Sie einfach nach einem anderen "ALPHA" (probieren Sie zum Beispiel 0.15f weiter), was einer unruhigeren oder weicheren Linie entspricht. Wenn das nicht die Information war, nach der Sie gesucht haben, entschuldige ich mich aufrichtig. – MeetTitan

+0

Ja, ich habe von 0,1 bis 0,9 probiert. Es ist entweder zu lückenhaft oder zu nervös, es kommt nicht in die Nähe der Daten von TYPE_ORIENTATION. –

1

Es stellt sich heraus, dass es eine andere, nicht besonders dokumentiert, Art und Weise Orientierungsdaten zu erhalten. In der Liste der Sensortypen ist TYPE_ROTATION_VECTOR ausgeblendet. So stellen einen bis:

Sensor mRotationVectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); 
sensorManager.registerListener(this, mRotationVectorSensor, SensorManager.SENSOR_DELAY_GAME); 

Dann:

@Override 
public void onSensorChanged(SensorEvent event) { 
    final int eventType = event.sensor.getType(); 

    if (eventType != Sensor.TYPE_ROTATION_VECTOR) return; 

    long timeNow   = System.nanoTime(); 

    float mOrientationData[] = new float[3]; 
    calcOrientation(mOrientationData, event.values.clone()); 

    // Do what you want with mOrientationData 
} 

Der Schlüsselmechanismus wird von den ankommenden Rotationsdaten an einen Orientierungsvektor über eine Rotationsmatrix gehen. Das etwas frustrierende daran ist, dass der Orientierungsvektor in erster Linie von Quaternion-Daten kommt, aber ich kann nicht sehen, wie man die Quaternion direkt liefert. (Wenn Sie sich jemals gefragt, wie quaternions beziehen sich auf orientatin und Rotationsinformation, und warum sie verwendet werden, finden here.)

private void calcOrientation(float[] orientation, float[] incomingValues) { 
    // Get the quaternion 
    float[] quatF = new float[4]; 
    SensorManager.getQuaternionFromVector(quatF, incomingValues); 

    // Get the rotation matrix 
    // 
    // This is a variant on the code presented in 
    // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/ 
    // which has been altered for scaling and (I think) a different axis arrangement. It 
    // tells you the rotation required to get from the between the phone's axis 
    // system and the earth's. 
    // 
    // Phone axis system: 
    // https://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords 
    // 
    // Earth axis system: 
    // https://developer.android.com/reference/android/hardware/SensorManager.html#getRotationMatrix(float[], float[], float[], float[]) 
    // 
    // Background information: 
    // https://en.wikipedia.org/wiki/Rotation_matrix 
    // 
    float[][] rotMatF = new float[3][3]; 
    rotMatF[0][0] = quatF[1]*quatF[1] + quatF[0]*quatF[0] - 0.5f; 
    rotMatF[0][1] = quatF[1]*quatF[2] - quatF[3]*quatF[0]; 
    rotMatF[0][2] = quatF[1]*quatF[3] + quatF[2]*quatF[0]; 
    rotMatF[1][0] = quatF[1]*quatF[2] + quatF[3]*quatF[0]; 
    rotMatF[1][1] = quatF[2]*quatF[2] + quatF[0]*quatF[0] - 0.5f; 
    rotMatF[1][2] = quatF[2]*quatF[3] - quatF[1]*quatF[0]; 
    rotMatF[2][0] = quatF[1]*quatF[3] - quatF[2]*quatF[0]; 
    rotMatF[2][1] = quatF[2]*quatF[3] + quatF[1]*quatF[0]; 
    rotMatF[2][2] = quatF[3]*quatF[3] + quatF[0]*quatF[0] - 0.5f; 

    // Get the orientation of the phone from the rotation matrix 
    // 
    // There is some discussion of this at 
    // http://stackoverflow.com/questions/30279065/how-to-get-the-euler-angles-from-the-rotation-vector-sensor-type-rotation-vecto 
    // in particular equation 451. 
    // 
    final float rad2deg = (float)(180.0/PI); 
    orientation[0] = (float)Math.atan2(-rotMatF[1][0], rotMatF[0][0]) * rad2deg; 
    orientation[1] = (float)Math.atan2(-rotMatF[2][1], rotMatF[2][2]) * rad2deg; 
    orientation[2] = (float)Math.asin (rotMatF[2][0])    * rad2deg; 
    if (orientation[0] < 0) orientation[0] += 360; 
} 

Dies scheint Daten sehr ähnlich Gefühl zu geben (ich habe nicht numerischen Tests ausführen) zu die alten TYPE_ORIENTATION Daten: Es war für die Bewegungssteuerung des Geräts mit marginalen Filterung verwendbar.

Es gibt auch hilfreiche Informationen here, und eine mögliche alternative Lösung here.

+0

Ich habe auch diese Methode untersucht. Seien Sie müde, wenn Sie Gyroskopdaten anstelle von Beschleunigungsmesserdaten verwenden (sie sind anders!). Gyroskope sind nach meinem besten Wissen nicht in so vielen Geräten. – MeetTitan

+0

Das ist interessant, weil etwa die Hälfte der Antworten auf dieses Problem auf 'TYPE_GRAVITY' angewiesen sind, die nach diesem http://davidcrowley.me/?tag=type_rotation_vector (etwa 16 Minuten in) ebenfalls von Gyroskopdaten abhängig sind, und niemandem scheint zu erwähnen, dass es diese Beschränkung gibt. Ich werde weiter jagen! –

+0

Nun, soweit ich weiß, ist der Beschleunigungssensor in jedem Gerät, während das Gyroskop OEM-abhängig ist. Könnte nicht schlecht sein, wenn Sie ein Gyroskop haben, aber Sie würden ein Problem haben, wenn jemand nicht. – MeetTitan