get the acceleration without gravity - android

I'm new to android development. I want to get actual acceleration of the phone.I found a code to get acceleration.but it gives acceleration with gravity. Please any one help me to find a way to get actual acceleration without gravity.
Here's the code i found,,Please help me with this code. thank you
package com.SensorTest;
import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;
public class SensorTestActivity extends Activity implements SensorEventListener {
SensorManager sensorManager = null;
//for accelerometer values
TextView outputX;
TextView outputY;
TextView outputZ;
//for orientation values
TextView outputX2;
TextView outputY2;
TextView outputZ2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
setContentView(R.layout.main);
//just some textviews, for data output
outputX = (TextView) findViewById(R.id.TextView01);
outputY = (TextView) findViewById(R.id.TextView02);
outputZ = (TextView) findViewById(R.id.TextView03);
outputX2 = (TextView) findViewById(R.id.TextView04);
outputY2 = (TextView) findViewById(R.id.TextView05);
outputZ2 = (TextView) findViewById(R.id.TextView06);
}
#Override
public void onAccuracyChanged(Sensor arg0, int arg1) {
// TODO Auto-generated method stub
}
#Override
public void onSensorChanged(SensorEvent event) {
synchronized (this) {
switch (event.sensor.getType()){
case Sensor.TYPE_ACCELEROMETER:
outputX.setText("acclaration x:"+Float.toString(event.values[0]));
outputY.setText("acclaration y:"+Float.toString(event.values[1]));
outputZ.setText("acclaration z:"+Float.toString(event.values[2]));
break;
case Sensor.TYPE_ORIENTATION:
outputX2.setText("orientation x:"+Float.toString(event.values[0]));
outputY2.setText("orientation y:"+Float.toString(event.values[1]));
outputZ2.setText("orientation z:"+Float.toString(event.values[2]));
break;
}
}
}
#Override
protected void onResume() {
super.onResume();
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), sensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), sensorManager.SENSOR_DELAY_GAME);
}
}

It is not possible to get the acceleration directly without gravity.
You can use a high-pass filter, like on the Android Reference Page, in the Sensor.TYPE_ACCELEROMETER section:
public void onSensorChanged(SensorEvent event) {
// alpha is calculated as t / (t + dT)
// with t, the low-pass filter's time-constant
// and dT, the event delivery rate
final float alpha = 0.8;
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
linear_acceleration[0] = event.values[0] - gravity[0];
linear_acceleration[1] = event.values[1] - gravity[1];
linear_acceleration[2] = event.values[2] - gravity[2];
}

Duplicate question: android remove gravity from accelerometer readings
The answer is using Sensor.TYPE_LINEAR_ACCELERATION (API >= 9 about 99% of all android devices).

The answer is here:
http://developer.android.com/reference/android/hardware/SensorEvent.html#values
Quote:
In particular, the force of gravity is always influencing the measured acceleration:
Ad = -g - ∑F / mass
For this reason, when the device is sitting on a table (and obviously not accelerating), the accelerometer reads a magnitude of g = 9.81 m/s^2
Similarly, when the device is in free-fall and therefore dangerously accelerating towards to ground at 9.81 m/s^2, its accelerometer reads a magnitude of 0 m/s^2.
It should be apparent that in order to measure the real acceleration of the device, the contribution of the force of gravity must be eliminated. This can be achieved by applying a high-pass filter. Conversely, a low-pass filter can be used to isolate the force of gravity.
public void onSensorChanged(SensorEvent event)
{
// alpha is calculated as t / (t + dT)
// with t, the low-pass filter's time-constant
// and dT, the event delivery rate
final float alpha = 0.8;
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
linear_acceleration[0] = event.values[0] - gravity[0];
linear_acceleration[1] = event.values[1] - gravity[1];
linear_acceleration[2] = event.values[2] - gravity[2];
}

Why not to subtract the gravity values directly from the observed acceleration using Android sensor. I think it is easier way. If any specific reason is there behind the use of high pass filter, please let me know with some link to supporting document or technical paper.
Thanks
Rajiv

Related

Using the phone's accelerometer and microphone to measure vibration

I am making an Android app that will measure the vibration when the phone is put on the floor. How do I use the accelerometer (for 0-50Hz) and microphone (for 50-500Hz) to measure the vibration? I am new to Android development but this is a project given to me. I know that there are a lot of apps related to this in the appstore but I have no idea how it is made. I searched for tutorials but I only found a tutorial concerning shake gestures.
First implement a listener for Accelerometer:
class SENSOR_EVENT_LISTENER implements SensorEventListener {
float[] accelerometer_data = new float[3];
float[] gravity = new float[3];
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
final float alpha = 0.8f;
// Isolate the force of gravity with the low-pass filter.
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
accelerometer_data[0] = event.values[0] - gravity[0];
accelerometer_data[1] = event.values[1] - gravity[1];
accelerometer_data[2] = event.values[2] - gravity[2];
break;
default:
return;
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
Then register your receiver:
final SensorManager sensor_manager = (SensorManager) m_context.getSystemService(Context.SENSOR_SERVICE);
listener = new SENSOR_EVENT_LISTENER();
final List<Sensor> sensors = sensor_manager.getSensorList(Sensor.TYPE_ACCELEROMETER);
if(sensors.size() > 0) {
sensor = sensor_manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensor_manager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}
Check values before vibration and during the vibration. You will see there's a remarkable change in the values during the vibration.
I'm not sure about microphone but listening accelerometer alone will be enough for you to measure vibration. I can update my code if you need help with microphone.
Hope this helps.
using the phone's microphone, you can stimulate the phone's vibration system. There is a watt of sound. The vibration also vibrates at a low volume.

Linear acceleration direction to track upward and downward movement of phone

I am trying to track the movement of the device only on the vertical direction, i.e. upward and downward movement. This should be irrespective of the orientation of the device. Things that i already know or have tried are these
Linear acceleration is given by sensor TYPE_LINEAR_ACCELERATION and the axes is the phone axes and hence tracking any particular axes does not make a difference.
I tried applying transpose or inverse of rotation vector( inverse or transpose for the rotation vector are same) and then tried tracking the z direction of the linear acceleration vector. Does not seem to help.
I am trying to do a dot product with gravity values (TYPE_GRAVITY) to get the direction of the acceleration but it seems to be error prone. Even when i move my device swiftly up, it says going down.
I will outline this method here
dotProduct = vectorA[0]*vectorB[0]+vectorA[1]*vectorB[1] + vectorA[2]*vectorB[2];
cosineVal = dotProduct/(|vectorA|*|vectorB|)
if(cosineVal > 0 ) down else Up.
What is the flaw with the method ? Please help, I have been stuck on this for some time now.
As I see it, in the 3rd method you trying to find the cos of angle between two vectors (gravity vector and acceleration vector). And the idea is if the angle is close to 180 degrees you have up movement, if angle is close to 0 degrees you have down movement. Cosine is function that has positive value when angle is from -90 to 90 degrees. So when your cosineVal value is positive it means phone is going down and even if cosineVal closer to 1 movement is straight down. So it is true vice versa. When cosine is negative ( from 90 degrees to 270) you have up movement.
Probably you can get vectors from Sensor.TYPE_ACCELEROMETER from https://developer.android.com/reference/android/hardware/SensorEvent.html#values there you have gravity vector and acceleration vector.
I made a code snippet below you can try.
public class MainActivity extends AppCompatActivity implements SensorEventListener {
private float[] gravity = new float[3];
private float[] linear_acceleration = new float[3];
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
public void onSensorChanged(SensorEvent event) {
// alpha is calculated as t / (t + dT)
// with t, the low-pass filter's time-constant
// and dT, the event delivery rate
final float alpha = 0.8f;
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
linear_acceleration[0] = event.values[0] - gravity[0];
linear_acceleration[1] = event.values[1] - gravity[1];
linear_acceleration[2] = event.values[2] - gravity[2];
float scalarProduct = gravity[0] * linear_acceleration[0] +
gravity[1] * linear_acceleration[1] +
gravity[2] * linear_acceleration[2];
float gravityVectorLength = (float) Math.sqrt(gravity[0] * gravity[0] +
gravity[1] * gravity[1] + gravity[2] * gravity[2]);
float lianearAccVectorLength = (float) Math.sqrt(linear_acceleration[0] * linear_acceleration[0] +
linear_acceleration[1] * linear_acceleration[1] + linear_acceleration[2] * linear_acceleration[2]);
float cosVectorAngle = scalarProduct / (gravityVectorLength * lianearAccVectorLength);
TextView tv = (TextView) findViewById(R.id.tv);
if (lianearAccVectorLength > 2) {//increase to detect only bigger accelerations, decrease to make detection more sensitive but noisy
if (cosVectorAngle > 0.5) {
tv.setText("Down");
} else if (cosVectorAngle < -0.5) {
tv.setText("Up");
}
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
}

Accelerometer giving random values when device is at rest

When I run my code, the values returned by the accelerometer seem to be random and even if I pick up the device and rotate it around, there doesn't seem to be any dramatic change in values or so obvious indication that the device has changed from a rest position to a moving position. I copied the code from Google's site. Basically I am trying to see if the device has moved at all from a rest position. Solution should work on Android 2.2 and above if possible:
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
float[] gravity = new float[3];
#Override
public void onSensorChanged(SensorEvent event)
{
try
{
final float alpha = 0.8f;
float[] linear_acceleration = new float[3];
// Isolate the force of gravity with the low-pass filter.
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
// Remove the gravity contribution with the high-pass filter.
linear_acceleration[0] = event.values[0] - gravity[0];
linear_acceleration[1] = event.values[1] - gravity[1];
linear_acceleration[2] = event.values[2] - gravity[2];
}
catch (Exception ex)
{
}
}
When you are coping solution from documentation do it with understanding.
gravity have to be a field of the class since you need store state of low-pass filter.
gravity holds average values of accelerometer, fast changes are your linear acceleration.
Have you tried Sensor.TYPE_LINEAR_ACCELERATION? This sensor
returns acceleration force excluding gravity and you don't need to
calculate it
Sensors are too sensitives and there is a lot of noise. It is almost impossible to get 0,0,0 values. You need to exclude small values.

How to shake up and down using Accelerometer in Android?

I tried the following way, but it doesn't work perfectly.. If device is kept in normal way, then also it fires shake event.
#Override
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
long curTime = System.currentTimeMillis();
// only allow one update every 200Ms.
if ((curTime - lastUpdate) > 200) {
lastUpdate = curTime;
x = event.values[SensorManager.DATA_X];
y = event.values[SensorManager.DATA_Y];
z = event.values[SensorManager.DATA_Z];
Vibrator vibrate = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
if (Round(y, 4) > 22) {
vibrate.vibrate(200);
Log.d("sensor", "==== Up Detected===");
} else if (Round(y, 4) < -20) {
vibrate.vibrate(200);
Log.d("sensor", "==== Down Detected=== ");
}
mLastX = x;
mLastY = y;
mLastZ = z;
}
}
public static float Round(float Rval, int Rpl) {
float p = (float) Math.pow(10, Rpl);
Rval = Rval * p;
float tmp = Math.round(Rval);
return (float) tmp / p;
}
Please Help.
Any Help would be highly appreciated..
Thanks
Sensor values are very raw and noisy. You need to add a layer of digital signal processing on top of them to get good results. Just using the raw values will lead to a lot of jitter in your results. You're trying to do some basic ones with your time delay, but you need to do more filtering.
Also, you're vibrating in response to a shake. That vibrate will cause the accelerometer to see movement, leading to more false positives.
Apply lowpass filter on as your y-axis values.
The basic filter will be as mentioned in documentation.
public void onSensorChanged(SensorEvent event){
// In this example, alpha is calculated as t / (t + dT),
// where t is the low-pass filter's time-constant and
// dT is the event delivery rate.
final float alpha = 0.8;
// Isolate the force of gravity with the low-pass filter.
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
// Remove the gravity contribution with the high-pass filter.
linear_acceleration[0] = event.values[0] - gravity[0];
linear_acceleration[1] = event.values[1] - gravity[1];
linear_acceleration[2] = event.values[2] - gravity[2];
}

Android accelerometer difficulties

I'm a beginner in Android game Development, and I developing a small Game.
I am facing some difficulties with the Motion Sensor: Accelerometer.
this game is in Landscape mode.
I want if my phone tilts in right, my character also goes right. ( same thing for left )
And when I stop tilting Character in the game should stop moving.
But i don't understand really good the operation of the accelerometer,
here is my code:
#Override
public void onSensorChanged(SensorEvent event) {
synchronized (this) {
long penchement = (long) (event.values[1]- 0.5);
if(penchement>0){
if(penchement>lastUpdate)
lastUpdate=penchement;
if(penchement>0.2){
booldroite=true; // boolean for going right
boolgauche=false; // boolean for going left
}
if(lastUpdate-penchement<0.2)
booldroite=false;
}
else{
if(penchement<lastUpdate)
lastUpdate=penchement;
if(penchement<-0.2){
boolgauche=true;
booldroite=false;
}
if(lastUpdate+penchement>-0.2)
boolgauche=false;
}
}
So my code works (a bit), but my character's moves aren't smooth at all. Sometimes my phone tilts but my Characters doesn't move...
Thank you very much in advance if you can help me.
Sensor readings are noisy by nature, that's the reason for the flickering movement of your character.
You will need to implement some kind of low pass filter after the readings. There's a basic low pass filter example in SensorEvent.
public void onSensorChanged(SensorEvent event) {
// alpha is calculated as t / (t + dT)
// with t, the low-pass filter's time-constant
// and dT, the event delivery rate
final float alpha = 0.8;
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
linear_acceleration[0] = event.values[0] - gravity[0];
linear_acceleration[1] = event.values[1] - gravity[1];
linear_acceleration[2] = event.values[2] - gravity[2];
}
You might want to use the values in gravity[] to find out the 'tilt' of the phone. Also, play around with the value of final float alpha: Values near 1.0 will improve the smoothness, while smaller values near 0.0 will obtain noisier readings, but have a faster response.
Bonne chance!

Categories

Resources