Anexo 6: Configuración del entorno de simulación, variables de sistema y protocolo de reproducibilidad¶
La reproducibilidad empírica constituye un pilar metodológico indispensable para la investigación en robótica aérea y sistemas ciberfísicos guiados por inteligencia artificial (§10.9). En arquitecturas híbridas como DroneLM —donde interactúan un motor de simulación física en tiempo real (Unreal Engine 5.5 + Cosys-AirSim), un grafo de decisión táctico asíncrono (LangGraph) y un servidor local de inferencia para modelos de lenguaje y visión (LM Studio / llama.cpp)—, garantizar que un evaluador independiente pueda replicar con fidelidad idéntica los resultados experimentales exige documentar exhaustivamente los archivos de configuración, los perfiles de escalabilidad gráfica, el diccionario completo de variables de entorno y los manifiestos de misión.
Este anexo consolida todos los parámetros operativos y el procedimiento de despliegue paso a paso que hicieron posible la batería de vuelos evaluada a lo largo de los tres Tiers de experimentación (§10.3).
A6.1 Archivo de configuración de AirSim (settings.json) anotado¶
El plugin Cosys-AirSim lee su configuración al inicio desde el archivo settings.json, ubicado en Windows en la carpeta Documents\AirSim del usuario: %USERPROFILE%\Documents\AirSim\settings.json, o %USERPROFILE%\OneDrive\Documents\AirSim\settings.json si la carpeta Documentos está redirigida a OneDrive, como en la estación de esta tesis. La siguiente es la configuración vigente, utilizada en las corridas de octubre de 2026:
{
"SeeDocsAt": "https://github.com/Cosys-Lab/Cosys-AirSim/blob/main/docs/settings.md",
"SettingsVersion": 2.0,
"LogMessagesVisible": false,
"SimMode": "Multirotor",
"LocalHostIp": "0.0.0.0",
"ApiServerPort": 41451,
"RecordUIVisible": false,
"ClockType": "SteppableClock",
"OriginGeopoint": {
"Latitude": 47.641468,
"Longitude": -122.140165,
"Altitude": 122
},
"Vehicles": {
"Drone1": {
"VehicleType": "SimpleFlight",
"Cameras": {
"FollowCam": {
"CaptureSettings": [
{
"ImageType": 0,
"Width": 1080,
"Height": 720,
"FOV_Degrees": 90
}
],
"X": -4.0,
"Y": -2.5,
"Z": -1.5,
"Pitch": -15.0,
"Roll": 0.0,
"Yaw": 28.0,
"Gimbal": {
"Stabilization": 0.0
}
}
}
}
},
"CameraDefaults": {
"CaptureSettings": [
{
"ImageType": 0,
"Width": 1080,
"Height": 720
},
{
"ImageType": 3,
"Width": 1080,
"Height": 720
},
{
"ImageType": 5,
"Width": 1080,
"Height": 720
},
{
"ImageType": 1,
"Width": 1080,
"Height": 720
}
]
},
"SubWindows": [
{
"WindowID": 1,
"CameraName": "FollowCam",
"VehicleName": "Drone1",
"ImageType": 0,
"Visible": true
},
{
"WindowID": 2,
"CameraName": "front_center",
"VehicleName": "Drone1",
"ImageType": 0,
"Visible": true
}
]
}
Anotaciones de ingeniería del archivo de configuración:¶
SimMode: "Multirotor": instruye a AirSim a instanciar el modelo físico cinemático de cuadricóptero gobernado por el controlador internoSimpleFlight, con dinámica de 6 grados de libertad (6-DoF).ClockType: "SteppableClock": sincroniza el paso temporal de la simulación física con las consultas RPC de la API. A diferencia deScalableClocko el reloj de pared del sistema,SteppableClockgarantiza que cuando la GPU experimenta caídas momentáneas de tasa de cuadros por la inferencia del modelo multimodal, la física de vuelo no sufra aceleraciones destructivas ni inestabilidades de integración numérica en el estimador inercial.ApiServerPort: 41451: puerto TCP de enlace Msgpack-RPC por el que se comunicanAirSimClienty el motor de Unreal Engine.CameraDefaults.CaptureSettings:ImageType 0(Scene): captura RGB del fotograma en color natural, utilizada por el estimador de flujo óptico (FlowTTCEstimator) y por la red de profundidad estimada de la capa estratégica (cap. 6, §6.14); conSTRATEGIC_SOURCE = vlm, por el VLM. La cámara frontal no se declara: se usa la del vehículo por defecto (AIRSIM_CAMERA_NAME = "0"), con el campo visual horizontal por defecto de 90°.ImageType 3(DepthPlanar) /ImageType 1(DepthPerspective): canal de profundidad en punto flotante utilizado exclusivamente en los scripts offline de validación del TTC monocular (cap. 7) y en el hilo de auditoría de DistMin (cap. 10, §10.6.1); ningún componente del lazo de vuelo lo solicita (cap. 5, §5.16).ImageType 5(Segmentation): canal de segmentación semántica de mallas para auditoría visual.Width: 1080, Height: 720: resolución nativa del render target frontal.Vehicles.Drone1.Cameras.FollowCam: cámara externa de seguimiento, 4 m detrás, 2.5 m a la izquierda y 1.5 m arriba del dron, inclinada 15° hacia abajo, sin estabilización. Solo la usa la auditoría: produce el video.follow.webmque el visor sincroniza con el frontal (cap. 4, §4.4). Ningún componente del lazo la lee; si no está declarada, la corrida se graba sin ese video.SubWindows: dos ventanas secundarias en el visor de Unreal Engine, con la cámara de seguimiento y la frontal en tiempo real, para supervisar las corridas.
A6.2 Perfil de escalabilidad gráfica de Unreal Engine 5.5 y preservación de gradientes¶
En estaciones de trabajo con una única GPU de consumo (NVIDIA GeForce RTX 5060 de 8 GB de VRAM), la simulación gráfica fotorrealista compite directamente por la memoria de video con los tensores del modelo de lenguaje (§8.1). Para permitir la coexistencia de ambos procesos sin provocar desbordamientos (Out-Of-Memory), es imperativo optimizar el rendimiento gráfico del motor.
Sin embargo, reducir indiscriminadamente la calidad gráfica introduce un problema crítico en la visión artificial: los algoritmos de flujo óptico denso (Farnebäck y DIS) requieren textura visual y gradientes de intensidad espacial para calcular correspondencias entre fotogramas. Si se desactivan las sombras o se aplica un desenfoque excesivo en las texturas de los edificios y el suelo, el flujo óptico decae por debajo del piso de ruido (0.35 px), dejando ciego al estimador de colisión.
A6.2.1 Configuración forzada en Config/DefaultScalability.ini¶
Para impedir que Unreal Engine 5.5 reduzca dinámicamente la resolución interna de los búferes de captura de cámara al aplicar presets bajos, se fijó el modo de detalle en el archivo de configuración del proyecto:
[EffectsQuality@0]
r.DetailMode=2
[EffectsQuality@1]
r.DetailMode=2
[EffectsQuality@2]
r.DetailMode=2
[EffectsQuality@3]
r.DetailMode=2
[EffectsQuality@Cine]
r.DetailMode=2
A6.2.2 Preset de escalabilidad en el Editor de Unreal Engine¶
Para las corridas de producción se configuró el siguiente perfil dentro del editor de Unreal Engine: * Resolution Scale: 100% (sin reescalado espacial en el motor). * View Distance: Medium (suficiente para el horizonte visual del dron). * Anti-Aliasing: TSR (Temporal Super Resolution) en Medium. * Post-Processing: Low (suprime desenfoques cinemáticos artificiales). * Shadows: Epic (crucial para proyectar sombras nítidas sobre el pavimento, proporcionando los gradientes texturales que explota el flujo óptico). * Global Illumination: Lumen en Low / Screen Space. * Reflections: Screen Space. * Effects: Epic (garantiza la resolución completa del render target de la cámara virtual de AirSim).
(Véase captura de referencia en el repositorio: informe/2026-0907 Scalability Config for Airsim and Shadows needed for TTC estimation.png).
A6.3 Especificación exhaustiva de variables de entorno (config/.env)¶
Toda la configuración operativa de DroneLM está centralizada en un único archivo de configuración (config/.env, basado en la plantilla committeable config/.env.example). Los submódulos acceden a estas variables con ruta absoluta resuelta desde la raíz del repositorio.
A continuación se detalla el diccionario completo de variables y su impacto de ingeniería:
1. Hardware y Conexión RPC AirSim¶
| Variable | Valor Nominal | Justificación y Efecto |
|---|---|---|
AIRSIM_IP |
"127.0.0.1" |
Dirección loopback local. En pruebas sobre LAN (192.168.110.110), la latencia RPC superaba los 8 s por congestión en simGetImages; ejecutar en localhost redujo la llamada a < 45 ms. |
AIRSIM_PORT |
41451 |
Puerto TCP estándar de la API de AirSim. |
AIRSIM_VEHICLE_NAME |
"SimpleFlight" / "Drone1" |
Nombre del vehículo en el árbol de actores de Unreal Engine. |
AIRSIM_CAMERA_NAME |
"0" |
Identificador de la cámara frontal montada en el fuselaje. |
DEFAULT_FRAME_WIDTH |
1080 |
Ancho de captura nativo devuelto por el sensor virtual. |
DEFAULT_FRAME_HEIGHT |
720 |
Alto de captura nativo devuelto por el sensor virtual. |
AIRSIM_RPC_TIMEOUT |
8 |
Segundos de espera máxima por llamada RPC antes de declarar timeout. |
AIRSIM_STRICT |
true |
Si es true, la falla de conexión con AirSim marca degraded=True y entra en hover de seguridad; si es false (solo en tests unitarios), permite generar fotogramas sintéticos. |
2. Servidor de Inferencia Local (VLM)¶
| Variable | Valor Nominal | Justificación y Efecto |
|---|---|---|
LOCAL_LLM_URL |
"http://127.0.0.1:11434/v1" |
Endpoint OpenAI-compatible de Ollama, en la misma estación y GPU que la simulación. 127.0.0.1 y no localhost: en Windows localhost resuelve primero a ::1 y cada conexión espera ~2 s antes de caer a IPv4. |
LOCAL_LLM_MODEL_NAME |
"lfm2.5-vl-1.6b:q4km-ctx8k" |
Modelo de vuelo: tag local de Ollama creado desde hf.co/LiquidAI/LFM2.5-VL-1.6B-GGUF:Q4_K_M con num_ctx=8192 (~1.6 GB de VRAM; con el contexto por defecto, 3.1 GB). No usar la variante -Extract (completa el esquema sin mirar la imagen) ni la conversión MLX (respuesta vacía con JSON restringido y logprobs). |
VLM_IMAGE_MAX_SIZE |
384 |
Lado mayor de la imagen de la capa estratégica. Limita los tokens visuales y, con ellos, la latencia. |
DEEP_SCAN_IMAGE_MAX_SIZE |
256 |
Lado mayor de cada imagen del barrido panorámico (varias imágenes por consulta). |
VLM_USE_JSON_SCHEMA |
true |
Decodificación restringida por esquema JSON (Anexo 4; cap. 8, §8.2). |
SLM_HTTP_TIMEOUT_S / SLM_DEEP_HTTP_TIMEOUT_S |
15.0 / 20.0 |
Timeout del cliente HTTP en la consulta estratégica y en el barrido. |
SLM_DEEP_WATCHDOG_MS |
18000 |
Watchdog del barrido panorámico; debe ser menor que SLM_DEEP_HTTP_TIMEOUT_S para que gane siempre al corte HTTP. |
DEADLOCK_STRATEGY |
"depth" |
Resolución de atascos: "depth" (barrido juzgado por la profundidad estimada, cap. 5 §5.12), "deep_vlm" (barrido + VLM; ablación) o "blind" (escape vertical sin barrido). Cualquier otro valor aborta el arranque. |
STRATEGIC_SOURCE |
"depth" |
Fuente de la capa estratégica: "depth" (profundidad monocular estimada, cap. 5 §5.10.6), "vlm" (grilla del VLM) u "off" (sin capa). Con depth/depth el VLM no corre en vuelo y el runner no exige el servidor de inferencia (cap. 11 §11.6). |
DEPTH_MODEL |
depth-anything/Depth-Anything-V2-Metric-Outdoor-Small-hf |
Red de profundidad métrica (~25 M parámetros, ~110 ms con la GPU compartida, ~440 MiB de VRAM). Se descarga de Hugging Face la primera vez. |
DEPTH_FREE_M |
15.0 |
Un sector (o un rumbo del barrido) es libre si el percentil 5 de su profundidad estimada es ≥ este valor; igual a VLM_SUBGOAL_DIST_M. |
DEPTH_STRATEGIC_PERIOD_S |
1.0 |
Período mínimo entre consultas de la capa estratégica con la profundidad (con el VLM rige VLM_STRATEGIC_PERIOD_S, 3 s). |
VLM_STRATEGIC_ENABLED |
true |
Habilita la capa estratégica cuando su fuente es el VLM; con STRATEGIC_SOURCE = depth no se consulta (para quitar la capa se usa STRATEGIC_SOURCE = off). |
VLM_STRATEGIC_PERIOD_S / VLM_STRATEGIC_MAX_AGE_S |
3.0 / 10.0 |
Período mínimo entre consultas estratégicas y edad máxima de una respuesta para aplicarla. |
CAMERA_HFOV_DEG |
90.0 |
Campo visual horizontal con el que se calculan las direcciones de los sectores de la grilla de 3×3 (cap. 5, §5.10.3). |
VLM_SUBGOAL_DIST_M / VLM_SUBGOAL_MIN_AHEAD_M |
15.0 / 4.0 |
Distancia máxima de la sub-meta desde el ancla (nunca más lejos que el waypoint real) y distancia mínima por delante del dron para aplicarla. |
VLM_MAX_DZ_M / VLM_SUBGOAL_MIN_ALT_M |
4.0 / 6.0 |
Cambio máximo de altitud de una sub-meta (filas 1 y 3 de la grilla) y altitud mínima de una sub-meta. |
VLM_SUBGOAL_TIE_DEG |
10.0 |
Margen angular dentro del cual dos sectores libres empatan y decide la fila (medio > arriba > abajo) (cap. 5, §5.10.3). |
VLM_NEAR_WP_M |
8.0 |
A menos de esta distancia horizontal del waypoint real no se consulta al VLM ni se barre. |
SCAN_FAILED_SECTOR_DEG / SCAN_REPEAT_RADIUS_M / SCAN_HISTORY_MAX |
45.0 / 10.0 / 12 |
Salida de deadlock: sector alrededor del rumbo que falló o de un rumbo ya probado que se descarta, radio en el que un deadlock anterior cuenta como «la misma zona» y deadlocks recordados (cap. 5, §5.12). |
ROUTE_PLAN_MODE |
vlm |
Plan de ruta con el VLM sobre el mapa cenital antes del vuelo (vlm) o manifiesto tal cual (off). Activado con el juez por parches (AUC 0.81 en el banco, cap. 4 §4.3.1); requiere un mapa registrado con el mundo (citysim_ortho.png, townsim_ortho.png). El lote de MiniSim, sin obstáculos, vuela con --route-plan off (cap. 10, §10.5.3). |
ROUTE_JUDGE / ROUTE_PATCH_STEP_M / ROUTE_PATCH_M / ROUTE_PATCH_CONTEXT_M |
patches / 8.0 / 16.0 / 48.0 |
Juicio de cada candidata: parches cada 8 m, de 16 m con 48 m de contexto, clasificados por el VLM; la candidata se puntúa con la probabilidad media de «calle» (cap. 4 §4.3.1). |
ROUTE_RENDER |
corridor |
Dibujo de cada candidata para el planificador: corridor (pasillo con recorte propio, rotado y con el resto oscurecido) o line (línea roja sobre el recorte común del tramo, norte arriba). Cada uno tiene su prompt (cap. 4 §4.3.1). |
ROUTE_CORRIDOR_M |
10.0 |
Ancho del pasillo dibujado (m). |
ROUTE_LLM_MODEL_NAME |
"lfm2.5-vl-1.6b:q4km-ctx8k" |
Modelo del planificador de ruta, en el mismo servidor que el de vuelo; si falta, se usa LOCAL_LLM_MODEL_NAME. Necesita JSON forzado y logprobs en la misma consulta (la versión GGUF de LFM los da; la MLX no). |
ROUTE_DETOURS_M / ROUTE_MIN_SIDE_M / ROUTE_MARGIN_M |
25,50 / 10.0 / 25.0 |
Rutas candidatas por tramo: desvíos paralelos a cada lado, lado mínimo de una ruta en L y margen del recorte del mapa. |
ROUTE_IMAGE_PX / ROUTE_TIE_EPS |
672 / 0.05 |
Tamaño de la imagen del mapa que ve el modelo y diferencia de probabilidad por debajo de la cual se elige la candidata más corta. |
VLM_STUCK_QUERY_CYCLES |
15 |
Ciclos sin acercarse al objetivo a partir de los cuales se consulta aunque la meta esté fuera de cuadro (cap. 5, §5.10.1). |
VLM_MAX_TOKENS_STRATEGIC / VLM_MAX_TOKENS_DEEP |
384 / 512 |
Tope de tokens de la respuesta por modo; una respuesta cortada por el tope se reporta como respuesta_truncada (cap. 8, §8.5.4). |
CEILING_SIDEWAYS_STUCK_M |
0.03 |
Desplazamiento horizontal por ciclo por debajo del cual, con avance ordenado, el dron está trabado de costado y no se cuenta para detectar techo. |
VLM_STRATEGIC_MAX_GOAL_OFF_DEG |
40.0 |
Azimut máximo de la meta para consultar; en la práctica rige el recorte cuadrado (±33.7° en un fotograma de 3:2). |
3. Lazo de Control Táctico¶
| Variable | Valor Nominal | Justificación y Efecto |
|---|---|---|
LOOP_HZ |
5.0 |
Frecuencia objetivo del bucle de control (\(200\text{ ms}\) por ciclo). Calibrada para absorber la captura y el flujo óptico con margen holgado. |
AGENT_ARM |
slm |
Brazo activo: slm (lazo rápido + VLM), fsm (máquina de estados determinista) o reactive (línea base). |
AIRSIM_SEED |
1 |
Semilla de la corrida. En modo --seed-jitter, perturba levemente la pose inicial (\(\pm 1.5\text{ m}\), \(\pm 10^\circ\)). |
CMD_DURATION_S |
120.0 |
Vigencia asignada al comando de velocidad en AirSim. Previene cabeceos parásitos (pitch jerk) causados por reemisiones continuas de comandos idénticos al PID interno de SimpleFlight. |
4. Percepción, Flujo Óptico y TTC¶
| Variable | Valor Nominal | Justificación y Efecto |
|---|---|---|
FLOW_ALGORITHM |
"dis" |
Backend de flujo óptico: "dis" (Dense Inverse Search, rápido) o "farneback". |
FLOW_DOWNSCALE_WIDTH |
640 |
Ancho al que se reduce el fotograma antes de calcular el flujo óptico (\(640 \times 427\text{ px}\)); el default del código es 320 (cap. 6, §6.3). |
CAMERA_FX / CAMERA_FY |
554.0 |
Distancia focal calibrada de la cámara en píxeles. |
FLOW_NOISE_FLOOR_PX |
0.35 |
Magnitud mínima en píxeles para considerar que un vector es señal física y no ruido numérico. |
FOE_OUTLIER_ANGLE_RAD |
0.35 |
Umbral angular en radianes (\(\approx 20^\circ\)) para el filtro de consistencia RANSAC-lite del FOE. |
TTC_AGGREGATION_PERCENTILE |
20 |
Percentil espacial de orden utilizado para resumir el TTC de una celda (\(P_{20}\)). |
FLOW_MAX_ROTATION_DEG |
2.0 |
Rotación angular máxima admisible entre cuadros sucesivos antes de declarar flujo degradado. |
5. Umbrales del ObstacleField¶
| Variable | Valor Nominal | Justificación y Efecto |
|---|---|---|
OBSTACLE_OCCUPANCY_BLOCKED |
0.011 |
Umbral de ocupación para declarar bloqueo. Calibrado por ROC/Youden (AUC 0.87; cap. 7, §7.5); un valor de 0.35 dejaría el canal inactivo. |
OBSTACLE_TTC_BLOCKED_S |
2.5 |
Umbral temporal de TTC (en segundos) por debajo del cual una celda se considera en colisión inminente. |
OBSTACLE_MIN_CONFIDENCE |
0.15 |
Piso de píxeles válidos para que una celda participe en la votación. |
OBSTACLE_MIN_CONFIDENCE_TTC |
0.35 |
Piso de confianza exigido para que el TTC por sí solo bloquee una celda sin apoyo de ocupación. |
5b. Detección de atasco, escape y cierre de misión¶
| Variable | Valor nominal | Justificación y efecto |
|---|---|---|
STOPPED_DELIBERATIVE_CYCLES / STOPPED_MIN_CMD_MPS |
10 / 0.30 |
Ciclos con avance comandado (≥ 0.30 m/s) sin movimiento real antes de declarar deadlock por dron trabado (cap. 5, §5.3.1). |
WP_NO_PROGRESS_MIN_M / WP_NO_PROGRESS_THRESHOLD |
2.0 / 50 |
Mejora mínima de la distancia al objetivo actual y ciclos (10 s) sin ella antes de declarar deadlock. |
OPTICAL_MIN_ALT_M |
4.5 |
Piso óptico: por debajo, el flujo no es válido y no se cuentan señales de atasco. |
MAX_ESCAPE_ALT_M |
30.0 |
Por encima de esta altitud, el escape determinista es GIRAR_90 en lugar de GANAR_ALTURA. |
SCAN_HEADING_COUNT_DEEP / SCAN_SETTLE_CYCLES_DEEP / MAX_DEEP_SCAN_IMAGES |
4 / 2 / 5 |
Rumbos del barrido panorámico, ciclos de asentamiento por rumbo e imágenes máximas por consulta. |
SCAN_ROT_TIMEOUT_CYCLES |
10 |
Timeout de rotación del barrido (2 s): si el dron no alcanza el rumbo, el barrido se abandona. |
CORNER_OFFSET_M |
15.0 |
Distancia del desvío determinista del brazo fsm. El barrido usa VLM_SUBGOAL_DIST_M, igual que la capa estratégica. |
SUBGOAL_DEDUP_M |
10.0 |
Una sub-meta a menos de esta distancia de la pendiente se ignora. |
MANEUVER_DURATION_S / ESCAPE_MANEUVER_DURATION_S / GIRAR90_DURATION_S |
2.0 / 1.6 / 1.0 |
Duración de las maniobras comprometidas: evasión lateral, escape vertical y giro de 90°. |
EVASION_STUCK_THRESHOLD / STUCK_HARD_FACTOR / MAX_CONSECUTIVE_ESCAPES |
10 / 1.5 / 2 |
Umbrales de atasco y de escape del brazo fsm (cap. 5, §5.11, §5.15.2). |
CEILING_DETECT_CYCLES / CEILING_DETECT_DZ_M / CEILING_MARGIN_M / CEILING_SAFE_GAP_M / CEILING_RELEASE_M |
10 / 0.15 / 0.8 / 3.0 / 15.0 |
Detección de techo del guiado (cap. 5, §5.15.3). |
GOV_ENABLED |
true |
Gobernador de velocidad (cap. 5, §5.13). |
FREEZE_CYCLES / FREEZE_RECOVERY |
25 / abort |
Ciclos con estado físico idéntico para declarar physics_locked, y acción: abortar la corrida o teletransportar a la última pose libre (cap. 5, §5.19). |
LAND_DESCENT_SPEED_MPS |
0.5 |
Velocidad del descenso de land_smooth() al final de la misión. |
DISTMIN_TRACK_ENABLED / DISTMIN_TRACK_PERIOD_S |
true / 1.0 |
Hilo de auditoría de DistMin y período de muestreo de la profundidad (cap. 10, §10.6.1). |
TAKEOFF_VERTICAL / TAKEOFF_ALT_TOL_M |
true / 1.0 |
Despegue vertical hasta la altitud del primer objetivo antes de avanzar (cap. 5, §5.15). |
FLOW_HOLDOVER_MAX_FRAMES / FLOW_MAX_YAW_DPS_NEAR_OBSTACLE |
3 / 5.0 |
Holdover del TTC y tope de guiñada cerca de un obstáculo (cap. 6, §6.12). |
6. Grabación de Video y Viewport¶
| Variable | Valor nominal | Default si ausente | Justificación y efecto |
|---|---|---|---|
FLIGHT_VIDEO_SCALE / FLIGHT_VIDEO_QUEUE_MAX |
0.6 / 300 |
0.6 / 300 |
Escala del video respecto del cuadro de la cámara y tamaño de la cola del hilo codificador (la codificación corre fuera del lazo de control, cap. 5, §5.18). |
FLIGHT_RECORD_VIDEO |
true |
false |
Habilita la grabación del video de auditoría .webm (VP8) por corrida. Desactivar en lotes headless elimina la escritura de un frame por ciclo a disco, reduciendo la carga de CPU en ≈ 30 %. |
FLIGHT_RECORD_VIEWPORT |
false |
false |
Activa el modo split-screen: el video resultante tiene doble ancho horizontal, con la cámara del drone a la izquierda y el viewport de Unreal Engine Editor a la derecha. Requiere las dependencias adicionales mss y pywin32. Si alguna de las dos no está instalada, o la ventana de UE no es visible en pantalla, el panel derecho se rellena con negro y la grabación continúa sin interrupción (degradación silenciosa). Debe desactivarse en corridas batch o headless donde el editor no está en pantalla. |
VIEWPORT_WINDOW_TITLE |
"UnrealEditor" |
"UnrealEditor" |
Subcadena del título de la ventana de Unreal Engine que utiliza ViewportCapture para localizar la ventana. La comparación ignora mayúsculas y minúsculas. Se selecciona la primera ventana visible que contenga la subcadena y tenga dimensiones superiores a \(100 \times 100\text{ px}\). En UE5 el título suele seguir el patrón "UnrealEditor – <NombreProyecto>". |
VIEWPORT_REGION |
(vacío) | (detección automática) | Coordenadas absolutas de pantalla en píxeles para capturar una región fija, con formato left,top,right,bottom. Cuando se define, ViewportCapture omite la búsqueda por título y captura exactamente esa región, lo que permite recortar el chrome del editor y mostrar únicamente el panel de viewport interior. Ejemplo para un viewport que ocupa la mitad derecha de un monitor de \(1920 \times 1080\text{ px}\): VIEWPORT_REGION=960,0,1920,1080. Si está vacío, la localización se delega a VIEWPORT_WINDOW_TITLE. |
A6.4 Estructura del repositorio y artefactos de corrida (airsim-runs/)¶
Cada vuelo ejecutado en simulación genera una carpeta autocontenida que ninguna otra corrida sobrescribe. Desde WebDCS (main.py), la carpeta es airsim-runs/<MISSION_ID>-<TIMESTAMP>/; el runner experimental la ubica en la jerarquía del diseño, <lote>/<escenario>/<brazo>/<estrategia>/seed_<N>_<timestamp>/ (cap. 10, §10.9.1). La lista completa de archivos y campos está en el capítulo 4 (§4.4); los principales son:
seed_<N>_<ts>/
├── seed_<N>_<ts>.summary.json <- Indicadores consolidados de la corrida
├── seed_<N>_<ts>.summary_by_wp.csv <- Indicadores por objetivo
├── seed_<N>_<ts>.jsonl <- Traza ciclo a ciclo con el DroneState completo
├── seed_<N>_<ts>.csv <- La misma traza en columnas (state.*)
├── seed_<N>_<ts>.distmin.ndjson <- Muestras de la auditoría de distancia mínima (1 Hz)
├── seed_<N>_<ts>.webm <- Video de la cámara frontal con overlay
└── seed_<N>_<ts>.viewer.html <- Visor de auditoría autocontenido
A6.4.1 Estructura del archivo de resumen (.summary.json)¶
Contiene la identificación de la corrida y sus indicadores. Ejemplo de una corrida slm:depth en citysim_pilot (valores ilustrativos; route_histogram y route_plan abreviados):
{
"scenario": "citysim_pilot",
"seed": 1,
"arm": "slm",
"code_version": "<hash>",
"deadlock_strategy": "depth",
"strategic_source": "depth",
"route_plan": {"mode": "vlm", "judge": "patches", "model": "lfm2.5-vl-1.6b:q4km-ctx8k",
"map": "citysim_ortho.png", "vias": 7, "legs_rerouted": 4},
"success": false,
"cycles": 2014,
"duration_s": 480.66,
"path_length_m": 702.68,
"collisions": 0,
"min_obstacle_dist_m": 6.562,
"min_obstacle_dist_source": "in_flight_thread",
"min_obstacle_dist_samples": 477,
"slm_invocations": 255,
"deliberation_rate": 0.127,
"slm_fallback_rate": 0.0,
"slm_timeout_rate": 0.0,
"route_histogram": {"reactive": 1585, "evasive": 419, "girar_90": 9, "tactical": 1},
"deadlock_events": 1,
"deep_scan_events": 1,
"deep_scan_resolution_rate": 0.0,
"deep_scan_fallback_rate": 1.0,
"vertical_escapes": 0,
"freeze_recoveries": 0
}
Nota sobre trazabilidad: el campo code_version almacena el hash de Git del commit sobre el que se ejecutó el vuelo. Si había cambios sin commitear en airsim-loop/, config/, airsim-plan/ o experiments/, lleva el sufijo -dirty (src/logging/code_version.py): el hash solo identifica el código con el árbol limpio. El runner no arranca un lote con el árbol sucio, y una corrida -dirty no entra al análisis (cap. 10, §10.9.3 y §10.12.2).
A6.4.2 Estructura de la traza ciclo a ciclo (.jsonl)¶
Cada línea del archivo es un registro JSON por ciclo del lazo (5 Hz), escrito por FlightLogger. Los campos principales son t y cycle; arm, scenario y seed; posición NED, velocidad y actitud; route y action; el obstacle_field completo por sector; latencias del ciclo; el bloque slm en los ciclos con una consulta del lazo lento; colisión; objetivo activo y distancia; y state, el DroneState completo serializado (cap. 4, §4.4).
A6.5 Manifiestos de misión y diseño de escenarios evaluados (Tiers 0, 1 y 2)¶
Los planes de vuelo se definen como manifiestos JSON ubicados en airsim-plan/missions/flightplans/. A continuación se especifican los escenarios representativos empleados en la tesis:
A6.5.1 Manifiesto Tier 1: Perímetro semiurbano (townsim_clear.json)¶
Evalúa el crucero perimetral sobre el mapa TownSim (Downtown West Modular Pack) a una altitud de tránsito de \(-30\text{ m}\) (30 m AGL):
{
"mission_id": "TOWNSIM_CLEAR",
"summary": "Tier 1: perímetro completo de townsim_calib.png a altitud de tránsito (-30m), sin cruzar ninguna fachada. Rol análogo a minisim_clear para Tier 0: control/smoke-test de crucero largo sin obstáculos deliberativos.",
"map": "townsim_ortho.png",
"waypoints": [
{ "x": 0.0, "y": 0.4, "z": -10.0, "label": "WP_1" },
{ "x": 0.4, "y": -71.6, "z": -30.0, "label": "WP_2" },
{ "x": -145.2, "y": -70.8, "z": -30.0, "label": "WP_3" },
{ "x": -146.8, "y": 83.6, "z": -30.0, "label": "WP_4" },
{ "x": 1.2, "y": 84.8, "z": -30.0, "label": "WP_5" },
{ "x": 0.0, "y": 27.2, "z": 0.0, "label": "WP_6" }
]
}
A6.5.2 Manifiesto Tier 1: Bloqueo frontal forzado (townsim_calib_cruce_frontal.json)¶
Diseñado para forzar una colisión inminente contra un edificio alto con aproximación frontal ortogonal:
{
"mission_id": "TOWNSIM_CALIB_CRUCE_FRONTAL",
"summary": "Tier 1: trayectoria ortogonal directa hacia la fachada principal de la manzana central. Obliga al lazo táctico a detectar TTC crítico, disparar giro o evasión de 90° o deliberar con el SLM para rodear la estructura.",
"map": "townsim_ortho.png",
"waypoints": [
{ "x": 0.0, "y": 0.0, "z": -10.0, "label": "WP_START" },
{ "x": -72.0, "y": 0.0, "z": -10.0, "label": "WP_OBSTACULO_FRONTAL" },
{ "x": -72.0, "y": 80.0, "z": -10.0, "label": "WP_DESVIO_LATERAL" }
]
}
A6.5.3 Manifiesto Tier 2: perímetro de manzana en rampa (citysim_clear.json)¶
Cuatro waypoints, ~420 m, sobre el perímetro de una manzana de CitySim. La altitud crece en rampa a lo largo del perímetro (−10, −30 y −50 m) y el último tramo vuelve a −10 m sobre el punto de partida; los cambios de altitud se hacen en diagonal, combinados con el avance horizontal:
{
"mission_id": "CITYSIM_CLEAR",
"summary": "Tier 2 (escenario base): perimetro de una manzana del grid regular de CitySim, 4 waypoints. Altitud creciente en rampa (-10, -30, -50 m) y retorno a -10 m sobre el punto de partida.",
"map": "citysim_ortho.png",
"waypoints": [
{ "x": 0.0, "y": 75.0, "z": -10.0, "label": "WP_1" },
{ "x": 130.0, "y": 75.0, "z": -30.0, "label": "WP_2" },
{ "x": 130.0, "y": 0.0, "z": -50.0, "label": "WP_3" },
{ "x": 0.0, "y": 0.0, "z": -10.0, "label": "WP_4" }
]
}
A6.5.4 Manifiesto Tier 1: Travesía del corredor arbolado (townsim_ini.json)¶
Ocho waypoints, ~340 m. Incluye WP_0b_SOBRE_PLAZA (cruza la plaza a −22 m para evitar la canopy de los árboles del patio) y WP_1b_PASO_MOLDURA (desvía 8 m al este del saliente de cornisa del edificio oeste). Justificación en el cap. 10, §10.3.2.
{
"mission_id": "TOWNSIM_INI",
"map": "townsim_ortho.png",
"waypoints": [
{ "x": 0.0, "y": 0.0, "z": -30.0, "label": "WP_0_ASCENSO" },
{ "x": -60.0, "y": 5.0, "z": -22.0, "label": "WP_0b_SOBRE_PLAZA" },
{ "x": -75.0, "y": 10.0, "z": -10.0, "label": "WP_1_ENTRADA_NORTE" },
{ "x": -70.0, "y": 0.0, "z": -10.0, "label": "WP_1b_PASO_MOLDURA" },
{ "x": -75.0, "y": -35.0, "z": -10.0, "label": "WP_2_CENTRO_CORREDOR" },
{ "x": -75.0, "y": -70.0, "z": -10.0, "label": "WP_3_SALIDA_SUR" },
{ "x": 0.0, "y": -70.0, "z": -30.0, "label": "WP_4_CLIMB_ESTE" },
{ "x": 0.0, "y": 0.0, "z": -10.0, "label": "WP_5_RETORNO" }
]
}
A6.6 Protocolo de reproducción experimental paso a paso¶
Para replicar de forma independiente los experimentos documentados en el capítulo 10, siga la secuencia metodológica descrita a continuación:
Paso 1: Verificación de software y dependencias¶
- Sistema operativo: Windows 10/11 (64-bit).
- Entorno conda
airsimenv(Python 3.10, PyTorch con CUDA ytransformerspara la red de profundidad; en la estación de la tesis,D:\TesisMCD\Airsimenv):La red de profundidad (conda activate airsimenv cd airsim-loop pip install -r requirements.txtDEPTH_MODEL) se descarga de Hugging Face la primera vez que se compila el grafo. - Verificar la disponibilidad del plugin binario de Cosys-AirSim en el proyecto de Unreal Engine 5.5 correspondiente (MiniSim, TownSim o CitySim).
Paso 2: Instalación del archivo settings.json¶
Guardar el contenido de §A6.1 como settings.json en la carpeta Documents\AirSim del usuario (o en OneDrive\Documents\AirSim si Documentos está redirigida), antes de abrir el proyecto de Unreal Engine.
Paso 3: Inicialización del servidor de modelos (Ollama, misma GPU)¶
- Ejecutar
startOllama.ps1(raízD:\TesisMCD, fuera del repositorio). El script lee el modelo deconfig/.env; reinicia Ollama conOLLAMA_MODELSapuntando a la carpeta de modelos de la aplicación,OLLAMA_KEEP_ALIVE=-1(el modelo no se descarga entre corridas) yOLLAMA_CONTEXT_LENGTH=8192; cierra losllama-serverhuérfanos (en Windows sobreviven al cierre deollama.exey retienen VRAM); descarga el GGUF base y crea el tag de 8 k si faltan, y precarga el modelo de vuelo:Con la configuración por defecto (powershell -ExecutionPolicy Bypass -File D:\TesisMCD\startOllama.ps1STRATEGIC_SOURCE = depth,DEADLOCK_STRATEGY = depth) el modelo solo se usa para el plan de ruta previo; si el lote corre con--route-plan off, Ollama puede quedar cerrado y su VRAM queda para Unreal. - Verificar que el modelo quedó residente:
curl http://127.0.0.1:11434/api/ps
Paso 4: Lanzamiento de la simulación¶
- Abrir el proyecto de Unreal Engine deseado (e.g.,
TownSim). - Presionar Play (PIE) o ejecutar el binario standalone.
- Constatar que en la esquina superior aparezca la notificación de conexión de AirSim en el puerto 41451.
Paso 5: Ejecución de los lotes (Runner)¶
Los seis lotes del diseño experimental (cap. 10, §10.5.3) se lanzan con los scripts versionados en experiments/, que fijan escenario, celdas, semillas, presupuesto (1200 s / 6000 ciclos) y modo del plan de ruta, y usan el intérprete de AIRSIM_ENV_DIR:
cd experiments
.\run_lote3_townsim_ini.ps1 -Pilot # piloto de validación
.\run_lote3_townsim_ini.ps1 # lote completo
.\run_lote3_townsim_ini.ps1 -Resume # reanudar un lote cortado
runner.py, que también puede usarse directamente:
python experiments/runner.py \
--scenarios ../airsim-plan/missions/flightplans/townsim_ini.json \
--cells slm:depth fsm:depth fsm:blind reactive \
--seeds 73 79 83 89 97 101 103 107 109 113 \
--max-seconds 1200 --max-cycles 6000 \
--route-plan vlm \
--out-dir ../airsim-runs/produccion/v4/lote3_townsim_ini
--deadlock-strategies ni --strategic-sources se usan los valores de config/.env (depth/depth). --route-plan vlm planifica la ruta con el VLM una vez por escenario y requiere el servidor de Ollama y un mapa registrado (TownSim o CitySim). Cada corrida se cierra con finalize_run (cap. 4, §4.4); una corrida interrumpida se cierra con python experiments/finalize_run.py --all <raíz>.
Paso 6: Procesamiento de datos y consolidación estadística¶
Una vez finalizados los lotes, generar las tablas estadísticas:
D:\TesisMCD\airsimenv\python.exe ..\airsim-loop\experiments\analyze_lotes.py ..\airsim-runs\produccion\v4 --family 18
analisis_lotes.md y dos CSV en la raíz de los lotes: por celda, éxito con IC de Wilson, SPL, tiempo de las misiones exitosas y distancia mínima; y las 18 comparaciones primarias con delta de Cliff, IC bootstrap y prueba de permutación exacta sobre rangos, con corrección de Bonferroni (cap. 10, §10.7).