← índice del proyecto
FICHAS
PROFUNDAS de las piezas para S1N-ECO · vuelo, oído y planificación ·
2026-10-30
Pedido de J.L.L.O. (/goal 2026-10-30): «hacer la ingeniería inversa o
benchmark de las piezas de los que tienen licencia… no la pieza
completa, ver qué tiene cada una con profundidad y qué hay que tomar en
cuenta».
Método:
- Lectura del código clonado (scratchpad
repos/, sin
ejecutar ni compilar) y de los papers.
- Commits leídos: px4 72f9914b, mavsdk 8b1ad92, pymavlink bd0a1b2,
mavlink 6bf60db, qgc 124bd78, odas 004cb82, usb_4_mic_array 00585f0,
pyroomacoustics ff7d61f.
- Las horas de esfuerzo son estimaciones. El costo de CPU y RAM en la
Raspberry no está documentado en ninguna fuente: se
mide con la regla 9.
Formato de cada ficha:
- Qué hace: con el archivo y la línea donde
está.
- Entradas y salidas.
- Dependencias.
- Límites.
- Licencia.
- Tomar en cuenta: lo que hay que vigilar al
integrarla.
- Esfuerzo y humo: horas estimadas y prueba de
humo.
A. Vuelo
A1. Geocerca de PX4
- Qué hace:
navigator_main.cpp:1223-1325
revisa cada 200 ms la posición actual; ya no predice
hacia adelante. La geometría está en geofence.cpp:886:
banda de altura, después polígonos y círculos con PNPOLY.
- Si la cerca llega a medias, se descarta entera (l. 449).
- Un polígono se descarta en silencio si el home o la posición no lo
cumplen (l. 512-519).
- Entradas y salidas: la cerca llega por el protocolo
de misión (FENCE) o por
etc/geofence.txt.
- Parámetros:
GF_ACTION: 0 nada, 1 aviso, 2 Hold (por omisión), 3
Return, 4 Terminar, 5 Aterrizar.
GF_SOURCE, GF_MAX_HOR_DIST,
GF_MAX_VER_DIST.
- En esta versión ya no existen
GF_PREDICT ni GF_ALTMODE.
- Límites:
- Si la posición tiene más de 1 s de antigüedad, no revisa.
DO_REPOSITION (goto): el navegador revisa sólo
el destino, no el trayecto, y sólo si GF_ACTION ≥
2 (navigator_main.cpp:390-414, 552). Commander
responde ACCEPTED si el cambio a Hold procede
(Commander.cpp:881-900; si no, TEMPORARILY_REJECTED o
DENIED, l. 903-913), así que MAVSDK devuelve Success aunque el navegador
rechace el destino. El rechazo sólo se ve en el STATUSTEXT «Reposition
is outside geofence» y en que la posición no cambia.
- Los puntos de offboard no se validan antes: sólo se
reacciona cuando el dron ya cruzó.
- Licencia: BSD-3.
- Esfuerzo y humo: 3 h. En el simulador (SITL): subir
un polígono, mandar un goto afuera (debe salir el STATUSTEXT y la
posición no debe cambiar, aunque el comando vuelva ACCEPTED) y volar en
offboard hacia el borde (debe pasar a Hold).
A2.
Protección si se pierde el enlace o el offboard
- Qué hace:
offboardCheck.cpp y
failsafe.cpp:775.
- Parámetros (
commander_params.yaml):
COM_OF_LOSS_T: 1,0 s por omisión.
COM_OBL_RC_ACT: 0 = modo Posición por omisión,
que supone que hay radio.
COM_DL_LOSS_T: 10 s.
NAV_DLL_ACT: 0 = apagado por
omisión.
- Offboard exige una señal de vida de al menos 2 Hz antes de
entrar.
- Límites:
mavlink_receiver.cpp:1355-1425 descarta sin
avisar los puntos con altura relativa al home sin home válido,
o sobre el terreno sin terreno válido. El resultado es un failsafe sin
explicación.
- Error de signo en la l. 1398 cuando el home no
coincide con el origen del EKF: por álgebra en NED da home−destino−ref
donde debería dar ref−home−destino (auditoría 2026-10-30). Confirmarlo
en SITL.
- Si se pierde el computador acompañante no hay failsafe por
latido:
Commander.cpp:3090-3098 sólo anota
«mission computer lost» a los 5 s. En offboard sí actúa
COM_OF_LOSS_T (failsafe.cpp:775); en
goto/Loiter el dron queda en Hold hasta el failsafe de batería o de
enlace.
- Tomar en cuenta: fijar a mano
COM_OBL_RC_ACT=5 (Hold) o 3 (Return),
NAV_DLL_ACT=2, y COM_RC_IN_MODE=4 si se vuela
sin radio.
- Esfuerzo y humo: 2 h. Matar el proceso de la Pi en
vuelo offboard; debe pasar a Hold en 1 s ± 0,2. Y en modo goto
(el que usa v1): matar la Pi y comprobar que el dron se queda
en Hold y aparece «mission computer lost».
A3. Entrada del
computador acompañante
- Dos caminos:
- MAVLink «Onboard» por TELEM2:
MAV_1_CONFIG=TELEM2. Trae TIMESYNC a 10 Hz, ATTITUDE a 100
Hz, GLOBAL_POSITION_INT a 50 Hz
(mavlink_main.cpp:1827-1866).
- uXRCE-DDS / ROS 2: exige Ubuntu con ROS 2 (Jazzy en
24.04 o Humble en 22.04), no corre sobre Raspberry Pi OS. Sus
px4_msgs deben calzar con la versión de PX4. La interfaz de
modos externos está marcada «Experimental».
- Decisión: MAVLink Onboard. Es más
simple, usa menos memoria (regla 9) y no ata el sistema operativo.
- Esfuerzo: 2 h (MAVLink) contra 8–12 h (DDS).
A4. MAVSDK: goto,
offboard e importar un .plan
- Qué hace:
set_position_global reenvía el punto solo a 20 Hz
(offboard_impl.hpp:102).
start() falla con NoSetpointSet si antes
no se fijó un punto.
goto_location usa MAV_CMD_DO_REPOSITION
con altura AMSL (action_impl.cpp:520-527).
- Al importar un .plan:
- Exige
.plan versión 1, misión versión 2 y cerca versión
2.
- Sólo acepta el ComplexItem «survey» versión 5
(
mission_import.cpp:378-391). Cualquier otro tipo hace
fallar todo el archivo.
- Dependencias: C++17, CMake 3.22. El paquete Python
v4 ya no usa gRPC.
- Tomar en cuenta:
- Usar goto: se valida el destino contra la geocerca
y no necesita un flujo continuo. Pero la validación es sólo del destino
y la respuesta siempre es ACCEPTED: la Pi debe validar el
trayecto contra la cerca y un modelo de terreno, y comprobar
que la posición avanzó.
- Iniciar como
ComponentType::CompanionComputer. Si
arranca como estación de tierra, PX4 la confunde con la estación real y
nunca detecta que se perdió la estación verdadera.
- Esfuerzo: 4 h.
A5. Mensaje propio
S1N_CELL
- Qué hace:
mavlink_direct de MAVSDK
carga el XML en tiempo de ejecución (load_custom_xml); el
checksum adicional (CRC extra) sale del XML. La alternativa es
mavgen de pymavlink.
- Límites:
- Carga útil de 255 bytes como máximo.
- PX4 reenvía un mensaje desconocido si el reenvío
está activo.
- QGroundControl lo descarta
(
Comms/MAVLinkProtocol.cc:119-130: un mensaje desconocido
falla el CRC extra y se salta).
- Licencia: el generador es LGPLv3, pero el
código generado que se compila dentro de un binario es
MIT (excepción en
pymavlink/COPYING). Para un
dialecto Python que corre como fuente, la excepción no es literal:
preguntar o generar en C.
- Tomar en cuenta: duplicar lo esencial en
NAMED_VALUE_FLOAT (nombre de 10 caracteres como máximo)
para que QGC lo vea, y fijar el XML por sha256.
- Esfuerzo: 3 h.
A6. Sincronización de tiempo
y pose
- Qué hace:
- PX4 responde TIMESYNC en nanosegundos desde el arranque. Su filtro
(
Timesync.hpp:65-91) rechaza viajes de ida y vuelta de más
de 10 ms.
- MAVSDK sincroniza cada 5 s.
Telemetry::Position de MAVSDK no trae marca de
tiempo.
- Receta:
- Leer
GLOBAL_POSITION_INT y ATTITUDE
crudos, que traen time_boot_ms.
- Correr un TIMESYNC propio para estimar el desfase.
- Sellar cada lectura con el reloj monotónico de la Pi.
- Interpolar la pose.
- Guardar el desfase y su incertidumbre con cada celda.
- Esfuerzo y humo: 5 h. Desfase estable a menos de 1
ms en 2 min.
A7. QGroundControl sin
modificar
- Qué hace:
- Su inspector muestra
NAMED_VALUE_FLOAT. Un
S1N_CELL propio no aparece.
.plan = JSON con fileType "Plan" y versión
1.
- Mapas sin internet:
importSets/exportSets,
más un proveedor de URL propia {x}{y}{z} para un servidor
local de mosaicos.
- Licencia: usarlo sin modificar no obliga a
nada.
- Esfuerzo: 2 h.
Orden de vuelo: A3 → A6 → A2 → A1 → A4 (goto
primero) → A7 → A5. DDS/ROS 2 sólo si hace falta un modo propio.
B. Oído
B1. ODAS:
localización SRP-PHAT por niveles
- Qué hace:
freq2freq.c:63-66 normaliza la fase (PHAT).
xcorr2aimg.c:63-71: energía de un punto = suma de las
correlaciones de los pares válidos / número de pares.
mod_ssl.c va de grueso a fino.
delay.c:66-72 usa campo lejano (sólo
dirección, sin distancia).
- Entradas y salidas: entra la STFT con trama 256 y
salto 128; sale
{x,y,z,E} por fuente.
- Dependencias: fftw3, libconfig, alsa,
pulseaudio.
- Límites:
- No limita banda ni pondera por ruido: el
weightedphasor
existe, pero no se usa.
- El ReSpeaker tiene 64 mm de apertura, que dan unas 3 muestras de
diferencia máxima a 16 kHz. La resolución es gruesa.
- Licencia: MIT.
- Tomar en cuenta: una víctima a 1–3 m está en
campo cercano. Conviene tomar sólo la
idea (jerarquía de grueso a fino y la tabla precalculada de
retardos) y escribir un SRP propio sobre las celdas del
mapa, en unas 150 líneas.
- Esfuerzo: 1–2 días.
B2. ODAS: filtros espaciales
- Qué hace: son una máscara de búsqueda, no
una ganancia sobre la señal.
directivity.c:87-130: patrón 1 / sigmoide / 0.
:184-232: ganancia por punto = producto de los
filtros.
:269-273: un par vale si su ganancia ≥
gainMin.
- Límites: los filtros se
multiplican (intersección de conos). Para excluir una
hélice se pone un filtro hacia la dirección opuesta con ángulo 180°−α.
No quita ruido: la hélice sigue entrando por los
lóbulos laterales.
- Tomar en cuenta: el cfg del ReSpeaker trae (0,0,+1)
con (80,100), que deja pasar casi todo el hemisferio superior. Hay que
reescribirlo según el montaje.
- Esfuerzo: 30 min.
B3. ODAS: seguimiento
- Qué hace:
mixture2mixture.c enumera
todas las asignaciones posibles de fuentes (falsa,
nueva o pista existente): son (pistas+2)^fuentes combinaciones, 1.296
por trama con 4×4.
- Tomar en cuenta:
- ODAS se conecta como cliente TCP y se cae con
exit si nadie escucha.
- Arma el JSON con
sprintf(buf,"%s…",buf), que es
comportamiento indefinido.
- No conviene usarlo: S1N hace el seguimiento en su
propio mapa de celdas.
B4. Entrada de
audio del ReSpeaker y del UMA-16
- Qué hace: el cfg trae
nChannels=6; fS=16000; map:(2,3,4,5). El mapa
empieza en 1 (hop2hop.c:56-63). Micrófonos
en ±32 mm.
- Tomar en cuenta:
- El número de tarjeta cambia en la Pi 5 (verificar con
arecord -l).
- La correspondencia entre canal y micrófono físico no está
comprobada.
- El UMA-16 no tiene cfg en ODAS: sus 120 pares encarecen el cálculo
unas 20 veces.
- Esfuerzo y humo: 2 h. Golpear junto a un micrófono
y ver que su canal sube.
B5. ReSpeaker:
tuning.py y firmware de 6 canales
- Qué apagar:
AGCONOFF,
STATNOISEONOFF, NONSTATNOISEONOFF (y sus
_SR), ECHOONOFF, TRANSIENTONOFF,
NLATTENONOFF, NLAEC_MODE,
CNIONOFF, RT60ONOFF.
HPFONOFF 0: el filtro pasaaltos corta
en 70/125/180 Hz, y los golpes tienen energía baja.
- Congelar con
FREEZEONOFF 1 y
AECFREEZEONOFF 1.
- Fijar
AGCGAIN en 1.
- Trampas:
tuning.py:109 usa tostring(), que no
existe en Python ≥ 3.9: cambiar a tobytes().
- No se sabe si los ajustes sobreviven a desconectar: aplicarlos en
cada arranque y comprobarlos.
- Faltan en el repo los firmwares de 6,02/12,04 dB que nombra el
README.
- Licencia: Apache-2.0.
- Esfuerzo y humo: 1 h. Aplauso a 1 m y a 4 m: el
pico debe bajar con la distancia.
B6.
Blanqueo del ruido propio (GEVD/GSVD-MUSIC), reimplementado desde los
papers
- Fuentes: Nakamura 2011, Okutani 2012 (IROS),
Hoshiba 2017/2024.
- Algoritmo:
- R(ω) = media de x·xᴴ.
- K(ω) = ruido de las hélices, grabado o tomado de tramas
pasadas.
- GEVD: R e = λ K e, usando Cholesky de K.
- P(θ,ω) = |aᴴa| / Σ_{subespacio de ruido} |aᴴe|, evaluado en
campo cercano sobre las celdas.
- Sumar las bandas pesando por √λ₁.
- Costo: con M=4 la descomposición es trivial; lo que
pesa es recorrer las celdas.
- Trampas:
- Fijar el número de fuentes Ns.
- Regularizar con K+δI.
- Si las hélices cambian de RPM, una K vieja
falla.
- Advertencia: estas ecuaciones están escritas de
memoria. Verificarlas contra los PDF antes de
pre-registrar.
- Licencia: propia, sin código de HARK.
- Esfuerzo: 2–3 días.
B7. pyroomacoustics (sólo en
el Mac)
- Qué hace:
ShoeBox (room.py:2759) con materiales por
octava (rough_concrete, brickwork).
- DOA con SRP, MUSIC y NormMUSIC; banda por omisión 500–4000 Hz.
- Trampas:
- Su DOA es sólo de campo lejano:
doa.py:289 llama a ModeVector sin
mode, y self.r nunca se usa.
- No simula transmisión a través de escombros. Un
buen resultado en simulación no prueba nada en terreno.
- Licencia: MIT.
B8. Detector de
golpes repetidos (receta propia)
No hay una receta publicada para golpes humanos. La curtosis como
detector de impulsos sí está establecida en el diagnóstico de
máquinas.
Receta:
- Pasaaltos suave, sin cortar bajo ~50 Hz.
- Energía en tramas de 10 ms con un piso de ruido móvil.
- Inicio de golpe si la energía supera el piso en k dB
y la curtosis de 20–50 ms es claramente mayor que
3.
- Al menos 3 golpes con intervalos de 0,5–3 s y poca variación.
- Coincidencia del geófono con los micrófonos.
- Rechazar lo tonal y estable (las hélices).
- Salida: verosimilitud por celda.
Costo O(N), despreciable. Esfuerzo: 1–2 días.
Orden de oído: B5 → B4 → B7 → B8 → B1 (SRP propio en
campo cercano) → B2 → B6. B3 no se usa.
C. Planificación
Fuente de SAROPS: Kratzke, Stone y Frost, FUSION 2010; texto extraído
en el scratchpad (sarops2.txt). Los repositorios se leyeron
sin ejecutarlos.
C1. SAROPS: escenarios
ponderados (§2, §2.1.1)
- Qué hace: cada relato (LKP, Área, Viaje, LOB) lleva
un peso del usuario; los pesos suman 1. Todos los escenarios tienen el
mismo número de partículas (2.500, 5.000 o 10.000), y cada partícula
pesa W_escenario/N.
- El escenario Área sortea por CDF inversa dentro del
polígono, no por rechazo.
- Las amenazas multiplican la densidad de la hora del incidente por κ
∈ {1, 3, 5, 10}.
- Para S1N: cada testigo o pista es un escenario con
su peso. Se normaliza entre escenarios. Conviene la CDF
inversa porque los polígonos de escombros son delgados y el rechazo
sería lento.
- Esfuerzo y humo: medio día. Dos escenarios con
pesos 0,7 y 0,3: la masa en cada zona debe dar 0,70 y 0,30, con
tolerancia de ±0,02.
C2.
SAROPS: actualizar después de buscar, y su extensión a S1N (§2.4)
- SAROPS: p_fail(p) = ∏ₖ(1 − λ(dₖ)), y el peso nuevo
es w·p_fail (normalizado).
- Con falsas alarmas α, un solo objetivo:
- P(aviso | p) = 1 − (1−α)(1−λ(d_p)).
- Ante un silencio, (1−α) se cancela al normalizar y queda exactamente
SAROPS.
- Varias víctimas (grilla de ocupación), corregido por la
auditoría del 2026-10-30: una celda ocupada avisa si detecta
o si da falsa alarma, así que P(aviso | ocupada) =
1−(1−α)(1−λ) y P(aviso | vacía) = α.
- Si el sensor avisa: ℓᵢ += log[(λ_s+α_s−α_s·λ_s)/α_s].
- Si calla: ℓᵢ += log(1−λ_s(dᵢ)).
- Víctimas esperadas = Σσ(ℓᵢ).
- La versión anterior (log(λ/α) y log((1−λ)/(1−α))) suponía P(aviso |
ocupada) = λ: con λ < α un aviso bajaba la
celda.
- Ojo: sumar la evidencia en cada celda trata un
mismo aviso como independiente para todas, y Σσ(ℓ) puede saltar decenas
de víctimas por un golpe. Para un aviso que puede venir de varias
celdas, usar la verosimilitud conjunta 1−(1−α)∏(1−λ_j).
- Límites:
- Supone pasadas independientes, y eso infla la POD cuando se
repasa el mismo lugar.
- λ es una curva hecha para el mar.
- Para S1N:
- λ_s(d) por sentido, con d = distancia a través del
material.
- Acotar ℓ a [−10, 10].
- Usar partículas para un objetivo y grilla para varios.
- Humo: con λ = 0,8 y α = 0,05, un aviso suma
log(16,2) ≈ +2,785, y un silencio suma log(0,2) ≈ −1,61. Un solo aviso
debe cambiar las víctimas esperadas en ≤ 1.
C3. SAROPS: planificador (§3)
- Cómo planifica:
- POS = Σ w·(1 − ∏ p_fail).
- Arranca voraz: un rectángulo que crece y se encoge como
acordeón.
- Después hace 12 movidas locales y un salto grande al azar.
- Restricciones:
- Recorrido útil = 85 % de velocidad × tiempo.
- La separación entre pasadas tiene un mínimo y un máximo.
- Muestra de 1.500 partículas con reposición, que
crece si la desviación supera el 5 % de la POS. Queda
fija durante la optimización y por eso sesga la POS hacia arriba; el
paper reevalúa el plan final con todas.
- Para S1N: reportar siempre la POS reevaluada con
todas las partículas. En tierra se usan sectores o celdas, no
rectángulos.
- Esfuerzo: 2–3 días.
C4. SORAL: Charnes–Cooper y
Washburn
- Qué hace:
Alloc-CC.cpp:410: ordena por PSR = E·p (E = W·v/A),
iguala las PSR más altas y reparte el tiempo con tiempo_j =
−ln(1−POD)/E_j.
Alloc-W.cpp:448: varios recursos, con condiciones
KKT.
- POD = 1 − e^{−cobertura}.
- Licencia: MIT. El README (l. 59, 78-82) dice que el
autor la cambió de GPL a MIT en 2017; las cabeceras GPL de los .cpp
quedaron viejas. Igual conviene reescribir el método: es corto y el C++
tiene errores.
- Errores hallados:
delete timeNeeded sobre un new double[]
(comportamiento indefinido).
getTotalPOS() (Allocatn.cpp:422-433)
acumula sin volver a 0.
- Para S1N: sirve de comparador. Con
el mismo POC y la misma E, el reparto de S1N debe dar POS ≥ el de
Charnes–Cooper.
- Humo: el caso de
demo.py (40 h): la
reescritura debe coincidir con el C++ hasta 1e-9.
C5. SAREnv: generador
sintético (MIT)
- Qué hace: ajusta una log-normal a los radios de
Koester (25/50/75/95 %), la multiplica por las capas de OSM y normaliza
(
generation.py:559-580, 414).
- El error 2πr (l. 779-781, 821-823, 980, 1022;
también
area_probability l. 840):
- Pone la densidad de r en cada celda. La ley
efectiva queda r·f(r), y la mediana sube por e^{σ²}.
- Con sus propios radios: llano templado de 1,49 a 5,91 km
(×3,96); montaña seca de 3,24 a 10,25 km (×3,17).
- Corrección:
bell /= (2*np.pi*dist_km)
antes de normalizar.
- Otros errores:
- Una celda sin capa de OSM queda con probabilidad 0.
- Los huecos de los polígonos sólo se restan en sus vértices.
- Los
Point se descartan.
while True (muestreo por rechazo,
lost_person.py:35) sin salida si la intersección queda
vacía o con área 0.
- Usa los bordes de celda (
xedges) como si fueran
centros.
- Para S1N: sirve como banco de pruebas con víctima
conocida, parchado y con un piso ε > 0.
- Humo: con capas uniformes, la mediana de r debe
caer en e^{μ} ± 10 %.
C6.
WiSAR TARR: costo anisotrópico (AGPL-3, reescribir sólo las
ecuaciones)
- Qué hace: Dijkstra con 8 vecinos
(
cost_distance.py:122-143). Costo de cada paso =
(v_llano/v)·(1 + 0,3·min(|pendiente|, 1))·fricción·distancia 3D, con
Tobler v = 6·e^{−3,5|pendiente + 0,05|}.
- Errores:
- La «pendiente cruzada» usa la pendiente a lo largo
del paso: la cuenta dos veces y no castiga ir a media ladera.
- Una celda NaN se toma como plana.
- La conversión a metros sólo vale en EPSG:4326.
- Compara costo-esfuerzo con radios de Koester medidos en línea
recta.
- Para S1N: la pendiente cruzada correcta es la
perpendicular a la marcha. El tiempo se calcula como costo/v_llano. En
escombros, la fricción sale de la clase de daño.
- Humo: en terreno plano, el costo debe dar la
distancia euclídea ± 9 % (con 8 vecinos el error llega a 8,24 % a 22,5°;
o usar 16 vecinos); en una rampa, subir debe costar más que bajar.
C7. Pátrač: cola sin
internet (reescribir)
- Qué hace: tabla SQLite
requests; un
hilo envía un pedido cada 60 s.
- Errores (corregido por la auditoría del
2026-10-30):
ConnectPost fuerza status=200 (l. 204)
en todo envío, sin excepción, también al subir archivos
y cuando el archivo no existe: el operador no ve el error y el dato no
queda en cola.
- En la cola, los archivos nunca se borran
(
data es texto, no None) y se reenvían cada
minuto sin fin.
- Pedidos sin tiempo máximo de espera.
- Un pedido fallido bloquea a todos.
literal_eval(None) mata el hilo.
- El GPX: usa sólo el anillo exterior y el sistema
checo EPSG:5514, fijo en el código.
- Licencia:
qgis_patrac_3 no trae
LICENSE en la raíz, pero las cabeceras de sus .py dicen GPL: se
reescribe. El otro repo, Patrac (Java), es Apache-2.0: es
otro proyecto.
- Para S1N: cola SQLite con estado, número de
intentos, uuid para no duplicar, espera que crece y borrado sólo con
respuesta 2xx. GPX con todos los anillos, en UTM 18S/19S según la
zona.
- Humo: el servidor responde 500 tres veces y luego
200: la fila debe enviarse una sola vez.
C8.
SearchWing: cámara → ancho → patrón → .plan (GPL-3, reescribir)
- Qué hace:
common.py:36-56 calcula la altura con la geometría de
la cámara.
- El cuadrado expansivo usa tramos 1, 1, 2, 2… × S.
- El exportador escribe
.plan con comando 16 y frame
3.
- Errores:
- g en m²/px reportado como m/px. Hace volar ~9 veces más
bajo de lo necesario para 1 m² (√(60/0,7) ≈ 9,3):
algoMinSize son píxeles²
(objectSizeSweepwidth.py:3) y el «[m²]» del toml está
mal.
- El comentario dice S = ancho/2, pero el código usa S = ancho.
- La huella de la cámara no es el W de la teoría de búsqueda.
- El campo
Altitude del .plan no se actualiza, y agrega
un campo efficiency que no es de QGC.
- Para S1N: reescribir con unidades explícitas y con
un W medido a partir de λ_s.
C9. MapScore: la vara
para medir el mapa
- Qué hace: usa la métrica de Rossmo con la
corrección de Koester.
- r = (n + m/2)/N, con n = celdas más probables que la del hallazgo y
m = celdas empatadas.
- R = (0,5 − r)/0,5 ∈ [−1, 1]. Un mapa uniforme da 0.
- Errores:
values[x,y] con x = columna: lee la imagen
transpuesta (l. 277 contra l. 178-179).
- Los 8 bits por píxel producen muchos empates.
- El DOI de la cita está mal escrito.
- Licencia: el repo no declara licencia (todos los
derechos reservados por omisión): se reimplementa sólo la métrica
publicada (Rossmo/Koester).
- Para S1N: es la vara del mapa. Se calcula
values[fila, col] en float64.
- Humo: un mapa con el máximo en la víctima da R ≈ 1,
uno uniforme da 0 y uno invertido da ≈ −1.
Orden de planificación: C9 (la vara) → C5 (el banco,
ya parchado) → C1 → C2 → C4 (comparador) → C6 → C3 → C8 → C7.
D. Las trampas
más peligrosas de todo el conjunto
- Vuelo: si MAVSDK arranca como estación de tierra y
además
NAV_DLL_ACT=0, PX4 nunca detecta que se perdió la
estación real.
- Vuelo: los puntos de offboard no se revisan antes
contra la geocerca. Usar goto o validar en la Pi.
- Oído: el canal 0 del ReSpeaker (procesado: AGC,
supresión de ruido) deforma los golpes; ODAS usa los canales crudos 1–4.
Falta comprobar si el pasaaltos (
HPFONOFF) actúa también
sobre ellos: apagarlo por precaución. Además, tuning.py no
funciona en Python ≥ 3.9.
- Oído: ODAS y pyroomacoustics suponen campo lejano,
y la víctima está cerca.
- Oído: ningún simulador modela el paso del sonido
por los escombros. Ganar en simulación no prueba nada en terreno.
- Plan: el 2πr de SAREnv empuja la mediana de la
distancia 3 a 4 veces hacia afuera.
- Plan: MapScore lee la imagen transpuesta.
- Plan: las celdas sin dato quedan en probabilidad 0.
Siempre poner un piso ε.
- Plan: la POS sale optimista por la muestra fija y
por tratar las pasadas como independientes.
- Unidades y colas: SearchWing (píxeles² contra
m/px), TARR (costo contra línea recta) y la cola de Pátrač, que oculta
los fallos del envío directo y reenvía archivos sin fin.
- Mapa: la regla de ocupación debe usar P(aviso |
ocupada) = 1−(1−α)(1−λ), y un aviso no puede contarse como independiente
en todas las celdas.
- Vuelo: goto responde ACCEPTED aunque el navegador
rechace el destino, y si se pierde la Pi en goto no hay failsafe por
latido (el dron queda en Hold).
E.
Auditoría independiente (2026-10-30) y correcciones aplicadas
- Veredicto: CONFIRMA CON RESERVAS.
- Se comprobaron 20 afirmaciones contra el código y el paper. Casi
todas las citas archivo:línea son exactas.
- Dos afirmaciones eran falsas, y ya están corregidas
arriba:
- la cola de Pátrač no borra datos ante 404/500: oculta el fallo del
envío directo y reenvía archivos sin fin;
- el goto no «se rechaza»: PX4 responde ACCEPTED y el rechazo sólo se
ve en STATUSTEXT.
- También se corrigieron:
- la regla de log-odds (ahora P(aviso | ocupada) = 1−(1−α)(1−λ));
- el signo de
mavlink_receiver.cpp:1398 (es un error, no
una duda);
- la licencia de SORAL (relicenciado a MIT en 2017);
- la ruta del
while True de SAREnv
(lost_person.py:35);
- el factor ×9 de SearchWing, ahora condicionado a la unidad;
- la muestra de 1.500 de SAROPS, que crece;
- el margen de ±9 % de TARR.
- Hallazgo nuevo: si se pierde el computador
acompañante, PX4 no tiene failsafe
(
Commander.cpp:3090-3098).
F.
Segunda auditoría (2026-10-30) y correcciones aplicadas
Revisor independiente, sin ver la primera auditoría: 27 afirmaciones
contra los clones (commits declarados coinciden). Veredicto:
CONFIRMA CON RESERVAS. Todos los errores atribuidos a otras
piezas se sostienen (SAREnv ×3,96 y ×3,17 recalculados; MapScore
transpuesto; cola de Pátrač; SORAL; campo lejano de ODAS y
pyroomacoustics; tostring() de tuning.py).
Claude comprobó a mano el README del firmware (l. 37),
Commander.cpp:904/913,
objectSizeSweepwidth.py:3 y la falta de licencia de
MapScore.
Corregido arriba:
- D3 / B5, exagerada: el procesado del ReSpeaker
actúa sobre el canal 0; los canales 1–4 son crudos. Se mantiene apagarlo
todo por precaución (el pasaaltos dice actuar «on microphone
signals»).
- A2, contradicción: el humo probaba offboard, que v1
no usa; se agrega el humo en goto y se precisa que en offboard sí actúa
COM_OF_LOSS_T.
- A1 / A4, matiz: ACCEPTED sólo si el cambio a Hold
procede; si no, TEMPORARILY_REJECTED o DENIED.
- Líneas corridas: A5
(
Comms/MAVLinkProtocol.cc:119-130), C4
(Allocatn.cpp:422-433); C6 con su archivo:línea.
- C8 cerrado: píxeles², no m².
- C7: status=200 en todo envío;
Patrac
(Java) es Apache-2.0.
- Licencias: MapScore sin licencia (sólo la métrica);
excepción MIT de MAVLink sólo para código compilado.
- Sin fuente todavía: curtosis de B8, «offboard exige
≥ 2 Hz» y «MAVSDK sincroniza cada 5 s». Las ecuaciones de B6 siguen
escritas de memoria.
- D1 mezcla dos cosas: el latido tipo estación de
tierra de MAVSDK enmascara la detección;
NAV_DLL_ACT=0 hace que no se actúe aunque
se detecte.