PDA

View Full Version : PID basics

naatumach
12-06-2010, 11:09 PM
I have recently ventured into embedded robotics, and to begin this learning process i purchased a AVR 40pin development board from robokits and a 2A motor driver. I am using Atmega16.
I am using avrstudio to write and simulate my code.
I can do a basic code of LEDs, Line sensor array, proximity sensors, and i have written all the codes myself. All examples on the internet gave errors in avrstudio, so i understood all codes and coded myself.
I tried going through the datasheet of atmega16, didnt understand much, but i hope to understand more as i progress.

Now i want to learn PID and PWM.
I tried to read many many PID pages on the internet, i understood some logic, but i am not able to understand how to implement that logic in making a code(especially on avrstudio). I dont want a source code but i want a good algorithm to understand and code PID. I understand it requires EEPROM also (which i have not much idea of).
Does PID require PWM also if i plan to run motors?

The problem statement that i have proposed to myself is to run a robot on
1. P (yet to learn)
2. PD (yet to learn)
3. PDI (yet to learn)
to follow a line or a proximity sensor. and give outputs on either the LEDs or on Motors.

Can someone guide me?

Regards
Vaibhav

asimov_18
12-07-2010, 02:20 AM
Hi
whats ur age and qualification

Asimov

naatumach
12-07-2010, 09:29 AM
Hello
I am 19. Doing mechanical engineering 2nd yr.

naatumach
12-07-2010, 09:34 AM

Asimov

Thank you for the link. I have downloaded it and have skimmed over it once. I will read it in depth on 9th, because my exams will end on 9th.

MohitM
12-07-2010, 09:58 AM
Basically PID is a control scheme that calculates:
1. The present error
2. The total of all previous errors
3. The difference between the present and previous error.

The measurement and the control action loop - meaning
1. System 'sensors' measure the process value (could be temperature, RPM, pressure),
2. System calculates the difference between the process value and set value,
3. System takes a control action (like turn on the heater, increase the motor PWM's duty cycle, open a pressure valve),
4. Go back to step 1.

The PID response is analog, but has been adapted to the digital world rather nicely. Check out the Microchip application note pointed above. It shows how this is done. In control systems I've designed I've used exclusively the scheme I'm indicating below (in pseudo-code) and although the tuning (more on this later) takes some effort, yet the control is great.

To begin with you can leave out the differential component. It complicates things greatly. Begin with a simple proportional control scheme and then move to a PI scheme.
Tuning basically means having to find values of Kp, Ki, Kd. This is best done by experiment (trial/error) although there are tuning schemes also like ZN.

Go through this and let me know if you have any questions.

<code>
PID_Calculate
;------------
;---------------------------------------------------------------------------------
; Ppp
; Produces proportional component by multiplying Error_Hi:Error_Lo by Kp
;
; ProH:Pro = Error_Hi:Error_Lo * PH:PL
;
; This term forces the output close to the desired output quickly,
; but will never completely eliminate the error.
;
; Input Variables:
; Error_Hi:Error_Lo = Error found at top of loop
; n4:n3 = Kp [Proportional gain factor (constant)]
;
; Output Variables:
; ProH:Pro Proportional component
;
; At maximum, Error=0x1000 and Kp=0xA, then ProH2:L2=0xA000
;
;---------------------------------------------------------------------------------

;---------------------------------------------------------------------------------
; IntCom Computes integral component
;
; Multiplies present error by Ki,
;
; IntH:Int = IntH:Int + Error_Hi:Error_Lo * IH:IL
;
; This term will eliminate all error, but not quickly
;
; Input Variables:
; Error_Hi:Error_Lo Present loop error
; IntH:Int Running total of errors
; Ki Integral gain factor (constant)
;
; Output Variables:
; IntH:Int Integral component
;---------------------------------------------------------------------------------

;---------------------------------------------------------------------------------
; DifCom Computes differential component
;
; Finds difference between this loop error and previous loop error
; DifH : Dif = (Error_P_Hi:Error_P_Lo - Error_Hi:Error_Lo) * DH:DL
;
; This term tends to slow controller response.
;
; Input Variables:
; Error_P_Hi:Error_P_Lo : Previous loop error
; Error_Hi:Error_Lo : Present loop error
; DH:DL : Differential gain factor (constant)
; XD : Differential division factor (constant)
;
; Output Variables:
; DifH:Dif Differential component
;---------------------------------------------------------------------------------

;---------------------------------------------------------------------------------
; Total Sums proportional, integral, and differential terms
;---------------------------------------------------------------------------------
</code>

MohitM
12-07-2010, 10:02 AM
Okay this is a mess... I am getting smilies where I shouldn't be and I cannot remove them. Also the <code> tags don't seem to work.

naatumach
12-07-2010, 03:54 PM
Basically PID is a control scheme that calculates:
1. The present error
2. The total of all previous errors
3. The difference between the present and previous error.

The measurement and the control action loop - meaning
1. System 'sensors' measure the process value (could be temperature, RPM, pressure),
2. System calculates the difference between the process value and set value,
3. System takes a control action (like turn on the heater, increase the motor PWM's duty cycle, open a pressure valve),
4. Go back to step 1.

The PID response is analog, but has been adapted to the digital world rather nicely. Check out the Microchip application note pointed above. It shows how this is done. In control systems I've designed I've used exclusively the scheme I'm indicating below (in pseudo-code) and although the tuning (more on this later) takes some effort, yet the control is great.

To begin with you can leave out the differential component. It complicates things greatly. Begin with a simple proportional control scheme and then move to a PI scheme.
Tuning basically means having to find values of Kp, Ki, Kd. This is best done by experiment (trial/error) although there are tuning schemes also like ZN.

Go through this and let me know if you have any questions.

<code>
PID_Calculate
;------------
;---------------------------------------------------------------------------------
; Ppp
; Produces proportional component by multiplying Error_Hi:Error_Lo by Kp
;
; ProH:Pro = Error_Hi:Error_Lo * PH:PL
;
; This term forces the output close to the desired output quickly,
; but will never completely eliminate the error.
;
; Input Variables:
; Error_Hi:Error_Lo = Error found at top of loop
; n4:n3 = Kp [Proportional gain factor (constant)]
;
; Output Variables:
; ProH:Pro Proportional component
;
; At maximum, Error=0x1000 and Kp=0xA, then ProH2:L2=0xA000
;
;---------------------------------------------------------------------------------

;---------------------------------------------------------------------------------
; IntCom Computes integral component
;
; Multiplies present error by Ki,
;
; IntH:Int = IntH:Int + Error_Hi:Error_Lo * IH:IL
;
; This term will eliminate all error, but not quickly
;
; Input Variables:
; Error_Hi:Error_Lo Present loop error
; IntH:Int Running total of errors
; Ki Integral gain factor (constant)
;
; Output Variables:
; IntH:Int Integral component
;---------------------------------------------------------------------------------

;---------------------------------------------------------------------------------
; DifCom Computes differential component
;
; Finds difference between this loop error and previous loop error
; DifH : Dif = (Error_P_Hi:Error_P_Lo - Error_Hi:Error_Lo) * DH:DL
;
; This term tends to slow controller response.
;
; Input Variables:
; Error_P_Hi:Error_P_Lo : Previous loop error
; Error_Hi:Error_Lo : Present loop error
; DH:DL : Differential gain factor (constant)
; XD : Differential division factor (constant)
;
; Output Variables:
; DifH:Dif Differential component
;---------------------------------------------------------------------------------

;---------------------------------------------------------------------------------
; Total Sums proportional, integral, and differential terms
;---------------------------------------------------------------------------------
</code>

Sir
Thank you for the pseudo code.
I have read through it many times now. but i am not able to figure out what "Error_Hi:Error_Lo" kind of syntax means in the pseudo code. probably i will be able to figure out more once i am clear with the syntax
Regards
Vaibhav

MohitM
12-07-2010, 04:17 PM
In pseudo-code / assembly, if I would like to represent a 16-bit number on 8-bit devices, I show them like this: Error_Hi:Error_Lo. They are both 8-bit variables in the RAM. Error_Hi is the MSB and Error_Lo is the LSB (B=Byte).
This is to do 16-bit maths on 8-bit microcontrollers.

In C, I'd guess you'd code something like this: "unsigned int16 Error;" and your life is much easier vis-a-vis assembly.

EDIT: Mind you, I just copied directly from a project, removing some code, I think was not required (its all PIC MPASM code while you need AVR code). Also I code in assembly almost entirely. So some pieces may still be left over that may confuse you.

naatumach
12-09-2010, 02:31 AM
I went through both the codes. Still i am not able to get my stepping stone...
I will try to be more objective in my approach.

I am writing my idea of what needs to be done, kindly tell me where i should put what.

I am writing my pseudo-code
Electronics used - L298 Motor driver, 2 DC motors, 7 sensor line sensing array, atmega 16, and all the rest as mentioned in my first post.
Programmed in C in AVRstudio 4

1.take Variables - error, Kp, Ki, Kd, sensor, motor ( i guess i need two variables viz error and new_error)
2. I know - Sensor is in PORTA , Motor is PORTB
3. Set PortA to input , PortB to output.
/*
I have seven sensors "xxxxxxx1" last bit stays high on portA since there is no sensor on last pin
For me the zero error would mean "0b00111001 "
bit 1 means the black line is under the current sensor
bit 0 means the white region is under the current sensor
Positive errors are " 0b11100001 (extreme error) , 0b01110001 (small error) "
Negative errors are " 0b00001111 (extreme error) , 0b00011101 (small error) "
for a beginner i could use these statements
if( PINA == 0b11100001) error= +2; // Turn Left
if( PINA == 0b01110001) error= +1; // Turn Left
if( PINA == 0b00111001) error= 0; // Go straight
if( PINA == 0b00011101) error= -1; // Turn Right
if( PINA == 0b00001111) error= -2; // Turn Right

*/
4. Save input from PortA (PINA) to variable sensor.
5. Now what? //These are my probable guesses
{
1.Calculate error. How ?
2.Form a loop to calculate error, Kp, Ki , Kd , new_error.
3.Send value to motor? I am guessing a PWM, but how do i generate a PWM? what value to send?
}

Regards
Vaibhav

MohitM
12-09-2010, 09:28 AM
I'll get back to you in a day or two. In the meantime can you read up how to generate the PWM on the AVR? That's something described in the datasheet and tutorials.

naatumach
12-09-2010, 10:53 AM
I'll get back to you in a day or two. In the meantime can you read up how to generate the PWM on the AVR? That's something described in the datasheet and tutorials.

will comply

regards
vaibhav

naatumach
12-11-2010, 07:15 PM
will comply

regards
vaibhav

I tried to read the data sheet.
Then i tried to reverse engineer the sample code that i had for PWM from robokits

// VISIT US AT WWW.ROBOKITS.ORG
// PROGRAM FOR PWM
// WRITTEN FOR AVR 40 PIN DEV BOARD

/*-------------------------------------------------------------------
Description:
This example shows how to generate average DC voltages between VCC
and GND using Pulse Width Modulation (PWM).

Check voltage on pin 18-19 of avr with respect to GND.

Note:

Change OC1A and OC1B pin pullup if used with any other AVR micro.
---------------------------------------------------------------------*/

/*----------------------------------------------------------------
-----------------------------------------------------------------*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include<compat/deprecated.h> //HEADER FILE FOR FUNCTIONS LIKE SBI AND CBI

#define XTAL 1000000L // Crystal frequency in Hz
#define OC1A_PIN PD5 // OC1A pin
#define OC1B_PIN PD6 // OC1B pin
#define OC1_DDR DDRD // OC1 DDR

int main(void)
{

DDRB=0XFF; //SET DATA DIRECTION REGISTER
//SET 1 for OUTPUT PORT
//SET 0 FOR INPUT PORT
//ALL OTHERS ARE OUTPUT

DDRD=0XF0; //SET DATA DIRECTION REGISTER
//SET 1 for OUTPUT PORT
//SET 0 FOR INPUT PORT
//PD.1, PD.2 AND PD.3 ARE INPUT
//ALL OTHERS ARE OUTPUT

sbi(PORTD,0); //ENABLE PULL UP FOR SW1
sbi(PORTD,1); //ENABLE PULL UP FOR SW2
sbi(PORTD,2); //ENABLE PULL UP FOR SWITCH INT0
sbi(PORTD,3); //ENABLE PULL UP FOR SWITCH INT1

uint8_t i = 0;

OC1_DDR |= _BV(OC1A_PIN) + _BV(OC1B_PIN); // set OC1A and OC1B pin as output, required for output toggling

TCCR1A = _BV(WGM10) | _BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(COM1B0); // enable 8 bit PWM, select inverted PWM

// timer1 running on 1/8 MCU clock with clear timer/counter1 on Compare Match
// PWM frequency will be MCU clock / 8 / 512, e.g. with 1Mhz Crystal 244 Hz.
TCCR1B = _BV(CS11) | _BV(WGM12);

for (;;)
{
for (i=0; i<255; i++)
{
OCR1AL = i;
OCR1BL = i;
_delay_ms(25); // delay 25 ms
}

for( i=255; i>0; i--)
{
OCR1AL = i;
OCR1BL = i;
_delay_ms(25); // delay 25 ms
}
}
}

I couldnt really understand the synatx.
but i understood how to manipulate the output from this code.
I tried the code on LEDs and Motors, and did a crude hit an trial method to understand the working of the code.
what this code gave when connected to a motor was that the motor started up and then switched off then on then off.
I understood how to increase the frequency of that activity.
what i was most interested in was to get a steady motor rpm, so i removed the loops and got steady motor RPM, which i could vary according to the code.

Then i implemented my pseudo-code.
and said
for error +2 the left motor should go at 0% RPM and Right at 100% RPM.
for error +1 the left motor should go at 50% RPM and Right at 100% RPM.
for error 0 the left motor should go at 100% RPM and Right at 100% RPM.
for error -1 the left motor should go at 100% RPM and Right at 50% RPM.
for error -2 the left motor should go at 100% RPM and Right at 0% RPM.

This logic is obviously not tuned. and i haven't tried it on the "track" but yes i got my desired output.
I couldn't test it on the robot as i was out of time but this is my progress.

Regards
Vaibhav

I am still left with the topic of the thread PID.

MohitM
12-12-2010, 08:36 PM
Let's say:
a. You want to control the RPM of a PMDC motor between 0 - 1000 (so your sensor should be able to measure 0 - 1000 RPM)
b. Your microcontroller has a PWM resolution of 10 bits i.e. a duty cycle that varies from 0-1023. For simplicity, assume that the PWM duty cycle varies from 0 to 1000 (not 0 - 1023). Additionally, you also have a MOSFET (or something can switch ON-OFF at a rate of 10 kHz) that can supply the max power without blowing up.

Now, the operator sets the RPM at 500 RPM.
1. The motor starts from standstill, i.e. 0 RPM.
2. The processor calculates the difference between set RPM and actual RPM, ErrorPresent = 500 - 0 = 500

3. Supposing, ProportionalGainConstant = 1 (this value is just for illustration)
4. ProportionalComponent = ErrorPresent * ProportionalGainConstant = 500 * 1 = 500

5. Supposing, IntegralGainConstant = 0.5 (this value is just for illustration)
6. IntegralComponent = IntegralComponentPrevious + (ErrorPresent * IntegralGainConstant) = 0 + (500 * 0.5) = 250

7. Supposing, DifferentialGainConstant = 0.1 (this value is just for illustration)
8. DifferentialComponent = (ErrorPrevious - ErrorPresent) * DifferentialGainConstant = (0 - 500) * 0.1 = -50
Since this is the first calculation and there is no previous error, we do not consider the differential component in the first instance.

9. Add all these three components...
10. PID_Value = ProportionalComponent + IntegralComponent + DifferentialComponent
11. PID_Value = 500 + 250 + 0 = 750

12. Now you can pass this value to the PWM module of the processor, i.e 750/1000 is the duty cycle that you switch the MOSFET on.
13. At DutyCycle = 750, the motor speeds up. Lets say that while it is speeding up, the processor goes back to step 2 for the next round of control loop, at which point in time the motor RPM is 200.

14. The processor saves the previous error in ErrorPrevious (= 500) and calculates the present error, ErrorPresent = 500 - 200 = 300.
15. So, ProportionalComponent = ErrorPresent * ProportionalGainConstant = 300 * 1 = 300
16. IntegralComponent = IntegralComponentPrevious + (ErrorPresent * IntegralGainConstant) = 250 + (300 * 0.5) = 400
17. DifferentialComponent = (ErrorPrevious - ErrorPresent) * DifferentialGainConstant = (500 - 300) * 0.1 = 20
18. PID_Value = 300 + 400 + 20 = 720
19. This is the latest PWM duty cycle value for the MOSFET.

So as you loop repeatedly, you'll realise how a PID controller responds to the system behaviour and running errors.

Practically, you need to find out what values of GainConstants give you a good response. By which I mean that the system reaches the set value in the quickest time, yet does not overshoot the set value.

Also, make sure that the PID value does not cross 1000. If it does, then limit it to 1000 (or whatever your processor's maximum PWM value can be).

Run a series of experiments and ask here for any clarifications.

Mohit Mahajan,
www.BioZen.co.in

naatumach
12-19-2010, 09:18 PM
Hello
I am sorry for the late reply
I will continue once my exams are over.
I guess i will get time to start again only january.

Regards
Vaibhav