androidsensorspitchtilt

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 value goes crazy when there has been no physical change in roll. I have not been able to find a solution to this problem. If anyone can point me in the right direction, it would be appreciated.

Here’s a short code dump, so you know it is not an accelerometer error.

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

Solution

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