Shop OBEX P1 Docs P2 Docs Learn Events
PID Motor control — Parallax Forums

PID Motor control

CRST1CRST1 Posts: 103
edited 2014-09-23 06:32 in Propeller 1
I have searched and can't seem to find anything on motor control that looks like I need. I'm looking for a PID routine that just controls the speed of a PWM controller for a DC motor. The ones I have found seem to be for robots and are based on position control. The controller I have uses 50% width for 0 speed and 100% duty for full speed forward and 0% duty for full speed reverse. The pulse width object I'm using takes 0 to 2400 for the control of the pulse width to the controller. 0 for reverse 1200 for stop and 2400 for foward. I'm looking for something to get me started on a PID formula. I don't need any position control just speed regulation. I have a simple proportional one written that works roughly but I need better control.
Can anyone give ne something that I can get started with and try to understand or a link to a simple example. I have studied the algorithms and am not getting the idea.

Thanks

Comments

  • T ChapT Chap Posts: 4,223
    edited 2014-09-22 17:55
    When you say speed regulation, what is the method you are using for feedback to determine the speed? I assume that regulation means you want precise speed control, which means you will need some method to feed-back the speed to the processor so that an algorithm can do the job of computing the error between what the real speed is versus what you want the speed to be. So in the most simplest context, the PID loop for this type of regulation takes the the intended speed and subtracts the actual speed to derive an error value. The error value is used to speed up or slow down the motor to correct the error in an effort to maintain a certain speed. How you compensate for the error is often done with some ratio of proportional, integral and possibly derivative(may not be required).
  • CRST1CRST1 Posts: 103
    edited 2014-09-22 17:59
    Sorry forgot to mention its a quad encoder 8k pulse per rev.
    I have the proportional part working but I am not grasping the ID part of the equation.
  • T ChapT Chap Posts: 4,223
    edited 2014-09-22 18:12
    I added to the first reply. 8K per rev? Have you started using any of the OBEX quad objects? What is the max RPM? You will need to find out of the object can handle the rpm * pulses. Although you don't state whether you are using an index input to calculate speed.

    Basically you want to create a PID engine that outputs a value to the motor controller. Your engine constantly watches the encoder and does it's speed calculation to determine speed. I cannot say what will happen when the object counting the pulses rolls over 4.4 billion, since I have never used the quadrature encoder object for a motor that may spin past that amount of pulses.

    You proportion is an abrupt calculation, and in many cases never is able to settle. The integral helps smooth out the correction since it is based on TIME, or the accumulation of error over time. With integral, you have a factor called integral windup, which means that your output may need to be limited with an integral maximum value. There is no real need for derivative in this case.

    Can you post your code so far?

    You tell the output to go to some speed. You then subtract what the actual speed is from what you are telling the motor to be. If the motor is exceeding the value you want, the PID loop reduces the output. If the motor is running slower than your desired speed, the PID loop increases the output to correct the error. The roll of the PID loop is to make the error become as close to zero as possible. In the real world, the goal is to make the error get close to zero but at the same time accomplish the error reduction within an amount of time (a desired speed of correction) in a smooth manner. This is where it gets tricky. The integral value accumulates by some value over time. You can change how fast it accumulates, and you can set how far it can accumulate. A slower rate of accumulation makes for a more graceful attempt at correction. A far rate of accumulation makes for a faster (potentially abrupt) correction. The balancing act is to avoid oscillation and overshooting the error, while getting the error corrected in a timely manner.
  • CRST1CRST1 Posts: 103
    edited 2014-09-22 18:50
    I havent posted any code yet so I have to see how to do it

    Ok, here's what I have so far. It's just in the starting rough stage.
    Like I said, it does work so far but its rough because its only proportional.
    CON _clkmode = xtal1 + pll16x
        _xinfreq = 5_000_000
    
      LCD_PIN       = 27
      LCD_BAUD      = 19_200
      LCD_LINES     = 4
      LCD_COLS      = 20
    
      ENCODER_PIN   = 0
    
    OBJ
      L_quad  :       "QuadDecoder"
      db    :       "FullDuplexSerial"
        
    VAR long L_set_Speed, R_set_Speed    'speeds to send to controller
        long L_cont_speed, L_Error
        long L_Speed, R_Speed            'actual speed of motor from encoder
        long L_offset, R_offset                    'actual count from encoder
        long L_offset_old, L_offset_new  'used for calculating number of counts in 100uS
        long R_offset_old, R_offset_new  'used for calculating number of counts in 100uS
        long Time_start                  'start of 100uS timer
                                                 ' example variable that will be accumulated to
    
    PUB go | x
      dira[14..16]~~
      outa[14]~
      outa[15]~~
      outa[16]~
      L_offset := 0                                          ' initialize the accumulator   You can set it to any desired value 
      L_set_Speed := 1200
      R_set_Speed := 1200
                                                            
      L_quad.start(ENCODER_PIN, @L_offset)                      ' start the encoder reader
      cognew(@entry, @L_set_Speed) 'start assembly cog
      db.start(31,30,0,115_200)
      repeat
    '' Compute speed from encoder pulses
        Time_start := cnt                'put current count into new time
        L_offset_old := L_offset       'put current encoder count into new left offset
        R_offset_old := R_offset      'put current encoder count into new right offset
        waitcnt(Time_start + 80000)       'wait for 1mS
        L_offset_new := L_offset       'put current encoder count into new left offset    
        R_offset_new := R_offset      'put current encoder count into new right offset  
        L_Speed := (L_offset_new - L_offset_old) * 8   'Left speed = tach counts/Sec
        R_Speed := (R_offset_new - R_offset_old) * 8  'Right speed = tach counts/Sec
     
    '' Get speed from controller
        L_cont_speed := 1600
    '' Compute error from speed and set speed
        L_Error := L_cont_speed - (L_speed + 1200)
    '' Compute control for output to motor
        L_set_Speed := L_set_Speed + (L_Error/6) #> 1200 <# 2400   
    
    
    
    
    
    
    
        db.str(string("Value = "))     
        db.dec(L_Speed)                 
        db.str(string("    "))
        db.dec(L_set_Speed)
        db.str(string("        "))
        db.tx(1)                        
        waitcnt(clkfreq/10 + cnt)      
    
    
          
    DAT
    ''assembly cog fetches the value in parameter for PWM percentage
            org 0
    entry   mov dira, diraval                       'set A and B PIN to output
            mov ctra, ctraval                       'establish counter A mode and APIN 
            mov ctrb, ctrbval                       'establish counter B mode and BPIN
            mov frqa, #1                            'set counter a to increment 1 each cycle
            mov frqb, #1                            'set counter b to increment 1 each cycle 
    
            mov time, cnt                            'record current time
            add time, period                         'establish next period
    
    :loop   mov  index, par                            'put the variable address into index
            rdlong valuea, index                       'get an up to date pulse a width    
            add  index, #4                             'add four to the index address
            rdlong valueb, index                       'get an up to date pulse b width    
            waitcnt time, period                    'wait until next period
            neg phsa, valuea                         'back up phsa so that it trips "value" cycles from now
            neg phsb, valueb                         'back up phsb so that it trips "value" cycles from now  
            jmp #:loop                              'loop for next cycle
            
    diraval long %00000000_00000000_00110000_00000000'APIN=output BPIN=output
    ctraval long %00100 << 26 + 12                   'NCO/PWM APIN=12
    dirbval long |< 20                               'BPIN=0
    ctrbval long %00100 << 26 + 13                   'NCO/PWM BPIN=13
    period  long 2400                                '
    time    res 1
    index   res 1
    valuea  res 1
    valueb  res 1
    fit
    

    It has some entrys for left and right motors but it's only using the left motor now.
  • evanhevanh Posts: 15,921
    edited 2014-09-23 01:57
    I'll take a stab. The general formula is: DemandOutput = (Kp * DirectError) + (Ki * IntegratedError) + (Kd * DifferentialError)

    Kp, Ki, and Kd are your basic tuning parameters. You can set them by hand initially as an educated guess then refine them in a limited real environment before letting the machine lose in the wild.

    DirectError, as you've already got, is a direct subtraction of measured speed from target speed.
    IntegratedError is just an accumulation of the direct errors. Be wary of this one, it can run away on you.
    And, surprise, DifferentialError is the subtraction of previous direct error from the latest direct error.

    Not too hard but, that said, you aren't really bound to just this one exact description. You can add filtering - generally always a good idea, each term has it's own filter. Filters are particularly effective on the differential term. You can add extra terms. You can take terms away. You can split and combine terms depending on feedback and feedforward sources. You can put bounds on terms and have auto-reset features. You can add offsets and predictive profile generation to the target speed. You can add auto-tune features that hunt for stable tuning parameters.

    Hope that brief run down helps.
  • T ChapT Chap Posts: 4,223
    edited 2014-09-23 06:10
    This is an over simplified method of creating a deadband region to kill the output if the error is within a predefined window set earlier for deadband.

    VAR   long    intergralaccumulatorvalue ,  I , imax, proportional  , deadband
    PUB Init
        intergralaccumulatorvalue := 5   'randon number used
        imax  := 10000   'random
        proportional   := 9     'random
        deadband := 20
    
    Pub PIDengine
     Repeat
        If direction  <>  prevdirection     'reset all if new direction command received
           P~
           I~
           Output~
        P :=  proportional   * error    'basic proportional value
        I :=  I +  intergralaccumulatorvalue     'accumulate while there is an error present
        If I > imax       ' long version of  I  <#= imax       
             I := imax
        repeat while target - actualspeed  =<  deadband    'set a dead band (example not for signed values)
           P~   'reset  if no error
           I~    'reset if no error
           output~    'kill output if in range of deadband, probably used only for stopped condition so more code is need to be sure 
        output := p + i    'set the output 
        prevdirection = direction   'add code to detect direction
    


    It takes a lot of tweaking to find what works in your case. The code I pulled this from is much more complicated for a specific use.
  • MicksterMickster Posts: 2,693
    edited 2014-09-23 06:32
    The following is an enhancement of Craig Weber's (incomplete) PID object:

    http://forums.parallax.com/showthread.php/105978-PID-Enhanced-is-finally-done!?highlight=Craig Weber


    Worthwhile reading:

    http://www.pmdcorp.com/downloads/Mathematics_of_Motion_Control_Profiles.pdf


    P
    .S. Never understood why he didn't stick with integers, though (PID object)
Sign In or Register to comment.