Closed loop speed controller


Closed Thread
Results 1 to 9 of 9
  1. #1
    Join Date
    Sep 2006
    Location
    Indiana, USA
    Posts
    72

    Cool Closed loop speed controller

    I need to control the speed of a 90 volt DC PM motor (1.3 Amp, 11:1 reduction, 173 RPM final), one direction rotation. I will be using a mosfet to drive it. I need to be able to vary the speed between dead stop ( or a slow crawl) and full speed (or close to it) and have closed loop control, i.e. as the motor meets varying loads, the PWM changes to keep the motor as close to the set speed as possible. I got the PWM thing down, in fact, I'll probably use DT's HPWM10 include for that part of it. I figure i'd use a hall effect sensor on the primary motor shaft so it'll be easier to read at lower RPM's. I'm guessing around 1900 RPM full speed? and using one magnet. I just don't know how to code it! i figured i'd use a 16F684 and Timer1 to get the speed into the PIC, and ADCIN to grab a 10 bit value from my speed control pot, but im lost as what to do from there.. i need some code example or thoughts on how to get the error between my ADCIN value and the motors speed and then how to adjust my PWM to keep it steady? i don't need to know how to "flash an LED" first, i use PIC's alot, i've just never tried any closed loop stuff before. Thanks in advance for any help!!

  2. #2
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    This would be a good place to start...

    PID-filter routine (2nd try). (HenrikOlsson)
    http://www.picbasic.co.uk/forum/showthread.php?t=5874

    It takes a bit of playing to get the right sample rate and P-I-D constants, but it works pretty good when you do.
    <br>
    DT

  3. #3
    Join Date
    Sep 2006
    Location
    Indiana, USA
    Posts
    72


    Did you find this post helpful? Yes | No

    Default A couple of questions...

    Thanks for the fast reply Darrel!

    1. The PID code is for a servo right? I see that it has direction control and I'm guessing when the setpoint and the ADC value match, it's going to stop the motor and fight to keep it in that position?

    2. what do I change in the code to make this thing run as a speed control?

  4. #4
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    That's what the example does, but the PID routine itself can be used for MANY different things.

    In Henrik's example, it's controlling "Position". But if the numbers presented to the PID are representative of the Speed, then it will adjust the output until the Speed is matched.

    It doesn't really matter what it is, temperature, humidity, speed, acceleration ...

    If you can read it, then you can make a "Setpoint" within that range.
    The PID loop adjusts the output until the "Reading" and the "Setpoint" are the same.

    It does it in a way that is as fast as possible while minimizing overshoot and rejecting spurious influences as much as it can.

    All you need to do, is find a way to read the speed of the motor, and a way to control the speed of the motor, which I think you already have.
    <br>
    DT

  5. #5
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,604


    Did you find this post helpful? Yes | No

    Default

    Hi,
    Just as Darrel says it can be used to control "anything". Basicly you feed it the Error and it returns the amount of Drive it thinks is neccesary to reduce the Error to zero based on the P- I- & D-gains and a couple of other paramters. It doesn't know, or care, if it's position, speed or whatever.

    Code:
    pidError = Setpoint - CurrentSpeed	'Calculate error
    Gosub PID				'Call PID. Result returned in pid_Out
    There are a couple of things you need to concider. Since you're driving the motor in one quadrant only. There may be occations when the output of the PID routine is "negative" ie. it want's to actually reverse the motor. Obviously you can't do that with only one transistor so you need to clamp the output from the PID routine to positive drive only, perhaps something like this:
    Code:
    pid_Error = Setpoint - CurrentSpeed   	'Calculate error
    Gosub PID                           	'Result returned in pid_Out                           
     If pid_out.15 then                 	'If "negative drive" is called for...
       pid_out = 0                       	'..set output to 0...
       pid_Ei = 0                        	'And keep the I-term from accumulating..
      
     Else                               	'and if positve drive is called for....
    
       pid_Out = ABS pid_Out             	'...set it to absoulte value of pid_Out
     Endif
    Since you want to controll the speed from zero all the way up I think you should concider more than a single a pulse per rev. Otherwise it can get really hard to get good control in the lower speedrange.

    Another thing that is important in digital PID control is constant sampling rate. Ideally you set up a timer interrupt to run at whatever rate you need. What rate to run at depends on the timeconstant of the load and you may need to experiment with a bit but for a basic motorcontrol I'd say somewhere between 100-1000Hz.

    /Henrik Olsson.
    Last edited by HenrikOlsson; - 26th January 2008 at 06:59.

  6. #6
    Join Date
    Sep 2006
    Location
    Indiana, USA
    Posts
    72


    Did you find this post helpful? Yes | No

    Default Thanks

    yeah, that was one thing i was worried about, when my speed goes below the clamp setting.. and it doesnt have to go to complete zero, I can handle that in other ways. my final output shaft is 173RPM, I might add more magnets to the main motor shaft, before the gear reduction just to be safe, i'll have to see what i can get away with. I'm not quite sure i know what your saying about sampling rate? I was going to use timer1 as a counter to get the tach of the motor, which people typically want as RPMs for a display and would count for a second, but are you saying i should count for less then a second? or as much as possible? and also, im sure the number i get for the speed isnt going to be 1:1 with my ADC (the speed setpoint), should i be adjusting something to scale those, or? I don't have everything on hand i need at the moment for a bench test and experiment, I'll have to order a few more things (fets, a rectifier and cap for the motor). i'd like to have a handle on code things before hand tho! thanks again.

  7. #7
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,604


    Did you find this post helpful? Yes | No

    Default

    Hi,
    About the sampling rate. The motor and whatever is attached to it has a certain mechanical time constant. If you apply a voltage to it it it takes a certain amount of time for it to "respond". If you sample a lot slower than this time constant you won't be able to get good control.

    I'll take a shot at an example:
    If we are controlling the temperature in our swimming pool (which probably have a very large time constant) perhaps we should read the actual temperature and calculate a new "drive-value" for the heater once every hour or even slower than that. But if we're controlling the position of the mirror in a laser-scanner we may need to read the actual position and calculate a new "drive-value" tens of thousands of times per second.

    Whatever the rate is (once a day or once every uS) it needs to be constant because the final output is based on the error itself (P) the magnitude and amount of time the error has been present (I) and the rate of change of the error (D).

    It all depends on how "stiff" you want the control-loop to be. You may get away with a sampling rate of 1s if you can live with very a slow response. If you need to "instantly" respond to the change in load you need to sample much much faster than that.

    There was a really good article available on the web called PID without a PhD explaining this a lot better than I can but I can't seem to find it. If you look around a bit you may be able to find it.

    EDIT: Here it is.

    HTH
    /Henrik Olsson
    Last edited by HenrikOlsson; - 26th January 2008 at 09:24. Reason: Found the article.

  8. #8
    Join Date
    May 2004
    Location
    NW France
    Posts
    3,648


    Did you find this post helpful? Yes | No

    Wink the octogonal wheel ???

    Hi, Enrik

    You should browse the Parallax site : in their applications ... there's all your link shows ... but for PICs AND PbP ( or compatible) written.

    http://www.parallax.com/Portals/0/Do...eb-PC-v1.0.pdf

    see also here : http://www.emesystems.com/BS2math5.htm#Smoothing

    " Hope it can help " ... as we use to say !!!

    Alain
    ************************************************** ***********************
    Why insist on using 32 Bits when you're not even able to deal with the first 8 ones ??? ehhhhhh ...
    ************************************************** ***********************
    IF there is the word "Problem" in your question ...
    certainly the answer is " RTFM " or " RTFDataSheet " !!!
    *****************************************

  9. #9
    Join Date
    Oct 2007
    Location
    The Netherlands
    Posts
    45


    Did you find this post helpful? Yes | No

    Default

    I made a simple pid speed control some time ago, not very scientific but it works o.k.
    I used a surplus motor with an optical encoder, then used the 'count' command to measure speed. I looked at the parallax code and copied their way of dealing with negative numbers.
    here's my code:

    Code:
    <font color="#008000">@ DEVICE PIC16F88, MCLR_OFF, INTRC_OSC_NOCLKOUT, WDT_ON, LVP_OFF, BOD_OFF, PWRT_ON, PROTECT_OFF, CCPMX_ON
    
    </font><b>DEFINE  </b>OSC 8
    
    <b>DEFINE  </b>CCP1_REG PORTB
    <b>DEFINE  </b>CCP1_BIT 3         <font color="#000080"><i>' portb.3 motor PWM output
    </i></font><b>DEFINE  </b>ADC_BITS        8  <font color="#000080"><i>' Set number of bits in result
    </i></font><b>DEFINE  </b>ADC_CLOCK       3  <font color="#000080"><i>' Set clock source (3 = RC)
    </i></font><b>DEFINE  </b>ADC_SAMPLEUS    50 <font color="#000080"><i>' Set sampling time in uS
    
    </i></font>PORTA =     %00000000
    TRISA =     %00000001		
    PORTB =     %00000000
    TRISB =     %00010000
    OSCCON =    %01111000    <font color="#000080"><i>'8MHZ clock
    </i></font>ANSEL =     %00000001       
    CMCON =     %00000111
    
    led         <b>VAR </b>PORTA.1
    sensor      <b>VAR </b>PORTB.4  <font color="#000080"><i>'optical feedback from motor
    
    </i></font>counter     <b>VAR BYTE
    </b>set_speed   <b>VAR WORD
    </b>actual_speed <b>VAR WORD</b>				
    error       <b>VAR WORD 
    </b>old_error   <b>VAR WORD
    </b>error_sum   <b>VAR WORD
    </b>sign        <b>VAR WORD 
    </b>Kp          <b>CON </b>1
    Ki          <b>CON </b>2                <font color="#000080"><i>'inverse !!
    </i></font>Kd          <b>CON </b>0                    
    power       <b>VAR WORD
    </b>P           <b>VAR WORD
    </b>I           <b>VAR WORD
    </b>D           <b>VAR WORD
    
    CLEAR                            </b><font color="#000080"><i>'clear all variables
    
    </i></font>loop:
        <b>GOSUB </b>read_sensors    
        <b>GOSUB </b>calculate_error
        <b>GOSUB </b>calculate_proportional
        <b>GOSUB </b>calculate_integral
        <b>GOSUB </b>calculate_differential
        <b>GOSUB </b>calculate_drive
        <b>HPWM </b>1,power,10000  <font color="#000080"><i>' drive motor
        </i></font><b>TOGGLE </b>led
    <b>GOTO </b>loop
    
    read_sensors:
        <b>ADCIN </b>0,set_speed                <font color="#000080"><i>'read potmeter
        </i></font><b>COUNT </b>sensor,20,actual_speed     <font color="#000080"><i>'count number of pulses 
    </i></font><b>RETURN                               </b><font color="#000080"><i>'from encoder in 20msec
    
    </i></font>calculate_error:
        error = set_speed - actual_speed 
    <b>RETURN
    
    </b>calculate_proportional:
        P = Kp * error 
    <b>RETURN
    
    </b>calculate_integral:
        error_sum = error_sum + error
        sign = error_sum
        <b>GOSUB </b>SetSign
        <b>IF ABS </b>error_sum &gt; (250*Ki) <b>THEN </b>error_sum = sign * (250*Ki) <font color="#000080"><i>' limit 
        </i></font>I = <b>ABS </b>error_sum / Ki
        I = sign * I
    <b>RETURN
    
    </b>calculate_differential
        D = Kd * (error - old_error)
        old_error = error
    <b>RETURN
    
    </b>calculate_drive:
        power = P + I + D		
        sign = power
        <b>GOSUB </b>SetSign
        <b>IF </b>sign = 1 <b>THEN          </b><font color="#000080"><i>' no negative power
            </i></font><b>IF </b>power &gt; 255 <b>THEN </b>power = 255
        <b>ELSE 
            </b>power = 0
        <b>ENDIF
    RETURN        
    
    </b>SetSign:
        <b>IF </b>sign.Bit15 = 0 <b>THEN 
            </b>sign = 1
        <b>ELSE
            </b>sign = -1
        <b>ENDIF 
    RETURN
    
    END
    
    
    
    
    
    </b>

Similar Threads

  1. Controlsystem for compact sporting (clay shooting)
    By Fredrick in forum mel PIC BASIC Pro
    Replies: 11
    Last Post: - 30th July 2009, 16:48
  2. Serin to Serin2 ??
    By Gixxer in forum mel PIC BASIC Pro
    Replies: 3
    Last Post: - 25th January 2008, 03:56
  3. Replies: 14
    Last Post: - 26th September 2007, 05:41
  4. Serial Relays
    By tazntex in forum General
    Replies: 3
    Last Post: - 17th May 2007, 17:42
  5. calculating speed of a loop
    By EDWARD in forum mel PIC BASIC Pro
    Replies: 1
    Last Post: - 5th June 2005, 20:34

Members who have read this thread : 0

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