Regulator PID


Tym razem odpalamy regulator PID, czyli matematyczny kod proporcjnalno, róźniczkująco, całkujący.

Jego zadanie polega na regulowaniu wartości porządanej w zależności od zmienności jej chwilowej wartości. W prostszy sposób można powiedzieć, że zadaniem regulatora PID jest korygowanie nastaw, w taki sposób aby ewentualne odchylenia wartości wyjściowej korygować do żądanej wielkści.

W tym celu stosowany jest kod matematyczny w którym regulowane są zmienne przypisane do elementów proporcjonalnego, różniczkującego i całkującego. Poniższy kod prezentuje sposób działania regulatora PID. klawiszami od 1 do 8 możemy zmieniać nastawy poszczególnych zmiennych.

import time
import random
import pygame
from pygame.locals import *
from sys import exit

pygame.init()

class PID:


    def __init__(self,P=2.0, I=0.0, D=1.0, Derivator=0, Integrator=0, Integrator_max=500, Integrator_min=-500):

        self.Kp=P
        self.Ki=I
        self.Kd=D
        self.Derivator=Derivator
        self.Integrator=Integrator
        self.Integrator_max=Integrator_max
        self.Integrator_min=Integrator_min
        
        self.set_point=0.0
        self.error=0.0

    def update(self,current_value):
        """
        Calculate PID output value for given reference input and feedback
        """

        self.error = self.set_point - current_value

        self.P_value = self.Kp * self.error
        self.D_value = self.Kd * ( self.error - self.Derivator)
        self.Derivator = self.error

        self.Integrator = self.Integrator + self.error

        if self.Integrator > self.Integrator_max:
            self.Integrator = self.Integrator_max
        elif self.Integrator < self.Integrator_min:
            self.Integrator = self.Integrator_min

        self.I_value = self.Integrator * self.Ki

        PID = self.P_value + self.I_value + self.D_value

        return PID

    def setPoint(self,set_point):

        self.set_point = set_point
        self.Integrator=0
        self.Derivator=0

    def setIntegrator(self, Integrator):
        self.Integrator = Integrator

    def setDerivator(self, Derivator):
        self.Derivator = Derivator

    def setKp(self,P):
        self.Kp=P

    def setKi(self,I):
        self.Ki=I

    def setKd(self,D):
        self.Kd=D

    def getPoint(self):
        return self.set_point

    def getError(self):
        return self.error

    def getIntegrator(self):
        return self.Integrator

    def getDerivator(self):
        return self.Derivator

BLACK = (0,0,0)
WHITE = (255,255,255)
BLUE = (0,0,255)
GREEN = (0,255,0)
RED = (255,0,0)

ekran=pygame.display.set_mode((1000,800))
pygame.display.set_caption("filtr dolnoprzepustowy")
czcionka = pygame.font.SysFont("monospace", 30)
ekran.fill(WHITE)
pygame.draw.line(ekran,BLACK,(10,10),(10,610),1)
pygame.draw.line(ekran,BLACK,(10,305),(950,305),1)

i=10
erro=0
erro2=0
t1=0
t2=0
wy2=0
wy=0
out2=0
PO=0
PO2=0
P=0.1
I=0
D=0
p=PID(P,I,D)
wzmocnienie=20
p.setPoint(PO)

while True:
    pid = p.update(erro)
    i=i+1
    if i>950:
        ekran.fill(WHITE)
        pygame.draw.line(ekran,BLACK,(10,10),(10,610),1)
        pygame.draw.line(ekran,BLACK,(10,305),(950,305),1)

        i=10
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_1:
                wzmocnienie=wzmocnienie*2
            if event.key == pygame.K_2:
                wzmocnienie=wzmocnienie/2
            if event.key == pygame.K_3:
                P=P+0.1
            if event.key == pygame.K_4:
                P=P-0.1
            if event.key == pygame.K_5:
                I=I+0.1
            if event.key == pygame.K_6:
                I=I-0.1
            if event.key == pygame.K_7:
                D=D+0.1
            if event.key == pygame.K_8:
                D=D-0.1
        p=PID(P,I,D)
    if i==100:
        PO=5
    if i==200:
        PO=1
    if i==300:
        PO=5
    if i==400:
        PO=1
    if i==500:
        PO=5
    if i==600:
        PO=1
    if i==700:
        PO=5
    if i==800:
        PO=1
    if i==900:
        PO=5
    pygame.draw.line(ekran,RED,(i,(305-wzmocnienie*PO)),(i-1,(305-wzmocnienie*PO2)),3)

    erro=PO-wy
    if erro>0:
        wy=wy2-pid
    else:
        wy=wy2-pid
    PO2=PO
    wy2=wy
    if wy<-300:
        out=-290
    else:
        out=wy
    pygame.draw.line(ekran,BLUE,(i,(305-wzmocnienie*out)),(i-1,(305-wzmocnienie*out2)),1)
    out2=out

    label = czcionka.render("PO= %.2f" %(PO), 1, BLUE,WHITE)
    ekran.blit(label, (650, 10))
    label = czcionka.render("error = %.2f" %(erro), 1, BLUE,WHITE)
    ekran.blit(label, (650, 120))
    label = czcionka.render("PID = %.2f" %(pid), 1, BLUE,WHITE)
    ekran.blit(label, (650, 350))
    label = czcionka.render("Wy = %.2f" %(wy), 1, BLUE,WHITE)
    ekran.blit(label, (650, 450))
    label = czcionka.render("I = %.2f" %(i), 1, BLUE,WHITE)
    ekran.blit(label, (650, 550))
    label = czcionka.render("Kl.3 i 4 P= %.2f" %(P), 1, BLUE,WHITE)
    ekran.blit(label, (50, 650))
    label = czcionka.render("Kl.5 i 6 I= %.2f" %(I), 1, BLUE,WHITE)
    ekran.blit(label, (50, 700))
    label = czcionka.render("Kl.7 i 8 D= %.2f" %(D), 1, BLUE,WHITE)
    ekran.blit(label, (50, 750))
    time.sleep(0.02)
    pygame.display.update()

	


Na poniszym zdjęciu można zuważyć sposób działania regulatora. Kolorem czerwonym prezentowana jest wartość pożądana, a kolorem niebieskim wartość jaka wychodzi z regulatora PID, czyli wartość adaptowana do pożądanej.



:)