Different accelerometer values across multiple devices - android

I'm making a game which uses the device's accelerometer to fill a progress bar.
On my Note 2 it takes me about 20 seconds shaking the phone up and down to fill the bar, however I tried on a ZTE Blade and it took me 4 seconds.
Is there any way to calibrate the accelerometers within my code? This is what I'm doing:
#Override
public void onSensorChanged(SensorEvent event) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
if (!mInitialized) {
mLastX = x;
mLastY = y;
mLastZ = z;
mInitialized = true;
} else {
float deltaX = Math.abs(mLastX - x);
float deltaY = Math.abs(mLastY - y);
float deltaZ = Math.abs(mLastZ - z);
if (deltaX < NOISE) deltaX = (float)0.0;
if (deltaY < NOISE) deltaY = (float)0.0;
if (deltaZ < NOISE) deltaZ = (float)0.0;
mLastX = x;
mLastY = y;
mLastZ = z;
if(deltaY == 0){
return;
}
sProgress += deltaY;
pb.setProgress(sProgress);
}
}

Issue may be with frequency of Accelerometer.Dont use below android constants while registering.
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
these valuse are different for different device.
int SENSOR_DELAY_FASTEST get sensor data as fast as possible
int SENSOR_DELAY_GAME rate suitable for games
int SENSOR_DELAY_NORMAL rate (default) suitable for screen orientation changes
int SENSOR_DELAY_UI rate suitable for the user interface
Use Hard coded values in microseconds like for frequency 1 Hz
mSensorManager.registerListener(this, mAccelerometer,1000000);
Hope it solves.

Another solution to making the frequency equal is something like the following solution.
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
if(sensorEventTooQuick(sensorEvent.timestamp)) {
return;
}
mLastSensorEventTime = sensorEvent.timestamp;
// process sensor
}
private boolean sensorEventTooQuick(long sensorTime) {
long diff = sensorTime - mLastSensorEventTime;
return diff < MINIMUM_SENSOR_EVENT_GAP;
}
Things to consider:
You want to make sure you choose MINIMUM_SENSOR_EVENT_GAP to be small enough as to not lose information. The accelerometer is very quick so this is usually not a problem, but could be an issue for other sensors depending on the application.
Make sure to test different values across different devices.
I do this since it guarantees that you will not receive events faster than your limit.

Related

Detect if phone is moving up or down using accelerometer and count changes

I would like to detect using accelerometer if phone is moving up or down and how many times direction was changed.
I am using this code:
lastY = 0;
lastYChange = 0;
initialized = false;
...
if (sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null) {
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
...
#Override
public void onSensorChanged(SensorEvent event) {
float y = event.values[1];
if (!initialized) {
lastY = y;
initialized = true;
}
else {
float yChange = lastY - y;
float deltaY = Math.abs(yChange);
if (deltaY < 2) {
deltaY = 0;
}
else {
if (lastYChange < 0 && yChange > 0) {
counter += 1;
textViewDirection.setText("Direction: Top");
textViewCounter.setText("Counter: " + counter);
}
else if (lastYChange > 0 && yChange < 0) {
counter += 1;
textViewDirection.setText("Direction: Bottom");
textViewCounter.setText("Counter: " + counter);
}
lastYChange = yChange;
}
lastY = y;
textViewAcceleration.setText("Acceleration is: " + deltaY);
}
}
So if I move phone in only one direction, for example top direction, counter should be increased by only 1, and textViewDirection should have value of "Direction: Top".
Instead, with this code counter is increased multiple times and textViewDirection is switching from "Direction: Top" and "Direction: Down".
Does anyone know to fix this? So that, for example, if I move phone up, then down, then up, counter should have value of 3, and textViewDirection should have value "Direction: Top", "Direction: Down" and "Direction: Top", respectively.
This is a possible accelerometer result when you move the sensor Unfortunately, y accelerometer waveform is not as straightforward to behave as you thought in your code. It oscillates a lot, e.g. when you stop the device

Android detect phone lifting action

I want to perform some activity when the user lifts the phone from a flat surface. The method I am using right now is detect shake motion using phone's Accelerometer using the following code:
sensorMan = (SensorManager) getSystemService(SENSOR_SERVICE);
accelerometer = sensorMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorMan.registerListener(this, accelerometer, SensorManager.SENSOR_STATUS_ACCURACY_HIGH);
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mGravity = event.values.clone();
// Shake detection
float x = mGravity[0];
float y = mGravity[1];
float z = mGravity[2];
mAccelLast = mAccelCurrent;
mAccelCurrent = FloatMath.sqrt(x * x + y * y + z * z);
float delta = mAccelCurrent - mAccelLast;
mAccel = mAccel * 0.9f + delta;
if (mAccel > 0.9) {
//Perform certain tasks.
}
}
The issue I am facing with this code is the 0.9f threshold is reached sometimes even if the phone is still on the flat surface. I tried logging the mAccel value and found it to be rannging from 9.0 to 0.4 even when the phone is not even touched. Is there any guaranteed way to detect the phone's lift movement?
Solved the issue. All I wanted to do was to check for the "Y" value stated in the question and check if the value was greater than 1.0.
Note that, if the phone is kept in vertical position the Y is always around 9.8 but in such cases you can check for X instead. In my case user had to lift the phone and somewhen he will tilt the phone so I put a check for if(y >= 1.0 && y <= 2.0);
EDIT : UPDATED CODE
#Override
public void onSensorChanged(SensorEvent event) {
try {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mGravity = event.values.clone();
// Shake detection
float x = mGravity[0];
float y = mGravity[1];
float z = mGravity[2];
float yAbs = Math.abs(mGravity[1]);
mAccelLast = mAccelCurrent;
mAccelCurrent = FloatMath.sqrt(x * x + y * y + z * z);
float delta = mAccelCurrent - mAccelLast;
mAccel = mAccel * 0.9f + delta;
if (yAbs > 2.0 && yAbs < 4.0 && !isAlerted() && !isCallActive()) {
alert();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
I would add the Gyroscope into the detection routine too.
The Phone gets Accelerated AND gets up from x=0 y=0 z=0 to, lets say y=120, that's the Trigger.
Look here
for Infos how to using it.
Another Sensor for lifting detection would be the Proximity Sensor, when the Phone lays flat on the Desk dinstance would be 0, if its picked up that value would raise quickly

Vibrate the device in the Sensor mode

I have implemented the Sensor functionality in my app.Now after 2 mins vibration is in activate mode due to which the device get some movement and sensor get work.What i want that the sensor get active only when the user manually move the device.There should be no effect of vibration on the sensor.Following is my code.Thanks for Advance...
vib= (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
sensorManager=(SensorManager)getSystemService(SENSOR_SERVICE);
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
vib.vibrate(val);
#Override
public void onSensorChanged(SensorEvent event) {
if(event.sensor.getType()==Sensor.TYPE_ACCELEROMETER) {
float x=event.values[0];
float y=event.values[1];
float z=event.values[2];
float deltaX = Math.abs(mLastX - x);
float deltaY = Math.abs(mLastY - y);
float deltaZ = Math.abs(mLastZ - z);
if(deltaX>NOISE){
compareX();
}
else if(deltaY>NOISE){
compareX();
}
else if(deltaZ>NOISE){
compareX();
}
mLastX = x;
mLastY = y;
mLastZ = z;
}
}
private void compareX() {
mStartTime = SystemClock.uptimeMillis();
mHandler.removeCallbacks(mUpdateTimeTask);
mHandler.postDelayed(mUpdateTimeTask, 100);
vib.cancel();
stopService(new Intent(getApplicationContext(),AlarmService.class));
System.out.println("Movemenet>><><><><><><>><Occur");
}
So if I'm reading that correctly, you want to turn on the accelerometer but not get changes in it due to vibration of the device- filter it out somehow. That really wouldn't be possible. The framework has no method to do it, and any attempt to do so would be difficult- the vibrator's strength will vary from device to device, and even between calls in the same device. And they don't really calibrate those things- there's really no way to do that accurately.

AccelerationSensor.accelerationchanged() making app slow

in my app, i use the AccelerationSensor.accelerationchanged(xAccel, yAccel, zAccel) API
the problem is the method is called every o.oooo1 change in any axis, so the app becomes very slow, some times even becomes "non-responding"
Is there a way to check if the integer part has changed and let away any decimal change?
This is what I am doing, in my onStartCommand() of my service
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
This is the function I am handling my data, it is pretty light weight but it should give you an idea using a threshold. In my case I really just need to know the device a bit, in my case it turned out the differenceValue neeeded to be about 1.75G but it might be different for you.
#Override
public void onSensorChanged(SensorEvent event) {
if(last[0] == 0.0f &&
last[1] == 0.0f &&
last[2] == 0.0f){
last[0] = event.values[0];
last[1] = event.values[1];
last[2] = event.values[2];
return;
}
float diff = 0f;
if(Math.abs(last[0] - event.values[0]) > differenceValue ||
Math.abs(last[1] - event.values[1]) > differenceValue ||
Math.abs(last[2] - event.values[2]) > differenceValue){
Log.d(TAG,"G values are "+event.values[0]+" "+event.values[1]+" "+event.values[2]);
Log.d(TAG,"Last G values are "+last[0]+" "+last[1]+" "+last[2]);
diff = Math.abs(last[0] - event.values[0]);
if(diff < Math.abs(last[1] - event.values[1])){
diff = Math.abs(last[1] - event.values[1]);
}
if(diff < Math.abs(last[2] - event.values[2])){
diff = Math.abs(last[2] - event.values[2]);
}
Log.d(TAG,"Sensor difference: "+diff);
//Do what ever processing you need here
}
last[0] = event.values[0];
last[1] = event.values[1];
last[2] = event.values[2];
}
When you register a listener for a sensor it allows you to set a frequency. Use a slower frequency.

Convert values form Sensor.TYPE_ORIENTATION to Euler angles?

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);
}
}

Categories

Resources