RASPBERRY: Máquina de estados (I)


AUTOMÁTAS DE ESTADOS FINITOS, FSM (Finite State Machines) (I)

1. Introducción

Los autómatas de estados finitos1 FSM son una técnica matemática que permite representar el funcionamiento de numerosos dispositivos de forma simple y rigurosa. Si a ello unimos la posibilidad de disposer de una plataforma hardware potente y barata como la Raspberry y disponemos del software adecuado, nos puede facilitar la realización de numerosos proyectos de elevada complejidad con gran utilidad en el campode la automática y la robótica.

El objetivo de este trabajo es implementar dicha teoría y establecer un método repetitivo y fácil de manejar sobre una plataforma de bajo coste y presentar algunos ejemplos de su aplicación.

Se ha elegido la plataforma hardware Raspberry porque

  • Dispone de numerosas entradas y salidas que permiten uctilizar sensores y actuadores
  • Es de bajo coste
  • Dispone de sistema operativo Linux (Raspbian) y de lenguaje Python3, con elevadas prestaciones
La presentación aquí efectuada es pyues sólo una parte de lo que permite la teoría de FSM y pretende servir de ejemplo y primer contacto con esta potente herramienta.

2. Principios de funcionamiento del autómata:

El autómata dispone de un conjunto de estados en los que se puede encontrar. Es decir, en un momento dado, el «autómata» está en uno de esos estados, que llamaremos «estado».
El sistema también posee unas entradas que lee y almacena en una variable que llamaremos «contexto». Asimismo posee una serie de salidas consistentes en realizar algunas ciertas acciones (incluida la acción «no hacer nada») que sólo se ejecutan cuando el sistema cambia de estado. Es decir, el sistema, mientras está en un cierto estado, no hace nada; sólo hace algo cuando cambia de estado, cuando se produce una transición de un estado a otro.

A lo largo del tiempo, los valores de las entradas pueden tomar ciertos valores.
Si, el estado en que se encuentra, «es sensible» a estas entradas, puede producirse un transición y cambiar de estado, ejecutandose la(s) accion(es) correspondiente(s) a dicha transición.
Y así hasta el infinito.
El autómata va tomando diversos estados y efectuando diversas acciones en función de ls entradas.
El autómata, los estados, las transiciones y las acciones se definen mediante una estructura de datos en la que está relacionado todo ello.

Eso es todo.
Se trata pues, de determinar, frente a cada caso, cuanles son los estados possibles del sistema, a que entradas es sensible cada estado, qué transiciones se pueden producir y qué acciones se llevan a cabo en cada una de ellas.

Veamos como se hace. En un ejemplo.

3. Primer ejemplo

3.1. Definición

Diseñaremos una FSM que a cada tecla que se pulse haga cambiar de estado un LED, de apagado a encendido y viceversa. Este es un ejemplo muy sencillo que puede efectuarse por medios mucho más sencillos pero su objetivo es el de mostrar el funcionamiento de una FSM, no el de ser una solución óptima.

Analicemos el caso.
Es evidente que el sistema partirá de un estado inicial que hemos de definir. Vamos a suponer, parece lógico, que partimos del LED apagado, llamémosle estado OFF. Al recibir como input una tecla (input «tecla») la FSM pasará a al estado encendido, llamémosle ON, efectuando la acción «encender LED». Estando en el estado ON, si recibe una tecal pasará al estado OFF de nuevo y efectuará la acción «apagar LED».

La FSM se puede representar así
















Vamos a establecer la definición de la FSM, usando lenguaje Python

FSM01= {‘NOMBRE’:’EJEMPLO 1’, # nombre de la FSM
OFF’: # definición del estado OFF
[[{‘TECLA’,’ON’, EnciendeLED]], # input, transición y acción
‘ON’: # definición del estado ON
[[[‘TECLA’,’OFF’,ApagaLED ]]} # input, transición y acción

3.2. Motor

Ahora diseñamos una funcion que será el motor de la FSM. Esta función intrepretará la definición de la FSM i determinará a partir del estado actual y los inputs (contexto) cual es el estado siguiente y las acciones a realizar. Esta es nuestro motor


# AUTOMATA ------------
def Engine(estado,contexto, fsm):
try:
d=fsm[estado]
nxt=estado
# definición estado actual
for i in range (len(d)): # mirar cada transición
t=d[i] # transición i
c=t[0] # condicion de transición
if c.intersection(contexto)==c: # mirar si c està contenida en ct
nxt=t[1] # proximo estado nxt
for k in range (2,len(t)): # efectuar acciones
t[k]() # llamada a cada accion
contexto=contexto-c # contexto restante "no consumido"
return nxt,contexto # dar por finalizado
return e,contexto
except:
print ("Error en Engine Estado:",e, " inputs:",contexto)
return "REPOS",None


3.3 Implementación inicial

A fin de facilitar este primer ejemplo, en lugar de un LED escribiremos en pantalla la palabra ON o la palabra OFF. La entrada se hará con la tecla <ENTER>

La implementación es la siguiente:


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# FSM Ejemplo 1
#

# Motor ------------
def Engine(e,ct, fsm):
try:
d=fsm[e]
nxt=e
# definición estado actual
for i in range (len(d)): # mirar cada transición
t=d[i] # transición i
c=t[0] # condicion de transición
if c.intersection(ct)==c: # mirar si c està contenida en ct
nxt=t[1] # proximo estado nxt
for k in range (2,len(t)): # efectuar acciones
t[k]() # llamada a cada accion
ct=ct-c # contexto restante "no consumido"
return nxt,ct # dar por finalizado
return e,ct
except:
print ("Error en Engine Estado:",e, " inputs:",ct)
return "REPOS",None

# Acciones ------------
def EnciendeLED():
global LED
LED="Encendido"
return

def ApagaLED():
global LED
LED="Apagado"
return

# Autómata ------------
FSM01= {"NOMBRE":"EJEMPLO 1", # nombre de la FSM
"OFF": # definición del estado OFF
[[{"TECLA"},"ON", EnciendeLED]], # input, transición y acción
"ON": # definición del estado ON
[[{"TECLA"},"OFF",ApagaLED ]]} # input, transición y acción



# bucle principal

LED="Apagado"
estado="OFF"

while True:
t=input()
print ("TECLA <ENTER> PULSADA")
contexto={"TECLA"}
print ("Estado inicial: %3s Contexto: %10s LED:%s"%(estado,contexto,LED))
estado,contexto=Engine(estado,contexto,FSM01)
print ("Estado final : %3s Contexto: %10s LED:%s"%(estado,contexto,LED))


Al ejecutar el programa se observa el siguinte resultado:



que es el resultado que esperábamos tener. Vemos como a cada input (en este caso la tecla ENTER), el autómata canvia de estado.

Este software, que ha sido desarrollado para una Raspberry, puede ser ejecutado en cualquier ordenador puesto que es independiente del hardware.


3.4 Implementación en RASPBERRY

Para finalizar, haremos una implementación con una RASPBERRY, utilizando los puertos de entrada/salida de la placa y la libreria GPIO que los gestiona. Para ello habr que añadir o modificar algunas líneas de código y algunas conexiones.

Habrá que poner un pulsador con una resistencia y un LED, también con su resistencia. Los esquemas, muy simples: connectar el LED al pin GPIO21 con una resistencia en serie (4700 ohm) i a la masa (GND) y el otra resistencia de 10000 ohm (aproximadamente) del pin GPIO20 a massa (GND) y el pulsador entre dicho pin y +3.3V (3V3). Estos pin pueden ser modificados adecuando las constantes que estan al inicio del programa


El nuevo programa será

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# FSM Ejemplo 2
# 

LED_PIN=21  # pin del GPIO al que conectamos el LED
SENSOR =20  # pin del GPIO al que conectamos el pulsador

import RPi.GPIO as GPIO
from time import sleep
import atexit

# Motor ------------
def Engine(e,ct, fsm):
    try:
        d=fsm[e]
        nxt=e
        # definición estado actual
        for i in range (len(d)):              # mirar cada transición
            t=d[i]                            # transición i
            c=t[0]                            # condicion de transición
            if c.intersection(ct)==c:         # mirar si c està contenida en ct
                 nxt=t[1]                     # proximo estado nxt
                 for k in range (2,len(t)):   # efectuar acciones
                     t[k]()                   # llamada a cada accion
                 ct=ct-c                       # contexto restante "no consumido"
                 return nxt,ct                   # dar por finalizado     
        return e,ct
    except:
        print ("Error en Engine Estado:",e, " inputs:",ct)
        return "OFF",None

# Acciones ------------
def EnciendeLED():
    global LED
    GPIO.output(LED_PIN, GPIO.HIGH)
    LED="ENCENDIDO"
    return

def ApagaLED():
    global LED
    GPIO.output(LED_PIN, GPIO.LOW)
    LED="APAGADO"
    return

# Autómata ------------
FSM01= {"NOMBRE":"EJEMPLO 1",                # nombre de la FSM
        "OFF":                               # definición del estado OFF 
          [[{"TECLA"},"ON", EnciendeLED]],   # input, transición y acción
        "ON":                                # definición del estado ON  
          [[{"TECLA"},"OFF",ApagaLED   ]]}   # input, transición y acción

def preparacontexto():
    if GPIO.input(SENSOR)==GPIO.HIGH:
        return {"TECLA"}
    else:
        return set()
    

# bucle principal

atexit.register(GPIO.cleanup)   # hacer que se ejecute al parar


#inicializar puertos RASPBERRY
GPIO.setmode (GPIO.BCM)
GPIO.setup(LED_PIN, GPIO.OUT)
GPIO.setup(SENSOR , GPIO.IN)

# inicializar FSM
estado="OFF"
ApagaLED()


i=0    # contador
while True:
    contexto=preparacontexto()
    if contexto=={"TECLA"}:
        x=1
    else:
        x=0
    estado,contexto=Engine(estado,contexto,FSM01)
    if LED=="ENCENDIDO":
        y=1
    else:
        y=0
    print ("%6d %3d %3d"%(i,x,y))
    i+=1
    sleep(0.2)
Y ponemos en marcha el programa (al que hemos añadido un retardo(sleep) para poder apreciarlo a ojo correctamente, puesto que en caso contrario es demasiado rápido el bucle.


Podemos ver que, cada vez que pulsamos el pulsador, el LED canvia de estado sin parpadeos, lo que también queda reflejado en la salida por pantalla, con ceros y unos. La primera columna es un contador, la segunda el estado del pulsador y la tercera el LED. Claramente, cada vez que la entrada es 1 la salida canvia de estado.

 



En breve incluiré más ejemplos






























Comentarios

Entradas populares de este blog

CALENTAMIENTO DE LA RASPBERRY

EL BLOG DE MI RASPBERRY