Skip to content Skip to sidebar Skip to footer

Android Pitch And Roll Issue

I am working on a tilt app for Android. I am having an issue with Portrait & landscape mode. When the pitch = 90 degrees (phone on end) and even slightly before the roll valu

Solution 1:

I found what I was looking for, Rotational Matrices.

I was using Euler angles (roll, pitch, yaw) for the pitch and roll. When the phone is on end 90 degrees, the x and z plain are the same and the phone goes crazy, a fundamental flaw with Euler angles.

I need to get the pitch and roll degrees using Rotational Matrices via getRotationMatrix

Here it is for all ;)

XML:

<?xml version="1.0" encoding="utf-8"?><!-- This file is res/layout/main.xml --><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent" ><Buttonandroid:id="@+id/update"android:text="Update Values"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="doUpdate" /><Buttonandroid: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" /><TextViewandroid:id="@+id/preferred"android:textSize="20sp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/update" /><TextViewandroid: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;


publicclassYOURCLASSextendsActivityimplementsSensorEventListener {
privatestaticfinalStringTAG="VirtualJax";
private SensorManager mgr;
private Sensor accel;
private Sensor compass;
private Sensor orient;
private TextView preferred;
private TextView orientation;
privatebooleanready=false;
privatefloat[] accelValues = newfloat[3];
privatefloat[] compassValues = newfloat[3];
privatefloat[] inR = newfloat[9];
privatefloat[] inclineMatrix = newfloat[9];
privatefloat[] orientationValues = newfloat[3];
privatefloat[] prefValues = newfloat[3];
privatefloat mAzimuth;
privatedouble mInclination;
privateint counter;
privateint mRotation;

@OverridepublicvoidonCreate(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);
    WindowManagerwindow= (WindowManager) this.getSystemService(WINDOW_SERVICE);
    intapiLevel= Integer.parseInt(Build.VERSION.SDK);
    if(apiLevel <8) {
        mRotation = window.getDefaultDisplay().getOrientation();
    }
    else {
        mRotation = window.getDefaultDisplay().getRotation();
    }
}

@OverrideprotectedvoidonResume() {
    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();
}

@OverrideprotectedvoidonPause() {
    mgr.unregisterListener(this, accel);
    mgr.unregisterListener(this, compass);
    mgr.unregisterListener(this, orient);
    super.onPause();
}

publicvoidonAccuracyChanged(Sensor sensor, int accuracy) {
    // ignore
}

publicvoidonSensorChanged(SensorEvent event) {
    // Need to get both accelerometer and compass// before we can determine our orientationValuesswitch(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 valueif(counter++ % 10 == 0) {
            doUpdate(null);
            counter = 1;
        }

    }
}

publicvoiddoUpdate(View view) {
    if(!ready)
        return;
    mAzimuth = (float) Math.toDegrees(prefValues[0]);
    if(mAzimuth < 0) {
        mAzimuth += 360.0f;
    }
    Stringmsg= 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();
}

publicvoiddoShow(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=newIntent(Intent.ACTION_VIEW, Uri.parse(
            "google.streetview:cbll=30.32454,-81.6584&cbp=1," +
                    Math.round(orientationValues[0]) + ",,0,1.0"
    ));
    startActivity(intent);
    return;
}

Solution 2:

I wouldn't use Euler angles (roll, pitch, yaw). It pretty much screws up the stability of your app as you already noticed.

See here why, and what to do instead: Strange behavior with android orientation sensor.

Solution 3:

Through experimentation I found that when you switch from Portrait to Landscape mode your rotation matrix doesn't change but you have to change it manually in order to use with OpenGL correctly

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

Also I hope you acquire the Rotation Matrix correctly on the first place:

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

Solution 4:

What you describe is called gimbal lock. At pitch +/-90, yaw -(+) roll is completely undefined. Near pitch +/-90, small nois/error in attitude can cause large fluctuations in yaw and roll individually even though there is no large change in actual orientation. Here is a great write-up on yaw, pitch roll (and how thay are not implemented well on many platforms):

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

Post a Comment for "Android Pitch And Roll Issue"