miércoles, 13 de marzo de 2024

Robot esquiva obstáculos con Arduino y un sensor de ultrasonidos

 Robot esquiva obstáculos con Arduino y un sensor de ultrasonidos


En este proyecto nos marcamos el objetivo de desarrollar un robot capaz de esquivar objetos. Un robot que avanzará en línea recta mientras que no detecte ningún obstáculo; en caso de detectar algún estorbo, girará sobre sí mismo hasta que esquive el objeto. Vamos a utilizar para ello una placa de Arduino UNO, un controlador de motores y un sensor de distancia. Una vez terminado el montaje, le añadiremos avisos luminosos.


Material necesario:

Placa Arduino o similar.

Módulo controlador de motores L298N.

Módulo HC-SR04,

sensor de ultrasonido y soporte.

Chasis robot Arduino 2WD.

2 ruedas.

Rueda loca.

2 motores reductores TT de doble eje.

Interruptor ON/OF de 2 pines.

Portapilas para la alimentación de la placa y 4 pilas AA.

Pila de 9 Voltios para el controlador y motores.

Cables y tornillos.

Protoboard pequeño.


Montaje del robot autónomo esquiva obstáculos.


Comenzamos montando los motores y añadiéndolos con sus soportes en forma de T al chasis del coche.


Asegurémonos de que en la imagen los motores estén colocados en la misma dirección a ambos lados del chasis y que la rueda pequeña del velocímetro esté ubicada en el interior del motor.

Luego, procedemos a montar la rueda loca utilizando las 4 tuercas de extensión, las cuales deben colocarse en la parte inferior del chasis.


montamos el soporte para las pilas y las ruedas.




Instalaremos el soporte para las pilas en la parte inferior del chasis. No existe un lugar específico predeterminado para su ubicación, por lo que las fijaciones mostradas en la imagen pueden ajustarse si los agujeros del chasis no coinciden.


Luego, procedemos a colocar el interruptor.


Hemos instalado el interruptor en el espacio central del chasis.


A continuación, procedemos al montaje de la placa Arduino y el controlador L298N. Fijamos ambas placas al chasis buscando la disposición más adecuada en el espacio disponible y asegurándonos de que coincidan algunos agujeros para utilizar tornillos de fijación. En nuestro caso, hemos utilizado 2 tuercas de extensión para asegurar la placa Arduino y un tornillo para el controlador L298N.


Para proporcionar un soporte más estable a las placas, es recomendable utilizar arandelas, tuercas o alargadores en la parte inferior para mantener los elementos firmemente sujetos entre el chasis y las placas.




En este paso, procederemos a conectar la placa Arduino con el controlador:

Utilizaremos los conectores INA e INB para controlar la velocidad de cada motor, conectando los pines PWM (modulación de ancho de pulso) etiquetados con el símbolo ~ (correspondientes a los pines digitales 11, 10, 9, 6, 5 y 3) de la siguiente manera:

  • ENA con el pin 6
  • IN1 con el pin 2
  • IN2 con el pin 7
  • IN3 con el pin 4
  • IN4 con el pin 3
  • ENB con el pin 5

A continuación, conectaremos los motores al módulo L298N. Encontraremos 4 conexiones marcadas como OUT1, OUT2, OUT3 y OUT4. En nuestro montaje, los cables del motor derecho se conectarán en las conexiones OUT1 (negro) y OUT2 (rojo), mientras que los cables del motor izquierdo se conectarán en OUT3 (rojo) y OUT4 (negro).

El interruptor colocado en el centro se utilizará para cortar la alimentación suministrada por la pila de 9V a los motores y al controlador. Para ello, conectaremos el cable rojo al interruptor. Desde la otra patilla del interruptor, pasaremos un cable rojo a la alimentación del controlador. El cable negro, de toma de tierra, se conectará al conector central de los 3 conectores de alimentación del controlador. Además, la toma de tierra del controlador L298N debe conectarse a una de las conexiones de toma de tierra (GND) de la placa Arduino.

Para alimentar la placa Arduino, utilizaremos el portapilas.

// MOTOR 1 int IN1 = 2; int IN2 = 7; int ENA = 6; // MOTOR 2 int IN3 = 4; int IN4 = 3; int ENB = 5; void setup() { pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT); pinMode(ENA, OUTPUT); pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT); pinMode(ENB, OUTPUT); } void loop() { //avanza(); //retrocede(); //derecha(); izquierda(); } void parada(uint16_t tiempo) { parar(); delay(tiempo); } void avanza(){ // MOTOR 1 analogWrite (ENA, 150); digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); // MOTOR 2 analogWrite(ENB, 150); digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); } void retrocede(){ // MOTOR 1 digitalWrite(ENA, HIGH); digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); // MOTOR 2 digitalWrite(ENB, HIGH); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); } void derecha(){ // MOTOR 1 analogWrite (ENA, 150); digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); // MOTOR 2 analogWrite (ENB, 150); digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); } void izquierda(){ // MOTOR 1 analogWrite (ENA, 150); digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); // MOTOR 2 analogWrite (ENB, 150); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); } void parar(){ // MOTOR 1 digitalWrite(ENA, 0); digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); // MOTOR 2 digitalWrite(ENB, 0); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); }


Probamos las funciones de avanzar, retroceder, girar a la derecha y girar a la izquierda. Si el movimiento de las ruedas es el correcto, lo dejamos tal como está; de lo contrario, debemos realizar ajustes en el código. Es importante recordar que los pines IN1 e IN2 controlan la dirección de rotación del motor, y al cambiar entre HIGH y LOW podemos modificar el sentido de giro.

Luego, instalamos el sensor de ultrasonido. En la parte frontal del chasis, fijamos el sensor de distancia utilizando un soporte diseñado para este propósito. En caso de no contar con dicho soporte, podemos utilizar una mini placa de pruebas (miniprotoboard) para asegurar el sensor en su lugar.



Para el cableado nos ayudaremos de un protoboard, el sensor dipone de 4 pines: GND que conectaremos al GND de la placa Arduino a través del protoboard, Vcc que conectaremos al pin de 5V de Arduino, también a través del protoboard, uniremos Echo al pin 8 de la placa y, por último, Trig, al pin digital 12 de Arduino. Pasamos a escribir el código para el funcionamiento del sensor de distancia. Al principio del código añadiremos:


#include "NewPing.h"

#define PIN_TRIG 12

#define PIN_ECHO 8

#define MAX_DISTANCIA 100

NewPing sonar(PIN_TRIG, PIN_ECHO, MAX_DISTANCIA);


En la primera línea del código se incluye la biblioteca NewPing.h, definimos seguidamente qué pines digitales van conectados a Trig y Echo en el sensor. En la cuarta línea, definimos la distancia máxima de medida para el sensor. Y, por último, creamos el objeto sonar, de la clase NewPing.


En la función setup(), inicializamos la comunicación serial:

Serial.begin(9600);


En la función loop(), comprobamos la distancia que mide:


delay(1000);

int tiempo = sonar.ping_median();

int distancia = tiempo / US_ROUNDTRIP_CM;

// Imprimir el tiempo medido en la consola

Serial.print("Tiempo: ");

Serial.print(tiempo);

Serial.println(" microsegundos");

// Imprimir la distancia medida en la consola

Serial.print("Distancia: ");

// US_ROUNDTRIP_CM constante para determinar la distancia. Convertir el tiempo en distancia (0 = indica fuera de rango)

Serial.print(distancia);

Serial.println(" cm");

Si subimos el código y comprobamos en el monitor serial:




Tenemos el siguiente esquema montado y hemos comprobado que los motores funcionan y el sensor de distancia mide correctamente.



Comenzamos a programar nuestro robot esquiva obstáculos: el sensor de distancia nos está midiendo continuamente el tiempo que tarda en volver la onda, si esta no vuelve, es que no hay ningun objeto delante, por lo tanto podemos pensar que puede avanzar libremente (fuera de rango es igual a 0).

if (distancia > 0){

}else{
  avanza();
}

Si hay una medida de tiempo, es que detecta algún objeto. Y, por lo tanto, nos calculará una distancia. Si se encuentra a menos de 30 cm de un objeto haremos que gire hacia la derecha, o que retroceda si se acerca a menos de 20 cm de un obstáculo. Si la distancia es igual o mayor de 30 cm, significa que tenemos camino libre hacia delante.

if (distancia > 0){

  if(distancia < 30){

    if(distancia > 20){

      retrocede();

      delay(200);

      parada(100);

    }else{

      derecha();

      delay(400);

      parada(500);

    }

  }else{

    avanza();

  }

 }else{

  avanza();

}

Si probamos este código comprobaremos que siempre retrocede y gira hacia la derecha cuando encuentra algún obstáculo.


Podemos hacer que gire también hacia la izquierda aleatoriamente lo que hará que le resulte más fácil salir de algún atolladero. ¿Cómo lo vamos a realizar? Primero crearemos una variable para guardar el número aleatorio.


long randomNumber;

Seguidamente, haremos que escoja entre 1 y 2, para ello añadiremos en el código:


if (distancia > 0){

  if(distancia < 30){

    if(distancia > 20){

      retrocede();

      delay(200);

      parada(100);

    }else{

      randomNumber = random(1,3);

      Serial.print("El numero aleatorio es = ");

      Serial.println(randomNumber);

      if (randomNumber == 1){

        izquierda();

        delay(400);

        parada(500);

      }else{

        derecha();

        delay(400);

        parada(500);

      }

    }

  }else{

    avanza();

  }

 }else{

  avanza();

}

Si probamos este nuevo código, veremos que unas veces girará hacia la derecha y otras a la izuierda, dando la posibilidad de escaparse y poder seguir avanzando en situaciones más dificiles.


¿Y si creamos un sistema de luces que nos avise hacia dónde avanza?


Un Led RGB nos avisará de la dirección del robot: rojo al retroceder, verde al avanzar y azul cuando gire.


El nuevo esquema sería:


En la imagen inferior hemos añadido el módulo Led RGB sobre la protoboard y hemos conectado los pines PWM de la placa Arduino:

Pin R al pin 11 de la placa
Pin G al pin 10 de la placa
Pin B al pin 9 de la placa
Y el GND al protoboard, que enlaza con el pin GND de Arduino.




Añadiremos al código las instrucciones necesarias para el funcionamiento de los avisos luminosos. Primero, crearemos unas constantes para indicar los pines correspondientes:

const int PinLedRojo = 11;
const int PinLedVerde = 10;
const int PinLedAzul = 9;
En la función setup() indicaremos que van a funcionar como salida:

pinMode(PinLedVerde, OUTPUT);
pinMode(PinLedRojo, OUTPUT);
pinMode(PinLedAzul, OUTPUT);
Creamos las funciones para los colores:

void verde(){
  Serial.println("¡Verde!");
  digitalWrite(PinLedRojo, LOW);
  digitalWrite(PinLedVerde, HIGH);
  digitalWrite(PinLedAzul, LOW);
}
void rojo(){
  Serial.println("¡Rojo!");
  digitalWrite(PinLedRojo, HIGH);
  digitalWrite(PinLedVerde, LOW);
  digitalWrite(PinLedAzul, LOW);
}
void azul(){
  Serial.println("¡Azul!");
  digitalWrite(PinLedRojo, LOW);
  digitalWrite(PinLedVerde, LOW);
  digitalWrite(PinLedAzul, HIGH);
}
Y en la función loop(), indicaremos qué color se enciende. Antes de la llamada a la función avanza(), indicamos que se encienda la luz verde; para la función retrocede(), la luz roja; y para las funciones derecha() e izquierda(), la luz azul.

El código completo quedaría como sigue:

#include "NewPing.h"
#define PIN_TRIG 12   // Pin del Arduino conectado al pin Trigger del sensor de ultrasonidos
#define PIN_ECHO 8   // Pin del Arduino conectado al pin Echo del sensor de ultrasonidos
#define MAX_DISTANCIA 100  // Distancia máxima a detectar en cm.
NewPing sonar(PIN_TRIG, PIN_ECHO, MAX_DISTANCIA);
// MOTOR 1
int IN1 = 2;
int IN2 = 7;
int ENA = 6;
// MOTOR 2
int IN3 = 4;
int IN4 = 3;
int ENB = 5;
long randomNumber;
const int PinLedRojo = 11;
const int PinLedVerde = 10;
const int PinLedAzul = 9;

void setup() {
  Serial.begin(9600);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(ENA, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(ENB, OUTPUT);
  pinMode(PinLedVerde, OUTPUT);
  pinMode(PinLedRojo, OUTPUT);
  pinMode(PinLedAzul, OUTPUT);
  randomSeed(analogRead(A0));  
}

void loop() {
  delay(1000);
  int tiempo = sonar.ping_median();
  int distancia = tiempo / US_ROUNDTRIP_CM;
  // Imprimir el tiempo medido en la consola
  Serial.print("Tiempo: ");
  Serial.print(tiempo);
  Serial.println(" microsegundos");
  // Imprimir la distancia medida en la consola
  Serial.print("Distancia: ");
  // US_ROUNDTRIP_CM constante para determinar la distancia. Convertir el tiempo en distancia (0 = indica fuera de rango)
  Serial.print(distancia);
  Serial.println(" cm");
  /*  */
  if (distancia > 0){  // hay espacio
    if(distancia < 30){
      //parar();
      if(distancia > 20){
        rojo();
        retrocede();
        delay(200);
        parada(100);
      }else{
        randomNumber = random(1,3);
        Serial.print("El numero aleatorio es = ");
        Serial.println(randomNumber);
        if (randomNumber == 1){
          azul();
          izquierda();
          delay(400);
          parada(500);
        }else{
          azul();
          derecha();
          delay(400);
          parada(500);
        }
      }
    }else{
      verde();
      avanza();
      //delay(200);
      //parada(500);
    }
  }else{
    verde();
    avanza();
  }
}
void parada(uint16_t tiempo) {
  parar();  // Para los motores
  delay(tiempo);        // Espera el tiempo que se le indique.
}

void avanza(){
 // MOTOR 1 
  analogWrite (ENA, 150);
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  // MOTOR 2
  analogWrite(ENB, 150);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
}
void retrocede(){
 // MOTOR 1 
  digitalWrite(ENA, HIGH);
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  // MOTOR 2
  digitalWrite(ENB, HIGH);
  digitalWrite(IN3, HIGH);
  digitalWrite(IN4, LOW);
}
void derecha(){
 // MOTOR 1 
  analogWrite (ENA, 150);
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  // MOTOR 2
  analogWrite (ENB, 150);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
}
void izquierda(){
 // MOTOR 1 
  analogWrite (ENA, 150);
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  // MOTOR 2
  analogWrite (ENB, 150);
  digitalWrite(IN3, HIGH);
  digitalWrite(IN4, LOW);
}
void parar(){
 // MOTOR 1 
  digitalWrite(ENA, 0);
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  // MOTOR 2
  digitalWrite(ENB, 0);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);
}

void verde(){
  Serial.println("¡Verde!");
  digitalWrite(PinLedRojo, LOW);
  digitalWrite(PinLedVerde, HIGH);
  digitalWrite(PinLedAzul, LOW);
}
void rojo(){
  Serial.println("¡Rojo!");
  digitalWrite(PinLedRojo, HIGH);
  digitalWrite(PinLedVerde, LOW);
  digitalWrite(PinLedAzul, LOW);
}
void azul(){
  Serial.println("¡Azul!");
  digitalWrite(PinLedRojo, LOW);
  digitalWrite(PinLedVerde, LOW);
  digitalWrite(PinLedAzul, HIGH);
}











Robot esquiva obstáculos con Arduino y un sensor de ultrasonidos

 Robot esquiva obstáculos con Arduino y un sensor de ultrasonidos En este proyecto nos marcamos el objetivo de desarrollar un robot capaz de...