incPID Routine - Help Needed


Results 1 to 40 of 64

Threaded View

  1. #1
    Join Date
    Jan 2011
    Location
    Sydney, Australia
    Posts
    172

    Default incPID Routine - Help Needed

    Hi All,

    I am endeavouring to put together a closed loop motor speed controller using Henrick's "incPID" routine. The end use for this project is to re-power my 9x20 lathe using a treadmill motor (180Vdc 2HP). For my trial project I am using a 24Vdc 500W electric scooter motor. My ultimate goal will be to maintain constant motor speed under varying mechanical load conditions.

    I have read everything I could find on this forum related to PIC control and I believe what I am trying to achieve should be "do-able."

    I have successfully put together a program based on the 16F684. I have managed to write code that will read a speed pot setting via ADCIN (10 bit) on AN0 (Pin 13) and successfully drive the 24V motor via HPWM output on CCP1/P1A (Pin 5). The PWM signal is fed to the motor via a low-side MOSFET with a suitable transistor drive circuit. Having achieved this, I then went to the next step and tried to implement the PID routine. My code is as follows:

    Code:
    ' PicBasic Pro program to test incPIDv1.5 routine by
    ' Henrick Olsson using PIC16F684
    ' Name: incPID 16F684.pbp
    ' Date: 2nd June, 2012
    ' Author: Barry Phillips
    ' Connect analog input 1 to channel-0 (RA0) Pin 13
    ' Connect analog input 2 to channel-1 (RA1) Pin 12
    
    CMCON0 = %00000111  ' Disable comparators
    ANSEL = %00000011  ' Set AN0 and AN1 as analogue input, all other digital inputs
    ' Define ADCIN parameters
    Define ADC_BITS 10 ' Set number of bits in result
    Define ADC_CLOCK 3 ' Set clock source (3=rc)
    Define ADC_SAMPLEUS 50 ' Set sampling time in uS
    
    '***************** PID-Filter "external" variables *****************************
    '***** These are the variables that the main application uses. *****************
    '*******************************************************************************
    pid_Status var byte              'Status of the PID-filter can be read here.
    pid_Status_Out_Sat var pid_Status.7 'Flag for saturated output
    pid_Status_I_Sat var pid_status.6   'Flag for saturated Integral drive.
    pid_Kp Var Word                  'Proportional gain.                                     
    pid_Ki Var Word                  'Integral gain.
    pid_Kd Var Word                  'Derivative gain.
    pid_Vel_Kff VAR WORD                'Velocity feedforward gain, set to 0 if not used!
    pid_Acc_Kff VAR WORD                'Acceleration feeforward gain, set to 0 if not used!
    pid_Error Var Word               'PID Filter error input. (setpoint - actual)
    pid_Velocity_cmd VAR WORD           'Input of commanded velocity, set by the main application.
    pid_Out Var Word                 'PID Filter output (P + I + D + Vff + Aff)
    pid_Ti var BYTE                  'Time constant for the integral time
    pid_I_Clamp var word             'Clamp for the integrator windup.
    pid_Out_Clamp var word           'Clamp the final output to value in this var.
    '*******************************************************************************
    '******************* PID-Filter "internal" variables ***************************
    '*******************************************************************************
    pid_P Var Word                   'Proportional drive.
    pid_I VAR Word                   'Integral drive.
    pid_D Var Word                   'Derivative drive.
    pid_Vel_FF VAR WORD                 'Velocity feedforward drive.
    pid_Acc_FF VAR WORD                 'Acceleration feedforward drive.
    pid_LastVelocity_cmd VAR WORD       'Previous commanded velocity, automatically set by the PID routine.
    pid_Sign Var BIT                    'Keeps track of the sign of various values
    pid_LastError var word           'Last error, used for calculating D drive
    pid_Ei Var Word                  'Integral drive accumulator.
    pid_EiT VAR Word                    'Temporary calculation variable
    pid_IntCount Var Byte            'Counter for the integrator, matches against Ti
    '*******************************************************************************
    '************************** Initialise variables *******************************
    '*******************************************************************************
    pid_P = 0                            'Reset P, I, D & feedforward variables.
    pid_I = 0
    pid_D = 0
    pid_Velocity_cmd = 0
    pid_LastVelocity_cmd = 0
    pid_Kp = $0300                     'Set Kp to 3.0 (was $0700)
    pid_Ki = $0000                     'Set Ki to 0.0 (was $0080)
    pid_Kd = $0000                     'Set Kd to 0.0 (was $0225)
    pid_Ti = 8                         'Update I-term every 8th call to PID
    pid_I_Clamp = 100                  'Clamp I-term to max ±100
    pid_Out_Clamp = 511                'Clamp the final output to ±511
    pid_Vel_Kff = 0                    'Feedforward terms not used...
    pid_Acc_Kff = 0                    '... set feedforward parameters to 0
    ADValue VAR Word                    'Output from F-V (LM2907)
    Setpoint    Var Word                'Output from Speed Set Pot
    Direction   VAR PORTA.5             'Direction bit set to POTA.5 (Pin 2)
    
    '*******************************************************************************
    '*********************** ROUTINE STARTS HERE ***********************************
    '*******************************************************************************
    Start:
     ADCIN 0, Setpoint          'Read channel 0 to Setpoint
        ADCIN 1, ADValue                'Read channel 1 to ADValue
       
        pid_Error = Setpoint - ADValue  'Calculate the error
        
    '*******************************************************************************
    '********************* GOTO HENRICK'S PID ROUTINE ******************************
    '*******************************************************************************
        Gosub PID                          'Result returned in pid_Drive
    '*******************************************************************************
    '********************TEST FOR VALID DRIVE CONDITIONS ***************************
    '*******************************************************************************
        IF pid_Out.15 THEN              'If negative drive is called for...
            pid_out=0                   '...set output to 0
            pid_Ei=0                    'And keep I-term from accumulating
        ELSE                            'and if positive drive is called for...    
        pid_Out = ABS pid_Out           '...set it to absolute value of pid_Out
        ENDIF
    '*******************************************************************************
    '******************** SEND HPWM SIGNAL TO Channel 1 (Pin 5) ********************
    '*******************************************************************************
        HPWM 1, pid_Out, 10000          'Set PWM output
        Pause 10                        'Wait....
        Goto Start                      '...and do it again.
    
    PID:                                 'This is the entrypoint from the main app.
    '*******************************************************************************
    '****************************  P I D  F I L T E R  *****************************
    '*******************************************************************************
    'Calculate velocity feedforward
    pid_Vel_ff = (ABS pid_Velocity_cmd) */ pid_Vel_Kff      'Multiply absolute value of commanded velocity by the gain
    if pid_Velocity_cmd.15 then pid_Vel_FF = -pid_Vel_FF    'If commanded velocity is negative so is the feedforward
    
    'Calculate acceleration feedforward
    pid_Acc_FF = pid_Velocity_cmd - pid_LastVelocity_Cmd    'Current commanded speed - previous commanded speed is the acc.
    If pid_Acc_ff.15 then                                   'If the result is negative...
      pid_Acc_FF = (ABS pid_acc_FF) */ pid_Acc_Kff          'we take the ABSolute value and multiply by the gain
      pid_Acc_FF = -pid_Acc_FF                              'Then we restore the sign
    ELSE                                                    'Result is positive
     pid_Acc_ff = pid_Acc_FF */ pid_Acc_Kff                 'Multiply result by the gain
    ENDIF
    pid_LastVelocity_Cmd = pid_Velocity_Cmd                 'And remember current commanded velocity for next loop.
    
    'Calculate the proportional drive
    pid_P = (ABS pid_Error) */ pid_Kp                    'Multiply by the P-gain
    If pid_Error.15 then pid_P = -pid_P                     'Re-apply sign if pid_Error is neg
    
    'Calculate the Integral drive
    pid_Ei = pid_Ei + pid_Error                             'Add error to acumulator.
    pid_IntCount = pid_IntCount + 1                         'Increment the reset-time counter.
    If pid_IntCount >= pid_Ti then                          'Is it time to update the I-term?
        pid_EiT = pid_Ei                                    'Copy accumulator
        pid_Sign = pid_EiT.15                               'Save Sign
        pid_EiT = ABS pid_EiT                               'Work with positive numbers
        pid_EiT = pid_EiT */ pid_Ki                         'Multiply by Ki gain
        pid_EiT = pid_EiT / pid_Ti                          'Divide by the reset time
        If pid_Sign then pid_EiT = -pid_EiT                 'Re-apply sign
        pid_I = pid_I + pid_EiT                             'Update I drive
        pid_Sign = pid_I.15                                 'Save Sign
        pid_I = ABS pid_I                                   'Work with positive numbers
        if pid_I >= pid_I_Clamp then                        'I-term is saturated.... 
            pid_Status_I_Sat = 1                            'set pid_I_Clamp flag....
            pid_I = pid_I_Clamp                             'and clamp I-term to what user have set.
        Endif
        If pid_Sign then pid_I = -pid_I                     'Re-apply sign
        pid_IntCount = 0                                    'Reset the counter.
        If pid_EiT > 0 then  
           pid_Ei = 0                                       'Reset the accumulator.
        ENDIF
    Endif
    'Calculate the derivative drive       
        pid_D = pid_Error - pid_LastError
        pid_Sign = pid_D.15                                 'Save Sign
        pid_D = (ABS pid_D) */ pid_Kd                       'Multiply by Kd gain
        If pid_Sign then pid_D = -pid_D                     'Re-apply sign.
    DerivDone:
            pid_LastError = pid_Error                       'Store error for next D calc.
    '*******************************************************************************
    'Calculate the total drive.
    pid_Out = pid_Acc_FF + pid_Vel_FF + pid_P + pid_I + pid_D
    pid_Sign = pid_Out.15                                   'Save Sign
    pid_Out = ABS pid_Out                                   'Convert from two's comp. to abs.
    If pid_Out > pid_Out_Clamp then                         'If output is saturated...
        pid_Status_Out_Sat = 1                              'set status bit and...
        pid_out = pid_Out_Clamp                             'clamp output.
    Endif
    If pid_Sign then pid_out = -pid_out                     'Re-apply sign.
    RETURN                                                  'And return to sender.
    I have also attached a copy of the circuit diagram
    [attach]incPID_16F684[/attach]

    Obviously, I am not having the success I want - otherwise I would not be telling you all this
    I have tried to simplify my setup by eliminating the feedforward terms and by setting the integral and differential terms to zero. In essence, I am looking purely at the proportional control in the first instance and, once that is under control, I propose to tweak the system using the other available terms.

    As shown in the code, I have set pid_Out for single quadrant control by setting any potential negative drive to zero. I have made various attempts using values of Kp ranging from $0100 to $0900 but in all cases I only get varying rates of "bang-bang" output (ie output switching from zero to pid_Out_Clamp). I thought of looking at some of the variables on a 16x2 LCD but decided this might create its own set of timing problems and decided against it. I am not sure where to go or what to do next. Does anyone have any suggestions?

    One thing that has always confused/concerned me with the HPWM statement in this program; pid_Out is a WORD variable (16 bits) but the PBP manual states that duty cycle in the HPWM command only ranges from 0-255 (8 bits). Am I missing something here?

    For the record, I am using PBP 3.0.1.1, MCS 5.0.0.0 and the Melabs U2 programmer.

    I would greatly appreciate any assistance to help resolve my problems with this code.

    Cheers
    Barry (VK2XBP)
    Sydney, Australia
    Attached Images Attached Images

Members who have read this thread : 1

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts