EchoSonnar
Grupo 3
Integrantes:
- Rodrigo Sotomayor González
- Sergio Redondo Redondo
- Jorge Fuentes Barrio
Introducción:
Nuestro proyecto EchoSonnar nace del interés de querer entender el funcionamiento sistemas de navegación que se basan en los ultrasonidos como el sistema de un submarino o como la forma de desplazarse de los murcielagos que se basa también en los ultrasonidos.
Objetivo:
La elaboración de nuestro proyecto tiene como objeto recrear un sistema de navegación de ultrasonidos a menor escala, de modo que podamos probar su funcionamiento en tiempo real y poder explicar los fundamentos físicos de los ultrasonidos con un ejemplo práctico. La aplicación de este proyecto esta orientada a ofrecer un modelo de funcionamiento que se pueda usar en las aulas de los institutos a modo de explicación práctica del temario relacionado con las ondas de sonido
Componentes:
- Sensor de ultrasonidos: usado para calcular la distancia de los obstáculos que se presentan frente a él.
- Servo motor: usado para cambiar la orientación del ultrasonidos.
- Arduino Uno: usada para conectar todos los componentes entre sí y con el código.
- Programa de Python: usado para recibir la información de entrada desde la placa de Arduino y formatear la salida para devolverla de forma gráfica.
- Botones: utilizados para mover manualmente el ultrasonidos, resetear la salida de la información o para cambiar a un modo en el que la rotación se realiza de forma automática.
- Resistencias: usadas para proteger algunos componentes.
- Cables
- Materiales para la maqueta: algunas planchas de cartón para fijar el servo y el ultrasonidos a una posición, además de algunos obstáculos para poder poner a prueba el funcionamiento del ultrasonidos.
Costes de los materiales:
Componentes | Precio |
Miuzei SG 9G Micro Servo Motor | 5€ |
Sensor de Ultrasonidos | 1€ |
Materiales de la maqueta | 6€ |
Código de Arduino:
En el código de Arduino hemos identificado las entradas de la información tanto como para el sensor de ultrasonidos como para la pulsación de los botones que hemos incorporado, además de recibir la posición actual del servo. También identificamos la salida de la información para el modo de funcionamiento del servo, indicándole si estamos en modo automático o en modo manual.
#include <Servo.h>
Servo myservo; // crea el objeto servo
int eco = 8;
int desencadenador = 7;
int limpiar=2;
int derecha=12;
int izquierda=4;
int pos=90;
bool cambiarModo=0;
int pinModo=13;
void setup() {
myservo.attach(9); // vincula el servo al pin digital 9
Serial.begin(9600);
pinMode(derecha,INPUT);
pinMode(izquierda,INPUT);
pinMode(limpiar, INPUT);
pinMode(pinModo,INPUT);
pinMode(eco, INPUT);
pinMode(desencadenador, OUTPUT);
digitalWrite(desencadenador, LOW);
myservo.write(pos);
}
void loop() {
if(digitalRead(pinModo)==HIGH){
if(cambiarModo==1){
cambiarModo = 0;
} else {
cambiarModo = 1;
delay(100);
}
}
long t; //timepo que demora en llegar el eco
float d;//distancia en centimetros
if(digitalRead(limpiar)==HIGH) Serial.println("Reinicio");
if(!cambiarModo){
while(digitalRead(derecha)==HIGH&&pos<180){
pos++;
digitalWrite(desencadenador, HIGH);
delayMicroseconds(10); //Enviamos un pulso de 10us
digitalWrite(desencadenador, LOW);
t = pulseIn(eco, HIGH); //obtenemos el ancho del pulso
d = t/59; //escalamos el tiempo a una distancia en cm
Serial.print(pos);
Serial.print(";");
Serial.println(d);
delay(100);
myservo.write(pos);
delay(15);
}
while(digitalRead(izquierda)==HIGH&&pos>0){
pos--;
digitalWrite(desencadenador, HIGH);
delayMicroseconds(10); //Enviamos un pulso de 10us
digitalWrite(desencadenador, LOW);
t = pulseIn(eco, HIGH); //obtenemos el ancho del pulso
d = t/59; //escalamos el tiempo a una distancia en cm
Serial.print(pos);
Serial.print(";");
Serial.println(d);
delay(100);
myservo.write(pos);
delay(15);
}
}
else{
while (cambiarModo&&pos<180)
{
pos++;
if(digitalRead(limpiar)==HIGH) Serial.println("Reinicio");
digitalWrite(desencadenador, HIGH);
delayMicroseconds(10); //Enviamos un pulso de 10us
digitalWrite(desencadenador, LOW);
t = pulseIn(eco, HIGH); //obtenemos el ancho del pulso
d = t/59; //escalamos el tiempo a una distancia en cm
Serial.print(pos);
Serial.print(";");
Serial.println(d);
delay(100);
myservo.write(pos);
delay(15);
if(digitalRead(pinModo)==HIGH){
cambiarModo = 0;
delay(100);
break;
}
}
while (cambiarModo&&pos>0)
{
pos--;
if(digitalRead(limpiar)==HIGH) Serial.println("Reinicio");
digitalWrite(desencadenador, HIGH);
delayMicroseconds(10); //Enviamos un pulso de 10us
digitalWrite(desencadenador, LOW);
t = pulseIn(eco, HIGH); //obtenemos el ancho del pulso
d = t/59; //escalamos el tiempo a una distancia en cm
Serial.print(pos);
Serial.print(";");
Serial.println(d);
delay(100);
myservo.write(pos);
delay(15);
if(digitalRead(pinModo)==HIGH){
cambiarModo = 0;
delay(100);
break;
}
}
}
}
Código Python:
El código de Python recibe la información de que devuelve el sensor de ultrasonidos, con esto operamos esta información y la formateamos para darle una salida como si de un sonar se tratase, dibujando los círculos concéntricos, y una vez mostrado el display el programa empieza a dibujar puntos a la distancia que les corresponda con respecto al centro del diagrama que consideremos que es el sonar. En todo momento mostramos la distancia en centímetros, el ángulo del servo y el error de medición. Las diferentes librerías usadas son: Serial para la comunicación con Arduino, math para realizar operaciones con los grados, pygame para dibujar el display e ir dibujando los puntos de forma más eficiente que la librería Turtle y la librería Sys que permite acceder y gestionar las variables manejadas por el intérprete.
# Importamos Librerias
import serial
import pygame
import math
import sys
from pygame.locals import *
# Inicializamos la ventana del Pygames
pygame.init()
pygame.display.set_caption('EchoSonar')
anchura, altura = 800, 800
pantalla = pygame.display.set_mode((anchura, altura))
# Establecemos comunicacion Serial
arduino = serial.Serial()
arduino.timeout = 1
arduino.port = 'COM5'
arduino.open()
color = (255, 255, 255)
puntos = []
# Definir las funciones de conversion
def conversion(x, y):
x, y = int(x), int(y)
n_x = x + anchura // 2
n_y = altura // 2 - y
return ([n_x, n_y])
def grados_a_radianes(angulo):
x = (angulo * 3.14) / 180
return (x)
# Función de dibujado de lineas y puntos
def anadir(angulo, distancia):
global puntos
distancia *= 10
angulo = grados_a_radianes(angulo)
x, y = math.cos(angulo) * distancia, math.sin(angulo) * distancia
pos = conversion(x, y)
puntos.append(pos)
def dibujar_puntos(puntos, pantalla=pantalla):
for p in puntos:
pygame.draw.circle(pantalla, color, p, 2)
def dibujar_circulos(pantalla=pantalla):
radio = 80
for x in range(1, anchura // 2):
n_radio = ((x // radio) + 1) * radio
pygame.draw.circle(pantalla, color, (anchura // 2, altura // 2), n_radio, 2)
def dibujar_linea(angulo, pantalla=pantalla):
a = math.tan(grados_a_radianes(angulo))
y = altura // 2
if a == 0:
y = 0
if angulo == 0:
x = anchura / 2
elif angulo == 180:
x = -anchura / 2
else:
x = y // a
pos = conversion(x, y)
pygame.draw.line(pantalla, color, (anchura / 2, altura / 2), pos, 2)
def escribir_texto(pantalla, texto, tamanio_texto):
fontObj = pygame.font.Font('freesansbold.ttf', tamanio_texto)
textoSurface = fontObj.render(texto, True, (255, 255, 255))
pantalla.blit(textoSurface, (0, 0))
while True:
pantalla.fill((0, 0, 0))
dibujar_circulos(pantalla)
entrada = arduino.readline()
entrada = entrada.decode()
print(entrada);
if ("Reinicio" in entrada):
puntos = []
pygame.display.update()
continue
entrada = entrada[:len(entrada) - 2]
entrada = entrada.split(";")
if len(entrada) != 2 or ("" in entrada):
angulo_dist=False
else: angulo_dist = entrada
if angulo_dist == False: continue
angulo, dist = float(angulo_dist[0]), float(angulo_dist[1])*0.75
dibujar_linea(angulo)
anadir(angulo, dist)
dibujar_puntos(puntos)
escribir_texto(pantalla, "Ángulo = " + str(angulo) + ", Distancia = " + str(dist) + ", Error = +- " + str(dist * 0.05) + "%",
20)
for evento in pygame.event.get():
if evento.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
Foto del display de Python:
Aquí se muestra un ejemplo de la ventana que se abre al ejecutar el programa de Python y tener en marcha el circuito de Arduino.
Fotos del proyecto:
Aquí podemos ver el diagrama de estados asociado al prototipo que hemos realizado:
En las siguientes imágenes podemos ver las conexiones de los cables con las entradas de los dispositivos y con los pines de la placa de Arduino, también podemos ver el apartado de los botones que utiliza unas resistencias de 10KΩ:
Problemas:
-Cambio de modo: Hemos tenido problemas para hacer funcionar los modos de funcionamiento del motor ya que una vez entraba en modo automático no conseguíamos hacerle salir del bucle de funcionamiento sin recargar el código. Conforme fuimos arreglando el código conseguimos que esto funcionase correctamente.
-Fallos en el funcionamiento del ultrasonidos: inicialmente cuando estuvimos probando el funcionamiento de los distintos componentes que íbamos a utilizar en el proyecto el sonar devolvía valores extraños y pensamos que quizás tenía que ver con el lugar en el que tomábamos las mediciones pero cuando comprobamos que era problema del sensor decidimos conseguir uno nuevo para poder avanzar en el proyecto.
-Librería Turtle: en una primera versión de nuestro display intentamos usar la librería Turtle pues ya la habíamos utilizado anteriormente en otras práctica de la asignatura de Diseño y Análisis de Algoritmos, esta pese a hacer su función de dibujar tenía un tiempo de retardo demasiado grande lo que provocaba perdidas de datos demasiado graves y no se acababa de dibujar correctamente la información en pantalla. En su lugar hemos utilizado la librería PyGame.