1. For downloading SimTools plugins you need a Download Package. Get it with virtual coins that you receive for forum activity or Buy Download Package - We have a zero Spam tolerance so read our forum rules first.

    Buy Download Package Now!
  2. Do not try to cheat our system and do not post an unnecessary amount of useless posts only to earn credits here. We have a zero spam tolerance policy and this will cause a ban of your user account. Otherwise we wish you a pleasant stay here! Read the forum rules
  3. We have a few rules which you need to read and accept before posting anything here! Following these rules will keep the forum clean and your stay pleasant here. Do not following these rules will lead to permanent exclusion from this website: Read the forum rules.

Using a rotary encoder in place of a potentiometer

Discussion in 'Electronic and hardware generally' started by Alexey, Apr 26, 2017.

  1. Alexey

    Alexey Active Member

    Joined:
    Sep 23, 2014
    Messages:
    371
    Occupation:
    Electronics Technician
    Location:
    Adelaide, Australia
    Balance:
    6,762Coins
    Ratings:
    +513 / 1 / -0
    My Motion Simulator:
    3DOF, DC motor, Arduino
    Hello all!

    To make a long story short I will start with this, I am never happy with anything I build....
    I have decided to build a simulator using 4 linear actuators to provide pitch, roll and heave.
    What has this got to do with rotary encoders you may ask? Well as I have been building and testing my actuator I have come across a really annoying problem. The problem is accurately reading the position of the actuator and also mounting a potentiometer along with the gear reduction etc. I found this to be really annoying and to top it off my 10 turn pots ended up being unreliable as they would partially loose contact with the track at some points and just generally poor performance all round. Sometimes they would work and sometimes they would misbehave. So I looked into the usual things such as stupidly expensive pots, string pots but they all had this annoying part mounting them accurately and adjusting travel distances with gears etc.

    My actuator:

    [​IMG]
    [​IMG]

    The easiest answer in my mind is using a rotary encoder as they turn forever and you don't have to worry about gearing them down. That is where I remember reading a few people asking "why can't we use rotary encoders?" and the answer was always "because they take up too much headroom in the processor". So why not just give the reading of the encoder to a separate processor then?

    Lets get straight into the meat of things, here is a video of me using a rotary encoder to move the feedback line in the SMC3 utils. I will go through things in a bit more detail after the video.

    To explain the video:
    at first I have already set the range of the encoder so that 5v is at count = 1000 which can be read on the 7 segment display. As I go through the range of the encoder you will see the feedback line follow the encoder count. Then I change my range to be 5v at 2000 counts. meaning you can have whatever rotational range and set the lower and upper points manually. In even more simpler terms, you can have 10 rotations or 100 rotations and the program will automatically adjust the output to suit 0 -5v.
    To demonstrate that, I adjust the encoder to be at 644 counts and reset the upper limit, you can see the feedback line shoot straight up to max when I activate the switch (not seen in video but you can hear me click it).



    A picture of my somewhat messy setup:
    [​IMG]

    So, lets get into the setup.
    [​IMG]

    At this very early stage the setup requires a separate arduino to read the encoder and output it to the arduino that runs the SMC3 code. At the moment I am using an arduino UNO but a nano could be used in its place as they are a cheaper board. I have a nano and I will test the code when I get around to fishing it out of its hiding place.... somewhere....

    The encoder is a 100 cpr unit that I salvaged from an old antenna rotator that was being thrown out. Having a low cpr is actually quite useful for an actuator because the arduino can easily keep up with the counts + rpm and is still very accurate. There is only 255 positions in the whole range of motion, the encoder counts are mapped to 0 - 255. So having a 1000 cpr encoder is only just putting strain on the processor.

    lastly there is two limit switches which are activated on the lowest and highest part of desired travel range.
    These can be activated by hand or setup up to be activated by a pre-determined point on the actuator/lever arm.


    How the code works:

    The encoder is read via a hardware interrupt routine which makes it very fast and accurate. The two encoder channels A and B are connected to pins 2 & 3. Every time the encoder outputs a pulse the code determines the direction of the encoder and will either add to the encoder count or decrease the encoder count. The count can go negative, which is undesirable but wont really hurt anything

    To have a live update of where the encoder count is I have used an arduino multi function shield with a seven segment display. I write the encoder count directly to the display. (as seen in the video)
    I have written a part in the code so that negative numbers are not displayed because negative numbers do not display correctly on the shield. Only 0000 is displayed when the count is negative.

    Now I will get into the calibration. There are variables stored for the maximum and minimum count numbers. To set these limits there are the two limits switches. When the motion range of the actuator/lever arm is at its lowest point you can press the lower limit switch and this will ZERO the encoder count. meaning it resets the encoder count from wherever it was to 0000. This sets the lowest point that your actuator/lever arm will sit.
    Then the upper limit switch can be activated at the highest point of travel of the actuator/lever arm. It doesn't even have to be set an min/max as you can set the range to be wherever you want (but still within the mechanical limit of motion range). At this stage the encoder count will be however many pulses have been read by the arduino, there is no set count number. I used 1000 and 2000 just for ease of demonstration in my video.
    To give a real world figure, my actuator has 32 turns in the desired travel range. This will theoretically give me 3200 encoder counts over the range.

    Now to getting to the output stage.

    To turn these counts into a 0 - 5v signal we must convert the counts to a PWM value. Why PWM?
    When PWM is read with an analogue meter you will actually read a 0 - 5v ouput with a 0 - 100% PWM
    A PWM signal is sent in arduino by using the analogWrite() function. This has a range of 0 to 255 (0 - 100%).
    So how does an encoder count of 3200 squeeze into 255? We can map the value! The map function will divide out the encoder count into a desired range (0 - 255). Example: I can have a 1 to 1 ratio where the encoder is set to a max count of 255. So every encoder count is a direct change to the PWM. Or I can have a 2 to 1 ratio where I set the encoder max count to 510 where every second encoder count changes the PWM.

    The code also has a constrain function where it will enforce an output of 0 - 255. Just in case of the cheeky code doing weird things.

    Unfortunately just outputting a PWM signal is not enough to get that analogue signal to mimic a potentiometer. When connected to the arduino running SMC3 you can see that the feedback pot actually picks up the PWM pulses and thus just goes from min to max rapidly. The get the signal we want a filter must be used. It could also be called a DAC. A simple RC filter is used as shown in the diagram. BUT! this is still not enough... Along with the DAC comes noise which makes the feedback line oscillate a small amount. not a huge amount but enough to make a motion simulator vibrate rapidly. To eliminate this we have to use a software filter function called smoothing.
    The function has to be inserted into the SMC3 code itself. This function is inserted at the point where the feedback pot is read. it averages the reading of the input and then inserts it back into the feedback pot value.
    This creates what looks like a smooth response as seen in the video. It's hard to tell if there is any oscillation left in the feedback.

    This is as far as I have gotten so far. I will post the code and the amended SMC3 code so that anyone curious can test it out for themselves. unfortunately I forgot my damn USB that has the code so I will do that tomorrow.
    • Creative Creative x 3
    • Like Like x 2
    • Informative Informative x 2
    • Winner Winner x 1
    Last edited: Apr 26, 2017
  2. RandomCoder

    RandomCoder Active Member Gold Contributor

    Joined:
    Feb 19, 2017
    Messages:
    139
    Occupation:
    Control Engineer
    Location:
    UK
    Balance:
    1,103Coins
    Ratings:
    +98 / 0 / -0
    I like your style, but two things come immediately to mind...
    1. Why go to all the extra trouble of trying to mimic a potentiometer when the first UNO which is directly connected to the encoder already knows the positional value of the encoder? Why not just communicate this to the second UNO using I2C? I've not got any experience in doing this but it would seem like a more logical approach.
    2. If you power the second UNO with a battery backup then you could retain the encoder position and not need to initialise on startup.
    Just a couple of ideas which I hope will prove useful.
    • Useful Useful x 2
  3. Alexey

    Alexey Active Member

    Joined:
    Sep 23, 2014
    Messages:
    371
    Occupation:
    Electronics Technician
    Location:
    Adelaide, Australia
    Balance:
    6,762Coins
    Ratings:
    +513 / 1 / -0
    My Motion Simulator:
    3DOF, DC motor, Arduino
    I can answer your first question with ease, because I am a coding newb.
    My code consists of a mash of different arduino examples and things I have pillaged from the internet.
    Reading through I2c, I can't see why it would be advantageous for me to use it at this stage. Maybe at a later stage where I could modify the SMC3 code to use the encoder count as positional control but at this stage it is set up for reading a potentiometer.

    Come to think about it.... I could map the encoder count to a 0 - 1023 (same as the range of reading a pot with analogRead).... Which could be handled by the arduino running SMC3...

    You've got me thinking now.....

    With the battery setup I was thinking of using something like a nicad battery so that every time the power supplies are turned on they can recharge the battery. This could eliminate the need to re calibrate every time the arduino is turned on.



    As promised, attached is the arduino code.

    Line changes in the SMC3 code is as follows:

    94, 96 and 97 = setup

    smooth function is added to line 214 and ends at line 231.

    The insertion of the smoothing function happens from line 1659 and ends in line 1661.

    Attached Files:

  4. Zed

    Zed VR Simming w/Vive Gold Contributor

    Joined:
    Apr 4, 2017
    Messages:
    504
    Location:
    USA
    Balance:
    4,107Coins
    Ratings:
    +507 / 2 / -0
    My Motion Simulator:
    2DOF, DC motor, JRK
    Hey Alexey, serial comms are pretty easy on the Arduino. The data is sent in bytes but there are commands to read multiple bytes if you want more than 8 bit resolution. You might want to home brew a packet of sorts or do some kind of handshake or something to make sure everything stays aligned as the transmitter starting up before the receiver could have the receiver not interpreting high and low bytes correctly if the transmitter just sends a stream of bytes. Single byte data is trivial. It's just as soon as you get least and most significant bytes that you have to know which is which. The Arduino supports high data rates so you can get updated data across really fast if you go that way. Cool project!
  5. Alexey

    Alexey Active Member

    Joined:
    Sep 23, 2014
    Messages:
    371
    Occupation:
    Electronics Technician
    Location:
    Adelaide, Australia
    Balance:
    6,762Coins
    Ratings:
    +513 / 1 / -0
    My Motion Simulator:
    3DOF, DC motor, Arduino
    Hi Zed, I've looked into the I2c bus that is available on the arduino and it can work quite effectively, I will certainly take up advice given by @RandomCoder . One arduino can be the master and another be the slave where the master will request data and the slave will receive the request and then send the requested data. Using the I2c bus It can have multiple slaves (multiple encoders) rather than a one to one serial.

    I've just got to figure out how to pad the data being sent through to make up for bits not being used when the encoder count is low.

    Cheers
    • Like Like x 1
  6. RandomCoder

    RandomCoder Active Member Gold Contributor

    Joined:
    Feb 19, 2017
    Messages:
    139
    Occupation:
    Control Engineer
    Location:
    UK
    Balance:
    1,103Coins
    Ratings:
    +98 / 0 / -0
    This is usually overcome by sending a header byte, the master will reject all data received until it sees the header byte and then it knows to expect the data which is often followed by an end byte to signify the end of the data. Different systems do it different ways but the theory is pretty much the same. I've not had a play with Arduino's yet but I'm looking forward to the opportunity when I get my own rig started.
    • Agree Agree x 1
  7. Alexey

    Alexey Active Member

    Joined:
    Sep 23, 2014
    Messages:
    371
    Occupation:
    Electronics Technician
    Location:
    Adelaide, Australia
    Balance:
    6,762Coins
    Ratings:
    +513 / 1 / -0
    My Motion Simulator:
    3DOF, DC motor, Arduino
    Oh I forgot to mention. In the arduino code for reading the encoder, there is a library included in line 1 which I forgot to delete.
    If anyone tests this code you will have to remove line 1 and also change line 56 from "digitalReadFast2" to the normal "digitalRead".
    This has no impact on the performance of the code as I have seen yet but will throw up an error if you do not have the library installed.

    Cheers
  8. llluis

    llluis Member

    Joined:
    Mar 30, 2017
    Messages:
    32
    Location:
    Quebec
    Balance:
    134Coins
    Ratings:
    +19 / 0 / -0
    My Motion Simulator:
    6DOF
    Great idea. Did you have any progress on this? I'm thinking of trying this too for my 6DOF and eliminate the belts, etc.
    1x Teensy would read all 6 encoders and provide the output to SMC3.
  9. steamtrac

    steamtrac New Member Gold Contributor

    Joined:
    Sep 2, 2016
    Messages:
    18
    Location:
    Germany
    Balance:
    177Coins
    Ratings:
    +0 / 0 / -0
    Great approach. But one question: What is The cycle time of the Arduino? 5ms? Do you feel any lag when using the additional controller inbetween? Thanks!
  10. Alexey

    Alexey Active Member

    Joined:
    Sep 23, 2014
    Messages:
    371
    Occupation:
    Electronics Technician
    Location:
    Adelaide, Australia
    Balance:
    6,762Coins
    Ratings:
    +513 / 1 / -0
    My Motion Simulator:
    3DOF, DC motor, Arduino
    The code works as is but there is far more to do in terms of calibration automation. Whilst the code can work on any length of travel each individual case needs a custom calibration routine to run the motors in one direction and hit the end stop, and then run the other way to find the other end stop to calibrate the range of travel. That part I have not done as I have extremely limited time to do anything these days.

    The base code is there, just needs finishing. It is available for anyone to modify as they see fit.

    I wouldn't know as it has not run on a motion sim. I doubt you could ever notice 5ms of delay. Even so I do not know what the cycle time of the code is but it is very small so I could hazard a guess of maybe 30 cycles of the processor for the loop. so that would be 1/16,000,000 x 30 = 1.8 micro seconds or thereabouts.