Vamos a redefinir completamente los estados del robot, pensando en la futura implementación de Flood Fill que se nos ha ocurrido, y teniendo en cuenta el camino guardado en el array de celdas. Los estados que tendremos ahora serían:
Quizá queda más claro así:
estado/paso | PASO_RECTO | PASO_DER | PASO_IZQ | PASO_STOP |
---|---|---|---|---|
PARADO | ||||
ESPERANDO | espera,DECIDE | espera,DECIDE | espera,DECIDE | espera,DECIDE |
DECIDE | arranca,AVANZANDO | gira_der,ESPERANDO | gira_izq,ESPERANDO | gira_180,FIN |
AVANZANDO | avanza,AVANZANDO | para,ESPERANDO | para,ESPERANDO | para,PARADO |
FIN | espera,PARADO | espera,PARADO | espera,PARADO | espera,PARADO |
.
Creemos que estos estados quedan bastante claros, aunque hay que hacer alguna pequeña trampa para
que funcione. Cuando se lanzan las acciones de giro, debemos evitar de alguna manera que después de
hacer la pausa lanzada en el estado ESPERANDO, cuando se vuelve a evaluar no se entre en un bucle infinito.
Podemos, o bien:
La segunda opción es la más fácil y rápida. Tiene el inconveniente que se pierde el camino original (todas las celdas del camino quedarán marcadas con PASO_RECTO). Pero por ahora no vemos que haya problema con eso.
Lo que tenemos pensado en la exploración es lo siguiente: cada vez que el robot esta en estado ESPERANDO, se hace un flood del laberinto y se marca el camino a la solución. El robot seguirá este camino, marcando las paredes que encuentre y teniendo en cuenta que una pared frontal puede cortar el camino. Esta comprobación se hará en DECIDE y en AVANZANDO (en DECIDE sólo hace falta para la casilla inicial). Cuando esto ocurra, se vuelve a hacer flood y se vuelve a ejecutar el camino. Eventualmente se encontrará la solución, aunque hay que seguir haciendo ese bucle flood/camino hasta que los pesos permanezcan inalterados en un recorrido completo desde la posición original hasta la solución.
Al hacer el algoritmo de flood únicamente al final de las rectas, podemos saltarnos caminos mejores si las celdas de la recta aún no fueron visitadas. Pero no creemos que eso sea un problema grave de tiempo ya que ese camino se seguiría inmediatamente después.
Una vez calculados todos los pesos de flood necesarios, se calcula el camino del origen a la solución y se ejecutan las acciones correspondientes, aunque en este caso los giros ya no serán en redondo, si no que se ejecutan sin detenciones tomando las curvas con un radio de 9cm.
Todo esto es muy bonito en la teoría pero veamos si somos capaces de hacer que el robot no choque con las paredes.
Así es como ha quedado la toma de decisiones de la siguiente acción (en el commit hay además algunos prints temporales, para depuración).
if (robot.estado == PARADO) {
motores_parar();
} else if (robot.estado == ESPERANDO) {
accion_ejecuta(ESPERA);
robot.estado = DECIDE;
} else if (robot.estado == DECIDE) {
switch (paso) {
case PASO_RECTO: accion_ejecuta(ARRANCA);
robot.estado = AVANZANDO;
break;
case PASO_DER: accion_ejecuta(GIRA_DER);
robot.estado = ESPERANDO;
robot.orientacion++;
laberinto_set_paso(robot.casilla, PASO_RECTO);
break;
case PASO_IZQ: accion_ejecuta(GIRA_IZQ);
Serial.println("GIRA_IZQ");
robot.estado = ESPERANDO;
robot.orientacion--;
laberinto_set_paso(robot.casilla, PASO_RECTO);
break;
case PASO_STOP: accion_ejecuta(GIRA_180);
Serial.println("GIRA_180");
robot.estado = FIN;
robot.orientacion--;
robot.orientacion--;
break;
}
} else if (robot.estado == AVANZANDO) {
Serial.println("E-AVANZANDO");
if (paso == PASO_RECTO) {
accion_ejecuta(AVANZA);
robot.estado = AVANZANDO;
} else {
accion_ejecuta(PARA);
robot.estado = ESPERANDO;
}
} else if (robot.estado == FIN) {
accion_ejecuta(ESPERA);
robot.estado = PARADO;
}
Hemos añadido algunos comandos para definir caminos por el puerto serie y en las primeras pruebas parece que funciona bien. Esto nos va a ser muy útil para mejorar la corrección de los sensores, ya que ahora podemos definir caminos sin depender de las paredes.