Get rotation and display in degrees

23.5k views Asked by At

I need something very simple, but I could not find a suitable example to learn from. My sole purpose is the following:


As the device is placed flat (on its back) on the desk, it should show 0 (or close to 0) for X and Y axis. When I lift it from the top part (where the speaker is) and the bottom part (where the microphone is) stays put down - it should show me how many degrees is the phone tilted. Mathematically described - show in degrees the angle between the back of the phone and the table, for one of the axises. When I lift the bottom part (and the top part stays put down) then show minus degrees.

The same goes for the other axis - rotating the phone around its long sides.


I tried assembling an app from different examples, using Gyroscope or Accelerometer or Rotation Vector Sensors, but could not come with something working properly.

Can someone give me an example of the onSensorChanged function (as all the work goes on in here) and just tell me which sensor is used, so I know what to register?

2

There are 2 answers

6
Csaba Szugyiczki On BEST ANSWER

There are a few examples and tutorials on the web, but be careful. Sensor.TYPE_ORIENTATION became deprecated. You need to calculate rotations by listening to these two sensors Sensor.TYPE_ACCELEROMETER and Sensor.TYPE_MAGNETIC_FIELD.

The tricky part after registering to receive notifications from these sensors, is to figure out how to handle the data received from them. The key part is the following:

public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
       mGravity = event.values;

    if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
       mGeomagnetic = event.values;

    if (mGravity != null && mGeomagnetic != null) {
       float R[] = new float[9];
       float I[] = new float[9];

       boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
       if (success) {
          float orientation[] = new float[3];
          SensorManager.getOrientation(R, orientation);
          azimuth = orientation[0]; // orientation contains: azimuth, pitch and roll
          pitch = orientation[1];
          roll = orientation[2];
       }
    }
}

This is how you should be calculating the azimuth, pitch, roll values of your device in the onSensorChanged(SensorEvent event) callback. Keep in mind that "All three angles above are in radians and positive in the counter-clockwise direction". You can simply convert them to degrees with Math.toDegrees()

As Louis CAD pointed out in the comments, it is a good idea to move the initialization of the I, R and orientation arrays out of the onSensorChanged callback, since it is called frequently. Creating and then leaving them behind for the GC is bad for your apps performance. I left it there for the sake of simplicity.

Based on how your device is rotated you might need to remap the coordinates to get the result you want. You can read more about remapCoordinateSystem and also about getRotationMatrix and getOrientation in the android documentation

Example code: http://www.codingforandroid.com/2011/01/using-orientation-sensors-simple.html

0
Konstantin Voronov On

Have a look at this simple CameraLevel app. https://github.com/konstantinvoronov/camerahorizon_overlay

Instead of using magnetic field and accelerometer sensors camerahorizon_overlay uses only accelerometer and work well.