Projekt 2
Budowa
i badanie regulatora rozmytego
5. Przykłady
W przykładzie wykorzystano bibliotekę Fuzzy Logic do realizacji prostego rozmytego systemu wnioskującego. Kod zapisano w notebooku Jupyter-a project_2.ipynb
.
Testy prowadzone będą przy założeniu zmian wokół ustalonego punktu pracy
, okres symulacji
.
Załóżmy, że chcemy zaprojektować regulator rozmyty o następującej strukturze:
- wygnały wejściowe: odchyłka
, pochodna odchyłki
,
-
dla każdego wejścia zdefiniujemy dwa zbiory rozmyte o liniowych funkcjach przynależności typu Z i S położonych symetrycznie względem osi y,
Wykorzystany kształt funkcji przynależności na wejściach modelu rozmytego -
dla wyjścia
zdefiniowano trzy singletony położone symetrycznie względem osi y,
Wykorzystany kształt funkcji przynależności na wejściach modelu rozmytego -
baza wiedzy systemu wnioskującego:
Reguły wnioskowania regulatora rozmytego Reguła R1: R1: R1: R1: - wartość wyjściowa z modelu rozmytego
podawana jest na blok całkujący w celu wyznaczenia wartości
.
Założono następujące zmienności sygnałów wejściowych i wyjścia:
Implementację modelu rozmytego w bibliotece Fuzzy Logic pokazano na poniższym listingu. W implementacji wykorzystano skalowanie przedziałów zmienności we/wy do przedziału przy wykorzystaniu współczynników
,
i
. Ze względu na właściwości biblioteki singletony przybliżono bardzo stromą funkcją trójkątną.
from fuzzylogic.classes import Domain, Set, Rule
from fuzzylogic.functions import R, S, triangular
import matplotlib.pyplot as plt
# Precyzja obliczeń
prec = 0.001
# Definicja zmiennej rozmyte e
e_dom = Domain("e", -1, 1, prec)
e_dom.N = S(-1,1)
e_dom.P = R(-1,1)
plt.figure()
e_dom.N.plot()
e_dom.P.plot()
plt.title('Zmienna lingwistyczna e')
# Definicja zmiennej rozmyte de
de_dom = Domain("de", -1, 1, prec)
de_dom.N = S(-1,1)
de_dom.P = R(-1,1)
plt.figure()
de_dom.N.plot()
de_dom.P.plot()
plt.title('Zmienna lingwistyczna de')
# Definicja zmiennej rozmyte dCV
dcv_dom = Domain("dCV", -1-prec, 1+prec, prec)
dcv_dom.N = triangular(-1-prec, -1+prec)
dcv_dom.Z = triangular(-prec, prec)
dcv_dom.P = triangular(1-prec, 1+prec)
plt.figure()
dcv_dom.N.plot()
dcv_dom.Z.plot()
dcv_dom.P.plot()
plt.title('Zmienna lingwistyczna dCV')
# Definicja reguł wnioskowania
R1 = Rule({(e_dom.N, de_dom.N): dcv_dom.N})
R2 = Rule({(e_dom.N, de_dom.P): dcv_dom.Z})
R3 = Rule({(e_dom.P, de_dom.N): dcv_dom.Z})
R4 = Rule({(e_dom.P, de_dom.P): dcv_dom.P})
# Definicja bazy wiedzy modelu rozmytego
rules = R1 | R2 | R3 | R4
# Przykład wnioskowania
values = {e_dom: -0.25, de_dom: -0.005}
print(R1(values), R2(values), R3(values), R4(values), "=>", rules(values))






Aby osadzić regulator rozmyty w symulatorze przygotowano dedykowany blok funkcyjny SimpleFuzzyController
(fuzzy.py
). Parametry modelu ke
, kde
i kdCV
zdefiniowano jako sygnały wejściowe bloku, a nie jako parametry wewnętrzne. Wejścia te zostaną wykorzystane w kolejnym projekcie.
"""
Prosty regulator rozmyty z dwoma wejściami: e, de
i jednym wyjściem dCV.
"""
# Simple fuzzy kontroller with 2 inputs: e, de
# and 1 output: dCV
class SimpleFuzzyController(Block):
def __init__(self, _name):
super().__init__(_name)
prec = 0.001
self.e_dom = Domain("e", -1, 1, prec)
self.e_dom.N = S(-1, 1)
self.e_dom.P = R(-1, 1)
self.de_dom = Domain("de", -1, 1, prec)
self.de_dom.N = S(-1, 1)
self.de_dom.P = R(-1, 1)
self.dcv_dom = Domain("dCV", -1-prec, 1+prec, prec)
self.dcv_dom.N = triangular(-1-prec, -1+prec)
self.dcv_dom.Z = triangular(-prec, prec)
self.dcv_dom.P = triangular(1-prec, 1+prec)
R1 = Rule({(self.e_dom.N, self.de_dom.N): self.dcv_dom.N})
R2 = Rule({(self.e_dom.N, self.de_dom.P): self.dcv_dom.Z})
R3 = Rule({(self.e_dom.P, self.de_dom.N): self.dcv_dom.Z})
R4 = Rule({(self.e_dom.P, self.de_dom.P): self.dcv_dom.P})
self.rules = R1 | R2 | R3 | R4
self.add_output()
def calculate(self):
ke = self.input_val(0)
kde = self.input_val(1)
kdcv = self.input_val(2)
e = self.input_val(3)
de = self.input_val(4)
values = {self.e_dom: e/ke, self.de_dom: de/kde}
self.output_val(0, self.rules(values)*kdcv )
W kolejnym kroku przygotowano podsystem sterowania rozmytego FuzzyControl
, w którym wykorzystano przygotowany blok funkcyjny. Zabieg ten wykonano w celu uporządkowania kodu symulatora. Krok ten może zostać pominięty, wtedy elementy składowe tego podsystemu trzeba zdefiniować bezpośrednio w jednej ze ścieżek przetwarzania.
class FuzzyControl(Subsystem):
"""
Konstruktor
Należy dodać wejścia: SP [m], PV [m]
@param _tp Czas próbkowania z jakim będzie działać regulator
"""
def __init__(self, _name, _inputs, _tp):
super().__init__(_name, _inputs)
# Obliczenie e=SP-PV oraz de/dt
e = self.add_block( SumDiff('e', [1, -1]) )
e.add_input(self.input(0))
e.add_input(self.input(1))
de = self.add_block( RealDifferentiator('de', _tp, 1, 5) )
de.add_input(e.output(0))
# Bloki do przechowywania parametrów
ke = self.add_block( Const('ke', 1) )
kde = self.add_block( Const('kde', 0.01) )
kdcv = self.add_block( Const('kdCV', 1) )
# Blok regulatora fuzzy
fcontroller = self.add_block( SimpleFuzzyController('fcontroller') )
fcontroller.add_input(ke.output(0))
fcontroller.add_input(kde.output(0))
fcontroller.add_input(kdcv.output(0))
fcontroller.add_input(e.output(0))
fcontroller.add_input(de.output(0))
# Całkowanie sygnału dCV
cv = self.add_block( FirstOrderIntegrator('cv', _tp, 1, 1, [0, 100]) )
cv.add_input(fcontroller.output(0))
# Dodanie wyjść: CV [%], e [m]
self.add_output(cv.output(0))
self.add_output(e.output(0))
self.add_output(de.output(0))
W ostatnim kroku należy podmienić w symulatorze (konstruktor klasy TtsPidSim
) zawartość ścieżki przetwarzania controller z poleceń obsługujących regulator PID:
controller = controller_sim.add_block( PID('controller', [sp.output(0), pv_measure.output(0), cvman.output(0)], self.tp*self.pr['control']) )
# add ins/outs: controller parameters
self.add_in_var('kp', controller.blocks['kp'].const)
self.add_in_var('Ti', controller.blocks['ti'].const)
self.add_in_var('Td', controller.blocks['td'].const)
self.add_in_var('Bias', controller.blocks['bias'].const)
self.add_in_var('Ienable', controller.blocks['i_enable'].const)
self.add_in_var('Denable', controller.blocks['d_enable'].const)
self.add_in_var('PIDmode', controller.blocks['mode'].const)
na obsługę regulatora rozmytego:
controller = controller_sim.add_block( FuzzyControl('controller', [sp.output(0), pv_measure.output(0)], self.tp*self.pr['control']) )
# add ins/outs: controller parameters
self.add_in_var('ke', controller.blocks['ke'].const)
self.add_in_var('kde', controller.blocks['kde'].const)
self.add_in_var('kdCV', controller.blocks['kdCV'].const)
# - monitoring variables: de
self.add_out_var('de', controller.output(2))
Poniżej pokazano przykładowe wyniki z działania regulatora rozmytego. Jak widać jakość regulacji pozostawia wiele do życzenia – zdecydowanie należy poprawić nastawy tego regulatora, a zapewne także zmienić jego strukturę.

