Robot de poursuite de ligne basé sur l'intelligence artificielle (IA)
Composants et fournitures
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
Applications et services en ligne
|
À propos de ce projet
Après avoir développé le robot Pick &Place basé sur Arduino, l'idée était de développer un robot suiveur de ligne basé sur l'IA intelligente artificielle. Développer un simple robot de suivi de ligne était bon pour le débutant, juste ce dont il avait besoin pour ajouter une sorte de capteurs infrarouges au lieu de servomoteurs dans mon récent projet. "L'idée était de tester mes compétences chez niveau avancé".
Même si c'était une tâche difficile, une sorte de recherche en ligne m'a beaucoup aidé.
Pourquoi un robot de suivi de ligne basé sur l'IA est-il nécessaire ?
Quand vient l'IA c'est la technologie récente qui a le potentiel de changer l'environnement mondial et donc la vie humaine efficace. Un rêve qui peut devenir réalité en mettant l'intelligence à nos robots pour comprendre les problèmes de la vie réelle et les résoudre en conséquence, ce rêve ne peut se réaliser que grâce à Intelligence Artificielle c'est pourquoi tout le monde en parle.
- Il peut être utilisé dans la chaîne d'assemblage/de production.
- Autobus de prise en charge des passagers.
Bien que mon prototype de robot puisse être un peu intelligent, ce n'est qu'un début pour moi d'orienter mes projets vers l'IA. Ainsi, ici, je vais essayer d'expliquer étape par étape le développement d'un robot de suivi de ligne utilisant l'intelligence artificielle dans un langage assez simple. En raison d'un manque de ressources, je ne construirai pas de robot mais je peux très bien expliquer comment pouvez-vous le construire en faisant une sorte de recherche. J'essaierai de fournir des codes précis qui fonctionneront sur votre IDE Arduino, afin que vous pouvez développer votre premier robot IA assez facilement.
Vous pouvez me soutenir sur Patreon d'ici :http://bit.ly/31NfQ6A
"Vous pouvez trouver l'appareil aux tarifs les moins chers aux USA sur la description de cette vidéo il suffit de cliquer sur le vidéo ."
Venons-en maintenant au projet "Un robot suiveur de ligne basé sur l'IA !".
UTILISATION DE CAPTEURS INFRAROUGES
Notre projet comprend 7 capteurs infrarouges comme indiqué sur le schéma.
- 5 pour le contrôle PID
- 1 pour la détection de gauche
- un autre pour la détection du côté droit.
Rôle des 5 capteurs de contrôle PID :Ces 5 capteurs seront utilisés pour générer respectivement une sortie numérique haute ou basse (1, 0).
Le capteur centré par rapport à la ligne noire, seul ce capteur produira HIGH (1). Le résultat également possible de ce capteur pourrait être :-
- 10000
- 11000
- 01000
- 01100
- 00100
- 00110
- 00010
- 00011
- 00001
- 00000
- 11111
Viennent maintenant les deux autres capteurs pour les résultats possibles à gauche et à droite
Capteur extrême gauche :sortie analogique haute ou basse
Capteur extrême gauche :sortie analogique haute ou basse
Pour stocker la valeur de 5 capteurs créons une variable tableau.
dans LFSensor[5]={1,1,1,1,1} ;
Pour stocker la valeur des capteurs gauche et droit nous utiliserons un entier
int farleft_sensor=0 ;
Comme nous le savons, nous avons 5 capteurs qui peuvent être utilisés pour stocker le chemin gauche et droit emprunté par le robot dans un tableau. Alors
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);
RÈGLE DE LA MAIN GAUCHE DU LABYRINTHE :
regarder cette vidéo pour comprendre la règle de la main gauche du labyrinthe
En bref, la règle de la main gauche peut être décrit comme :
- Placez votre main gauche sur le mur.
- Commencez à avancer
- À chaque intersection et dans tout le labyrinthe, gardez votre main gauche en contact avec le mur sur votre gauche.
- Finalement, vous arriverez au bout du labyrinthe. Vous n'emprunterez probablement pas le chemin le plus court et le plus direct, mais vous y arriverez.
Donc, la clé ici est d'identifier les intersections , définissant le cours à suivre en fonction des règles ci-dessus. Concrètement, dans notre genre de labyrinthe 2D, nous pouvons trouver 8 types d'intersections différents (voir la première image ci-dessus) :
En regardant la photo, on peut se rendre compte que les actions possibles aux intersections sont :
À une "Croix "
- Aller à gauche, ou
- Aller à droite, ou
- Allez tout droit
- À une "Croix "Aller à gauche, ou aller à droite, ou aller tout droit
Dans un "T " :
- Aller à gauche, ou
- Aller à droite
- Dans un "T " : Aller à gauche ou à droite
À un "Droit seulement " :
- Aller à droite
- À un "Droit seulement " : Aller à droite
À un "À gauche seulement " :
- Aller à gauche
- Dans un "À gauche seulement " : Aller à gauche
A "Droite ou Gauche " :
- Aller à gauche, ou
- Allez tout droit
- À "Droite ou gauche " : Aller à gauche ou aller tout droit
À "Droite ou à droite " :
- Aller à droite, ou
- Allez tout droit
- À "Droite ou à droite " : Aller à droite ou aller tout droit
Dans une "impasse " :
- Revenir en arrière (« Demi-tour »)
- Dans une "Impasse ":Retour ("De retour")
À "Fin du labyrinthe " :
- Arrêtez
- À "Fin du labyrinthe " : Stop
Mais, en appliquant la "règle de la main gauche", les actions seront réduites à une option chacune :
- À une "croix" :aller à gauche
- À un "T" :aller à gauche
- À "Droite uniquement" :aller à droite
- À une "gauche uniquement" :aller à gauche
- À une "Droite ou à gauche" :aller à gauche
- À un "droit ou à droite" :allez tout droit
- Dans une « impasse » :Revenir en arrière (« Tu fais demi-tour »)
- Au "Fin du Labyrinthe" : Stop
Nous y sommes presque! "Soyez calme !"
Lorsque le robot atteint une « impasse » ou la « fin d'un labyrinthe », il est facile de les identifier, car il n'existe pas de situations ambiguës (nous avons déjà implémenté ces actions sur le robot suiveur de ligne, vous vous souvenez ?). Le problème est lorsque le robot trouve une " LIGNE " par exemple car une ligne peut être une " Croix " (1) ou un " T " (2). De plus, lorsqu'il atteint un "virage à gauche ou à droite", il peut s'agir d'un simple virage (options 3 ou 4) ou d'options pour aller tout droit (5 ou 6). Pour découvrir exactement de quel type d'intersection se trouve le robot, une étape supplémentaire doit être franchie :le robot doit parcourir un « pouce supplémentaire » et voir ce qui va suivre (voir la deuxième image ci-dessus, à titre d'exemple).
Ainsi, en termes de flux, les actions possibles peuvent maintenant être décrites comme :
Dans une "IMPASSE" :
- Revenir en arrière (« Demi-tour »)
- Dans une " IMPASSE " :Revenez en arrière (" Demi-tour ")
À une "LIGNE" :
- Courez un pouce supplémentaire
- S'il y a une ligne :C'est une "Croix" ==> Aller à GAUCHE
- S'il n'y a pas de ligne :c'est un "T" ==> Aller à GAUCHE
- S'il y a une autre ligne :c'est la "Fin du Labyrinthe" ==> STOP
- À une "LIGNE" :Courez un pouce supplémentaire S'il y a une ligne :C'est une « Croix » ==> Aller à GAUCHES'il n'y a pas de ligne :c'est un « T » ==> Aller à GAUCHE S'il y a une autre ligne :c'est la « Fin du Labyrinthe » ==> ARRÊTER
À un « VIRANT À DROITE » :
- Courez un pouce supplémentaire
- s'il y a une ligne :c'est une ligne droite ou droite ==> Aller TOUT DROIT
- S'il n'y a pas de ligne :c'est une droite uniquement ==> Aller à DROITE
- À un « VIRANT À DROITE » :Courez un centimètre supplémentaire s'il y a une ligne :C'est une Droite ou à Droite ==> Aller TOUT DROITS'il n'y a pas de ligne :c'est une Droite Seulement ==> Aller à DROITE
Lors d'un "VIRAGE A GAUCHE" :
- Courez un pouce supplémentaire
- s'il y a une ligne :c'est une ligne droite ou à GAUCHE ==> Aller à GAUCHE
- S'il n'y a pas de ligne :c'est une GAUCHE uniquement ==> Aller à GAUCHE
- Lors d'un « VIREMENT À GAUCHE » :Courez un centimètre supplémentaire s'il y a une ligne :C'est une Droite ou GAUCHE ==> Aller à GAUCHES'il n'y a pas de ligne :c'est une GAUCHE Seulement ==> Aller à GAUCHE
Notez qu'en fait, en cas de « TOUR À GAUCHE », vous pouvez sauter le test, car vous prendrez de toute façon à GAUCHE. J'ai laissé l'explication plus générique uniquement pour plus de clarté. Au vrai code, je sauterai ce test.
Voici quelques problèmes qui peuvent survenir lors de la réalisation de ce projet.
Sujet connexe
RÉSOLUTION DU PROBLÈME DU PILOTE Arduino
regarder cette vidéo pour résoudre le problème du pilote Arduino :
Code
- RoBot_Maze_Solve_2
- Fonctions
- Fonctions générales.
- sensorFuntions
- RobotDefines.h
- MotorFuntions
RoBot_Maze_Solve_2C/C++
FonctionsC/C++
void mazeSolve(void){ while (!status) { readLFSsensors(); switch (mode) { case NO_LINE:motorStop(); allerEtTourner (GAUCHE, 180); recIntersection('B'); Pause; case CONT_LINE :runExtraInch(); readLFSsensors(); if (mode !=CONT_LINE) {goAndTurn (LEFT, 90); recIntersection('L');} // ou c'est un "T" ou une "Croix"). Dans les deux cas, va à GAUCHE else mazeEnd(); Pause; cas RIGHT_TURN:runExtraInch(); readLFSsensors(); if (mode ==NO_LINE) {goAndTurn (RIGHT, 90); recIntersection('R');} else recIntersection('S'); Pause; cas LEFT_TURN :goAndTurn (LEFT, 90); recIntersection('L'); Pause; case FOLLOWING_LINE:followLine(); Pause; } }}//---------------------------------------------- void recIntersection(char direction){ path[pathLength] =direction; // Stocke l'intersection dans la variable de chemin. pathLength ++; simplifierPath(); // Simplifier le chemin appris.}//----------------------------------------- ------void mazeEnd(void){ motorStop(); BT1.print("La fin ==> Chemin :"); for(int i=0;i"); Serial.println(pathLength); état =1 ; mode =ARRÊTÉ;}//-------------------------------------------- ---void followLine(void){ //readLFSsensors(); calculerPID(); motorPIDcontrol(); }//------------------------------------------------ -------------------------------------------// Simplification du chemin. La stratégie est que chaque fois que nous rencontrons une// séquence xBx, nous pouvons la simplifier en supprimant l'impasse. Par// exemple, LBL -> S, car un seul S contourne l'impasse // représentée par LBL.void simplifierPath(){ // ne simplifie le chemin que si l'avant-dernier virage était un 'B' if( pathLength <3 || path[pathLength-2] !='B') return ; int totalAngle =0; int je; for(i=1;i<=3;i++) { switch(path[pathLength-i]) { case 'R':totalAngle +=90; Pause; cas 'L' :totalAngle +=270 ; Pause; cas 'B' :totalAngle +=180 ; Pause; } } // Récupère l'angle sous la forme d'un nombre compris entre 0 et 360 degrés. TotalAngle =TotalAngle % 360 ; // Remplace tous ces tours par un seul. switch(totalAngle) { case 0:path[pathLength - 3] ='S'; Pause; cas 90 :path[pathLength - 3] ='R' ; Pause; cas 180 :path[pathLength - 3] ='B' ; Pause; cas 270 :path[pathLength - 3] ='L' ; Pause; } // Le chemin est maintenant plus court de deux étapes. pathLength -=2; } //------------------------------------------------------------ ------------------------------------------------ void mazeOptimization ( void){ while (!status) { readLFSsensors(); switch (mode) { case FOLLOWING_LINE:followLine(); Pause; case CONT_LINE :if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (chemin[cheminIndex]); pathIndex++;} break; case LEFT_TURN :if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (chemin[cheminIndex]); pathIndex++;} break; case RIGHT_TURN :if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (chemin[cheminIndex]); pathIndex++;} break; } } }//----------------------------------------------- --------void mazeTurn (char dir) { switch(dir) { case 'L':// Tourner à gauche goAndTurn (LEFT, 90); Pause; case 'R' :// Tourner à droite goAndTurn (RIGHT, 90); Pause; case 'B' :// Revenir en arrière goAndTurn (RIGHT, 800); Pause; case 'S' :// Aller tout droit runExtraInch(); Pause; }}
Fonctions Générales.C/C++
void ledBlink(int times){ for (int i =0; i0) { Serial.print("Commande reçue de BT ==> "); Serial.println(appareil); commande =appareil ; appareil ="" ; //Réinitialiser la variable BT1.flush(); } }//---------------------------------------------- ---------------------------void manualCmd(){ switch (command[0]) { case 'g':mode =FOLLOWING_LINE; Pause; cas 's' :motorStop(); // éteindre les deux moteurs break ; cas 'f' :motorForward(); Pause; cas 'r':motorTurn(RIGHT, 30); Arrêt moteur(); Pause; cas 'l' :motorTurn(LEFT, 30); Arrêt moteur(); Pause; cas 'b' :motorBackward(); Pause; cas 'p' :Kp =commande[2] ; Pause; cas 'i' :Ki =commande[2] ; Pause; cas 'd' :Kd =commande[2] ; Pause; }}//---------------------------------------------- --------------------------void sendBTdata (int data) // envoie des données à BT{ digitalWrite (ledPin, HIGH); BT1.print("Données d'Arduino"); BT1.print(données); BT1.print(" xx"); BT1.println('\n'); digitalWrite (ledPin, LOW);}//----------------------------------------- ----------------void calculerPID(){ P =erreur; I =I + erreur ; D =erreur-précédenteError ; Valeur PID =(Kp*P) + (Ki*I) + (Kd*D) ; erreur précédente =erreur;}//-------------------------------------------- -------------void checkPIDvalues(){ BT1.print("PID:"); BT1.print(Kp); BT1.print(" - "); BT1.print(Ki); BT1.print(" - "); BT1.println(Kd); Serial.print("PID:"); Serial.print(Kp); Serial.print(" - "); Serial.print(Ki); Serial.print(" - "); Serial.println(Kd); }//------------------------------------------------ void testLineFollowSensors(){ int LFS0 =digitalRead(lineFollowSensor0) ; int LFS1 =digitalRead(lineFollowSensor1) ; int LFS2 =digitalRead(lineFollowSensor2) ; int LFS3 =digitalRead(lineFollowSensor3) ; int LFS4 =digitalRead(lineFollowSensor4) ; Serial.print ("LFS:L 0 1 2 3 4 R ==> "); Serial.print (LFS0) ; Serial.print (" "); Serial.print (LFS1) ; Serial.print (" "); Serial.print (LFS2) ; Serial.print (" "); Serial.print (LFS3) ; Serial.print (" "); Serial.print (LFS4) ; Serial.print (" ==> "); Serial.print (" P:"); Serial.print (P); Serial.print (" I:"); Serial.print (I); Serial.print (" D:"); Serial.print (D); Serial.print (" PID:"); Serial.println (PIDvalue);}
sensorFuntionsC/C++
RobotDefines.hC/C++
int mode =0;# définit STOPPED 0# définit FOLLOWING_LINE 1# définit NO_LINE 2# définit CONT_LINE 3# définit POS_LINE 4# définit RIGHT_TURN 5# définit LEFT_TURN 6const int power =250;const int iniMotorPower =250;const int adj =0;float adjTurn =8;int extraInch =200;int adjGoAndTurn =800;const int ledPin =13;const int buttonPin =9;// LFSensor plus à gauche est "0"const int lineFollowSensor0 =12; const int lineFollowSensor1 =18 ; const int lineFollowSensor2 =17 ; const int lineFollowSensor3 =16;const int lineFollowSensor4 =19;const int farRightSensorPin =0; //Broche analogique A0const int farLeftSensorPin =1; //Broche analogique A0const int THRESHOLD =150;int farRightSensor =0;int farLeftSensor =0;int LFSensor[5]={0, 0, 0, 0, 0};// contrôleur PIDfloat Kp=50;float Ki=0;float Kd=0;float error=0, P=0, I=0, D=0, PIDvalue=0;float previousError=0, previousI=0;#define RIGHT 1#define LEFT -1Servo leftServo;Servo rightServo; //------------------------------------------------ -//Définitions et variables spécifiques de Maze Phase 2 (optimisation) char dir ; // La variable chemin stockera le chemin que le robot a pris:// 'L' pour gauche// 'R' pour droite// 'S' pour tout droit (passant tout droit à travers une intersection)// 'B' pour retour (U-turn)char path[100] ="";char non signé pathLength =0; // la longueur du pathint pathIndex =0;unsigned int status =0; // résolution =0; atteindre la fin =1
MotorFuntionsC/C++
void motorStop(){ leftServo.writeMicroseconds(1500); rightServo.writeMicroseconds(1500); retard(200);}//------------------ --- void motorForward(){ leftServo.writeMicroseconds(1500 - (power+adj)); rightServo.writeMicroseconds(1500 + puissance);}//-------------------------------------- -------void motorBackward(){ leftServo.writeMicroseconds(1500 + puissance); rightServo.writeMicroseconds(1500 - puissance);}//-------------------------------------- -------void motorFwTime (temps entier non signé){ motorForward(); temporisation); Arrêt moteur();}//--------------------------------------------- --void motorBwTime (temps int non signé){ motorBackward(); temporisation); Arrêt moteur();}//--------------------------------------------- -----void motorTurn(int direction, int degrés){ leftServo.writeMicroseconds(1500 - (iniMotorPower+adj)*direction); rightServo.writeMicroseconds(1500 - iniMotorPower*direction); délai (arrondi(adjTurn*degrees+20)); Arrêt moteur();}//--------------------------------------------- --------void motorPIDcontrol(){ int leftMotorSpeed =1500 - (iniMotorPower+adj) - PIDvalue ; int rightMotorSpeed =1500 + iniMotorPower - PIDvalue ; // La vitesse du moteur ne doit pas dépasser la contrainte maximale de la valeur PWM (leftMotorSpeed, 1000, 2000); contraindre (rightMotorSpeed, 1000, 2000); leftServo.writeMicroseconds(leftMotorSpeed); rightServo.writeMicroseconds(rightMotorSpeed); //Serial.print (PIDvalue); //Serial.print (" ==> Gauche, Droite :"); //Serial.print (leftMotorSpeed); //Série.print (" "); //Serial.println (rightMotorSpeed);}//-------------------------------------- -------------void runExtraInch(void){ motorPIDcontrol(); retard (extraPouce); Arrêt moteur();}//--------------------------------------------- --------void goAndTurn(int direction, int degrés){ motorPIDcontrol(); delay(adjGoAndTurn); motorTurn(direction, degrés);}
Schémas
Processus de fabrication
- Robot évitant les murs basé sur Raspberry Pi – FabLab NerveCentre
- Un robot mobile avec un évitement d'obstacles basé sur la vision
- Robot « artistique » à NPE
- Automatisation :ligne de robot repensée
- Bosch ajoute l'intelligence artificielle à l'industrie 4.0
- Ligne de robots SCARA élargie
- L'intelligence artificielle est-elle une fiction ou une mode ?
- L'intelligence artificielle aide le robot à reconnaître les objets au toucher
- Robots d'intelligence artificielle