sadar / docs /REPORT.es.md
Ruperth's picture
feat: polish mobile responsive layout across all dashboard screens
96bba34
|
Raw
History Blame Contribute Delete
22.8 kB
# SADAR
**Smart Anomaly Detection for Aviation Routes**
Memoria técnica
---
**Proyecto:** SADAR · Flight Conformance Monitor
**Dominio:** Vigilancia de tráfico aéreo · datos ADS-B
**Aeropuerto de estudio:** Madrid-Barajas (LEMD)
**Enfoque:** Aprendizaje profundo de una sola clase para detección de anomalías en trayectorias
**Fecha:** 2026
---
## Resumen ejecutivo
SADAR es un sistema de aprendizaje profundo que aprende el patrón normal de las
operaciones de aproximación y despegue en Madrid-Barajas (LEMD) a partir de datos de
vigilancia ADS-B y señala en tiempo real cualquier trayectoria que se aparte de ese
patrón. El sistema está concebido como **monitor de conformidad de vuelos**: no clasifica
eventos concretos, asigna a cada vuelo del espacio aéreo una puntuación interpretable de
anomalía para que un operador pueda revisar los casos que quedan fuera de la
distribución aprendida.
Se entrenan tres autoencoders secuenciales en igualdad de condiciones sobre unos 20.000
vuelos reales obtenidos de la red OpenSky (un autoencoder LSTM, un autoencoder
Transformer y un VAE-LSTM), junto con un baseline no secuencial basado en Isolation
Forest. Los cuatro detectores se evalúan sobre el mismo conjunto de test y sobre un banco
sintético con cinco tipos de inyección y varias intensidades. Se selecciona el
**VAE-LSTM** como modelo de producción: obtiene la mejor PR-AUC sobre datos reales
(0,299) y la mejor ROC-AUC media en el banco sintético (0,792). También se evalúan dos
estrategias de ensemble (consenso y más sensible) y se descartan, porque ninguna mejora
al mejor modelo individual.
El modelo seleccionado se sirve a través de una aplicación FastAPI y de un dashboard
React + Vite que combina un radar en vivo estilo torre de control, un ranking de
anomalías, una página de métricas y un simulador interactivo. Todo el sistema es
reproducible desde una única imagen Docker y está listo para desplegarse en Hugging Face
Spaces.
---
## 1. Motivación y alcance
La monitorización de trayectorias es una función central de la gestión del tráfico
aéreo. Hoy se apoya en reglas deterministas y en supervisión humana: separación,
perfiles de descenso, esperas o códigos de transpondedor de emergencia se vigilan
contra umbrales fijos. Esas reglas funcionan bien para las situaciones para las que
fueron diseñadas, pero no pueden anticipar comportamientos que sean inusuales sin ser
una violación de manual.
SADAR explora un enfoque complementario, basado en datos. En lugar de codificar reglas,
el sistema **aprende** cómo es una operación normal en LEMD a partir de una muestra
amplia de vuelos reales, y trata cada nueva trayectoria como más o menos compatible con
esa distribución. La salida es una puntuación continua de conformidad que el operador
puede ordenar, filtrar y umbralizar.
El alcance del proyecto es deliberadamente acotado:
- **Qué hace.** Detecta desviaciones respecto al patrón normal de tráfico de un único
aeropuerto (LEMD) a partir de un conjunto fijo de variables dinámicas extraídas de las
observaciones ADS-B.
- **Qué no hace.** Predecir eventos operativos concretos, sustituir al controlador,
emitir recomendaciones de seguridad certificadas, ni generalizar sin reentrenamiento a
aeropuertos con procedimientos distintos.
Esta formulación como **monitor de conformidad** es la que hace tratable el problema con
los datos disponibles: solo requiere ejemplos de vuelo normal, que son abundantes, y
deja clara la interpretación de la salida ("esta trayectoria no se parece a las que el
modelo ha visto"), sin sobrevender.
---
## 2. Datos
### 2.1 Fuente
El dataset se construye a partir de observaciones ADS-B captadas por la red OpenSky en
la zona terminal de Madrid-Barajas. Cubre aproximadamente 18 días de operaciones
muestreados entre junio de 2017 y marzo de 2020. Cada aeronave se reporta una vez cada
10 segundos, la cadencia natural de ADS-B.
| Propiedad | Valor |
|---|---|
| Trayectorias totales | ≈ 20.000 (≈ 950 a 1.200 por día) |
| Filas por día | 140.000 a 240.000 |
| Columnas | 21 |
| Puntos por vuelo (mín · mediana · máx) | 40 · 188 · 1.246 |
### 2.2 Variables
El esquema en bruto contiene información posicional (latitud, longitud, tiempo),
dinámica (altitud barométrica y geométrica, velocidad respecto al suelo, rumbo, régimen
vertical), campos derivados (distancia a pista, fase de vuelo), identidad (ICAO24,
indicativo, identificador de vuelo) y estado del transpondedor (squawk, indicador de
"en tierra", indicador de alerta, SPI y último contacto). Un campo, `operation`, está
vacío en todo el dataset y se descarta.
### 2.3 Calidad y limpieza
Un análisis de perfilado sobre el dataset completo revela varios problemas que se
gestionan en el pipeline de preprocesado:
- **Valores ausentes.** `geoaltitude` falta en el 34,9 % de las filas, `baroaltitude` en
el 19,6 %, los campos cinemáticos (`velocity`, `heading`, `vertrate`) en torno al
19 %. El resto se queda por debajo del 4 %.
- **Archivo duplicado.** Un día llega dos veces con nombres distintos y se deduplica por
hash de contenido.
- **Días vacíos o erróneos.** Algunos días llegan sin filas o con errores en el
manifest y se descartan.
- **Huecos de cobertura.** Cerca del 8 % de los vuelos en aire muestran un salto
superior a 120 s entre observaciones consecutivas. Casi siempre son artefactos de
cobertura del receptor, no eventos operativos, por lo que se utilizan como
característica, jamás como etiqueta.
Tras la limpieza, el dataset contiene un conjunto estable de operaciones normales en
LEMD que abarca varias estaciones e incluye el inicio de 2020, lo que sirve además como
cambio de distribución natural para poner a prueba el split temporal.
### 2.4 Observaciones destacadas
El análisis exploratorio confirma un pequeño conjunto de casos raros que se reservan
únicamente para validación:
- Cuatro vuelos llevan un código de transpondedor de emergencia en algún punto.
- Aproximadamente un centenar de vuelos muestran un patrón claro de motor y al aire
(aproximación seguida de ascenso sostenido).
Ninguno de estos vuelos entra en el conjunto de entrenamiento. Se utilizan como prueba
de cordura del modelo desplegado.
---
## 3. Formulación del problema
El dataset contiene muchos vuelos normales y casi ninguna anomalía etiquetada. Eso
descarta la clasificación supervisada y apunta al **aprendizaje de una sola clase**:
entrenar un modelo que capture la distribución de trayectorias normales y puntuar los
nuevos vuelos según su compatibilidad con esa distribución.
En la práctica, los tres modelos profundos son **autoencoders** entrenados solo con
vuelos normales. El objetivo de entrenamiento es reconstruir la ventana de entrada a
partir de una representación latente comprimida; en inferencia se utiliza el **error de
reconstrucción** como puntuación de conformidad. Una trayectoria parecida a los patrones
normales se reconstruye con precisión y recibe un score bajo; una trayectoria que se
aparta de esos patrones es más difícil de reconstruir y recibe un score alto.
Esta formulación encaja con el contexto operativo por tres motivos. Sólo necesita datos
normales, que son los únicos disponibles a escala. Produce una puntuación continua e
interpretable en lugar de una etiqueta dura, lo que es la salida adecuada para una
herramienta de apoyo a un operador humano. Y se comporta bien ante modos de fallo
desconocidos: cualquier trayectoria que el modelo no haya aprendido a reconstruir
emerge como anómala con independencia de qué es lo que la hace inusual.
---
## 4. Pipeline de preprocesado
El pipeline transforma los parquet en bruto en tensores de longitud fija ya
estandarizados y listos para entrenar. Cada paso está implementado como una función
pequeña y testeable bajo `src/sadar/data/`.
1. **Carga y fusión.** Se leen todos los parquet bajo `$SADAR_DATA_DIR` con pyarrow, se
concatenan y se deduplican. Los días vacíos o erróneos se descartan.
2. **Transformación de coordenadas.** La latitud y la longitud se proyectan a un sistema
métrico relativo a pista con `pyproj`. Así el modelo es robusto a la curvatura de las
coordenadas y recibe metros como unidad.
3. **Codificación del rumbo.** El rumbo se codifica como `(sin(rumbo), cos(rumbo))` para
evitar la discontinuidad entre 0 y 360 grados.
4. **Tratamiento de ausentes.** Los huecos cortos dentro de una trayectoria por lo demás
completa se rellenan con interpolación lineal. Las trayectorias con demasiados
ausentes se descartan.
5. **Resampleado.** Cada vuelo se resamplea a una rejilla uniforme de 10 s, que coincide
con la cadencia nominal de ADS-B.
6. **Ventanado.** Cada trayectoria resampleada se trocea en ventanas de longitud fija.
La longitud de ventana es un hiperparámetro definido en
`configs/preprocessing.yaml`.
7. **Estandarización.** Cada variable se normaliza a media cero y varianza unidad. El
escalador se ajusta **solo con el conjunto de entrenamiento** para evitar fugas, y se
guarda como `data/processed/scaler.npz`, de manera que la misma transformación se
aplica en inferencia.
8. **Split sin fugas.** La división se hace conjuntamente por `flight_id` y por fecha:
ningún vuelo aparece en más de un conjunto, y el test es estrictamente posterior en
el tiempo al entrenamiento. Los años 2017 a 2019 se usan para entrenamiento y
validación; 2020 se reserva para test. Este split realista es lo que permite tomar
las cifras de latencia con confianza.
9. **Limpieza del set de entrenamiento.** Los pocos vuelos con squawks de emergencia y
los go-around sospechosos se eliminan del entrenamiento. Sólo trayectorias
"normales" entran al modelo.
La entrada de todos los modelos profundos es una ventana de siete variables:
`[x_rel, y_rel, baroaltitude, velocity, sin_hdg, cos_hdg, vertrate]`.
---
## 5. Modelos
### 5.1 Baseline Isolation Forest
Detector de referencia, no secuencial. Cada ventana se resume por estadísticos por
variable (media, desviación, mínimo, máximo) y se alimenta a un Isolation Forest. El
baseline sirve para cuantificar cuánto añade realmente el modelado secuencial.
### 5.2 Autoencoder LSTM
Un encoder LSTM de dos capas comprime la ventana de entrada en un vector latente, y un
decoder LSTM espejado la reconstruye. Se entrena de extremo a extremo con MSE y el loop
de entrenamiento compartido descrito más abajo. Es la arquitectura caballo de batalla:
estable, rápida en inferencia y alineada con la bibliografía sobre modelado de
trayectorias ADS-B.
### 5.3 Autoencoder Transformer
El mismo esqueleto encoder-decoder, pero con atención multi-cabeza en lugar de
recurrencia. El Transformer captura dependencias a largo plazo dentro de la ventana sin
el cuello de botella secuencial de las LSTM, a cambio de más parámetros y una superficie
de hiperparámetros mayor.
### 5.4 VAE-LSTM (modelo de producción seleccionado)
Un autoencoder variacional construido sobre encoder y decoder LSTM. En lugar de producir
un único vector latente, el encoder produce una distribución posterior; el decoder
reconstruye a partir de una muestra. La función de pérdida es la ELBO habitual: MSE de
reconstrucción más un término KL que regulariza la posterior hacia una gaussiana
estándar, escalado por un factor beta. Esto aporta una interpretación probabilística al
score y se comporta mejor en el paso de selección de umbral.
### 5.5 Ensembles (evaluados y descartados)
Como parte de la comparación se evalúan dos estrategias de ensemble. Tras calibrar cada
modelo con z-score sobre las normales de validación, los tres modelos profundos se
combinan por la **media** de sus scores (consenso) o por el **máximo** (más sensible).
Ninguna combinación supera al mejor modelo individual en el test real, así que el
ensemble queda documentado y descartado. El detector final es el VAE-LSTM en solitario.
### 5.6 Infraestructura de entrenamiento compartida
Los tres modelos profundos comparten el mismo loop de entrenamiento, implementado una
sola vez en `src/sadar/models/training.py`. El loop usa:
- **Adam** con weight decay,
- planificador **ReduceLROnPlateau**,
- **early stopping** sobre la pérdida de validación,
- **MLflow** para tracking (losses, hiperparámetros, mejor checkpoint),
- **Optuna** para búsqueda de hiperparámetros, usado sobre todo en el VAE-LSTM.
Cada arquitectura tiene su propio fichero YAML bajo `configs/`. La reproducibilidad se
garantiza con semillas fijas y guardando junto al checkpoint la configuración exacta de
preprocesado utilizada.
---
## 6. Banco de anomalías sintéticas
Como las anomalías reales son escasas y carecen de etiqueta, se construye un banco
controlado sobre los vuelos normales del test. Cada escenario parte de una ventana
normal real e inyecta una perturbación conocida y etiquetada que se aplica de forma
gradual a partir de la mitad de la ventana. Se implementan cinco tipos de inyección,
cada uno en varias intensidades:
| Tipo | Descripción |
|---|---|
| Desviación de ruta | Salida lateral del corredor, parametrizada en metros. |
| Anomalía de altitud | Desplazamiento de altitud inconsistente con la fase. |
| Anomalía de velocidad | Aceleración o deceleración multiplicativa. |
| Holding | Giro de 360 ° repetido con periodo configurable. |
| Congelado de transpondedor | La señal se mantiene fija desde un punto dado. |
El banco produce etiquetas de verdad (cada inyección sabe exactamente cuándo empieza) y
por tanto permite dos métricas que los datos reales no pueden dar: **PR-AUC por tipo de
anomalía** y **latencia de detección** (mediana de segundos entre el inicio de la
inyección y el primer instante en que el score por paso cruza el umbral de alerta).
El banco sintético se utiliza únicamente para evaluación. Ninguna ventana sintética
entra jamás en el entrenamiento.
---
## 7. Evaluación
### 7.1 Conjunto de test real
Los cuatro detectores se puntúan sobre el mismo split de test. La métrica principal es
**PR-AUC**, más informativa que la ROC-AUC dado el fuerte desbalance de clases en datos
reales.
| Modelo | ROC-AUC | PR-AUC | ROC-AUC media sintética | Latencia mediana |
|---|---|---|---|---|
| Baseline Isolation Forest | 0,515 | 0,133 | 0,593 | n/d |
| Autoencoder LSTM | 0,648 | 0,260 | 0,779 | 115 s |
| Autoencoder Transformer | 0,614 | 0,227 | 0,743 | 115 s |
| **VAE-LSTM (seleccionado)** | **0,659** | **0,299** | **0,792** | 120 s |
| Ensemble (media) | 0,652 | 0,273 | 0,778 | n/d |
| Ensemble (máximo) | 0,658 | 0,278 | 0,774 | n/d |
Se desprenden tres conclusiones:
- Los modelos profundos aportan valor sustancial sobre el baseline (la PR-AUC
aproximadamente se duplica).
- El VAE-LSTM es el mejor en conjunto: mayor PR-AUC sobre datos reales, mayor ROC-AUC
media sintética y latencia competitiva.
- Las dos estrategias de ensemble no superan al mejor modelo individual y no se
retienen.
### 7.2 Banco sintético (ROC-AUC por anomalía)
El VAE-LSTM es el mejor en todos los tipos de anomalía. El patrón es consistente: las
perturbaciones grandes y sostenidas son más fáciles que las sutiles, y las maniobras
estructuradas (holding, cambios fuertes de velocidad) son más fáciles que los
desplazamientos pequeños.
| Anomalía | Intensidad | Baseline | LSTM | Transformer | VAE-LSTM |
|---|---|---|---|---|---|
| Desviación de ruta | 20.000 m | 0,513 | 0,556 | 0,543 | 0,558 |
| Desviación de ruta | 40.000 m | 0,537 | 0,684 | 0,657 | 0,700 |
| Desviación de ruta | 80.000 m | 0,618 | 0,883 | 0,865 | 0,899 |
| Desviación de altitud | 300 m | 0,502 | 0,512 | 0,505 | 0,514 |
| Desviación de altitud | 800 m | 0,518 | 0,581 | 0,531 | 0,593 |
| Desviación de altitud | 1.500 m | 0,561 | 0,726 | 0,606 | 0,752 |
| Factor de velocidad | x1,6 | 0,620 | 0,901 | 0,835 | 0,927 |
| Factor de velocidad | x2,2 | 0,743 | 0,985 | 0,965 | 0,989 |
| Factor de velocidad | x0,4 | 0,673 | 0,901 | 0,884 | 0,925 |
| Holding | 240 s/giro | 0,614 | 0,972 | 0,979 | 0,978 |
| Holding | 120 s/giro | 0,634 | 0,975 | 0,985 | 0,984 |
| Congelado transpondedor | fijo | 0,582 | 0,668 | 0,558 | 0,686 |
### 7.3 Curvas y matrices de confusión
Para cada detector se generan curvas precision-recall y ROC, junto con una matriz de
confusión tomada al umbral del percentil 99 de validación. Las figuras se guardan como
PNG en `reports/figures/` y los arrays subyacentes se almacenan dentro de
`reports/model_comparison.json`, de modo que el dashboard puede renderizarlas sin
recalcular.
---
## 8. Inferencia y servicio
El VAE-LSTM seleccionado se sirve mediante una aplicación FastAPI compacta
(`src/sadar/serve/app.py`). En el arranque el servicio carga:
- el checkpoint del modelo de producción,
- el escalador ajustado con los datos de entrenamiento,
- el informe de métricas precomputado,
- y una muestra curada de trayectorias para que el dashboard pueda funcionar sin los
datos brutos.
La superficie HTTP es deliberadamente reducida. El dashboard se comunica con el backend
a través de cinco endpoints:
| Endpoint | Función |
|---|---|
| `GET /api/health` | Sonda de vida (también devuelve un identificador de build). |
| `GET /api/scene` | Escena de radar actual: aeronaves, scores, umbral. |
| `GET /api/flights` | Lista ordenada de vuelos monitorizados con metadatos. |
| `GET /api/metrics` | Métricas comparativas y curvas precomputadas. |
| `POST /api/simulate` | Inyecta una anomalía en un vuelo y devuelve la respuesta. |
En producción, el mismo proceso FastAPI también sirve la SPA de React ya compilada (la
SPA se monta en `/`, la API queda bajo `/api`). Todo el sistema corre en un único
contenedor.
---
## 9. Frontend
El dashboard es una aplicación React 18 + Vite + TypeScript diseñada para que parezca y
se sienta como una consola de torre de control: fondo oscuro, bloques de datos en
monoespaciada, criticidad codificada por color (verde y cian para normal, ámbar y rojo
para alerta). Expone cuatro pantallas:
1. **Consola torre.** Un radar en directo centrado en LEMD con un panel lateral que
lista todos los vuelos monitorizados y su estado actual. Las aeronaves en seguimiento
o en alerta se diferencian por color.
2. **Simulador.** El corazón interactivo de la demo. El operador elige un vuelo, un tipo
de anomalía, una intensidad y un instante de inicio, e inyecta la perturbación en
tiempo real. El score de la derecha reacciona a la inyección y la latencia de
detección se reporta en segundos.
3. **Métricas.** El informe comparativo completo: curvas ROC y PR, desglose de PR-AUC
por anomalía, justificación de la selección del modelo y resumen del detector final.
4. **Presentación.** Un recorrido guiado utilizado para presentaciones en vivo del
proyecto.
La aplicación está internacionalizada en inglés y español mediante un pequeño contexto
de traducción.
---
## 10. Reproducibilidad y despliegue
El proyecto está diseñado para que el pipeline completo (preprocesado, entrenamiento,
evaluación, servicio) pueda reproducirse desde cero.
- **Entorno Python.** Fijado con `uv` y `pyproject.toml`; un único comando
(`uv sync`) reconstruye el entorno.
- **Entorno de frontend.** Gestionado con `pnpm` y `pnpm-lock.yaml`.
- **Configuración.** Todos los parámetros (rutas, longitud de ventana, dimensiones del
modelo, plan de entrenamiento, umbrales de evaluación) viven en ficheros YAML bajo
`configs/`. El código no lee rutas literales.
- **Semillas.** Una única semilla se fija al inicio de cada entry point y se propaga a
NumPy, scikit-learn y PyTorch.
- **Tracking.** MLflow registra todas las ejecuciones en local; Optuna guarda su
historial de búsqueda junto a los hiperparámetros resultantes.
- **Containerización.** Un único Dockerfile construye una imagen autocontenida que
incluye el modelo entrenado, el escalador, los informes precomputados y la SPA. La
misma imagen se utiliza en local (`docker compose up`) y en producción (Hugging Face
Spaces).
Un `Makefile` expone el flujo como targets con nombre: `make install`,
`make preprocess`, `make train-lstm`, `make train-transformer`, `make train-vae`,
`make tune-vae`, `make compare`, `make serve`, `make dev`, `make docker-up`.
---
## 11. Limitaciones y trabajo futuro
### 11.1 Limitaciones
- **Alcance.** El dataset cubre un solo aeropuerto (LEMD) y un periodo acotado. No se
espera que el modelo transfiera a otras terminales sin reentrenamiento.
- **Sin planes de vuelo.** La "ruta prevista" se aproxima como "patrón normal
aprendido". Un vuelo que se aparte de su plan de vuelo pero permanezca dentro del
patrón normal del tráfico podría no ser marcado.
- **Huecos de cobertura.** Los huecos del transpondedor en ADS-B están dominados por la
cobertura del receptor. Se usan como característica de entrada, no como etiqueta.
- **Desbalance de clases.** Los eventos anómalos reales son extremadamente raros. Las
cifras de PR-AUC sobre el test real se calculan con un conjunto positivo pequeño; el
banco sintético es el referente cuantitativo principal.
### 11.2 Trabajo futuro
- Ampliar el dataset a más aeropuertos y validar la generalización entre terminales.
- Incorporar los planes de vuelo, cuando estén disponibles, como señal auxiliar.
- Combinar el score de conformidad con un clasificador posterior entrenado sobre
eventos operativos etiquetados (motor y al aire, aproximaciones frustradas, holding)
para producir alertas más interpretables.
- Explorar ensembles probabilísticos que ponderen cada modelo por su verosimilitud
calibrada en lugar de hacerlo con un reductor fijo.
---
## 12. Conclusiones
SADAR muestra que un enfoque de aprendizaje profundo de una sola clase es una base
viable para la monitorización de conformidad de trayectorias en un aeropuerto
importante. Tres autoencoders secuenciales entrenados con los mismos datos y evaluados
sobre el mismo banco arrojan una elección de modelo clara y defendible: el
**VAE-LSTM** combina la mejor PR-AUC sobre datos reales, el mejor desempeño medio en el
banco sintético y una latencia mediana de detección competitiva, mejorando con holgura
a un baseline no secuencial. El sistema es completamente reproducible, se empaqueta en
un único contenedor y se sirve a través de un dashboard construido en torno a un caso
de uso real.
El resultado no es un producto cerrado, pero sí un prototipo sólido de cómo la
monitorización de conformidad basada en datos puede complementar las herramientas
basadas en reglas que usan hoy los operadores del tráfico aéreo.
---
**Repositorio.** Smart Anomaly Detection for Aviation Routes (SADAR).
Madrid-Barajas (LEMD) · 2026.