Question

Je travaille sur une application d'inclinaison pour Android. J'ai un problème avec le portrait et le mode paysage. Lorsque le pas = 90 degrés (téléphone à bout) et même légèrement avant que la valeur de rouleau ne devienne folle lorsqu'il n'y a pas eu de changement physique dans le rouleau. Je n'ai pas pu trouver de solution à ce problème. Si quelqu'un peut me diriger dans la bonne direction, ce serait apprécié.

Voici un vidage de code court, vous savez donc que ce n'est pas une erreur d'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;
                    }
Était-ce utile?

La solution

J'ai trouvé ce que je cherchais, des matrices de rotation.

J'utilisais des angles Euler (rouleau, tangage, lacet) pour le tangage et le rouleau. Lorsque le téléphone est à la fin de 90 degrés, les X et Z sont les mêmes et le téléphone devient fou, un défaut fondamental avec des angles d'Euler.

J'ai besoin d'obtenir les degrés de hauteur et de roulis à l'aide de matrices de rotation via GetRotationMatrix

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

Autres conseils

Je n'utiliserais pas Angles d'Euler (rouler, tangage, lacet). Il bousille à peu près la stabilité de votre application comme vous l'avez déjà remarqué.

Voyez ici pourquoi, et que faire à la place: Comportement étrange avec le capteur d'orientation Android.

Grâce à l'expérimentation, j'ai trouvé que lorsque vous passez du portrait en mode paysage, votre matrice de rotation ne change pas, mais vous devez la changer manuellement pour l'utiliser correctement OpenGL

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

J'espère également que vous acquérez correctement la matrice de rotation 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);

Ce que vous décrivez est appelé verrouillage de cardan. À Pitch +/- 90, le rouleau de lacet - (+) est complètement indéfini. Près de la hauteur +/- 90, les petits noirs / erreur d'attitude peuvent provoquer de grandes fluctuations dans le lacet et rouler individuellement, même s'il n'y a pas de changement important dans l'orientation réelle. Voici un excellent article sur le lacet, le rouleau de pitch (et comment ils ne sont pas bien mis en œuvre sur de nombreuses plateformes):

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top