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.
- ¿Puede guardar / ver un archivo de vídeo y los datos del sensor simultáneamente?
- Android: Problemas al calcular la orientación del dispositivo
- Cómo utilizar el evento del sensor Android para determinar si el dispositivo está orientado hacia arriba o hacia abajo
- Diferentes valores entre los sensores TYPE_ACCELEROMETER / TYPE_MAGNETIC_FIELD y TYPE_ORIENTATION
- Rotación Opengl de Android con datos de sensores
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; }
- Cómo usar los datos del sensor onSensorChanged en combinación con OpenGL
- Detectar si el teléfono está en el bolsillo o no
- Acelerómetro (acceso rápido) mediante NativeActivity NDK
- Valor de Android: Sensor.getResolution ()
- No es posible obtener los valores del sensor de proximidad en android
- Android - SensorManager comportamiento extraño de getOrientation
- Sistema de coordenadas del dispositivo Android Convert a sistema de coordenadas "usuario"
- ¿Cómo puedo bloquear el diseño de mi programa Android a una orientación
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/
- Establecer un elemento de escucha de clic largo en GridView
- Reemplazar imagen del menú de aplicaciones recientes