Robot esquiva obstáculos con Arduino y un sensor de ultrasonidos
| Actualizado:
Comentarios: 2
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.
Contenido
- 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.
Nos fijaremos que en la imagen los motores están colocados en la misma dirección a ambos lados del chasis y la rueda pequeña del velocímetro, está colocada por la parte interior del motor.
Montamos la rueda loca. Utilizamos para ello las 4 tuercas de extensión, que se deben colocar por la parte inferior del chasis.
A continuación, montaremos el soporte para las pilas y las ruedas.
Colocaremos el soporte para pilas en la zona inferior del chasis, no hay un lugar predefinido donde colocarlo, las fijaciones que proponemos en la imagen pueden cambiarse por otra colocación mejor si no coinciden los agujeros del chasis.
Colocamos seguidamente el interruptor.
Hemos colocado el interruptor en el hueco central del chasis.
Montaje de la placa Arduino y el controlador L298N.
Fijamos la placa Arduino y la placa controladora de los motores L298N al chasis buscando la mejor colocación posible en el espacio del que disponemos, y algún agujero que coincida con los de las placas para utilizar tornillos que las sujeten. En nuestro caso, hemos sujetado la placa de Arduino con 2 tuercas de extensión, y el controlador L298N, con solo un tornillo.
Para que las placas tengan un mejor apoyo, deberemos usar alguna arandela, tuerca o alargador por la parte inferior de la placa para que haga de soporte y mantenga fijos los elementos entre el chasis y las placas.
En este paso conectaremos la placa Arduino con el controlador:
Los conectores INA e INB se utilizarán para controlar la velocidad de cada uno de los motores, realizaremos la conexión usando los pines PWM (Pulse-Width Modulation) que aparecen etiquetados con el símbolo ~ (son los pines digitales 11, 10, 9, 6, 5, y 3).
- 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 con el módulo L298N, podemos encontrar 4 conexiones etiquetadas como OUT1, OUT2, OUT3 y OUT4. En este montaje se han conectado los cables del motor derecho en las conexiones OUT1 (negro) y OUT2 (rojo). Los cables del motor izquierdo se han colocado en OUT3 (rojo) y OUT4 (negro).
El interruptor que se colocó en la parte central se va a utilizar para cortar la alimentación que suministra la pila de 9V a los motores y al controlador. Por tanto, tendremos que conectar el cable rojo al interruptor. Unimos ese cable al interruptor y desde la otra patilla del interuptor pasaremos un cable rojo a la alimentación del controlador. El cable negro, de toma de tierra, debe introducirse en el conector central de los 3 conectores de alimentación del controlador. Pero además, hay que tener en cuenta que la toma de tierra de controlador L298N debe conectarse también a una de las conexiones de toma de tierra (GND) de la placa Arduino.
Para alimentar la placa Arduino utilizaremos el portapilas.
Ya tenemos todo listo para que funcionen los motores, probemos:
// 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);
}
Testamos las funciones avanza, retrocede, derecha e izquierda. Si el giro de las ruedas es el correcto lo dejamos como está, si no es el deseado, debemos cambiar el código. Tened en cuenta que IN1 e IN2 son los responsables del giro del motor, cambiando HIGH y LOW podemos cambiar el sentido de giro.
Colocar el sensor de ultrasonido.
En la parte frontal del chasis sujetaremos el sensor de distancia ayudándonos de un soporte para el sensor ultrasónico. Si no tenemos soporte, podemos utilizar la misma miniprotoboard para sujetar el sensor.
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);
}