android remove gravity from accelerometer readings - android

I am developing an application for Android where I need to remove gravity from accelerometer readings. I have read multiple discussions on this problem, I have also found an algorithm here, but I didn't really understand it.
I want to filter gravity from each axis, not from the total acceleration.
Could you please help me out? My code should be something like:
public void onSensorChanged(SensorEvent sensorEvent) {
float vals[] = sensorEvent.values;
float accelerationX = filterGravity(vals[0]);
float accelerationY = filterGravity(vals[1]);
float accelerationZ = filterGravity(vals[2]);
}
What code should I place in the filterGravity() method?

For a basic solution you would need a low pass filter other approaches like a Kalman filter are pretty tough regarding the maths behind. A simple example for Android is one click away from your link at http://developer.android.com/reference/android/hardware/SensorEvent.html#values.
Simply spoken a low pass filter builds a weighted average from all your history values. If you have for example a filtering factor of 0.1 it means that 10% of your current value is added to the previous mean value: newMeanValue = 10% of currentValue + 90% of oldMeanValue. That means even if there is an abrupt peak it will only push your mean value slowly because of the 10%.

Linear acceleration is what you need. Check Sensor.TYPE_LINEAR_ACCELERATION here.

If you do not have a phone with TYPE_LINEAR_ACCELERATION, you are stuck with TYPE_ACCELERATION, which cannot separate gravity (tilt) from linear acceleration.
One option is to apply the low-pass filter. Another approach is using sensor fusion if the gyroscope is available. Both approaches have their advantages and disadvantages.
I have lots of working examples in the open source project Acceleration Explorer.

Not too sure what you are trying to acomplish but if you are look for the magnitude (which will give a result between 0 and 1) then all you do is divide the result by 10
public void onSensorChanged(SensorEvent sensorEvent) {
float vals[] = sensorEvent.values;
float accelerationX = (vals[0]/10);
float accelerationY = (vals[1]/10);
float accelerationZ = (vals[2]/10);
}
In a game environment when the sensor is at full tilt your object will be at its maximum speed

Related

Get min and max values for getOrientation on accelerometer?

I am currently working with the accelerometer on Android, and have run into an interesting situation. I need to find out the maximum values for the following, WITHOUT requiring the end user to flop their phone around in some elaborate calibration routine. The values I am looking for specifically are...
#Override
public void onSensorChanged(SensorEvent event) {
SensorManager.getOrientation(outR, xyz);
float X, Y, XMax, YMax;
X = xyz[1];
Y = xyz[2];
XMax = ???;
YMax = ???;
}
I've poked around online, but have not had much luck determining what the values would be (it appears it changes from phone to phone), nor seen anyone mention a way to pull that value from somewhere.
Does anyone know how I might determine the maximum/minimum values of xyz[1] and xyz[2]?
UPDATE: An Example of what I mean is the following:
When I report directly the outputed float from xyz[1], and then move my phone forward and backward all the way, I get the following results:
min value it reports: -1.50 (give or take a 10ths)
max value it reports: 1.50 (give or take a 10ths)
when I roll my phone left and right, I get the following for xyz[2]:
min value it reports: -3.14 (give or take a 10ths)
max value it reports: 3.14 (give or take a 10ths)
I was told this changes between phones, however this could be wrote.
I don't know what max/min values you're talking about but getOrientation will populate a vector with the following data:
xyz[0]: azimuth [-π,π]
xyz[1]: pitch [-π,π]
xyz[2]: roll [-π/2,π/2]
From docs:
All three angles above are in radians and positive in the
counter-clockwise direction.
Refer also to the SensorManager.java:1094 class:
https://android.googlesource.com/platform/frameworks/base/+/b267554/core/java/android/hardware/SensorManager.java

Gyroscope does not show any drift

I'm in a little bit weird situation. The situation I currently have is typically a good one - no gyro drift at all - but I have no clue why this is the case. I expected the gyroscope to drift a lot as reported everywhere. Therefore this question to find the reason why I do not see any drift.
I use a Galaxy Nexus (Android 4.0.3) and its gyroscope to do some orientation change detection in the end. In the first place, I just wanted to log the sensor readings and expected to see large drifting values as for example here. Also on other websites I read of drifting of about 1 degree per second or similar measurements.
My code to log the sensor data is very basic:
SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);
LogSensorEventListener listener = new LogSensorEventListener(
SensorLoggerActivity.this, Sensor.TYPE_GYROSCOPE);
sm.registerListener(listener, sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
SensorManager.SENSOR_DELAY_FASTEST);
LogSensorEventListener is also a very basic implementation of the SensorEventListenerInterface:
#Override
public void onSensorChanged(SensorEvent event) {
final float dT = (event.timestamp - mTimestamp) / 1000000000.f;
if (dT < 1) { // just a fix for the first step
mRelZ += event.values[2] * dT;
list.add(mRelZ);
list2.add(event.timestamp);
}
mTimestamp = event.timestamp;
mRelZ is initially set to 0 and the two lists should keep track of time and measured value. If finished with my measurement (clicking a button) the values are written to file:
try {
for (int i = 0; i < list.size(); i++) {
long time = list2.get(i);
if (mStartTime < 0) {
mStartTime = time;
time = 0;
} else {
time = time - mStartTime;
}
float timef = time / 1000000000.0f;
sb.append(timef);
sb.append(";");
sb.append(Math.toDegrees(list.get(i)));
sb.append("\n");
}
mOutFileStream.write(sb.toString().getBytes());
mOutFileStream.flush();
mOutFileStream.close();
} catch (IOException e) {
}
Everything works fine but the only drift I can see (for example when having the device lying on a table and logging values for let's say 60 seconds) is about 0.2 degree.
If I move the device to get something like in the screenshot above no drift can be observed at all...
What I'm I doing wrong (or right?)??
Thanks for any advice!
the gyro you have might be a post-filtered one, i found this in some of the SAMSUNG I9100 devices. they use gyros produced by INVENSENSE CORP.
edit 2014/12/10 for commenting #Lourenço Castro
i believe #Lourenço Castro was right about the 'factory drift'(or zero-drift) and 'accumulated drift';
Concerning to complementary/kalman filters, on Android, the 6dof(ACC+GYRO) impl. works fine, it can remove both factory and accumulated drift of gyroscope thanks to accelerometer's calibration;
However, the 9dof impl., or adding a magnetometer sensor into consideration, filter runs into status you dont want. According to my tests, it is caused by uncalibed status of magnetometer which need guys to shake (known as drawing 'eight')the phone to remove before fusion.
9dof provides a full rotation estimation while 6dof can not handle the accumulated drift of rotating around y axis(see android's ref. for axis definition). And I dont think that 'shaking-and-drawing-number-8' before one starts an app is a good UE, so we have to go back to 6dof method and try to find a way to remove factory drift of rotation around y-axis.
(a lot of boring but amazing content about complementary/kalman filter should be here. but i guess those come here already know it.)
You can try a 360 panorama app included in Google Camera which can be downloaded from market. This app use a visual-aid(image processing based motion estimation) for calibrating the sensor before we really start to capturing, it's easy to verify this. And no use of Magnetometer, i guess.
So my advice to use sensor-fusion on Android is:
1. no magnetometer; (dont know if this is a hardware problem or can be solved by software.)
2. accelerometer+gyroscope provides smooth and stable 6dof motion estimation;
3. try to solve the drift around y-axis, mainly means that visual-method should be added to;
4. try 3 and try 3...
Maybe someone will run into this question and i hope all above might be helpful.
thanks to those posts related to this questions on StackOverflow.
i dont remember your names exactly but you all helped me a lot. :)
I was browsing for this subject and, although this is an old question, I don't believe the accepted answer is correct.
The drift that is "reported everywhere" is caused by the integration of noisy gyroscope data (as seen on the link provided by the O.P.), not by simply outputting the sensor data. You will need this integration to calculate orientation changes over time.
Longer integration periods will contain larger noise amounts which will accumulate drift fairly quickly. I believe the internal filtering of the signal which happens on Invensense hardware is used to remove drift caused by their own pre-processing of the gyroscope.
In Android's case, in API 18 (Jelly Bean MR2), a GYROSCOPE_UNCALIBRATED sensor was added, in which you can (presumably) verify this factory drift calibration. Anyway, when you try to integrate either the calibrated and uncalibrated sensor events, you will get drift, since both are pretty noisy. To reduce this issue you will have to delve into complementary or Kalman filters.
(Sorry about not posting more informational links, not enough reputation).
Hope this helps future users.

Android sensor: getRotationMatrix() returns wrong values, why?

It's past several days since I started using this function and have not yet succeeded in obtaining valid results.
What i want is basically convert acceleration vector from device's coordinates system, to real world coordinates. I' know that is possible because i have acceleration in relative coordinates and i know the orientation of the device in real world system.
Reading Android developers seems that using getRotationMatrix() i get R = rotation matrix.
So if i want A (acceleration vector in world system) from A' (acceleration vector in phone system) i must do simply:
A=R*A'
But i cant'n understand why the vector A has ALWAYS the first and the second component zero (example: +0,00;-0,00;+6,43)
My current code is similar to this:
public void onSensorChanged(SensorEvent event) {
synchronized (this) {
switch(event.sensor.getType()){
case Sensor.TYPE_ACCELEROMETER:
accelerometervalues = event.values.clone();
break;
case Sensor.TYPE_MAGNETIC_FIELD:
geomagneticmatrix =event.values.clone();
break;
}
if (geomagneticmatrix != null && accelerometervalues != null) {
float[] Rs = new float[16];
float[] I = new float[16];
SensorManager.getRotationMatrix(Rs, I, accelerometervalues, geomagneticmatrix);
float resultVec[] = new float[4];
float relativacc[]=new float [4];
relativacc[0]=accelerationvalues[0];
relativacc[1]=accelerationvalues[1];
relativacc[2]=accelerationvalues[2];
relativacc[3]=0;
Matrix.multiplyMV(resultVec, 0, Rs, 0, relativacc, 0);
//resultVec[] is the vector acceleration relative to world coordinates system..but doesn't WORK!!!!!
}
}
}
This question is very similar to this one Transforming accelerometer's data from device's coordinates to real world coordinates but there i can't find the solution...i had tried all the ways..
Please help me, i need help!!!
UPDATE:
Now my code is below, i had tried to explain matrix product, but nothing change:
float[] Rs = new float[9];
float[] I = new float[9];
SensorManager.getRotationMatrix(Rs, I, accelerationvalues, geomagneticmatrix);
float resultVec[] = new float[4];
resultVec[0]=Rs[0]*accelerationvalues[0]+Rs[1]*accelerationvalues[1]+Rs[2]*accelerationvalues[2];
resultVec[1]=Rs[3]*accelerationvalues[0]+Rs[4]*accelerationvalues[1]+Rs[5]*accelerationvalues[2];
resultVec[2]=Rs[6]*accelerationvalues[0]+Rs[7]*accelerationvalues[1]+Rs[8]*accelerationvalues[2];
Here some example of data read and result:
Rs separated by " " Rs[0] Rs[1]....Rs[8]
Av separated by " " accelerationvalues[0] ...accelerationvalues[2]
rV separated by " " resultVec[0] ...resultVec[2]
As you can notice the component on x and y axes in real world are zero (around) even if you move speddy the phone. Instead the relative acceleration vector detect correctly each movement!!!
SOLUTION
The errors in the numberrs are relative to float vars multiplication that is not the same as a double multyplication.
This sums to the fact that rotation matrix isn't costant if the phone, even if with the same orientation, is accelerating.
So is impossible translate acceleration vector to absolute coordinates during motion...
It's hard but it's the reality.
Finnaly i found the answer:
The errors in the numbers are relative to float vars multiplication that is not the same as a double multyplication. Here there is the solution.
This sums to the fact that rotation matrix isn't costant if the phone, even if with the same orientation, is accelerating. So is impossible translate acceleration vector to absolute coordinates during motion... It's hard but it's the reality.
FYI the orientation vector is made from magnetomer data AND gravity vector. This cause a ciclic problem: convert relative acc needs oirentation needs magnetic field AND gravity, but we know gravity only if the phone is stop by relative acc..so we are return to begin.
This is confirmed in Android Developers where is explained that rotation matrix give true result only when the phone isn't accelerate (e.g. they talk of free fall, infact there shouldn't be gravity mesaurement) or when it isn't in a non regulare magnetic field.
The matrices returned by this function are meaningful only when the
device is not free-falling and it is not close to the magnetic north.
If the device is accelerating, or placed into a strong magnetic field,
the returned matrices may be inaccurate.
In others world, fully un-useful...
You can trust this thing doing simple experiment on the table with Android Senor or something like this..
You must track down this arithmetic error before you worry about rotation, acceleration or anything else.
You have confirmed that
resultVec[0]=Rs[0]*accelerationvalues[0];
gives you
Rs[0]: 0.24105562
accelerationValues[0]: 6.891896
resultVec[0]: 1.1920929E-7
So once again, simpify. Try this:
Rs[0] = 0.2;
resultVec[0] = Rs[0] * 6.8
EDIT:
The last one gave resultVec[0]=1.36, so let's try this:
Rs[0] = 0.2;
accelerationValues[0] = 6.8
resultVec[0] = Rs[0] * accelerationValues[0];
If you do the sums, using the printed values you have appended, I get
`(0.00112, -0.0004, 10)`
which is not as small as what you have. Therefore there is an arithmetic error!
Could the problem be that you are using accelerationvalues[] in the last block, and accelerometervalues[] later?
I have developed several applications that make use of android sensors, so I am answering to one of your questions according to my experience:
But i cant'n understand why the vector A has ALWAYS the first and the
second component zero (example: +0,00;-0,00;+6,43)
I have observed this problem with the acceleration sensor and the magnetic field sensor, too. The readings are zero for some of the axis (two as you point, or just one in other occasions). This problem happens when you have just enabled the sensors (registerListener()) and I assume that it is related to some kind of sensor initialization.
In the case of the acceleration sensor, I have observed that just a small shaking of the device makes it to start giving correct sensor readings.
The correct solution would be the method onAccuracyChanged() giving the correct information about the sensor state. It should be returning a staus of SensorManager.SENSOR_STATUS_UNRELIABLE, but instead of that, it permanently returns SensorManager.SENSOR_STATUS_ACCURACY_HIGH on all physical devices that I have tested so far. With the method onAccuracyChanged() properly implemented, you could ignore bad readings or ask the user to wait while the sensor is being initialized.

How can I find the velocity using accelerometers only?

Using only the phone's (Android) built in accelerometer, how would I go about finding its velocity?
I have been tinkering with the maths of this but whatever function I come up with tends to lead to exponential growth of the velocity. I am working from the assumption that on startup of the App, the phone is at a standstill. This should definitely make finding the velocity (at least roughly) possible.
I have a decent background in physics and math too, so I shouldn't have any difficulty with any concepts here.
How should I do it?
That will really depend on what the acceleration is and for how long. A mild, long acceleration could be measurable, but any sudden increase in acceleration, followed by a constant velocity, will make your measurements quite difficult and prone to error.
Assuming constant acceleration, the formula is extremely simple: a = (V1-V0)/t . So, knowing the time and the acceleration, and assuming V0 = 0, then V1 = a*t
In a more real world, you probably won't have a constant acceleration, so you should calculate Delta V for each measurement, and adding all those changes in velocity to get the final velocity. Always consider that you won't have a continuous acceleration data, so this is the most feasible way (i.e, real data vs integral math theory).
In any way, even in the best scenario, you will end up with a very high error margin, so I do not recommend this approach for any app that truly depends on real velocities.
First, you have to remove the acceleration due to gravity from the accelerometer data. Then it's just a matter of integrating the acceleration to get the velocity. Don't forget that acceleration and velocity are properly vectors, not scalars, and that you will also have to track rotation of the phone in space to properly determine the orientation of the acceleration vector with respect to the calculated velocity vector.
There is nothing else to do but agree with the reasonable arguments put forward in all the great answers above, however if you are the pragmatic type like me, I need to come up with a solution that works somehow.
I suffered a similar problem to yours and I decided to make my own solution after not finding any on-line. I only needed a simple "tilt" input for controlling a game so this solution will probably NOT work for more complex needs, however I decided to share it in case others where looking for something similar.
NOTE: I have pasted my entire code here, and it is free to use for any purpose.
Basically what I do in my code is to look for accelerometer sensor. If not found, tilt feedback will be disabled. If accelerometer sensor is present, I look for magnetic field sensor, and if it is present, I get my tilt angle the recommended way by combining accelerometer and magnetic field data.
public TiltSensor(Context c) {
man = (SensorManager) c.getSystemService(Context.SENSOR_SERVICE);
mag_sensor = man.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
acc_sensor = man.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
has_mag = man.registerListener(this, mag_sensor, delay);
has_acc = man.registerListener(this, acc_sensor, delay);
if (has_acc) {
tiltAvailble = true;
if (has_mag) {
Log.d("TiltCalc", "Using accelerometer + compass.");
}
else {
Log.d("TiltCalc", "Using only accelerometer.");
}
}
else {
tiltAvailble = false;
Log.d("TiltCalc", "No acceptable hardware found, tilt not available.");
//No use in having listeners registered
pause();
}
}
If however only the accelerometer sensor was present, I fall back to accumulating the acceleration, that is continuously damped (multiplied by 0.99) to remove any drift. For my simple tilt needs this works great.
#Override
public void onSensorChanged(SensorEvent e) {
final float[] vals = e.values;
final int type = e.sensor.getType();
switch (type) {
case (Sensor.TYPE_ACCELEROMETER): {
needsRecalc = true;
if (!has_mag) {
System.arraycopy(accelerometer, 0, old_acc, 0, 3);
}
System.arraycopy(vals, 0, accelerometer, 0, 3);
if (!has_mag) {
for (int i = 0; i < 3; i++) {
//Accumulate changes
final float sensitivity = 0.08f;
dampened_acc[i] += (accelerometer[i] - old_acc[i]) * sensitivity;
//Even out drift over time
dampened_acc[i] *= 0.99;
}
}
}
break;
case (Sensor.TYPE_MAGNETIC_FIELD): {
needsRecalc = true;
System.arraycopy(vals, 0, magnetic_field, 0, 3);
}
break;
}
}
In conclusion I will just repeat that this is probably not "correct" in any way, it simply works as a simple input to a game. To use this code I simply do something like the following (yes magic constants are bad mkay):
Ship ship = mShipLayer.getShip();
mTiltSensor.getTilt(vals);
float deltaY = -vals[1] * 2;//1 is the index of the axis we are after
float offset = ((deltaY - (deltaY / 1.5f)));
if (null != ship) {
ship.setOffset(offset);
}
Enjoi!
Integrating acceleration to get velocity is an unstable problem and your error will diverge after a couple of seconds or so. Phone accelerometers are also not very accurate, which doesn't help, and some of them don't allow you to distinguish between tilt and translation easily, in which case you're really in trouble.
The accelerometers in a phone are pretty much useless for such a task. You need highly accurate accelerometers with very low drift - something which is way beyond what you will find in a phone. At best you might get useful results for a few seconds, or if very lucky for a minute or two after which the results become meaningless.
Also, you need to have a three axis gyroscope which you would use to integrate the velocity in the right direction. Some phones have gyros, but they are even poorer than the accelerometers as far as drift and accuracy are concerned.
One possibly useful application though would be to use the accelerometers in conjunction with gyros or the magnetic compass to fill in for missing data from the GPS. Each time the GPS gives a good fix one would reset the initial conditions of position, speed and orientation and the accelerometers would provide the data until the next valid GPS fix.
Gravity is going to destroy all of your measurements. The phone, at standstill, is experiencing a high constant upward (yes, UP) acceleration. An accelerometer can't distinguish between acceleration and gravity (technically, they are the same), so it would get to extremely high velocities after a few seconds.
If you never tilt your accelerometer even slightly, then you can simply subtract the constant gravitional pull from the z-axis (or whichever axis is pointing up/down), but thats quite unlikely.
Basically, you have to use a complicated system of a gyroscope/magnetometor and an accelerometer to calculate the exact direction of gravity and then subtract the acceleration.
v = Integral(a) ?
Generally though, I'd think the inaccuracies in the accelerometers would make this quite tough
If the phone is at standstil, you have ZERO acceleration, so your speed is 0. Probably you should find location data from GPS and get the associated time samples and compute velocity distance over time.

Accelerometer values behaving like tilt meter?

The values I'm getting for accel, x, y and z below are not as expected.
It seems to be acting as a Tilt sensor rather than accelerometer.
When I throw the phone in the air and catch it, the accel value doesn't change by more than about 10%. Contrast this to when I rotate the phone randomly, I get much larger variations of 50-100%!
What could explain this? I simply want to detect when the phone is in freefall, (and/or impacting something).
SensorManager sm = (SensorManager)getSystemService(SENSOR_SERVICE);
Sensor sensor = sm.getDefaultSensor(SensorManager.SENSOR_ACCELEROMETER);
sm.registerListener(
sel = new SensorEventListener(){
#Override public void onAccuracyChanged(Sensor arg0, int arg1) {}
#Override public void onSensorChanged(SensorEvent arg0) {
double x = se.values[0];
double y = se.values[1];
double z = se.values[2];
double accel = Math.sqrt(
Math.pow(x, 2) +
Math.pow(y, 2) +
Math.pow(z, 2));
}
},
sensor,
SensorManager.SENSOR_DELAY_NORMAL
);
(As a side question, the values for x, y and z seem much higher than they should be, with accel averaging at about 50-80, when standing still? Shouldn't it be around 9.8?)
The x, y and z values seem very sensitive to changes in the orientation of the phone, but not at all representative of acceleration. Am I missing something??
Example values with phone still, lying on back:
Accel = 85.36, x = 6.8, y = 45.25 z = 30.125
I had to replace
Sensor sensor = sm.getDefaultSensor(SensorManager.SENSOR_ACCELEROMETER);
with
Sensor sensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Could be because when you throw the phone it is almost the same plane or angle and at low speed, when you turn the phone it changes its course and orientation rapidly and that gives higher values for the result. Accelerometer may be a misnomer for a multi-function device, there could be a selection parameter for the function you really want to get results from.
With the phone lying on its back you should get close to zero on the X and Y sensors and about 9.8 on the Z sensor. The 9.8 is of course due to gravity.
My first though would be that there is something wrong with the phone and would suggest trying same code on another phone.
However I notice that there is something wrong in the math but haven;t figured out what yet.
with x,y,z having the values you mention the resultant (square root of sum of squares) works out to 54.78 rather than 85.36 as you mention in your post.
I'm quite new to Java so I cannot easily spot what might be wrong and haven't had the opportunity yet to try that piece of code on my phone, but I think the math is simple enough for me to determine that the result is wrong. (or at least I hope so).
The other thing to check (assuming you figureout the math problem) is that the small change when you throw the phone in the air might simply be due to the slow response time. The accelerometer output may simply be changing too slowly so by the time the phone has landed the output wouldn't have changed that much. The response can be improved by using SENSOR_DELAY_GAME or SENSOR_DELAY_FASTEST instead of normal.
By the way, shouldn't that be arg0.values[] rather than se.values[]? Where does the se come from? The sensor values go into the argument of the onSensorChanged (arg0 in this case) so I cannot figure out how they are supposed to end up in se. (But then again there are many things in Java I still don't understand)

Categories

Resources