How to get pitch and roll from accelerometer data on Windows Phone
This article explains how to retrieve the pitch and roll values from the accelerometer values.
If you are developing a Windows Phone game or any kind of application that uses the phone movement as an input, you should probably use the Motion class. This class handles the low-level sensor calculation and allows applications to easily obtain the device’s attitude (yaw, pitch, and roll), rotational acceleration, and linear acceleration both due to gravity and user movement.
The main advantage of using the Motion class is that internal algorithms already compensate for noise using the values from all available sensors (e.g. accelerometer, gyroscope and compass). So instead of using the accelerometer directly you could use the pitch and roll values - for example you might control a car using the pitch to for speed and the roll value for movement left/right). This would actually ensure that you have a better control than the one provided by the accelerometer data without the need to use filters like explained in the article Using the Accelerometer on Windows Phone 7.
Unfortunately while the accelerometer is mandatory on every Windows Phone device, the compass is an optional sensor - and the Motion class will not work if your device doesn't have an compass. Therefore on some phones, including the Lumia 520 and Lumia 625, you will not be able to use the Motion class. As a developer you will have to choose between using the more noisy accelerometer that works on all the devices, use the more precise Motion but only support devices with a compass, or support different control algorithms and choose the "best" one available.
It is possible to retrieve the pitch and roll values from the accelerometer data, which means that you can have a single control algorithm using pitch and roll. When the Motion class is supported you can use the more precise values - on devices without a compass you can use the values calculated from the accelerometer data.
Obtaining the pitch and roll angles is then a matter of being able to read the accelerometer, convert these readings to the "g" unit (1g = 9.8 m/s²), and apply the corresponding equations. The process of obtaining and converting the accelerometer readings depends on the accelerometer the phone uses. The formula used to calculate the acceleration from the raw accelerometer readings is:
In our case the Accelerometer class already gives us the values expressed in G-forces so we already have Gx, Gy and Gz
Having the acceleration data calculated on all the axes we can easily calculate the pitch and roll values using these formulas:
Please note that the values are not defined when Gx and Gz are both equal to 0.
At this point we have the pitch and roll values, but the result is pretty noisy so we apply a Low-pass filter to remove the short-term fluctuations. The formula for a low-pass filter is pretty easy and it uses the previous read values, the current one and a smoothing factor which I chose equal to 0.5. To better understand the smoothing have a look at the article Using the Accelerometer on Windows Phone 7.
The formula applied is:
On = On-1 + α(In – On-1)
O is the output (filtered value) and I is the input (raw value), and α is a “coefficient” with a value between 0 and 1. If the coefficient is 1, the output is exactly the same as the input. If the coefficient is 0, the output is always the initial number. Those are the boring cases, the more interesting cases are when the coefficient is somewhere in between. The lower the coefficient, the more smoothing goes on. This formula gives you a single number to twiddle to get the smoothing effect you want.
The implementation is pretty easy: we will subscribe to the accelerometer values, apply the filters and memorize the previous values and apply the formulas to calculate the pitch and roll (I am also remapping the roll to the -180 - 180 interval). I have chosen to convert the values from radians to angles ( * 180/Pi).
const double alpha = 0.5;
double fXg = 0;
double fYg = 0;
double fZg = 0;
void acc_ReadingChanged(Windows.Devices.Sensors.Accelerometer sender, AccelerometerReadingChangedEventArgs args)
//Low Pass Filter
fXg = args.Reading.AccelerationX * alpha + (fXg * (1.0 - alpha));
fYg = args.Reading.AccelerationY * alpha + (fYg * (1.0 - alpha));
fZg = args.Reading.AccelerationZ * alpha + (fZg * (1.0 - alpha));
//Roll & Pitch Equations
double pitch = (Math.Atan2(-fYg, fZg) * 180.0) / Math.PI;
double roll = (Math.Atan2(fXg, Math.Sqrt(fYg * fYg + fZg * fZg)) * 180.0) / Math.PI;
pitch = (pitch >= 0) ? (180 - pitch) : (-pitch - 180);
In order to have a better idea on how good/bad the algorithm works in the attached sample I used the Telerik chart control to parallel plot, in real-time, the pitch and roll values. To run this sample you will need the Telerik controls. if you don't have it you can download the trial version from here: Telerik for Windows Phone.
Here are some screenshots I have taken from the sample. You will see that the calculated values are pretty good (also take into consideration that the accelerometer curve is always in advance because I force the readings one after the other and the first read is always the accelerometer).
From the graphs you can see that pitch and roll information captured from the accelerometer alone can quite closely match that of the Motion class, but is more susceptible to noise even with a low pass filter. It is always better to use the values from the Motionclass when available.