views:

1367

answers:

3

I have a code snippet to detect accelerometer movements. It works some times by properly detecting slight movements, but sometimes it detects movements when I kept my device idle too. Are there any problems with built-in accelerometer detection on Android?

I use an HTC G-1 device. My code snippet is below. How do I resolve it so I can detect small device movements but not detect anything when the device is idle?

private static final int SHAKE_THRESHOLD = 50;

public void onSensorChanged(int sensor, float[] values) {

    if (sensor == SensorManager.SENSOR_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;

            if (speed > SHAKE_THRESHOLD) { 
                long curTime = System.currentTimeMillis();
                long diff = (curTime - shakeTime);       
                shakeTime = curTime;

                if (myFlagIgnoreShakeDetection==true)  //Caused unneccessary accelerometer   
                                                       //notification looping when device is idle
                   return;

                // Doing something...
            }
         last_x = x;
         last_y = y;
         last_z = z;
        }

    }

}
+1  A: 

Here are a few code discrepancies...

  • There may be a problem regarding the updating of last_x, last_y, and last_z. I believe they should be included inside the if ((curTime - lastUpdate) > 100) { statement. In other words, they are being updated every time onSensorChanged is called, not every 100 milliseconds. You should probably move the updating of those three variables into the curly brace above them.

  • On the line where you compute the speed, the formula ends with ... / diffTime * 10000; Are you wanting to multiply just diffTime by 10000, or the entire result? Since / and * typically have the same operator precedence in most languages I know of (such as Java), your equation will be evaluated from left to right, dividing first by diffTime then multiplying that result by 10000.

    I'm guessing you mean to multiply just diffTime by 10000, thus dividing the final result by that amount. This is the difference between dividing by 10000 or multiplying by 10000, which means you are probably getting values for speed that are 10^8 greater than you should, thus tripping your threshold even when the device is idle. You need to put parentheses around the multiplication, like ... / (diffTime * 10000);, to make sure it's performed before the division takes place.

    Additionally, if you are intending to scale diffTime from milliseconds to seconds, your scale factor should be 1000.

gnovice
sorry for the confusion. It is there inside "if ((curTime - lastUpdate) > 100) {" only. Actually i missed a "}" (close brace) in last line of my code. I tried to use Mr.peejaybee suggestion, but still it doesn't work as expected. Some times, if it is detecting accelerometer even when the device is idle, which causes false notification and i couldn't execute my code as my expectation.
@unknown: If you missed the `}` at the **end** of your code, then those three lines **aren't** inside the if statement that checks the time. Note that you have two opening braces (one for each if statement) and two closing braces following the second if statement, so that the three lines being updated at the end are **outside** the first if statement.
gnovice
@gnovice, yes, the OP definitely needs to update last_x, etc., inside the update if test.
Bob Cross
I kept like that(pl see my code updated as you have suggested) still doesn't work. I am getting accelerometer continuously even when the device is idle.
Why is, if (sensor == SensorManager.SENSOR_ACCELEROMETER) and if (speed > SHAKE_THRESHOLD) are getting called even when the device is idle?
@unknown: Have you addressed the second bullet point I added above? That may be the smoking gun.
gnovice
If float speed = Math.abs(x+y+z - last_x - last_y - last_z) / (diffTime * 1000); nothing works related to accelerometer. Not even detecting which was working some time atleast. Let's see how can i resolve it?
@unknown: There are a few things you need to try now: Do you have to scale the result of `Math.abs(...)` too to get it into certain units (meters, cm, mm)? Do you have to readjust `SHAKE_THRESHOLD`? Do you need to use proper vector addition like peejaybee suggested?
gnovice
Finally i could able to understand what is causing the problem, but found it while testing on HTC device, not through the code. When i use the device for sometime and then put it for idle, it works as expected(no false events) in a scenario. Scenario is, i need put the device a bit movement(little bit forcefully on my desk) for idle, then it stops notifying false events. Otherwise if i put the device idle without movement on a desk(very slowly), then also accelerometer keeps on notifying my app, which causes false events. Any suggestions on my code now?
I fixed it by removing a "return" statement in my code. Pl refer my code above which shows "return" [ if (myFlagIgnoreShakeDetection==true) ] inside accelerometer code, which caused looping false accelerometer notification unnecessarily when the device is in idle. Please be cautious, Accelerometer detection code should NOT have any "return" statement inside.
Those extra lines might have been helpful to show in the original post. You probably would have gotten a resolution to your problem faster. ;)
gnovice
thank you very much
A: 

I personally, in my augmented reality library, use a rolling average for the updates:

float kFilteringFactor = (float)0.05;    
rollingZ = (float) ((rawZValue * kFilteringFactor) + (rollingZ * (1.0 - kFilteringFactor)));

This tends to smooth out the data pretty well, and you can tweak the filtering factor to get the responsiveness you want.

rawZValue is the raw value coming in from the accelerometer.

haseman
What does rollingZ do? Can you provide a bit more?
Haseman, How would you modify code based on your suggestion? That would be great help for me to understand easily.
I fixed it by removing a "return" statement in my code. Pl refer my code above which shows "return" [ if (myFlagIgnoreShakeDetection==true) ] inside accelerometer code, which caused looping false accelerometer notification unnecessarily when the device is in idle. Please be cautious, Accelerometer detection code should NOT have any "return" statement inside.
Personally, I bring the rollingZ variable into my AR kit as the actual values. rawZValue would, in your code, be values[SensorManager.DATA_Z];I wrote an article about it here:http://www.devx.com/wireless/Article/43005/0/page/2
haseman
A: 

Hi ... My teammates and I are also working on using a similar approach to calculate the distance. But I am not sure about why you are using 10000 in place of 1000 as the conversion factor in float speed = Math.abs(x+y+z - last_x - last_y - last_z) / (diffTime * 10000);

Venkat