Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Maze Solver Robot, usando inteligencia artificial

Componentes y suministros

Arduino Nano R3
× 1
Sensor SparkFun RedBot - Seguidor de línea
× 1
ZX03 (basado en TCRT5000) Sensores infrarrojos reflectantes (salida analógica)
× 2
dispositivo Android
× 1
Servo de rotación continua RobotGeek
× 2
portapilas 4xAA
× 2

Aplicaciones y servicios en línea

Arduino IDE
MIT App Inventor 2

Acerca de este proyecto

Introducción

Este tutorial fue desarrollado sobre mi último proyecto:Line Follower Robot - Control PID - Configuración de Android. Una vez que tenga un robot con capacidades de seguimiento de línea, el siguiente paso natural es darle cierto grado de inteligencia. Entonces, nuestro querido "Rex, el Robot" ahora intentará encontrar cómo escapar de un "laberinto" de la manera más corta y rápida (por cierto, odia al Minotauro.

Para empezar, ¿cuál es la diferencia entre Maze y Laberinto ? Según http://www.labyrinthos.net, en el mundo de habla inglesa a menudo se considera que para ser calificado como un laberinto, un diseño debe tener opciones en el camino. Claramente, esto incluirá muchas de las instalaciones modernas en parques de entretenimiento y atracciones turísticas, incluido nuestro laberinto 2D aquí. El consenso popular también indica que los laberintos tienen un camino que conduce inexorablemente desde la entrada a la meta, aunque a menudo por las rutas más complejas y tortuosas.

La mayoría de los laberintos, por complejo que pueda parecer su diseño, se formaron esencialmente a partir de una pared continua con muchas uniones y ramificaciones. Si el muro que rodea la meta de un laberinto está conectado al perímetro del laberinto de la entrada, el laberinto siempre se puede resolver manteniendo una mano en contacto con el muro, por muchos desvíos que pueda implicar. Esos laberintos "simples" se conocen correctamente como " simplemente conectados "o" laberinto perfecto "o, en otras palabras, que contengan sin bucles .

Volviendo a nuestro proyecto, se dividirá en dos partes (o " pasa "):

  • (primer pase) :El robot encuentra la salida de un " laberinto perfecto desconocido ". No importa dónde lo pongas dentro del laberinto, encontrará una" solución ".
  • (Segundo pase) :Una vez que el robot encontró una posible solución de laberinto, debería optimizar su solución encontrando la " ruta más corta de principio a fin ".

El video a continuación mostrará un ejemplo de Rex encontrando la salida. La primera vez que el robot explora el laberinto, por supuesto, perderá mucho tiempo " pensando "sobre qué hacer en cualquier intersección. Al probar las numerosas posibilidades, tomará varios caminos equivocados y callejones sin salida, lo que lo obligará a correr caminos más largos y realizar giros en U innecesarios" U-Giros ". Durante este" 1er paso " , el robot acumulará experiencias ", tomando notas "sobre las diferentes intersecciones y la eliminación de las ramas defectuosas. En su" 2nd Pass ", el robot va directo y rápidamente hasta el final sin ningún error o duda. A lo largo de este tutorial, exploraremos en detalle cómo hacerlo:

Paso 1:lista de materiales

La lista de materiales es básicamente la misma que se usó con el robot seguidor de línea, excepto que incluí 2 sensores adicionales para una mejor precisión en la detección de las intersecciones IZQUIERDA y DERECHA:

El robot final sigue siendo muy barato (alrededor de $ 85,00):

Cuerpo (puede adaptarlo a sus necesidades o materiales disponibles):

  • 2 X cuadrados de madera (80X80 mm)
  • 3 X clips de carpeta
  • 2 X ruedas de madera (diámetro:50 mm)
  • 1 X Lanzador de bolas
  • 9 X bandas elásticas
  • Tira de marco de comando de 3M
  • Juntas de plástico para fijar el sensor
  • BreadBoard y cableado
  • 2 X juegos de baterías de hidruro metálico 4XNi (5V cada juego)
  • 2 X SM-S4303R Servo de plástico de 360 ​​grados de rotación continua
  • Arduino Nano
  • Módulo Bluetooth HC-06
  • 5 sensores de línea X (módulo de sensor de seguimiento de seguimiento de línea infrarrojo TCRT5000 4CH + 1 sensor de seguimiento independiente)
  • 2 X ZX03 (basado en TCRT5000) Sensores infrarrojos reflectantes (salida analógica)
  • 1 LED
  • 1 botón

Nota :Usé el ítem 7 anterior con salida analógica, porque no tenía a mano sensores con salida digital como los del ítem 6. Lo ideal es tener todos los sensores iguales, si es posible. También probé el proyecto manteniendo solo los 5 sensores originales. Funcionará, pero requiere ajustes más sensibles para descubrir intersecciones.

Paso 2:cambios en el cuerpo

Retire el conjunto original de 5 sensores de seguimiento de línea y arregle el nuevo " Far LEFT" y " Extremo DERECHA "Sensores reflectantes en cada extremo de la barra de plástico de soporte. Se recomienda tener los 7 sensores lo más alineados posible.

Paso 3:instalar y probar los nuevos sensores

La nueva matriz de ahora 7 sensores , está montado de manera que los 5 originales se utilizan exclusivamente para el control PID (y la detección de la "línea completa", explicado más adelante) y los 2 nuevos, que quedan para ser utilizados exclusivamente para la detección de intersecciones IZQUIERDA y DERECHA.

Como repaso rápido, recordemos cómo funcionan los 5 sensores "digitales" originales:

Si un sensor está centrado con relación a la línea negra, solo ese sensor específico producirá un ALTO. Por otro lado, el espacio entre sensores debe calcularse para permitir que 2 sensores puedan cubrir todo el ancho de la línea negra simultáneamente, produciendo también una señal ALTA en ambos sensores.

Cómo funcionan los 2 nuevos sensores "analógicos":

Si uno de los sensores está centrado con relación a la línea negra, la salida será un valor analógico, generalmente produciendo una salida en Arduino ADC por debajo de "100" (recuerde que el ADC produce una salida de 0 a 1023). Con superficies más claras, el valor de salida será mayor (probé de 500 a 600 sobre papel blanco, por ejemplo). Este valor debe probarse en diferentes situaciones de luz y materiales de superficie para definir la constante UMBRAL correcta que se utilizará en su caso (vea la imagen aquí).

Mirando el código de Arduino, cada uno de los sensores se definirá con un nombre específico (considere que el sensor de seguimiento de línea original más a la izquierda debe asignarse con una etiqueta " 0 "):

  const int lineFollowSensor0 =12; // Usando inputconst digital int lineFollowSensor1 =18; // Usando el pin analógico A4 como entrada digitalconst int lineFollowSensor2 =17; // Usando el Pin Analógico A3 como Entrada Digitalconst int lineFollowSensor3 =16; // Usando el pin analógico A2 como entrada digitalconst int lineFollowSensor4 =19; // Usando el Pin analógico A5 como inputconst digital int farRightSensorPin =0; // Pin analógico A0const int farLeftSensorPin =1; // Pin analógico A1  

Para recordar, las posibles salidas de la matriz de sensores originales de 5 cuando se sigue una línea son:

  1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0  

Con la adición de los 2 nuevos, sus posibles resultados son:

  • Sensor lejano IZQUIERDO:Salida analógica mayor o menor que un UMBRAL
  • Sensor de extrema DERECHA:Salida analógica mayor o menor que un UMBRAL

Para almacenar los valores de cada sensor, se crea una variable de matriz para los 5 sensores digitales originales:

  int LFSensor [5] ={0, 0, 0, 0, 0};  

Y dos variables enteras para los 2 nuevos sensores analógicos:

  int farRightSensor =0; int farLeftSensor =0;  

Cada posición del arreglo y variables se actualizará constantemente con la salida de cada uno de los sensores:

  LFSensor [0] =digitalRead (lineFollowSensor0); LFSensor [1] =digitalRead (lineFollowSensor1); LFSensor [2] =digitalRead (lineFollowSensor2); LFSensor [3] =digitalRead (lineFollowSensor3); LFSensor [4] =digitalRead (lineFollowSensor4); farRightSensor =analogRead (farRightSensorPin); farLeftSensor =analogRead (farLeftSensorPin);  

Tener 5 sensores, como se vio en el proyecto Robot Follower Line, permite generar una "variable de error" que ayudará a controlar la posición del robot sobre la línea. Además, se utilizará una variable llamada "modo" para la definición si el robot está siguiendo una línea . , sobre una línea continua , una intersección o sin línea en absoluto.

Esta variable " modo "también se utilizará con el" Extremo IZQUIERDA / DERECHA "sensores. Para la representación, consideremos los sensores del extremo izquierdo y derecho que tienen 3 estados posibles:

  • H (más alto que UMBRAL),
  • L (menor que UMBRAL) y
  • X (irrelevante).

Para las salidas digitales, será el habitual 0, 1 y también introduciremos la X:

  • H 0 X X X X L ==> modo =RIGHT_TURN; error =0; (vea el ejemplo en la imagen de arriba)
  • L X X X X 0 H ==> modo =LEFT_TURN; error =0;
  • X 0 0 0 0 0 X ==> modo =NO_LINE; error =0;
  • H 0 0 0 0 1 H ==> modo =FOLLOWING_LINE; error =4;
  • H 0 0 0 1 1 H ==> modo =FOLLOWING_LINE; error =3;
  • H 0 0 0 1 0 H ==> modo =FOLLOWING_LINE; error =2;
  • H 0 0 1 1 0 H ==> modo =FOLLOWING_LINE; error =1;
  • H 0 0 1 0 0 H ==> modo =FOLLOWING_LINE; error =0;
  • H 0 1 1 0 0 H ==> modo =FOLLOWING_LINE; error =-1;
  • H 0 1 0 0 0 H ==> modo =FOLLOWING_LINE; error =-2
  • H 1 1 0 0 0 H ==> modo =FOLLOWING_LINE; error =-3;
  • H 1 0 0 0 0 H ==> modo =FOLLOWING_LINE; error =-4;
  • X 1 1 1 1 1 X ==> modo =CONT_LINE; error =0;

Entonces, implementando la lógica anterior en la función:

  void readLFSsensors ()  

devolverá las variables " modo "y" error "que se utilizará en la lógica del programa. Es importante probar la lógica de los sensores antes de seguir con el proyecto. La función de abajo está incluida en el código y se puede utilizar con fines de prueba:

  void testSensorLogic (void) {Serial.print (farLeftSensor); Serial.print ("<==IZQUIERDA DERECHA ==>"); Serial.print (farRightSensor); Serial.print ("modo:"); Serial.print (modo); Serial.print ("error:"); Serial.println (error);}  

Paso 4:Resolver el laberinto:la regla de la mano izquierda

Como se discutió en la introducción, la mayoría de los laberintos, por más complejos que parezca su diseño, se formaron esencialmente a partir de una pared continua con muchas uniones y ramificaciones. Si el muro que rodea la meta de un laberinto está conectado al perímetro del laberinto de la entrada, el laberinto siempre se puede resolver manteniendo una mano en contacto con el muro, por muchos desvíos que pueda implicar. Estos laberintos "simples" se conocen correctamente como " simplemente conectados . "

Buscando en Wikipedia, aprendemos que:

En resumen, la Regla de la mano izquierda se puede describir como:

En cada intersección y en todo el laberinto, mantenga su mano izquierda tocando la pared a su izquierda.

  • Coloque su mano izquierda en la pared.
  • Empiece a caminar hacia adelante
  • Eventualmente, llegarás al final del laberinto. Probablemente no irá por el camino más corto y directo, pero lo logrará.

Entonces, la clave aquí es identificar las intersecciones , definiendo qué curso tomar en base a las reglas anteriores. Específicamente en nuestro tipo de laberinto 2D, podemos encontrar 8 tipos diferentes de intersecciones (ver la primera imagen de arriba):

Mirando la imagen, podemos darnos cuenta de que las posibles acciones en las intersecciones son:

1. En una " Cruz ":

  • Vaya a la izquierda o
  • Vaya a la derecha o
  • Siga recto

2. En " T ":

  • Vaya a la izquierda o
  • Ir a la derecha

3. En " Solo a la derecha ":

  • Ir a la derecha

4. En " Solo a la izquierda ":

  • Ir a la izquierda

5. En " Recta o izquierda ":

  • Vaya a la izquierda o
  • Siga recto

6. En " Recta o derecha ":

  • Vaya a la derecha o
  • Siga recto

7. En un " callejón sin salida ":

  • Regresar ("giro en U")

8. En " Fin del laberinto ":

  • Deténgase

Pero, aplicando la "Regla de la mano izquierda", las acciones se reducirán a una opción cada una:

  • En una "cruz":ve a la izquierda
  • En una "T":ve a la izquierda
  • En "Solo a la derecha":vaya a la derecha
  • En "Solo a la izquierda":ve a la izquierda
  • En "Recta o izquierda":Vaya a la izquierda
  • En "Recta o derecha":siga recto
  • En un "callejón sin salida":retroceda ("giro en U")
  • En el "final del laberinto":deténgase

¡Estamos casi alli! "¡Cálmate!"

Cuando el robot llega a un "Dead End" o al "Fin de un Laberinto", es fácil identificarlos, porque no existen situaciones ambiguas (ya hemos implementado esas acciones en el Line Follower Robot, ¿recuerdas?). El problema es cuando el robot encuentra una "LÍNEA", por ejemplo, porque una línea puede ser una "Cruz" (1) o una "T" (2). También cuando llega a un "GIRO IZQUIERDA o DERECHA", esos pueden ser un simple giro (opciones 3 o 4) u opciones para ir recto (5 o 6). Para descubrir exactamente en qué tipo de intersección se encuentra el robot, se debe dar un paso adicional:el robot debe correr una "pulgada extra" y ver qué sigue (vea la segunda imagen de arriba, como ejemplo).

Entonces, en términos de flujo, las posibles acciones ahora se pueden describir como:

1. En un "FINAL MUERTO":

  • Regresar ("giro en U")

2. En una "LÍNEA": Ejecute una pulgada adicional

  • Si hay una línea:es una "Cruz" ==> Ir a la IZQUIERDA
  • Si no hay línea:es una "T" ==> Ir a la IZQUIERDA
  • Si hay otra línea:es el "Fin del laberinto" ==> DETENER

3. En un "GIRO A LA DERECHA": Corre una pulgada más

  • si hay una línea:es una recta o derecha ==> ir RECTA
  • Si no hay línea:es solo a la derecha ==> Ir a la DERECHA

4. En un "GIRO A LA IZQUIERDA": Corre una pulgada adicional

  • si hay una línea:es una recta o IZQUIERDA ==> Ve a la IZQUIERDA
  • Si no hay línea:es solo IZQUIERDA ==> Ir a IZQUIERDA

Tenga en cuenta que, de hecho, en caso de un "GIRO A LA IZQUIERDA", puede saltarse la prueba, porque tomará a la IZQUIERDA de todos modos. Dejé la explicación más genérica solo por claridad. En el código real, me saltaré esta prueba. La tercera imagen de arriba muestra un laberinto muy simple en el piso de mi laboratorio, usado para propósitos de prueba.

Paso 5:Implementación del algoritmo "Mano izquierda en la pared" en el código Arduino

Una vez que tengamos el readLFSsensors () función modificada para incluir los 2 sensores adicionales, podemos volver a escribir la función de bucle para ejecutar el algoritmo como se describe en el último paso:

  void loop () {readLFSsensors (); cambiar (modo) {caso NO_LINE:motorStop (); goAndTurn (IZQUIERDA, 180); descanso; caso CONT_LINE:runExtraInch (); readLFSsensors (); if (modo ==CONT_LINE) mazeEnd (); else goAndTurn (IZQUIERDA, 90); // o es una "T" o "Cruz"). En ambos casos, va al descanso IZQUIERDO; case RIGHT_TURN:runExtraInch (); readLFSsensors (); if (modo ==NO_LINE) goAndTurn (DERECHA, 90); descanso; case LEFT_TURN:goAndTurn (IZQUIERDA, 90); descanso; case FOLLOWING_LINE:nextLine (); descanso; }}  

Aquí aparecen algunas funciones nuevas:

  • siguienteLínea () es el mismo que se usa con el Robot de línea siguiente donde, si solo sigue una línea, debe calculatePID () ; y controlar los motores en función de los valores PID: motorPIDcontrol ();
  • runExtraInch (): empujará el robot hacia adelante solo un poco. La cantidad de tiempo que funcionará el robot dependerá del tiempo que utilice en la función de retardo, antes de ordenar al motor que se detenga.
  void runExtraInch (void) {motorPIDcontrol (); retraso (pulgada extra); motorStop ();}  
  • goAndTurn (dirección, ángulo): esta función especial es importante porque no puedes girar el robot tan pronto como te des cuenta del tipo de intersección que te encuentras. Recuerda que proyectamos un Robot Diferencial que al hacer giros "gira alrededor de su eje" y así, para moverse 90o y seguir la línea de manera continua, el centro de las ruedas debe estar alineado con el centro de intersección. Una vez que la línea de sensores está por delante de su eje, debe hacer avanzar el robot para alinearlos. La constante de tiempo adjGoAndTurn debe ajustarse dependiendo de la distancia entre el eje y la línea del sensor (" d "), la velocidad y el tamaño de las ruedas (consulte la ilustración de arriba).
  void goAndTurn (dirección int, grados int) {motorPIDcontrol (); retraso (adjGoAndTurn); motorTurn (dirección, grados);}  

En este punto, ¡el robot de hecho está "resolviendo un laberinto"! Acaba de terminar el "primer pase". No importa dónde empieces dentro de un laberinto, siempre llegarás al final.

A continuación, una prueba de esta fase del proyecto:

Paso 6:almacenamiento de la ruta

Consideremos el ejemplo que se muestra en la foto de arriba. En el punto de partida elegido, el robot encontrará 15 intersecciones antes de llegar al final del Laberinto:

  • Izquierda (L)
  • Atrás (B)
  • Izquierda (L)
  • Izquierda (L)
  • Izquierda (L)
  • Atrás (B)
  • Recto (S)
  • Atrás (B)
  • Izquierda (L)
  • Izquierda (L)
  • Atrás (B)
  • Recto (S)
  • Izquierda (L)
  • Izquierda (L)
  • Finalizar

Lo que se debe hacer en cualquiera de esas intersecciones es guardar cada acción realizada exactamente en la misma secuencia en que ocurre. Para eso, creemos una nueva variable (matriz) que almacenará la ruta que ha tomado el robot:

  char path [100] ="";  

También debemos crear 2 índices de variables para usar junto con la matriz:

  unsigned char pathLength =0; // la longitud del pathint pathIndex =0; // utilizado para llegar a un elemento de matriz específico.  

Entonces, si ejecutamos el ejemplo que se muestra en la imagen, terminaremos con:

  path =[LBLLLBSBLLBSLL] y pathLengh =14  

Paso 7:simplificar (optimizar) la ruta

Volvamos a nuestro ejemplo. Mirando el primer grupo de intersecciones, nos dimos cuenta de que la primera rama izquierda es de hecho un "callejón sin salida" y, por lo tanto, si el robot en lugar de un "Izquierda-Atrás-Izquierda" solo pasa directamente en esa primera intersección, mucha energía ¡y se ahorraría tiempo! En otras palabras, una secuencia "LBL" de hecho sería la misma que "S". Así es exactamente como se puede optimizar la ruta completa. Si analiza todas las posibilidades donde se utiliza un "giro en U", el conjunto de 3 intersecciones donde aparece este "giro en U" ("B") ("xBx") se puede reducir a solo uno.

Lo anterior es solo un ejemplo, a continuación puede encontrar la lista completa de posibilidades (pruébelo):

  • LBR =B
  • LBS =R
  • RBL =B
  • SBL =R
  • SBS =B
  • LBL =S

Tomando el camino completo o nuestro ejemplo, podemos reducirlo:

  ruta =[LBLLLBSBLLBSLL] ==> LBL =Spath =[SLLBSBLLBSLL] ==> LBS =Rpath =[SLRBLLBSLL] ==> RBL =Bpath =[SLBLBSLL] ==> LBL =Spath =[SSBSLL ] ==> SBS =Bpath =[SBLL] ==> SBL =Rpath =[RL]  

¡Increíble! Mirando el ejemplo, es muy claro que si el robot gira a la DERECHA en la primera intersección y luego a la IZQUIERDA, ¡llegará al final del laberinto en el camino más corto!

El código total de First Path of Maze Solver se consolidará en la función mazeSolve () . Esta función es de hecho la función loop () utilizada antes, pero incorpora todos esos pasos de almacenamiento y optimización de ruta. Cuando finalizó la primera ruta, la matriz ruta [] tendrá la ruta optimizada. Se introduce una nueva variable:

  unsigned int status =0; // resolviendo =0; llegar a Maze End =1  

Debajo de la función Primera ruta:

  void mazeSolve (void) {while (! status) {readLFSsensors (); cambiar (modo) {caso NO_LINE:motorStop (); goAndTurn (IZQUIERDA, 180); recIntersection ('B'); descanso; caso CONT_LINE:runExtraInch (); readLFSsensors (); if (modo! =CONT_LINE) {goAndTurn (IZQUIERDA, 90); recIntersection ('L');} // o es una "T" o una "Cruz"). En ambos casos, va a la IZQUIERDA else mazeEnd (); descanso; case RIGHT_TURN:runExtraInch (); readLFSsensors (); if (modo ==NO_LINE) {goAndTurn (DERECHA, 90); recIntersection ('R');} else recIntersection ('S'); descanso; case LEFT_TURN:goAndTurn (IZQUIERDA, 90); recIntersection ('L'); descanso; case FOLLOWING_LINE:nextLine (); descanso; }}}  

Aquí se introdujo una nueva función: recIntersection (dirección). Esta función se utilizará para almacenar la intersección y también para llamar a otra función simplifyPath () , eso reducirá el grupo de 3 intersecciones que implican un "giro en U" como vimos antes.

  void recIntersection (dirección de caracteres) {ruta [longitud de ruta] =dirección; // Almacena la intersección en la variable de ruta. pathLength ++; simplifyPath (); // Simplifica la ruta aprendida.}  

El CRÉDITO para simplifyPath ( ) es para Patrick McCabe para el código de resolución de ruta (para obtener más detalles, visite https://patrickmccabemakes.com). La estrategia de la simplificación de la ruta es que siempre que nos encontramos con una secuencia xBx, podemos simplificarla eliminando el callejón sin salida. Por ejemplo, LBL ==> S como vimos en el ejemplo.

  void simplifyPath () {// solo simplifica la ruta si el penúltimo turno fue una 'B' if (pathLength <3 || path [pathLength-2]! ='B') return; int totalAngle =0; int i; for (i =1; i <=3; i ++) {switch (path [pathLength-i]) {case 'R':totalAngle + =90; descanso; caso 'L':totalAngle + =270; descanso; caso 'B':totalAngle + =180; descanso; }} // Obtenga el ángulo como un número entre 0 y 360 grados. totalAngle =totalAngle% 360; // Reemplaza todos esos giros con uno solo. switch (totalAngle) {caso 0:ruta [rutaLongitud - 3] ='S'; descanso; caso 90:ruta [pathLength - 3] ='R'; descanso; caso 180:ruta [pathLength - 3] ='B'; descanso; caso 270:ruta [pathLength - 3] ='L'; descanso; } // La ruta ahora es dos pasos más corta. pathLength - =2; }  

Paso 8:Segundo paso:¡Resuelve el Laberinto lo más rápido posible!

El programa principal: loop () es tan simple como eso:

  bucle vacío () {ledBlink (1); readLFSsensors (); mazeSolve (); // Primera pasada para resolver el laberinto ledBlink (2); while (digitalRead (buttonPin) {} pathIndex =0; status =0; mazeOptimization (); // Segunda pasada:ejecuta el laberinto lo más rápido posible ledBlink (3); while (digitalRead (buttonPin) {} mode =STOPPED; status =0; // 1er paso pathIndex =0; pathLength =0;}  

Entonces, cuando finaliza el Primer Paso, lo que debemos hacer es alimentar al robot con la matriz de ruta optimizada. Comenzará a ejecutarse y cuando se encuentre una intersección, ahora definirá qué hacer en función de lo que está almacenado en ruta [] .

  void mazeOptimization (void) {while (! status) {readLFSsensors (); cambiar (modo) {caso FOLLOWING_LINE:nextLine (); descanso; caso CONT_LINE:if (pathIndex> =pathLength) mazeEnd (); else {mazeTurn (ruta [índice de ruta]); pathIndex ++;} descanso; case LEFT_TURN:if (pathIndex> =pathLength) mazeEnd (); else {mazeTurn (ruta [índice de ruta]); pathIndex ++;} descanso; case RIGHT_TURN:if (pathIndex> =pathLength) mazeEnd (); else {mazeTurn (ruta [índice de ruta]); pathIndex ++;} descanso; }}}  

Para ordenar qué hacer, una nueva función mazeTurn (ruta []) fue creado. La función mazeTurn (ruta []) será:

  void mazeTurn (char dir) {switch (dir) {case 'L':// Gire a la izquierda goAndTurn (IZQUIERDA, 90); descanso; case 'R':// Gire a la derecha goAndTurn (DERECHA, 90); descanso; case 'B':// Dar marcha atrás goAndTurn (DERECHA, 800); descanso; case 'S':// Ir directamente runExtraInch (); descanso; }}  

¡El segundo pase está hecho! El siguiente video muestra el ejemplo completo trabajado aquí, primera y segunda pasada. Debajo del código Arduino utilizado en este tutorial:

FV6XNJWINJ45XWM.ino F2FXS8MINJ45XX6.h FX5MHFMINJ45XX7.ino FT2S1WXINJ45XXA.ino F9IC3HQINJ45XXB.ino FU2HRXJINJ45XXV.ino

Paso 9:uso de Android para realizar ajustes

La aplicación de Android desarrollada para el proyecto de la siguiente línea también se puede utilizar aquí (si lo necesita, la aplicación de Android y su código están disponibles en:Robot seguidor de línea - Control PID - Configuración de Android. El código de Arduino presentado en el último paso ya incluye la comunicación con el dispositivo Android. Si no desea utilizar la aplicación de Android, no hay problema porque el código es " transparente ".

Usé mucho Android durante el proyecto para enviar datos de prueba desde el robot al dispositivo usando el " Message Received "Campo. Varias variables deben estar bien definidas para garantizar que el robot gire en el ángulo correcto. Las más importantes son las siguientes (las marcadas en negrita las cambié varias veces):

  const int adj =0; flotador adjTurn =8; int adjGoAndTurn =800; UMBRAL =150const int potencia =250; const int iniMotorPower =250; int extraInch =200;  

Paso 10:Conclusión

Esta es la segunda y última parte de un proyecto complejo, que explora la potencialidad de un robot seguidor de línea, donde Inteligencia Artificial (AI) se utilizaron conceptos simples para resolver un laberinto.

No soy una IA experto y en base a cierta información que obtuve de la web, entendí que lo que hizo nuestro pequeño Rex, el robot, resolver el laberinto podría considerarse una aplicación de IA. Echemos un vistazo a las 2 fuentes siguientes:

De Wikipedia:

O de este artículo universitario:"Robot de resolución de laberintos usando Freeduino y LSRB Algorithm International Journal of Modern Engineering Research (IJMER)"

Los archivos actualizados para este proyecto se pueden encontrar en GITHUB. Espero poder contribuir para que otros aprendan más sobre electrónica, robots, Arduino, etc. Para obtener más tutoriales, visite mi Blog:MJRoBot.org

¡Saludos desde el sur del mundo!

Gracias

Marcelo

Código

  • Fragmento de código n. ° 1
  • Fragmento de código n. ° 4
  • Fragmento de código n. ° 5
  • Fragmento de código n. ° 6
  • Fragmento de código n.º 7
  • Fragmento de código n. ° 8
  • Fragmento de código n. ° 12
  • Fragmento de código n. ° 13
  • Fragmento de código n. ° 14
  • Fragmento de código n. ° 15
  • Fragmento de código n. ° 16
  • Fragmento de código n. ° 17
Fragmento de código n. ° 1 Texto sin formato
 const int lineFollowSensor0 =12; // Usando inputconst digital int lineFollowSensor1 =18; // Usando el pin analógico A4 como entrada digitalconst int lineFollowSensor2 =17; // Usando el Pin Analógico A3 como Entrada Digitalconst int lineFollowSensor3 =16; // Usando el pin analógico A2 como entrada digitalconst int lineFollowSensor4 =19; // Usando el Pin analógico A5 como inputconst digital int farRightSensorPin =0; // Pin analógico A0const int farLeftSensorPin =1; // Pin analógico A1 
Fragmento de código n.º 4 Texto sin formato
 LFSensor [0] =digitalRead (lineFollowSensor0); LFSensor [1] =digitalRead (lineFollowSensor1); LFSensor [2] =digitalRead (lineFollowSensor2); LFSensor [3] =digitalRead (lineFollowSensor3); LFRead [4] =digitalRead (4] =digitalRead lineFollowSensor4); farRightSensor =analogRead (farRightSensorPin); farLeftSensor =analogRead (farLeftSensorPin); 
Fragmento de código n. ° 5 Texto sin formato
 void testSensorLogic (void) {Serial.print (farLeftSensor); Serial.print ("<==IZQUIERDA DERECHA ==>"); Serial.print (farRightSensor); Serial.print (" mode:"); Serial.print (mode); Serial.print (" error:"); Serial.println (error);}
Code snippet #6Plain text
void loop(){ readLFSsensors(); switch (mode) { case NO_LINE:motorStop(); goAndTurn (LEFT, 180); descanso; case CONT_LINE:runExtraInch(); readLFSsensors(); if (mode ==CONT_LINE) mazeEnd(); else goAndTurn (LEFT, 90); // or it is a "T" or "Cross"). In both cases, goes to LEFT break; case RIGHT_TURN:runExtraInch(); readLFSsensors(); if (mode ==NO_LINE) goAndTurn (RIGHT, 90); descanso; case LEFT_TURN:goAndTurn (LEFT, 90); descanso; case FOLLOWING_LINE:followingLine(); descanso; }}
Code snippet #7Plain text
void runExtraInch(void){ motorPIDcontrol(); delay(extraInch); motorStop();}
Code snippet #8Plain text
void goAndTurn(int direction, int degrees){ motorPIDcontrol(); delay(adjGoAndTurn); motorTurn(direction, degrees);}
Code snippet #12Plain text
void mazeSolve(void){ while (!status) { readLFSsensors(); switch (mode) { case NO_LINE:motorStop(); goAndTurn (LEFT, 180); recIntersection('B'); descanso; case CONT_LINE:runExtraInch(); readLFSsensors(); if (mode !=CONT_LINE) {goAndTurn (LEFT, 90); recIntersection('L');} // or it is a "T" or "Cross"). In both cases, goes to LEFT else mazeEnd(); descanso; case RIGHT_TURN:runExtraInch(); readLFSsensors(); if (mode ==NO_LINE) {goAndTurn (RIGHT, 90); recIntersection('R');} else recIntersection('S'); descanso; case LEFT_TURN:goAndTurn (LEFT, 90); recIntersection('L'); descanso; case FOLLOWING_LINE:followingLine(); descanso; }}} 
Code snippet #13Plain text
void recIntersection(char direction){ path[pathLength] =direction; // Store the intersection in the path variable. pathLength ++; simplifyPath(); // Simplify the learned path.}
Code snippet #14Plain text
void simplifyPath(){ // only simplify the path if the second-to-last turn was a 'B' if(pathLength <3 || path[pathLength-2] !='B') return; int totalAngle =0; int i; for(i=1;i<=3;i++) { switch(path[pathLength-i]) { case 'R':totalAngle +=90; descanso; case 'L':totalAngle +=270; descanso; case 'B':totalAngle +=180; descanso; } } // Get the angle as a number between 0 and 360 degrees. totalAngle =totalAngle % 360; // Replace all of those turns with a single one. switch(totalAngle) { case 0:path[pathLength - 3] ='S'; descanso; case 90:path[pathLength - 3] ='R'; descanso; case 180:path[pathLength - 3] ='B'; descanso; case 270:path[pathLength - 3] ='L'; descanso; } // The path is now two steps shorter. pathLength -=2; } 
Code snippet #15Plain text
void loop() { ledBlink(1); readLFSsensors(); mazeSolve(); // First pass to solve the maze ledBlink(2); while (digitalRead(buttonPin) { } pathIndex =0; status =0; mazeOptimization(); // Second Pass:run the maze as fast as possible ledBlink(3); while (digitalRead(buttonPin) { } mode =STOPPED; status =0; // 1st pass pathIndex =0; pathLength =0;}
Code snippet #16Plain text
void mazeOptimization (void){ while (!status) { readLFSsensors(); switch (mode) { case FOLLOWING_LINE:followingLine(); descanso; case CONT_LINE:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; case LEFT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; case RIGHT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; } } }
Code snippet #17Plain text
void mazeTurn (char dir) { switch(dir) { case 'L':// Turn Left goAndTurn (LEFT, 90); descanso; case 'R':// Turn Right goAndTurn (RIGHT, 90); descanso; case 'B':// Turn Back goAndTurn (RIGHT, 800); descanso; case 'S':// Go Straight runExtraInch(); descanso; }}
Github
https://github.com/Mjrovai/MJRoBot-Maze-Solverhttps://github.com/Mjrovai/MJRoBot-Maze-Solver

Esquemas

z7IdLkxL1J66qOtphxqC.fzz

Proceso de manufactura

  1. Robot que usa Raspberry Pi y Bridge Shield
  2. Robot controlado por gestos con Raspberry Pi
  3. Robot controlado por Wifi usando Raspberry Pi
  4. DETECCIÓN HUMANA DE ROBOT SONBI USANDO KINECT Y FRAMBUESA PI
  5. Bosch agrega inteligencia artificial a la industria 4.0
  6. ¿La inteligencia artificial es ficción o moda?
  7. La inteligencia artificial recibe un enorme impulso de Kubernetes
  8. La inteligencia artificial ayuda al robot a reconocer objetos al tacto
  9. Uso de inteligencia artificial para rastrear la deforestación
  10. Robots de inteligencia artificial
  11. ¿Por qué usar un robot colaborativo?