lunes, 5 de septiembre de 2011

ArduWallyTruck: camión de radiocontrol controlado con Arduino

Este es mi primer proyecto realizado con Arduino.

El reto que me planteé era controlar un camión de radio control colocándole un sensor de distancia infrarrojo, situado sobre un servo de modo que cuando detecta un obstáculo hace girar al sensor de modo que pueda descubrir si hay algún obstáculo que le impida girar.


Para controlar el camión he utilizado los controles de marcha y giro del emisor de radiocontrol (que he colocado sobre el propio camión) manejándolos mediante cada uno de los cuatro MOSFET, símplemente para abrir o cerar cada uno de los cuatro interruptores.
Tanto el sensor de infrarrojos como el servo sobre el que se apoya van directamente a la placa de Arduino.
Debido a la velocidad que desarrolla el camión unido al poco ángulo de giro de sus ruedas me ha obligado a frenar cuando se detecta un obstáculo al frente y entonces, tras detectar si hay espacio para torcer retroceder girando. Inicialmente para frenar únicamente cortaba la alimentación del motor, pero debido a la inercia conseguía unos estupendos choques. Así pues para frenar pongo la marcha atrás el tiempo necesario para paralo. Otro problema fue el servo utilizado para conseguir el giro del sensor. En las pruebas realizadas alimentando la placa mediante USB funcionaba perfectamente, pero cuando lo alimentaba mediante la batería fallaba. Para solucionarlo cambié el servo por otro más pequeño, con menos consumo de energía.

Componentes utilizados:

  • El camión de radiocontrol comprado en una tienda de chinos por 9,90€ (yo lo compré en la tienda, no en la web que enlazo).
  • Arduino UNO
  • 4 MOSFET modelo IRF3710 para utilizarlos como interruptores de las señales de avance y retroceso del motor y de giro a derecha o izquierda. Aprendí a utilizarlos gracias al proyecto Landruino
  • Una caja para la batería de 9v.
    .
  • 1 sensor de distancia infrarrojo
  • Una placa para prototipos
  • Un servo para hacer girar el sensor. Usé este microservo porque cuando utilicé otro mayor no funcionaba adecuadamente que debido a la batería, porque cuando lo alimentaba con la salida usb iba perfectamente.

Diagrama (¡mi primer diagrama con Fritzing!):


Programa:

 #include <Servo.h>
 
 const int distanciaLimiteFrontal=180; // Valor mas alto cuanto más cerca
 const int distanciaLimiteLateral=450;
 const int pinAdelante = 9;    // connected to the base of the transistor
 const int pinAtras = 10;
 const int pinDerecha = 11;
 const int pinIzquierda = 12;
 const int retardoServo = 1000;
 const int tiempoGiro = 1100;
 const int tiempoFreno = 600;

 int sensorValue;
 Servo myservo; 
 int grados;

 //Permite saber hacia donde se giró la vez anterior
 //para intentar girar de nuevo hacia ahí
 int gradosGiroAnterior=0;

 void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600);

   // preparo el led como salida
   pinMode(13, OUTPUT);
   // set  the transistor pin as output:
   pinMode(pinAdelante, OUTPUT);
   pinMode(pinAtras, OUTPUT);
   pinMode(pinDerecha, OUTPUT);
   pinMode(pinIzquierda, OUTPUT);
  
   myservo.attach(8);  // attaches the servo on pin 8 to the servo object
   myservo.write(90); // Lo inicializamos en el centro
   delay(retardoServo);

 }

 void loop() {
   //Siempre comenzamos iteracción con el servo al frente
   centraServo();
   sensorValue = analogRead(A2);
   Serial.print("En loop sensorValue=");
   Serial.println(sensorValue, DEC);  
   //Detectamos si hay algún obstáculo al frente
   if (sensorValue < distanciaLimiteFrontal){
     avanza();
   }else{  
     paraConRetroceso(); //Paramos mientras se decide hacia donde girar
     Serial.print("Se ha rebasado la distancia limite. La distancia para colision es ");
     Serial.println(sensorValue, DEC); 
    
     grados=gradosDireccionASeguir();
     if (grados == 0){
       giroConRetroceso(pinIzquierda);
     }else if (grados == 180){
       giroConRetroceso(pinDerecha);
     }else{
       Serial.println("No se puede girar.");
     }   
   }
 }


 void avanza(){
   Serial.println("Avanza");
   digitalWrite(pinAdelante, HIGH);
   Serial.println("Damos potencia al motor");
 }

 void centraServo(){
   if (myservo.read()!=90){
     Serial.println("Se coloca a 90 grados");
     myservo.write(90); //Colocamos el servo orientado al frente
     delay(retardoServo); //Le damos tiempo a colocarse
   }  
 }

 void para(){
   Serial.println("Para");
   digitalWrite(pinAdelante, LOW);
   digitalWrite(pinAtras, LOW);
 }

 // Para evitar que avance por la inercia
  void paraConRetroceso(){
   Serial.println("Para");
   digitalWrite(pinAdelante, LOW);
   digitalWrite(pinAtras, HIGH);
   delay(tiempoFreno);
   digitalWrite(pinAtras, LOW);
 }


  void giroConRetroceso(int pinSentido){
    Serial.print("Giro con retroceso. pinSentido: ");
    Serial.println(pinSentido, DEC);
    digitalWrite(pinAdelante, LOW); //Paro la marcha adelante
    digitalWrite(pinSentido, HIGH);
    digitalWrite(pinAtras, HIGH);
    delay(tiempoGiro);
    digitalWrite(pinAtras, LOW);
    digitalWrite(pinSentido, LOW);
 }


 int distanciaObstaculo(int grados){
     myservo.write(grados);
     delay(retardoServo);
     int distancia = analogRead(A2);
     Serial.print("Distancia a ");
     Serial.print(grados, DEC);
     Serial.print("grados es ");
     Serial.println(distancia, DEC); 
     return distancia;
 }


 //Devuelve los grados 0 ó 180 cuya dirección está más despejada de obstáculos
 int gradosDireccionASeguir(){
   int distancia0Grados = distanciaObstaculo(0);
   int distancia180Grados = distanciaObstaculo(180);
   if (distancia0Grados > distanciaLimiteLateral && distancia180Grados > distanciaLimiteLateral){
     Serial.println("Grados direccion a seguir=999");
     return 999; //No se puede ir ni a derecha ni a izquierda
   }
   if (distancia0Grados < distanciaLimiteLateral && distancia180Grados > distanciaLimiteLateral){
     Serial.println("Grados direccion a seguir=0");
     gradosGiroAnterior=0;
     return 0; //A cero grados está más despejado
   }
   if (distancia0Grados < distanciaLimiteLateral && distancia180Grados > distanciaLimiteLateral){
     Serial.println("Grados direccion a seguir=0");
     gradosGiroAnterior=0;
     return 0; //A cero grados está más despejado
   }
   if (distancia0Grados > distanciaLimiteLateral && distancia180Grados < distanciaLimiteLateral){
     Serial.println("Grados direccion a seguir=180");
     gradosGiroAnterior=180;
     return 180; //A 180 grados está más despejado
   }
   if (distancia0Grados < distanciaLimiteLateral && distancia180Grados < distanciaLimiteLateral){
     //Podríamos a derecha o izquierda y lo decidimos en función del giro anterior
     if (gradosGiroAnterior == 0){
       Serial.println("Grados direccion a seguir=0");
       gradosGiroAnterior=0;
       return 0;
     }else{
       Serial.println("Grados direccion a seguir=180");
       gradosGiroAnterior=180;
       return 180; //A 180 grados está más despejado 
     }    
   }
 }