para magnético magnetico descargar activar android screen-orientation device-orientation sensormanager

magnético - Android SensorManager extraño cómo volver a correlacionar el sistema de coordenadas



descargar sensor magnetico para android (5)

Así es como hago la magia en mi aplicación:

float[] rotationMatrixOrig = new float[9]; SensorManager.getRotationMatrix(rotationMatrixOrig, null, lastAccelerometerValue, lastMagnetometerValue); int screenRotation = app.getCurrentActivity().getWindowManager().getDefaultDisplay().getRotation(); int axisX, axisY; boolean isUpSideDown = lastAccelerometerValue[2] < 0; switch (screenRotation) { case Surface.ROTATION_0: axisX = (isUpSideDown ? SensorManager.AXIS_MINUS_X : SensorManager.AXIS_X); axisY = (Math.abs(lastAccelerometerValue[1]) > 6.0f ? (isUpSideDown ? SensorManager.AXIS_MINUS_Z : SensorManager.AXIS_Z) : (isUpSideDown ? SensorManager.AXIS_MINUS_Y : SensorManager.AXIS_Y)); break; case Surface.ROTATION_90: axisX = (isUpSideDown ? SensorManager.AXIS_MINUS_Y : SensorManager.AXIS_Y); axisY = (Math.abs(lastAccelerometerValue[0]) > 6.0f ? (isUpSideDown ? SensorManager.AXIS_Z : SensorManager.AXIS_MINUS_Z) : (isUpSideDown ? SensorManager.AXIS_X : SensorManager.AXIS_MINUS_X)); break; case Surface.ROTATION_180: axisX = (isUpSideDown ? SensorManager.AXIS_X : SensorManager.AXIS_MINUS_X); axisY = (Math.abs(lastAccelerometerValue[1]) > 6.0f ? (isUpSideDown ? SensorManager.AXIS_Z : SensorManager.AXIS_MINUS_Z) : (isUpSideDown ? SensorManager.AXIS_Y : SensorManager.AXIS_MINUS_Y)); break; case Surface.ROTATION_270: axisX = (isUpSideDown ? SensorManager.AXIS_Y : SensorManager.AXIS_MINUS_Y); axisY = (Math.abs(lastAccelerometerValue[0]) > 6.0f ? (isUpSideDown ? SensorManager.AXIS_MINUS_Z : SensorManager.AXIS_Z) : (isUpSideDown ? SensorManager.AXIS_MINUS_X : SensorManager.AXIS_X)); break; default: axisX = (isUpSideDown ? SensorManager.AXIS_MINUS_X : SensorManager.AXIS_X); axisY = (isUpSideDown ? SensorManager.AXIS_MINUS_Y : SensorManager.AXIS_Y); } float[] rotationMatrix = new float[9]; SensorManager.remapCoordinateSystem(rotationMatrixOrig, axisX, axisY, rotationMatrix);

Demos de API -> Gráficos -> Compass

Solo funciona correctamente, hasta que no cambie la orientación natural del dispositivo. En la mayoría de los teléfonos es el retrato y la mayoría de las tabletas de 10 pulgadas son el paisaje. Si cambias de lo necesario deberás rotarlo con 90 grados. Me gustaría ver una solución 3D para ese sistema.

100% seguro de usar el método remapCoordinateSystem() .

Me gustaría ver cómo (código) si pudiera ver una explicación de cómo se calculan esos ejes (matemática teórica) sería bueno.

He intentado entender, pero olvidé todo álgebra lineal.

Here dice por qué debemos usar, ¡pero no dice cómo!

float R[] = new float[9]; // X (product of Y and Z) and roughly points East // Y: points to Magnetic NORTH and tangential to ground // Z: points to SKY and perpendicular to ground float I[] = new float[9]; boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);

Parece que esas coordenadas están en esta posición: - el dispositivo dice en una tabla (los ejes x, y y están en la tabla)

Solo y solo si

getWindowManager().getDefaultDisplay().getRotation() == Surface.ROTATION_0

La pregunta es cómo completar este código: - esas ramas de casos

switch (mScreenRotation) { case Surface.ROTATION_0: Log.v("SurfaceRemap", "0 degree"); axisX = SensorManager.AXIS_X;// is this valid? axisY = SensorManager.AXIS_Y;// is this valid? break; case Surface.ROTATION_90: Log.v("SurfaceRemap", "90 degree"); // examples says remapCoordinateSystem(inR, AXIS_Y, AXIS_MINUS_X, outR); axisX = SensorManager.AXIS_Y; axisY = SensorManager.AXIS_MINUS_X; break; case Surface.ROTATION_180: Log.v("SurfaceRemap", "180 degree"); break; case Surface.ROTATION_270: Log.v("SurfaceRemap", "270 degree"); break; default: Log.v("SurfaceRemap", "don''t know the mScreenRotation value: "+mScreenRotation+" you should never seen this message!"); break; } boolean remapped = SensorManager.remapCoordinateSystem(R, axisX, axisY, R); float orientation[] = new float[3]; SensorManager.getOrientation(R, orientation);// All three angles above are in radians and positive in the counter-clockwise direction. inclination = SensorManager.getInclination(I);

Edición: escribí una pequeña aplicación de prueba, donde en la pantalla muestra la rotación de la pantalla: 0, 90, 270 grados (no se puede hacer 180 ahora)

Parece que, si la rotación 0 no se modifica ( axisX = SensorManager.AXIS_X;axisY = SensorManager.AXIS_Y; ), los 90 grados deberían ser:

axisX = SensorManager.AXIS_MINUS_Y; axisY = SensorManager.AXIS_X;

que la documentación de Google dice valores equivocados! La pregunta es ¿dónde?

getRotationMatrix devuelve esto:

X se define como el producto vectorial YZ (es tangencial al suelo en la ubicación actual del dispositivo y aproximadamente apunta al Este).

Y es tangencial al suelo en la ubicación actual del dispositivo y apunta hacia el Polo Norte magnético.

Z apunta hacia el cielo y es perpendicular al suelo.

¡Vea el teléfono de arriba! Quiero a la derecha de la izquierda, con la cámara de fondo a tierra.

getOrientation devuelve esto:

X se define como el producto vectorial YZ (es tangencial al suelo en la ubicación actual del dispositivo y aproximadamente al oeste).

Y es tangencial al suelo en la ubicación actual del dispositivo y apunta hacia el Polo Norte magnético.

Z apunta hacia el centro de la Tierra y es perpendicular al suelo.

values[0] : azimut, rotación alrededor del eje Z.

values[1] : paso, rotación alrededor del eje X.

values[2] : balanceo, rotación alrededor del eje Y.

¿Cómo debe ser el teléfono?

Finalmente me gustaría tener valores de ángulos como aviones. Mi teléfono (yo) hacia el norte: (guiñada es azimut)

if ScreenRotation = 0 degree Pitch axis = -orientationAxisX = rotationAxisX Roll axis = orientationAxisY = rotationAxisY Yaw axis = orientationAxisZ = -rotationAxisZ


Gracias, keianhzo, tu respuesta funciona muy bien con los teléfonos en el piso. Para las aplicaciones de AR en las que se ve "a través" de la pantalla, encontré que esto funciona: use el eje apropiado:

int screenRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); //use the correct axis int axisX = SensorManager.AXIS_X; int axisY = SensorManager.AXIS_Y; switch (mMode) { case LOOK_THROUGH: { // look through always uses x and z axisX = SensorManager.AXIS_X; axisY = SensorManager.AXIS_Z; break; } case FLAT: { // flat changes the x axis depending on rotation state switch (screenRotation) { case Surface.ROTATION_0: axisX = SensorManager.AXIS_X; axisY = SensorManager.AXIS_Y; break; case Surface.ROTATION_90: axisX = SensorManager.AXIS_Y; axisY = SensorManager.AXIS_MINUS_X; break; case Surface.ROTATION_180: axisX = SensorManager.AXIS_MINUS_X; axisY = SensorManager.AXIS_MINUS_Y; break; case Surface.ROTATION_270: axisX = SensorManager.AXIS_MINUS_Y; axisY = SensorManager.AXIS_X; break; default: break; } break; } default: break; }

Obtener los grados de orientación:

boolean success = SensorManager.remapCoordinateSystem(getQuaternion().getMatrix4x4().getMatrix(), axisX, axisY, mRotationMatrixTransformed); if (success) { SensorManager.getOrientation(mRotationMatrixTransformed, mOrientationValues); for (int i = 0; i < 3; i++) { mOrientationDegrees[i] = (float) Math.toDegrees(mOrientationValues[i]); } //And for look through, add the rotation state if (mMode == MODE.LOOK_THROUGH) { // look through has different angles depending on rotation state switch (screenRotation) { case Surface.ROTATION_90: { mOrientationDegrees[2] += 90; break; } case Surface.ROTATION_180: { mOrientationDegrees[2] += 180; break; } case Surface.ROTATION_270: { mOrientationDegrees[2] += 270; break; } } }


Lo que hago es

  1. el sistema de coordenadas de reasignación como keianhzo sugirió en su respuesta , para reasignar las coordenadas de acuerdo con la rotación de pantalla
  2. Luego reasigno el sistema de coordenadas resultante de nuevo con

    SensorManager.remapCoordinateSystem(rotationMatrixScreenRemapped, SensorManager.AXIS_X, SensorManager.AXIS_Z, rotationMatrixCameraRemapped); para reasignar las coordenadas según la cámara (similar a AR), como se sugiere en la documentation

¡Hasta ahora espero que funcione bien!


Para completar las ramas del switch, solo trato de pensar siguiendo el método remapCoordinateSystem javadoc:

X define en qué eje del mundo y en qué dirección se mapea el eje X del dispositivo.
Y define en qué eje del mundo y en qué dirección se mapea el eje Y del dispositivo.

Entonces, tome su dispositivo, gírelo desde su orientación natural (90, 180 o 270 grados) y pregúntese: ¿El eje X positivo en la orientación del dispositivo original a qué eje corresponde en la orientación del dispositivo actual? Y lo mismo para el eje Y.

Entonces, en caso de que su dispositivo gire 90 grados, verá que el eje X positivo original corresponde al eje Y positivo actual y que el eje Y positivo original corresponde al eje X negativo de orientación actual.

Entonces debería ser:

switch (mScreenRotation) { case Surface.ROTATION_0: axisX = SensorManager.AXIS_X; axisY = SensorManager.AXIS_Y; break; case Surface.ROTATION_90: axisX = SensorManager.AXIS_Y; axisY = SensorManager.AXIS_MINUS_X; break; case Surface.ROTATION_180: axisX = SensorManager.AXIS_MINUS_X; axisY = SensorManager.AXIS_MINUS_Y; break; case Surface.ROTATION_270: axisX = SensorManager.AXIS_MINUS_Y; axisY = SensorManager.AXIS_X; break; default: break; }

Eso funcionó para mí, espero que ayude.


Si la IU del teléfono está bloqueada en la rotación 0 , remapCoordinateSystem() los siguientes valores sin remapCoordinateSystem()

Pitch (phone) = -Pitch (API) Roll (phone) = Roll (API) Yaw (phone) = Azimuth (API)

  • Al menos cerca de 0,0,0 valores.

Si la UI del teléfono obliga a la rotación 90 :

¡El valor de guiñada tiene -90 grados (- PI / 2) en la orientación anterior! => Iré al Este en realidad en lugar de al Norte.

Si llevo el teléfono a la posición 0,0,0:

Pitch (phone) = -Roll (API) Roll (phone) = -Pitch (API) Yaw (phone) = Azimuth (API)

Si la interfaz de usuario del teléfono forzado a rotación 180 :

¡El valor de guiñada tiene +/- 180 grados (+/- PI) en la orientación anterior! => Iré al sur en realidad en lugar de al norte.

Si llevo el teléfono a la posición 0,0,0:

Pitch (phone) = Pitch (API) Roll (phone) = -Roll (API) Yaw (phone) = Azimuth (API)

Si la interfaz de usuario del teléfono forzado a rotación 270 :

¡El valor de guiñada tiene +90 grados (+ PI / 2) en la orientación anterior! => Iré a West en realidad en lugar de North.

Si llevo el teléfono a la posición 0,0,0:

Pitch (phone) = Roll (API) Roll (phone) = Pitch (API) Yaw (phone) = Azimuth (API)

Escribí una pequeña corrección y la probé con: android:screenOrientation="fullSensor"

public static final void fixRotation0(float[] orientation) { //azimuth, pitch, roll orientation[1] = -orientation[1]; // pitch = -pitch } public static final void fixRotation90(float[] orientation) { //azimuth, pitch, roll orientation[0] += Math.PI / 2f; // offset float tmpOldPitch = orientation[1]; orientation[1] = -orientation[2]; //pitch = -roll orientation[2] = -tmpOldPitch; // roll = -pitch } public static final void fixRotation180(float[] orientation) { //azimuth, pitch, roll orientation[0] = (float)(orientation[0] > 0f ? (orientation[0] - Math.PI) : (orientation[0] + Math.PI)); // offset orientation[2] = -orientation[2]; // roll = -roll } public static final void fixRotation270(float[] orientation) { //azimuth, pitch, roll orientation[0] -= Math.PI / 2; // offset float tmpOldPitch = orientation[1]; orientation[1] = orientation[2]; //pitch = roll orientation[2] = tmpOldPitch; // roll = pitch }

En la mayoría de los casos está funcionando. Cuando gire rápidamente 180 grados alrededor de 1 eje, el sistema se atornillará.

El código completo disponible en Github