Accelerometer Output to Angular Position

Nick M.
7 min readApr 7, 2021

Introduction

Many devices have integrated accelerometers among many other sensors. This article is intended as a practical guide to translating accelerometer output into angular position or angular position change (delta) and serve as a basis for more complicated articles. Reading the Pitfalls section is important because this attempts to be a shortcut to direct application.

There are some assumptions for applying the methods here:

  • Access to accelerometer output, this is not a guide about a particular hardware environment as there are far to many variations in platforms to cover here. You need to have output before starting.
  • Basic coding proficiency, basic coding is not covered here.
  • This is for readers interested more in how to do this, there are many explanations that get deep in to the mechanics of the math used here.
  • This covers only 2d rotations or rotations about single axis. Perhaps another article will cover 3d rotations.

I have an accelerometer that outputs to a web server that makes the data available via GET request or UDP push. The exact response format or process is not important as long as you get your output into a form you can work with. My data comes packaged in a vector like this: (x,y,z). Your device probably does something similar after some messing with it.

Many accelerometers or data processing libraries offer a configuration to filter out the constant acceleration of gravity. It is important not to filter out gravity for the method applied here. In Image 1 (I1) the initial orientation is shown with positive y for the device being perpendicular to the earths surface. The vector out for my device is (0, -32, 0) here the Z axis is parallel to the Earths surface and coming out of the block in I1 straight toward you. The Z axis is also the axis about which all rotation will happen here. The -32 in the in the y direction is the effect of gravity. If this is raw output the value is unitless and the accelerometer data sheet would need to be consulted to relate it real world values i.e. 32 = 1g or similar. We can ignore this safely because we only need the maximum value along an axis to calibrate.

I know by experimentation that no matter how I rotate the accelerometer that 32 or -32 is the greatest magnitude I will get on one axis and the other values will be 0, and at that position my accelerometer is perpendicular to a ray starting at the center of the earth going through the accelerometer, also know as flat. This can be important if I need to make sure my device is rotating about one axis. If the X and Y vector significantly deviates from 32 then the device is accelerating or not aligned with the Z axis normal to gravity. If the Z axis vector is non-zero, this is also an indicator that I cannot trust my results. Its worth mentioning that you can do more than calculate rotation about a single axis, its just out of scope for this article.

Image #1 Flat device orientation

About angular rotations

Positive rotation is counter clockwise movement and negative rotation is clockwise. Image 2 shows the unit circle which has positive angles listed from 0° to 360° and form 0 to 2π which is the same but in radians (rad). This is the most common orientation scheme. If you normalize a 2d vector you get from your accelerometer it will fall some where on the unit circle.

Image 2 Unit Circle

If the I2 unit circle is unfamiliar or intimidating, I recommend reading more about it as understanding the unit circle is critical for deeper understanding of Trigonometry and other positional calculations.If I rotate my device 45° (that is positive so counter clockwise)I will get an accelerometer reading of (-22.6,-22.6,0) and from here on out I will drop the Z value. That makes sense, before it was (0, -32) now I rotated it to represent what you see in Image 3 essentially changing the accelerometer to (-sqrt(2)/2) * gravity for each axis. If you check the unit circle in quadrant 3 you can see the connection.

Image 3 Device rotated approx 45°

How to Do It

We have our accelerometer, we know what the output looks like. We know what our rotations should look like (values in degrees or radians), now lets apply it practically.

Enter the atan2 function. This function will take a Vector2, (x, y) and give an angle in radians from π to -π. Conveniently, most math libraries implement an atan2 function.

Note: atan vs atan2 function matters. atan gives angular values without consideration for what quadrant of the unit circle the value exists in. atan2 has extra builtin logic to give us the correct angle based on what quadrant of the unit circle the ray created by gravity passes through.

Looking at I1 again the acceleration vector is (0,-32,0). atan2(0, -32) = -1.57 radians. In this case I want degrees so I convert => -1.57 * (180°/π) = -90° also the same as 270° if you are looking at Image 2. Gravity is straight down from this rotation so, that seems like a good result. Keep in mind what this gives us is the vector of the pull of gravity relative to the accelerometer not the rotation of our device.

Table 1 Various accelerometer outputs and corresponding angles

Table 1 shows a number of accelerometer outputs and the angles calculated. It may be necessary for your application to apply an offset angle to get usable data. If you want to calculate the angle as 0° at the position shown in Image 1 instead of 90° then the offset angle is the initial rotation. In the case of I1 subtract 90° from all of my calculations. Note: at positions 0° to 89.999° this can result in a negative angle. If I don’t want to represent it this way, I add 360° to that negative calculated angle to get the same representation as a positive angle.

Here is a basic python example that calculates the angle of gravity in both radians and degrees, and prints them both followed by an empty line:

import mathimport collectionsvector = collections.namedtuple('vector', ['x', 'y'])values = [vector(0,32), vector(20.62, 20.63), vector(27.71, 16)]for vec in values:    angle = math.atan2(vec.x, vec.y)    print(angle)    print(math.degrees(angle))    print()

One advanced step can be to mitigate noise. The more interested in the correctness of the angle you are the more strategies you must employ to obtain good results. Consider this situation, you have a phone with an accelerometer in it and you are monitoring the angle. The cars engine is running and that vibration is giving you some instability. I identify that my app only needs the angle updated perhaps once per second. If you can sample 100 times in that one second and calculate the simple arithmetic mean or average from those vector samples before calculating the angle, you can cut your noise error by about a factor of 10. That can lead to some delay in settling on a new angle. You could also add a limit where if the vector changes quickly and largely you reset your average so that the new angular position is more quickly adopted. There is no limit to the employable strategies but, avoid layers and layers of complexity.

Pitfalls and General Advice:

  • If you are moving around with constantly changing acceleration it can be difficult to use this method and get consistent results without employing additional strategies.
  • Again, using atan instead of atan2 is a different process.
  • If your accelerometer is not mounted perfectly or its laying on a table in a building, it may look flat but your data may say otherwise, you may find that you need to account for previously unnoticed variation in the everyday world.
  • Pay particular to your application logic at the transition from 360° to 0°. This boundary can cause issues.
  • If you are not rotating about 1 axis you may get unexpected results.
  • Know your application. If you identify some physical constraints, applying those constraints can simplify making robust solutions that provide reasonable results even in the worst circumstances.
  • If you are working in a power constrained system, protect your user. Do not sample at 1000 Hz if 10Hz will suffice.
  • Temperature can effect accelerometer accuracy.
  • Using offset angles can lead to reporting values that are non-intuitive for your user.
  • If you get to a huge amount of complexity in your filtering and accuracy strategies, consider that you may be solving your problem with the wrong base solution. I.E. is there a library that can help, or another sensor all together? Or some application specific constraints available? The more complex the more likely you will get unexpected results.
  • There is no limit to how you can do something like this. If you don’t like my way you can probably find another path or make your own. Same as most things in life :)

--

--

Nick M.

Nick uses [Buzzwords] but, in its confusion it hurt itself. Its not very effective.