Issue
I am currently developing an android app that collects IMU datas(Accelerometer, Gyroscope and Magnetic field) for positioning, which requires a very high level accuracy in measurement time. All three sensors are set to SENSOR.DELAY.FASTEST and this is my code.
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
final float alpha = 0.97f;
//in milliseconds since last boot
time = SystemClock.elapsedRealtime();
switch (sensorEvent.sensor.getType()){
case Sensor.TYPE_ACCELEROMETER:
Log.d(TAG,"TYPE_ACCELEROMETER: "+sensorEvent.timestamp);
//System.arraycopy(sensorEvent.values,0,LastAccReading,0,sensorEvent.values.length);
LastAccReading[0] = alpha * LastAccReading[0] + (1-alpha) * sensorEvent.values[0];
LastAccReading[1] = alpha * LastAccReading[1] + (1-alpha) * sensorEvent.values[1];
LastAccReading[2] = alpha * LastAccReading[2] + (1-alpha) * sensorEvent.values[2];
break;
case Sensor.TYPE_MAGNETIC_FIELD:
Log.d(TAG,"TYPE_MAGNETIC_FIELD: "+sensorEvent.timestamp);
//System.arraycopy(sensorEvent.values,0,LastMagReading,0,sensorEvent.values.length);
LastMagReading[0] = alpha * LastMagReading[0] + (1-alpha) * sensorEvent.values[0];
LastMagReading[1] = alpha * LastMagReading[1] + (1-alpha) * sensorEvent.values[1];
LastMagReading[2] = alpha * LastMagReading[2] + (1-alpha) * sensorEvent.values[2];
break;
case Sensor.TYPE_GYROSCOPE:
Log.d(TAG,"TYPE_GYROSCOPE: "+sensorEvent.timestamp);
LastGyroReading[0] = sensorEvent.values[0];
LastGyroReading[1] = sensorEvent.values[1];
LastGyroReading[2] = sensorEvent.values[2];
}
// Rotation matrix based on current readings from accelerometer and magnetometer.
SensorManager.getRotationMatrix(rotationMatrix, inclinationMatrix,
LastAccReading, LastMagReading);
// Express the updated rotation matrix as three orientation angles.
SensorManager.getOrientation(rotationMatrix, orientationAngles);
//Log.i("OrientationTestActivity",String.format("Orientation: %f, %f, %f", orientationAngles[0], orientationAngles[1], orientationAngles[2]));
}
By looking at the sensorEvent.timestamp enter image description here I can see that Accelerometer and Gyroscope has the same sampling rate around 420Hz and magnetic field is much slower at about 100Hz. And the accelerometer and gyroscope are not returning sensor values at the same time, which means that at a certain timestamp only one of the three sensor values are valid and the other two would have a time offset of a few milliseconds.
Is there anything I can do about that? Thanks in advance.
Solution
Consumer sensors are not the most accurate and are subject to noise and drift (check any IMU datasheet for details). They are accurate enough for common use cases.
At a minimum any Android based sensor will need to conform to the Android Compatibility Definition Document section on Sensors.
As sensors in devices isn't a new subject, it may help to watch this old Google Talks video on sensor fusion: Sensor Fusion on Android Devices: A Revolution in Motion Processing
While you may be concerned about different time stamps, I believe your desired level of precision unrealistic as the sensors have noise margins and drift to their values.
As to what developers do given existing error margins, given that the use case is human motion, sensor data is usually averaged over time and/or passed through a low-pass filter. For most use cases this is enough but as noted in this article on ARCore and ARKit on those devices supporting AR, certifying the sensors in those devices will require stricter (and most costly) calibration.
Those attempting VR/AR type behavior are probably using SensorDirectChannel but be aware of the stated rate variance and hardware/OS limitations.
Answered By - Morrison Chang
Answer Checked By - David Marino (JavaFixing Volunteer)