Android Pitch and Roll Issue

后端 未结 4 1452
情话喂你
情话喂你 2021-01-03 06:17

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 ro

相关标签:
4条回答
  • 2021-01-03 06:50

    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;
    }
    
    0 讨论(0)
  • 2021-01-03 07:07

    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/

    0 讨论(0)
  • 2021-01-03 07:08

    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:

    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
            SensorManager.getRotationMatrixFromVector(
                    mRotationMatrix , event.values);
            SensorManager.getOrientation (mRotationMatrix, values);
    
    0 讨论(0)
  • 2021-01-03 07:09

    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.

    0 讨论(0)
提交回复
热议问题