I am making an android project to detect when mobile fall down, can anyone tell me which sensor should I use in my app, I know accelerometer will use for this kind of purpose, but accelerometer can also detect when I shake the phone in my hand and I want to get the toast only when the mobile falls down.
here is my code:
int count = 1;
private boolean init;
private Sensor mySensor;
private SensorManager SM;
private float x1, x2, x3;
private static final float ERROR = (float) 7.0;
private static final float SHAKE_THRESHOLD = 15.00f; // m/S**2
private static final int MIN_TIME_BETWEEN_SHAKES_MILLISECS = 1000;
private long mLastShakeTime;
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
long curTime = System.currentTimeMillis();
if ((curTime - mLastShakeTime) > MIN_TIME_BETWEEN_SHAKES_MILLISECS) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
double acceleration = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)
+ Math.pow(z, 2))
- SensorManager.GRAVITY_EARTH;
Log.d("mySensor", "Acceleration is " + acceleration + "m/s^2");
if (acceleration > SHAKE_THRESHOLD) {
mLastShakeTime = curTime;
Toast.makeText(getApplicationContext(), "FALL DETECTED",
Toast.LENGTH_LONG).show();
} } }}
There is no "Fall Sensor", your guess in using the accelerometer is right. Record and measure the accelerometer data when it falls and deduct a model from there.
You have to use accelerometer. But it will detect very small movement.
The only way to do is , get the difference between two readings on change of state.
If its very very high, the mobile has traveled a longer distances say fell down from hand, or change an abnormal position say a shock.
For this we have to use little bit of Physics, any object falling under the influence of gravity has an acceleration of 9.8 m/s/s, downward (on Earth). SO with this knowledge we can get the downward direction speed
1)based on the axis(Y-axis) using accelerometer check it is downward and
2) check if it is equal to this 9.8 m/s constant and also check the
if the fall speed is approx near 9.8 m/s show the toast
Hi all i done this by myself a little change was get me rid of this , that is just taking two variable set those values negative and call them as threshold value of acceleration .
thanks everyone
Related
I want to be able to detect a situation where the phone has an acceleration towards the ground (probably means that the Gravity sensor has to be used here also).
I have read a lot about this topic in the Android docs, about High and Low pass filters and other posts, and right now what I have is a code sample that gets the acceleration in the X, Y and Z axis after stripping the gravity:
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
final float alpha = (float) 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];
}
So, the linear_acceleration is supposedly the acceleration in the X, Y, Z axis without the gravity.
This is all nice, but the problem obviously is that is depends on how the user holds the phone, for example, in the elevator - if he holds it flat, parallel to the ground - the Z axis will change, if he holds it up straight - the Y axis will change , etc.
So, for example, if the user holds the phone diagonally, the acceleration will be "divided" between the different axes, and some sort of math work, considering where the gravity direction is, will be needed to calculate the actual acceleration in that direction.
Correct me if I am wrong?
Is there a reliable way to detect the downward (towards the earth) acceleration? maybe using other sensors like Gyroscope?
BTW - about the TYPE_LINEAR_ACCELERATION type, I read this answer, saying that its actually not very accurate.
Use some basic physics. Acceleration is a vector. The magnitude of a vector v is always equal to (v.v)^.5, or the square root of the dot product. Or in simpler terms (x^2+y^2+z^2)^.5. That will tell you the amount of acceleration, but not if its towards or away from the earth.
If you need to know if its going towards or away from earth- you can combine that with data from SensorManager.getOrientation. You may need to do that before they enter the elevator though- the orientation code uses gravity as one of its inputs, so it may be screwed up if you try to use it in an elevator. You'd need to test it out.
If you need to break it down to acceleration in terms of earth x, y, and z axes- simple geometry. Take the angle from the orientation result, and use trig properties to convert axes. If you don't know the formulas you need to read up on trig a bit or you'll get them wrong even if I tell them to you.
I also wanted to just be able to measure vertical movement. This is how I did it and it worked for me. First time posting on this site and I have no idea how to format correctly.
Use two different android Sensors: Type_linear_Acceleration and Type_Gravity
Linear acceleration will give you acceleration in the X, Y and Z axis of the phone, and Gravity will do the same, but just for gravity. You know that the sum of the gravity values should = 9.8, but this will be split between X, Y and Z coordinates depending on the phone orientation.
I wont go into the math of it too much, but the following will give you vertical acceleration without gravity. If you want to understand it a bit more run through some values as if the phone was held vertically, then horizontally, it works even if the phone is oblique.
vertical acceleration = (LinearAccelX * GravityX / 9.8)+ (LinearAccelY * GravityY / 9.8)+ (LinearAccelZ * GravityZ / 9.8).
See code below (irrelevant parts removed):
{public class MainActivity extends AppCompatActivity implements SensorEventListener {
SensorManager sm;
Sensor linearaccelerometer;
Sensor gravity;
double Yaccel;
double Xaccel;
double Zaccel;
double gravityY;
double gravityX;
double gravityZ;
double verticalAccel;
sm = (SensorManager) getSystemService(SENSOR_SERVICE);
linearaccelerometer = sm.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
gravity = sm.getDefaultSensor(Sensor.TYPE_GRAVITY);
sm.registerListener(this, linearaccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
sm.registerListener(this, gravity, SensorManager.SENSOR_DELAY_NORMAL);
}
public void onSensorChanged(SensorEvent event) {
Sensor sensor = event.sensor;
if (sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
Xaccel = (double) event.values[0];
Yaccel = (double) event.values[1];
Zaccel = (double) event.values[2];
}
if (sensor.getType() == Sensor.TYPE_GRAVITY) {
gravityX = (double) event.values[0];
gravityY = (double) event.values[1];
gravityZ = (double) event.values[2];
}
verticalAccel = (Xaccel * gravityX / 9.8) + (Yaccel * gravityY / 9.8) + (Zaccel *gravityZ /9.8);
}
i am building an app that counts the steps using accelerometer for android devices.(android version< .4.4)
to do this i calculate the gravity (G) using x,y and z. if the value of G is larger than STEP_THRESHOLD then an step has been occured:
#Override
public void onSensorChanged(SensorEvent event) {
synchronized (this) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
return;
}
final float x = event.values[0];
final float y = event.values[1];
final float z = event.values[2];
final float g = Math.abs((x * x + y * y + z * z) / (SensorManager.GRAVITY_EARTH * SensorManager.GRAVITY_EARTH));
if (g > StepUtil.GRAVITY_THRESHOLD) {
/*
* check step times and other checkings
*/
.
.
.
stepCounter++;
}
}
}
the problem is:
since accelerometer hardwares is varies in each device, there is not a certain value for STEP_THRESHOLD.
it seems that the STEP_THRESHOLD value should be dynamic for each device!
is there any criterion to change STEP_THRESHOLD value base on accelerometer accuracy?
any help will be appreciated.
since accelerometer hardwares is varies in each deviceŁ there is not a certain value for STEP_THRESHOLD
Having that said, what your app is apparently missing is simple calibration, which would allow your users to set the threshold level on their device. Tell them to do couple of steps, take sensor values, take mean/median/average (whatever will work for you better) and you got your STEP_THRESHOLD. You can even send this value back to your server so you can build kind of database and come with some more/less universtal starting value of STEP_AVERAGE.
I just did my project for getting data gyroscope sensor. If I put my handphone on the table horizontally, the result of gyroscope sensor are :
Roll (X) : 5.326322E-7
Pitch (Y) : 5.326322E-7
Yaw (Z) : 5.326322E-7
Logically, the result should be 0, because the handphone lay on the table. So,anybody can help me? I give my code below. Thank you very much for the response in advance :).
public void onSensorChanged(SensorEvent event) {
if(event.sensor.getType()==Sensor.TYPE_GYROSCOPE)
{
float roolX = event.values[0];
float pitchY = event.values[1];
float yawZ = event.values[2];
koordinatrollX.setText("Orientation X (Roll) :" + Float.toString(event.values[0]));
koordinatpitchY.setText("Orientation Y (Pitch) :" + Float.toString(event.values[1]));
koordinatyawZ.setText("Orientation Z (Yaw):" + Float.toString(event.values[2]));
}
E-7 = 10^-7 so it's very close to 0. You can't expect the hardware to be perfect calibrated or the table to be perfect flat. You could let the user recalibrate based on the surface the phone is at but the result you get is already very close to zero.
I'm trying to detect left, right, up and down shakes. I researched and found this code.
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float[] values = event.values;
// Movement
float x = values[0];
float y = values[1];
float z = values[2];
long actualTime = System.currentTimeMillis();
if ((actualTime - lastUpdate) > 100)
{
long diffTime = (actualTime - lastUpdate);
lastUpdate = actualTime;
if(Round(x,4)>8.0000){
Log.d("sensor", "=====LEFT====");
}
else if(Round(x,4)<-8.0000){
Log.d("sensor", "=====RIGHT====");
}
else if(Round(z,4) < -0.0){
Log.d("sensor", "=====UP====");
}
else if(Round(y,4) < 1.0){
Log.d("sensor", "=====DOWN====");
}
float speed = Math.abs(x+y+z - last_x - last_y - last_z) / diffTime * 10000;
if (speed > SHAKE_THRESHOLD) {
//Log.d("sensor", "shake detected w/ speed: " + speed);
//Toast.makeText(this, "shake detected w/ speed: " + speed, Toast.LENGTH_SHORT).show();
}
last_x = x;
last_y = y;
last_z = z;
}
}
}
I tried to study it but I have some questions.
1. Where can I set the delay every after shake detection? I want it to detect a shake after 1 second of the last detected shake.
2. Why is that if I place it to a flat surface, it always detects down.
3. I can't detect a right movement shake
Any help and suggestion will be appreciated.
First off, this code appears to detect movement rather than a shake (where I understand shake to be a motion which goes one way, then back the other), which might be causing problems in how you understand it's working.
Where can I set the delay every after shake detection? I want it to detect a shake after 1 second of the last detected shake.
if ((actualTime - lastUpdate) > 100) is where the interval between detections occurs - the 100 here tells the program to only see what the change is after 100 ms, so if you wanted 1 second, change this to 1000
Why is that if I place it to a flat surface, it always detects down.
I'm not entirely sure of the context and direction here as it's not clear, but don't forget that 'down' as you think of it might not be down in your space, but in the phone's, where down is only down when the phone is upright. If the phone is on it's back, left and right are the same, but down (i.e. towards the ground) is actually back for the sensor, as it's towards the back of the phone.
I can't detect a right movement shake
TBH I've no idea why, the code seems fine here. May be a phone thing, may be an implementation problem (as I said, the above code is movement overall, not a returning 'shake', so it could be how you're shaking it right to test this isn't triggering it, but your shake left is triggering it
I have to write a compass app in Android. The only thing the user sees on the screen is a cube with a red wall which has to point north. This is not important. What's important is that I need to rotate that cube accordingly to the rotation of the device itself so that the red wall continues to point north no matter how the phone is being held. My code is simple and straightforward:
#Override
public void onSensorChanged(SensorEvent event) {
synchronized (this) {
switch (event.sensor.getType()){
case Sensor.TYPE_ACCELEROMETER:
direction = event.values[2];
break;
case Sensor.TYPE_ORIENTATION:
if (direction < 0) {
angleX = event.values[1];
angleY = -event.values[2];
angleZ = event.values[0];
} else {
angleX = -event.values[1];
angleY = -event.values[2];
angleZ = event.values[0];
}
break;
}
}
}
I have added this extra direction variable that simply stores whether the phone's display is pointing downwards or upwards. I don't know if I need it but it seems to fix some bugs. I am using the SensorSimulator for android but whenever my pitch slider goes in the [-90, 90] interval the other variables get mixed up. It's like they get a 180 offset. But I can't detect when I am in this interval because the range of the pitch is from -90 to 90 so I can move that slider from left to write and I will always be in that interval.
This was all just to show you how far has my code advanced. I am not saying how this problem should be solved because I will only probably stir myself into a dead end. You see, I have been trying to write that app for 3 days now, and you can imagine how pissed my boss is. I have read all sorts of tutorials and tried every formula I could find or think of. So please help me. All I have to do is know how to rotate my cube, the rotation angles of which are EULER ANGLES in degrees.
Here's some code I wrote to do something pretty similar, really only caring about the rotation of the device in the roll direction. Hope it helps! It just uses the accelerometer values to determine the pitch, no need to get orientation of the view.
public void onSensorChanged(SensorEvent event) {
float x = -1 * event.values[0] / SensorManager.GRAVITY_EARTH;
float y = -1 * event.values[1] / SensorManager.GRAVITY_EARTH;
float z = -1 * event.values[2] / SensorManager.GRAVITY_EARTH;
float signedRawRoll = (float) (Math.atan2(x, y) * 180 / Math.PI);
float unsignedRawRoll = Math.abs(signedRawRoll);
float rollSign = signedRawRoll / unsignedRawRoll;
float rawPitch = Math.abs(z * 180);
// Use a basic low-pass filter to only keep the gravity in the accelerometer values for the X and Y axes
// adjust the filter weight based on pitch, as roll is harder to define as pitch approaches 180.
float filterWeight = rawPitch > 165 ? 0.85f : 0.7f;
float newUnsignedRoll = filterWeight * Math.abs(this.roll) + (1 - filterWeight) * unsignedRawRoll;
this.roll = rollSign * newUnsignedRoll;
if (Float.isInfinite(this.roll) || Float.isNaN(this.roll)) {
this.roll = 0;
}
this.pitch = filterWeight * this.pitch + (1 - filterWeight) * rawPitch;
for (IAngleListener listener : listeners) {
listener.deviceRollAndPitch(this.roll, this.pitch);
}
}