Get rotation and display in degrees - android

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?

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

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.

Related

constant rotating device- how to receive valid orientation data?

I am currently implementing an speedometer by receiving orientation data from my phone. I am using
SensorManager.getRotationMatrix(R, I, gravity, geomagnetic);
float orientation[] = new float[3];
SensorManager.getOrientation(R, orientation);
float azimuth = orientation[0];
double azimuthD = Math.toDegrees(azimuth);
if(azimuthD < 0) azimuthD = 360 + azimuthD;
With this i am able to receive the rotation data from my phone, such as azimuth etc..
Anyway, this works fine while the device is placed on a table or something. But when rotating around a certain point (in my case the device is fixed on a wheel and rotating at a certain speed) the values are far away from being accurate. I believe, since I am using gravity and the geomagnetic sensor, there could be an conflict with forces that influence these sensors, while rotating. As the wheel turns, the rotation changes relative to a point, but the local device rotation stays the same.
How can I access the orientation of the device while it's turning without running into a lot of noisy data?
I read some about the ´Sensor.TYPE_ROTATION_VECTOR´ property, but couldn't quite figure out how it works. Also I read about the possibility to remap the coordination system, but how is that supposed to help, since my phone is never not vertical to the floor more like with an angle of 5°-10°.
I would appreciate any help.
Cheers,
viehlieb
I guess i found my answer.
The solution was to throw away all the code i posted above and use the gyroscope, obviously.
The gyroscope values measure angular velocity of the device's rotation. The coordinate system used is the devices own coordinate system. In my case the relevant value was the rotation around the z-axis.
Values are in radiant per second, which can be mapped to m/s if you figure multiply the wheel's circumference. So the trick was in the OnSensorChanged method:
if(sensorEvent.sensor.getType() == Sensor.TYPE_GYROSCOPE){
gyroscope = sensorEvent.values;
double rotZ = gyroscope[2];
double degrees = Math.toDegrees(gyroscope[2]);
//calculate the speed with circumference = 2.23m
float speed = (float) degrees/ 360.0f * 2.23f * 3.6f;
}
If now you'd like to have accurate values, you could store them in an array and calculate the average. Remember to clear the array every 20th time (or so) the onSensorChanged method is called. With SENSOR_DELAY_GAME registered there's sufficient data over which you could build the average.

sensors data with respect to true north android

I am using the SensorEvent API in order to get data for my app from different sensors (more specifically: TYPE_ROTATION_VECTOR, TYPE_GRAVITY, TYPE_GYROSCOPE, TYPE_LINEAR_ACCELERATION). Now, I know that in iOS there is the so called CMAttitudeReferenceFrameXTrueNorthZVertical, which gives all the sensors values with respect to True North, whereas z axis will always be vertical.
I couldn't find anything similar in Android, so I am thinking to manually translate the coordinate system. I am also thinking of using the remapCoordinateSystem method. However, I still don't know how to get the data with respect to True North. Did anyone have to deal with something similar before?
This answer is inspired by the class used here:
Yout should have the onSensorChanged method in the SensorEventListener
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
//get the rotation matrix from the sensor (this will be using the magnetic north)
SensorManager.getRotationMatrixFromVector(mRotationMatrix, event.values);
//change the coordinate system (your new matrix is in the variable mChangedRotationMatrix)
SensorManager.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_Y,
SensorManager.AXIS_MINUS_X, mChangedRotationMatrix);
//get the orientationMatrix of your new coordinate system
SensorManager.getOrientation(mChangedRotationMatrix, mOrientation);
//get the magnetic heading
float magneticHeading = (float) Math.toDegrees(mOrientation[0]);
//adjust accordingly by calculating the true north with either the "computeTrueNorth" method available the above link or the method used in the link below
}
}
To get the true north in degrees you may use this answer

Correct way to use ONLY Gyroscope and Accelerometer to get the reliable current angle in any axis on ANDROID

A week ago i didn't know anything about Android Motion Sensors. After know the amazing thing called Virtual Reality I started to search about which sensors are used to get those results. Than I had a idea for a APP but I still don't know which sensors I should use for the situation below:
I have to get the phone orientation in reference to it self. I mean, I should be able to isolate each axis in degress. Something like it:
In this case, using gyroscope, I think that this variation is on the Z Axis.
Using ONLY gyroscope I had a good result for this situation, but after some repetions, I got a famous problem for the Gyro Sensor: Drift.
After this tutorial:
http://www.thousand-thoughts.com/articles/#articles
things became more clear in my head, but I still am having problems like latency between the real movement, and the output and wrong outputs when I change the device orientation (I think that the gravity is the guilty for that).
Is there some code example about how to get 0 - 360 degrees for each axis using ONLY the gyroscope and accelerometer sensors?
(I may had commited some english mistakes. Sorry for that)
The following code will give you correct lean angle, but only if your phone Z axis is 0. (like the way you illustrated it).
When starting to change the Z axis as well, it become problematic, i'm still working on that. (* Degrees has minus "-" sign when lean left and "+" sign to the right)
float[] mGravity;
float[] mGeomagnetic;
float[] temp = new float[9];
float[] RR = new float[9];
//Load rotation matrix into R
SensorManager.getRotationMatrix(temp, null,
mGravity, mGeomagnetic);
//Remap to camera's point-of-view
SensorManager.remapCoordinateSystem(temp,
SensorManager.AXIS_X,
SensorManager.AXIS_Z, RR);
//Return the orientation values
float[] values = new float[3];
SensorManager.getOrientation(RR, values);
Double degrees = (values[2] * 180) / Math.PI;

Acceleration from device's coordinate system into absolute coordinate system

From my Android device I can read an array of linear acceleration values (in the device's coordinate system) and an array of absolute orientation values (in Earth's coordinate system). What I need is to obtain the linear acceleration values in the latter coord. system.
How can I convert them?
EDIT after Ali's reply in comment:
All right, so if I understand correctly, when I measure the linear acceleration, the position of the phone completely does not matter, because the readings are given in Earth's coordinate system. right?
But I just did a test where I put the phone in different positions and got acceleration in different axes. There are 3 pairs of pictures - the first ones show how I put the device (sorry for my Paint "master skill") and the second ones show readings from data provided by the linear acc. sensor:
device put on left side
device lying on back
device standing
And now - why in the third case the acceleration occurs along the Z axis (not Y) since the device position doesn't matter?
I finally managed to solve it! So to get acceleration vector in Earth's coordinate system you need to:
get rotation matrix (float[16] so it could be used later by android.opengl.Matrix class) from SensorManager.getRotationMatrix() (using SENSOR.TYPE_GRAVITY and SENSOR.TYPE_MAGNETIC_FIELD sensors values as parameters),
use android.opengl.Matrix.invertM() on the rotation matrix to invert it (not transpose!),
use Sensor.TYPE_LINEAR_ACCELERATION sensor to get linear acceleration vector (in device's coord. sys.),
use android.opengl.Matrix.multiplyMV() to multiply the rotation matrix by linear acceleration vector.
And there you have it! I hope I will save some precious time for others.
Thanks for Edward Falk and Ali for hints!!
Based on #alex's answer, here is the code snippet:
private float[] gravityValues = null;
private float[] magneticValues = null;
#Override
public void onSensorChanged(SensorEvent event) {
if ((gravityValues != null) && (magneticValues != null)
&& (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)) {
float[] deviceRelativeAcceleration = new float[4];
deviceRelativeAcceleration[0] = event.values[0];
deviceRelativeAcceleration[1] = event.values[1];
deviceRelativeAcceleration[2] = event.values[2];
deviceRelativeAcceleration[3] = 0;
// Change the device relative acceleration values to earth relative values
// X axis -> East
// Y axis -> North Pole
// Z axis -> Sky
float[] R = new float[16], I = new float[16], earthAcc = new float[16];
SensorManager.getRotationMatrix(R, I, gravityValues, magneticValues);
float[] inv = new float[16];
android.opengl.Matrix.invertM(inv, 0, R, 0);
android.opengl.Matrix.multiplyMV(earthAcc, 0, inv, 0, deviceRelativeAcceleration, 0);
Log.d("Acceleration", "Values: (" + earthAcc[0] + ", " + earthAcc[1] + ", " + earthAcc[2] + ")");
} else if (event.sensor.getType() == Sensor.TYPE_GRAVITY) {
gravityValues = event.values;
} else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
magneticValues = event.values;
}
}
According to the documentation you get the linear acceleration in the phone's coordinate system.
You can transform any vector from the phone's coordinate system to the Earth's coordinate system by multiplying it with the rotation matrix. You can get the rotation matrix from getRotationMatrix().
(Perhaps there already is a function doing this multiplication for you but I don't do Android programming and I am not familiar with its API.)
A nice tutorial on the rotation matrix is the Direction Cosine Matrix IMU: Theory manuscript. Good luck!
OK, first of all, if you're trying to do actual inertial navigation on Android, you've got your work cut out for you. The cheap little sensor used in smart phones are just not precise enough. Although, there has been some interesting work done on intertial navigation over small distances, such as inside a building. There are probably papers on the subject you can dig up. Google "Motion Interface Developers Conference" and you might find something useful -- that's a conference that Invensense put on a couple months ago.
Second, no, linear acceleration is in device coordinates, not world coordinates. You'll have to convert yourself, which means knowing the device's 3-d orientation.
What you want to do is use a version of Android that supports the virtual sensors TYPE_GRAVITY and TYPE_LINEAR_ACCELERATION. You'll need a device with gyros to get reasonably accurate and precise readings.
Internally, the system combines gyros, accelerometers, and magnetometers in order to come up with true values for the device orientation. This effectively splits the accelerometer device into its gravity and acceleration components.
So what you want to do is to set up sensor listeners for TYPE_GRAVITY, TYPE_LINEAR_ACCELERATION, and TYPE_MAGNETOMETER. Use the gravity and magnetometer data as inputs to SensorManager. getRotationMatrix() in order to get the rotation matrix that will transform world coordinates into device coordinates or vice versa. In this case, you'll want the "versa" part. That is, convert the linear acceleration input to world coordinates by multiplying them by the transpose of the orientation matrix.

Android orientation as steering - roll corrupting pitch?

I am working on a project which includes an Android application which is used for
controlling/steering.
Speed: When you tilt the phone forward/backwards (pitch) it simulates giving gas and breaking.
Direction: When you tilt the phone left/right (roll) it simulates steering to the left and right.
I have already written some code which seemed to work fine. But when I took a closer look, I found that some values are acting weird.
When I tilt the phone forward/backward to handle the speed it works perfect I get the expected speed and direction values. But when I tilt the phone to the left/right to handle the direction it seems to corrupt some values. When it is tilting to the left/right that doesn't only change the direction value (roll) but it also affects the speed value (pitch).
For extra information:
Programming for Android 2.2
Device is an Google Nexus One
Holding the device in portrait
The most relevant code I use to read the sensor values is as follows:
public void onSensorChanged(SensorEvent sensorEvent)
{
synchronized (this)
{
if (sensorEvent.sensor.getType() == Sensor.TYPE_ORIENTATION)
{
float azimuth = sensorEvent.values[0]; // azimuth rotation around the z-axis
float pitch = sensorEvent.values[1]; // pitch rotation around the x-axis
float roll = sensorEvent.values[2]; // roll rotation around the y-axis
System.out.println("pitch: " + pitch);
System.out.println("roll: " + roll);
System.out.println("--------------------");
// Convert the sensor values to the actual speed and direction values
float speed = (pitch * 2.222f) + 200;
float direction = roll * -2.222f;
So when I run the code, and I look at the printed values. When tilting the device left/right, it seems to affect the pitch value as well. How come? And how can I get the pure pitch value, when 'roll'-ing? So that tilting the phone to the left/right doesn't affect/corrupt the pitch value.
You could read up on Gimbal lock. That's bitten me before.

Categories

Resources