Robot Camarero
Realizado por: Francisco Pradas Martín, Pablo Gutiérrez Gordillo, Ariel Carnés Blasco, Javier Pina Diego
Componentes
Nombre | Detalles | Unidades | Precio Individual | Precio Total |
Placa Arduino UNO | Disponíamos de 2 placas adicionales | 3 | 11,49€ | 22,98€ |
Resistencias | – | 10 | – | – |
Protoboard | 170 ptos | 2 | – | – |
Kit Elegoo Smart Robot Car Kit V2.0 | No hubo que comprarlo, ya disponíamos de él | 1 | 67,99€ | 67,99€ |
Sensor Infrarrojos + Mando | Utilizamos el que venía en el kit y uno que venía incorporado en el coche | 2 | – | – |
Pantalla LCD | Para la estación central | 1 | – | – |
Servomotores | Para estación central y para estación de carga | 2 | – | – |
Motor Stepper | Para estación de carga | 1 | – | – |
Sensor ultrasónico | Para el coche (integrado) y para la estación de carga | 2 | – | – |
Potenciómetro | Para estación central | 1 | – | – |
Módulo de potencia | Para estación central | 1 | – | – |
Pila (4,5V) | Para estación central y para estación de carga | 2 | 7,57€ | 15,14 |
Cartón/ cartón pluma | Para estación central y estación de carga | 4 | 1,20€ | 4,80€ |
PRECIO TOTAL | En la columna izquierda el precio real de todo el proyecto, ya que disponíamos de muchos de los materiales | 4,80€ | 110,91€ |
Objetivo del proyecto
Tras debatir entre los miembros del grupo y hacer una lluvia de ideas sobre posibles proyectos, obtuvimos 3 posibilidades:
- Estación meteorológica
- Robot camarero
- Ratón resuelve-laberintos
Finalmente y tras una charla con el profesor, decidimos descartar la estación meteorológica por ser demasiado sencilla, y el ratón por complejidad algorítmica.
Una vez decidido el proyecto que íbamos a realizar, decidimos hacer una lluvia de ideas para ver cómo podíamos implementarlo, para después comenzar con la investigación de qué íbamos a necesitar para llevarlo a cabo (materiales, programación, aprovechamiento de recursos…).
La idea del proyecto era conseguir que un vehículo fuera de un punto «A» a uno o varios puntos «B», recogiera una carga y la trajera de vuelta al punto de inicio. Tras pensar formas de interconectar varias estaciones de carga, vimos que era algo complejo a nivel hardware, ya que necesitaríamos cables muy largos (lo que supondría pérdida de eficiencia), una base para cablear todo el proyecto por debajo de la misma para no ser visto (demasiado costoso y difícil de transportar).
Por tanto, optamos por una opción «B», que consistía en implementar 3 tipos de carga en una sola estación de carga, y que lo que girara fuera la propia estación. Algo así como una estación giratoria. Sin embargo, esto también suponía un problema ya que el motor no tenía fuerza suficiente para mover la estación completa, por lo que finalmente optamos por hacer que se moviera únicamente una ruleta con 3 huecos (uno para cada tipo de carga), que se encontraba en la parte alta de la estación. Así además el sistema era más escalable porque si en un futuro queríamos avanzar aún más en el proyecto lo único que debíamos hacer era implementar otra estación de carga e introducirla en la estación central.
Además, quisimos añadir una estación central que sirviera como «puesto de control», en el que el vehículo se detuviera hasta saber a por qué producto tenía que ir. Cada parte del proyecto debía ser autónoma para que con un solo mando y un par de teclas pudiese controlarse todo.
Problemas encontrados
Algunos de los problemas que nos han surgido durante la realización de la práctica han sido:
- Infrarrojos
- Coche
Tuvimos bastantes problemas con el infrarrojo. Desde problemas con la distancia a la que detectaba la señal del mando, hasta problemas con valores basura e interferencias con otras señales. Tuvimos que hacer muchas pruebas, correcciones y mediciones hasta que conseguimos minimizar el número de errores lo máximo posible.
Respecto al coche, al tratarse de un modelo antiguo y con bastante uso, en un primer momento tuvimos problemas con las conexiones, ya que algunos cables estaban sueltos y no hacían buen contacto, por lo que hubo que soldar prácticamente todos los cables para que no se soltaran.
Después, el coche presentaba algunos problemas a la hora de hacer la función de siguelíneas, ya que los sensores detectaban señales erróneas (manchas en el suelo, sombras…). Esto se solucionó tensando más la línea de cinta aislante magnética y ajustando el potenciómetro de los sensores para hacerlos menos sensibles a otros elementos.
Código completo del proyecto
//PROYECTO COCHE CAMARERO
//MOVIMIENTO DEL COCHE
#include <Servo.h> //servo library
#define LT1 digitalRead(10)
#define LT2 digitalRead(4)
#define LT3 digitalRead(2)
Servo myservo; // create servo object to control servo
int Echo = A4;
int Trig = A5;
float DistanciaUmbral1 = 10;
float DistanciaUmbral2 = 5;
int motorDerechoAdelante = 6;
int motorDerechoAtras = 7;
int motorIzquierdoAtras = 8;
int motorIzquierdoAdelante = 9;
int ENA = 5;
int ENB = 11;
int velocidad = 80;
void setup() {
// Configuramos los pines de los motores como salidas
pinMode(motorIzquierdoAdelante, OUTPUT);
pinMode(motorIzquierdoAtras, OUTPUT);
pinMode(motorDerechoAdelante, OUTPUT);
pinMode(motorDerechoAtras, OUTPUT);
pinMode(ENA, OUTPUT);
pinMode(ENB, OUTPUT);
digitalWrite(ENA, HIGH);
digitalWrite(ENB, HIGH);
pinMode(Trig, OUTPUT);
pinMode(Echo, INPUT);
pinMode(LT1, INPUT);
pinMode(LT2, INPUT);
pinMode(LT3, INPUT);
// Inicializamos la comunicación serial para depuración
Serial.begin(9600);
}
void loop() {
while (!detectarBarrera()) {
seguirLinea();
}
detenerse(); // Se detiene cuando detecta una barrera
// Detectar pelota y realizar media vuelta si es necesario
if (detectarPelota()) {
mediavuelta();
// Volver a seguir la línea después de dar la media vuelta
while (!detectarBarrera()) {
seguirLinea();
}
}
// Si no cae una pelota, permanece detenido.
}
void seguirLinea() {
if (LT2) {
avanzar();
} else if (LT1) {
girarIzquierda();
while (LT1);
} else if (LT3) {
girarDerecha();
while (LT3);
} else {
detenerse();
}
}
void girarIzquierda() {
// Avanzar hacia adelante
analogWrite(ENA, 120);
analogWrite(ENB, 120);
digitalWrite(motorIzquierdoAdelante, LOW);
digitalWrite(motorIzquierdoAtras, HIGH);
digitalWrite(motorDerechoAdelante, HIGH);
digitalWrite(motorDerechoAtras, LOW);
}
void girarDerecha() {
// Retroceder
analogWrite(ENA, velocidad);
analogWrite(ENB, velocidad);
digitalWrite(motorIzquierdoAdelante, HIGH);
digitalWrite(motorIzquierdoAtras, LOW);
digitalWrite(motorDerechoAdelante, LOW);
digitalWrite(motorDerechoAtras, HIGH);
}
void detenerse() {
// Detener ambos motores
analogWrite(ENA, velocidad);
analogWrite(ENB, velocidad);
digitalWrite(motorIzquierdoAdelante, LOW);
digitalWrite(motorIzquierdoAtras, LOW);
digitalWrite(motorDerechoAdelante, LOW);
digitalWrite(motorDerechoAtras, LOW);
}
void avanzar() {
// Girar a la izquierda
analogWrite(ENA, velocidad);
analogWrite(ENB, velocidad);
digitalWrite(motorIzquierdoAdelante, HIGH);
digitalWrite(motorIzquierdoAtras, LOW);
digitalWrite(motorDerechoAdelante, HIGH);
digitalWrite(motorDerechoAtras, LOW);
}
void retroceder() {
// Girar a la derecha
analogWrite(ENA, velocidad);
analogWrite(ENB, velocidad);
digitalWrite(motorIzquierdoAdelante, LOW);
digitalWrite(motorIzquierdoAtras, HIGH);
digitalWrite(motorDerechoAdelante, LOW);
digitalWrite(motorDerechoAtras, HIGH);
}
void mediavuelta() {
// Girar a la derecha
analogWrite(ENA, velocidad);
analogWrite(ENB, velocidad);
digitalWrite(motorIzquierdoAdelante, HIGH);
digitalWrite(motorIzquierdoAtras, LOW);
digitalWrite(motorDerechoAdelante, HIGH);
digitalWrite(motorDerechoAtras, LOW);
}
bool detectarBarrera() {
digitalWrite(Trig, LOW);
delayMicroseconds(2);
digitalWrite(Trig, HIGH);
delayMicroseconds(10);
digitalWrite(Trig, LOW);
long tiempoEcho = pulseIn(Echo, HIGH);
float distancia1 = tiempoEcho * 0.034 / 2;
return (distancia1 < DistanciaUmbral1);
}
bool detectarPelota() {
digitalWrite(Trig, LOW);
delayMicroseconds(2);
digitalWrite(Trig, HIGH);
delayMicroseconds(10);
digitalWrite(Trig, LOW);
long tiempoEcho = pulseIn(Echo, HIGH);
float distancia2 = tiempoEcho * 0.034 / 2;
return (distancia2 < DistanciaUmbral2);
}
//CODIGO DE LA ESTACIÓN CENTRAL
#include <Servo.h>
#include <IRremote.h>
#include <LiquidCrystal.h>
int rs =12;
int e =11;
int d4=5;
int d5=4;
int d6=3;
int d7=2;
LiquidCrystal lcd(rs,e,d4,d5,d6,d7);
Servo servo1,servo2,servo3; //pueden ser 2 servo y un motor que eso si lo tenemos
int sensor=10; //pin donde va el infrarrojo
IRrecv infrarrojo(sensor);
void abrir_puerta();
void cerrar_puerta();
void setup() {
// put your setup code here, to run once:
lcd.begin(16,2);
Serial.begin(9600);
lcd.setCursor(0,0);
lcd.print("LCD Setup");
delay(2000);
lcd.clear();
infrarrojo.enableIRIn();
servo1.attach(9);
servo1.write(0);
}
void loop() {
lcd.setCursor(0,0);
lcd.print("Estacion?");
//codigo del infrarrojo
if(infrarrojo.decode()){
Serial.println(infrarrojo.decodedIRData.decodedRawData,HEX);
if(infrarrojo.decodedIRData.decodedRawData == 0xE916FF00){
lcd.setCursor(0,1);
lcd.print("Estacion 0");
abrir_puerta();
delay(2000);
servo1.write(0);
}
if(infrarrojo.decodedIRData.decodedRawData == 0xF30CFF00){
lcd.setCursor(0,1);
lcd.print("Estacion 1");
abrir_puerta();
delay(2000);
servo1.write(0);
}
if(infrarrojo.decodedIRData.decodedRawData == 0xE718FF00){
lcd.setCursor(0,1);
lcd.print("Estacion 2");
abrir_puerta();
delay(2000);
servo1.write(0);
}
Serial.println();
infrarrojo.resume();
}
delay(10);
}
void abrir_puerta(){
for(int pos = 0; pos<= 90; pos++){
servo1.write(pos);
delay(15);
}
}
void cerrar_puerta(){
for(int pos = 90; pos>=0; pos--){
servo1.write(pos);
delay(15);
}
}
//CÓDIGO DE LA ESTACIÓN DE CARGA
#include <Stepper.h>
#include <Servo.h>
#include <IRremote.h>
//2048 pasos es una vuelta
int pasos_por_revolucion=2048;
int angulo_a=0;
int angulo_b=680;
int angulo_c=-angulo_b;
int velocidad=10;
Stepper mystepper(pasos_por_revolucion,8,10,9,11);
Servo servo1;
int sensor=3; //pin donde va el infrarrojo
IRrecv infrarrojo(sensor);
int trigger=A0;
int echo=5;
long tiempo_trigger;
void abrir_puerta();
void cerrar_puerta();
void reconocer_coche();
void setup() {
// put your setup code here, to run once:
mystepper.setSpeed(velocidad);
mystepper.step(angulo_a);
Serial.begin(9600);
infrarrojo.enableIRIn();
servo1.attach(6);
servo1.write(0);
pinMode(trigger,OUTPUT);
pinMode(echo,INPUT);
}
void loop() {
//Serial.print("Estacion?");
//codigo del infrarrojo
if(infrarrojo.decode()){
//Serial.println(infrarrojo.decodedIRData.decodedRawData,HEX);
if(infrarrojo.decodedIRData.decodedRawData == 0xE916FF00){
Serial.println("Estacion 0");
mystepper.step(angulo_a);
reconocer_coche();
mystepper.step(-angulo_a);
}
if(infrarrojo.decodedIRData.decodedRawData == 0xF30CFF00){
Serial.println("Estacion 1");
mystepper.step(angulo_b);
reconocer_coche();
mystepper.step(-angulo_b);
}
if(infrarrojo.decodedIRData.decodedRawData == 0xE718FF00){
Serial.println("Estacion 2");
mystepper.step(angulo_c);
reconocer_coche();
mystepper.step(-angulo_c);
}
Serial.println();
infrarrojo.resume();
}
delay(10);
}
void abrir_puerta(){
for(int pos = 0; pos<= 90; pos++){
servo1.write(pos);
delay(15);
}
}
void cerrar_puerta(){
for(int pos = 90; pos>=0; pos--){
servo1.write(pos);
delay(15);
}
}
void reconocer_coche(){
Serial.println("--------------------------------");
digitalWrite(trigger,LOW);
delayMicroseconds(10);
digitalWrite(trigger,HIGH);
delayMicroseconds(10);
digitalWrite(trigger,LOW);
tiempo_trigger=pulseIn(echo,HIGH);
float distancia=(tiempo_trigger*0.034)/2;
Serial.print(distancia);
if(distancia<=15){
delay(200);
abrir_puerta();
delay(2000);
servo1.write(0);
}
else{
reconocer_coche();
}
}
Explicación del funcionamiento del prototipo
- Coche
Este es el vehículo que transportará la carga. Su objetivo es detectar cuando la barrera de entrada sube para así ir a la estación de carga. En la estación de carga tendrá que esperar a recibir la carga, en cuanto la reciba dará media vuelta y se retirará por la estación central.
En el código hay una serie de funciones para controlar el movimiento del coche. A diferencia de lo que podría parecer obvio de que hay una variable para cada motor, hay dos variables para cada lado del coche (izquierdo y derecho) para cada movimiento posible (adelante y atrás). Además se añaden dos funciones para detectar objetos (una barrera y una pelota) que se controlan con un sensor ultrasónico, por lo que dichas funciones calculan la distancia a la que está el coche, y actúan en función de los resultados obtenidos. Para el movimiento del coche, se ha incluido la función para seguir líneas mediante el control de 3 sensores que se encuentran debajo del coche y una estructura de código de else-ifs.
- Estación central
Esta será la estación de la que sale el coche. Su objetivo es el de abrir la barrera de la entrada una vez se haya seleccionado un tipo de carga. Es un código simple que está dividido por 3 ifs. Dependiendo de qué tecla reconozca la estación se irá la ejecución del código por un lado o por otro y los mensajes por pantalla cambiarán además de abrir y cerrar la compuerta de la estación.
- Estación de carga
Esta será la estación en la que tendremos la carga que vamos a transportar. Su objetivo es identificar el tipo de carga deseado y esperar al camión para soltarlo. Es bastante parecido al código de la estación anterior, la diferencia es que dependiendo de qué botón se pulse se girará hacia un producto u otro y la puerta se abrirá si se detecta un vehículo a la espera de que se dispense el producto.
Video del funcionamiento del prototipo
Todas las fotos y videos del proyecto están aquí adjuntadas: