¿Cómo conseguir que la orientación del teléfono Android coincida con la orientación humana?

Estoy haciendo una aplicación de mapa, incluyendo la flecha de ubicación que muestra de qué manera estás enfrentando, así:

Mapa con una flecha azul

Recibo la orientación directamente desde SensorManager.getOrientation() , usando el primer valor devuelto: azimut. Cuando el teléfono se mantiene para que la pantalla apunte por encima del horizonte, y en el retrato, la flecha funciona bien. Sin embargo:

  • Cuando el teléfono está sujeto para que la pantalla apunte por debajo del horizonte, la flecha apunta 180 grados lejos de la dirección que el usuario está mirando.
  • Cuando el teléfono está sujeto de manera que la pantalla esté mirando al nivel del horizonte, la flecha no tiene idea de la dirección que apunta. Azimut no devuelve resultados significativos en absoluto.
  • Cuando el teléfono está inclinado hacia la izquierda o hacia la derecha (o mantenido en modo horizontal), la flecha se inclina hacia la izquierda o hacia la derecha.

La imagen cuidadosamente construida y científica a continuación muestra lo que quiero decir (donde el azul es la cara del usuario, rojo es la dirección de la flecha, la pantalla es aproximadamente cara a cara del usuario, y Google Maps hace exactamente lo que quiero):

Gráfico de lo que sucede vs lo que quiero

(Tenga en cuenta que, con Google Maps, no realiza correctamente las dos últimas acciones de la lista si la función de rotación automática está desactivada, pero ni siquiera en esa etapa, una cosa a la vez.)

Parece como si estuviera simplemente usando la dirección de orientación del eje Y como se muestra aquí: http://developer.android.com/reference/android/hardware/SensorEvent.html , cuando quiero que use el reverso de la dirección de orientación del eje Z , La mayor parte del tiempo, y el Y cuando el teléfono es plano. Sin embargo, dados los valores que getOrientation() devuelve, tendría que escribir casos complejos para solucionar algunos de los problemas, y el teléfono de cara al horizonte caso de uso es insoluble. Estoy seguro de que hay una manera más fácil.

Aquí está mi código (Where lastAcceleration y lastMagneticField provienen del sensor interno):

 float[] rotationMatrix = new float[9]; if(SensorManager.getRotationMatrix(rotationMatrix, null, lastAcceleration, lastMagneticField)){ float[] orientMatrix = new float[3]; SensorManager.getOrientation(rotationMatrix, orientMatrix); orientation = orientMat[0]*180/(float)Math.PI; } 

¿Qué estoy haciendo mal? ¿Hay alguna forma más fácil de hacer esto?

Edit: Sólo para aclarar, estoy haciendo la suposición de que el usuario está sosteniendo el dispositivo frente a ellos, y la pantalla está apuntando hacia ellos. Más allá de eso, obviamente no puedo decir si sólo uno de ellos gira. Además, estoy utilizando el movimiento del usuario cuando se están moviendo, pero esto es para cuando están parados.

¿Has llamado remapCoordinateSystem? De lo contrario, sólo obtendrá el valor correcto cuando el teléfono esté en posición vertical. Para el caso Cuando el teléfono está sujeto para que la pantalla se enfrente esté nivelada con el horizonte, no hay manera de que el usuario pueda hacer frente. Debido a que para obtener la cara que tiene que proyectar el valor z de la lectura del sensor en el plano xy en la coordenada mundial y es cero cuando el dispositivo se mantiene horizontal.

Para ser más precisos, si desea obtener el teléfono frente a continuación, el teléfono tiene que estar inclinado por lo menos unos 25 grados de la horizontal y usted tiene que llamar remapCoordinateSystem. El código siguiente le dará lo que desea para las últimas 2 imágenes de arriba.
Código

 float[] rotationMatrix = new float[9]; if(SensorManager.getRotationMatrix(rotationMatrix, null, lastAcceleration, lastMagneticField)){ float[] orientMatrix = new float[3]; float remapMatrix = new float[9]; SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_X, SensorManager.AXIS_Z, remapMatrix); SensorManager.getOrientation(remapMatrix, orientMatrix); orientation = orientMat[0]*180/(float)Math.PI; } 

GetOrientation le da los valores correctos asumiendo que el teléfono está tendido de forma plana. Por lo tanto, si el teléfono se mantiene vertical, entonces usted tiene que reasignar la coordenada para obtener la posición plana. Geométricamente, se proyecta el eje -z del teléfono hacia abajo hasta el plano xy del mundo y luego se calcula el ángulo entre este vector de proyección y el eje y del mundo.

Esto parece bastante complicado. Estoy desarrollando un PNS para Android y me enfrento a un problema de alguna manera similar para el que todavía estoy necesitando la luz: ¿Cómo obtener la rotación entre el eje del acelerómetro y el vector de movimiento?

La cosa es que me parece absolutamente imposible encontrar en qué dirección el usuario se enfrenta (no el dispositivo) si no se mueve. Que es el humano no tiene sensor en su cuerpo, así que ¿qué pasa si el dispositivo se mantuvo en la misma posición absoluta, pero el usuario girado en 90 °? No veo ninguna manera de encontrar esto.

Lo que puedo sugerir (y que encaja en realidad a mi problema) es que usted podría (no sé lo que realmente hacer en su código) utilizar el movimiento del usuario para determinar su rumbo. Dejame explicar. Digamos que usted tiene una primera posición A. El usuario va a B. Luego puede construir el vector AB y obtener el título del usuario cuando se detiene en B. Entonces tendría que limitar su código a la dirección que se enfrenta cuando Llegando al destino.

Sé que esto no es tan bueno como lo que Google Maps consigue, pero ¿sabes qué utiliza Google para esto? Quiero decir que sólo utilizan los sensores accelero y mag.field?

Parece que la forma adecuada de obtener el cojinete cuando el usuario está sosteniendo el teléfono verticalmente es usar algo como esto:

 // after calling getRotationMatrix pass the rotationMatix below: SensorManager.remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR); 

Si desea manejar ambas formas (vertical y plana) es probable que tenga que detectar eso y luego sólo realizar este remapeo cuando es vertical.

Consulte la documentación de la API aquí .

Usted debe tomar el tono y determinar si el usuario está cerca de sostener el teléfono verticalmente.

He elegido que después de 45 grados de tono hacia arriba o hacia abajo desde plana sobre la mesa, el sistema de coordenadas debe ser remapeado.

 if (Math.round(Math.toDegrees(-orientation[1])) < 45 && Math.round(Math.toDegrees(-orientation[1])) > -45) { //do something while the phone is horizontal }else{ //R below is the original rotation matrix float remapOut[] = new float[9]; SensorManager.remapCoordinateSystem(R, SensorManager.AXIS_X, SensorManager.AXIS_Z, remapOut); //get the orientation with remapOut float remapOrientation[] = new float[3]; SensorManager.getOrientation(remapOut, remapOrientation); 

Ha funcionado bastante bien. Déjeme saber si alguien puede sugerir una mejora en esto. Gracias.

  • Sensor de dirección Android
  • Android: ¿Qué hilo llama .onSensorChanged?
  • Android acelerómetro no funciona cuando se desactiva la pantalla
  • Obtener la temperatura de la batería en Android
  • Mostrar una lista de sensores de Android
  • ¿Es posible tener la distancia movida por un teléfono inteligente utilizando sólo el acelerómetro?
  • Obtención del valor del sensor de luz
  • Feed de sensores Android con datos falsos
  • Cómo medir HeartBeat usando un dispositivo Android
  • Lectura de la temperatura a través de DHT11 usando Android Things
  • Android: evita que la función de vibración del teléfono afecte a los datos del acelerómetro
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.