Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Manufacturing Technology >> Processus de fabrication

Robot suiveur de ligne - Contrôle PID - Configuration Android

Composants et fournitures

Arduino Nano R3
× 1
SparkFun RedBot Sensor - Line Follower
× 1
Module de capteur de suivi de ligne infrarouge TCRT5000 4CH
× 1
Appareil Android
× 1
Support de 4 piles AA
× 2
Servo à rotation continue RobotGeek
× 2

Applications et services en ligne

Arduino IDE
MIT App Inventor 2

À propos de ce projet

Le but de ce projet est de construire un robot suiveur de ligne avec contrôle PID. Nous utiliserons également un appareil Android pour configurer facilement les principaux paramètres de contrôle pour un réglage meilleur et rapide.

Ce projet est le premier d'un projet plus complexe en 2 parties où mon intention est d'explorer le potentiel des robots suiveurs de ligne. Sur la 2ème partie :Maze Solver Robot, utilisant l'intelligence artificielle avec Arduino, le robot explorera et résoudra des labyrinthes, en utilisant des techniques d'intelligence artificielle simples.

Ci-dessous, une vidéo montrant le robot suivant un circuit de ligne :

Étape 1 :Nomenclature

La liste des matériaux nécessaires est très simple et le robot final est très bon marché (environ 75,00 $) :

Corps (peut être adapté à vos besoins) :

  • 2 carrés de bois (80X80mm)
  • 3 pinces à reliure
  • 2 roues en bois (diamètre :50 mm)
  • 1 roulette à billes
  • 9 bandes élastiques
  • Bande de cadre de commande 3M
  • Joints en plastique pour la fixation du capteur
  • Planche à pain et câblage
  • 2 ensembles de batteries à hydrure métallique 4XNi (5 V chaque ensemble)
  • 2 X SM-S4303R Rotation Continue 360 ​​Degrés Servo Plastique
  • Arduino Nano
  • Module Bluetooth HC-06
  • 5 capteurs de ligne X (module de capteur suiveur de ligne infrarouge TCRT5000 4CH + 1 capteur de piste indépendant)
  • 1 LED
  • 1 bouton

Étape 2 : réglage des moteurs

Pour les moteurs, 2 servos continus (SM-S4303R) ont été utilisés. Ils seront "collés" ensemble pour former un bloc unique et solide comme vous pouvez le voir sur la photo (utilisez la bande 3M Command, de la colle ou du ruban adhésif double face). Ces servos fonctionneront à une vitesse donnée, définie par la largeur d'impulsion reçue sur son entrée de données. Pour ce servo spécifique, la largeur d'impulsion va de 1,0 ms (1 000 microsecondes) à 2,0 ms (2 000 microsecondes). D'autres servos peuvent fonctionner avec une largeur d'impulsion différente.

En regardant dans les détails :

  • Une impulsion de 1,5 ms positionnera le servo en position neutre, ou « arrêté ».
  • Une impulsion de 1,0 ms commandera le servo à pleine vitesse (environ 70 tr/min) dans une direction
  • Une impulsion de 2,0 ms à pleine vitesse dans la direction opposée.
  • Une impulsion entre 1,0 et 1,5 ms ou 1,5 ms et 2,0 ms générera une vitesse proportionnelle.

Une fois que vous avez physiquement connecté les deux servos, suivez le circuit de dessin ci-dessus pour les sourcer (externe 5V ou 6V) et alimentez-les avec le signal d'Arduino :

  • Servo gauche : Arduino Pin 5
  • Servo droit :broche Arduino 3

Une fois tous connectés, la première chose à faire est d'envoyer une impulsion de 1.5ms pour vérifier si les moteurs sont "arrêtés" (ne tournent pas). Sinon, les servos doivent être réglés à l'arrêt complet (cherchez le boulon jaune, en dessous du servo).

REMARQUE :Si votre servo n'a pas ce réglage physique, essayez de changer le paramètre "1500" microseconde à l'intérieur de la fonction (haut ou bas) jusqu'à ce que vous obteniez l'arrêt complet.

Le code Arduino ci-dessous peut faire le travail :

#include  // Bibliothèque Servo Servo leftServo;Servo rightServo;Void setup(){ leftServo.attach(5); rightServo.attach(3); leftServo.writeMicroseconds(1500); rightServo.writeMicroseconds(1500);}boucle vide(){} 

Étape 3 :Assembler le corps et les moteurs pour le test de mouvement

  • Avec la bande de cadre 3M Command, fixez les 2 Servos à celui de la pièce de bois équarri.
  • Fixez le 2ème bois équarri à celui du dessus à l'aide des pinces à reliure. Ajustez la longueur de la plate-forme à vos besoins.
  • Fixez la roulette à billes à l'aide du clip de reliure.
  • L'alimentation électrique des moteurs proviendra de l'un des ensembles de batteries 5 V. Cet ensemble de batterie sera installé entre la maquette et le cadre du corps.
  • Connectez la batterie à utiliser avec les servos :à gauche du réseau électrique latéral exclusivement pour la source des servos
  • Connectez l'Arduino Nano à la maquette
  • Connectez le GND du réseau électrique à Arduino GND.
  • Connectez les servos à Arduino :GAUCHE ==> Broche 5 ; DROITE ==> Broche 3
  • Connectez la LED à la broche Arduino 13
  • Connectez le bouton à la broche Arduino 9

A noter qu'en raison de la façon dont les servos sont montés (en opposition) la plage de vitesse est :

  • La vitesse d'avance du servomoteur droit passe de 1 500 us (arrêté) à 2 000 us (pleine vitesse)
  • La vitesse d'avance du servomoteur gauche passe de 1 500 us (arrêté) à 1 000 (pleine vitesse)

Une LED externe est ajoutée à la broche 13, à des fins de signalisation et de test (vous pouvez utiliser la LED Arduino interne, à la place d'une LED externe si vous le souhaitez, mais tenez compte du fait qu'il sera difficile de la voir au milieu des câbles).

Un bouton est également connecté à la broche 9. Ce bouton est très utile à des fins de test et pour le démarrage du robot.

Par exemple :

while(digitalRead(buttonPin)) { }motorTurn (LEFT, 500);motorTurn (RIGHT, 500); 

Notez que les 2 lignes qui commanderont au Robot de tourner à GAUCHE, d'attendre 500 ms et de tourner à DROITE ne se produiront qu'après avoir appuyé sur le bouton (buttonPin =0). Avant cela, le programme sera arrêté dans la boucle infinie.

Le code ci-dessous peut être utilisé comme base pour un test moteur complet (Avant, Arrière, Arrêt complet, Tourner à gauche, Tourner à droite). Si nécessaire, vous devez ajuster les retards pour l'angle de braquage requis en fonction de vos moteurs (de plus, les valeurs d'impulsion gauche et droite doivent parfois être légèrement différentes pour compenser tout déséquilibre des moteurs.

FDDQRQOIN4TTVY0.ino

Étape 4 :Le module Bluetooth (optionnel)

Le module Bluetooth HC-06 doit être installé sur la planche à pain comme indiqué dans le dessin. La bibliothèque Arduino SoftSerial sera utilisée.

Sous les connexions des broches HC-06 :

  • Pin Tx à Arduino broche 10 (Rx)
  • Broche RX à Arduino broche 11 (Tx)
  • VCC/GND vers Arduino 5V/GND

Le Robot fonctionnera avec ou sans Bluetooth. Le code est construit de manière à ce que si vous n'activez pas le BT, les paramètres par défaut seront ceux qui seront utilisés par le robot. Donc, ne vous inquiétez pas si vous préférez ne pas installer le module HC-06, le code fonctionnera toujours correctement. Dans la dernière partie de ce tutoriel, j'explorerai comment utiliser une application Android pour envoyer des données afin de mieux régler les paramètres du robot et/ou déplacer le robot en mode manuel. Je laisserai l'utilisation du Bluetooth et de l'application en option au cas où quelqu'un souhaiterait explorer davantage l'utilisation d'un robot suiveur de ligne pour les compétitions, par exemple.

Étape 5 :Ajout des capteurs de ligne

Connectez les câbles aux broches Arduino comme ci-dessous :

  • Capteur 0 =12
  • Capteur 1 =18
  • Capteur 2 =17
  • Capteur 3 =16
  • Capteur 4 =19
  • Fixez les 5 capteurs sur une barre en plastique comme indiqué sur les photos
  • Il est conseillé d'étiqueter les capteurs à des fins de test. Le nom des capteurs va de "0" (plus à gauche) à "4" (plus à droite)
  • Faire passer les câbles sous le cadre en utilisant les élastiques pour les fixer. Veillez à ne pas vous confondre avec les roues ou la roulette.
  • Correction du deuxième jeu de piles 5 V et connexion à Arduino Vin.

Dans mon cas j'utilise un module avec 4 capteurs intégrés + 1 supplémentaire. Tous sont compatibles. Pour plus de simplicité, dans le schéma, j'ai inclus 5 capteurs autonomes connectés entre eux. Les résultats finaux sont les mêmes dans les deux configurations.

Étape 6 :Implémentation de la logique du capteur infrarouge

Le capteur IR se compose d'une LED IR individuelle et d'une photodiode IR. La lumière IR émise par la LED frappe la surface et est réfléchie vers la photodiode IR. La photodiode génère alors une tension de sortie proportionnelle au niveau de réflectance de la surface (valeurs plus élevées pour les "surfaces claires" et plus faibles pour les "surfaces noires/sombres").

Dans le cas des capteurs utilisés, un circuit intégré au module génère en sortie un simple signal numérique (HIGH :Dark; LOW :Light). Un potentiomètre installé au niveau du module (voir photo) ajustera le bon niveau de lumière pour être considéré comme "sombre" ou "clair". Il fonctionne de telle sorte que lorsque la couleur de la lumière réfléchie est noire/foncée, un niveau numérique HAUT ("1") est généré à sa sortie et un niveau BAS ("0") pour une autre couleur plus claire. J'ai utilisé ici un module intégré avec 4 capteurs et un module supplémentaire avec un seul capteur (forme différente, mais même logique). La combinaison est un ensemble de 5 capteurs que j'ai trouvé bon pour un contrôle agréable et fluide, comme expliqué ci-dessous.

Le réseau de 5 capteurs est monté de manière à ce que si un seul capteur est centré par rapport à la ligne noire, seul ce capteur spécifique produira un HAUT. De l'autre côté, l'espace entre les capteurs doit être calculé pour permettre à 2 capteurs de couvrir simultanément toute la largeur de la ligne noire, produisant également un HAUT sur les deux capteurs (voir les images ci-dessus).

Les sorties possibles du réseau de capteurs lors du suivi d'une ligne sont :

  • 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

Le fait d'avoir 5 capteurs permet de générer une "variable d'erreur" qui aidera à contrôler la position du robot sur la ligne, comme indiqué ci-dessous.

Considérons que la condition optimale est lorsque le robot est centré, avec la ligne juste en dessous du "capteur central" (capteur 2). La sortie du tableau sera :0 0 1 0 0 et dans cette situation, l'"erreur" sera "zéro". Si le robot commence à se déplacer vers la gauche (la ligne "semble se déplacer" vers la droite"), l'erreur doit augmenter avec un signal positif. Si le robot commence à se déplacer vers la droite (la ligne "semble se déplacer" vers la gauche"), dans le de la même manière, l'erreur doit augmenter, mais maintenant avec un signal négatif.

La variable d'erreur, liée à l'état du capteur sera :

0 0 1 0 0 ==> Erreur =0

  • 0 0 0 0 1 ==> Erreur =4
  • 0 0 0 1 1 ==> Erreur =3
  • 0 0 0 1 0 ==> Erreur =2
  • 0 0 1 1 0 ==> Erreur =1
  • 0 1 1 0 0 ==> Erreur =-1
  • 0 1 0 0 0 ==> Erreur =-2
  • 1 1 0 0 0 ==> Erreur =-3
  • 1 0 0 0 0 ==> Erreur =-4

En regardant le code Arduino, chacun des capteurs sera défini avec un nom spécifique (considérez que le capteur de suivi de ligne plus à gauche doit être affecté avec une étiquette "0") :

const int lineFollowSensor0 =12;const int lineFollowSensor1 =18;const int lineFollowSensor2 =17;const int lineFollowSensor3 =16;const int lineFollowSensor4 =19; 

Afin de stocker les valeurs de chaque capteur, une variable de type tableau sera créée :

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

Chaque position du tableau sera constamment mise à jour avec la sortie de chacun des capteurs :

LFSensor[0] =digitalRead(lineFollowSensor0);LFSensor[1] =digitalRead(lineFollowSensor1);LFSensor[2] =digitalRead(lineFollowSensor2);LFSensor[3] =digitalRead(lineFollowSensor3);LFSensor[4] =digitalRead(lineFollowSensor4) ; 

Ayant la valeur de chacun des capteurs, une logique doit être mise en place pour générer la variable d'erreur :

if((LFSensor[0]==0 )&&(LFSensor[1]==0 )&&(LFSensor[2]==0 )&&(LFSensor[3]==0 )&&(LFSensor [4]==1 )) erreur =4;else if((LFSensor[0]==0 )&&(LFSensor[1]==0 )&&(LFSensor[2]==0 )&&(LFSensor[3] ==1 )&&(LFSensor[4]==1 )) erreur =3; else if((LFSensor[0]==0 )&&(LFSensor[1]==0 )&&(LFSensor[2]==0 )&&(LFSensor[3]==1 )&&(LFSensor[4]==0 )) erreur =2;sinon if((LFSensor[0]==0 )&&(LFSensor[1]==0 )&&(LFSensor[2]==1 )&&(LFSensor[3]==1 )&&(LFSensor[4]==0 )) erreur =1;else if((LFSensor[0]==0 )&&(LFSensor[1]==0 )&&(LFSensor[2]==1 )&&(LFSensor[ 3]==0 )&&(LFSensor[4]==0 )) error =0;else if((LFSensor[0]==0 )&&(LFSensor[1]==1 )&&(LFSensor[2]==1 )&&(LFSensor[3]==0 )&&(LFSensor[4]==0 )) erreur =- 1;else if((LFSensor[0]==0 )&&(LFSensor[1]==1 )&&(LFSensor[2]==0 )&&(LFSensor[3]==0 )&&(LFSensor[4]==0 )) erreur =-2;else if((LFSensor[0]==1 )&&(LFSensor[1]==1 )&&(LFSensor[2]==0 )&&(LFSensor[3]==0 )&&(LFSensor[4]==0 )) erreur =-3;else if((LFSensor [0]==1 )&&(LFSensor[1]==0 )&&(LFSensor[2]==0 )&&(LFSensor[3]==0 )&&(LFSensor[4]==0 )) erreur =-4 ; 

Étape 7 :Contrôler la direction (Contrôle proportionnel - P)

Parfait! À ce stade, notre robot est assemblé et opérationnel. Vous devez effectuer quelques tests de base avec les moteurs, lire la sortie des capteurs et les tester sur une ligne. Ce qui manque, c'est le vrai "cerveau", les premiers pas d'une "intelligence artificielle". Nous y parviendrons en mettant en œuvre une logique de contrôle qui garantira que le Robot continuera à suivre la ligne.

Contrôle proportionnel simple :

Supposons que le Robot passe sur une ligne et que la sortie du Sensor Array soit :"0 0 1 0 0 " . L'erreur correspondante est "0". Dans cette situation, les deux moteurs doivent fonctionner en marche avant à vitesse constante.

Par exemple :

Définition de la variable :iniMotorSpeed ​​=250 ; signifie que le servo GAUCHE recevra des impulsions de 1 250 us et le servo DROIT 1 750 us. Avec ces paramètres, le Robot avancera à mi-vitesse. N'oubliez pas que la vitesse d'avance du servo de DROITE variera avec une longueur d'impulsion de 1 500 us (arrêté) à 2 000 us (pleine vitesse) et le servo de GAUCHE de 1 500 us (arrêté) à 1 000 us (pleine vitesse).

rightServo.writeMicroseconds(1500 + iniMotorPower);leftServo.writeMicroseconds(1500 - iniMotorPower); 

Supposons maintenant que le robot se dirige vers la gauche (c'est comme si la "LIGNE va à droite") et recouvre également le capteur 3. La sortie du tableau sera :"0 0 1 1 0" et l'erreur =1 . Dans cette situation, ce dont vous avez besoin est de tourner le robot vers la droite. Pour ce faire, vous devez diminuer la vitesse du servo DROIT ce qui signifie diminuer la durée de l'impulsion. De plus, la vitesse du servo GAUCHE doit augmenter, ce qui signifie diminuer la longueur de l'impulsion du servo GAUCHE. Pour ce faire, nous devons changer la fonction de contrôle du moteur :

rightServo.writeMicroseconds(1500 + iniMotorPower - erreur); ==> Erreur positive :diminuez la vélocitéleftServo.writeMicroseconds(1500 - iniMotorPower - error); ==> Erreur positive :augmenter la vitesse 

La logique ci-dessus est correcte, mais il est facile de comprendre que l'ajout ou la soustraction de "1" microseconde à la longueur d'impulsion ne générera pas la correction requise sur un temps réaliste. Il est intuitif que le nombre à ajouter ou à soustraire soit supérieur, par exemple 50, 100, etc. Pour obtenir cela, l'"erreur" doit être multipliée par une constante (appelons-la "K"). Une fois que l'influence de cette constante sera proportionnelle à l'erreur, nous la nommerons "Constante proportionnelle :Kp" .

La fonction motrice sera :

int Kp =50;rightServo.writeMicroseconds(1500 + iniMotorPower - Kp*error);leftServo.writeMicroseconds(1500 - iniMotorPower - Kp*error); 

Nous pouvons reprendre ce qui se passera avec les moteurs comme indiqué ci-dessous :

  • Matrice de capteurs :0 0 1 0 0 ==> erreur =0 ==> Longueur d'impulsion du servo droit =​​1 750us ==> Longueur d'impulsion du servo gauche =1 250us (les deux moteurs à la même vitesse)
  • Matrice de capteurs :0 0 1 1 0 ==> erreur =1 ==> Longueur d'impulsion du servomoteur droit =​​1 700us (plus lent) ==> Longueur d'impulsion du servomoteur gauche =1 200us (plus rapide)

Si la situation est inverse et le Robot conduit à droite, l'erreur serait "négative" et la vitesse des servos devrait changer :

  • Matrice de capteurs :0 0 1 0 0 ==> erreur =0 ==> Longueur d'impulsion du servo droit =​​1 750us ==> Longueur d'impulsion du servo gauche =1 250us (les deux moteurs à la même vitesse)
  • Matrice de capteurs :0 1 1 0 0 ==> erreur =-1 ==> Longueur d'impulsion du servomoteur droit =​​1 800us (plus rapide) ==> Longueur d'impulsion du servomoteur gauche =1 300us (plus lent)

A ce stade, il est clair que plus le Robot est poussé d'un côté, plus l'erreur sera grande et plus vite il doit revenir au centre. La vitesse avec le Robot réagira à l'erreur sera proportionnelle à celle-ci. C'est ce qu'on appelle le "Contrôle proportionnel" , c'est la composante "P" d'un réseau de contrôle plus complexe, le PDI (Proportionnel, Dérivé, Intégral).

Étape 8 :Contrôle PID (facultatif)

Au cas où vous voudriez sauter à cette partie, c'est également OK. Vous pouvez rester avec le contrôle proportionnel expliqué sur la dernière étape, ou brûler quelques méninges pour implémenter un système de contrôle plus complexe dans votre robot, c'est votre choix.

Si vous avez pris votre décision, allons-y !

PID (Proportionnel, Dérivé et Intégral) est l'un des schémas de contrôle les plus courants. La plupart des boucles de contrôle industrielles utilisent une certaine saveur de contrôle PID. Il existe de nombreuses façons de régler une boucle PID, y compris la technique manuelle utilisée dans cet exemple.

Considérez le PID comme un simple ressort. Un ressort a une longueur d'origine qui, lorsqu'elle est perturbée par une expansion ou une contraction, a tendance à retrouver sa longueur d'origine dans les plus brefs délais. De même, un algorithme PID dans un système a une valeur de consigne d'une quantité physique particulière à contrôler, appelée « point de consigne ', qui, lorsqu'il est modifié pour une raison quelconque, le système contrôle les autres fonctionnalités nécessaires, pour revenir au point de consigne d'origine dans les plus brefs délais. Les contrôleurs PID sont utilisés partout où il est nécessaire de contrôler une quantité physique et de la rendre égale à une valeur spécifiée. Exemple, Contrôleur de croisière dans les voitures, les robots, les régulateurs de température, les régulateurs de tension, etc.

Comment fonctionne le PID ?

Le système calcule l''erreur ', ou 'écart ’ de la grandeur physique à partir du point de consigne, en mesurant la valeur actuelle de cette grandeur physique à l'aide d'un ou de plusieurs capteurs. Pour revenir au point de consigne, cette 'erreur ’ devrait être minimisé et devrait idéalement être égal à zéro. De plus, ce processus doit se dérouler le plus rapidement possible. Idéalement, il ne devrait y avoir aucun décalage dans la réponse du système au changement de son point de consigne.

Plus d'informations peuvent être trouvées dans de nombreux livres et sites Web, y compris ici.

Implémentation du PID

i) Terme d'erreur (e) :

Ceci est égal à la différence entre le point de consigne et la valeur actuelle de la grandeur contrôlée. Erreur =set_pointvaleur_courante (dans notre cas est la variable d'erreur obtenue à partir de la position de Robot sur la ligne

ii) Terme proportionnel (P) :

Ce terme est proportionnel à l'erreur.

P =erreur

Cette valeur est responsable de l'ampleur du changement requis dans la quantité physique pour atteindre le point de consigne. Le terme de proportion est ce qui détermine le temps de montée de la boucle de régulation ou la vitesse à laquelle elle atteindra le point de consigne.

iii) Terme intégral (I) :

Ce terme est la somme de toutes les valeurs d'erreur précédentes.

I =I + erreur

Cette valeur est responsable de la rapidité de réponse du système au changement du point de consigne. Le terme intégral est utilisé pour éliminer l'erreur en régime permanent requise par le terme proportionnel. Habituellement, les petits robots n'utilisent pas le terme intégral car nous ne sommes pas préoccupés par l'erreur d'état stable et cela peut compliquer le "réglage de la boucle ".

iv) Terme différentiel ou dérivé (D) :

Ce terme est la différence entre l'erreur instantanée du point de consigne, et l'erreur de l'instant précédent.

D =erreur - erreur précédente

Cette valeur est responsable de ralentir le taux de changement de la quantité physique lorsqu'elle s'approche du point de consigne. Le terme dérivé est utilisé pour réduire le dépassement ou combien le système doit "surcorrection ".

Équation :

PIDvalue =(Kp*P) + (Ki*I) + (Kd*D)

Où :

Kp est la constante utilisée pour faire varier l'l'amplitude du changement nécessaire pour atteindre le point de consigne. Ki est la constante utilisée pour faire varier le taux auquel le changement doit être apporté dans la quantité physique pour atteindre le point de consigne. Kd est la constante utilisée pour faire varier la stabilité du système.

Une approche pour régler la boucle peut être Méthode d'essai d'erreur :

Définissez la variable Kd sur 0 et réglez d'abord le terme Kp seul. Kp de 25 est un bon point de départ dans notre cas ici. À la dernière étape, nous avons utilisé un Kp de 50 qui fonctionne très bien avec mon Robot. Une fois que le robot répond raisonnablement, réglez la partie dérivée de la boucle de contrôle (Kd ). Réglez d'abord les valeurs Kp et Kd chacune à la moitié de la valeur Kp. Par exemple, si les réponses du robot sont raisonnables avec un Kp =50, alors définissez Kp =25 et Kd =25 pour commencer. Augmentez le gain Kd (dérivé) pour diminuer le dépassement, diminuez-le si le robot devient instable.

  • Si le robot réagit trop lentement, augmentez la valeur.
  • Si le robot semble réagir rapidement en devenant instable, diminuez la valeur.

Un autre élément de la boucle à prendre en compte est le taux d'échantillonnage/boucle réel . Accélérer ou ralentir ce paramètre peut faire une différence significative dans les performances du robot. Ceci est défini par le délai déclarations que vous avez dans votre code. Il s'agit d'une méthode de tentative d'Essai pour obtenir le résultat optimal.

Sur la base de l'approche ci-dessus, la fonction ci-dessous a été implémentée :

void calculatePID(){ P =erreur ; I =I + erreur ; D =erreur-précédenteError ; Valeur PID =(Kp*P) + (Ki*I) + (Kd*D) ; previousError =error;} 

La constante Kp simple utilisée sur la dernière étape sera remplacée pour cette PIDvalue plus complète :

void motorPIDcontrol(){ int leftMotorSpeed ​​=1500 - iniMotorPower - PIDvalue ; int rightMotorSpeed ​​=1500 + iniMotorPower - PIDvalue ; leftServo.writeMicroseconds(leftMotorSpeed); rightServo.writeMicroseconds(rightMotorSpeed);} 

Mais notez que si vous avez Kd et Ki =0 , valeur PID n'est que Kp*error utilisé dans la dernière étape.

Étape 9 :Le code final

A cette étape, le Robot peut suivre une boucle constante et le fera sans s'arrêter. Le programme en boucle serait :

boucle vide (){ readLFSsensors(); // lit les capteurs, stocke les valeurs dans le tableau de capteurs et calcule "l'erreur" calculatePID(); motorPIDcontrol();} 

Mais pour un fonctionnement plus complet et réel, il est important d'ajouter au moins quelques éléments de base "commandes " done "avec la ligne ". Par exemple, introduisons une nouvelle variable :" mode ". Nous allons définir 3 états pour cette variable :

Mode :

  • #define ARRÊTÉ 0
  • #define FOLLOWING_LINE 1
  • #define NO_LINE 2

Si tous les capteurs trouvent une ligne noire, la sortie du réseau de capteurs serait :1 1 1 1 1. Dans cette condition, nous pouvons définir le mode comme « ARRÊTÉ » et le robot doit effectuer un « arrêt complet ".

if((LFSensor[0]==1 )&&(LFSensor[1]==1 )&&(LFSensor[2]==1 )&&(LFSensor[3]==1 )&&(LFSensor [4]==1 )) { mode =ARRÊTÉ;} 

Une autre situation courante avec Follower Line Robots est celle où il trouve "pas de ligne ", ou la sortie du réseau de capteurs est :0 0 0 0 0. Dans ce cas, nous pouvons le programmer pour qu'il revienne à 180o ou qu'il tourne sous de petits angles jusqu'à ce qu'une ligne soit trouvée et que la condition normale de suivi de ligne reprenne.

else if((LFSensor[0]==0 )&&(LFSensor[1]==0 )&&(LFSensor[2]==0 )&&(LFSensor[3]==0 )&&( LFSensor[4]==0 )) { mode =NO_LINE;} 

La boucle () complète serait :

void loop() { readLFSsensors(); switch (mode) { case STOPPED:motorStop(); Pause; cas NO_LINE :motorStop(); MotorTurn(GAUCHE, 180); Pause; case FOLLOWING_LINE :calculatePID(); motorPIDcontrol(); Pause; }} 

Le vrai code final intégrera une logique supplémentaire ainsi que des variables qui doivent être initialisées, etc. Au cours de l'explication ci-dessus, je les ai omis par souci de simplicité, mais tout devrait être clair en regardant le code final.

Ci-dessous se trouve le code Arduino final :

FUXCUEAIN699SZC.ino FLD2J3KIN699SZF.h FOFYXFZIN699SZT.ino FMPX9KJIN699SZU.ino FFE5AZ3IN699T06.ino

Étape 10 :Réglage du contrôle PID à l'aide de l'application Android

In the previous code, you can find at "robotDefines.h " tab the following definitions for the constants to be used with the PID control:

float Kp=50;float Ki=0;float Kd=0; 

As explained at previous step, the best way to define the correct constant to be used with a PID controller is using the "Try-error" methodology. The bad side of that is that you must re-compile the program each time that you must change it. One way to speed-up the process is to use the Android App to send the constants at the "Set-Up Phase" .

I developed an Android App exclusively for that. In short there are the traditional manual commands:

  • FW, BW, Left, Right and Stop where the app will send respectively to the BT module:'f', 'b', 'l', 'r' and 's'.

Also 3 sliders were included, one for each PID constants:

  • Kd:"d/XXX" where "XXX" it is a number from 0 to 100.
  • Kp:"p/XXX"
  • Ki:"i/XXX"

An extra button was included that will work exactly as the button connected on Arduino Pin9. You can use one or the other, it does not matter.

Below you can find the .aia file that can be modified at MIT AppInventor and the .apk file to be installed directly in your Android device.

FTH62BVIN699T4B.aia F60H7R0IN699T4I.apk

Step 11:Changing the Code for PID remote tuning

During the Setup, we will introduce a loop where you can send the PID parameters to the Robot before you put him over the line:

 while (digitalRead(buttonPin) &&!mode) { checkBTcmd(); // verify if a comand is received from BT remote control manualCmd (); command =""; } checkPIDvalues(); mode =STOPPED; 

The manual command function will be:

void manualCmd(){ switch (command[0]) { case 'g':mode =FOLLOWING_LINE; Pause; case 's':motorStop(); //turn off both motors break; case 'f':motorForward(); Pause; case 'r':motorTurn(RIGHT, 30); motorStop(); Pause; case 'l':motorTurn(LEFT, 30); motorStop(); Pause; case 'b':motorBackward(); Pause; case 'p':Kp =command[2]; Pause; case 'i':Ki =command[2]; Pause; case 'd':Kd =command[2]; Pause; }} 

In the video, you can see some tests using the Android App. Below the final code including the PID setup via Android:

FGAEB9BIN7QQQAW.ino FBMONSNIN7QQQCD.ino F8B3CDHIN7QQQCL.h FNOMRUNIN7QQQCP.ino FA3K57ZIN7QQQCR.ino

Step 12:Conclusion

This is the first part of a more complex project, exploring the potentiality of a line follower robot. In the next part, I will develop a Maze solve robot, based on this this project here. Hopefully I can contribute for others to learn more about electronics, robot, Arduino, etc.

The update files for this project can be found at GITHUB. For more tutorials, please visit my Blog:MJRoBot.org

Greetings from the south of the world!

Thanks

Marcelo

Code

  • Extrait de code n° 1
  • Code snippet #2
  • Code snippet #3
  • Code snippet #5
  • Code snippet #6
  • Code snippet #10
  • Code snippet #11
  • Code snippet #12
  • Code snippet #15
  • Code snippet #17
  • Code snippet #18
Extrait de code n°1Texte brut
#include  // Servo library Servo leftServo;Servo rightServo;Void setup(){ leftServo.attach(5); rightServo.attach(3); leftServo.writeMicroseconds(1500); rightServo.writeMicroseconds(1500);}void loop(){}
Code snippet #2Plain text
while(digitalRead(buttonPin)) { }motorTurn (LEFT, 500);motorTurn (RIGHT, 500);
Code snippet #3Plain text
const int lineFollowSensor0 =12;const int lineFollowSensor1 =18;const int lineFollowSensor2 =17;const int lineFollowSensor3 =16;const int lineFollowSensor4 =19;
Code snippet #5Plain text
LFSensor[0] =digitalRead(lineFollowSensor0);LFSensor[1] =digitalRead(lineFollowSensor1);LFSensor[2] =digitalRead(lineFollowSensor2);LFSensor[3] =digitalRead(lineFollowSensor3);LFSensor[4] =digitalRead(lineFollowSensor4);
Code snippet #6Plain text
if((LFSensor[0]==0 )&&(LFSensor[1]==0 )&&(LFSensor[2]==0 )&&(LFSensor[3]==0 )&&(LFSensor[4]==1 )) error =4;else if((LFSensor[0]==0 )&&(LFSensor[1]==0 )&&(LFSensor[2]==0 )&&(LFSensor[3]==1 )&&(LFSensor[4]==1 )) error =3; else if((LFSensor[0]==0 )&&(LFSensor[1]==0 )&&(LFSensor[2]==0 )&&(LFSensor[3]==1 )&&(LFSensor[4]==0 )) error =2;else if((LFSensor[0]==0 )&&(LFSensor[1]==0 )&&(LFSensor[2]==1 )&&(LFSensor[3]==1 )&&(LFSensor[4]==0 )) error =1;else if((LFSensor[0]==0 )&&(LFSensor[1]==0 )&&(LFSensor[2]==1 )&&(LFSensor[3]==0 )&&(LFSensor[4]==0 )) error =0;else if((LFSensor[0]==0 )&&(LFSensor[1]==1 )&&(LFSensor[2]==1 )&&(LFSensor[3]==0 )&&(LFSensor[4]==0 )) error =- 1;else if((LFSensor[0]==0 )&&(LFSensor[1]==1 )&&(LFSensor[2]==0 )&&(LFSensor[3]==0 )&&(LFSensor[4]==0 )) error =-2;else if((LFSensor[0]==1 )&&(LFSensor[1]==1 )&&(LFSensor[2]==0 )&&(LFSensor[3]==0 )&&(LFSensor[4]==0 )) error =-3;else if((LFSensor[0]==1 )&&(LFSensor[1]==0 )&&(LFSensor[2]==0 )&&(LFSensor[3]==0 )&&(LFSensor[4]==0 )) error =-4;
Code snippet #10Plain text
void calculatePID(){ P =error; I =I + error; D =error-previousError; PIDvalue =(Kp*P) + (Ki*I) + (Kd*D); previousError =error;}
Code snippet #11Plain text
void motorPIDcontrol(){ int leftMotorSpeed =1500 - iniMotorPower - PIDvalue; int rightMotorSpeed =1500 + iniMotorPower - PIDvalue; leftServo.writeMicroseconds(leftMotorSpeed); rightServo.writeMicroseconds(rightMotorSpeed);}
Code snippet #12Plain text
void loop (){ readLFSsensors(); // read sensors, storage values at Sensor Array and calculate "error" calculatePID(); motorPIDcontrol();}
Code snippet #15Plain text
void loop() { readLFSsensors(); switch (mode) { case STOPPED:motorStop(); Pause; case NO_LINE:motorStop(); motorTurn(LEFT, 180); Pause; case FOLLOWING_LINE:calculatePID(); motorPIDcontrol(); Pause; }}
Code snippet #17Plain text
 while (digitalRead(buttonPin) &&!mode) { checkBTcmd(); // verify if a comand is received from BT remote control manualCmd (); command =""; } checkPIDvalues(); mode =STOPPED;
Code snippet #18Plain text
void manualCmd(){ switch (command[0]) { case 'g':mode =FOLLOWING_LINE; Pause; case 's':motorStop(); //turn off both motors break; case 'f':motorForward(); Pause; case 'r':motorTurn(RIGHT, 30); motorStop(); Pause; case 'l':motorTurn(LEFT, 30); motorStop(); Pause; case 'b':motorBackward(); Pause; case 'p':Kp =command[2]; Pause; case 'i':Ki =command[2]; Pause; case 'd':Kd =command[2]; Pause; }}
Github
https://github.com/Mjrovai/MJRoBot-Line-Followerhttps://github.com/Mjrovai/MJRoBot-Line-Follower

Schémas

oIDhLcHQ30lDgVBXZvb8.fzz

Processus de fabrication

  1. L'avenir de la robotique
  2. Capteur de suivi de ligne avec RPi
  3. Contrôle du capteur et de l'actionneur Raspberry Pi
  4. ROBOT WIFI RASPBERRY PI CONTRLÉ À PARTIR D'UN SMARTPHONE ANDROID
  5. Automatisation :ligne de robot repensée
  6. Ligne de robots SCARA élargie
  7. Télécommande universelle utilisant Arduino, 1Sheeld et Android
  8. Contrôle PID d'un robot préhenseur dans Matlab / Simulink
  9. Contrôle de qualité automatisé de la ligne d'emballage