Arduino: explorador

Desarrollo básico de un robot controlado por Arduino que avanza evitando obstáculos.

  1. Componentes
    1. Placa Arduino UNO
    2. Sensor de ultrasonidos HC-SR04
    3. Controlador de motores L298N puente H
  2. Estructura del vehículo
  3. Programación: básico
  4. Programación: Avanzado
  5. Calibración

Componentes

Placa Arduino UNO

La placa utilizada para introducir las órdenes al robot es Arduino Uno.

Las placas Arduino utilizan el microcontrolador ATmega328, además permite el uso de diferentes ratios de transferencias así como la facilidad de no necesitar drivers para Linux o Mac.

La tarjeta es programable mediante código Open Source y puede ser utilizada para el desarrollo de objetos interactivos o conectada a software de nuestro PC. El programa de desarrollo de IDE ARDUINO puede ser gratuitamente descargado de la página oficial de Arduino.

Características Generales:

  • Microcontrolador ATmega328.
  • Voltaje de entrada 7-12V.
  • 14 pines digitales de I/O (6 salidas PWM).
  • 6 entradas análogas.
  • 32k de memoria Flash.
  • Reloj de 16MHz de velocidad.

Sensor de ultrasonidos HC-SR04

Un sensor de ultra sonidos es un dispositivo para medir distancias. Su funcionamiento se base en el envío de un pulso de alta frecuencia, no audible por el ser humano. Este pulso rebota en los objetos cercanos y es reflejado hacia el sensor, que dispone de un micrófono adecuado para esa frecuencia.

Midiendo el tiempo entre pulsos, conociendo la velocidad del sonido, podemos estimar la distancia del objeto contra cuya superficie impacto el impulso de ultrasonidos

El sonido tarda 29.2 ms en recorrer 1 cm (ida) y otros 29.2 ms en recorrer 1 cm (vuelta) .

El sensor es de baja precisión, tiene un rango de medición de 20 cm a 200 cm, con una resolución de 0.3 cm.

Aquí tienes alguno de los errores comunes:

El sensor dispone de 4 pines:

  • VCC: Alimentación de 5V.
  • GND: Conexión a tierra.
  • TRIG: pide al sensor que lance un pulso de sonido.
  • ECHO: Mide el tiempo que tarda el pulso de sonido en llegar a un obstáculo.

Controlador de motores L298N puente H

Arduino, y en general todos los autómatas, no disponen de potencia suficiente para mover actuadores como un motor. De hecho, la función de un procesador no debe ser ejecutar acciones si no mandar ejecutar acciones a drivers que realicen el “trabajo pesado”.

El L298N es un controlador (driver) de motores, que permite encender y controlar dos motores de corriente continua desde Arduino, variando tanto la dirección como la velocidad de giro.

  • Básicamente un L298N consiste en dos puentes-H, uno para la salida A y otro para la salida B.
  • Un puente-H es un componente ampliamente utilizado en electrónica para alimentar una carga de forma que podemos invertir el sentido de la corriente que le atraviesa.
Conexión del controlador de motores

Estructura del vehículo

En el vehículo tendremos:

  • Un controlador de motores en la parte superior, con el que ajustaremos la potencia de los motores según cada situación.
  • Dos motores de corriente continua, que impulsaran el vehículo mediante unas ruedas instaladas en ellos.
  • Estos motores serán alimentados mediante 4 pilas AA.
  • Un interruptor que controla sólo los motores, para que podamos parar el robot cuando queramos sin tener que desconectarlo de las pilas.

Programación: básico

Este código controla un robot que utiliza un sensor ultrasónico para detectar objetos en su entorno. Dependiendo de la distancia detectada, el robot puede girar a la derecha o a la izquierda para evitar obstáculos cercanos, reducir su velocidad si hay objetos detectados a una distancia segura, o avanzar a máxima velocidad si el camino está despejado. Los mensajes de estado se imprimen en el monitor serial para facilitar la depuración y seguimiento del comportamiento del robot.

int pinTrigger = 12; 
int pinEcho = 11; 
int M1Forward = 5; 
int M1Back = 6;
int M2Forward = 10;
int M2Back = 9;

void setup() {
  Serial.begin(9600);
  pinMode (pinTrigger, OUTPUT);
  pinMode (pinEcho, INPUT);
  pinMode (M1Forward, OUTPUT);
  pinMode (M1Back, OUTPUT);
  pinMode (M2Forward, OUTPUT);
  pinMode (M2Back, OUTPUT);
  delay (3500);
}

void loop() {
 long duration, distance;
 digitalWrite(pinTrigger, LOW);
 delayMicroseconds (2);
 digitalWrite(pinTrigger, HIGH);
 delayMicroseconds (10);
 digitalWrite(pinTrigger, LOW);
 duration = pulseIn(pinEcho, HIGH);
 Serial.println("Duration");
 Serial.println(duration);
 distance = (duration/2) / 29.1;
 if (distance < 50) {
   delayMicroseconds (500);
   Serial.println("Objeto encontrado. Redireccionando.");
   int direccion = random (1, 2);
   Serial.println(direccion);
     if (direccion==1){
      Serial.println("derecha");
      analogWrite(M1Forward, 0);
      analogWrite(M1Back, 250);
      analogWrite(M2Forward, 230);
      analogWrite(M2Back, 0);
     }
     else {
      Serial.println("izquierda");
      analogWrite(M1Forward, 230);
      analogWrite(M1Back, 0);
      analogWrite(M2Forward, 0);
      analogWrite(M2Back, 250);
     }
       delay(125);
       analogWrite(M1Forward, 0);
       analogWrite(M1Back, 0);
       analogWrite(M2Forward, 0);
       analogWrite(M2Back, 0);
   }
 if ((distance > 50)&&(distance < 75)) {
    analogWrite(M1Forward, 150);
    analogWrite(M1Back, 0);
    analogWrite(M2Forward, 120);
    analogWrite(M2Back, 0);
    Serial.println("Reduzco velocidad, objeto detectado en el radar.");
   }
 
 if (distance > 75) {
    analogWrite(M1Forward, 255);
    analogWrite(M1Back, 0);
    analogWrite(M2Forward, 255);
    analogWrite(M2Back, 0);
    Serial.println("avanzo, campo libre.");
 
 }
}

Esta es la explicación del código:

  1. Se definen las conexiones de los componentes del robot en el Arduino. Los pines del Arduino se asignan a variables para facilitar su uso posterior.
  2. La función setup() se ejecuta una vez al inicio del programa y se encarga de configurar los pines del Arduino. En este caso, se establecen los pines de disparo y eco del sensor ultrasónico como salida y entrada respectivamente, y los pines de control de los motores como salidas. También se utiliza una pausa de 3.5 segundos (3500 milisegundos) antes de comenzar el bucle principal.
  3. La función loop() es el bucle principal del programa y se ejecuta continuamente después de que se haya completado la función setup(). Aquí es donde ocurre la detección de objetos y las decisiones de movimiento.
  4. Se declara una variable duration para almacenar la duración del pulso de eco del sensor ultrasónico, y una variable distance para almacenar la distancia calculada en centímetros.
  5. Se apaga el pin de disparo del sensor ultrasónico, se espera un corto período de tiempo y luego se enciende durante 10 microsegundos para generar un pulso ultrasónico.
  6. Se apaga nuevamente el pin de disparo para finalizar el pulso.
  7. La función pulseIn() se utiliza para medir la duración del pulso de eco que regresa del sensor ultrasónico. La duración se almacena en la variable duration.
  8. Se imprime la duración en el monitor serial para fines de depuración.
  9. Se calcula la distancia dividiendo la duración por 2 y luego dividiéndola por 29.1 (la velocidad del sonido en el aire a temperatura ambiente).
  10. Si la distancia es menor a 50 centímetros, se considera que hay un objeto cercano y se toman las siguientes acciones:
    • a. Se espera un breve período de tiempo de 500 microsegundos.
    • Se imprime un mensaje en el monitor serial indicando que se ha encontrado un objeto y se está redirigiendo.
    • Se genera un número aleatorio entre 1 y 2 para determinar la dirección de giro del robot.
    • Si el número aleatorio es 1, el robot gira a la derecha. Se establece la velocidad de los motores M1 y M2 para girar hacia la derecha.
    • Si el número aleatorio es diferente de 1, el robot gira a la izquierda. Se establece la velocidad de los motores M1 y M2 para girar hacia la izquierda.
    • Después de girar durante 125 milisegundos, se detienen los motores estableciendo el valor de PWM (modulación por ancho de pulso) en 0 para todos los pines de control de los motores.
  11. Si la distancia está entre 50 y 75 centímetros, se considera que hay un objeto detectado en el radar pero a una distancia más segura. En este caso, se reduce la velocidad de los motores para evitar colisiones. Se establecen los valores de PWM en los pines de control de los motores M1 y M2 para disminuir la velocidad.
  12. Si la distancia es mayor a 75 centímetros, se considera que el camino está despejado y el robot avanza a máxima velocidad. Se establecen los valores de PWM en los pines de control de los motores M1 y M2 para avanzar a toda velocidad.
  13. Se imprime un mensaje en el monitor serial indicando el estado del movimiento del robot («Reduzco velocidad, objeto detectado en el radar» o «Avanzo, campo libre»).
  14. El bucle loop() continúa y se repite el proceso de detección y toma de decisiones en cada iteración.

Programación: Avanzado

  • Uso de constantes para los pines y valores fijos: En lugar de utilizar variables para almacenar los pines y valores fijos, puedes utilizar constantes. Esto hace que el código sea más legible y facilita la modificación de los valores en un solo lugar si es necesario. Aquí está el código con las constantes correspondientes:
const int PIN_TRIGGER = 12;
const int PIN_ECHO = 11;
const int M1_FORWARD = 5;
const int M1_BACK = 6;
const int M2_FORWARD = 10;
const int M2_BACK = 9;

void setup() {
  Serial.begin(9600);
  pinMode(PIN_TRIGGER, OUTPUT);
  pinMode(PIN_ECHO, INPUT);
  pinMode(M1_FORWARD, OUTPUT);
  pinMode(M1_BACK, OUTPUT);
  pinMode(M2_FORWARD, OUTPUT);
  pinMode(M2_BACK, OUTPUT);
  delay(3500);
}

// Resto del código...
  • Uso de funciones para el control de los motores: En lugar de repetir las mismas líneas de código para controlar los motores en diferentes partes del programa, puedes crear funciones que realicen estas tareas y llamar a esas funciones en los lugares correspondientes. Esto mejora la legibilidad del código y facilita la modificación del comportamiento de los motores en un solo lugar. Luego, en el bucle loop(), puedes llamar a estas funciones en lugar de repetir las líneas de código correspondientes.
// Función para detener los motores
void stopMotors() {
  analogWrite(M1_FORWARD, 0);
  analogWrite(M1_BACK, 0);
  analogWrite(M2_FORWARD, 0);
  analogWrite(M2_BACK, 0);
}

// Función para girar a la derecha
void turnRight() {
  analogWrite(M1Forward, 0);
  analogWrite(M1Back, 250);
  analogWrite(M2Forward, 230);
  analogWrite(M2Back, 0);
}

// Función para girar a la izquierda
void turnLeft() {
  analogWrite(M1Forward, 230);
  analogWrite(M1Back, 0);
  analogWrite(M2Forward, 0);
  analogWrite(M2Back, 250);
}

// Resto del código...
  • Implementación de una función para medir la distancia: Puedes encapsular la lógica para medir la distancia en una función separada para mejorar la modularidad y reutilización del código. Aquí tienes un ejemplo:
// Función para medir la distancia en centímetros
int measureDistance() {
  digitalWrite(PIN_TRIGGER, LOW);
  delayMicroseconds(2);
  digitalWrite(PIN_TRIGGER, HIGH);
  delayMicroseconds(10);
  digitalWrite(PIN_TRIGGER, LOW);
  long duration = pulseIn(PIN_ECHO, HIGH);
  int distance = (duration / 2) / 29.1;
  return distance;
}

// Resto del código...

Así quedaría el código completo:

const int PIN_TRIGGER = 12;
const int PIN_ECHO = 11;
const int M1_FORWARD = 5;
const int M1_BACK = 6;
const int M2_FORWARD = 10;
const int M2_BACK = 9;

void setup() {
  Serial.begin(9600);
  pinMode(PIN_TRIGGER, OUTPUT);
  pinMode(PIN_ECHO, INPUT);
  pinMode(M1_FORWARD, OUTPUT);
  pinMode(M1_BACK, OUTPUT);
  pinMode(M2_FORWARD, OUTPUT);
  pinMode(M2_BACK, OUTPUT);
  delay(3500);
}

void stopMotors() {
  analogWrite(M1_FORWARD, 0);
  analogWrite(M1_BACK, 0);
  analogWrite(M2_FORWARD, 0);
  analogWrite(M2_BACK, 0);
}

void turnRight() {
  analogWrite(M1_FORWARD, 0);
  analogWrite(M1_BACK, 250);
  analogWrite(M2_FORWARD, 230);
  analogWrite(M2_BACK, 0);
}

void turnLeft() {
  analogWrite(M1_FORWARD, 230);
  analogWrite(M1_BACK, 0);
  analogWrite(M2_FORWARD, 0);
  analogWrite(M2_BACK, 250);
}

int measureDistance() {
  digitalWrite(PIN_TRIGGER, LOW);
  delayMicroseconds(2);
  digitalWrite(PIN_TRIGGER, HIGH);
  delayMicroseconds(10);
  digitalWrite(PIN_TRIGGER, LOW);
  long duration = pulseIn(PIN_ECHO, HIGH);
  int distance = (duration / 2) / 29.1;
  return distance;
}

void loop() {
  int distance = measureDistance();
  Serial.println("Distance: " + String(distance) + " cm");

  if (distance < 50) {
    delayMicroseconds(500);
    Serial.println("Object detected. Redirecting.");
    int direction = random(1, 3);

    if (direction == 1) {
      Serial.println("Turning right");
      turnRight();
    } else {
      Serial.println("Turning left");
      turnLeft();
    }

    delay(125);
    stopMotors();
  } else if (distance >= 50 && distance < 75) {
    analogWrite(M1_FORWARD, 150);
    analogWrite(M1_BACK, 0);
    analogWrite(M2_FORWARD, 120);
    analogWrite(M2_BACK, 0);
    Serial.println("Reducing speed, object detected on radar.");
  } else {
    analogWrite(M1_FORWARD, 255);
    analogWrite(M1_BACK, 0);
    analogWrite(M2_FORWARD, 255);
    analogWrite(M2_BACK, 0);
    Serial.println("Advancing, clear path.");
  }
  
  delay(100); // Pequeña pausa entre iteraciones del bucle loop
}

Calibración

(Entrada en construcción)

Deja un comentario

Comments (

0

)

Diseña un sitio como este con WordPress.com
Comenzar