UD05 · Notebook 7 — Control difuso: regar el césped¶
En esta práctica simularemos el sistema de riego de cesped de un campo de fútbol. Nuestro sistema de riego está formado por un sensor de temperatura y un sensor de humedad en el terreno.
Dependiendo de los valores de estos sensores, el sistema de riego decidirá si activar o no el aspersor y con que intensidad. El objetivo es mantener la humedad del terreno de juego entre el 20% y el 25%.
Variables físicas del sistema¶
Primero, definiremos el funcionamiento de las variables físicas del sistema. Para simplificar el problema, consideraremos que la temperatura se definirá por una función sinusoidal.
La función de python que define la temperatura es la siguiente:
import numpy as np
def calc_temperatura(t):
return 20 + 50 * np.sin(1.5 * np.pi * t / 200)
# Graficamos la función
import matplotlib.pyplot as plt
t = np.arange(0, 240, 0.1)
plt.plot(t, calc_temperatura(t), label='temperatura')
plt.legend()
plt.show()
La humedad del terreno comenzará en un 20% y aumentará o disminuirá dependiendo de la temperatura y si se activa o no el aspersor. La función de python que define la humedad del terreno es la siguiente:
def humedad_terreno(h_actual=20, temperatura=20, aspersor=0):
h = h_actual + 0.1 * (aspersor - 50) + 0.1 * (temperatura - 20) / 30
return h
Definición de sistema de riego simple¶
En esta sección, definiremos un controlador simple para el sistema riego. Este controlador activará el aspersor cuando la humedad en el terreno sea inferior al 20% y desactivará el aspersor cuando la humedad en el terreno sea más del 25%. De lo contrario, el controlador mantendrá el aspersor con una intensidad proporcional a la humedad del terreno.
def controlador_simple(humedad_terreno_actual, temperatura=20):
if humedad_terreno_actual < 20:
return 100
elif humedad_terreno_actual > 25:
return 0
else:
return (25 - humedad_terreno_actual) * 100 / 3
def simula_riego(cnt, humedad_terreno_actual=20, t_final=100):
# Definimos los vectores de tiempo y de valores de las variables
temperaturas = []
humedades_terreno = []
aspersores = []
for i in range(t_final):
temp = calc_temperatura(i)
temperaturas.append(temp)
aspersor = cnt(humedad_terreno_actual, temp)
aspersores.append(aspersor)
humedad_terreno_actual = humedad_terreno(h_actual=humedad_terreno_actual, temperatura=temp, aspersor=aspersor)
humedades_terreno.append(humedad_terreno_actual)
# Graficamos los resultados
plt.plot(temperaturas, label='temperatura')
plt.plot(aspersores, label='aspersor')
plt.plot(humedades_terreno, label='humedad terreno')
plt.legend()
simula_riego(controlador_simple, 20, 100)
Controlador difuso¶
En esta sección, definiremos un controlador difuso para el sistema de regado.
Para la implementación del controlador, utilizaremos la librería [Scikit-Fuzzy] (https://pythonhosted.org/scikit-fuzzy/). Esta librería nos permitirá definir las funciones de pertenencia y las reglas difusas de una manera simple.
Comenzaremos importando las librerías necesarias:
%pip install scikit-fuzzy networkx
# scikit-fuzzy 0.4.2 ya no arranca en Python 3.12+ (importa `imp` y `distutils`, que
# se eliminaron del lenguaje), asi que instalamos la 0.5.0. En esa version todo
# funciona menos `ControlSystem.view()`: en su visualizador, la linea que guarda el
# sistema quedo dentro de un `if` y nunca se ejecuta. Se arregla en tres lineas:
from skfuzzy.control.visualization import ControlSystemVisualizer
_init_original = ControlSystemVisualizer.__init__
def _init_parcheado(self, control_system):
_init_original(self, control_system)
self.ctrl = control_system
ControlSystemVisualizer.__init__ = _init_parcheado
# Importamos las librerías necesarias
import skfuzzy as fuzz
from skfuzzy import control as ctrl
Definición de antecedentes y consecuentes¶
Primero, definiremos los antecedentes y los consecuentes del sistema de riego. Los antecedentes son los sensores de temperatura y humedad del terreno. Los consecuentes son los valores del Aspersor.
# Definimos los antecedentes y el consecuente
# Fondo
# Temperatura
temperatura = ctrl.Antecedent(np.arange(-10, 101, 1), 'temperatura (ºC)')
# Humedad del suelo
humedad = ctrl.Antecedent(np.arange(0, 101, 1), 'humedad terreno (%)')
# Consecuentes
# Aspersor
aspersor = ctrl.Consequent(np.arange(0, 101, 1), 'aspersor (%)')
Definición de funciones de pertenencia¶
A continuación, definiremos las funciones de pertenencia de los antecedentes y de los consecuentes. Para cada antecedente y para cada consecuente, definiremos las funciones de pertenencia que consideramos necesarias. En este caso, definiremos las funciones de pertenencia triangular.
Para definir las funciones de pertenencia, utilizaremos la función trimf de la librería Skfuzzy. Esta función recibe como parámetro un vector con los puntos de la función de pertenencia.
Para definir los puntos de las funciones de pertenencia,nos basaremos en los criterios de la siguiente tabla:
| Antecedente | Puntos de la función de pertenencia |
|---|---|
| Humedad | Seco: 0, 0, 17 Apropiado: 15, 20, 25 Húmedo: 23, 50, 100 |
| Temperatura | Frío: -10, 0, 15 Normal: 12, 20, 28 Calor: 24, 31.5, 39 Muy calor: 35, 65.5, 100 |
| Aspersor | Muy poco: -20, 0, 20 Poco: 15, 30, 45 Medio: 40, 55, 70 Mucho: 65, 75, 85 Máximo: 80, 100, 120 |
# Definimos las funciones de adherencia de los antecedentes y el consecuente
# Terreno de humedad
humedad['seco'] = fuzz.trimf(humedad.universe, [0, 0, 17])
humedad['apropiado'] = fuzz.trimf(humedad.universe, [15, 20, 25])
humedad['mojado'] = fuzz.trimf(humedad.universe, [23, 50, 100])
# Temperatura
temperatura['frio'] = fuzz.trimf(temperatura.universe, [-50, 0, 15])
temperatura['normal'] = fuzz.trimf(temperatura.universe, [12, 20, 28])
temperatura['calor'] = fuzz.trimf(temperatura.universe, [24, 31.5, 39])
temperatura['mucho_calor'] = fuzz.trimf(temperatura.universe, [35, 65.5, 100])
# Aspersor
aspersor['muy poco'] = fuzz.trimf(aspersor.universe, [-20, 0, 20])
aspersor['poco'] = fuzz.trimf(aspersor.universe, [15, 30, 45])
aspersor['medio'] = fuzz.trimf(aspersor.universe, [40, 55, 70])
aspersor['mucho'] = fuzz.trimf(aspersor.universe, [65, 75, 85])
aspersor['total'] = fuzz.trimf(aspersor.universe, [80, 100, 120])
# Vemos las funciones de pertenencia
humedad.view()
temperatura.view()
humedad.view()
aspersor.view()
Definición de reglas difusas¶
Ahora definimos las reglas difusas del sistema de riego. Para definir las reglas difusas, utilizaremos la librería Ctrl.Rule de la librería skfuzzy. Esta función recibe los antecedentes y los consecuentes de la regla difusa, así como el operador lógico que se aplicará entre los antecedentes.
En nuestro caso, definiremos las siguientes reglas difusas:
| Humedad | Temperatura | Aspersor |
|---|---|---|
| Seco | Frío | Mucho |
| Seco | Normal | Mucho |
| Seco | Caliente | Total |
| Seco | Muy caliente | Total |
| Apropiado | Frío | Poco |
| Apropiado | Normal | Poco |
| Apropiado | Caliente | Medio |
| Apropiado | Muy caliente | Medio |
| Húmedo | Frío | Muy poco |
| Húmedo | Normal | Muy poco |
| Húmedo | Caliente | Muy poco |
| Húmedo | Muy caliente | Poco |
# Definimos las reglas difusas
regla1 = ctrl.Rule(humedad['seco'] & temperatura['frio'], aspersor['medio'])
regla2 = ctrl.Rule(humedad['seco'] & temperatura['normal'], aspersor['medio'])
regla3 = ctrl.Rule(humedad['seco'] & temperatura['calor'], aspersor['medio'])
regla4 = ctrl.Rule(humedad['seco'] & temperatura['mucho_calor'], aspersor['medio'])
regla5 = ctrl.Rule(humedad['apropiado'] & temperatura['frio'], aspersor['poco'])
regla6 = ctrl.Rule(humedad['apropiado'] & temperatura['normal'], aspersor['poco'])
regla7 = ctrl.Rule(humedad['apropiado'] & temperatura['calor'], aspersor['medio'])
regla8 = ctrl.Rule(humedad['apropiado'] & temperatura['mucho_calor'], aspersor['medio'])
regla9 = ctrl.Rule(humedad['mojado'] & temperatura['frio'], aspersor['muy poco'])
regla10 = ctrl.Rule(humedad['mojado'] & temperatura['normal'], aspersor['muy poco'])
regla11 = ctrl.Rule(humedad['mojado'] & temperatura['calor'], aspersor['muy poco'])
regla12 = ctrl.Rule(humedad['mojado'] & temperatura['mucho_calor'], aspersor['poco'])
Definición del sistema de riego¶
Luego definimos el sistema de riego. Para definir el sistema de riego, utilizaremos la función ctrl.controlsystem de la librería skfuzzy. Esta función recibe como parámetro una lista con las reglas difusas del sistema.
# Definimos el sistema de riego
sistema_riego = ctrl.ControlSystem([regla1, regla2, regla3, regla4, regla5, regla6, regla7, regla8, regla9, regla10, regla11, regla12])
# Vemos el sistema de riego
sistema_riego.view()
Definición de simulador de sistema de riego¶
Luego definimos el simulador del sistema de riego. Para definir el simulador del sistema de riego, utilizaremos la función ctrl.controlsystemsimulation de la librería skfuzzy. Esta función recibe el sistema de riego como un parámetro.
# Definimos el simulador del sistema de riego
simulador_riego = ctrl.ControlSystemSimulation(sistema_riego)
Simulación del sistema de riego¶
Ahora simulamos el sistema de riego.
def controlador_difuso(humedad_terreno_actual, temperatura):
# Definimos los antecedentes del sistema de riego
simulador_riego.input['humedad terreno (%)'] = humedad_terreno_actual
simulador_riego.input['temperatura (ºC)'] = temperatura
# Ejecutamos el sistema de riego
try:
simulador_riego.compute()
except Exception as e:
print("Humedad terreno: " + str(humedad_terreno_actual) + " Temp.: " + str(temperatura))
# Devuelve el valor del ascersor
return simulador_riego.output['aspersor (%)']
simula_riego(controlador_difuso, 20, 100)