Search code examples
cembeddedavrmotordriverpid-controller

PID feedback & Position Controller with DC motor/encoder


I'm having a hard time getting both PID feedback and positioning to run at the same time.

My thought to calculate the RPM is to:

  1. start a timer and count encoder pulses using an interrupt.
  2. use some simple math to convert to RPM.
  3. reset variables used and start over.

I can calculate RPM but then I can only call my PID controller after the calculation (however long I want to wait to get good resolution.) This results in very messy code. Is there a simpler method or something I'm missing?

Info about my application: I am programming to an Atmel ATmega328P with a DC motor/dual magnetic encoder with ~600 pulses per revolution (after gearhead). I want to call GoToTarget(#) and have the motor go to the position while updating the PID parameters. In addition,it must go both ways.


Solution

  • Your speed calculation timer and your PID loop timer should be the same thing - not separate.

    Do not waste time and resolution converting pulse count to speed in RPM; if the timer is accurately periodic - and it needs to be for a stable PID - then the pulse count is directly proportional to speed, and the PID does not care about the units - that can be adjusted for by the value of the coefficients. If you want your set-point speed to be in real-world units, convert that to the equivalent number of pulses-per-PID-period instead of the other way around.

    Your loop should look like this ;

    for(;;)
    {
        WaitTimer() ;
        pulses = pulse_count - previous_pulse_count ;
        previous_pulse_count = pulse_count ;
        control = pid( pulses ) ;
        motor( control ) ;
    }
    

    You then need only an interrupt to count pulses - it need not perform any calculation, just increment a counter - and perhaps an interrupt for the timer, depending on the implementation of WaitTimer() and whether this is multi-threaded.

    Note also that if the shared variable pulse_count is not atomic, you will need to disable interrupts while it is read (best wrapped in a function), and it will need to be declared volatile.