Filtering data from acceleration and magnetic sensors on Android

Asked

Viewed 570 times

4

I have a player open source for Android some time ago. Months ago I created an audio viewer (the type of ones with bars and colors) where the user could interact, simulating an augmented reality (the view appears to be "around" the person, as the person moves the device).

After a few tests, I noticed that the data from the magnetic sensors and acceleration oscillate a lot (as is to be expected in analog sensors), so I use a first-order low-pass filter to smooth the movement. So far, no news.

It turns out that by watching this clip of Björk on Youtube device, I realized that it follows the same principle, and offers a 360 degree experience.

However, I noticed that the movement on Youtube is not as smooth/slow as in my viewer, but it does not "shiver" when released on a smooth surface. In other words: the raw data, soon my viewer, oscillate a lot when the device is at rest, so I had to filter, and the movement ends up being a little slow. But on Youtube, there are no "tremors" when the device is on the same surface at rest, but it responds much faster than my viewer.

As you can see in code where I handle sensor data (function onSensorData, from line 286), I am using the following filter:

coefNew = (0.03125f / 16.0f) * delta;
coefOld = 1.0f - coefNew;

Remarks:

  • coefNew is used in input (x) and coefOld at the previous exit (y)
  • delta is the interval, in milliseconds, between the previous and the current
  • / 16.0f * delta serves to adjust the coefficient depending on the fps rate

I have tried to "play" with the coefficient, and if I increase its value, the movement starts to respond faster, but the tremors come back (as expected).

Even though I was in C++, I based myself on the class code SensorManager, from Android itself, to generate the linear transformation matrix: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/android/hardware/SensorManager.java

Hence my question: Is there another type of filter / technique that can be used in this situation? Does anyone know how Youtube achieves that fast responsive but not "quivering" move (there is another Android data acquisition method to create the linear transformation matrix)?

1 answer

3


After much search I was able to choose the top 3 links that enlightened me in this issue:

I basically discovered that:

  • The magnetic sensor data is actually quite noisy on all devices, much noisier than the gravity/acceleration sensor data
  • Not using the magnetic sensor involves having to use the gyroscope, which is also quite noisy, and, in turn, ends up having to use a Kalman filter to estimate and filter the values returned by the sensor
  • Use other second-order (or higher-order) low-pass filters, such as Filtros Butterworth, among others, it does not really remove the type of noise present in the magnetic sensor data (I tested filters of various orders, with different cut frequencies, all without the expected result)
  • Utilise Holt-Winters double exponential Smoothing (also with references here) or lets the noise pass too much when the device is effectively stopped, or creates a overshoot unwanted
  • Use sensors of type TYPE_ROTION_VECTOR and TYPE_GEOMAGNETIC_ROTATION_VECTOR does not really solve, because the data that form the quaternion returned in Sensorevent.values are not filtered by Android
  • Using the type sensor TYPE_GAME_ROTION_VECTOR is very risky, although it does not use the magnetic sensor internally, because I tested it on some "top of the line" devices, with API 18+, and this sensor was not present (Sensormanager.getDefaultSensor() returned null)
  • Unable to find Youtube source code so easily :)

So I gave up and started doing some simulations in Excel using real data obtained from the magnetic sensor during the use of the device, as in the following graphic, which shows the device at rest, then the device being rotated, and finally, rotated back, almost to the starting position:

Dados reais obtidos do sensor magnético

I decided to use a low-pass filter with adaptive cutting. The lower the change in value, in relation to the previous value, the lower the cut-off frequency (producing a slower response), and the higher the change, the higher the cut-off frequency (producing a faster response).

The graph below shows the coefficient used with the low-pass filter. It follows the following quadratic equation (absDelta represents the absolute value of the difference between the sensor’s previous value and the current value):

coeficiente = (0.05 × absDelta²) + (0.025 × absDelta)

Reposta do coeficiente do filtro passa-baixa

The final value limited to 0.15, producing the following code:

absDelta = valorAtual - valorAnterior;
coefNew = (absDelta >= 1.5f ?
              0.15f :
              ((0.05f * absDelta * absDelta) + (0.025f * absDelta))
          ) * 0.0625f * delta;
coefOld = 1.0f - coefNew;

Remarks:

  • coefNew is used in input (x) and coefOld at the previous exit (y)
  • delta is the interval, in milliseconds, between the previous and the current
  • 0.0625f * delta serves to adjust the coefficient depending on the fps rate

Finally, the result obtained with this technique is as shown in the graphic below, where the black lines show the filtered value:

Dados filtrados do sensor magnético

This filter is applied independently for each of the x, y and z axes.

The complete code is already on project repository, within the function onSensorData() (currently on line 286), but today (2015-07-07) is still not in the version of the application that is on Play Store.

I did several tests and the result pleased me a lot. It responds quickly when I move the device quickly, and filters out a lot of the noise when I leave the device stationary.

I even used other functions in place of the parable shown above, such as a Interpolation of Hermite or other cubic functions, but the response was either too slow, or the noise was too apparent. Still, I came to another parable that can also be used, but that "for my taste" left the response too slow for small real variations of the device:

coeficiente = (0.065 × absDelta²) + (0.0025 × absDelta)

For the gravity/acceleration sensor data, I kept the first order low-pass filter, because the data is not as noisy as the magnetic sensor:

coefNew = (0.140625f / 16.0f) * delta;
coefOld = 1.0f - coefNew;

Browser other questions tagged

You are not signed in. Login or sign up in order to post.