2012-02-26 4 views
3

Je travaille sur une application d'inclinaison pour Android. J'ai un problème avec le mode paysage Portrait &. Lorsque le pitch = 90 degrés (téléphone à la fin) et même légèrement avant que la valeur de roulis devienne folle quand il n'y a pas eu de changement physique en roulis. Je n'ai pas été capable de trouver une solution à ce problème. Si quelqu'un peut me diriger dans la bonne direction, ce serait apprécié.Android Pitch and Roll Numéro

Voici un vidage de code court, donc vous savez qu'il ne s'agit pas d'une erreur de l'accéléromètre.

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; 
        } 

Répondre

8

J'ai trouvé ce que je cherchais, des matrices rotatives. J'utilisais des angles d'Euler (roulis, tangage, lacet) pour le tangage et le roulis. Lorsque le téléphone est à 90 degrés, les plis x et z sont les mêmes et le téléphone devient fou, un défaut fondamental avec des angles d'Euler.

Je dois obtenir la hauteur et degrés rouleau en utilisant des matrices de rotation via getRotationMatrix

Ici, il est pour tous;)

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> 

code:

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; 
} 
+0

J'ai le même problème, bien que j'utilise aussi des matrices de rotation. J'utilise getRotationMatrixFromVector() à la place de getRotationMatrix(), car j'utilise TYPE_ROTATION_VECTOR. Y a-t-il des différences entre ces deux méthodes? La deuxième question est ce que ceci est pour vous dans le code si (mAzimuth <0) { mAzimuth + = 360.0f; }? – Nazerke

+0

Impossible de commenter sur le vecteur, mais le mAzimuth est le cap de la boussole, il ne fournit pas comme une lecture propre 0-360, vous devez convertir les valeurs neg en 360, plus beaucoup d'autres réglages si vous vouloir calculer correctement. Vous penseriez que vous pourriez juste demander à l'appareil pour le lancement, le rouleau et la boussole et le ramener, pas moyen, vous arrivez à le comprendre, un PITA majeur pour ce qui aurait dû être simple. – user1234051

+0

Quels sont ces autres tweaks? Que faites-vous avec mInclination? Est-ce que cela fait partie de la correction? J'utilise aussi getRotationMatrixFromVector() et je vois l'azimut varier avec la hauteur, ce que je ne veux pas, et Google maps ne le fait pas en mode boussole. – Flyview

2

Je ne voudrais pas utiliser angles d'Euler (roulis, tangage, lacet). Il bloque la stabilité de votre application comme vous l'avez déjà remarqué.

Voir ici pourquoi, et quoi faire à la place: Strange behavior with android orientation sensor.

+0

Merci, j'ai vérifié le poste, je comprends bien pourquoi les anges Euler sont mauvais choix, mais je ne suis pas familier avec les matrices de rotation et ne peux pas trouver de bonnes informations sur leur mise en œuvre dans Android. D'autres références? – user1234051

+0

Vous n'avez pas à l'implémenter, il est implémenté par le SensorManager. Je voudrais commencer à la [SensorManager.getRotationMatrix] (http://developer.android.com/reference/android/hardware/SensorManager.html#getRotationMatrix). – Ali

+0

Merci, j'ai regardé ça. Malheureusement, je n'apprends pas bien des exemples théoriques de Google. Je trouve les choses en regardant le code de travail, nouveau au concept de matrices de rotation et aussi Android. Un diable d'une montée en montée en ce moment. – user1234051

0

Grâce à l'expérimentation, j'ai trouvé que lorsque vous passez du mode portrait au mode paysage votre matrice de rotation ne change pas, mais vous devez modifier manuellement afin d'utiliser avec OpenGL correctement

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]; 

Aussi je vous souhaite acquérir correctement la rotation Matrice sur la première place:

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

Qu'est-ce que vous décrivez est appelé verrou de cardan. Au pas +/- 90, le mouvement de lacet - (+) est complètement indéfini. Près du pas +/- 90, un petit bruit/une erreur d'attitude peut causer de grandes fluctuations de lacet et de roulis individuellement même s'il n'y a pas de grand changement dans l'orientation réelle. Voici une grande écriture sur lacet, roulis de tangage (et comment Thay ne sont pas correctement mises en œuvre sur de nombreuses plateformes):

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

Questions connexes