Android linear acceleration accuracy - android

I have an app where I poll the sensors for acceleration data and save the XYZ values to a SQL DB. Codewise it is pretty simple:
public void onSensorChanged(SensorEvent event) {
sensor = event.sensor;
int i = sensor.getType();
if (i == MainActivity.TYPE_ACCELEROMETER) {
accelerometerMatrix = event.values;
} else if (i == MainActivity.TYPE_GYROSCOPE) {
gyroscopeMatrix = event.values;
} else if (i == MainActivity.TYPE_GRAVITY) {
gravityMatrix = event.values;
} else if (i == MainActivity.TYPE_MAGNETIC) {
magneticMatrix = event.values;
}
//insert into database
}
MainActivity.TYPE_ACCELEROMETER is just a field I set in my MainActivity class.
If I set this to Sensor.TYPE_LINEAR_ACCELERATION I notice that the values that get stored to my DB are fairly low in accuracy (only 2 decimal places). But if I use the hardware accelerometer Sensor.TYPE_ACCELERATION then the accuracy is much higher (6dp)
My question is, am I retrieving the linear acceleration values incorrectly to cause me to only get 2dp of accuracy? Or is this just a limitation of how the sensors are fused together in Android? If its the latter, how do I get higher accuracy in my accelerometer values, after filtering out gravity, when working with fused sensors?

As far as I know, you cannot change accuracy of the hardware sensors on Android. You can only adjust sampling period with registerListener() method from SensorManager class or monitor accuracy change with onAccuracyChanged() callback from SensorEventListener interface.
It looks that you are retrieving sensor readings correctly in your code snippet.
I would suggest doing the following things:
try to perform the same measurement with just one sensor and
check if application behavior has changed - maybe multiple readings
are causing measurement inaccuracy?
try to run this application on a different device if you have one and verify if behavior and accuracy is different - maybe accuracy of the sensors is different on different devices?
check accuracy and datatypes in your database and verify if it handles more accurate data correctly
If you don't notice any difference after applying mentioned suggestions, I think we cannot do anything about increasing sensor readings accuracy.
Moreover, I performed some experiments with sensors on Android some time ago using Nexus 5 and Nexus 6 and I see that different sensors has different accuracy as you also noticed. According to my knowledge, we cannot control that from the Android SDK API level.

Related

Pedometer (Step Counter)

I am developing a Pedometer Android application to count number of steps taken and using the steps calculate the distance covered and calories burned. I have followed the tutorial
Create a Simple Pedometer and Step Counter in Android and done exactly like it. It detects number of steps when the sensor detects motion.
But there are some problems with it:
When I stand at the same place with my device in my hand and just move my hand or give a jerk to device, it detects the change and adds to step count.
If I move very slowly with device in my hand it does not detect the change.
If i jump, then it adds several steps in the counter.
I have checked some other applications from Play Store they do not do this kind of stuff.
I have searched but cannot find an appropriate solution or tutorial for it. Any help or suggestions. Thanks
The problem here is that your implementation is not sophisticated enough: it only checks if there is a spike in the accelerometer data and assumes that the spike is coming from a step. It has no idea where the spike in acceleration is really coming from: it might as well come from you jumping or shaking the device in your hand.
How to make it more accurate then? Well, that is a really difficult question which has been topic for scientific papers for a really long time. Even the most sophisticated fitness trackers (which use machine learning, signal processing and other statistical methods) have difficulties to determine when the step is real and when it is just noice or user playing with the device.
Luckily Android does have it's own builtin step counter and step detector, which are more sophisticated than the class in yor example.
So unless you really want to learn signal processing and AI (which I highly recommended, although I don't know much about the data science of step detection), I would suggest to use builtin detector and counter.
By implementing SensorEventListener listener within a class and overriding the two methods onSensorChanged and onAccuracyChanged you can start tracking steps.
public class StepActivity extends Activity implements SensorEventListener{
SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor sSensor= sensorManager .getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
...
}
Now we have initialised the SensorManager and Sensor and have the Sensor registered as a listener within the activity, we now need to implement the onSensorChanged function that will be triggered by a SensorEvent whenever there is a change to the Sensor we registered, in our case the TYPE_STEP_DETECTOR.
private long steps = 0;
#Override
public void onSensorChanged(SensorEvent event) {
Sensor sensor = event.sensor;
float[] values = event.values;
int value = -1;
if (values.length > 0) {
value = (int) values[0];
}
if (sensor.getType() == Sensor.TYPE_STEP_DETECTOR) {
steps++;
}
}
That's a very naive method to achieve step count. You should use Android's built-in step counter because it also uses other sensors if available such as gyroscope which can improve the step detection. You should especially use this built-in version if you are going to built things on top it. You need a reliable underlying layer. You can also try using linear acceleration sensor which is calculated by removing gravity component from the accelerometer. The gravity makes accelerometer very sensitive, that's why you see step counter increasing when you are just standing.
The details can be found here:
https://source.android.com/devices/sensors/sensor-types#step_detector
If you still want to develop your own from scratch, then look at this code:
https://github.com/bagilevi/android-pedometer
You can also try Google scholar for the latest papers on step counting algorithms. Especially try to read the latest survey on the topic.

Android onSensorChanged(SensorEvent event) - Pedometer sensor (Nexus 5)

I am using the pedometer on a nexus 5, and a oneplus one phone. They both have a sensor named "Pedometer", and the vendor is "QTI", and the sensorType is 33171009
Under andorid's sensor documentation, there is no documentation for this type of sensor.
The SensorEventListener calls public void onSensorChanged(SensorEvent event) with a sensor event, where the sensor name is "pedometer", and the values are an array of 16 float values. Since there is no documentation on this type of sensor, I do not know what each of these values mean.
It would've been helpful if the object SensorEvent also told us what each value is. Rather, you have to look up the values array in the documentation to see what each value represents.
Nonetheless, this particular sensor (Pedometer) is not mentioned anywhere in the android sensor documentation (at least from what I have discovered, if anybody knows where this exists that would be very helpful).
Digging into the source code, I find the instantiation of this sensor (pedometer) inside SystemSensorManager.java via a native method nativeGetNextSensor(Sensor sensor, int next).
The array is of size 16 because of this method:
static int getMaxLengthValuesArray(Sensor sensor, int sdkLevel) {
// RotationVector length has changed to 3 to 5 for API level 18
// Set it to 3 for backward compatibility.
if (sensor.mType == Sensor.TYPE_ROTATION_VECTOR &&
sdkLevel <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return 3;
}
int offset = sensor.mType;
if (offset >= sSensorReportingModes.length) {
// we don't know about this sensor, so this is probably a
// vendor-defined sensor, in that case, we don't know how many value
// it has
// so we return the maximum and assume the app will know.
// FIXME: sensor HAL should advertise how much data is returned per
// sensor
return 16;
}
return sSensorReportingModes[offset];
}
Does anybody know what each of these 16 float values represent?
There's a reason that SensorEvent doesn't tell you what the values are- it doesn't know. It can't know. SensorEvent is a generic class- it knows nothing about the types of values it can be returning. This allows OEMs to add new types of sensors that Google didn't think of when creating the API while still using the same framework. This is actually what makes it such a cool API. Want to hook up a thermometer? You can do that. A mass spectrometer? You can do that. A Geiger counter? You can do that. If you were limited to the predefined types this would be impossible, unless you waited for Google to update Android itself.
As for what those 16 values mean- its going to depend on the hardware. There's going to be a C or C++ driver somewhere feeding that data to the Android framework, and that driver is likely not open sourced (but maybe you're lucky and it is). I'd suggest going to QTI's website, finding the pedometers they sell, and looking at the hardware documentation. While its going to be for C or C++, it will likely be fairly simple to see what data it exports that would be sent up to the java layer.Th

What is the working mechanism of SENSOR_TYPE_GRAVITY to get its data? - Android

I have a 3-D acceleration vector (a, b, c) obtained from an Android phone accelerometer.
I wish to calculate the angle between this vector (a, b, c) and the gravity vector, which points exactly downwards.
I notice that in Android, there is a way of getting gravity vector under the context of the phone coordinate system instead of the world coordinate system. By simply use sensor type SENSOR_TYPE_GRAVITY, I can get the 3-D gravity vector (d, e, f). Since both vectors are under the same coordinate system, the phone coordinate system. By simple vector inner product, their angle can be easily found.
After finding this angle, even if we move from the phone coordinate system to the real world coordinate system, the angle remains unchanged.
My question:
What is the working mechanism behind the SENSOR_TYPE_GRAVITY?
I don't think there is another hardware that measures the gravity. Thus, it is just the acclerometer who measures the gravity. How is the gravity extracted our of the resultant accleration?
I am asking because I am concerned with the accuracy of this way of getting the gravity.
If it is inaccurate, I can implement a LPF to filter the DC component, the gravity, out by myself. I am not sure whether getting it from SENSOR_TYPE_GRAVITY is more accurate than I do it myself.
================================== UPDATES =======================================
Just to clarify, can I get the BOTH acceleration and gravity data correctly with the following codes?
By correctly, I mean do the acceleration and the gravity belong to one same particular time instant? I need to use the values at ONE time instant. So the situation where the acceleration is for time=13:12:00 and the gravity is for a later time, say time=13:12:01 is NOT desired.
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
acceleration[0] = event.values[0];
acceleration[1] = event.values[1];
acceleration[2] = event.values[2];
}
if (event.sensor.getType() == Sensor.TYPE_GRAVITY) {
gravity[0] = event.values[0];
gravity[1] = event.values[1];
gravity[2] = event.values[2];
}
}
The three sensors Sensor.TYPE_ACCELEROMETER, Sensor.TYPE_GRAVITY and Sensor.TYPE_LINEAR_ACCELERATION are related by the equation
"Sensor.TYPE_ACCELEROMETER" = "Sensor.TYPE_GRAVITY" + "Sensor.TYPE_LINEAR_ACCELERATION"
If you register for updates for all three, what you find is that Sensor.TYPE_ACCELEROMETER always arrives first, followed by Sensor.TYPE_GRAVITY and Sensor.TYPE_LINEAR_ACCELERATION, and that the values always satisfy that equation. Internally, Android is using filtering, and in particular a Kalman filter, to separate the two.
A low pass filter is a simple way of doing something similar. However, a lot of thought will have gone into the Android mechanism, so I'm sure that if a low pass filter was better, then Android would have implemented that.

Android get normalized acceleration

I wish to get the acceleration vector of an Android phone. The problem is, the accelerometer coordinates are relative to the phone's rotation. What I want is the "absolute" acceleration, i.e., it should return the same values whichever way the phone is facing. (I want to detect if a user that is skiing is sliding down a slope without using GPS. I also need to be able to differentiate sliding and going up the chairlift.)
I can probably get those values by combining the accelerometer with the gyroscope, but I have no idea how I could offset the accelerometer's values with the gyroscope's.
Is this possible, and if so, how?
What you describe can't be done, unless you redefine the problem a bit. To help you redefine it, I'll outline the main issues:
First, I'm guessing that what you mean by "absolute acceleration" is acceleration with respect to geographical reference. The can't be done with the accelerometer alone, since it has no idea about geographical references. If you move far enough for the gps, or use the compass, you might be able to get around this, but each of these has its own issues (though at least the problem is soluble).
The second issue is that gravity and acceleration are completely indistinguishable using an accelerometer alone (this is known as the "equivalence principle"). Therefore, any measured acceleration will always be the vector sum of gravity and the acceleration, but there are always multiple solutions to these equations, and in the usual cases where the acceleration is smaller than gravity, you really can't determine anything about the acceleration. Since gravity is somewhat constant though, there are ways around this too, using, say, a gyroscope, or maybe your user could hold the phone in a fixed orientation (e.g., by looking at external cues like the horizon), and either of these approaches might let you subtract the influence of gravity, but it's generally a non-trivial problem.
The final point to not is that you seem to be thinking in an earth-fixed coordinate system and the phone's accelerometer is only phone-fixed. That is the accelerometer's z-axis many not have anything to do with up and down on the earth -- and the relationship will depend on the orientation of the phone. Really, many people would prefer an earth-fixed system, but the phone just doesn't know that. You can use external cues (GPS, magnetic field, gyroscope, gravity, horizon, etc) to try to align them, but given only a single arbitrary reading form the accelerometer, the information just isn't there.
Definitions:
acceleration vector: this is the x, y, z reading from the accelerometer (and each reading will depend on the phones orientation), sometimes written as A=(ax, ay, az).
acceleration magnitude: this is a=sqrt(ax2 + ay2 + az2), and this should not depend on the phones orientation (if the different axes are calibrated to be the same). If the phone is stationary, this will basically just be a reading of gravity. Note also that a lot of the information in the acceleration vector is lost using this measure.
normalized acceleration: The acceleration direction, that has magniture 1, i.e., A/a
acceleration in earth coordinates: I think this is what you really want, there's just no easy way to get it, and really even if you could, I don't think it would be as useful as it might seem at first.
Skiing:
I think you have a good shot at determining when someone is skiing based on the measurements from the accelerometer. Things like bumps and turns should all be quite distinctive using the accelerometer. For these I'd use the full acceleration vector. For example, in turns, the acceleration magnitude would stay roughly constant and the direction would sweep. Also note that free-fall (i.e., basically whenever the skier doesn't have their skies/feet/butt/etc on the ground, whether they're going upward when launching off a bump/jump, or falling out of the chairlift), the acceleration magnitude will be zero in free-fall. For the chairlift, it seems that it will likely have a distinctive rhythmic sway mostly within a single plane.
All of these things could be figured out. I'd recommend, if you really want to solve this problem, is to record data from your accelerometer while skiing, and see if you can determine when you're skiing based on the characteristics of the data. (My guess is, that your major stumbling block with this will be math, because it might be a bit tricky to come up with an algorithm the can distinguish the signatures of skiing, so it seems that it would be a good idea to review vector math, and things like dot-products and cross-products, and also, I suspect that a little bit on another topic known as FFTs or Fourier transforms might be useful in sorting out the time and frequency signatures of skiing vs swinging in the chair lift.)
You could also fold in GPS measurements, which wouldn't be as reliable, or give good time resolution, but could at least be used to double-check your algorithm.
You can calculate acceleration regardless of the phone's orientation using:
a = sqrt(x*x + y*y + z*z)
Where a is the absolute acceleration and x, y and z are accelerometer values for each of the phone's 3 axes.
Some phones have a barometer (air pressure sensor) built in. After applying a moving average, I have found it to be write ready to determine if the user is going up or down -perhaps useful for your problem. On the galaxy s4 and 5 I get a resolution good enough to determine whether the device just moved from the table, to the floor.
Note, gradual changes in weather will affect your readings, so you must consider the Delta over a reasonable time interval, and ignore changes around some threshold.
Consider using the GPS. In a flight logging app I use the acceleration (albeit the absolute value, not the vector) to filter noisy GPS data (I remove locations where the acceleration needed for the change in speed is not plausible):
/**
* Remove noise from the measurement of the location.
* #param loc a location
* #return Answer <code>false</code> iff the location should not be used.
*/
private boolean filterNoise(final Location loc) {
if( ! loc.hasSpeed() )
return true;
if( this.recentSpeeds.isEmpty() ) { // rescentSpeeds is a queue of locations
this.recentSpeeds.add(loc);
return true;
}
final Location lastFix = this.recentSpeeds.getHead();
final long delta_t = (loc.getTime() - lastFix.getTime()) / 1000;
if( delta_t == 0 )
return false;
final float delta_v = loc.getSpeed() - lastFix.getSpeed();
final float a = delta_v / delta_t;
if( Math.abs(a) <= AccelThreshold ) {
this.recentSpeeds.add(loc);
return true;
}
return false;
}
If you compute the speed using the coordinates from the last fix and the current fix you get the acceleration as vector.

Android compass noise algorithm

I am trying to filter out the noise from the orientation/compass sensor in my magic phone.
Some of the readings seem to be 90-180 degrees off and there is a lot of jiggle. I have tried different things with limited success, so I was wondering if anyone could recommend an algorithm to filter this sort of noise to get a stable output.
BR, Mads
You need Low Pass Filter. There are explanation and simple algorithm on wikipedia
This is really late but it might help people like me who came to this question.
Use Type Rotation vector sensor. No need to use a low pass filter or calculate an average value of the last x sensor values.
Here's some code:
private float[] mMatrixR = new float[9];
private float[] mMatrixValues = new float[3];
#Override
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_ROTATION_VECTOR:
// Get rotation matrix
SensorManager.getRotationMatrixFromVector(mMatrixR, event.values);
SensorManager.getOrientation(mMatrixR, mMatrixValues);
// Use this value in degrees
mAzimuth = Math.toDegrees(mMatrixValues[0]);
}
I found the values very fast and smooth and use these in my app. I used accelerometer and magnetometer as a backup in case rotation type vector isn't present in the device, it's a software based sensor (Sensor fusion) which uses the magnetometer, accelerometer and gyro (if present).
I got rid of most of the noise by just using a slower update time. I'm not sure if Android has a built-in filter for these, but it seems to stabalize a lot. Something like:
mSensorManager.registerListener(
mSensorListener,
mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
// SENSOR_DELAY_UI, instead of SENDOR_DELAY_FASTEST (or similar)
// seems to iron out a lot of the jitter
SensorManager.SENSOR_DELAY_UI
);
SensorManager offers:
SENSOR_DELAY_FASTEST : get sensor data as fast as possible
SENSOR_DELAY_GAME : rate suitable for games
SENSOR_DELAY_NORMAL : rate (default) suitable for screen orientation changes
SENSOR_DELAY_UI : rate suitable for the user interface
What have you tried? How many readings do you get per second?
I would suggest something along the lines of an average of the last X number of readings to get rid of the "jiggles" and throw away any readings that are wildly different from the current direction to stop any crazy "jumping" of values.
Depending on how many readings you are getting, and how much averaging you are doing, your app may lose responsiveness.
The following link might be useful.
http://www.chem.uoa.gr/applets/appletsmooth/appl_smooth2.html
If you do get a significant number of completely-wrong values, you probably don't want to just average them. You could try applying a median filter first - take N samples, calculate the median, and throw out anything more than +- some threshold value. You can apply a smoothing filter after that.
If your readings are "90-180 degrees off", then either you need to calibrate your compass, or your sensor is faulty.
Certainly, the magnetic sensor has a lot of jiggle, but the "standard deviation" of such noise is about 4 degrees off. You can choose a variety of mathematical filters (low pass, Kalman) and algorithms (averaging, drop spurious readings) to apply to the measurements that can give you acceptable results.
Are you using:
List<Sensor> sens = mySensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
You might be registering 2 separate sensor handles which are both being directed to your onSensorChanged method. Now, on my onSensorChanged method, I'm sending the bearing value to either the primary or secondary method based on the Vendor name. So try this code out:
Sensor sen = e.sensor;
double bearing = 0;
if (sen.getType()==Sensor.TYPE_ORIENTATION) {
bearing = e.values[SensorManager.DATA_X];
}
if (sen.getVendor().equals(sensorVendors[0])) {
myCompassView.setBearing(bearing);
} else {
myCompassView.setSecondaryBearing(bearing);
}

Categories

Resources