Edición de Pitch and Roll de Android

Estoy trabajando en una aplicación de inclinación para Android. Tengo un problema con el modo de retrato y paisaje. Cuando el tono = 90 grados (teléfono en extremo) e incluso levemente antes de que el valor del rollo se vuelva loco cuando no ha habido ningún cambio físico en rollo. No he podido encontrar una solución a este problema. Si alguien me puede señalar en la dirección correcta, sería apreciado.

Aquí hay un volcado de código corto, por lo que sabes que no es un error de acelerómetro.

final SensorEventListener mEventListener = new SensorEventListener(){ public void onAccuracyChanged(Sensor sensor, int accuracy) {} public void onSensorChanged(SensorEvent event) { setListners(sensorManager, mEventListener); SensorManager.getRotationMatrix(mRotationMatrix, null, mValuesAccel, mValuesMagnet); SensorManager.getOrientation(mRotationMatrix, mValuesOrientation); synchronized (this) { switch (event.sensor.getType()){ case Sensor.TYPE_ACCELEROMETER: System.arraycopy(event.values, 0, mValuesAccel, 0, 3); long actualTime = System.currentTimeMillis(); //Sensitivity delay if (actualTime - lastUpdate < 250) { return; } else { sysAzimuth = (int)Math.toDegrees(mValuesOrientation[0]); sysPitch = (int)Math.toDegrees(mValuesOrientation[1]); sysRoll = (int)Math.toDegrees(mValuesOrientation[2]); //invert direction with -1 pitch = (sysPitch - pitchCal)*-1; roll = (sysRoll - rollCal); azimuth = sysAzimuth; lastUpdate = actualTime; } 

Encontré lo que buscaba, Matrices Rotacionales.

Yo estaba usando los ángulos de Euler (rollo, tono, guiñada) para el tono y el rollo. Cuando el teléfono está en extremo 90 grados, el x y el llano de z son iguales y el teléfono va loco, un defecto fundamental con los ángulos de Euler.

Tengo que obtener los grados de tono y rodar utilizando matrices de rotación a través de getRotationMatrix

Aquí está para todos;)

XML:

 <?xml version="1.0" encoding="utf-8"?> <!-- This file is res/layout/main.xml --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:id="@+id/update" android:text="Update Values" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="doUpdate" /> <Button android:id="@+id/show" android:text="Show Me!" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="doShow" android:layout_toRightOf="@id/update" /> <TextView android:id="@+id/preferred" android:textSize="20sp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/update" /> <TextView android:id="@+id/orientation" android:textSize="20sp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/preferred" /> </RelativeLayout> 

Código:

 package YOURPACKAGE; import android.app.Activity; import android.content.Intent; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.view.View; import android.view.WindowManager; import android.widget.TextView; public class YOURCLASS extends Activity implements SensorEventListener { private static final String TAG = "VirtualJax"; private SensorManager mgr; private Sensor accel; private Sensor compass; private Sensor orient; private TextView preferred; private TextView orientation; private boolean ready = false; private float[] accelValues = new float[3]; private float[] compassValues = new float[3]; private float[] inR = new float[9]; private float[] inclineMatrix = new float[9]; private float[] orientationValues = new float[3]; private float[] prefValues = new float[3]; private float mAzimuth; private double mInclination; private int counter; private int mRotation; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); preferred = (TextView)findViewById(R.id.preferred); orientation = (TextView)findViewById(R.id.orientation); mgr = (SensorManager) this.getSystemService(SENSOR_SERVICE); accel = mgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); compass = mgr.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); orient = mgr.getDefaultSensor(Sensor.TYPE_ORIENTATION); WindowManager window = (WindowManager) this.getSystemService(WINDOW_SERVICE); int apiLevel = Integer.parseInt(Build.VERSION.SDK); if(apiLevel <8) { mRotation = window.getDefaultDisplay().getOrientation(); } else { mRotation = window.getDefaultDisplay().getRotation(); } } @Override protected void onResume() { mgr.registerListener(this, accel, SensorManager.SENSOR_DELAY_GAME); mgr.registerListener(this, compass, SensorManager.SENSOR_DELAY_GAME); mgr.registerListener(this, orient, SensorManager.SENSOR_DELAY_GAME); super.onResume(); } @Override protected void onPause() { mgr.unregisterListener(this, accel); mgr.unregisterListener(this, compass); mgr.unregisterListener(this, orient); super.onPause(); } public void onAccuracyChanged(Sensor sensor, int accuracy) { // ignore } public void onSensorChanged(SensorEvent event) { // Need to get both accelerometer and compass // before we can determine our orientationValues switch(event.sensor.getType()) { case Sensor.TYPE_ACCELEROMETER: for(int i=0; i<3; i++) { accelValues[i] = event.values[i]; } if(compassValues[0] != 0) ready = true; break; case Sensor.TYPE_MAGNETIC_FIELD: for(int i=0; i<3; i++) { compassValues[i] = event.values[i]; } if(accelValues[2] != 0) ready = true; break; case Sensor.TYPE_ORIENTATION: for(int i=0; i<3; i++) { orientationValues[i] = event.values[i]; } break; } if(!ready) return; if(SensorManager.getRotationMatrix(inR, inclineMatrix, accelValues, compassValues)) { // got a good rotation matrix SensorManager.getOrientation(inR, prefValues); mInclination = SensorManager.getInclination(inclineMatrix); // Display every 10th value if(counter++ % 10 == 0) { doUpdate(null); counter = 1; } } } public void doUpdate(View view) { if(!ready) return; mAzimuth = (float) Math.toDegrees(prefValues[0]); if(mAzimuth < 0) { mAzimuth += 360.0f; } String msg = String.format( "Preferred:\nazimuth (Z): %7.3f \npitch (X): %7.3f\nroll (Y): %7.3f", mAzimuth, Math.toDegrees(prefValues[1]), Math.toDegrees(prefValues[2])); preferred.setText(msg); msg = String.format( "Orientation Sensor:\nazimuth (Z): %7.3f\npitch (X): %7.3f\nroll (Y): %7.3f", orientationValues[0], orientationValues[1], orientationValues[2]); orientation.setText(msg); preferred.invalidate(); orientation.invalidate(); } public void doShow(View view) { // google.streetview:cbll=30.32454,-81.6584&cbp=1,yaw,,pitch,1.0 // yaw = degrees clockwise from North // For yaw we can use either mAzimuth or orientationValues[0]. // // pitch = degrees up or down. -90 is looking straight up, // +90 is looking straight down // except that pitch doesn't work properly Intent intent=new Intent(Intent.ACTION_VIEW, Uri.parse( "google.streetview:cbll=30.32454,-81.6584&cbp=1," + Math.round(orientationValues[0]) + ",,0,1.0" )); startActivity(intent); return; } 

No usaría ángulos de Euler (rollo, tono, guiñada). Es bastante mucho tornillos de la estabilidad de su aplicación como ya lo notó.

Vea aquí por qué, y qué hacer en su lugar: Comportamiento extraño con el sensor de orientación de Android .

A través de la experimentación encontré que cuando cambias de retrato a modo de paisaje tu matriz de rotación no cambia pero tienes que cambiarlo manualmente para poder usarlo con OpenGL correctamente

 copyMat(mRotationMatrixP, mRotationMatrix); // permute and negate columns 0, 1 mRotationMatrixP[0] = -mRotationMatrix[1]; mRotationMatrixP[4] = -mRotationMatrix[5]; mRotationMatrixP[8] = -mRotationMatrix[9]; // permute 1, 0 mRotationMatrixP[1] = mRotationMatrix[0]; mRotationMatrixP[5] = mRotationMatrix[4]; mRotationMatrixP[9] = mRotationMatrix[8]; 

También espero que adquiera la matriz de rotación correctamente en el primer lugar:

 public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { SensorManager.getRotationMatrixFromVector( mRotationMatrix , event.values); SensorManager.getOrientation (mRotationMatrix, values); 

Lo que usted describe es llamado gimbal lock. En el tono +/- 90, la guiñada – (+) rollo es completamente indefinida. Alrededor del tono +/- 90, el ruido / error pequeño en la actitud puede causar grandes fluctuaciones en la guiñada y rodar individualmente, aunque no hay grandes cambios en la orientación real. Aquí hay un gran artículo sobre la guinada, el rollo de tono (y cómo no se implementan bien en muchas plataformas):

http://www.sensorplatforms.com/understanding-orientation-conventions-mobile-platforms/

  • Android: ¿Cómo se trata con baromters inexactos?
  • Cómo construir un simulador de sensores para Android?
  • Obtener el ángulo del dispositivo mediante la función getOrientation ()
  • ¿Cómo voy a detectar el sensor de movimiento en el receptor de radiodifusión?
  • Valor de retorno falso inesperado de android.hardware.SensorManager.registerListener en Android 4.3
  • Cómo saber si mi SensorManager tiene un Sensor registrado
  • ¿Cómo rastrear la posición de un dispositivo Android en un edificio?
  • Cómo utilizar SensorManager.getOrientation () en lugar de TYPE_ORIENTATION
  • ¿Cómo debo calcular el azimut, el tono, la orientación cuando mi dispositivo Android no es plano?
  • ¿Cómo reemplazar adecuadamente Sensor.TYPE_ORIENTATION (que ahora está obsoleto)?
  • Android - Cómo acercarse al algoritmo de detección de caídas
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.