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 à IA sur Arduino Nano

Composants et fournitures

Applications et services en ligne

À propos de ce projet

Après un robot « Pick & Place », l'objectif était de développer un robot suiveur de ligne doté d'intelligence artificielle. Un suiveur de ligne simple convient aux débutants ; ici, on remplace les servos par des capteurs IR et on y ajoute de l'IA. « L'idée était de tester mes compétences à un niveau avancé. »

Pourquoi un robot suiveur de ligne basé sur l'IA ?

L'IA est la technologie qui peut transformer notre environnement et améliorer la vie humaine. Donner aux robots la capacité de comprendre et résoudre des problèmes réels, c'est la promesse de l'IA. Applications : lignes d'assemblage/production, navettes de transport autonomes. Ce prototype est un premier pas vers l'IA embarquée.

Capteurs infrarouges (7 au total)

Stockage : int LFSensor[5] pour les 5 capteurs PID ; int farLeftSensor, farRightSensor pour les latéraux.

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 pour labyrinthe (Left-Hand Rule)

Poser la main gauche sur le mur, avancer, garder la main en contact au niveau de chaque intersection. On finit par sortir (pas nécessairement par le plus court).

Types d'intersections (2D) et actions selon la règle :

IntersectionAction (règle main gauche)
Croix (+)Tourner à gauche
TTourner à gauche
Droite seulementTourner à droite
Gauche seulementTourner à gauche
Tout droit ou GaucheTourner à gauche
Tout droit ou DroiteAller tout droit
ImpasseDemi-tour (180°)
Fin de labyrintheStop

Pour distinguer une « ligne » (Croix ou T) d'un simple virage, le robot doit avancer d'un « pas supplémentaire » (extra inch) et lire à nouveau les capteurs :

Même principe pour virage droite/gauche : avancer un peu, tester la présence de ligne continue pour choisir entre « virage seul » ou « tout droit + virage ».

Problèmes courants

Problèmes de driver Arduino : voir vidéo de dépannage (lien dans le projet original).

Code

Le code est structuré en plusieurs onglets/fichiers :

Fichier principal : RoBot_Maze_Solve_2

/* Robot 7 capteurs - Résolveur labyrinthe & suiveur ligne avec PID programmable via BT
   Base mouvement : Nano Mouse Robot (Michael Backus)
   Suivi ligne PID : samvrit.tk
   Résolution chemin : Patrick McCabe (patrickmccabemakes.com)
   Marcelo Jose Rovai - 23 avril 2016 - https://mjrobot.org
*/
#include 
#include "robotDefines.h"
String command, device;
#include 
SoftwareSerial BT1(10, 11); // Rx=10, Tx=11

void setup() {
  Serial.begin(9600); BT1.begin(9600);
  pinMode(ledPin, OUTPUT); pinMode(buttonPin, INPUT_PULLUP);
  pinMode(lineFollowSensor0, INPUT); pinMode(lineFollowSensor1, INPUT);
  pinMode(lineFollowSensor2, INPUT); pinMode(lineFollowSensor3, INPUT);
  pinMode(lineFollowSensor4, INPUT);
  leftServo.attach(5); rightServo.attach(3);
  BT1.print("check the PID constants to be sent to Robot"); Serial.print("check the PID constants to be sent to Robot"); BT1.println('\n');
  while (digitalRead(buttonPin) && !mode) { checkBTcmd(); manualCmd(); command = ""; }
  checkPIDvalues(); mode = STOPPED; status = 0;
}

void loop() {
  ledBlink(1); BT1.println("Start First Pass"); Serial.println("Start First Pass");
  readLFSsensors(); mazeSolve();
  ledBlink(2); BT1.println("End First Pass"); Serial.println("End First Pass");
  while (digitalRead(buttonPin) && !mode) { checkBTcmd(); manualCmd(); command = ""; }
  BT1.println("Starting 2nd Pass"); Serial.println("Starting 2nd Pass");
  pathIndex = 0; status = 0; mazeOptimization();
  ledBlink(3); BT1.println("End 2nd Pass"); Serial.println("End 2nd Pass");
  while (digitalRead(buttonPin) && !mode) { checkBTcmd(); manualCmd(); command = ""; }
  mode = STOPPED; status = 0; pathIndex = 0; pathLength = 0;
}

Fonctions : résolution labyrinthe (mazeSolve, recIntersection, mazeEnd, followingLine, simplifyPath, mazeOptimization, mazeTurn)


void mazeSolve(void) {
  while (!status) {
    readLFSsensors();
    switch (mode) {
      case NO_LINE: motorStop(); goAndTurn(LEFT, 180); recIntersection('B'); break;
      case CONT_LINE: runExtraInch(); readLFSsensors();
        if (mode != CONT_LINE) { goAndTurn(LEFT, 90); recIntersection('L'); } else mazeEnd(); break;
      case RIGHT_TURN: runExtraInch(); readLFSsensors();
        if (mode == NO_LINE) { goAndTurn(RIGHT, 90); recIntersection('R'); } else recIntersection('S'); break;
      case LEFT_TURN: goAndTurn(LEFT, 90); recIntersection('L'); break;
      case FOLLOWING_LINE: followingLine(); break;
    }
  }
}

void recIntersection(char dir) { path[pathLength]=dir; pathLength++; simplifyPath(); }

void mazeEnd(void) {
  motorStop();
  BT1.print("The End ==> Path: "); for(int i=0;i "); Serial.println(pathLength);
  status = 1; mode = STOPPED;
}

void followingLine(void) { calculatePID(); motorPIDcontrol(); }

void simplifyPath() {
  if (pathLength < 3 || path[pathLength-2] != 'B') return;
  int totalAngle = 0;
  for(int i=1;i<=3;i++) {
    switch(path[pathLength-i]) { case 'R': totalAngle+=90; break; case 'L': totalAngle+=270; break; case 'B': totalAngle+=180; break; }
  }
  totalAngle %= 360;
  switch(totalAngle) { case 0: path[pathLength-3]='S'; break; case 90: path[pathLength-3]='R'; break; case 180: path[pathLength-3]='B'; break; case 270: path[pathLength-3]='L'; break; }
  pathLength -= 2;
}

void mazeOptimization(void) {
  while (!status) {
    readLFSsensors();
    switch (mode) {
      case FOLLOWING_LINE: followingLine(); break;
      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;
    }
  }
}

void mazeTurn(char dir) {
  switch(dir) { case 'L': goAndTurn(LEFT,90); break; case 'R': goAndTurn(RIGHT,90); break; case 'B': goAndTurn(RIGHT,800); break; case 'S': runExtraInch(); break; }
}

Fonctions générales (ledBlink, checkBTcmd, manualCmd, sendBTdata, calculatePID, checkPIDvalues, testLineFollowSensors)


void ledBlink(int times) { for(int i=0;i0){ Serial.print("Command received from BT ==> "); Serial.println(device); command=device; device=""; BT1.flush(); } }
void manualCmd() {
  switch(command[0]) {
    case 'g': mode=FOLLOWING_LINE; break; case 's': motorStop(); break; case 'f': motorForward(); break;
    case 'r': motorTurn(RIGHT,30); motorStop(); break; case 'l': motorTurn(LEFT,30); motorStop(); break;
    case 'b': motorBackward(); break; case 'p': Kp=command[2]; break; case 'i': Ki=command[2]; break; case 'd': Kd=command[2]; break;
  }
}
void sendBTdata(int data){ digitalWrite(ledPin,HIGH); BT1.print("Data from Arduino"); BT1.print(data); BT1.print(" xx"); BT1.println('\n'); digitalWrite(ledPin,LOW); }
void calculatePID(){ P=error; I+=error; D=error-previousError; PIDvalue=(Kp*P)+(Ki*I)+(Kd*D); previousError=error; }
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(){ /* lecture brute + debug PID */ }

Fonctions capteurs (readLFSsensors, readLateralSensors)


void readLFSsensors() {
  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);

  if (LFSensor[0]&&LFSensor[1]&&LFSensor[2]&&LFSensor[3]&&LFSensor[4]) { mode=CONT_LINE; error=0; }
  else if(!LFSensor[0] && farRightSensor "); Serial.print(farRightSensor);
  Serial.print(" mode: "); Serial.print(mode); Serial.print(" error:"); Serial.println(error);
}
void readLateralSensors(void) { }

Définitions (RobotDefines.h)


int mode = 0;
#define STOPPED 0
#define FOLLOWING_LINE 1
#define NO_LINE 2
#define CONT_LINE 3
#define POS_LINE 4
#define RIGHT_TURN 5
#define LEFT_TURN 6

const 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;
const int lineFollowSensor0 = 12; const int lineFollowSensor1 = 18; const int lineFollowSensor2 = 17;
const int lineFollowSensor3 = 16; const int lineFollowSensor4 = 19;
const int farRightSensorPin = 0; const int farLeftSensorPin = 1; const int THRESHOLD = 150;
int farRightSensor=0, farLeftSensor=0, LFSensor[5]={0,0,0,0,0};
float Kp=50, Ki=0, Kd=0, error=0, P=0, I=0, D=0, PIDvalue=0, previousError=0, previousI=0;
#define RIGHT 1; #define LEFT -1;
Servo leftServo; Servo rightServo;

unsigned char dir; char path[100]=""; unsigned char pathLength=0; int pathIndex=0; unsigned int status=0;

Fonctions moteurs (MotorFuntions)


void motorStop(){ leftServo.writeMicroseconds(1500); rightServo.writeMicroseconds(1500); delay(200); }
void motorForward(){ leftServo.writeMicroseconds(1500-(power+adj)); rightServo.writeMicroseconds(1500+power); }
void motorBackward(){ leftServo.writeMicroseconds(1500+power); rightServo.writeMicroseconds(1500-power); }
void motorFwTime(unsigned int t){ motorForward(); delay(t); motorStop(); }
void motorBwTime(unsigned int t){ motorBackward(); delay(t); motorStop(); }
void motorTurn(int dir, int deg){ leftServo.writeMicroseconds(1500-(iniMotorPower+adj)*dir); rightServo.writeMicroseconds(1500-iniMotorPower*dir); delay(round(adjTurn*deg+20)); motorStop(); }
void motorPIDcontrol(){
  int leftSpeed = 1500-(iniMotorPower+adj)-PIDvalue;
  int rightSpeed = 1500+iniMotorPower-PIDvalue;
  constrain(leftSpeed,1000,2000); constrain(rightSpeed,1000,2000);
  leftServo.writeMicroseconds(leftSpeed); rightServo.writeMicroseconds(rightSpeed);
}
void runExtraInch(){ motorPIDcontrol(); delay(extraInch); motorStop(); }
void goAndTurn(int dir, int deg){ motorPIDcontrol(); delay(adjGoAndTurn); motorTurn(dir,deg); }

Schéma

Schéma de câblage (image fournie).

Processus de fabrication

  1. Construire un robot évitant les murs avec Raspberry Pi – Guide pas à pas
  2. Robot mobile évitant les obstacles par vision sur Raspberry Pi
  3. Le robot calligraphe de Star Automation brille au NPE2018
  4. Wittmann Battenfeld lance la gamme WX153 : robots avancés pour l'automatisation de l'injection à haute force de fermeture
  5. Bosch dévoile une solution de fabrication pilotée par l'IA au salon Hannover Messe
  6. FANUC élargit sa gamme de robots SCARA : six modèles, charges 3–20 kg, portées 400–1100 mm
  7. Intelligence artificielle dans le retail : Bénéfices prouvés, pas un effet de mode
  8. L'IA permet aux robots d'identifier plus de 90 objets par le toucher
  9. Robotique pilotée par l'IA : Machines intelligentes de pointe