I used information and code from this answer (rewriting code to Javascript) in my simple demo PhoneGap Buld application, to recalculate gravity (G) to real acceleration (m/s2) with 1 second frequency.
This is actual code (important part):
function onAccelerationSuccess(acceleration)
{
var g = 9.80665;
acceleration.x = (acceleration.x * g).toFixed(2) + ' m/s\u00b2';
acceleration.y = (acceleration.y * g).toFixed(2) + ' m/s\u00b2';
acceleration.z = ((acceleration.z + 1) * g).toFixed(2) + ' m/s\u00b2';
...
}
watchID = navigator.accelerometer.watchAcceleration(onAccelerationSuccess, onAccelerationError, {frequency: 1000});
Mentioned answer and many, many sources claims, that with my phone lying on the table face-up, I should get values of (0, 0, -1) G for the x, y, and z axes respectively. Assuming Earth's natural acceleration (g = 9.80665), I should see real acceleration values of (0, 0, 9.81) m/s2 and these values should not change (as phone is resting still). Am I right?
However, actually I'm seeing, that my real values are:
X axis: -1.87, -1.50, -2.25,
Y axis: 2.26, 1.88, 1.51, 0.76,
Z axis: 101.87, 101.49, 102.25, 102.62, 103.37.
These values are constantly changing, but only between these mentioned, and not every axis gets changed value each second. Sometimes, a value for some axis remains for 2-3 seconds.
What is happening? How can a phone variate its acceleration, if it is holding still on my desk? How can any device, that is not moving in any direction have such enormous acceleration like 100 m/s2?
I have heard that accelerators on-board mobile devices are more like toy than real a measurement device and that they're producing a lot of noise or jitter to returned values. But, for God sake, this is a complete garbage, that is making use of this function completely pointless.
I tested this code on Google Nexus (first edition) phone, with Android 4.2.2. App with Phonegap 2.9.0.
EDIT: I've tested my mobile application with Ripple Emulator and I'm getting perfectly valid values:
Acceleration in the X axis is 0.00 m/s².
Acceleration in the Y axis is 0.00 m/s².
Acceleration in the Z axis is 9.81 m/s².
Is something wrong with accelerometer / compass / gyro on-board my Nexus?
There is no bug. I've read many SO questions about iOS native programming in ObjectiveC, when I was dealing with accelerometer. And I missed PhoneGap API documentation, which says, that values passed are already recalculated:
Acceleration values include the effect of gravity (9.81 m/s^2).
After removing double gravity calculation all seems to be fine.
I'm betting values +/-0.3 m/s2 for x and y axis and around 10.3-10.4 m/s2 for z axis, when phone is lying on my desk. But I assume, these are variations and mentioned noise, that is comming from fairly cheap accelerometer chip used in mobile devices.
Related
I've been trying for a long time to try to figure out why the way my game plays varies slightly differently on different devices (i.e.: some devices seem to be more sensitive than others with regards to the accelerometers).
I've just noticed that the when tilting my devices and logging the output, on one device, the output seems to be between -9.5 and + 9.5 and on another, it appears to be around -10.7 to +10.7.
I'm using this returned data to move my sprite.
#Override
public void onSensorChanged(SensorEvent event) {
tiltAmount = event.values[device_rotation];
Log.v("Tag", "Value: " + tiltAmount);
}
In the above code example, as I'm tilting the device to the opposite extremes (90° anti-clockwise and 90° clockwise), I'm getting the ranges described above.
I would like this data to be consistent across device.
Does anyone have any idea how I could normalise it?
It seems like you have extreme values defined. In that scenario, you could divide a given value by the total dynamic range to get normalized value:
NormalizedValue = x / (Highest - Lowest)
I am making a 2d game. The phone is held horizontally and a character moves up/down & left/right to avoid obstacles. The character is controlled by the accelerometer on the phone. Everything works fine if the player doesn't mind (0,0) (the point where the character stands still) being when the phone is held perfectly flat. In this scenario it's possible to just read the Y and X values directly and use them to control the character. The accelerometer values are between -10 and 10 (they get multiplied by an acceleration constant to decide the movement speed of the character), libgdx is the framework used.
The problem is that having (0,0) isn't very comfortable, so the idea is to calibrate it so that 0,0 will be set to the phones position at a specific point in time.
Which brings me to my question, how would I do this? I tried just reading the current X and Y values then subtracting it. The problem with that is that when the phone is held at a 90 degree angle then the X offset value is 10 (which is the max value) so it ends up becoming impossible to move because the value will never go over 10 (10-10 = 0). The Z axis has to come into play here somehow, I'm just not sure how.
Thanks for the help, I tried explaining as best as I can, I did try searching for the solution, but I don't even know what the proper term is for what I'm looking for.
An old question, but I am providing the answer here as I couldn't find a good answer for Android or LibGDX anywhere. The code below is based on a solution someone posted for iOS (sorry, I have lost the reference).
You can do this in three parts:
Capture a vector representing the neutral direction:
Vector3 tiltCalibration = new Vector3(
Gdx.input.getAccelerometerX(),
Gdx.input.getAccelerometerY(),
Gdx.input.getAccelerometerZ() );
Transform this vector into a rotation matrix:
public void initTiltControls( Vector3 tiltCalibration ) {
Vector3.tmp.set( 0, 0, 1 );
Vector3.tmp2.set( tiltCalibration ).nor();
Quaternion rotateQuaternion = new Quaternion().setFromCross( Vector3.tmp, Vector3.tmp2 );
Matrix4 m = new Matrix4( Vector3.Zero, rotateQuaternion, new Vector3( 1f, 1f, 1f ) );
this.calibrationMatrix = m.inv();
}
Whenever you need inputs from the accelerometer, first run them through the rotation matrix:
public void handleAccelerometerInputs( float x, float y, float z ) {
Vector3.tmp.set( x, y, z );
Vector3.tmp.mul( this.calibrationMatrix );
x = Vector3.tmp.x;
y = Vector3.tmp.y;
z = Vector3.tmp.z;
[use x, y and z here]
...
}
For a simple solution you can look at the methods:
Gdx.input.getAzimuth(), Gdx.input.getPitch(), Gdx.input.getRoll()
The downside is that those somehow use the internal compass to give your devices rotation compared to North/South/East/West. I did only test that very shortly so I'm not 100% sure about it though. Might be worth a look.
The more complex method involves some trigonometry, basically you have to calculate the angle the phone is held at from Gdx.input.getAccelerometerX/Y/Z(). Must be something like (for rotation along the longer side of the phone):
Math.atan(Gdx.input.getAccelerometerX() / Gdx.input.getAccelerometerZ());
For both approaches you then store the initial angle and subtract it later on again. You have to watch out for the ranges though, I think Math.atan(...) is within -Pi and Pi.
Hopefully that'll get you started somehow. You might search for "Accelerometer to pitch/roll/rotation" and similar, too.
Is there a way to get the velocity without GPS in Android? I don't need the accurate values.
Well, sort of, but you will need to do alot of processing..
You can make frequent accelerometer readings and integrate the values once to get velocity. This won't get you an accurate starting velocity but after a while it will probably work (unless you start when the phone is driving along in a car). See also this post.
Now, some pseudo code:
We start at t=0 and measure acceleration in all three axis.
a = get_acceleration()
vx = vx + a.x - gravity.x;
vy = vy + a.y - gravity.y;
vz = vz + a.z - gravity.z;
After doing this for a few seconds, the sum of all the acceleration values (if you sample frequently, ie, 50Hz) should be velocity. You will also need to work out which way up your device is and therefore how much of the acceleration components you are reading is due to gravity and compensate.
I'm trying to figure out what the meaning of the output from gravity sensors means in Android.
I understand is the gravity applied in X, Y and Z, what I don't understand is the difference with accelerometer sensor.
I know gravity is a fuse sensor which uses gyroscope as well and others...but getting for example a value of 29.4 (9.8 * 3) in the gravity[0] sensor means that there is a force of 3G applied in the X axle?
So it would similar to use acceleration = SQRT(X^2 + Y^2 + Z^2) but more precise?
Thanks in advance! Guillermo.
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)