I'm currently writing a simple android application to calculate the biases of the devices imu. While doing this I've come across a problem with the value of event.timestamp
Using the code:
float dT = (event.timestamp-accel_timestamp)*NS2S;
from the example at the Android Reference Guide to calculate the rotation matrix from the quaternion.
When I run the code using a Galaxy Nexus-S I get a dT of 0.06~0.07 sec between measurements but when I run the same code on a LG Nexus 4 or Nexus 7 the dT is always 0. I'm aware of the question, Android SensorEvent timestamp issue that the Nexus 7 timestamp is a unix timestamp but the difference between successive measurements shouldn't always be zero. The Nexus 4 and Nexus 7 both have the same IMU could this be a bug in how the timestamp is created from the IMU?
Wow, ok this is surely a bug!
The timestamp of each SensorEvent is being overwritten as if it were a static variable...
When I record a string of the timestamp when the event occurs, all values are different.
The events are stored in an array unchanged.
Every SensorEvent in the array now has the same timestamp, but the values arrays are still different (i.e., they're not the same object and contain different information... EXCEPT for the timestamp).
Google/HTC, please return 3 hours of my life!
I'll go file a bug report, unless anyone can explain this behaviour. It's certainly not documented in the API.
In the meantime, try out this solution:
import android.hardware.Sensor;
import android.hardware.SensorEvent;
public class UnbrokenSensorEvent {
public long timestamp;
public float[] values;
public Sensor sensor;
public UnbrokenSensorEvent(SensorEvent event){
this.timestamp = event.timestamp;
this.values = event.values;
this.sensor = event.sensor;
}
}
Then do something like this in your listener:
ArrayList<UnbrokenSensorEvent> results = new ArrayList<UnbrokenSensorEvent>();
public void onSensorChanged(SensorEvent event) {
results.add(new UnbrokenSensorEvent(event));
}
The refactoring should be quite easy, since SensorEvent and UnbrokenSensorEvent have the same public fields. If you need to use other SensorEvent functionality, just go ahead and chuck it into the Unbroken version.
It's hacky, but IMHO a quick hack is always better than waiting for an API to be updated!
Also do note the documentation on SensorEventListener's onSensorChanged -method:
NOTE: The application doesn't own the event object passed as a
parameter and therefore cannot hold on to it. The object may be part
of an internal pool and may be reused by the framework.
Found here: http://developer.android.com/reference/android/hardware/SensorEventListener.html#onSensorChanged%28android.hardware.SensorEvent%29
...which suggests that one should not hold references to SensorEvent -objects.
If you copied snippet from here
Notice it has a bug.
Need replace
private float timestamp;
to
private long timestamp;
In other case your delta time will always contain weird value
Related
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.
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.
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 was wondering for one of the apps I'm writing does the Location.getSpeed() account for Altitude change? i.e. if I am going down a slope does it return a speed as if I'm going straight or does it actual do the calculation based on the triangle formed due to the downward slope?
The Location class does NOT consider altitude when giving you the speed.
The source for getSpeed is
public float getSpeed() {
return mSpeed;
}
And mSpeed assignment is mSpeed = (Parcel)in.readFloat();. Ultimately, if I'm not mistaken, it looks like the Parcel comes from ILocationManager.aidl which is private so I don't know, but I would guess it pretends you're not on a slope when it decides what the speed is.
And a lot less fun, but the docs say about getSpeed():
Get the speed if it is available, in meters/second over ground
which sounds like it does not consider altitude changes.
If you really want to know, you could somehow test it or you could file a bug regarding the getSpeed() documentation.
I'm building an Android app with a simple 3D engine. It was working earlier, but was incredibly messy. I've refactored the heck out of it, but I did little to the 3D engine. Now I'm running into a very strange problem where calling translatef within the draw() method (which itself is called from within onDrawFrame within the class that implements GLSurfaceView.Renderer) does not use the current value of the x/z float variables, which are scoped to the class. I cannot explain it. There is zero code that would reset them to their initial values, but that's what is happening. The only time these variables are touched are when the class is instantiated (done once, never touched again), when they change due to user input (verified as accurate with the debugger at every point possible), and when translatef is called, at which point they are only read.
The thing is, they are always the initial value that was passed to the constructor when read by the translatef method, but are accurate otherwise at any other point in the class when they are used or altered. If, say, we set z to z + 1, it remains at that value regardless of the value read by translatef. What gives? I'm at a loss as to why the draw() and/or translatef() methods are somehow sticking to the original values and not reading the modified values. I'm utilizing GL ES 1.0 and Android 2.2 as my testing platform.
There's too much to just dump it all here, but this should be sufficient to get the problem across:
private float movex, movez;
public GLEngine(){
//The values are set in the constructor and are accurate at this point.
movex = Controller.getX();
movey = Controller.getZ();
}
public void move(float x, float z){
//Again, debugging in here shows that the values are being set properly.
movex = x;
movez = z;
}
public void onDrawFrame(GL10 gl){
//Debugging anywhere within this method shows only the values for movex and
//movez that were set in the constructor.
...
gl.translatef(movex, .5f, movez);
...
I got a tip to make movex and movez volatile as this was likely related to threading issues (although I have not explicitly declared any threads here), but that didn't help.
As both values are held outside the class, I was able to make this work by pulling them from the controller in the onDrawFrame method, but this requires the variables to be pulled and additional operations to be performed on them on every draw, which is costly. There has to be a better way...
Since x and z are private variables, it should be easy to track down.
You are only ever setting them via your move method. To find this out, delete or comment out your move method (temporarily) and do a save. Eclipse will highlight any code that is now broken as a result.
Once you are sure that all changes for x and z are going through the move method, set a breakpoint there. (You can set conditional breakpoints as well).
If you stop at a breakpoint, you can can see which method called move.
This should let you track the problem down.