Urgent issue with rotary encoders and PID control (interrupts) - Page 1
Forum section: electronics / general

author: ultrablaze
author_rank: Contributor
timestamp: May 27, 2014, 05:35:19 pm
content: Hello,We are a group of engineering students very close to a deadline on a robotics projects (roughly 32 hours...)We are stuck because of what seems to be a timing issue that is causing us to miss encoder ticks when we try to move the robot at any decent speed. We are using 2048 PPR rotary encoders handled via interrupts, and want to use the data to update a PID control system for the motors.We designed to the system on Matlab to handle via an error on position and speed, for now we would be happy enough if just the position part worked properly. We are using an arduino mega 2560 for our robot.Originally we had coded the interrupt functions so it got ticks on both A and B of each encoder using 2 digitalReadFast2 for each.We called the automatic control functions through an isrt running through flexiTimer at different intervals (we had hoped for every 50ms but even 200ms would be fine).We would miss ticks constantly, and would cover about 33cm for the robot to have thought it covered 30. We think this is because somewhere we are missing ticks, possibly due to interference from the flexitimer interrupt...?So a few questions :1) any idea on our problem?2) is flexitimer interrupt going to interfere with the encoder interrupts?If we try and call the function every 50ms with milis() that's even worse right since it disables them reactionaries interrupts whenever called? Anyway to do something with interrupt priority to be sure not to miss ticks?3) is there a difference between digitalReadFast and digitalReadFast2?We are trying to modify the interrupt functions for the encoders based on this article :http://www.hessmer.org/blog/2011/01/30/quadrature-encoder-too-fast-for-arduino/We hope this will maybe make the interrupt routines for the encoders faster and let us miss less ticks. Currently on no access to internet on my phone but I'll get my code up ASAP.Thank you for Any help, we are totally stuck! We aren't that familiar with real time operating constraints yet.

author: Andy Watson
author_rank: Super Contributor
timestamp: May 27, 2014, 06:06:32 pm
content: Quote from: ultrablaze on May 27, 2014, 05:35:19 pmHow fast will the rotary encoder be turning?The article you linked-to suggests that you can achieve a 50 fold increase in speed by coding in assembler. I would consider (depending on the max speed of the encoder), hand-coding an interrupt to run at least twice as fast the maximum encoder rate. Use the same interrupt to provide a timer function to kick your control loop (you'll be totally lost if your control loop does not operate at a regular time slice). Speed-up your control loop by reducing all maths to simple addition and subtraction, where possible, replace all other maths functions by look-up tables.

author: dannyf
author_rank: Super Contributor
timestamp: May 27, 2014, 06:50:00 pm
content: I have gotten to 100K pulses per second on 1Mhz AVR so at 16Mhz, you could do 1.6M pulses per second.As to your issues:1) I don't know how you concluded that you are missing pulses. With the code and slow non-interrupt serial transmission, the right clicks and left clicks could diverse;2) The code is not very robust and is susceptible to bounces if you are using a mechanical encoder. I typically use a state machine based approach here.3) You may check to see how fast / slow the isrs are, particularly digitalFastRead().4) Do you really need to keep left / right clicks? Do you really need to keep them in "long" types?

author: rob77
author_rank: Super Contributor
timestamp: May 27, 2014, 07:16:09 pm
content: Quote from: dannyf on May 27, 2014, 06:50:00 pmi assume the 100k interrupts per second you achieved with minimalistic hand written interrupt handler routine in assembly. or am i wrong ?

author: dannyf
author_rank: Super Contributor
timestamp: May 27, 2014, 07:47:26 pm
content: coded in C.Minimalist, only because it was a demo piece. Since it runs off interrupts, it doesn't matter.

author: Dago
author_rank: Frequent Contributor
timestamp: May 28, 2014, 05:21:30 am
content: Well my firs piece of advice is to use a microcontroller with a timer that supports quadrature inputs (ATxmegas, most ARMs etc.), then the CPU doesn't have to do a thing to keep track of them (except handle overflow)But at T-32 hours I guess this advice comes in too late.Basically throw out all the arduino crap (it's slow as hell) and redo it with regular C stuff. Maybe write the interrupt handlers with ASM to make them as fast as possible. Do as little as possible in the interrupt handlers.

author: SL4P
author_rank: Super Contributor
timestamp: May 28, 2014, 01:50:45 pm
content: Agree here +1Remove digitalInputRead(), and replace with direct pin/port reads.Remember to keep your interrupt code to the smallest possible -just incrementing/decrementing ticks. Do other stuff outside - in main()

author: nctnico
author_rank: Super Contributor
timestamp: May 28, 2014, 02:27:49 pm
content: I'd just use a fixed interval timer interrupt and sample the encoder outputs. The problem with using interrupts is that they will also respond to 'switch bouncing'. You'll need to do some filtering on the pins in software and a fixed interval timer interrupt will do that. A while back there was a thread about rotary encoders on this forum which contained useful sample code.

author: ecat
author_rank: Frequent Contributor
timestamp: May 28, 2014, 04:23:03 pm
content: I read this post this morning and thought about answering but the urgent cry for help accompanied by a couple of assumptions, a link to some random guy's dodgy code and absolutely zero information just made me mad.Okay. I'm glad that's off my chest.If switch bounce in the encoders were a problem I'd expect the robot to travel a much shorter distance than desired. So, switch bounce is unlikely to be an issue.We do not know if the encoders are mechanical, magnetic, optical or something exotic. At 2048PPR I very much doubt they are mechanical - though I would love to see the link that proves me wrong on this - so, switch bounce is unlikely to be an issue.If the encoder is attached to the drive wheel and said wheel rotates at, say, one revolution per second then you only have 2048 pulses to count each second, for a resolution of 360/2048 = 0.18 degrees of rotation, a pulse rate I would hope to be within the capability of the Arduino interrupts + FastRead or PortRead. If the encoder is attached directly to the motor and you have, say, a 100:1 gear ratio that makes for a resolution of 0.0018 degrees (!) and a massive 204800 pulses per second for my one revolution per second example. If the latter is what you are striving for then you should perhaps consider an fpga, or a psychiatrist, note these two options are not mutually exclusive - that was a joke.What resolution are you trying to achieve? How many pulses per second are you expecting? Code? Data sheets?This brings us to the the code in the link should you use it.Using type long on an 8 bit processor is not the best of ideas. Using type signed or unsigned long in an interrupt routine verges on the double-plus-ungood. Make the data type used in the interrupt routine sufficient to handle the pulses you are expecting between loops around your main function. In the main function you can totalise to a wider data type - even a pair of signed/unsigned longs if necessary.When accessing the variables used in your interrupt routine you should (almost) always do so with interrupts disabled. This is especially true if two or more values should be kept in synchronisation or if the data types involved require more than a single instruction to copy, such as long ints on an 8 bit micro.http://arduino.cc/en/Reference/noInterruptsCode:noInterrupts();// copy the values from your interrupt count variables to local variablesinterrupts();// do interesting stuff

author: dannyf
author_rank: Super Contributor
timestamp: May 28, 2014, 04:25:53 pm
content: QuoteIf you use the state machine approach, it is incredibly resistant to bounces.