Estación Sensorial Móvil
Autores
Ángel Baeza Sánchez
Javier Raúl Alonso Tejera
Vicente Manuel Aguilera Gallardo
Introducción
Para el proyecto de la asignatura de Diseño de Sistemas Empotrados se ha decidido proceder a la construcción y programación de un vehículo de tres ruedas con diversos sensores los cuales aportan diversas funcionalidades; se ha bautizado el vehículo como Estación Sensorial Móvil. La Estación Sensorial Móvil (ESM para abreviar), cuenta con una multitud de sensores. Entre ellos se encuentran: Un sensor de distancia, un sensor de luminosidad y un sensor de infrarrojos.
El propósito final de la ESM es su uso bajo dos modos de funcionamiento, un modo automático, en el que la estructura se mueve automáticamente en línea recta utilizando sensores de distancia girando para evitar chocarse, y un modo “control”, en el que la ESM hace uso de su sensor de infrarrojos para poder ser controlada a distancia. Además, en ambos modos, la ESM utiliza el sensor de luminosidad para encender unos leds a modo de faros para poder situarla en la oscuridad.
Materiales y costes
Material | Cantidad | Precio Total |
---|---|---|
Motor 1.5V-2V | 2 | 3€ |
Cableado variado | – | – |
Puente H (Controlador para los motores) | 1 | 5€ |
Ruedas motoras | 2 | 2.5€ |
Rueda «loca» adicional | 1 | 0.95€ |
Mando infrarrojo | 1 | – |
Alimentación (Pilas y baterías) | – | – |
Materiales para la estructura | – | 5€ |
PCB | 1 | – |
Buzzer pasivo | 1 | 1€ |
Receptor infrarrojo | 1 | 0,3€ |
Sensor de distancia | 1 | 1€ |
Resistencias y transistores varios | – | – |
Leds de posición | 2 | 0,20€ |
LDR | 1 | 0,20€ |
TOTAL | 19,15€ Aprox. |
Estos son los precios aproximados de la mayoría de piezas (Obviamos la placa Arduino UNO).
Circuito, Diseño e Implementación – Hardware
La ESM está formada por cinco componentes principales:
- Se ha construido una PCB personalizada, la cual integra un sensor LDR, un sensor de distancia, un sensor de infrarrojos, un buzzer y un par de leds.
- Una estructura motora compuesta por la propia estructura de madera ligera , dos ruedas traseras con dos motores, y una rueda móvil central.
- La placa de Arduino.
- Un Puente H para coordinar los motores.
- Un sistema de alimentación propio.
A la hora de la construcción de la ESM, en primer lugar, se ha medido el tamaño de la placa de Arduino y de la PCB que se iba a utilizar, y se ha elegido una plancha de madera para sostener la circuitería de un tamaño acorde, dejando espacio para la batería y el resto de componentes.
Una vez lista la plancha de madera, se le han adherido las ruedas traseras, con sus respectivos motores y se ha atornillado una rueda móvil a la parte frontal de la estructura.
Una vez listo el esqueleto del proyecto, se ha procedido a pegar la placa de Arduino a este, y a soldar los diferentes componentes a la PCB la cual se ha adherido también al cuerpo principal.
Finalmente, se ha construido un sistema de alimentación utilizando celdas de baterías de un portátil en desuso, creando una batería de unos 12 voltios. Esta batería está separada en dos nodos, uno para suministrar un voltaje de 3V a los motores, y otro para suministrar menos de 12V a la placa. Una vez todos los componentes han sido construidos, se han conectado todos los componentes del sistema mediante cables.
Una vez el proyecto ha alcanzado este punto, se ha comenzado a desarrollar el código para su correcto funcionamiento, a la vez que se han pulido diferentes apartados de la estructura que no funcionaban del todo bien.
Funcionamiento – Software
La implementación software se basa en la utilización de una máquina de estados para poder controlar los diferentes modos de funcionamiento y entradas recibidas de forma más correcta.
En principio denominamos 5 estados posibles:
- Idle
Es el centro de todos los estados, permite pasar al resto de estados. Es el equivalente a un punto muerto. Almacena el código para activar la luz en función del foto-resistor.
2. Navegación
En este modo, la ESM avanza indefinidamente, cuando el sensor de ultrasonidos detecta una superficie, frena y gira hacia la derecha hasta que deja de detectar un obstáculo. Sigue girando durante una fracción de segundo más para asegurar que no choca con nada, y posteriormente retoma su avance.
a. Adelante -> Estado sobre el que avanza hasta detectar obstáculo.
b. Girar -> Estado en el que gira hasta dejar de detectar obstáculo.
3. Control
En este estado se permite controlar la ESM con el mando a distancia.
Cuando se presiona un botón, la ESM realiza esa acción en bucle hasta que se cancele esa opción presionando otro botón.También se incluye un botón de frenado para pausar el movimiento del coche.
4. Alarma
Si el sensor de ultrasonidos encuentra un obstáculo a una distancia d, suena una alarma a un volumen v. Tanto V como D son configurables usando el mando.
5. Piano
Se le asocia un tono del buzzer a cada uno de los números del mando. Esto permite utilizar la ESM como un piano móvil.
La frecuencia del sonido del buzzer se puede configurar también utilizando el mando.
Problemas y Soluciones
El primer problema encontrado fue al terminar el esqueleto del proyecto, y es que se había pensado que la ESM fuese de tracción a cuatro ruedas, sin embargo, debido a la calidad de las ruedas empleadas, esto impedía que girase de forma correcta. La solución fue sustituir las dos ruedas delanteras por una rueda móvil singular, facilitando así el giro del prototipo.
Otro de los problemas principales que se han encontrado a lo largo del desarrollo es la dificultad que conlleva soldar, ya que, al comenzar el proyecto, se decidió utilizar componentes propios de los alumnos, en lugar de los recibidos por el profesor con el objetivo de no tener que desmontar el proyecto. Por otra parte, aunque quede un buen resultado, hay que tener cuidado al soldar y tener algo de habilidad para no ensuciar en demasía y ocasionar problemas de funcionalidad.
Un problema menor que hemos encontrado ha sido la dificultad a la hora de entenderse con el puente H, puesto que no respondía correctamente a las órdenes de código; Afortunadamente, al cambiarlo por otro, el prototipo volvió a funcionar correctamente.
El problema final que se ha encontrado ha sido que, una vez terminado el prototipo, faltaba potencia a la hora de mover la ESM. En un primer momento se pensó que era cosa del peso, por lo que se reconstruyó toda la estructura para reducirlo. Esto no fue suficiente, por lo que se valoró cambiar los motores del proyecto, sin embargo, al final, bastó por cambiar la fuente de alimentación de los motores por la batería fabricada.
Con el fin de mejorar el rendimiento y funcionalidad general de la estación, sería recomendable optar por motores con mayor potencia (Unas ruedas de mejor calidad también ayudarían).
Código
#include <IRremote.h> //including infrared remote header file const int pResistorPin = A0; const int IRPin = 2; // the pin where you connect the output pin of IR sensor7 const int LEDPin = 3; const int buzzerPin = 4; const int A1A = 5; const int A1B = 6; const int pingPin = 7; // Trigger Pin of Ultrasonic Sensor const int echoPin = 8; // Echo Pin of Ultrasonic Sensor const int B1A = 9; const int B1B = 10; //IR VALUES /* RED: 0x33B8807F GREEN: 0x33B8A05F YELLOW: 0x33B8906F BLUE: 0x33B8B04F VOLUP: 0x33B8708F VOLDOWN: 0x33B840BF HOME : 0x33B8C03F UP: 0x33B8E01F DOWN: 0x33B822DD RIGHT: 0x33B848B7 LEFT: 0x33B808F7 1: 0x33B8827D 2: 0x33B8A25D 3: 0x33B8B24D 4: 0x33B842BD 5: 0x33B8629D 6: 0x33B8728D 7: 0x33B8C23D 8: 0x33B8E21D 9: 0x33B8F20D */ IRrecv irrecv(IRPin); decode_results IRresults; bool newIRvalue = false; //IDLE VARIABLES const float MAXLIGHT = 1400; const float MINLIGHT = 200; const float CHANGELIGHT = 50; float lightFactor = 600; //PIANO VARIABLES float pianoIntensity = 1; const float MINPIANO = 0.1; const float MAXPIANO = 2; const float CHANGEPIANO = 0.1; //ALARM VARIABLES const float MAXALARMDISTANCE = 510; const float MINALARMDISTANCE = 10; const float CHANGEALARMDISTANCE = 20; float alarmDistance = MINALARMDISTANCE; const float MAXALARMINTENSITY = 2; const float MINALARMINTENSITY = 0.2; const float CHANGEALARMINTENSITY = 0.1; float alarmIntensity = MINALARMINTENSITY; //NAVIGATION MODE const int MINDISTANCE = 50; const int DELAYTIME = 750; //MODES int currentMode; const int IDLEMODE = 1; const int NAVMODE = 2; const int ALARMMODE = 3; const int PIANOMODE = 4; const int CONTROLMODE = 5; int currentState; bool homeIsActive = true; //NAVIGATION STATE const int FORWARDSTATE = 1; const int TURNINGSTATE = 2; void setup() { Serial.begin(9600); // Starting Serial Terminal Serial.println("STARTING"); //Distance Sensor pinMode(pingPin, OUTPUT); pinMode(echoPin, INPUT); //IR Sensor irrecv.enableIRIn(); //Light Sensor pinMode(pResistorPin, INPUT); pinMode(LEDPin, OUTPUT); digitalWrite(LEDPin, HIGH); pinMode(buzzerPin, OUTPUT); pinMode(B1A, OUTPUT); pinMode(B1B, OUTPUT); pinMode(A1A, OUTPUT); pinMode(A1B, OUTPUT); currentMode = IDLEMODE; } void loop() { newIRvalue = getIR(); if (currentMode != IDLEMODE) { if (newIRvalue) { //If HOME has been pressed if (IRresults.value == 0x33B8C03F) { Serial.println("STARTING IDLE MODE"); currentMode = IDLEMODE; stopMovement(); tone(buzzerPin, 300, 200); blinkLEDs(50, 2); tone(buzzerPin, 500, 200); blinkLEDs(50, 2); tone(buzzerPin, 200, 200); } } } switch (currentMode) { //IDLE case IDLEMODE: //Serial.print("IR: "); //Serial.println(IRresults.value, HEX); idleMode(); break; case NAVMODE: navMode(); break; case ALARMMODE: alarmMode(); break; case PIANOMODE: pianoMode(); break; case CONTROLMODE: controlMode(); default: break; } } /// void idleMode() { if (newIRvalue) { switch (IRresults.value) { //1 -> NAVIGATION case 0x33B8827D: digitalWrite(LEDPin, HIGH); Serial.println("STARTING NAVIGATION MODE"); currentMode = NAVMODE; currentState = FORWARDSTATE; tone(buzzerPin, 100, 200); blinkLEDs(50, 2); tone(buzzerPin, 100, 200); break; //2 -> CONTROL case 0x33B8A25D: digitalWrite(LEDPin, HIGH); Serial.println("STARTING CONTROL MODE"); currentMode = CONTROLMODE; currentState = -1; tone(buzzerPin, 150, 200); blinkLEDs(50, 2); tone(buzzerPin, 150, 200); break; //3 -> ALARM case 0x33B8B24D: digitalWrite(LEDPin, HIGH); Serial.println("STARTING ALARM MODE"); currentMode = ALARMMODE; currentState = -1; tone(buzzerPin, 200, 200); blinkLEDs(50, 2); tone(buzzerPin, 200, 200); break; //4 -> PIANO case 0x33B842BD: digitalWrite(LEDPin, HIGH); Serial.println("STARTING PIANO MODE"); currentMode = PIANOMODE; currentState = -1; tone(buzzerPin, 250, 200); blinkLEDs(50, 2); tone(buzzerPin, 250, 200); break; //Up -> Light Up case 0x33B8E01F: if (lightFactor < MAXLIGHT) { lightFactor += CHANGELIGHT; Serial.print("Light Factor up: "); Serial.println(lightFactor); tone(buzzerPin, 400, 150); } else { tone(buzzerPin, 100, 250); delay(270); } break; //Down -> Light Down case 0x33B822DD: if (lightFactor > MINLIGHT && lightFactor > 0) { lightFactor -= CHANGELIGHT; Serial.print("Light Factor Down: "); Serial.println(lightFactor); tone(buzzerPin, 240, 150); } else { tone(buzzerPin, 100, 250); delay(270); } break; default: break; } } else { if (getLight() < lightFactor) { digitalWrite(LEDPin, LOW); } else { digitalWrite(LEDPin, HIGH); } } } void navMode() { switch (currentState) { case FORWARDSTATE: if (getDistance() > MINDISTANCE) { moveForward(255); } else { currentState = TURNINGSTATE; stopMovement(); Serial.println("NAVIGATION: Turning"); delay(DELAYTIME); } break; case TURNINGSTATE: if (getDistance() < MINDISTANCE) { moveRight(255); } else { delay(DELAYTIME); currentState = FORWARDSTATE; Serial.println("NAVIGATION: Going forward"); stopMovement(); delay(DELAYTIME); } break; } } void controlMode() { const int intensity = 255; if (!newIRvalue) { return; } switch (IRresults.value) { //FORWARD case 0x33B8E01F: Serial.println("Moving Forward in Control"); moveForward(intensity); break; //BACK case 0x33B822DD: Serial.println("Moving BackWard in Control"); moveBackward(intensity); break; //RIGHT case 0x33B848B7: Serial.println("Moving Right in Control"); moveRight(intensity); break; //LEFT case 0x33B808F7: Serial.println("Moving Left in Control"); moveLeft(intensity); break; //STOP case 0x33B88877: Serial.println("Stoping Movement in Control"); stopMovement(); break; default: break; } } void alarmMode() { if (newIRvalue) { switch (IRresults.value) { //UP case 0x33B8E01F: if (alarmDistance < MAXALARMDISTANCE) { alarmDistance += CHANGEALARMDISTANCE; tone(buzzerPin, 400, 100); delay(120); tone(buzzerPin, 400, 100); Serial.print("Alarm Distance up: "); Serial.println(alarmDistance); delay(200); } else { Serial.println("Alarm Distace at maximum!"); blinkLEDs(50, 4); } break; //DOWN case 0x33B822DD: if (alarmDistance > MINALARMDISTANCE && alarmDistance > 0) { alarmDistance -= CHANGEALARMDISTANCE; tone(buzzerPin, 200, 100); delay(120); tone(buzzerPin, 200, 100); Serial.print("Alarm Distance Down: "); Serial.println(alarmDistance); delay(200); } else { Serial.println("Alarm Distace at minimum!"); blinkLEDs(50, 4); } break; //Volume Up case 0x33B8708F: if (alarmIntensity < MAXALARMINTENSITY) { alarmIntensity += CHANGEALARMINTENSITY; Serial.print("Alarm Volume up: "); Serial.println(alarmIntensity); tone(buzzerPin, 1000 * alarmIntensity, 100); delay(200); } else { blinkLEDs(100, 3); } break; //Volume Down: case 0x33B840BF: Serial.print("Alarm Volume Down: "); Serial.println(alarmIntensity); if (alarmIntensity > MINALARMINTENSITY) { alarmIntensity -= CHANGEALARMINTENSITY; tone(buzzerPin, 1000 * alarmIntensity, 100); delay(200); } else { blinkLEDs(200, 3); } break; default: break; } } if (getDistance() < alarmDistance) { tone(buzzerPin, 1000 * alarmIntensity, 30); } } void pianoMode() { const unsigned long DURATION = 100; if (!newIRvalue) { return; } switch (IRresults.value) { //1 case 0x33B8827D: Serial.println("TONE 1"); tone(buzzerPin, 100 * pianoIntensity, DURATION); blinkLEDs(DURATION * 0.2, 1); break; //2 case 0x33B8A25D: Serial.println("TONE 2"); tone(buzzerPin, 200 * pianoIntensity, DURATION); blinkLEDs(DURATION * 0.2, 1); break; //3 case 0x33B8B24D: Serial.println("TONE 3"); tone(buzzerPin, 300 * pianoIntensity, DURATION); blinkLEDs(DURATION * 0.2, 1); break; //4 case 0x33B842BD: Serial.println("TONE 4"); tone(buzzerPin, 400 * pianoIntensity, DURATION); blinkLEDs(DURATION * 0.2, 1); break; //5 case 0x33B8629D: Serial.println("TONE 5"); tone(buzzerPin, 500 * pianoIntensity, DURATION); blinkLEDs(DURATION * 0.2, 1); break; //6 case 0x33B8728D: Serial.println("TONE 6"); tone(buzzerPin, 600 * pianoIntensity, DURATION); blinkLEDs(DURATION * 0.2, 1); break; //7 case 0x33B8C23D: Serial.println("TONE 7"); tone(buzzerPin, 700 * pianoIntensity, DURATION); blinkLEDs(DURATION * 0.2, 1); break; //8 case 0x33B8E21D: Serial.println("TONE 8"); tone(buzzerPin, 800 * pianoIntensity, DURATION); blinkLEDs(DURATION * 0.2, 1); break; //9 case 0x33B8F20D: Serial.println("TONE 9"); tone(buzzerPin, 900 * pianoIntensity, DURATION); blinkLEDs(DURATION * 0.2, 1); break; //Volume Up case 0x33B8708F: Serial.print("Piano Volume Up: "); Serial.println(pianoIntensity); if (pianoIntensity < MAXPIANO) { pianoIntensity += CHANGEPIANO; tone(buzzerPin, 1000 * pianoIntensity, DURATION * 0.5); delay(DURATION * 0.5); tone(buzzerPin, 1000 * pianoIntensity, DURATION * 0.5); delay(DURATION * 0.5); } else { Serial.print("Volume at Maximum!"); blinkLEDs(DURATION * 0.4, 3); } break; //Volume Down: case 0x33B840BF: if (pianoIntensity > MINPIANO && pianoIntensity > 0) { pianoIntensity -= CHANGEPIANO; Serial.print("Piano Volume Down: "); Serial.println(pianoIntensity); tone(buzzerPin, 1000 * pianoIntensity, DURATION * 0.5); delay(DURATION * 0.5); tone(buzzerPin, 1000 * pianoIntensity, DURATION * 0.5); delay(DURATION * 0.5); } else { Serial.print("Volume at Minimum!"); blinkLEDs(DURATION * 0.4, 3); } break; default: break; } } /// void blinkLEDs(int t, int n) { for (int i = 0; i < n; i++) { digitalWrite(LEDPin, LOW); delay(t); digitalWrite(LEDPin, HIGH); delay(t); } digitalWrite(LEDPin, HIGH); } //MOVEMENT void moveForward(int intensity) { digitalWrite(A1A, intensity); digitalWrite(A1B, 0); digitalWrite(B1A, 0); digitalWrite(B1B, intensity); } void moveRight(int intensity) { digitalWrite(A1A, intensity); digitalWrite(A1B, 0); digitalWrite(B1A, intensity); digitalWrite(B1B, 0); } void moveLeft(int intensity) { digitalWrite(A1A, 0); digitalWrite(A1B, intensity); digitalWrite(B1A, 0); digitalWrite(B1B, intensity); } void moveBackward(int intensity) { digitalWrite(A1A, 0); digitalWrite(A1B, intensity); digitalWrite(B1A, intensity); digitalWrite(B1B, 0); } void stopMovement() { digitalWrite(A1A, 0); digitalWrite(A1B, 0); digitalWrite(B1A, 0); digitalWrite(B1B, 0); } //SENSORS long getDistance() { long duration; digitalWrite(pingPin, HIGH); delay(50); digitalWrite(pingPin, LOW); duration = pulseIn(echoPin, HIGH) * 0.01724; //Serial.print("DISTANCE: "); //Serial.println(duration); return duration ; } bool getIR() { if (irrecv.decode(&IRresults)) { if (IRresults.value != 0xFFFFFFFF) { irrecv.resume(); return true; } irrecv.resume(); } return false; } float getLight() { return analogRead(pResistorPin); }