Sonar 3D: ver el cuarto con ultrasonido
La fase 1 demostró que el sonar funciona con el parlante y el micrófono de una laptop, pero sólo mide distancia. Para dibujar el entorno en pantalla, como en The Dark Knight, hace falta saber también la dirección de cada eco. Esta fase usa un haz ultrasónico de 40 kHz, que es direccional, montado sobre servos que barren el cuarto, con una Raspberry Pi Pico que emite, graba y mueve todo. Cuesta unos 97.000 COP (~25 USD).
Hechos: la fase 1 (sonar acústico, 88 tests), el diseño, la compra de las piezas y el plano del circuito. Falta todo lo demás: el armado, el firmware de la Pico, el software de escaneo 3D y la validación. Lo que en esta página aparece como propuesta es diseño aún no implementado y puede cambiar.
Por qué hardware dedicado
Girar la laptop con servos no sirve: su parlante y su micrófono son casi omnidireccionales. La pared a 2.78 m devolvería el mismo eco desde cualquier ángulo, y la "imagen" sería una esfera borrosa. Lo que da la dirección es un haz estrecho, y el ancho de un haz depende de la relación λ/D entre la longitud de onda y el tamaño del emisor.
5 kHz (laptop)
λ = 6.9 cm. Para un haz estrecho haría falta un emisor de medio metro.
40 kHz (HC-SR04)
λ = 8.6 mm. Un transductor de 16 mm ya forma un haz direccional.
| Fase 1 · laptop | Fase 2 · Pico + 40 kHz | |
|---|---|---|
| Dirección | No (omnidireccional) | Sí: haz de unos 15–30° apuntado por servos |
| Reloj | Windows, latencia de 42 ms y buffers perdidos | Un solo reloj: la Pico dispara y muestrea, sin latencia desconocida |
| Muestreo | 48 kHz | 500 kHz (ADC del RP2040) |
| Primer metro | Tapado por la resonancia del parlante y el chasis | Sólo la diafonía TX→RX de los primeros centímetros |
| Audible | Sí (2–8 kHz) | No para humanos; sí para perros y gatos |
| Salida | A-scan (distancia) | Nube de puntos 3D (.ply) + vista estilo radar |
Primera propuesta descartada: interfaz de audio USB
El primer diseño (28-oct) usaba una interfaz Behringer UMC202HD de 192 kHz con un par de transductores TCT40-16T/R. La tienda local no vendía interfaces de audio, y los transductores sueltos estaban agotados. El rediseño con la Pico sale más barato (~25 USD en vez de 100–170) y una sola placa emite, graba y mueve los servos.
Cómo funciona el escáner
De eco a punto 3D
Cada eco que pasa la detección se convierte en un punto con intensidad igual a su SNR. Si el haz no es estrecho, el punto se "unta" sobre todo el cono: por eso la resolución angular la pone el haz, no el paso del servo.
Cuánto tarda
Ejemplo: pan de −60° a +60° y tilt de −15° a +30°, ambos en pasos de 5°, son 25 × 10 = 250 direcciones. Cada una tarda unos 0.2 s en asentar el servo más 8 pings × 30 ms, en total ≈ 0.44 s. El escaneo completo toma ≈ 2 minutos.
Los pings van separados ~30 ms para que la reverberación del anterior se apague: eso son 10 m de recorrido, o sea 5 m de alcance.
Lista de materiales
Comprados en didacticaselectronicas.com (Colombia). Precios y stock al 28-oct-2026. Marca lo que ya tienes en la mesa de trabajo.
| Pieza | Cant. | COP c/u | Para qué | |
|---|---|---|---|---|
| Raspberry Pi Pico H | 1 | 20.944 | Dispara, muestrea a 500 kHz, mueve los servos, habla con el PC por USB | |
| HC-SR04 | 3 | 4.522 | #1 emisor (sin modificar), #2 donante del receptor, #3 repuesto | |
| Soporte pan-tilt + 2 servos SG90 | 1 | 19.888 | Apuntar el haz | |
| MCP6002-E/P (DIP-8) | 2 | 3.142 | Amplificar el eco (uno es repuesto) | |
| Protoboard 830 puntos | 1 | 8.482 | Montar sin soldar | |
| Kit 140 cables puente | 1 | 14.451 | Conexiones en la protoboard | |
| Kit 200 resistencias ¼ W | 1 | 9.401 | Se usan 4× 10 kΩ, 1× 22 kΩ y 2× 1 kΩ | |
| Condensador cerámico 100 nF (104) | 3 | 73 | Acople de la ganancia y desacople del chip | |
| Electrolítico 10 µF 25 V | 1 | 96 | Estabilizar la referencia de 1.65 V | |
| Electrolítico 470 µF 100 V | 1 | 1.647 | Reserva de corriente para los servos | |
| Dupont M/H 30 cm ×10 | 1 | 2.094 | HC-SR04 y servos a la protoboard | |
| Cable micro-USB de datos | 1 | — | Muchos cables sólo cargan y no transmiten datos | |
| Cautín y estaño | — | — | Desoldar el receptor del HC-SR04 #2: es lo único que se suelda | |
| Multímetro (muy recomendado) | — | — | Medir los 1.65 V y la continuidad |
Total ≈ 97.000 COP sin envío.
Verifica que los condensadores de 100 nF sean de patitas y no SMD. El de 470 µF a 100 V es más grande de lo necesario, pero sirve igual.
Plano del circuito
Pines de la Pico
Los pines físicos se cuentan desde la esquina del conector USB: del 1 al 20 por un lado y del 21 al 40 por el otro. La numeración viene impresa en la parte de abajo de la placa.
| Pin físico | Nombre | Se conecta a |
|---|---|---|
| 40 | VBUS (5 V del USB) | Riel +5 V: VCC del HC-SR04 #1, rojo de los servos, + del 470 µF |
| 38 | GND | Riel GND |
| 36 | 3V3(OUT) | Riel +3.3 V: sólo el MCP6002 y el divisor de referencia |
| 33 | AGND | GND (tierra del ADC) |
| 31 | GP26 / ADC0 | Salida del amplificador (pin 7 del MCP6002) |
| 22 | GP17 | Señal del servo TILT |
| 21 | GP16 | Señal del servo PAN |
| 20 | GP15 | TRIG del HC-SR04 #1 |
HC-SR04 #1 (emisor)
| Pin | Va a |
|---|---|
| VCC | +5 V (pin 40) |
| TRIG | GP15 (pin 20) |
| GND | GND |
| ECHO | NO CONECTAR: saca 5 V y quema la Pico. No se necesita. |
El receptor (lo único que se suelda)
- En el HC-SR04 #2, mira la serigrafía: cada transductor tiene una T o una R impresa cerca. Desuelda el que dice R.
- Suelda un cable de 20–30 cm a cada patita (sirve un dupont cortado, del lado macho) y trénzalos: el cabezal se mueve y el trenzado reduce el ruido.
- Con el multímetro, busca cuál patita tiene continuidad con la carcasa metálica: esa va a GND. Si no tienes multímetro funciona en cualquier sentido, sólo que con un poco más de ruido.
Servos y condensador
Marrón → GND · rojo → +5 V · naranja → GP16 (PAN, el de abajo) o GP17 (TILT, el de arriba). Pon el 470 µF entre +5 V y GND cerca de los servos: absorbe los picos de corriente que, si no, reinician la Pico.
Conectado al revés se puede inflar o reventar.
El amplificador del eco
El eco llega al transductor en milivoltios. El ADC de la Pico mide de 0 a 3.3 V. Dos etapas no inversoras con el MCP6002 (amplificador dual rail-to-rail, alimentado a 3.3 V) lo llevan a décimas de voltio, centrado en 1.65 V.
Visto desde arriba · la muesca o el punto marca el pin 1
Números clave
Etapa 2: 1 + 10k/1k = ×11
Total en papel: ≈ ×253
Corte inferior: 1/(2π·1k·100n) ≈ 1.6 kHz
Vmid = 3.3 V / 2 = 1.65 V
Los 100 nF en serie con el 1 kΩ hacen que la ganancia sólo actúe sobre la señal que cambia: en DC cada etapa tiene ganancia 1 y la salida queda quieta en 1.65 V, la mitad del rango del ADC.
REFERENCIA (Vmid = 1.65 V)
+3.3V ──[10k]──┬──[10k]── GND
├──(10 µF)── GND (+ hacia Vmid, − hacia GND)
Vmid
ETAPA 1 (×23)
Receptor patita A ────────────┬──── pin 3 (IN+ A)
Receptor patita B (carcasa) ─ GND
└──[10k]── Vmid
pin 1 (OUT A) ──[22k]── pin 2 (IN− A)
pin 2 (IN− A) ──[1k]──(100 nF)── GND
ETAPA 2 (×11)
pin 1 (OUT A) ─────────────────── pin 5 (IN+ B) cable directo
pin 7 (OUT B) ──[10k]── pin 6 (IN− B)
pin 6 (IN− B) ──[1k]──(100 nF)── GND
pin 7 (OUT B) ─────────────────── Pico GP26 / ADC0 (pin 31)
ALIMENTACIÓN
pin 8 (VDD) ── +3.3V pin 4 (VSS) ── GND
pin 8 ──(100 nF)── GND lo más cerca posible del chipPiezas: 4× 10 kΩ, 1× 22 kΩ, 2× 1 kΩ, 3× 100 nF, 1× 10 µF.
El MCP6002 tiene un producto ganancia-ancho de banda de ~1 MHz. Con ganancia ×23, la etapa 1 sólo llega hasta unos 43 kHz, justo encima de los 40 kHz que nos interesan. A 40 kHz la ganancia real estimada es ≈ ×17 en la etapa 1 y ≈ ×10 en la 2, o sea ≈ ×170 en total y no ×253. No daña nada; sólo da menos ganancia. Si en las pruebas el eco sale débil, se puede agregar una tercera etapa con el segundo MCP6002.
Armado paso a paso
En este orden, para poder probar cada parte antes de agregar la siguiente.
Pico y rieles de alimentación
Inserta la Pico atravesando el canal central de la protoboard. Une el pin 40 (VBUS) al riel +5 V, el 38 (GND) al riel −, y el 36 (3V3) a un riel aparte de +3.3 V. Une también el 33 (AGND) a GND.
Amplificador, sin el receptor
Arma el MCP6002 según el esquemático, con la muesca hacia un lado conocido. Conecta la Pico por USB y mide con el multímetro:
- Vmid (unión de los dos 10 kΩ): ≈ 1.65 V
- Pin 7 del MCP6002: ≈ 1.65 V
- Pin 8: ≈ 3.3 V
Si el pin 7 está en 0 V o en 3.3 V, revisa el divisor, la orientación del chip y los condensadores de 100 nF.
Receptor
Desuelda la R del HC-SR04 #2, suelda los cables trenzados y conéctalos: la patita de la carcasa a GND y la otra al pin 3.
HC-SR04 #1, sin ECHO
VCC → +5 V, GND → GND, TRIG → GP15. Deja ECHO al aire.
Servos y 470 µF
Conecta el condensador (franja blanca a GND) y luego los servos. No atornilles todavía los brazos: primero los centramos a 90° con la prueba P2.
Montaje mecánico
Arma el soporte pan-tilt con los servos ya centrados. En la plataforma de arriba fija el HC-SR04 #1 mirando al frente y pega el receptor justo al lado, separado 2–3 cm y apuntando en la misma dirección. Sirve cinta doble faz, silicona caliente o bandas elásticas. Deja holgura en los cables para que no tiren cuando el cabezal gire.
- ECHO del HC-SR04 conectado a la Pico: la quema (saca 5 V).
- MCP6002 alimentado a 5 V: su salida puede pasar de 3.3 V y dañar el ADC. Aliméntalo sólo del pin 36.
- 470 µF al revés.
Pruebas de encendido
Antes del firmware de captura, unos scripts cortos en MicroPython verifican cada parte por separado.
Instala MicroPython en la Pico: mantén BOOTSEL presionado al conectar el USB, copia el .uf2 oficial de
micropython.org a la unidad que aparece, y luego usa
Thonny o mpremote.
Estos scripts son sólo de diagnóstico. Leer el ADC en un bucle de Python da apenas unas decenas de miles de muestras por segundo; la captura real a 500 kS/s necesita DMA (fase F5).
P1 · La Pico responde
pip install mpremote
mpremote connect list # debe aparecer un puerto COM con la Pico
mpremote exec "import sys; print(sys.implementation)"P2 · Centrar los servos (antes de atornillar los brazos)
from machine import Pin, PWM import time def servo(pin): p = PWM(Pin(pin)); p.freq(50); return p def angle(p, deg): # SG90: ~0.5 ms = 0°, ~2.5 ms = 180°. Ajusta si tu servo zumba en los extremos. p.duty_ns(int(500_000 + deg / 180 * 2_000_000)) pan, tilt = servo(16), servo(17) angle(pan, 90); angle(tilt, 90) # centro: ahora atornilla los brazos time.sleep(1) for d in (60, 120, 90): # barrido corto, un servo a la vez angle(pan, d); time.sleep(0.5)
Esperado: los dos servos van al centro y el PAN gira ±30°. Si la Pico se reinicia al moverlos, revisa el 470 µF o usa una fuente aparte de 5 V con la GND unida.
P3 · Nivel de reposo del ADC
from machine import ADC, Pin Pin(23, Pin.OUT, value=1) # fuente de la Pico en modo PWM: menos rizado en el ADC adc = ADC(26) xs = [adc.read_u16() for _ in range(2000)] m = sum(xs) / len(xs) print("media", m, "≈", round(m / 65535 * 3.3, 3), "V", "min", min(xs), "max", max(xs))
Esperado: media ≈ 32 768 (≈ 1.65 V), con poca dispersión. Si toca 0 o 65 535 hay un problema en el amplificador.
P4 · Disparo y eco crudo
from machine import ADC, Pin import time, array Pin(23, Pin.OUT, value=1) trig, adc = Pin(15, Pin.OUT, value=0), ADC(26) N = 3000 buf = array.array("H", bytes(2 * N)) trig.value(1); time.sleep_us(10); trig.value(0) t0 = time.ticks_us() for i in range(N): buf[i] = adc.read_u16() dt = time.ticks_diff(time.ticks_us(), t0) fs = N / dt * 1e6 print("fs efectiva ≈", int(fs), "Hz") peak = max(range(N), key=lambda i: abs(buf[i] - 32768)) print("pico en muestra", peak, "→", round(peak / fs * 1e3, 2), "ms")
Esperado: con la mano a ~20 cm, algo de actividad por encima del reposo. A esta fs baja (que sub-muestrea los 40 kHz) la forma de onda no es fiable, pero confirma que el emisor dispara, que el receptor capta y que el amplificador responde. El HC-SR04 tarda unos cientos de µs en emitir después del TRIG; la diafonía directa TX→RX marca ese instante.
Tabla de aceptación
| Prueba | Pasa si… | Si falla |
|---|---|---|
| P1 Pico | Aparece el puerto COM y responde el REPL | Cable sólo de carga; reinstala el .uf2 |
| P2 Servos | Centran y barren sin reiniciar la Pico | 470 µF, fuente aparte, un servo a la vez |
| P3 ADC | Media ≈ 32 768 (1.65 V) | Divisor, orientación del chip, alimentación a 3.3 V |
| P4 Eco | Actividad tras el disparo que cambia al mover la mano | TRIG en GP15, VCC a 5 V, polaridad del receptor, cables trenzados |
Arquitectura propuesta
La fase 1 se diseñó con puertos y adaptadores justamente para esto: el procesamiento de señal no sabe de dónde viene la grabación. La fase 2 agrega un dispositivo nuevo, un posicionador y el escaneo 3D, y reutiliza el pipeline.
sonar pico info, sonar pico ping, sonar scan3dArchivos nuevos previstos
| Archivo | Contenido |
|---|---|
firmware/pico_sonar/ | Firmware de captura: disparo, ADC por DMA a 500 kS/s, servos por PWM, protocolo USB |
src/sonar/pico.py | Adaptador serial PicoSonar (nueva dependencia: pyserial) |
src/sonar/positioner.py | Puerto Positioner + fake |
src/sonar/simulation3d.py | Cuarto sintético (planos, esquinas, objetos) y patrón de haz |
src/sonar/scan3d.py | ScanGrid, PointCloud, to_cartesian, export_ply |
src/sonar/viz3d.py | Vista 3D y vista "radar" |
tests/test_scan3d.py … | Pared plana → puntos coplanares; esquina; .ply válido; protocolo contra un puerto serie falso |
AudioDevice
AudioDevice.playrec(signal) supone que nosotros generamos la forma de onda. Con el HC-SR04, la ráfaga la genera su
propio microcontrolador al recibir TRIG. Además, como la Pico dispara y muestrea con el mismo reloj, los frames llegan ya alineados:
se acaban la latencia desconocida y los buffers perdidos de Windows.
Protocolo Pico ↔ PC
Comandos de texto por línea sobre el USB serie (CDC), y respuestas binarias para los datos. Simple de depurar con cualquier terminal.
| Comando | Respuesta | Notas |
|---|---|---|
INFO | SONAR-PICO 0.1 fs=500000 n=12000 | Versión, tasa de muestreo y muestras por ping |
SERVO <pan> <tilt> | OK | Grados; la Pico espera a que el servo asiente antes de responder |
PING <n> | DATA <muestras> <n> + muestras × int32 LE | Suma de n capturas alineadas al disparo; el PC divide entre n |
RAW | DATA <muestras> 1 + muestras × uint16 LE | Una captura sin promediar, para depurar |
Presupuesto: 12 000 muestras (24 ms ≈ 4 m de alcance) × 4 bytes = 48 kB por dirección. La RAM de la Pico (264 kB) alcanza sobrado, y el USB full-speed lo envía en una fracción de segundo.
Por qué C para el firmware de captura
Recomendación: MicroPython para las pruebas de encendido (iterar rápido) y C con el Pico SDK (o el core arduino-pico) para la captura, donde el ADC por DMA a 500 kS/s y el PWM de los servos funcionan de forma determinista. La decisión queda abierta para F5.
Procesamiento a 40 kHz
| Etapa | Fase 1 (laptop) | Fase 2 (Pico) |
|---|---|---|
| Forma de onda | Chirp 2–8 kHz que generamos nosotros | Ráfaga fija de 8 ciclos de 40 kHz (la genera el HC-SR04) |
| Plantilla del filtro adaptado | El chirp emitido | Medida: la diafonía directa TX→RX promediada en la calibración |
| Pasa-banda | Banda del chirp | ~35–45 kHz |
| t₀ | Camino directo (cancela la latencia) | Igual, por consistencia (aquí la latencia ya es fija) |
| Alineación entre pings | Imprescindible (buffers perdidos) | Se mantiene como red de seguridad |
| Fondo | Escena estática | Diafonía TX→RX + soporte; se calibra apuntando a espacio libre |
| Detección | La misma: umbral global + OS-CFAR + rechazo de lóbulos laterales | |
Preset previsto
fs = 500_000 # ADC del RP2040 f_start, f_end = 35_000, 45_000 ping_interval = 0.030 # ~5 m de alcance sin solapar la reverberación max_range = 4.0 min_range = 0.05 # zona ciega por diafonía; se ajusta midiendo
Resolución esperada: la ráfaga dura 200 µs y el transductor es de banda estrecha (resuena varios ciclos más), así que dos objetos se separarán a partir de unos 5–10 cm. La precisión de un eco aislado debería ser mucho mejor gracias a la interpolación del pico. Ambas cifras son estimaciones y se miden en F10.
Hoja de ruta
Cada fase tiene una meta y criterios de "terminado" verificables. Las fases ya cumplidas vienen marcadas.
Qué esperar del resultado
✔ Sí
Un mapa 3D grueso del cuarto: paredes, esquinas, muebles grandes y puertas, en bloques del tamaño del haz. Una vista tipo radar que se va llenando mientras escanea. Un archivo .ply que se abre en MeshLab o CloudCompare.
✘ No
Una foto ni detalles finos. Ver a través de paredes. Tiempo real a 30 fps: un escaneo completo toma ~2 minutos. Objetos blandos o muy pequeños lejos.
A 40 kHz una pared lisa refleja casi todo el sonido como un espejo. Sólo devuelve eco cuando el haz le pega casi de frente; en ángulo, el sonido se va hacia otro lado. Por eso la nube saldrá dispersa en las paredes y más densa en esquinas, bordes y objetos irregulares. Los murciélagos tienen el mismo problema.
Riesgos técnicos y mitigaciones
| Riesgo | Efecto | Mitigación |
|---|---|---|
| Servos alimentados del USB (picos de ~0.5 A o más) | La Pico se reinicia | 470 µF, mover un servo a la vez; si no basta, fuente de 5 V 2 A con GND común |
| TRIG a 3.3 V en un HC-SR04 de 5 V | Puede no disparar en algunos clones | Suele funcionar (umbral TTL); si no, un transistor o un conversor de nivel |
| Ráfaga fija de 8 ciclos (no chirp) | Menos resolución que con un chirp | Plantilla medida; a futuro, excitar el transductor desde la Pico (PIO), aunque el transductor es de banda estrecha |
| Ancho de banda del MCP6002 (1 MHz) | Ganancia ≈ ×170 en vez de ×253 | Suficiente en principio; tercera etapa con el MCP6002 de repuesto |
| Diafonía TX→RX en el mismo cabezal | Satura los primeros centímetros | Separar 2–3 cm, una barrera de espuma, resta de fondo |
| Ruido digital en el ADC | Piso de ruido alto (el ADC del RP2040 tiene ~9 bits efectivos) | AGND, GP23 en alto, cables trenzados, promediar pings |
| Reflexión especular | Nube dispersa en paredes lisas | Pasos finos de ángulo, varias pasadas, acumular la nube |
| Precisión mecánica del SG90 | Error de unos pocos grados al apuntar | Acercarse a cada posición siempre desde el mismo lado; calibrar los ángulos |
Bitácora
Registro de decisiones y resultados. Agrega una entrada cada vez que algo funcione, falle o cambie el diseño.
La fase 1 llega a su límite
El sonar de la laptop mide 0.26 m con un objeto a 30 cm, pero el primer metro queda tapado por la resonancia del parlante y el chasis, y no hay dirección. Conclusión: para una imagen 3D hace falta un haz direccional.
Primera propuesta: interfaz USB de 192 kHz + TCT40
Descartada por disponibilidad en la tienda local y por costo (100–170 USD).
Rediseño con Raspberry Pi Pico
Una placa emite (vía HC-SR04), graba a 500 kS/s y mueve los servos. Lista de compras: ~97.000 COP.
Piezas compradas
Plano del dispositivo
Pines de la Pico, amplificador de dos etapas con valores del kit de resistencias, orden de armado y tres errores críticos. Se dejó lista la especificación para generar una imagen del circuito (pendiente).
Documentación de la fase 2
Esta página.
Inicio del armado: falta el MCP6002
Pan-tilt armado con el HC-SR04 emisor arriba y la Pico en la protoboard. El MCP6002 no llegó con la compra; se pide de nuevo. Se decidió usar el potenciómetro como ganancia ajustable de la etapa 2 (en lugar del 10 kΩ entre los pines 7 y 6).
Llega el MCP6002 → amplificador y pruebas P3–P4
Mientras tanto: P1, P2 (servos con el 470 µF) y desoldar el receptor.
Plantilla para nuevas entradas
<div class="tl ok"><div class="when">DD-MMM-AAAA</div><h4>Título</h4> <p>Qué se probó, qué se esperaba, qué pasó, qué se cambió.</p></div> <!-- clases: ok = funcionó · warn = parcial · bad = falló -->
Seguridad
Los humanos no oyen 40 kHz, pero los perros (hasta ~45 kHz) y los gatos (hasta ~64 kHz) sí. Haz sesiones cortas y no escanees con mascotas en el cuarto.
Todo funciona a 5 V del USB: no hay riesgo para personas. El riesgo es para las piezas: ver los tres errores críticos del armado. Desconecta el USB antes de cambiar cables.
Escanea tu propio espacio. Un mapa 3D de un cuarto revela su distribución; no lo compartas sin permiso de quien vive ahí.