Locked History Actions


Related News: Fundraiser

Just a short side notice for those of you who are interested in our magnetic rotary encoder:
We are currently doing a fundraiser to turn it into regular product on tindie.com. Check out https://www.tindie.com/products/LongHairedHacker/sindri-magnetic-rotary-encoder-1/ if you want one.

Accuracy testing for the magnetic rotary encoder

One of the questions that is frequently asked when showing off the rotary encoder is: How accurate is it ?

Of course the resolution of the sensor ship itself can be found in the datasheet. It is 10bit for 360°. Applying some simple math yields \( \frac{360°}{2^{10}} = 0.35 \), resulting an accuracy of \( \pm 0.175° \). But that is only a rough estimation for a bestcase scenario. In reality magnet missaligments, a sensor chip that is not perfectly even soldered to the board or simply an bad cable can introduce measurement errors.

So a typical follow-up question is: How can you test the encoder ?

And there's your problem. Measuring angles below 1° is nothing that a hobbyist can easily accomplish with his tools. One possible approach is to attach a mirror to the encoders axis and point a laser beam at it. The laser beam will the be reflected all across the room (you would need more the 5m distance for good results) to a big scale. Rotating the mirror even the tiniest bit will cause huge changes (multiple centimetres) to the position where the beam hits the scale.

The math below

Disclaimer: The next part contains some math, which may be unsuitable for liberal-arts majors, as well as many unproven assumptions, which may be unsuitable for mathematicians and oversimplifications that may be unsuitable for engineers.

A this point some basic math can save you the trouble of setting up a large scale at your basements wall. Let's look at the sensor from a mathematical point of view: $$ s(\alpha) = \alpha \cdot \frac{1024}{360} $$ It reads the angle alpha and translates it perfectly linearly into a value between 0 and 1024. So in an ideal world the value we measure, called m, is \( m = s(\alpha) \). In the real world it's rather: $$ m = s(\alpha) + e_{const} + e_{angle}(\alpha) + e_{time}(t) $$ There are 3 different types of errors involved:

  • A constant error to all measurements or an offset to put it simply
  • An error that depends on the current angle. This is the case if the sensor does not work perfectly linear.
  • An error depending on time. E.g. magnetic interferences or ripple on the supply voltage.

Of course there are other errors possible as well, depending on how model the system, but these 3 are sufficient here.

The first type of errors is simple to handle. If all the measurements have a constant offset, we subtract the offset to get the actual value. We simple change the point of origin.

The last one can be hard to minimize, but at least easy to measure. Take some measurements while keeping alpha constant. Since the AS5043 is amazingly robust sensor, it is usually safe to assume that it is approximately 0. Some measurements taken in the test setup shown later confirm this.

This leaves only the error which depends on our current angle. It is important not to think of \( e_{angle}(\alpha) \) as normal function like \( f(x) = x^2 \) but rather as a huge list with an own error value for each value of alpha. Since alpha is a continuous value this list would have to be infinite. This also means errors can be completely unsystematic, which makes filtering them out really hard. In any case it will introduce non linearity to our measurements hence reducing the precious with which the actual rotation of the axis can be computed from a measurement. Fortunately this article is about detecting these errors and estimating their impact and not about filtering them out.

So all we need to do now is take some measurements and solve \( m = s(\alpha) + 0 + e_{angle}(\alpha) + 0 \) for \(e_{angle}(\alpha) \). To do so we need some idea how \( s(\alpha) \) looks like. Also wee need a way to measure alpha with the highest possible precision. And again we need a laser and large scale at our basement wall.

But wait ... there is a better solution. Let's say we got the encoder axis rotating with constant 60rmp and we sample the encoders value every 20ms. Also assume that the rotation increments the encoder value. Then we still don't know \( s(\alpha) \), but we know something about the difference between one value and the next: $$ s(\alpha_2) - s(\alpha_1) = {360°/s} \cdot {0.02s} \cdot \frac{1024}{360°} = 20.48 $$ This of course only works when we don't hit the corner case where the value warps around from 1024 to 0. So in an ideal world you could now assume that \( m_1 - m_2 = 20.48 \). Expanding \( m_1 \) and \( m_2 \) using the definition with error values from above yields: $$ m_2 - m_1 = s(\alpha_2) + e_{angle}(\alpha_2) - s(\alpha_1) - e_{angle}(\alpha_1) $$ Now we can substitute \( s(\alpha_2) - s(\alpha_1) = 20.48 \) : $$ m_2 - m_1 = 20.48 + e_{angle}(\alpha_2) - e_{angle}(\alpha_1) $$ Seems like we've just found our measurement error.

Still we don't have the actual error for a given angle, but we can compute the error \(e_{grad} \) in the gradient between 2 measurements. If you think about it: The smaller the error in the gradient, the more linear the sensor works. Also you can estimate \( e_{angle}(\alpha_2) \) and \( e_{angle}(\alpha_1) \) from this. One possible way to do this is to assume that \( e_{angle}(\alpha_2) \) and \( e_{angle}(\alpha_1) \) have a value somewhere around: \( \pm \frac{e_{grad}}{2} \). Therefore the gradient error is a good criteria to judge the overall accuracy of the device.

In the end this leaves us with the problem of tuning our motor to run with a know speed. Which can be accomplished using some kind of encoder disc, a led, a photo-resistor and a oscilloscope.

Once again doing some math can save us the work. Imagine the error for the gradient is not totally random, but results in a Gaussian distribution for a the gradient values. This means correct value with zero errors is of course the one in the middle of the bell curve. It also means that there are as many gradients with an error of +x as there are gradients with an error of -x, for any value of x you can come up with. If you don't get the statistics stuff the important message here is: If you compute the average value all the errors cancel each other out.

So imagine we have got 1000 samples and then compute 999 gradient from them. After that we filter out all the gradient that are too big because the value wrapped around from 1024 to 0 between the two measurements. If we use the remaining list of gradients to compute an average, it will be very close the actual gradient for a single step, because the errors cancel each other out. Finally subtracting the average gradient from a gradient value yields the error of this gradient. With that in mind you don't even need to know how many rotations per minute your motor does as long as its speed is constant.

Now we can measure the accuracy of the encoder by just spinning its axis at a constant speed while sampling the encoder value. No further measurements or equipment required.

The test setup

A simple test setup has been constructed using a leftover DC motor with a integrated worm drive.


A KaBoard is used to retrieve data from the AS5043 using the SSI interface. The raw 10bit value is forwarded to a computer via USB. A python script running on the computer collects 1000 samples over about 10ms to 15ms, which can then used to compute error values and to plot different diagrams.

Since communication delays using the usb to rs232 connection made it hardly possible to sample data with a fixed time step, the python script also keeps track of time that passed between receiving two samples. An average time step between two measurements using this setup is about 10ms long. The measured time is used in further calculations to normalize the gradients. Of course this introduces a small timing error, so as a further optimization the time keeping should be moved into the microcontroller software. Looking at the data from the tests without this optimization it can assumed that it does not significantly alter the results.

All source codes used can be found in our mercurial repo.


The first thing the python script does is plotting a simple Diagram showing the rotation value (as a raw 10bit integer) over time. This is mostly for checking that the encoder and the microcontroller are set up correctly and no unexpected things happen.

The X-Axis shows the time passed in milliseconds. The Y-values are raw positions ranging from 0 to 1024. Looking at this diagram we can already see the output is very linear with only a few small errors. This also indicates that the motor is running at a constant speed. If the speed varied across several rotations the single saw teeth would look less identical. Also if the speed varied within a rotation, there would be some kind of bend in the raising part of the tooth. If there is variation of speed in this data it is not significantly bigger then the error introduced by the time measurement and the rounding errors. Zooming into one full 360° rotation confirms this assumption.

Next gradients are computed for each time step. Gradients bigger then 900 are filtered out because they are most likely caused by a wraparound from 1024 to 0. After that each gradient is divided by the time between the two samples and converted to degrees per millisecond. Then the average gradient and the average gradient error are computed and a histogram of the gradients is plotted.

The big peak in the middle of the histogram is the average gradient at 0.454133°/ms. All the other values around are distributed in a very roughly Gaussian way. Therefore most of the errors should cancel each other in the average value, as discussed earlier. This also confirms again that the motor was running at constant speed. If its speed varied in a significant way we would see a considerably broader spectrum in the histogram.

Using this average gradient the script can compute an average gradient error. In this case the average error was 0.017907°/ms. Multiplying this with this sampling interval of 10ms yields 0.17907° which is amazingly close to the estimated accuracy from the datasheet values, even though the setup introduced a lot of timing errors.

Therefore it is save to assume that the encoder operates with an accuracy of \( \pm 0.175° \) for most use cases.