Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Robô Seguidor de Linha com IA no Arduino Nano

Componentes e Materiais

Componente Quantidade
Arduino Nano 1
Transceptor Bluetooth RF Sem Fio Seeed Master/Slave 1
Módulo Sensor IR Infravermelho Reflexivo Fotoelétrico (Amazon Web Services) 1
Servo de Rotação Contínua Sony 1
Bateria Android 4,8V 1

Aplicativos e Serviços Online

Sobre o Projeto

Após desenvolver o Robô Pick & Place baseado em Arduino, a ideia foi desenvolver um robô seguidor de linha baseado em Inteligência Artificial (IA). Desenvolver um seguidor de linha simples era bom para iniciante, bastava adicionar sensores infravermelhos em vez de servomotores no projeto recente. "A ideia era testar minhas habilidades em nível avançado".

Embora fosse tarefa difícil, pesquisas online ajudaram muito.

Por que Robô Seguidor de Linha Baseado em IA?

Quando se trata de IA, é tecnologia recente com potencial para mudar o ambiente mundial e a vida humana efetivamente. Um sonho que pode se tornar real colocando inteligência em nossos robôs para entender problemas reais e resolvê-los adequadamente - esse sonho só se torna realidade através da Inteligência Artificial, por isso todos falam sobre ela.

Embora meu protótipo tenha um pouco de inteligência, é apenas o começo para mover meus projetos rumo à IA. Aqui explico passo a passo o desenvolvimento de robô seguidor de linha usando IA em linguagem simples. Por falta de recursos, não construirei o robô, mas explico bem como você pode construí-lo fazendo pesquisas. Tentarei fornecer códigos precisos que funcionem no Arduino IDE, para que você desenvolva seu primeiro robô IA facilmente.

Apoie-me no Patreon: https://bit.ly/31NfQ6A

"Encontra as taxas mais baratas de equipamentos nos EUA na descrição deste vídeo, clique no vídeo."

Uso de Sensores Infravermelhos

Robô Seguidor de Linha com IA no Arduino Nano

Nosso projeto inclui 7 sensores infravermelhos conforme diagrama:

Papel dos 5 Sensores de Controle PID: Geram saída digital alta ou baixa (1, 0). O sensor centralizado em relação à linha preta produz HIGH (1). Resultados possíveis:

Outros dois sensores (esquerda e direita): saída analógica alta ou baixa.

Para armazenar valores dos 5 sensores, criamos array:

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

Para sensores esquerda/direita, usamos inteiros:

int farleft_sensor=0;

Como temos 5 sensores para armazenar caminho esquerdo/direito em array:

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

REGRA DA MÃO ESQUERDA EM LABIRINTOS

Assista este vídeo para entender a regra da mão esquerda em labirintos.

Em resumo, a Regra da Mão Esquerda:

A chave é identificar as interseções, definindo qual caminho seguir baseado nas regras. Em labirinto 2D, existem 8 tipos de interseções:

Robô Seguidor de Linha com IA no Arduino Nano

Ações possíveis nas interseções:

Aplicando a "Regra da Mão Esquerda", ações reduzem a uma opção cada:

Quase lá! "Mantenha a calma!"

Quando robô atinge "Beco Sem Saída" ou "Fim do Labirinto", é fácil identificá-los (já implementamos essas ações no Robô Seguidor de Linha, lembra?). O problema é quando encontra uma "LINHA" - pode ser "Cruzamento" (1) ou "T" (2). Também em "CURVA ESQUERDA/DIREITA", pode ser curva simples (opções 3 ou 4) ou opções de ir reto (5 ou 6). Para descobrir exatamente o tipo de interseção, passo adicional: robô deve andar uma "polada extra" e ver o que vem a seguir.

Fluxo de ações possíveis:

Nota: No caso de "CURVA ESQUERDA", pode pular o teste, pois irá à esquerda de qualquer forma. Deixei explicação genérica para clareza. No código real, pulo este teste.

Alguns problemas que podem surgir:

Tópico Relacionado: RESOLVENDO PROBLEMA DE DRIVER ARDUINO

Assista este vídeo para resolver problema de driver Arduino.

Código

RoBot_Maze_Solve_2 (Principal)

/*------------------------------------------------------------------
 7 sensors Smart Robot - Maze Solver and Line Follower with programable PID controller via BT
 ==> Basic movement based on Nano Mouse Robot, developed by Michael Backus (https://www.akrobotnerd.com/ )
 ==> Line follow based on https://samvrit.tk/tutorials/pid-control-arduino-line-follower-robot/?ckattempt=1
 ==> CREDIT to Patrick McCabe for the path Solving Code, visit patrickmccabemakes.com!!
 Marcelo Jose Rovai - 23 April, 2016 - Visit: https://mjrobot.org
-------------------------------------------------------------------*/
#include <Servo.h>
#include "robotDefines.h"

String command;
String device;
#include <SoftwareSerial.h>
SoftwareSerial BT1(10, 11); // Pin 10 Rx, Pin 11 Tx

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

Funções Principais

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 = 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: 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;
  }
}

Funções Gerais

void ledBlink(int times) {
  for (int i = 0; i < times; i++) {
    digitalWrite(ledPin, HIGH); delay(500);
    digitalWrite(ledPin, LOW); delay(500);
  }
}

void checkBTcmd() {
  while (BT1.available()) {
    delay(10);
    char c = BT1.read();
    device += c;
  }
  if (device.length() > 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);
  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(" ==> P: "); Serial.print(P); Serial.print(" I: "); Serial.print(I); 
  Serial.print(" D: "); Serial.print(D); Serial.print(" PID: "); Serial.println(PIDvalue);
}

Funções de Sensores

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

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;
int 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; Servo rightServo;

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

Funções de Motor

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áticos

Esquemático do circuito disponível na seção de esquemáticos do projeto original.

Processo de manufatura

  1. Construa um Robô que Evita Paredes com Raspberry Pi – Guia Passo a Passo
  2. Robô Móvel Evita-Obstáculos Baseado em Visão no Raspberry Pi
  3. Robô Empunhando Pincel da Star Automation Brilha na NPE2018
  4. Wittmann Battenfeld Lança Linha de Robôs WX153 para Automação de Injeção de Alta Força
  5. Bosch Apresenta Solução de Manufatura Impulsionada por IA na Hannover Messe
  6. FANUC Expande Linha SCARA: Seis Modelos, Cargas de 3–20 kg, Alcance de 400–1100 mm
  7. Inteligência Artificial no Varejo: Benefícios Comprovados, Não Moda Passageira
  8. IA Capacita Robôs a Identificar 90+ Objetos pelo Tato
  9. Robótica Impulsionada por IA: Máquinas Inteligentes de Ponta
  10. IA: Benefícios, Riscos e Impacto na Indústria