2010-11-13 10 views
14

Hay 2 formas de obtener los 3 valores de rotación (acimut, paso, balanceo).Diferentes valores entre los sensores TYPE_ACCELEROMETER/TYPE_MAGNETIC_FIELD y TYPE_ORIENTATION

Uno es registrar un oyente de un tipo TYPE_ORIENTATION. Es la forma más fácil y me da una correcta gama de valores de cada rotación, como dice la documentación: acimut: [0, 359] lanzamiento: [-180, 180] roll: [-90, 90]

El otro, el más preciso y complejo de entender la primera vez que lo ves. Android lo recomienda, así que quiero usarlo, pero obtengo valores diferentes.

azimuth: [-180, 180]. -180/180 es S, 0 i N, 90 E y -90 W.
tono: [-90, 90]. 90 es 90, -90 es -90, 0 es 0 pero -180/180 (acostado con la pantalla hacia abajo) es 0.
rollo: [-180, 180].

Debería obtener los mismos valores pero con decimales, ¿verdad?

Tengo el siguiente código:

aValues = new float[3]; 
mValues = new float[3]; 

sensorListener = new SensorEventListener(){ 
    public void onSensorChanged (SensorEvent event){ 
     switch (event.sensor.getType()){ 
      case Sensor.TYPE_ACCELEROMETER: 
       aValues = event.values.clone(); 
       break; 
      case Sensor.TYPE_MAGNETIC_FIELD: 
       mValues = event.values.clone(); 
       break; 
     } 

     float[] R = new float[16]; 
     float[] orientationValues = new float[3]; 

     SensorManager.getRotationMatrix (R, null, aValues, mValues); 
     SensorManager.getOrientation (R, orientationValues); 

     orientationValues[0] = (float)Math.toDegrees (orientationValues[0]); 
     orientationValues[1] = (float)Math.toDegrees (orientationValues[1]); 
     orientationValues[2] = (float)Math.toDegrees (orientationValues[2]); 

     azimuthText.setText ("azimuth: " + orientationValues[0]); 
     pitchText.setText ("pitch: " + orientationValues[1]); 
     rollText.setText ("roll: " + orientationValues[2]); 
    } 

    public void onAccuracyChanged (Sensor sensor, int accuracy){} 
}; 

por favor ayuda. Es muy frustrante

¿Tengo que tratar con esos valores o estoy haciendo algo mal?

Gracias.

+1

He estado trabajando en esto yo misma por aproximadamente 2 semanas. Tu código se ve como debería (según la documentación que he podido encontrar), pero como has notado, no coincide con los resultados del sensor TYPE_ORIENTATION. Parecía una cosa simple comprobar los valores de orientación [0] para un valor negativo y agregarlo. Pero eso no acaba de hacer. No muestra la frecuencia de las actualizaciones de sus sensores. Descubrí que las actualizaciones más rápidas producen mejores resultados, aunque los resultados de TYPE_ORIENTATION parecen ser bastante estables. Si desea trabajar en conjunto, póngase en contacto con [email protected] –

+0

Todos los sensores de velocidad son GAME, pero no creo que el problema esté relacionado con la velocidad. Es extraño porque todos los blogs y foros (¡y el libro que estoy leyendo!) Implementan los sensores de la misma manera. –

+2

Bueno, todo el mundo dice cómo usar el acelerómetro y el campo magnético, pero nadie dice que los valores devueltos SON DIFERENTES de TYPE_ORIENTATION, incluso la documentación oficial. Buen trabajo, gente. Esperando una respuesta ... –

Respuesta

1

Falta un cálculo crítico en sus cálculos.
La llamada remapCoordinateSystem después de hacer una getRotationMatrix.

Agregue eso a su código y todo estará bien.
Puede leer más al respecto here.

6

Podría estar disparando en la oscuridad aquí, pero si entiendo su pregunta correctamente, se está preguntando por qué obtiene [-179..179] en lugar de [0..360]?

Nota que -180 es la misma que +180 y el mismo que 180 + N*360 donde N es un número entero (integer).

En otras palabras, si usted quiere conseguir los mismos números que con el sensor de orientación se puede hacer esto:

// x = orientationValues[0]; 
// y = orientationValues[1]; 
// z = orientationValues[2]; 
x = (x + 360.0) % 360.0; 
y = (y + 360.0) % 360.0; 
z = (z + 360.0) % 360.0; 

Esto le dará los valores en el rango [0..360] como lo hubiera querido.

+0

No puedo recordar cómo resolví el problema xD, ¡pero +1 para ti! : P –

21

Sé que estoy reproduciendo thread nigromante aquí, pero he estado trabajando en esto mucho últimamente, así que pensé en agregar mi 2 ¢.

El dispositivo no contiene brújula ni inclinómetros, por lo que no mide el acimut, el cabeceo o el balanceo directamente. (Llamamos a esos ángulos de Euler, por cierto). En cambio, usa acelerómetros y magnetómetros, que producen vectores XYZ de 3 espacios. Estos se utilizan para calcular los valores de acimut, etc.

vectores están en el dispositivo de espacio de coordenadas:

Device coordinates

coordenadas mundo han Y mirando al norte, X mirando hacia el este, y Z hacia arriba:

World coordinates

Por lo tanto, de un dispositivo " la orientación "neutral" está acostada boca arriba sobre una mesa, con la parte superior del dispositivo hacia el norte.

El acelerómetro produce un vector en la dirección "ARRIBA". El magnetómetro produce un vector en la dirección "norte". (Tenga en cuenta que en el hemisferio norte, esto tiende a apuntar hacia abajo debido a magnetic dip.)

El vector de acelerómetro y vector magnetómetro se pueden combinar matemáticamente a través de SensorManager.getRotationMatrix() que devuelve una matriz de 3x3 que mapear vectores en coordenadas del dispositivo a coordenadas mundiales o viceversa. Para un dispositivo en la posición neutral, esta función devolvería la matriz de identidad.

Esta matriz no varía con la orientación de la pantalla. Esto significa que su aplicación necesita conocer la orientación y compensar en consecuencia.

SensorManager.getOrientation() toma la matriz de transformación y calcula los valores azimuth, pitch y roll. Estos se toman en relación con un dispositivo en la posición neutral.

No tengo idea de cuál es la diferencia entre llamar a esta función y simplemente usar el sensor TYPE_ORIENTATION, excepto que la función le permite manipular primero la matriz.

Si el dispositivo está inclinado hacia arriba a 90 ° o cerca de él, el uso de ángulos de Euler se desmorona. Este es un caso degenerado matemáticamente. En este ámbito, ¿cómo se supone que el dispositivo debe saber si está cambiando el acimut o el balanceo?

La función SensorManager.remapCoordinateSystem() se puede utilizar para manipular la matriz de transformación para compensar lo que puede saber sobre la orientación del dispositivo. Sin embargo, mis experimentos han demostrado que esto no cubre todos los casos, ni siquiera algunos de los más comunes. Por ejemplo, si desea volver a asignar para un dispositivo en posición vertical (por ejemplo, para tomar una foto), sería necesario multiplicar la matriz de transformación de esta matriz:

1 0 0 
0 0 1 
0 1 0 

antes de llamar getOrientation(), y esto no es uno de los remapeos de orientación que remapCoordinateSystem() admite [alguien por favor corrígeme si me he perdido algo aquí].

OK, así que esto ha sido una manera larga de decir que si estás usando la orientación, ya sea desde el sensor TYPE_ORIENTATION o desde getOrientation(), probablemente lo estás haciendo mal. La única vez que realmente quiere los ángulos de Euler es mostrar información de orientación de forma fácil de usar, anotar una fotografía, conducir la pantalla del instrumento de vuelo, o algo similar.

Si desea realizar cálculos relacionados con la orientación del dispositivo, es casi seguro que es mejor utilizar la matriz de transformación y trabajar con vectores XYZ.

Trabajando como consultor, cada vez que alguien viene a mí con un problema relacionado con ángulos de Euler, hacer copias de seguridad y pedirles que lo que están realmente tratando de hacer, y luego encontrar una manera de hacerlo con vectores lugar.

Al volver a su pregunta original, getOrientation() debería devolver tres valores en [-180 180] [-90 90] y [-180 180] (después de convertir de radianes). En la práctica, pensamos en acimut como números en [0 360], por lo que simplemente debe agregar 360 a los números negativos que reciba. Tu código se ve correcto como está escrito. Ayudaría si supiera exactamente qué resultados esperaba y qué obtendría en su lugar.

Editado para agregar: Un par de pensamientos más. Las versiones modernas de Android usan algo llamado "fusión de sensores", lo que básicamente significa que todas las entradas disponibles (acelerador, magnetómetro, giroscopio) se combinan en una caja negra matemática (normalmente un filtro de Kalman, pero depende del proveedor). Todos los diferentes sensores (aceleración, campo magnético, giroscopios, gravedad, aceleración lineal y orientación) se toman como salidas de esta caja negra.

Siempre que sea posible, debe usar TYPE_GRAVITY en lugar de TYPE_ACCELEROMETER como la entrada a getRotationMatrix().

+0

gracias por su explicación, ¿me puede orientar sobre esta pregunta? http://stackoverflow.com/questions/27137239/get-moving-device-direction-without-gps – Arash

+1

He agregado una respuesta a esa pregunta. Probablemente no te guste; es un problema muy difícil de resolver. –

Cuestiones relacionadas