Introduction: Rotary Encoder: 3D Printed

In this instructable I will walk you through creating a custom rotary encoder for use with arduino projects. The two main parts of the encoder are 3D printed and I have included the files for them here, although chances are you will want to design your own to fit your specific needs. If you do not have access to a 3D printer, these parts can be fashioned from heavy cardboard or plastic. But first we will look at the theory behind rotary encoders, and i strongly encourage you to read through it if you are not familiar with optical encoders.

A heads up: please read the note on the last step of the instructable. There are much more accurate encoders available online, fairly cheap. This instructable is intended primarily as a learning experience as to how optical rotary encoders function, however these encoders absolutely do work for slower, low precision robotics.


If you like this instructable, consider voting for it in the 3D printing or Arduino challenges :)

What is a rotary encoder?

Simply put a rotary encoder is a way of tracking how many degrees a shaft has spun. The type of encoder we will be making here is known as an optical encoder. This means that it uses directed light passing through slits that rotate with the shaft to count steps, or degrees. The most simple form of this type of encoder is what I like to call and absolute encoder; If you look at the disc in my picture, an absolute encoder would have only one ring of slits. A laser or other light source is directed through one side, and a photo resistor is placed on the other. Photo resistors work by varying their resistance based on the amount of light directed on them, thus varying the voltage measured across it. So as the disc spins, the light is blocked and then allowed to pass through the slit repeatedly. By using an arduino to measure the voltage across the photo resistor we are able to tell the state of the disc, which we can quantify as a 1 or a 0. In the disc I have provided here, the slots are spaced every 5 degrees, resulting in 18 slits, or 36 states per full rotation. By counting how many changes in state we have we can tell how many degrees the shaft has turned. This is very useful for robotics applications when you would like to track how far your robots wheels have turned, which can then be calculated into a distance traveled linearly. However, there is a problem with this absolute style of encoder, specifically that it can only tell how far the shaft has spun overall. This means if your robot moves in reverse, it would still be counted as a forward move. This is where the second set of slots and optics comes into play. Now with the second set, we have two signals at any given time, and because the slots are offset by 2.5 degrees (half of the primary separation) we can get four unique states instead of only two. The states we will see (in order) are 00,10,11,01. Now by recording previous state values we can determine which order we see the states in, and therefore what direction the shaft is moving.

For Example: If we observe this set of states (in order) 11,01,00,10,11,01 we can determine the shaft is rotating in the positive direction. But if we observe 01,11,10,00,01,11 then we can conclude that the shaft is moving in the negative direction.

Now that we know a little more about the theory of how this encoder functions, we can move on to constructing one and coding the arduino to read it.

Step 1: Parts and Considerations

Materials required:

  • Arduino
  • Disc and sensor mount (see stl files on first step)
  • Photo resistors or photo diodes (this instructable will assume use of photo resistor, as this is what I have had the most luck using. Although a photo diode can be used with some slight modification to the code)
  • Resistors (experimentation using different resistors is best)
  • Laser diode or high intensity LED (I use the laser diodes from cheap cat toys)

Considerations:

While constructing the encoder there are a couple considerations that need to be made. The first, and most important, is the accuracy. The more slits you have, the more accurate your encoder will be. Since in my file I used a slit of 5 degrees, that means that at any given reading I have an accuracy of +/- 2.5 degrees (because the two sets of slots are offset by this). So, reading this you may think to yourself, "Self, I'm going to make so many slots that my encoder will be super accurate". The problem with doing this is that as the slots get smaller and smaller, it becomes harder and harder to differentiate between states. Furthermore, if the shaft is spinning faster than your arduino can sample the photo resistors, you will end up missing states, which results in lose of accuracy. Another consideration to make is the diameter of the disc you will use. A larger diameter means that it is easier to differentiate between states, which means that you could utilize a higher slot count. However, like in the last example, we have to consider the speed at which the disc will turn. A larger diameter disc means that the disc will spin faster than a smaller one, and if it spins more quickly than the arduino can sample, you will again lose states. And one final consideration is the use of multiple encoders on the same arduino. For most robotics applications, one would use at least two encoders, one for the left wheel and another for the right. This is useful when trying to use a PID controller to keep a robot moving in a straight line. However, we have seen that the arduino sample rate is the primary limiting factor in the max speed of the encoder; now if we add a second encoder to the same arduino to sample, we have effectively halved that maximum speed as it now has to sample both encoders. For such an application I have used a dedicated ardunio for each encoder that feeds information back to a master arduino or pi using I2C or serial communication.

Now on to the build...

Step 2: Building the Encoder

If you have read the previous steps then you should have a pretty solid idea of how this should be setup, but I will go over the basic setup for you. If you are familiar with basic electronics, then this should be a walk in the park.

I apologize in advance, as I do not currently have one of these encoders set up, I do not have any pictures, but i will try to be as descriptive as possible.

The Hardware:

After printing or creating the disc and mounts, insert a laser diode into both holes on one side of the mount, facing inward toward the disc. If you use the mount I have provided the files for, you may have trouble with your diode not fitting correctly. If this is the case, I find a bit of hot glue can easily remedy this :) Next, insert the photo diodes or photo resistors on the opposite side of the mount with the sensor facing toward the laser. Before mounting it to the bot or aligning it with the disc, it is smart to hook it up to the arduino to ensure the laser and sensors are properly aligned and to get a base reading of the sensor. For information on how to do this, refer to the electronics setup on the next step. Once you are sure of proper alignment, you can mount the disc on the shaft. if you are using my files, I have included 6 mm holes in the collar to accommodate a set screw. You may want to flatten both sides of the shaft or drill holes so the set screws have a good area to grab to prevent slippage. Once the disc is mounted you will want to find a way to secure the laser and sensor mount. While doing so, be sure that the lasers are centered on the disc's slots and is perpendicular to the discs tangent (in other words, make sure it is straight). You can then move onto finalizing the electrical side and programming the arduino.

The electrical:

The lasers can be hooked in parallel to the 3.3v or 5v output on the arduino. I did not use a resistor, although you may want to. One leg of the photo resistor will then be connected to the +5v pin on the arduino and the other pin will go to an analog port. Then connect a resistor (start with around 10k) between the analog pin and the GND. When reading the analog pin you will observe a value between 0-255, where 0 is no light and 255 is full light. (note: you may not see 255 with direct light, this is why I mentioned calibrating it before inserting the disc to get a baseline of "full light". If you do not see a significant enough difference between the no light and full light readings, you can swap out the resistor with a higher value to increase the sensitivity. Once this is all set up (be sure to check connections twice!) you can move onto programming the arduino.

Step 3: Coding the Encoder

In the arduino IDE paste the following code, or if you prefer, use this as a guide to write your own. Please note, you will have to change the threshold values to coincide with your values. I like to get a reading in full light and in no light and then set the threshold at as close to the halfway point as possible.

Note: This is a very brute force way of doing this (a lot of if/else statements) however, I think it does a good job of illustrating the concept of the state changes. Also note that this code does not handle skipped states. For example, if the previous state is 00 and the current state is 11, it will not change the position as it is not sure weather it went forward and skipped 1 state or went backward and skipped 2.

Update: It seems that the formatting of my code does not like to stick on this form. I will leave the code below, but be warned it does not have proper formatting and the #include String library does not show. Please download the .ino I uploaded here. Sorry for the inconvenience.

----Start Code----

#include

const int s1 = 0; //sensor 1 analog pin

const int s2 = 1; //sensor 2 analog pin

String prev = "00"; //buffer to store prev reading

String reading = "";

long position = 0L;

thresh = 121;

void setup()

{

Serial.begin(9600);

int val1 = analogRead(s1);

if (val1>thresh)

val1 = 1;

else

val1= 0;

int val2 = analogRead(s2);

if (val2>thresh)

val2= 1;

else

val2= 0;

prev = String(val1)+String(val2);

}

void loop()

{

int val1 = analogRead(s1);

int val2 = analogRead(s2);

if (val1>thresh)

val1 = 1;

else

val1=0;

if (val2>thresh)

val2 = 1;

else

val2=0;

reading = String(val1)+String(val2);

if (reading != prev){

if (reading == "00"){

if (prev == "01")

position++;

if (prev == "10")

position--;

}else if (reading == "10"){

if (prev == "00")

position++;

if (prev == "11")

position--;

}else if (reading == "11"){

if (prev == "10")

position++;

if (prev == "01")

position--;

}else if (reading == "01"){

if (prev == "11")

position++;

if (prev == "00")

position--;

}

Serial.println(position);

}

prev = reading;

}

----End Code----

Step 4: Finished!

Congratulations, if all went well you should have a functioning optical rotary encoder!

Note:

This encoder build is not the most effective way to achieve accurate motion encoding. I say this because on sites like amazon, it is possible to buy an encoder (~$13) that is much more accurate (600 steps per full revolution). In addition the arduino code to interact with those encoders is much more robust than the code presented here. This was intended to be primarily a learning experience as to how optical rotary encoders work, while getting some hands on experience. That being said, these encoders do work fine for large, slower moving robots that do not need a huge amount of accuracy. In fact, I first built and used these encoders for a sample retrieval robot project at college. Our robot was around 5 feet long and 4 feet wide and had a max speed of a walking pace, for this application these encoders worked wonderfully as it was plenty accurate at that scale and speed. However, we did end up swapping these for the encoders from amazon before the competition to eliminate some trouble we had when performing under varying light circumstances as the threshold value had to be calibrated each time (we were running the robot outside).

I think building these encoders is a great learning experience and would be particularly useful as a demonstration tool in a robotics course. I hope you enjoyed this project and learned something along the way.

Design Now: 3D Design Contest 2016

Participated in the
Design Now: 3D Design Contest 2016