Control de la maquinaria de un ascensor
Está práctica consiste en escribir una aplicación
que controla la maquinaria de un ascensor ya existente para que se
comporte de la manera descrita en este documento.
Los detalles técnicos relativos a la maquinaria del ascensor y
la descripción de la interfaz de control de dicha maquinaria que
ha de usarse para escribir la aplicación de control están
descritos en el documento HTML que se
encuentra la página web de
la asignatura. Dicho documento describe los detalles de cómo
integrar el software de la aplicación de control con el software
que simula el ascensor y su maquinaria. También describe un
conjunto de funciones que permiten actuar sobre la maquinaria y
consultar su estado, detallando los argumentos a pasar y el
comportamiento del ascensor al invocar cada función. Finalmente,
el documento describe una lista de constantes que definen los valores
concretos de cada parámetros de funcionamiento del ascensor
(velocidad, número de plantas, altura de cada planta, posiciones
de los topes de recorrido en el hueco del ascensor, etc.).
Indice
Especificación del
comportamiento del ascensor
Inicialización de la maquinaria
Cuando se visualiza el simulador, la posición vertical del
ascensor en el hueco es impredecible (se genera de forma aleatoria).
Además, cuando la cabina está detenida entre dos plantas,
es imposible obtener información sobre la posición en el
hueco. A menos que dé la casualidad de que la cabina se halle
detenida justo en una planta, será necesario mover la cabina
hasta que los sensores disponibles nos permitan conocer una
posición que el algoritmo de control pueda establecer como punto
de inicio de la actividad. Para ello tenemos dos opciones:
- La primera es llevar la cabina hasta uno de los sensores de fin
de carrera, donde ésta se parará automáticamente
cuando se active dicho sensor. Una vez aquí, podremos mover la
cabina hasta pararla en una planta concreta.
- La segunda opción consiste en mover la cabina hacia
arriba
o hacia abajo hasta que se encienda un sensor de planta o bien un
sensor de fin de carrera. En el primer caso debemos parar la cabina
para que quede emplazada en la planta alcanzada. En el segundo caso
procederemos como en el primer método de inicialización.
La ventaja de este método es que el tiempo máximo de
inicialización se limita al tiempo que tarda el ascensor en
recorrer la distancia entre dos plantas.
Control de la puerta
La puerta del ascensor ha de permanecer cerrada mientras no tengan que
subir o bajar pasajeros. Esto quiere decir que cuando el ascensor se
halle sin actividad y detenido en una planta la puerta se
mantendrá cerrada. Los únicos momentos en los que se
abrirá la puerta serán:
- Cuando la cabina llega a una planta siguiendo una orden de
cualquiera de las botoneras. En este caso, la puerta se abre
automáticamente, permanece abierta cinco segundos y
después se cierra automáticamente, tanto si tiene que
acudir a otra planta como si va a quedar en reposo en esta planta.
- Cuando la cabina se encuentra detenida en una planta y alguien
pulsa el botón correspondiente a dicha planta en alguna de las
botoneras. En este caso la puerta se abre automáticamente,
permanece abierta cinco segundos y después se cierra
automáticamente.
- Si la cabina está detenida en una planta y la puerta
está dándose en el momento en que alguien pulsa el
botón correspondiente a dicha planta en alguna de las botoneras,
la puerta volverá a abrirse inmediatamente sin esperar a que
termine de cerrarse.
Existe un sensor que indica si hay algún obstáculo
interpuesto en el camino de la puerta, pero este sensor no controla el
motor de la puerta para provocar su apertura automática. Si la
puerta se está cerrando con un obstáculo interpuesto, el
motor continuará insistiendo en la operación de cerrado,
aunque la puerta no llegará cerrarse (se suponen los mecanismos
de seguridad adecuados para que la puerta no ejerza demasiada
presión sobre el obstáculo). Es responsabilidad del
módulo de control detectar el obstáculo y abrir
nuevamente la puerta. La secuencia de control de la puerta será
la siguiente:
- Abrir la puerta totalmente.
- Si se pulsa el botón interno o externo para la planta
actual mientras se abre la puerta, se da por atendida la
petición.
- Esperar 5 segundos.
- Si se pulsa el botón interno o externo para la planta
actual mientras espera, se reinicia el temporizador a 5 segundos de
nuevo.
- Cerrar la puerta totalmente.
- Si se detecta un obstáculo mientras se cierra la puerta
volver al paso inicial (volver a abrir la puerta y esperar).
- Si se pulsa el botón interno o externo para la planta
actual mientras se cierra volver al paso inicial.
- Si la puerta consigue cerrarse completamente, se ha terminado
esta secuencia.
Atención a las peticiones y movimiento
de la cabina
Se pretende conseguir el comportamiento adecuando para funcionar en un
edificio de vecinos. El ascensor realiza ciclos de subida y bajada
alternantes. Es posible pasar también a estados de inactividad
en los que no hay peticiones. Un ciclo de subida no implica que la
cabina se mueva hacia arriba, sino que el ascensor está
atendiendo peticiones para que un supuesto usuario pueda viajar desde
una planta inferior a una superior: es posible que el ascensor tenga
primero que bajar a un piso inferior para recoger al usuario que luego
pulsará el botón de un piso superior, sin embargo, el
ascensor se encontraría en ciclo de subida.
Es decir, los ciclos de subida y bajada son estados lógicos del
autómata de control, independientemente de si la cabina sube o
baja. El funcionamiento que se
prentende es el siguiente:
- Ciclo de subida: El
objetivo es subir parando en todos los destinos solicitados desde la
botonera interna. Solo se atienden los destinos de la botonera interna
por encima de la planta actual e incluyendo la planta actual si
todavía está parada la cabina. Las peticiones internas
por debajo de la planta actual y las peticiones de los botones externos
se ignoran durante la subida y se posponen para el siguiente ciclo de
bajada. Si se pulsa el botón externo desde una planta donde
está detenida la cabina con la puerta abierta, se da por
atendida la petición (y se abre la puerta si se ha comenzado el
cierre de la misma).
El ciclo de subida termina cuando se
cierra la puerta y no queda ninguna petición de la botonera
interna por encima de la planta actual. Si quedan otras peticiones
pasamos al ciclo de bajado. Si no quedan más peticiones se pasa
al estado de inactividad.
- Ciclo de bajada: El
objetivo es bajar parando en todos los destinos solicitados tanto desde
la botonera interna como externa. En este ciclo se distinguen dos
estados:
- Si acabamos de entrar en el ciclo de bajada y hay
peticiones externas por encima de la planta actual, la cabina sube
hasta la petición más alta de todas y desde allí
comienza a bajar (nota: la cabina sube pero el ciclo es de bajada ya
que se acude a peticiones para bajar). Si solo hay peticiones por
debajo de la planta actual se comienza el movimiento de bajada.
- Si ya hemos comenzado el movimiento de bajada, se ignoran las
peticiones de todo tipo por encima de la planta actual. Durante la
bajada se para en todos los destinos por debajo de la planta actual
solicitados tanto desde el interior como desde el exterior del ascensor.
El ciclo de bajada termina cuando no
queden más destinos por debajo de la planta actual. Si no queda
ningún destino se pasa al estado de inactividad. Si no, se pasa
al estado de subida de nuevo.
- Estado de inactividad: En
este estado no existen ninguna petición que atender. La cabina
permanece detenida y con la puerta cerrada. Es el estado inicial del
ascensor tras la inicialización. En este estado, cualquier
petición desde la misma planta en la que está detenida la
cabina causa la apertura de la puerta, pero no el inicio de ciclos de
subida o bajada.
Este estado termina cuando se aparece
una petición cualquiera de una planta que no sea la actual.
Consejos para la implementación
Procedimientos básicos de control
La mayoría de sensores disponibles no genera ningún tipo
de
interrupción cuando se activan. Esto incluye a los sensores de
planta y
a los sensores de fin de carrera. Los únicos sensores que
generan
interrupciones son los botones de las botoneras interna y externa y el
comparador que indica el encoder ha acumulado 2700 pulsos. Para estos
dos sensores existen funciones que permiten indicar qué thread
debe ser
avisado mediante el signal que nosotros elijamos. Una vez programado
esto, tenemos que programar la función de atención al
signal en el
thread correspondiente para que se ejecute cuando el signal sea enviado
por el simulador.
Para controlar el movimiento del ascensor podríamos usar una
estrategia
simple tal cómo activar el motor y establecer una
temporización que nos
avise en el instante en que tenemos que para el motor o cambiar la
velocidad. El problema de este estrategia, es que no tiene en cuenta
los errores introducidos por el simulador, que se
corresponderían en la
realidad con tiempos no constantes de arrancada y de frenado. Por
tanto, es preferible seguir una estrategia donde los diferentes
sensores disponibles nos permitan corregir continuamente las
diferencias existentes entre las predicciones del control y el
comportamiento de la maquinaria.
Una estrategia sencilla de verificación y corrección
consiste en
verificar que la planta actual por la que pasa la cabina cada vez que
se activa un sensor de planta coincide con la planta prevista por el
módulo de control. Podría pasar, por ejemplo, que se
averiase un sensor
de planta y perdiéramos la cuenta del número de plantas
al faltarnos un
sensor.
Otra estrategia recomendable consiste en tener en cuenta el
desbordamiento del contador del encoder que nos avise mediante un
interrupción en lugar de intentar consultar continuamente el
sensor de
planta que podría pasar desapercibido si el thread en
cuestión no se
puede ejecutar durante el breve paso de la cabina por el sensor.
En cuanto la parada en la planta de destino, es importante la
precisión
ya que el mecanismo de la puerta no abrirá si la cabina no se
encuentra
dentro de un margen de tolerancia alrededor de la altura de la planta.
Por esa misma razón, es importante basarse en la
interrupción del
contador del encoder, que está directamente relacionada con la
posición
de la cabina, en lugar de basarse en el tiempo transcurrido desde que
se puso en marcha el motor de la cabina.
El movimiento de la cabina siempre debe usar la velocidad lenta para
arrancar y para parar, y la velocidad rápida para viajar entre
plantas.
La secuencia de movimiento resultante para cualquier desplazamiento de
la cabina entre dos plantas será, por tanto, la siguiente:
- Iniciar el movimiento velocidad lenta en la dirección
requerida.
- Una vez recorrida la distancia de arranque, pasamos a velocidad
rápida (como no hay ningún sensor de posición que
indique esto, nos
basaremos en una temporización).
- Cuando se detecta la interrupción del contador del
encoder
(nos
estamos aproximando a una planta) tenemos la oportunidad de decidir si
tenemos que detenernos en la próxima planta. También
podemos llevar la
cuenta de las plantas atravesadas contando interrupciones del encoder
en lugar de contar las activaciones de los sensores de planta.
- Si tenemos que detenernos, se pasa el motor a velocidad lenta y
se establece un temporizador para que avise en el instante en que se ha
de parar el motor.
- Cuando el temporizador avisa (mediante un signal) se para el
motor.
Autómatas
Es evidente que existen varios autómatas en el funcionamiento
del ascensor para controlar diversos comportamientos. Algunos de ellos
(si no todos son):
- El control de la operación de la puerta durante una
parada.
- El control del motor de la cabina durante el movimiento desde
una
planta a la planta de destino.
- El control de los ciclos de subida, bajada e inactividad del
ascensor.
Es muy recomendable dibujar un esquema de cada autómata,
especificando los distintos estados por los que pasa y, sobre todo, las
transiciones especificando los eventos o condiciones que hacen que pase
a un nuevo estado. Es conveniente prever también qué ha
de hacer el autómata en cada estado si se dan condiciones
imprevistas en el funcionamiento normal, aunque la
implementación de todas las condiciones anormales podría
llegar a ser tediosa, por lo que esto último depende de la
meticulosidad de cada autor en el diseño. Resumiendo, es opcional tratar posibles
condiciones excepcionales que nunca deberían darse pero que
podrían aparecer en caso de avería de algún
sensor, etc.
La implementación de los autómatas en C sigue siempre un
esquema muy sencillo:
/* Es
conveniente definir simbolos para hacer mas claro el codigo */
#define
AUTOMATA1_INACTIVO 0
#define
AUTOMATA1_SUBIENDO 1
#define
AUTOMATA1_BAJANDO 2
/* funcion
que contiene un bucle de control con un autómata */
void f()
{
/* Variable que contiene el estado actual: Inicializada con Einicial */
int estado=INACTIVO;
while (1)
{
switch(estado)
{
case AUTOMATA1_INACTIVO:
...acciones
al entrar en el estado 0...
/* espera a que se cumpla alguna condicion para la transición
a otro estado, por ejemplo un sensor o una variable
booleana
cambiada en otro proceso */
while
(numero_destinos()==0) usleep(20000);
/* indica el
nuevo estado al que se pasa:
puede depender de la condicion de la transición. */
if (destinos_encima()>0) estado = AUTOMATA1_SUBIENDO;
if (destinos_debajo()>0) estado = AUTOMATA1_BAJANDO;
break;
case AUTOMATA1_SUBIENDO:
...acciones al entrar en el
estado 1...
break;
case AUTOMATA1_BAJANDO:
...acciones al entrar en el
estado 2...
break;
}
}
}
División en tareas
Un problema de diseño es como coordinar los distintos
autómatas que controlan diversos aspectos. En algunos casos,
dichos autómatas van de un estado inicial a uno final y no
tienen que recordar ningún estado hasta que vuelven a ser
necesitados. Este es el caso del control de la puerta, que puede ser
ejecutado como una subrutina cuando la cabina se para en una planta.
Una vez cerrada la puerta, la función que implementa el
autómata podría retornar devolviendo el control ya que no
es necesario recordar el estado de la puerta una vez cerrada.
Sin embargo, hay otros autómatas que tienen que cambiar de
estado simultáneamente. Un ejemplo podría ser qué
pasa si se pulsa un botón interior cuando la cabina está
subiendo hasta la petición más alta en el comienzo de un
ciclo de bajada. Si la petición está en el camino del
ascensor, el ascensor debe pasar al ciclo de subida de nuevo y
detenerse en dicha planta. En este caso, el autómata de
movimiento de la cabina no puede ejecutarse como una subrutina dentro
de los estados del autómata del control de destinos ya que
éste último puede cambiar de estado durante el viaje de
una planta a otra.
Para poder implementar esto, también es posible asignar una
tarea separada a cada autómata ejcutando un bucle infinito como
el de la función mostrada arriba. La comunicación entre
las tareas se puede lograr mediante variables compartidas y
semáforos productor-consumidor.
Lectura de las peticiones de las botoneras
Un detalle a tener en cuenta es la forma en que funcionan las
botoneras. Cada botonera (la interna o la externa) se lee con una
función que retorna un número entero que contiene un mapa
de bits que representa los botones que han sido pulsados y que siguen
activos (luz encendida). Aunque podemos recibir un signal cuando se
pulsa un botón, al leer el estado de las botoneras no podemos
saber qué botón o botones se acaban de pulsar. Ni
siquiera en cual de las botoneras.
Para poder controlar esto es necesario distinguir entre el estado de
las botoneras y la lista de peticiones ya recibidas, lo que viene a ser
una copia del estado de las botoneras guardada por nuestra
aplicación. Si cada vez que consultamos las botoneras guardamos
una copia de su estado, al recibir un nuevo signal podemos compararlas
bit a bit y saber qué bits se han activado desde la
última vez que la consultamos. Para una comparación de
bits podemos usar un código como este:
int mapaint_old=0;
compara_botint ()
{
int mapa;
int mascara, cambios;
mapa = lee_botint();
cambios = mapa ^ mapa_old; /* XOR de ambos mapas */
mapaint_old = mapa;
mascara = 1;
for (i=0; i<N; i++)
{
if (cambios & mascara != 0)
{
printf("el boton %d ha sido pulsado\n",i);
peticiones_internas[i]=1;
}
mascara = mascara << 1; /* SHIFT izquierda de la mascara */
}
}
Otra cosa que parece conveniente es almacenar las peticiones en
tablas que permitan manejarlas más facilmente. Por ejemplo:
/* Variables
globales */
int
peticiones_externas[N_PISOS];
int
peticiones_internas[N_PISOS];
y definir funciones que nos permitan hacer consultas complejas sobre el
conjunto de peticiones. Por ejemplo:
/* Retorna la
primera peticion por encima de la planta actual */
int
busca_primera_peticion_porencima(int planta)
{
int peticion=-1;
for (j=planta+1; j<N_PISOS; j++)
if (peticiones_internas[j]==1 || peticiones_externas[j]==1) peticion=j;
return peticion;
}
Por último, hay que tener en cuenta que es posible recibir un
aviso de una botonera en cualquier momento, incluso unos milisegundos
antes de tomar una decisión, por lo que no parece recomendable
decidir el destino del ascensor cuando partimos de una planta, sino
cuando se pulsa una tecla. Hay que tener en cuenta que el
próximo destino del ascensor solo puede cambiar como
consecuencia de la pulsación de un botón, por tanto no
tiene sentido calcularlo continuamente si no se ha pulsado ninguna
tecla. Una estrategia recomendable es:
- Mantener una variable global con la planta de próximo
destino del ascensor en función de las peticiones recibidas y
del estado del ascensor (ciclo de subida/bajada/reposo,
dirección de la cabina, etc).
- Actualizamos la variable solamente cuando tiene sentido:
- Cuando se pulsa una tecla se está añadiendo una
petición, con lo que es posible que el destino cambie.
- Cuando se para en un piso se está eliminando una
petición de la tabla, que además era el último
destino calculado.
- Consultamos dicha variable cada vez que es necesario tomar una
decisión. Por ejemplo, cuando el encoder nos avisa de que nos
aproximamos a una planta hay que decidir si se reduce la velocidad para
detener la cabina o va a pasar de largo.
Nota: No es posible parar en una
planta sin hacer el cambio a velocidad lenta previo, es decir, no se
puede parar en una planta si pulsamos el botón cuando la cabina
está más cerca que la distancia de frenada reglamentaria.