I am trying to make an app has a reset button and displays how much phone has moved linearly as well as angularly. It has been difficult because I don't know much about accelerometer and gyroscope or even android programming. So I have been thinking about just using these data I got from the app called Sensor Kinetic first to do the calculation to measure how much phone moved linearly and angularly.
The two images below are the sample data (time, X, Y, Z). I believe it can be done with these data should be but I haven't progressed for the past few hours researching. I know they are not syncing in term of time but I only the starting point and the last point to measure.
gyroscope_sample_data rad/s :
accelerometer_sample_data m/s^2 :
I have been trying too look up how to measure the distance angularly and linearly but I found a lot of work talking about it but nothing seems to have done it this way or measure both linear and angular distance.
Edited: I wrote this to calculate the distance the phone move linearly with the accelerometer but I received around 103 meter for only rotating the phone around me. I am also not sure about the gyroscope for angular distance either so let me know what you all think.
def acc_calculation(dataset):
dx, dy, dz = 0.0, 0.0, 0.0
vx, vy, vz = 0.0, 0.0, 0.0
acceleration_x, acceleration_y, acceleration_z = dataset['X_value'], dataset['Y_value'], dataset['Z_value']
for i in range(1, len(dataset['time'])):
dt = float(dataset['time'][i]) - float(dataset['time'][i-1])
print(dt)
vx += (float(acceleration_x[i-1]) + float(acceleration_x[i]))/2.0*dt
vy += (float(acceleration_y[i-1]) + float(acceleration_y[i]))/2.0*dt
vz += (float(acceleration_z[i-1]) + float(acceleration_z[i]))/2.0*dt
dx += vx*dt;
dy += vy*dt;
dz += vz*dt;
dl = math.sqrt(dx**2 + dy**2 + dz**2)
return dl
I am trying to post more related links but stackoverflow doesn't let me. If anyone has any leads and especially detailed information about it for apps or work that already done it, I would really appreciate.
Accelerometer & Gyro Tutorial :
http://www.instructables.com/id/Accelerometer-Gyro-Tutorial/step3/Combining-the-Accelerometer-and-Gyro/#intro
How can I find distance traveled with a gyroscope and accelerometer?
Related
I am getting raw acceleration data from an accelerometer and am trying to double integrate it in order to get the position.
The android phone used to get the data is set on a flat surface for 3 seconds to diminish drift. I take the mean of acceleration over the resting period to zero out the beginning. This worked out fine, but when we integrate to velocity and position (using cumtrapz) we are getting unrealistically high y values (meters/s for velocity and meters for position.)
The raw data is waving the phone at a certain tempo.
Does anyone have ideas on why the position gets such high values?
Below are the graphs showing what I described as well as my code.
Edit: Even when the phones is not rotated, the values are unrealistic and not indicative of how the phone moved. In the attached pictures, the phone was moved in the shape of a box on a flat surface with no rotation involved.
%VarName2 = accelerometer values in X direction
%VarName3 = accelerometer values in Y direction
%VarName4 = accelerometer values in Z direction
%elapsedArray = time values for each sample of accelerometer data
ddx = VarName2 - mean(VarName2(1:limit));
ddx = ddx(1:length(ddx)-200);
elapsedArray = elapsedArray(1:length(elapsedArray)-200);
ddy = VarName3 - mean(VarName3(1:limit));
ddy = ddy(1:length(ddy)-200);
ddz = VarName4 - mean(VarName4(1:limit));
ddz = ddz(1:length(ddz)-200);
velX = cumtrapz(ddx .* elapsedArray);
velY = cumtrapz(ddy .* elapsedArray);
velZ = cumtrapz(ddz .* elapsedArray);
dx = velX - mean(velX(1:limit));
dy = velY - mean(velY(1:limit));
dz = velZ - mean(velZ(1:limit));
posX = cumtrapz(dx .* elapsedArray);
posY = cumtrapz(dy .* elapsedArray);
posZ = cumtrapz(dz .* elapsedArray);
x = posX - mean(posX(1:limit));
y = posY - mean(posY(1:limit));
z = posZ - mean(posZ(1:limit));
figure;
plot(ddx);
title('Acceleration in X')
xlabel('Time (sec)')
ylabel('Acc (meters squared');
figure;
plot(dx);
title('Velocity in X')
xlabel('Time (sec)')
ylabel('Velocity (meters)');
figure;
plot(x);
title('Position X')
xlabel('Time (sec)')
ylabel('Position (meters)');
figure;
plot(y);
title('Position Y')
xlabel('Time (sec)')
ylabel('Position (meters)');
figure;
plot(z);
title('Position Z')
xlabel('Time (sec)')
ylabel('Position (meters)');
Acceleration in X direction
Velocity and Position in X direction
What you are seeing is the result of time drift. Let's assume that the accelerometer readings you are measuring have a very small error, dErr, at every time point. Once you integrate these values to get velocity, the error at each time point will be multiplied by a factor t. Integrating a second time to get position will cause the original error to be multiplied by a factor of t^2. Therefore, the error at each time point will propogate at dErr(t)*t^2.
In order to get a good estimate for position, you can try to incorporate prior information about position, but will likely have to use a combination of accelerometer and gyroscope data. You might also have to look into Kalman Filters.
Here is a Google Tech Talk explaining this issue:
https://youtu.be/C7JQ7Rpwn2k?t=23m33s
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.
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 want to be able to feature a fairly simple fall detection algorithm in my application. At the moment in onSensorChanged(), I am getting the absolute value of the current x,x,z values and subtracting SensorManager.GRAVITY_EARTH (9.8 m/s) from this. The resulting value has to be bigger than a threshold value 10 times in a row to set a flag saying a fall has been detected by the accelerometer, the threshold value is about 8m/s.
Also I'm comparing the orientation of the phone as soon as the threshold has been passed and the orienation of it when the threshold is no longer being passed, this sets another flag saying the orientation sensor has detected a fall.
When both flags are set, an event occurs to check is user ok, etc etc. My problem is with the threshold, when the phone is held straight up the absolute value of accelerometer is about 9.8 m/s, but when i hold it still at an angle it can be over 15m/s. This is causing other events to trigger the fall detection, and if i increase the threshold to avoid that, it won't detect falls.
Can anyone give me some advice here with what possible values i should use or how to even improve my method? Many thanks.
First, I want to remind you that you cannot just add the x, y, z values together as they are, you have to use vector mathematics. This is why you get values of over 15 m/s. As long as the phone is not moving, the vector sum should always be about 9.8 m/s. You calculate it using SQRT(x*x + y*y + z*z). If you need more information, you can read about vector mathematics, maybe http://en.wikipedia.org/wiki/Euclidean_vector#Length is a good start for it.
I also suggest another algorithm: In free fall, all three of the x,y,z values of the accelerometer should be near zero. (At least, that's what I learned in physics classes a long time ago in school.) So maybe you can use a formula like if the vector sum of x,y,z <= 3 m/s than you detect a free fall. And if the vector sum then raises to a value over 20 m/s, than you detect the landing.
Those thresholds are just a wild guess. Maybe you just record the x,y,z values in a test application, and then move around the phone, and then analyze offline how the values (and their normal and vector sum) behave to get a feeling for which thresholds are sensible.
I have acutally published a paper on this issue. Please feel free to check out "ifall" # ww2.cs.fsu.edu/~sposaro
We basically take the root sum of squares and look for 3 things
1. Lower threshold broke. Ie fallinging
2. Upper threshold broke. Ie hitting the ground
3. Flatline around 1g, ie longlie, laying on the ground for an extended period of time
I forgot to update this thread, but iFall is now available on the Android Market.
Also check out ww2.cs.fsu.edu/~sposaro/iFall for more information
Its possible using the Accelerometer sensor.
Write this in the sensor changed listener..
if (sensor == Sensor.TYPE_ACCELEROMETER) {
long curTime = System.currentTimeMillis();
// only allow one update every 100ms.
if ((curTime - lastUpdate) > 100) {
long diffTime = (curTime - lastUpdate);
lastUpdate = curTime;
x = values[SensorManager.DATA_X];
y = values[SensorManager.DATA_Y];
z = values[SensorManager.DATA_Z];
float speed = Math.abs(x + y + z - last_x - last_y - last_z) / diffTime * 10000;
Log.d("getShakeDetection", "speed: " + speed);
if (speed > DashplexManager.getInstance().SHAKE_THRESHOLD) {
result = true;
}
last_x = x;
last_y = y;
last_z = z;
}
}