Introdução a Simulação de Controle de Sistemas com Python — Parte 02
Agora que fizemos uma revisão sobre os temas relativos à simulação de Controle de Sistemas na Parte 01, estamos prontos para escrever o código e entender o que cada linha significa.
FUNÇÃO DE TRANSFERÊNCIAS DOS SUBSISTEMAS
As Funções de Transferências dos sub-sistemas do sistema que queremos controlar, são:
Modelo do Circuito RC:
valores dos componentes:
- Capacitor: C=10μF
- Resistor: R=20kΩ
Com esses valores, a Função de Transferência H(s) fica da seguinte forma:
Sensor:
A Função de Transferência P(s) do sensor para esse Sistema de Controle por Realimentação e dada por:
Controlador:
Já para o Controlador, usaremos apenas a parte proporcional do controlador PID, assim temos que a Função de Transferência C(s) e dada por:
Essas são as Funções de Transferência necessárias para codificarmos nossa simulação.
ESCREVENDO O CÓDIGO PARA SIMULAÇÃO
Antes de iniciarmos a codificação da simulação, instalaremos as bibliotecas necessárias.
Instalação das Bibliotecas via pip
Abra seu Terminal, certifique-se de ter instalado o Python3 na sua máquina. Feito isso, copie o seguinte código no seu terminal:
python -m pip install -U pip
pip install control
python -m pip install -U matplotlib
pip install numpy
Código de Simulação — Resposta ao Degrau Unitário
Importando as Bibliotecas necessárias para a simulação
Crie um arquivo .py, por exemplo, simu_circuito_rc.py e importe as bibliotecas digitando as seguintes linhas de código:
import control as ctl
import matplotlib.pyplot as plt
import numpy as npplt.style.use("seaborn")
# %matplotlib inline # use no Jupyter Notebook, Google Colab
Logo abaixo das bibliotecas importadas, digite as seguintes linhas de código, elas são as constantes relativas aos valores de componentes e ao tempo de simulação.
R = 20.0e3 # Resistência do Resistor
C = 10.0e-6 # Capacitância do Capacitor
tau = R*C # Multiplicação de R POR C.
Temp_simu = 2.0 # Tempo de simulação.
Agora criaremos uma função em Python, para ficar melhor de realizar as simulações com diferentes valores de K_p. A linha de código abaixo mostra a estrutura de uma função em Python.
Código:
import control as ctl
import matplotlib.pyplot as plt
import numpy as npplt.style.use("seaborn")# Use no Jupyter notebook, colab
# %matplotlib inline# Variáveis da Simulação
R = 20.0e3
C = 10.0e-6
tau = R*C
Temp_simu = 2.0
# Função que calcula a resposta ao degrau
def Simulacao_RC(K_p = 3.0):
# Cria a Função de Transferência em Malha Aberta
numerador = [1/tau]
denominador = [1.0, 1/tau]
H_s = ctl.tf(numerador, denominador)
#print(f"\n\n> Função de Transferência em Malha Aberta:\n {H_s}") # Cria a Função de Transferência do Controlador
C_s=ctl.tf([K_p],[1.])
#print(f"\n> Função de Transferência do Controlador:\n {C_s}") # Cria a Função de Transferência do Sensor
P_s=ctl.tf([1.],[1.]) # Função de Transferência em Malha Fechada
G_s=ctl.series(C_s, H_s);
G1_s=ctl.feedback(G_s, P_s, sign=-1);
# print(f"\n> Função de Transferência em Malha fechada:\n {G1_s}") # Calcula a resposta ao Degrau Unitário
T_mf, yout_mf = ctl.step_response(G1_s, Temp_simu) # Plotando o degrau unitário
Temp_deg = np.linspace(-0.2, Temp_simu, 1000)
degrau = np.ones_like(Temp_deg)
degrau[Temp_deg < 0] = 0
return T_mf, yout_mf, Temp_deg, degrau, K_p
# Função que plota 3 gráficos com K_p diferentes para a resposta ao degrau
def prot_grafico(T_mf, yout_mf, Temp_deg, degrau, K_p, x):
plt.subplot(1, 3, x)
plt.plot(T_mf, yout_mf, linewidth = 1.2)
plt.plot(Temp_deg, degrau, color = "r", linestyle = "--",
linewidth = 1.2)
# Customizando a figura com Titulo, Títulos nos eixos, Legenda e
Grid
plt.title("Circuito RC - Resposta ao Degrau em Malha Fechada",
fontweight="bold", fontsize = 10)
plt.ylabel("Tensão no Capacitor (V)", fontweight="bold",
fontsize = 8)
plt.xlabel("Tempo (s)", fontweight="bold", fontsize = 8)
plt.legend(["Resposta ao Degrau", "Degrau Unitário"],
fontsize = 8)
if __name__ == "__main__":
# Plotando a resposta ao Degrau usando o metodo plt.plot() da
biblioteca matplotlib
plt.figure(figsize=(15, 7)) T_mf, yout_mf, Temp_deg, degrau, K_p = Simulacao_RC(K_p = 1)
prot_grafico(T_mf, yout_mf, Temp_deg, degrau, K_p, x = 1) T_mf, yout_mf, Temp_deg, degrau, K_p = Simulacao_RC(K_p = 4)
prot_grafico(T_mf, yout_mf, Temp_deg, degrau, K_p, x = 2) T_mf, yout_mf, Temp_deg, degrau, K_p = Simulacao_RC(K_p = 8)
prot_grafico(T_mf, yout_mf, Temp_deg, degrau, K_p, x = 3) plt.show()
Saída:
Explicação do Código
def Simulacao_RC(K_p = 3.0):
A linha acima é uma função que comporta o código de simulação, assim podemos fazer varias simulações modificando o valor de K_p para diferentes valores e ver como o sistema se comporta com essas mudanças.
Criando a função de transferência usando a Biblioteca Control-Python
Onde:
- tau = RC
# Cria a Função de Transferência em Malha Aberta
numerador = [1/tau] # Numerador da função de transferência
denominador = [1.0, 1/tau] # Denominador da função de transferência
H_s = ctl.tf(numerador, denominador)
A Biblioteca Control-Python tem um método que constrói a Função de Transferência de um sistema no domínio de Laplace, o código acima demostra como funciona a construção de uma Função de Transferência usando esse método.
Ele recebe dos parâmetros, sendo o primeiro o numerador e o segundo o denominador da função de transferência, assim é possível criar uma função de transferência no domínio de Laplace. que tem como resultado a seguinte equação:
Função de Transferência do Controlador
# Cria a Função de Transferência do Controlador
C_s=ctl.tf([K_p],[1.])
A Função de Transferência para o nosso controlador em questão, é um ganho proporcional, ou seja, apenas uma constante, no entanto, a Biblioteca Control-python sempre precisa receber os dois parâmetros — o numerador e denominador — assim, podemos escrever o denominador valendo 1, já que a divisão por um, não altera o valor dividido.
K_p é a variável que passamos como argumento na função Simulacao_RC(K_p = 3.0), logo, podemos colocar diferentes valores em k_p e observar como o sistema se comporta para esses valores.
Exemplo:
Função de Transferência do Sensor
O sensor é unitário, então sua função de transferência é 1. Usando a Biblioteca Control-python sua escrita fica da seguinte forma.
# Cria a Função de Transferência do Sensor
P_s = ctl.tf([1.],[1.])
Como você pode perceber, a escrita usando essa biblioteca é um pouco diferente do que estamos acostumados, mas o resultado é igual, já que 1/1=1.
FUNÇÃO DE TRANSFERÊNCIA EM MALHA FECHADA OU POR REALIMENTAÇÃO
Agora que temos as funções de transferências, podemos conectar a função do controlador com a do sistema usando o método ctl.series() da e depois realimentar o sistema com o sinal do sensor usando o método ctl.feedback().
# Função de Transferência em Malha FechadaG_s=ctl.series(C_s, H_s);
G1_s=ctl.feedback(G_s, P_s, sign=-1);
O método ctl.series() recebe as funções que vão ser conectadas em série, no nosso caso C_s e H_s e o método ctl.feedback() recebe a função gerada pelo método anterior e a função do sensor que será a realimentação. O terceiro parâmetro é se o sinal realimentado é positivo ou negativo, geralmente esse sinal de realimentação é negativo.
Obtida a Função em Malha Fechada, agora podemos usá-la para simular o comportamento do nosso sistema usando como exemplo o sinal degrau. Para isso, usaremos outro método da Biblioteca Control-python ctl.step_response() que recebe a Função em Malha Fechada encontrada na etapa anterior, e o tempo para simulação, após a simulação esse método retorna duas matrizes onde a primeira corresponde aos valores de simulação e a segunda aos tempos para cada instante simulado.
# Calcula a resposta ao Degrau UnitárioT_mf, yout_mf = ctl.step_response(G1_s, Temp_simu)
Gerando valores usando o Numpy para posterior plotagem de degrau unitário.
# Plotando o degrau unitárioTemp_deg = np.linspace(-0.2, Temp_simu, 1000)
degrau = np.ones_like(Temp_deg)
degrau[Temp_deg < 0] = 0
por fim, a função def Simulacao_RC(K_p = 3.0): após ser executada retorna os valores avaixo:
return T_mf, yout_mf, Temp_deg, degrau, K_p
FUNÇÃO PARA PLOTAR OS GRÁFICOS
Agora, tendo esses valores de simulação, podemos plotar o gráfico, e para isso criaremos uma função.
# Função que plota 3 gráficos com K_p diferentes para a resposta ao degraudef prot_grafico(T_mf, yout_mf, Temp_deg, degrau, K_p, x):
#
# CÓDIGO PARA PLOTAGEM VEM AQUI ....
Essa função tem 6 parâmetros, sendo eles( T_mf, yout_mf, Temp_deg, degrau, K_p, x )
Onde:
- T_mf: Matriz contendo os instantes de tempo de simulação retornado pela função Simulacao_RC.
- yout_mf: Matriz retornada pela função Simulacao_RC com os valores simulados em cada instante de tempo T_mf
- Temp_deg: Matriz com os valores de tempo gerados para plotar o degrau unitário também retornada pela função Simulacao_RC
- degrau: Matriz com os valores gerados para plotar o degrau unitário também retornada pela função Simulacao_RC
- K_p: Esse é o valor do ganho do controlador proporcional e é usado para colocar no título do gráfico.
- x: É usado para criar um ID para o sub-gráfico
Tendo definido o significado dos parâmetros acima, agora entenderemos o código da função.
plt.subplot(1, 3, x)
O método plt.subplot() é usado para plotar vários gráficos em uma mesma figura, no nosso caso, vamos plotar 3 sub-gráficos um ao lado do outro; Dessa forma, os parâmetros que devemos usar para indicar essa configuração são três.
O primeiro indica a quantidade de linhas, no nosso caso é uma, a quantidade de colunas, no nosso caso são 3, e, por último, um identificador único para cada sub-gráfico, no nosso caso o sub-gráfico 1 terá o ID 1, o sub-gráfico 2 ID 2 e o sub-gráfico 3 ID 3.
plt.plot(T_mf, yout_mf, linewidth = 1.2)
O método plt.plot(argumento1, argumento2 …) recebe no primeiro argumento os valores de tempo retornados pela simulação, já o segundo recebe os valores de simulação, linewidth = 1.2 muda a espessura da linha do gráfico.
plt.plot(Temp_deg, degrau, color = "r", linestyle = "--", linewidth = 1.2)
O código acima plota o degrau unitário, na cor vermelha.
# Customizando a figura com Titulo, Títulos nos eixos, Legenda e Gridplt.title("Circuito RC - Resposta ao Degrau em Malha Fechada", fontweight="bold", fontsize = 17)
plt.ylabel("Tensão no Capacitor (V)", fontweight="bold", fontsize = 14)
plt.xlabel("Tempo (s)", fontweight="bold", fontsize = 14)
plt.legend(["Resposta ao Degrau", "Degrau Unitário"], fontsize = 14)#plt.grid()
plt.show() # Plota o gráfico
O código acima é usado para nomear a figura, os eixos e os sub-gráficos.
SIMULAÇÃO
Com essas duas funções podemos fazer a simulação do nosso sistema para diferentes valores de K_p. Nesse casso vamos usar três valores diferentes para nosso controlador proporcional.
# Plotando a resposta ao Degrau usando o metodo plt.plot() da biblioteca matplotlibplt.figure(figsize=(25, 7))# SIMULAÇÃO 01
T_mf, yout_mf, Temp_deg, degrau, K_p = Simulacao_RC(K_p = 1)prot_grafico(T_mf, yout_mf, Temp_deg, degrau, K_p, x = 1)
# SIMULAÇÃO 02
T_mf, yout_mf, Temp_deg, degrau, K_p = Simulacao_RC(K_p = 4)prot_grafico(T_mf, yout_mf, Temp_deg, degrau, K_p, x = 2)
# SIMULAÇÃO 02
T_mf, yout_mf, Temp_deg, degrau, K_p = Simulacao_RC(K_p = 8)prot_grafico(T_mf, yout_mf, Temp_deg, degrau, K_p, x = 3)# PLOTA OS SUB-GRÁFICOS GERADOS EM UMA MESMA FIGURA
plt.show()
Saída:
OUTROS VALORES DE SIMULAÇÃO
# Plotando a resposta ao Degrau usando o metodo plt.plot() da biblioteca matplotlibplt.figure(figsize=(25, 7))
# SIMULAÇÃO 01
T_mf, yout_mf, Temp_deg, degrau, K_p = Simulacao_RC(K_p = -3)
prot_grafico(T_mf, yout_mf, Temp_deg, degrau, K_p, x = 1)# SIMULAÇÃO 02
T_mf, yout_mf, Temp_deg, degrau, K_p = Simulacao_RC(K_p = 0)
prot_grafico(T_mf, yout_mf, Temp_deg, degrau, K_p, x = 2)# SIMULAÇÃO 03
T_mf, yout_mf, Temp_deg, degrau, K_p = Simulacao_RC(K_p = 3)
prot_grafico(T_mf, yout_mf, Temp_deg, degrau, K_p, x = 3)plt.show()
Saída:
Observe que para diferentes valores de K_p nosso sistema em malha fechada tem um comportamento diferente. Em outras palavras, nosso sistema pode ser estável ou instável dependendo do valor de K_p, mas essa análise de estabilidade fica para um próximo post.
CONCLUSÃO
O Python, portanto, é uma excelente ferramenta que possibilita uma gama ampla de pacotes e módulos auxiliadores no processo de análise e simulação de sistemas simples e complexos. Bibliotecas como a Control-python, Matplotlib, Numpy e muitas outras podem ser de grande utilidade para profissionais e estudantes; uma vantagem no uso de Python para simular e analisar sistemas é que diferentemente de softwares caros, seu uso é livre e gratuito.
CÓDIGO NO GITHUB E GOOGLE COLAB:
REFERÊNCIAS
- Sinais e Sistemas Lineares. B.P. Lathi — Sinais e Sistemas Lineares (2006, Bookman).
- Matplotlib, Site Oficial, 2020.
Disponível em: https://matplotlib.org/stable/users/installing.html. Acesso em: 14 de Out. de 2021.
- Numpy, Site Oficial, 2020.
Disponível em: https://numpy.org/doc/stable/user/quickstart.html. Acesso em: 14 de Out. de 2021.
- Python Control, Site Oficial, 2020.
Disponível em: https://python-control.readthedocs.io/en/0.8.3/intro.html. Acesso em: 14 de Out. de 2021.
- Controlador proporcional integral derivativo, Wikipédia, a enciclopédia livre., 2020.
Disponível em: https://pt.wikipedia.org/wiki/Controlador_proporcional_integral_derivativo. Acesso em: 14 de Out. de 2021.