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.
Related
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
I need to get the current value of the proximity sensor (rather than implementing a continuous listener). On some devices, the first reported value will be a default value (e.g. "FAR") that isn't necessarily accurate, and actual values will only start appearing after the second or third reading. At the moment, I've implemented a 1-second Handler and use the last reported value (after the second has elapsed) as the "true" value, but this solution seems crude (and slow). Is there a better approach that works on all 4.0+ devices? I could simply count up until I've received 3 readings, but on some devices (e.g. GNex), the first value will be correct, and the value will only change after that if there is actually a change in the sensor.
You can do what I did:
You probably have an if statement on the listener - one logic flow for near and one for far.
Instead of waiting on the handler - do this:
if(near) {
myHandler.removeCallbacks(yourRunnableForFar);
myHandler.postDelayed(yourRunnableForNear,100);
else {
myHandler.removeCallbacks(yourRunnableForNear);
myHandler.postDelayed(yourRunnableForFar,100);
}
Notice that the inaccurate first reading(s) will immediately be followed by an accurate one, so the last one "wins".
This code works well if you didn't register sensors other than proximity. If you have a flow of readings from other sensors, than use a static flag (such as the boolean near) to trigger the handler calls only on state change.
Elaboration:
yourRunnableForFar and yourRunnableForNear - are placeholders that implement Runnable to hold your app logic on what to do when the proximity sensor returns "near" (event.values[0] == 0) or "far" (not 0).
myHandler is just any Handler you might created, or declare one just for this with Handler myHandler = new Handler(Looper.getMainLooper());
You might want to acquire a proximity lock on near, and release it and clear the listener on far. But this is app logic that might be completely different from app to app.
I need some help getting info from the orientation sensor. As I have seen in just about every tutorial/guide out there, the values are passed to an event (onSensorChanged(SensorEvent event) in which they can be manipulated.
My problem is that I don't want to keep the electro-magnetic/orientation sensor running constantly (for the sake of battery life). I want to be able to turn it on, grab the current value and switch it off. Is there any way to do this?
I have done some searching and found that I can try multi-threading, but I'm not fully comfortable with that.
What I'm looking for is something like (Sorry for lack of formatting I can't seem to figure it out):
private void getOrientationNOW() {
m_SensorManager.registerListener(mySensorEventListener, m_MagneticSensor, SensorManager.SENSOR_DELAY_FASTEST);
//---->Something here to get the current value from the sensor
m_SensorManager.unregisterListener(mySensorEventListener);
}
If this is possible, please help me!
Thank you all in advance!
When you register a listener for a sensor the activity will be called every time the sensor values changes according to the parameters. So if you want to get the values only one once what you could do is unregister the listener for that sensor after getting the value once.
I would like to write a program using tilting functionality in android.
Is there any way to intercept it? What do I get back? a vector indicating the direction of the tilt?
The main thing to get your head around it the concept of a listener.
In Android there isn't a method called getXtilt(), getYtilt() etc to get the orientation.
Instead you need to create a listener which you register with the system.
Look at this.
See the onSensorChanged(SensorEvent event) method? The Android system will invoke that method every time the sensor changes (which is very frequently). In this case it will be the TYPE_ACCELEROMETER sensor readings you will be receiving.
So when you get the SensorEvent 'event' have a look at the event.values[] array. It will contain the sensor readings. In the example code in the Android doc they register the Sensor.TYPE_ACCELEROMETER. You should register the
Sensor.TYPE_ORIENTATION. Have a look at the values array for
Sensor.TYPE_ORIENTATION. They are the tilt values you are looking for.
hope that helps
Sounds like you would have to use the Accelerometer and interpret the values and then make a decision based on them. Look up SensorEventListener and it should get you in the right direction.
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);
}