Todos hemos visto esa imagen clásica en las películas de submarinos: una pantalla verde, una línea que gira y un punto brillante (“blip”) que indica dónde está el enemigo.
Hoy vamos a construir exactamente eso: un radar de ultrasonidos con Arduino.
Este proyecto conecta dos mundos. Por un lado, el Hardware (Arduino moviendo un sensor y midiendo distancias). Por otro, el Software de PC (Processing), que recogerá esos datos y dibujará una interfaz gráfica en tiempo real.
De coordenadas polares a cartesianas
Tenemos un sensor de ultrasonidos montado sobre un servo.
- El servo nos dice hacia dónde miramos: el Ángulo (
). - El sensor nos dice cuán lejos está el objeto: el Radio (r).
Esto es un sistema de coordenadas polares
Aquí es donde la trigonometría se convierte en nuestra mejor amiga:
El ordenador recibirá el par (ángulo, distancia) a través del cable USB, aplicará estas fórmulas y pintará un punto verde en la posición calculada.
Componentes
- [1x|blue] Arduino Uno | El cerebro del proyecto
- [1x|orange] Sensor Ultrasónico HC-SR04 | Mide distancias
- [1x|green] Micro Servomotor (SG90) | Barrido del sensor
- [1x|gray] Soporte para montar sensor sobre servo | Impresión 3D o cinta
- [Varios|gray] Protoboard y cables | Conexiones flexibles para el sensor
Esquema de conexión
El montaje es sencillo, pero debemos asegurarnos de que los cables del sensor sean lo suficientemente largos o flexibles para permitir el movimiento de barrido del servo sin tiranteces.
Servo de barrido
El servo mueve el sensor de lado a lado. Si lo alimentas con fuente externa, une su GND con el de Arduino.
[ { “from”: “Servo Rojo (VCC)”, “to”: “Fuente externa regulada de 5V”, “color”: “red” }, { “from”: “Servo Marrón (GND)”, “to”: “GND Arduino”, “color”: “black” }, { “from”: “Servo Naranja (Señal)”, “to”: “Pin 12”, “color”: “green” } ]
Sensor HC-SR04
El sensor mide la distancia en cada posición angular del servo.
[ { “from”: “HC-SR04 VCC”, “to”: “5V Arduino”, “color”: “red” }, { “from”: “HC-SR04 GND”, “to”: “GND Arduino”, “color”: “black” }, { “from”: “HC-SR04 Trig”, “to”: “Pin 10”, “color”: “orange”, “note”: “Salida del Arduino” }, { “from”: “HC-SR04 Echo”, “to”: “Pin 11”, “color”: “yellow”, “note”: “Entrada al Arduino” } ]
Firmware de Arduino
El trabajo de Arduino es simple: Mover -> Medir -> Enviar. No va a calcular nada complejo, solo va a escupir datos crudos por el Puerto Serie.
El formato de envío es importante. Necesitamos que Processing entienda dónde acaba cada lectura. Usaremos una línea CSV por muestra: ángulo,distancia.
#include <Servo.h>
const int trigPin = 10;
const int echoPin = 11;
const int servoPin = 12;
Servo miServo;
void setup() {
Serial.begin(9600); // Iniciamos comunicación Serie
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
miServo.attach(servoPin);
}
void loop() {
// Barrido de ida (de 15º a 165º)
// Evitamos 0º y 180º porque el servo a veces tiembla en los extremos
for(int i = 15; i <= 165; i++){
miServo.write(i);
delay(30); // Damos tiempo al servo para llegar y estabilizarse
int distancia = calcularDistancia();
// Enviamos al PC: "angulo,distancia\n"
Serial.print(i);
Serial.print(",");
Serial.println(distancia);
}
// Barrido de vuelta (de 165º a 15º)
for(int i = 165; i > 15; i--){
miServo.write(i);
delay(30);
int distancia = calcularDistancia();
Serial.print(i);
Serial.print(",");
Serial.println(distancia);
}
}
int calcularDistancia(){
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
unsigned long duracion = pulseIn(echoPin, HIGH, 30000UL);
// Sin eco dentro del tiempo máximo: fuera del rango visual
if (duracion == 0) return 41;
int distancia = duracion * 0.0343 / 2;
// Limitamos la distancia máxima a 40cm para que se vea bien en pantalla
// Si mide más, lo recortamos
if (distancia > 40) distancia = 41;
return distancia;
}
Visualización con Processing
La visualización la haremos con Processing, un entorno creativo basado en Java y con una estructura parecida a la del IDE de Arduino.
- Descarga e instala Processing.
- Copia el siguiente código.
Este sketch lee el puerto serie, extrae el ángulo y la distancia, y dibuja una línea verde que se desvanece (estela) simulando el fósforo de los radares antiguos.
import processing.serial.*; // Importar librería Serie
import java.awt.event.KeyEvent;
import java.io.IOException;
Serial myPort;
String angle="";
String distance="";
String data="";
int iAngle, iDistance;
int index1=0;
void setup() {
size(1200, 700); // Tamaño de la ventana
smooth();
// Busca tu puerto Arduino (normalmente es el primero o segundo de la lista)
// Cambia el [0] si no te conecta
myPort = new Serial(this, Serial.list()[0], 9600);
myPort.bufferUntil('\n'); // Una muestra por línea
}
void draw() {
fill(0,4); // Relleno negro semitransparente para crear el efecto de desvanecimiento (trail)
rect(0, 0, width, height);
fill(98,245,31); // Color Verde Radar
// Trasladamos el origen (0,0) al centro-abajo de la pantalla
translate(width/2, height);
// --- DIBUJAR LÍNEAS DEL RADAR (ESTÉTICA) ---
strokeWeight(2);
stroke(98,245,31); // Verde
noFill();
// Arcos de distancia (10cm, 20cm, 30cm, 40cm)
// Multiplicamos por ratio para escalar a la pantalla
float pixelsPorCm = 15;
arc(0,0, 20*pixelsPorCm, 20*pixelsPorCm, PI, TWO_PI);
arc(0,0, 40*pixelsPorCm, 40*pixelsPorCm, PI, TWO_PI);
arc(0,0, 60*pixelsPorCm, 60*pixelsPorCm, PI, TWO_PI);
arc(0,0, 80*pixelsPorCm, 80*pixelsPorCm, PI, TWO_PI);
// --- DIBUJAR EL OBJETO DETECTADO ---
// Convertimos coordenadas Polares a Cartesianas
float pixDistance = iDistance * pixelsPorCm;
// OJO: Processing usa radianes. Y el servo va de 15 a 165.
// También invertimos el sentido porque Processing dibuja Y hacia abajo por defecto
// pero hemos hecho translate/rotate. Simplifiquémoslo:
float rads = radians(iAngle);
// Si la distancia es menor de 40 (rango máximo), pintamos objeto
if(iDistance < 40){
// Matemáticas polares:
// x = r * cos(theta)
// y = r * sin(theta) -> Negativo porque en Processing Y crece hacia abajo
float x = pixDistance * cos(rads);
float y = -pixDistance * sin(rads);
strokeWeight(9);
stroke(255,10,10); // Rojo para el objeto
point(x, y);
}
// Línea de barrido (Scanner)
strokeWeight(3);
stroke(30,250,60);
line(0,0, 40*pixelsPorCm * cos(rads), -40*pixelsPorCm * sin(rads));
}
// Evento que se ejecuta cuando llega una línea por USB
void serialEvent (Serial myPort) {
try {
String linea = myPort.readStringUntil('\n');
if (linea == null) return;
data = trim(linea);
// Buscamos la coma
index1 = data.indexOf(",");
// Partimos el string
angle = data.substring(0, index1);
distance = data.substring(index1 + 1, data.length());
// Convertimos a enteros
iAngle = int(angle);
iDistance = int(distance);
// Corregir la orientación del servo para que coincida con la pantalla
// (Depende de cómo hayas montado el motor)
iAngle = 180 - iAngle;
} catch (Exception e) {
// Si llega basura por el puerto, la ignoramos
}
}
Cómo funciona la estela
Cuando ejecutes el programa, verás algo fascinante:
- La función
draw()en Processing se ejecuta en bucle (como elloopde Arduino). - Al poner
fill(0, 4)y dibujar un rectángulo negro sobre toda la pantalla al principio de cada frame, lo que hacemos es “oscurecer un poquito” lo que había dibujado antes. - Esto crea el efecto de estela. La línea verde del barrido actual brilla fuerte, pero la línea de hace 1 segundo se ve tenue.
Esto no es solo estético; nos permite ver la “historia” del barrido, tal y como lo hace el fósforo de una pantalla CRT de radar real.
Desafíos y mejoras
¿Te ha funcionado? Ahora te reto a mejorarlo:
- Reducir el campo de detección: El HC-SR04 integra ecos dentro de un haz relativamente ancho. Puedes probar un sensor óptico de tiempo de vuelo como el VL53L0X, teniendo en cuenta que mide de otra forma y también tiene límites de alcance y reflectividad.
- Detección de intrusos: Programa Processing para que si detecta algo a menos de 10cm, reproduzca un sonido de alarma (
.wav) usando la librería de sonido. - Radar 360: Modifica la mecánica para usar un anillo rozante (slip ring) y permitir giros completos, mapeando los 360 grados en la pantalla.