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

Robot seguidor de líneas con IA en Arduino Nano

Componentes y materiales

Componente Cantidad
Arduino Nano 1
Transceptor Bluetooth RF Maestro/Esclavo (Seeed) 1
Módulo sensor IR reflectivo fotoeléctrico (Amazon Web Services) 1
Servo rotación continua (Sony) 1
Batería 4.8V (Android) 1

Aplicaciones y servicios en línea

Sobre el proyecto

Tras desarrollar un robot Pick & Place con Arduino, la idea era crear un robot seguidor de líneas basado en Inteligencia Artificial (IA). Un seguidor de líneas simple es bueno para principiantes; solo hacía falta añadir sensores infrarrojos en lugar de servos en mi proyecto anterior. "La idea era poner a prueba mis habilidades a nivel avanzado."

Aunque fue una tarea difícil, la investigación online me ayudó mucho.

¿Por qué un robot seguidor de líneas con IA?

La IA es tecnología reciente con potencial para cambiar el mundo y mejorar la vida humana. El sueño de dotar a robots de inteligencia para entender y resolver problemas reales solo puede hacerse realidad mediante Inteligencia Artificial; por eso todos hablan de ella.

Mi prototipo tiene algo de inteligencia; es solo el inicio de mi camino hacia la IA. Explicaré paso a paso el desarrollo en lenguaje sencillo. Por falta de recursos no construiré el robot físico, pero explicaré cómo hacerlo investigando. Proporcionaré códigos precisos que funcionen en Arduino IDE para que desarrolles tu primer robot IA fácilmente.

Apóyame en Patreon: https://bit.ly/31NfQ6A

"Encuentras los componentes más baratos en EE. UU. en la descripción del vídeo, solo haz clic en el vídeo."

Uso de sensores infrarrojos

El proyecto usa 7 sensores IR como muestra el diagrama:

Rol de los 5 sensores PID: generan salida digital HIGH/LOW (1/0). Solo el sensor centrado sobre la línea negra da HIGH (1). Posibles patrones:

10000  11000  01000  01100  00100
00110  00010  00011  00001  00000  11111

Sensores laterales (izq/der): salida analógica HIGH/LOW.

Array para 5 sensores: int LFSensor[5]={1,1,1,1,1};

Variables para laterales: int farleft_sensor=0;

Lectura:

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);

Regla de la mano izquierda en laberintos

Mira este vídeo para entender la regla de la mano izquierda en laberintos.

En resumen: coloca tu mano izquierda en la pared, avanza, mantén la mano tocando la pared izquierda en cada intersección. Llegarás al final (no necesariamente por el camino más corto).

Clave: identificar intersecciones y decidir según la regla. En laberinto 2D hay 8 tipos de intersecciones (ver imagen). Acciones posibles:

Aplicando la regla de la mano izquierda, las acciones se reducen a una por intersección:

Identificar "Callejón sin salida" y "Fin de laberinto" es fácil (no hay ambigüedad). El problema surge con "LÍNEA" (puede ser Cruce o T) y "GIRO IZQ/DER" (puede ser giro simple o opción de recto). Para saber exactamente el tipo, el robot debe avanzar un "paso extra" y ver qué hay después (ver imagen 2).

Flujo de acciones:

Posibles problemas

Tema relacionado: Resolución de problemas de driver Arduino (ver vídeo).

Código

El código está modularizado en varios archivos:

RoBot_Maze_Solve_2 (C/C++)

/*------------------------------------------------------------------
  7 sensors Smart Robot - Maze Solver and Line Follower
  with programmable PID controller via BT
  => Basic movement based on Nano Mouse Robot, Michael Backus (https://www.akrobotnerd.com/)
  => Line follow based on https://samvrit.tk/tutorials/pid-control-arduino-line-follower-robot/
  => CREDIT to Patrick McCabe for path Solving Code, visit patrickmccabemakes.com!!
  Marcelo Jose Rovai - 23 April, 2016 - Visit: https://mjrobot.org
  ------------------------------------------------------------------*/
#include <Servo.h>
#include "robotDefines.h"
String command, device;
#include <SoftwareSerial.h>
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; // 1st pass
}

void loop() {
  ledBlink(1);
  BT1.println("Start First Pass"); Serial.println("Start First Pass");
  readLFSsensors(); mazeSolve(); // Primera pasada: resolver laberinto
  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(); // Segunda pasada: recorrer óptimo
  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;
}

Functions (C/C++)

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 direction) {
  path[pathLength] = direction; 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:
      case LEFT_TURN:
      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;
  }
}

GeneralFunctions (C/C++)

void ledBlink(int times) { for(int i=0;i 0) {
    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 = 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() {
  int LFS0 = digitalRead(lineFollowSensor0), LFS1 = digitalRead(lineFollowSensor1);
  int LFS2 = digitalRead(lineFollowSensor2), 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(" ==> P: "); Serial.print(P); Serial.print(" I: "); Serial.print(I); Serial.print(" D: "); Serial.print(D);
  Serial.print(" PID: "); Serial.println(PIDvalue);
}

sensorFunctions (C/C++)

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]==1)&&(LFSensor[1]==1)&&(LFSensor[2]==1)&&(LFSensor[3]==1)&&(LFSensor[4]==1)) { mode = CONT_LINE; error = 0; }
  else if((LFSensor[0]==0) && (farRightSensor < THRESHOLD)) { mode = RIGHT_TURN; error = 0; }
  else if((farLeftSensor < THRESHOLD) && (LFSensor[4]==0)) { mode = LEFT_TURN; error = 0; }
  else if((LFSensor[0]==0)&&(LFSensor[1]==0)&&(LFSensor[2]==0)&&(LFSensor[3]==0)&&(LFSensor[4]==0)) { mode = NO_LINE; error = 0; }
  else if((LFSensor[0]==0)&&(LFSensor[1]==0)&&(LFSensor[2]==0)&&(LFSensor[3]==0)&&(LFSensor[4]==1)) { mode = FOLLOWING_LINE; error = 4; }
  else if((LFSensor[0]==0)&&(LFSensor[1]==0)&&(LFSensor[2]==0)&&(LFSensor[3]==1)&&(LFSensor[4]==1)) { mode = FOLLOWING_LINE; error = 3; }
  else if((LFSensor[0]==0)&&(LFSensor[1]==0)&&(LFSensor[2]==0)&&(LFSensor[3]==1)&&(LFSensor[4]==0)) { mode = FOLLOWING_LINE; error = 2; }
  else if((LFSensor[0]==0)&&(LFSensor[1]==0)&&(LFSensor[2]==1)&&(LFSensor[3]==1)&&(LFSensor[4]==0)) { mode = FOLLOWING_LINE; error = 1; }
  else if((LFSensor[0]==0)&&(LFSensor[1]==0)&&(LFSensor[2]==1)&&(LFSensor[3]==0)&&(LFSensor[4]==0)) { mode = FOLLOWING_LINE; error = 0; }
  else if((LFSensor[0]==0)&&(LFSensor[1]==1)&&(LFSensor[2]==1)&&(LFSensor[3]==0)&&(LFSensor[4]==0)) { mode = FOLLOWING_LINE; error = -1; }
  else if((LFSensor[0]==0)&&(LFSensor[1]==1)&&(LFSensor[2]==0)&&(LFSensor[3]==0)&&(LFSensor[4]==0)) { mode = FOLLOWING_LINE; error = -2; }
  else if((LFSensor[0]==1)&&(LFSensor[1]==1)&&(LFSensor[2]==0)&&(LFSensor[3]==0)&&(LFSensor[4]==0)) { mode = FOLLOWING_LINE; error = -3; }
  else if((LFSensor[0]==1)&&(LFSensor[1]==0)&&(LFSensor[2]==0)&&(LFSensor[3]==0)&&(LFSensor[4]==0)) { mode = FOLLOWING_LINE; error = -4; }

  Serial.print(farLeftSensor); Serial.print(" <== LEFT RIGH==> "); Serial.print(farRightSensor);
  Serial.print(" mode: "); Serial.print(mode); Serial.print(" error:"); Serial.println(error);
}

void readLateralSensors(void) { }

RobotDefines.h (C/C++)

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; // A0
const int farLeftSensorPin  = 1; // A1
const int THRESHOLD = 150;

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

float Kp=50, Ki=0, Kd=0;
float error=0, P=0, I=0, D=0, PIDvalue=0;
float previousError=0, previousI=0;

#define RIGHT 1
#define LEFT -1

Servo leftServo, rightServo;

unsigned char dir;
char path[100] = "";
unsigned char pathLength = 0;
int pathIndex = 0;
unsigned int status = 0; // 0=resolviendo, 1=fin alcanzado

MotorFunctions (C/C++)

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 time) { motorForward(); delay(time); motorStop(); }
void motorBwTime(unsigned int time) { motorBackward(); delay(time); motorStop(); }

void motorTurn(int direction, int degrees) {
  leftServo.writeMicroseconds(1500 - (iniMotorPower+adj)*direction);
  rightServo.writeMicroseconds(1500 - iniMotorPower*direction);
  delay(round(adjTurn*degrees+20)); motorStop();
}

void motorPIDcontrol() {
  int leftMotorSpeed  = 1500 - (iniMotorPower+adj) - PIDvalue;
  int rightMotorSpeed = 1500 + iniMotorPower - PIDvalue;
  constrain(leftMotorSpeed,  1000, 2000);
  constrain(rightMotorSpeed, 1000, 2000);
  leftServo.writeMicroseconds(leftMotorSpeed);
  rightServo.writeMicroseconds(rightMotorSpeed);
}

void runExtraInch(void) { motorPIDcontrol(); delay(extraInch); motorStop(); }

void goAndTurn(int direction, int degrees) { motorPIDcontrol(); delay(adjGoAndTurn); motorTurn(direction, degrees); }

Esquemático

Diagrama de conexiones (imagen referenciada).

Proceso de manufactura

  1. Construye un robot que evita paredes con Raspberry Pi – Guía paso a paso
  2. Robot móvil que evita obstáculos basado en visión en Raspberry Pi
  3. Robot calígrafo de Star Automation brilla en NPE2018
  4. Wittmann Battenfeld lanza el WX153: Línea avanzada de robots para automatización de inyección de alta fuerza
  5. Bosch presenta solución de fabricación impulsada por IA en Hannover Messe
  6. FANUC amplía su línea de robots SCARA: seis modelos, cargas de 3‑20 kg, alcances de 400‑1100 mm
  7. Robótica Impulsada por IA: Máquinas Inteligentes de Vanguardia
  8. La IA permite a robots identificar más de 90 objetos mediante el tacto
  9. IA: Beneficios, riesgos e impacto en la industria
  10. Big Data vs IA: La sinergia detrás de la transformación digital
  11. Inteligencia artificial en retail: beneficios probados, no una moda pasajera