Robot esquiva obstáculos con Arduino
En este artículo vamos a construir un robot muy específico con Arduino, un robot que esquiva obstáculos y consigue salir de un laberinto.
Construcción del Robot
Materiales
El chasis que verás en las fotos es específico, creado a medida por la empresa What's Next? para el proyecto [Robots Boost Skills]. El resto de componentes son genéricos y se pueden comprar Arduinos oficiales, What's Next Yellow o cualquier clon compatible.
Esta es la lista de materiales:
- Chasis que permita 2 ruedas con motor DC analógicos y una rueda delantera.
- 2 Motores DC analógicos con reducción y ruedas.
- Arduino Uno o equivalente.
- 3 Sensores ultrasónicos HR-SC04
- Arduino Motor Shield o alguno que tenga el mismo Chip L298
- Baterías, entre 9V y 12V
Construcción del Chasis
En el caso del robot del ejemplo, el chasis tiene todos los elementos necesarios. Si lo tienes que construir, aquí tienes algunos consejos:
- Se recomienda cuidar el centro de gravedad de robot para que no plante rueda y tenga la adherencia necesaria. Por ejemplo, las baterías deberían estar entre las ruedas motrices y la rueda delantera.
- Los sensores deberían estar situados de forma que se cubra el mismo ángulo por los dos lados. En nuestro caso, al haber 3 sensores, se han puesto uno en el centro hacia adelante y los otros en un ángulo de 45º.
- Hay que dejar espacio para los cables y para poder modificar las conexiones sin necesidad de desmontar todo el robot.
Cableado
Hay que tenen en cuenta que el Arduino Motor Shield utiliza algunos pines para su funcionamiento:
Function A B Direction D12 D13 PWM D3 D11 Brake D9 D8 Curr. Sensor A0 A1
Por tanto, no podemos utilizar esos pines. Cada sensor necesita 2 pines digitales para el echo i el trig. Así que necesitamos ocupar 6 pines digitales, más los 6 para los motores. Para los sensores utilitzaremos los pines 2,4,5,6,7,10.
Este es el esquema de cables del robot:
Alimentaremos tanto al Arduino como al Shield por la entrada del shield. Este alimenta al arduino por el pin VIN. Como el voltaje va a ser moderado, no es necesario aislar el Arduino del shield. Si los motores necesitaran más de 9V hay que cortar el pin VIN y alimentar por separado el Arduino.
Programación del Robot
Como este artículo pretende ser didàctico, no vamos a poner la solución final diréctamente (tampoco hay una solución final perfecta). Iremos poniendo versiones distintas en las que se explican las sucesivas mejoras.
Leyendo de los sensores
El siguiente ejemplo lee la distancia de los sensores y la imprime por el puerto serie:
int trigiz = 7;
int echoiz = 10;
int trigcen = 5;
int echocen = 6;
int trigder = 2;
int echoder = 4;
int distiz, distcen, distder;
void setup()
{
pinMode(trigiz , OUTPUT);
pinMode(trigcen , OUTPUT);
pinMode(trigder , OUTPUT);
pinMode(echoiz , INPUT);
pinMode(echocen , INPUT);
pinMode(echoder , INPUT);
Serial.begin(9600);
}
int ping(int trig, int echo){
digitalWrite(trig,LOW);
delayMicroseconds(2);
digitalWrite(trig,HIGH);
delayMicroseconds(10);
digitalWrite(trig,LOW);
int duration = pulseIn(echo,HIGH);
int distance = duration * 0.034 / 2;
return(distance);
}
void loop()
{
distiz = ping(trigiz,echoiz);
Serial.print("Izquierda: "); Serial.print(distiz);
distcen = ping(trigcen,echocen);
Serial.print(" Centro: "); Serial.print(distcen);
distder = ping(trigder,echoder);
Serial.print(" Derecha: "); Serial.println(distder);
}
Como se ve, necesita enviar un trig de 10 microsegundos y escuchar hasta que vuelve. Después calcula la distancia teniendo en cuenta la velocidad del sonido.
Mover el Robot
En nuestro proyecto, los motores se mueven con el Arduino Motor Shield rev3. Aquí hay un ejemplo de cómo mover los motores adelante y atrás:
void setup() {
//Setup Channel A
pinMode(12, OUTPUT); //Initiates Motor Channel A pin
pinMode(9, OUTPUT); //Initiates Brake Channel A pin
//Setup Channel B
pinMode(13, OUTPUT); //Initiates Motor Channel A pin
pinMode(8, OUTPUT); //Initiates Brake Channel A pin
}
void loop(){
//Motor A forward @ full speed
digitalWrite(12, HIGH); //Establishes forward direction of Channel A
digitalWrite(9, LOW); //Disengage the Brake for Channel A
analogWrite(3, 255); //Spins the motor on Channel A at full speed
//Motor B backward @ half speed
digitalWrite(13, LOW); //Establishes backward direction of Channel B
digitalWrite(8, LOW); //Disengage the Brake for Channel B
analogWrite(11, 123); //Spins the motor on Channel B at half speed
delay(3000);
digitalWrite(9, HIGH); //Engage the Brake for Channel A
digitalWrite(8, HIGH); //Engage the Brake for Channel B
delay(1000);
//Motor A backward @ full speed
digitalWrite(12, LOW); //Establishes backward direction of Channel A
digitalWrite(9, LOW); //Disengage the Brake for Channel A
analogWrite(3, 123); //Spins the motor on Channel A at half speed
//Motor B forward @ full speed
digitalWrite(13, HIGH); //Establishes forward direction of Channel B
digitalWrite(8, LOW); //Disengage the Brake for Channel B
analogWrite(11, 255); //Spins the motor on Channel B at full speed
delay(3000);
digitalWrite(9, HIGH); //Engage the Brake for Channel A
digitalWrite(8, HIGH); //Engage the Brake for Channel B
delay(1000);
}
Funciones
La programación de este robot puede ser muy larga y complicada. Hay algunas cosas que se hacen siempre igual y que no es necesario volver a copiar y pegar el código. Por ejemplo, la lectura de los sensores es igual o el movimiento del robot.
En el ejemplo de los sensores hay una función para leer de los sensores:
int ping(int trig, int echo){
digitalWrite(trig,LOW);
delayMicroseconds(2);
digitalWrite(trig,HIGH);
delayMicroseconds(10);
digitalWrite(trig,LOW);
int duration = pulseIn(echo,HIGH);
int distance = duration * 0.034 / 2;
return(distance);
}
También se puede hacer una función para moverse:
void front(){
digitalWrite(12, LOW); //Establishes backward direction of Channel A
digitalWrite(9, LOW); //Disengage the Brake for Channel A
analogWrite(3, 123); //Spins the motor on Channel A at half speed
digitalWrite(13, HIGH); //Establishes forward direction of Channel B
digitalWrite(8, LOW); //Disengage the Brake for Channel B
analogWrite(11, 123); //Spins the motor on Channel B at full speed
}
void left(){
digitalWrite(12, LOW); //Establishes backward direction of Channel A
digitalWrite(9, LOW); //Disengage the Brake for Channel A
analogWrite(3, 123); //Spins the motor on Channel A at half speed
digitalWrite(13, HIGH); //Establishes forward direction of Channel B
digitalWrite(8, HIGH); //Disengage the Brake for Channel B
analogWrite(11, 0); // <-- Stop the B motor
}
void right(){
digitalWrite(12, LOW); //Establishes backward direction of Channel A
digitalWrite(9, HIGH); //Disengage the Brake for Channel A
analogWrite(3, 0); // <-- Stops the A motor
digitalWrite(13, HIGH); //Establishes forward direction of Channel B
digitalWrite(8, LOW); //Disengage the Brake for Channel B
analogWrite(11, 123);
}
Incluso se podría hacer que la función recibiera el tiempo que deseamos que esté moviendose y la velocidad:
void front(int time, int speed){
digitalWrite(12, LOW); //Establishes backward direction of Channel A
digitalWrite(9, LOW); //Disengage the Brake for Channel A
analogWrite(3, speed); //Spins the motor on Channel A at half speed
digitalWrite(13, HIGH); //Establishes forward direction of Channel B
digitalWrite(8, LOW); //Disengage the Brake for Channel B
analogWrite(11, speed); //Spins the motor on Channel B at full speed
delay(time);
}
Cambiar de dirección en función de los sensores
Una vez ya sabemos cómo mover el robot y leer de los sensores, debemos saber utilitzar la información de los sensores para decidir la dirección del robot.
La primera aproximación a esto es ir hacia el lado que más distancia lee del robot. Más adelante ya se intentará que sea un poco más inteligente. Para ello, hay que modificar la función loop() para llamar a las funciones de lectura de los sensores:
void loop()
{
distiz = ping(trigiz,echoiz);
Serial.print("Izquierda: "); Serial.print(distiz);
distcen = ping(trigcen,echocen);
Serial.print(" Centro: "); Serial.print(distcen);
distder = ping(trigder,echoder);
Serial.print(" Derecha: "); Serial.println(distder);
if(distiz > discen && distiz > disder){
left(100,130); # Tiempo y velocidad
}
if(disder > discen && disder > distiz){
right(100,130);
}
if(discen > disder && discen > distiz){
front(100),130;
}
}