Android getOrientation Azimuth se contamina cuando el teléfono está inclinado

Estoy teniendo un problema realmente molesto con una vista de AR que actúa como una brújula. Entonces, cuando sostengo el teléfono en posición vertical (para que la pantalla apunte a mi cara), entonces llamo al sistema remapCoordinateSystem que el tono es 0 cuando lo mantienes en posición vertical. Entonces el azimut (funcionalidad de la brújula) es perfecto, pero tan pronto como incline el teléfono, el acimut se arruina, si me inclino hacia adelante, el acimut aumenta y si me doblo hacia atrás, disminuye.

Uso 2 sensores para obtener las lecturas Sensor.TYPE_MAGNETIC_FIELD y Sensor.TYPE_GRAVITY .

Yo uso un filtro de paso bajo que es bastante básico, está implementado con una constante alfa y se usa directamente en los valores de lectura de los sensores.

Aquí está mi código:

 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); 

Como ven, no hay magia aquí. Yo llamo a este fragmento de código cuando los valores gravitymeterValues ​​y los valores del magnetómetro están listos para ser utilizados.

Mi pregunta es ¿cómo evito que el azimut se vuelva loco cuando inclino el teléfono?

Revisé una aplicación gratuita en Google Play Store, Compass y no resolvió este problema, pero espero que haya una solución.

Tengo 2 soluciones en mente:

  1. Haga que la vista AR solo funcione en angularjs de afinación muy restringidos, ahora tengo algo como pitch >= -5 && pitch <= 30 . Si esto no se cumple, se le muestra una pantalla que le pide al usuario que gire el teléfono hacia el retrato.

  2. De alguna manera utilizo el tono para suprimir el azimut, aunque parece una solución bastante específica de dispositivo, pero, por supuesto, estoy abierto a sugerencias.

También puedo agregar que he estado buscando un par de horas para encontrar una solución decente y que no he encontrado ninguna que me haya dado mejores soluciones que 2) aquí.

¡Gracias por adelantado!

Para ver el código completo, consulte https://github.com/hoananguyen/dsensor
Mantenga un historial y promedie, no conozco la interpretación correcta de tono y rollo, por lo que el siguiente código es solo para acimut.

Miembros de la clase

 private List mRotHist = new ArrayList(); 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 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; }