While working on my master thesis, I’ve made some experiences with sensors in Android devices and I thought I’d share them with other Android developers stumbling over my blog. In my work I was developing a head tracking component for a prototype system. Since it had to adapt audio output to the orientation of the users head, it required to respond quickly and be accurate at the same time.
I used my Samsung Galaxy S2 and decided to use its gyroscope in conjunction with the accelerometer and the magnetic field sensor in order to measure the user’s head rotations both, quickly and accurately. To acheive this I implemented a complementary filter to get rid of the gyro drift and the signal noise of the accelerometer and magnetometer. The following tutorial describes in detail how it’s done.
There are already several tutorials on how to get sensor data from the Android API, so I’ll skip the details on android sensor basics and focus on the sensor fusion algorithm. The Android API Reference is also a very helpful entry point regarding the acquisition of sensor data. This tutorial is based on the Android API version 10 (platform 2.3.3), by the way.
This article is divided into two parts. The first part covers the theoretical background of a complementary filter for sensor signals as described by Shane Colton here. The second part describes the implementation in the Java programming laguage. Everybody who thinks the theory is boring and wants to start programing right away can skip directly to the second part. The first part is interesting for people who develop on other platforms than Android, iOS for example, and want to get better results out of the sensors of their devices.
Update (March 22, 2012):
I’ve created a small Android project which contains the whole runnable code from this tutorial. You can download it here:
SensorFusion1.zip
Update (April 4, 2012):
Added a small bugfix in the examples GUI code.
Update (July 9, 2012):
Added a bugfix regarding angle transitions between 179° <–> -179°. Special thanks to J.W. Alexandar Qiu who pointed it out and published the soultion!
Update (September 25, 2012):
Published the code under the MIT-License (license note added in code), which allows you to do with it pretty much everything you want. No need to ask me first
Sensor Fusion via Complementary Filter
Before we start programming, I want to explain briefly how our sensor fusion approach works. The common way to get the attitude of an Android device is to use the SensorManager.getOrientation() method to get the three orientation angles. These two angles are based on the accelerometer and magenotmeter output. In simple terms, the acceletometer provides the gravitiy vector (the vector pointing towards the centre of the earth) and the magnetometer works as a compass. The Information from both sensors suffice to calculate the device’s orientation. However both sensor outputs are inacurate, expecially the output from the magnetic field sensor which includes a lot of noise.
The gyroscope in the device is far more accurate and has a very short response time. Its downside is the dreaded gyro drift. The gyro provides the angular rotation speeds for all three axes. To get the actual orientation those speed values need to be integrated over time. This is done by multiplying the angular speeds with the time interval between the last and the current sensor output. This yields a rotation increment. The sum of all rotation increments yields the absolut orientation of the device. During this process small errors are introduced in each iteration. These small errors add up over time resulting in a constant slow rotation of the calculated orientation, the gyro drift.
To avoid both, gyro drift and noisy orientation, the gyroscope output is applied only for orientation changes in short time intervals, while the magnetometer/acceletometer data is used as support information over long periods of time. This is equivalent to low-pass filtering of the accelerometer and magnetic field sensor signals and high-pass filtering of the gyroscope signals. The overall sensor fusion and filtering looks like this:
So what exactly does high-pass and low-pass filtering of the sensor data mean? The sensors provide their data at (more or less) regular time intervals. Their values can be shown as signals in a graph with the time as the x-axis, similar to an audio signal. The low-pass filtering of the noisy accelerometer/magnetometer signal (accMagOrientation in the above figure) are orientation angles averaged over time within a constant time window.
Later in the implementation, this is accomplished by slowly introducing new values from the accelerometer/magnetometer to the absolute orientation:
// low-pass filtering: every time a new sensor value is available // it is weighted with a factor and added to the absolute orientation accMagOrientation = (1 - factor) * accMagOrientation + factor * newAccMagValue;
The high-pass filtering of the integrated gyroscope data is done by replacing the filtered high-frequency component from accMagOrientation with the corresponding gyroscope orientation values:
fusedOrientation =
(1 - factor) * newGyroValue; // high-frequency component
+ factor * newAccMagValue; // low-frequency component
In fact, this is already our finished comlementary filter.
Assuming that the device is turned 90° in one direction and after a short time turned back to its initial position, the intermediate signals in the filtering process would look something like this:
Notice the gyro drift in the integrated gyroscope signal. It results from the small irregularities in the original angular speed. Those little deviations add up during the integration and cause an additional undesireable slow rotation of the gyroscope based orientation.


Hi
Thanks for this great tutorial, been suffering with the gyro drift since starting a port of an IOS game to Android (IOS does something like this behind the scenes).
This tutorial was really helpful, and easy to follow and so far results seem much improved.
The Android Dev community salutes you.
Thanks Again
Tom
Can you send me the full code, please?
@Ray: I will provide a sample project within a few days. The code snippets in the tutorial are from a larger project which would be only confusing.
@Tom: Thank you! I really appreciate your feedback. I’m glad the tutorial was helpful. It’s very motivating to me.
Many thanks!
It is a fantastic tutorial, really helpful. Thanks for sharing.
@John: You’re welcome
I’ve added a complete source code project for this tutorial. However, I’m not satisfied with the application’s output, yet. In the original project I did additional low-pass-filtering on the accelerometer/magnetometer output which made the output even less noisy. I simply averaged all orientation angles in accMagOrientation within a time window of 30 samples in each sensor pass.
Hi Paul,
One things bothers me – are you sure Sensor Fusion is not implemented natively on Android? Is there a way to check this?
After watching Google Tech Talk (http://youtu.be/C7JQ7Rpwn2k) it seems, that they’ve worked on this feature. What’s more, here (http://goo.gl/YqSU4) is written about “fused sensor” available through TYPE_LINEAR_ACCELERATION, TYPE_GRAVITY, TYPE_ROTATION_VECTOR. These properties weren’t available on every phone, but it would be really strange if the Galaxy SII wouldn’t support them. After all it’s a top model. Are you really sure, that sensor fusion is not available on your phone? Do you know the reason – why?
Cheers,
Filip
Hi Filip,
yes that bothered me, too. I talked to an employee at my university who did a lot of stuff with sensor fusion and Android. He referd the same Google Tech Talk video and told me that every device running Android 2.3 and higher should support sensor fusion. However, I was not convinced, since I wasn’t able to find specific information on sensor fusion in the Android API. I searched for a while, but the only thing I could find was this Google Tech Talk video you mentioned.
The guy talking in this video does not work for Google but for a MEMS-sensor manufacturer (InvenSense). Also, if you listen closely, at the very beginning of his presentation (somewhere at 2:40) he states that they had to modify the Nexus1 which he uses in his demos. So no indication for fancy sensor fusion algorithms in non-custom Android devices here.
I also have been referred to the sensor type TYPE_ROTATION_VECTOR being told that this is what I wanted. I compared the sensor quality with the output of my algorithm and I was disappointed. It behaved exactly like the magnetometer in conjunction with the accelerometer (maybe that’s what they mean by “fused”, but that’s not what I was looking for). The only change was the data type. It provies a quaternion-like rotation vector instead of a vector with three angles. The sensor types you mentioned are virtual sensors (if you check the vendor in your code you should get something like “Google Inc.” instead of a hardware vendor). They use raw sensor data and provide new data in different orientation/movement conventions. However, the provided data seems not to be improved in terms of signal quality.
The reason for this may be that only a few Android devices have a gyro. So It’s not that important for Android to support a nice accelerometer/magnetometer/gyro fusion solution. That’s different for iOS, since there are only a few devices which all have the necessary sensors.
Also, it is not a question of the device but a question of the OS. The Galaxy S2 is indeed a fabulous Android smartphone. But it’s Android which apparently lacks a proper sensor fusion algorithm.
To me it seems like Android really does not have any sensor fusion approaches, yet. At least not in Gingerbread. Maybe Ice Cream Sandwich introduces some sensor fusion stuff. Anyway, if you find out that I’m wrong, please drop me a line!
Hi Paul, this is really great article. If I understand it correctly, your solution enables you to get orientation vector from fused sensors. I have also seen the Google Tech Talk video and the guy mentions that it’s possible to get more precise linear acceleration and gravity information using sensor fusion. Could you please advise how could one do it (just the principle)? I am not so good at these things and it would be really helpful for me. Thank you.
Hi Braňo,
you can use the orientation angles from the sensor fusion to calculate the “down”-vector (or the gravity-vector). This way you should get a more accurate and reliable gravity information than the accelerometer provides. This improved gravity information can be used to calculate linear acceleration more accurately, since you need to eliminate gravity from the total acceleration data. You need to know where down is (namely the current tilt of your device).
So basically the formula would be something like
step1 : [orientation]–>(by trigonometry)–>[gravity vector]
step2 : [raw acceleration] – [gravity vector] = [linear acceleration]
(let me know if this isn’t clear enough, maybe I missed something out)
btw: Do yourself a favour and do not try to get the device’s position by double integrating your acceleration data. You will end up with your position somwhere on the moon
Thanks for the project zip;
After adding some logging and running on my Galaxy Nexus running Android 4.0.2:
* onCheckedChanged() never seems to be invoked; radioSelection is always 0
* updateOreintationDisplay() gets invoked even while SensorFusion is not displayed.
Thanks for the feedback, Steve.
I will fix the issues as soon as possible. I was already wondering, why the displayed sensor data is still noisy after I selected the sensor fusion. I rushed a bit through the GUI code. Sorry for that.
Fixed it.
I just forgot a “mRadioGroup.onCheckedChangedListener(this);” in the onCreate method.
Hi Paul,
like I said earlier, your article really helped me, great work. I have changed the code a little bit (so I could run it on my Android 2.2) and extracted it into separate SensorFusionManager. I also added few things (computation of gravity vector from rotation matrix). I would like to publish it as a library on GitHub. I would preserve link to your blog, of course. I have not seen the license anywhere, so I want to know, if you would be ok with that. Thanks.
0Steve, it’s not just on the iPhone. Nova on Android also supports gpyoscoric controls, so when I’m playing it on my Galaxy Tab I just point the phone in the direction I want to move or fire.I guess the new APIs must give some additional control otherwise I’m not clear exactly what’s new.
Hi Paul,
Thanks for your extensive reply – I thought that you haven’t replied, till today when I accidentally drop in on here.
Your hint with ICS turned me to changelog (http://developer.android.com/sdk/android-4.0.html). Here’s what I found:
Additionally, Android’s three synthetic sensors have been greatly improved so they now have lower latency and smoother output. These sensors include the gravity sensor (TYPE_GRAVITY), rotation vector sensor (TYPE_ROTATION_VECTOR), and linear acceleration sensor (TYPE_LINEAR_ACCELERATION). The improved sensors rely on the gyroscope sensor to improve their output, so the sensors appear only on devices that have a gyroscope.
Would be cool if you’d test it as a more experienced person in this area. Anyway, I’ll try to do it on my own in a next few days.
Thanks again,
Filip
Hi Filip,
these are good news. Thanks for the link. So Android 4.0 does sensor fusion out of the box if we trust this changelog. Unfortunatelly I don’t own a device running Android 4.0 and I still have to wait for the european upgrade for my Samsung Galaxy S2 (I don’t like custom roms). But I will test it as soon as possible.
Hi Paul,
Nice tutorial. I read it and understood how the sensor fusion work and why we do get the Gyro drift.
Do you know if it is possible, with the current phones to retrieve the linear position? If yes, can you provide me with a reference?
Also, do you have any information or link that can be helpful regarding how to retrieve position from the acceleration ? I know there are lot of error when doing the integration methods, so I am wondering if someone may have some clue about it.
Thanks again for your sharing.
Hi Ghislain,
Thank you for your feedback. I appreciate every comment.
I’m afraid I see no possibility to retrieve the phone’s linear position in an accurate manner using the accelerometer and gyro. Maybe you can combine those sensors with the GPS data in some way (with a Kalman-Filter, for example, but don’t ask me how it works in detail, I haven’t figured it out, yet). Still, I don’t believe that the positioning will be accurate, since the resolution of GPS is somwhere between two and three metres. You will also have difficulties with indoor situations.
So my answer is: I don’t think it is possible with the aforementioned sensors. Systems like the Wii-Motion, Kinect or PS3 Move rely on visual data to retrieve linear positions. Maybe it is possible to use the smartphone cameras (front and back) and run some sort of vision algorithms to get visual cues from the environment. And maybe you can conclude the device’s position from those “cue points” and the orientation data. I haven’t thought that idea out yet, but perhaps it’s worth a shot.
After testing it seems that ICS supports sensor fusion out of the box. Whoa!
Filip
Thank you Paul,
Great info.
Hi Again
For those asking about TYPE_ROTATION_VECTOR sensor, this does provide filtered results, but so far the implementations seem to vary from device to device.
My Galaxy S2 was giving really jerky results (though it seems to have been fixed in the 4.0 update recently).
This lead me to the conclusion that unless I could get hold of every device I was targeting to check the output of TYPE_ROTATION_VECTOR, then something like Pauls solution was the way to go.
Cheers
Tom
PS
Our first game using this style of sensor fusion is now available on Google Play
https://play.google.com/store/apps/details?id=com.AppToyz.AlienAttack&feature=search_result#?t=W251bGwsMSwyLDEsImNvbS5BcHBUb3l6LkFsaWVuQXR0YWNrIl0.
Pingback: enddl22s Blog » sensor fusion via complementary filter
@Braňo:
I’m so sorry I overlooked your comment for so long. I realized your request just now, while browsing though my blog’s history. I hope you will read this. If you want to publish any copy or modification of code freely published by me, just do it. You have my permission
If I ever should publish code I don’t want to be copied I will add an according notice. However, I don’t know why I should do that.
Hello, there’s something weird when the phone is pointing to exact south, the accelerometer/magnetometer option can return …, 178,179, -179, 178, … correctly, but the drift eliminated gyro and fused sensor data will have sort of “intermediate: value between them and have long transition period from negative to positive value.
Good article anyway
Hi. Thanks for your feedback.
You’re right, there seem to be some problems with transition cases as you pointed out (179° –> -179°). Maybe I’ve missed to write some logic to handle those angle transitions in the filtered output. I didn’t figure out where the problem is exactly, yet.
Hello Paul,
I fixed it by modifying:
public void calculateFusedOrientation() {
if ((gyroOrientation[0] 0.5 * Math.PI)){
fusedOrientation[0] =
(float) (FILTER_COEFFICIENT * (gyroOrientation[0] + 2 * Math.PI) +
ONE_MINUS_FILTERCOEFF * accMagOrientation[0]);
fusedOrientation[0] -= (fusedOrientation[0] > Math.PI)? 2 * Math.PI : 0;
}else if ((accMagOrientation[0] 0.5 * Math.PI)){
fusedOrientation[0] =
(float) (FILTER_COEFFICIENT * gyroOrientation[0] +
ONE_MINUS_FILTERCOEFF * (accMagOrientation[0] + 2 * Math.PI));
fusedOrientation[0] -= (fusedOrientation[0] > Math.PI)? 2 * Math.PI : 0;
}else{
fusedOrientation[0] =
FILTER_COEFFICIENT * gyroOrientation[0] +
ONE_MINUS_FILTERCOEFF * accMagOrientation[0];
}
// CONTINUE WITH fusedOrientation[1] and fusedOrientation[2]
Ah d*mn with the web syntax,
basically it’s just caused by adding positive and negative angle with different weight (FILTER_COEFFICIENT) in the calculateFusedOrientation sub-routine,
what I’ve done is to check the existence of negative angles and add 2Pi to the negative angles, perform the weighted sum, then remove the 2Pi if the weighted result is greater than Pi.
Hi Paul,
thanks for this great tutorial!
I was playing with orientation sensors and was looking for a way to speed up updates using the gyroscope (I’m low-pass filtering accelerometer & magnetometer with alpha = 0.98, very slow but almost noiseless).
First, I’ll need to learn more about the matrix operations, I’m not very good at algebra.
One thing I noticed is that you’re using Math’s double methods, and then casting back to float.
I don’t think it might be a great improvement, but you could use the FloatMath methods to perform that operations. According to the documentation, it should speed up the execution by a factor of 3.
Regards,
Renato
@J.W. Alexandar Qiu:
Thank you very much! That’s great! I will include your bugfix as soon as I get to it.
@Renato Villone:
. I’m also sure that there are many ways to speed up the whole rotation matrix calculations in general. Usually, I prefer quaternions over matrices or euler angles but in this case matrices worked better for me somehow.
Thank you for your feedback! I’m aware of the fact that the tutorial code isn’t optimal. The unnecessary double-calculations and type-casts are definitely slower than a float only solution. To be honest, I wasn’t aware of Android’s FloatMath class at the time I wrote the code
Maybe I’ll write a tutorial on rotation matrices and matrix operations. It’s really not that hard, once you understand some basic principles.
Hi Paul,
Thanks for putting up your thesis work and the related research in Orientation. Awesome work.
). And whats more this happens only for Azimuth(Z-Axis).
I was struggling with the same problem too, and wanted to accurately track two android devices(Samsung Galaxy S2 phones) with respect too each other in one of my gaming applications. I tried a lot of things including the GRAVITY vector and the ROTATION flag in the sensor manager. But with the gravity vector, the rotation around Z-direction(perpendicular to the plane of the phone) while with the ROTATION flag, the values around the Z-direction are very unstable. Your post is really helpful in understanding why this happens. But unfortunately, it seems that even with your implementation, there is a weird flicker every now and then, and I noticed it happens when any electronic device is in its vicinity(
Please let me know if this is a known issue or if I am missing out on something.
Appreciate your help.
Thanks,
Hitesh
Hi Hitesh,
The issue with the Azimuth is a known one. It is glitchy sometimes, especially when the electromagnetic field in the environment of the android device is altered (e.g. by large metal constructions like steel girders, magnets or in most cases other electronic devices as you pointed out). This is due to the magnetometer which is the main source for the device’s rotation in the horizontal plane (the one which has the z-axis as normal vector). The Accelerometer and Gyroscope are not influneced by electomagnetic interference which explains why only rotations about the z-axis are affected.
However, I tested my solution in a computer lab, surrounded by several running machines and the phone directly mounted to headphones (which contain magnets) and the results were still acceptable. I don’t know the environment in which you are working but maybe there is even stronger interference. While moving the most data is acquired from the gyroscope. Do you observe the flickers also when the device is in motion or only when it is at rests?
Also take in account that there still is a bug in my code regarding transitions between azimuth-angles of -179° –> 179°. J.W. Alexandar fixed that one some comments above, but I had no time to maintain the code, yet.
Also, at the beginning of my work I stuggled with heavy glitches of the magnetometer. I did some research and finally found some reports by several users who complained that the compass-output in google maps was complete bogus. Some other users suggested to “reset” the magnetometer by shaking the phone or by waving it around in 8-shaped gestures. Well, I was desperate. I tried it and it worked. It was weird but apparently it was a valid workaround for my problem.
Thanks for a quick response Paul. I will incorporate J.W. Alexandar’s fix. The environment where I am testing this is fairly full of electromagnetic disturbance but the behavior of Azimuth is rather weird. Sometimes I observe that for the same orientation of the phone in the XY plane which ideally should be the same Azimuth value, it shows different values in a very short span. This is usually when there is a vigorous rotation in the XY plane. On the other hand, if there is a slow rotation in the XY plane and the phone is brought to the same rotation, there is almost consistency in the Azimuth value.
Do you think upgrading to ICS would make any difference. I read in one of the posts that ICS is much better and does Sensor fusion on its own.
Can you point me to any links or any information on how to reset the magnetometer using gestures or shaking as you pointed out. Would be great help! Thanks a lot…
This was the original forum thread in which some people suggested to shake the phone to get rid of strage magnetometer behaviour:
).
http://forum.xda-developers.com/showthread.php?t=1152082
(see post #5)
So, just try to shake it a couple of times (but don’t drop it
I don’t believe that the shaking thing is a feature. I think it is simply a glitch in the magnetometer (either chip or api).
I heard of the sensor API improvents in ICS, too, but I haven’t tested them, yet. It surely is worth a look. I upgraded my own device to ICS a couple of weeks ago and just when I was including J.W. Alexandar’s fix to my code I encountered the same behaviour again. It disappeared after I shook the device. Maybe it’s something with the hardware. However, I don’t know if that’s the same problem you are having with your device, Hitesh.
Great stuff guys!
Respect!
Hi
Really great work!!
I am working on tracking the motion of of a car by means of Android phone. I am new to Android programming, I was trying to determine the position of a car by position algorithm as mentioned in
http://www.freescale.com/files/sensors/doc/app_note/AN3397.pdf
I am able to determine the position of a car but I am not sure whether the position is appropriate then I checked it by Runga Kutta method using Matlab, both the results were not matching, so I have a doubt whether how to justify my true position.
Secondly in some forums I did study that in order to determine the position I need to transform the device accelerometer readings to earth’s frame to know the position, since I am simply calculating the acceleration and subtracting the gravity component to know the linear acceleration but I am stuck as how to transform my current acceleration in to earth’s frame, please can you suggest something regarding this as well.
thanks
Shashank
Hi Shasha,
the paper you are referring to presents the standard way of how to double integrate acceleration data to get position information. Although I doubt that this approach is applicable with the sensors in today’s mobile devices. I also doubt that this approach will achieve stable position data using a low cost accelerometer like the MMA7260QT which is suggested in the paper. When dealing with accelerometers you always have noise and inaccuracies due to temeraure and sampling rate. And since you perform a double integration these errors are not neglectable. Inaccuracies in the floating point format of your values suffice to deviate your position vector by several meters within minutes or even seconds (and it will get worse over time). That’s why I did sensor fusion in the first place, to compensate accumulating noise by using a second data source with a different error behavior. And that was for rotation which is much less tricky than absolute position.
But to give you an answer on your question regarding the acceleration vector from the local coordinate system (device frame) into the global coordinate system (world frame):
You can acquire the device’s attitude either by using the SensorManager.getRotationMatrix method in the Android API (see http://developer.android.com/reference/android/hardware/SensorManager.html) and calculate the inverse rotation matrix by transposing it (swap rows and columns). Then you can apply this rotation matrix on your acceleration vector. However, SensorManager.getRotationMatrix is quite inaccurate sinde it relies on accelerometer and magnetometer data. You’ll want to do sensor fusion to get more a accurate attitude information. But still, this information has it’s deviations, too, and will inevitably falsify your position over time.
The only approach I see to get somewhat reliable position data from an android device’s sensors is to use all available data (accelerometer, gyroscope, magnetomerer and most important GPS!) and construct a Kalman Filter which takes your car’s movement characteristics into account (turning radius, maximaum acceleration, maximum valocity etc.). But Kalman Filters are things I yet know very little about. I’m very sorry that can’t help you further.
Thanks Paul
Hi Paul
I am quiet puzzled in case of displaying my output as I do every thing as your posts says but when I try to display it in the android screen my application gets crashed. And I am not able to figure it out why does it happens.
If you have some time, could I email you my code
Thanks
Shasha
Sorry for my previous query, I have figured out my problem and I am able to display it.
I will rather like to ask you about a post by J.W. Alexandar Qiu , where the transitions from -179 to 179 has been dealt, I would like to know that why has the comparison been made with -0.5* Math.PI. Sorry, if I am sounding little silly on this part but I am not able to understand if you could kindly let me know
Thanks
Shasha
I just forget to add one more query concerning the previous post itself about the concept of adding 2*Math.PI, why is it done and how is it stabilising our result.
Thanks
Shasha
Hi Paul,
I computed orientation from gyro as follows(as in ur code):
SensorManager.getRotationMatrixFromVector(deltaMatrix, deltaVector);
gyroMatrix = matrixMultiplication(gyroMatrix, deltaMatrix);
SensorManager.getOrientation(gyroMatrix, gyroOrientation);
I set the sensor delay to SENSOR_DELAY_NORMAL.But,I am not getting the values correctly.Do I need to do some improvisations?If Yes,please suggest the changes.
I am trying to compute distance from accelerometer readings. For this,first I am transforming the accelerometer readings into navigation frame by multiplying with the orientation obtained from gyro.Is this the correct process for implementation?I computed orientation from gyro as follows(as in ur code):
SensorManager.getRotationMatrixFromVector(deltaMatrix, deltaVector);
gyroMatrix = matrixMultiplication(gyroMatrix, deltaMatrix);
SensorManager.getOrientation(gyroMatrix, gyroOrientation);
matrixmultiply(avg_accel,gyroMatrix);
The results are very much deviating!!
Please reply me asap.
Thanks.
Hello guys,
first of all, sorry for the delay, I started a new job and time is getting a scarce resource for me.
@Shasha:
Glad you solved the problem in your code. Regarding the transition Problem:
I guess you were wondering about the following line(s) of code:
if (gyroOrientation[0] < -0.5 * Math.PI && accMagOrientation[0] > 0.0)
//...
I understand the term (-0.5 * Math.PI) as a tolerance. Actually, you have to check whether gyroOrientation is negative while the accMagOrientation is positive (or vice versa). But within an area of 90 degrees the algorithm still works. You could test the code with a check for
< 0.0. Theoretically that should work, too.Now, why do we add 360 to the negative orientation? Because, when one orientaion value is negative and the other one is positive, we cannot simply perform a linear interpolation with those values (which is what our complementary filter does). Consider, the orientation values are not deltas (differences) but absolute values we use for our filtering. E.g. the difference between -170 and 170 degrees is 340 degrees. But since our value domain is circular and we desire the smaller difference, the value our filter yould expect is 20 degrees. If we do not recognize this, our algorthm will rotate in the opposite direction in order to interpolate between accelerometer and gyro. That is why we transform the negative value into the positive value domain, to perform our filtering with positive values only. But that makes it very likely that our filtered orientation value gets bigger than 180. Since we only want to work with values between -180 – 180 after the filtering, we need to remove 360 degrees to be back in our desired range of values.
@Jane:
I’m sorry but I don’t understand your problem completely, since I don’t have enough information. How do you calculate avg_accel and what does it contain? If you want to calculate the absolute orientation from the gyro (and only from the gyro, no sensor fusion), that’s the proper way to do so. But firstly, take into account that your gyroMatrix will have a constant drift which will gradually falsify your orientation information. Secondly, if you want to compute distance from your acceleration data, you have chosen a troublesome path. As I mentioned in earlier posts, I do not recommend calculating position or distance values by using the accelerometer. You have to double integrate the accelerometers output, and thus also the noise it produces. This will lead to immense deviations which I don’t know how to get rid off. So you have two problems: gyro drift (solvable with the solution above) and double integration of accelerometer noise (not solvable, yet, as far as I know).
Hi Paul,
thanks a lot for this blog post, really appreciate it,
I was wondering if I can use your code for a project I am working on in school.
Thanks.
Hi Mike.
Sure, feel free to use the code as you like. I do not restrict any reuse or modification of it.
This article is very helpful specially for those who are new to sensor programming.
By the way, I and my groupmates are currently conducting a thesis with regards to indoor navigation(although you have stated lately that it is not yet possible at the moment). But we are looking forward into the possiblities of exploring new technologies that would augment your solutions.
I would be glad to include your effort as part of our thesis. I would appreciate any piece of advice that you might share. Thanks a lot!
Hi Enzo,
thank you for your positive comment.
I wouldn’t say that indoor navigation is impossible in general. My statements regarded the specific use of MEMS motion sensors in present mobile devices for position measurement. Maybe there are solutions, but I’m not aware of any. I could imagine to use other approaches for indoor positioning which partly rely on the facilities you want to navigate.
For example, I could think of a computer vision approach, which uses the device’s camera to recognize the indoor environment and maybe connect to some sort of network to look up the user’s location within a building. An indoor situation has the advantage that the lighting does not vary as much as outdoors. You could use the motion sensors to improve the output from the application. However, I believe that the image data should suffice.
I also heard the buzzwords “indoor GPS” now and than but I don’t have any specific information on this, yet. However, there seems to be quite some interest in this topic. I’m affraid that I’m not much of a help on this particular subject.
I would be glad, too, if any of my work is helpful to you
I’m very honored that so many people are interested in this article. You can of course reuse any of my code in case you want to.
Hi Paul..Thanks for the reply..
avg_accel contains the averaged accelerometer readings.
I have a query regarding the complementary filter..U mentioned,
“To avoid both, gyro drift and noisy orientation, the gyroscope output is applied only for orientation changes in short time intervals, while the magnetometer/acceletometer data is used as support information over long periods of time”
What is meant by short time intervals and long periods of time…?How is this concept applied in implementation.?
Can you please explain how did u arrive at TIME CONSTANT value and the filter coefficient value!
One more query…..
Actually,I am trying to extract gravity values from the accelerometer readings!!
The accelerometer readings are with respect to its body frame.I need to find out the exact orientation in order to transform the readings from body frame to navigating frame..and then determine gravity..
Do u have any idea about doing it?
Waiting for ur reply..
Regards,
Hima Bindu