{"id":11696,"date":"2026-03-31T09:32:28","date_gmt":"2026-03-31T07:32:28","guid":{"rendered":"https:\/\/bitacora.eniac2000.com\/?p=11696"},"modified":"2026-03-30T09:48:04","modified_gmt":"2026-03-30T07:48:04","slug":"aplicacion-android-para-fotogrametria-con-ia-fase-2-primera-version-capaz-de-volar","status":"publish","type":"post","link":"https:\/\/bitacora.eniac2000.com\/?p=11696","title":{"rendered":"Aplicaci\u00f3n Android para fotogrametr\u00eda con IA. Fase 2: Primera versi\u00f3n capaz de volar"},"content":{"rendered":"<div class=\"seriesmeta\">Esta entrada es la parte 3 de 12 de la serie <a href=\"https:\/\/bitacora.eniac2000.com\/?series=fotogrametria-asistida-por-ia\" class=\"series-1957\" title=\"Fotogrametr\u00eda asistida por IA\">Fotogrametr\u00eda asistida por IA<\/a><\/div>\n<p>En el <a href=\"https:\/\/bitacora.eniac2000.com\/?p=11672\">art\u00edculo anterior<\/a> de esta serie describ\u00ed c\u00f3mo se construy\u00f3 la Fase 1 de la aplicaci\u00f3n: desde la verificaci\u00f3n del entorno hasta tener una app funcional para planificar misiones de fotogrametr\u00eda sobre un mapa. El resultado era ya \u00fatil como herramienta de planificaci\u00f3n, pero le faltaba lo m\u00e1s importante: ser capaz de hacer volar al dron. Eso es lo que vino despu\u00e9s, y fue bastante m\u00e1s accidentado de lo que esperaba.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Integrando el DJI Mobile SDK v5<\/h2>\n\n\n\n<p>El primer paso de la Fase 2 fue integrar el <strong>DJI Mobile SDK v5.17.0<\/strong>, que es el que da acceso al control del Mini 3 Pro desde una aplicaci\u00f3n Android propia. La integraci\u00f3n no es trivial: requiere a\u00f1adir las dependencias espec\u00edficas de DJI en el fichero de Gradle, configurar su repositorio Maven en <code>settings.gradle.kts<\/code>, y gestionar una API Key de desarrollador que se inyecta en el manifest en tiempo de compilaci\u00f3n a trav\u00e9s de <code>local.properties<\/code>. Hasta aqu\u00ed, trabajo de fontaner\u00eda habitual.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"461\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-145658-1024x461.png\" alt=\"Aplicaci\u00f3n ya mostrando los mapas del Catastro\" class=\"wp-image-11679\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-145658-1024x461.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-145658-300x135.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-145658-768x346.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-145658-1536x691.png 1536w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-145658-2048x922.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Aplicaci\u00f3n ya mostrando los mapas del Catastro<\/figcaption><\/figure>\n\n\n\n<p>Lo m\u00e1s interesante de esta fase fue la arquitectura que se dise\u00f1\u00f3 para encapsular toda la interacci\u00f3n con el SDK en un paquete propio (<code>dji\/<\/code>), con responsabilidades bien separadas:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>DjiSdkManager<\/code><\/strong>: gestiona la inicializaci\u00f3n del SDK y el estado de conexi\u00f3n con el dron, expuesto como un <code>StateFlow&lt;Boolean><\/code>.<\/li>\n\n\n\n<li><strong><code>DroneTelemetry<\/code><\/strong>: se suscribe a todos los datos en tiempo real del dron \u2014GPS, heading, bater\u00eda, se\u00f1al, estado de vuelo\u2014 usando los listeners del <code>KeyManager<\/code> del SDK.<\/li>\n\n\n\n<li><strong><code>MissionExecutor<\/code><\/strong>: el motor de navegaci\u00f3n aut\u00f3noma. Aqu\u00ed es donde ocurre lo m\u00e1s interesante, y tambi\u00e9n donde llegaron los mayores problemas.<\/li>\n\n\n\n<li><strong><code>CameraConfigurator<\/code><\/strong>: control en vivo de los par\u00e1metros de c\u00e1mara (ISO, velocidad de obturaci\u00f3n, balance de blancos).<\/li>\n\n\n\n<li><strong><code>FlightLogger<\/code><\/strong>: logger que escribe a un fichero en el almacenamiento del dispositivo, pensado para diagn\u00f3stico en campo donde no hay acceso a un PC con ADB.<\/li>\n<\/ul>\n\n\n\n<p>La inicializaci\u00f3n del SDK merece una menci\u00f3n especial porque tiene un orden concreto que no se puede alterar: primero <code>SDKManager.init()<\/code> con su callback, y solo dentro del callback de \u00e9xito se llama a <code>registerApp()<\/code>. Invertirlo produce errores silenciosos que son dif\u00edciles de diagnosticar.<\/p>\n\n\n\n<p>Para completar la integraci\u00f3n tambi\u00e9n fue necesario a\u00f1adir un fichero <code>accessory_filter.xml<\/code> que permite que la app se abra autom\u00e1ticamente al conectar el mando DJI por USB, y un <code>network_security_config.xml<\/code> requerido por el SDK para sus comunicaciones internas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">La pantalla de vuelo: FPV y telemetr\u00eda<\/h2>\n\n\n\n<p>Con el SDK integrado, el siguiente paso fue construir la pantalla de ejecuci\u00f3n de misiones. El dise\u00f1o final es un layout horizontal a dos columnas, pensado para usarse en modo landscape con el tel\u00e9fono conectado al mando:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Columna izquierda (60%)<\/strong>: imagen FPV en vivo a pantalla casi completa, con un bot\u00f3n de retorno superpuesto como overlay.<\/li>\n\n\n\n<li><strong>Columna derecha (40%)<\/strong>: mini-mapa con el plan de vuelo y la posici\u00f3n del dron, telemetr\u00eda en tiempo real, estado de la misi\u00f3n y controles de vuelo (iniciar, pausar, parada de emergencia).<\/li>\n<\/ul>\n\n\n\n<p>El FPV se implementa con un <code>SurfaceView<\/code> que registra su superficie en el SDK de DJI para recibir el stream de v\u00eddeo. La integraci\u00f3n con Jetpack Compose se hace, igual que con MapLibre, mediante <code>AndroidView<\/code>.<\/p>\n\n\n\n<p>El dise\u00f1o de la zona de c\u00e1mara pas\u00f3 por varias iteraciones antes de llegar a algo satisfactorio. El primer intento fue un panel lateral con los controles; ocupaba demasiado espacio y dejaba la imagen FPV demasiado peque\u00f1a para ser \u00fatil. Despu\u00e9s se prob\u00f3 con un aspecto fijo 16:9, y luego 21:9, sin que ninguno funcionara bien en landscape. La soluci\u00f3n final fue la m\u00e1s obvia en retrospectiva: FPV a pantalla completa con controles transl\u00facidos superpuestos, al estilo de cualquier app de c\u00e1mara moderna. Los valores actuales de ISO, obturaci\u00f3n y balance de blancos aparecen como chips en la parte inferior; al tocar uno se despliega un selector.<\/p>\n\n\n\n<p>En cuanto a las capas cartogr\u00e1ficas, en esta fase se a\u00f1adieron dos espec\u00edficas para Espa\u00f1a que son muy \u00fatiles en campo:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Ortofotos PNOA<\/strong> del IGN: im\u00e1genes a\u00e9reas de alta resoluci\u00f3n via WMTS. Mucho m\u00e1s \u00fatiles que OSM para identificar el terreno antes de volar.<\/li>\n\n\n\n<li><strong>Catastro<\/strong>: parcelas y edificios superpuestos sobre las ortofotos via WMS INSPIRE. Imprescindible para saber exactamente sobre qu\u00e9 propiedad est\u00e1s volando.<\/li>\n<\/ul>\n\n\n\n<p>Ambas capas se seleccionan desde un bot\u00f3n en la esquina del mapa, y la selecci\u00f3n se persiste en el ViewModel para que no se pierda al navegar entre pantallas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">El descubrimiento que lo cambi\u00f3 todo: el Mini 3 Pro no soporta misiones onboard. Una prueba para Claude Code<\/h2>\n\n\n\n<p>Llegados aqu\u00ed es donde el proyecto se encontr\u00f3 con su mayor obst\u00e1culo, y tambi\u00e9n donde la experiencia se volvi\u00f3 m\u00e1s instructiva.<\/p>\n\n\n\n<p>El mecanismo est\u00e1ndar de DJI para ejecutar misiones de waypoints es subir al dron un archivo KMZ con la ruta definida y dejarlo volar de forma aut\u00f3noma usando su propio hardware de navegaci\u00f3n. Es el equivalente a programar un GPS y decirle \u00abllega t\u00fa solo\u00bb. La primera implementaci\u00f3n del motor de ejecuci\u00f3n usaba exactamente este sistema: <code>WaypointMissionManager.pushKMZFileToAircraft()<\/code>.<\/p>\n\n\n\n<p>El resultado fue un escueto error: <strong>\u00abUpload: null\u00bb<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"461\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-144535-1024x461.png\" alt=\"El error de Upload Null\" class=\"wp-image-11697\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-144535-1024x461.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-144535-300x135.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-144535-768x346.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-144535-1536x691.png 1536w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-144535-2048x922.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">El error de Upload Null<\/figcaption><\/figure>\n\n\n\n<p>Despu\u00e9s de descartar problemas de configuraci\u00f3n, el diagn\u00f3stico fue demoledor: el DJI Mini 3 Pro no soporta misiones onboard. Esta funcionalidad est\u00e1 reservada para la gama enterprise de DJI (Matrice, Mavic 3E y similares). El Mini 3 Pro, a pesar de ser un dron muy capaz, no tiene el hardware de navegaci\u00f3n necesario para ejecutar misiones de forma aut\u00f3noma sin intervenci\u00f3n continua de la app.<\/p>\n\n\n\n<p>Estrictamente hablando, <em>esto era algo que ya sab\u00eda<\/em>. Lo que quer\u00eda averiguar era si Claude Code iba a ser lo suficientemente <em>listo<\/em> como para detectarlo, y tomar las acciones en consecuencia. As\u00ed, y de manera completamente intencionada, no hab\u00eda dicho nada durante el proceso de preparaci\u00f3n de la aplicaci\u00f3n, y hab\u00eda omitido este detalle. Y Claude Code no fue capaz de averiguarlo desde el inicio. Hab\u00eda optado por la implementaci\u00f3n convencional, sin tener en cuenta las especificidades del modelo de dron con el que est\u00e1bamos trabajando.<\/p>\n\n\n\n<p>Una vez que Claude Code fue consciente de esto, no lo qued\u00f3 m\u00e1s remedio que replantear completamente el motor de ejecuci\u00f3n, e ir a la alternativa que s\u00ed est\u00e1 soportada para el Mini 3 Pro, que no es otra que <strong>Virtual Stick<\/strong>: un modo del SDK que simula los sticks del mando, enviando comandos de vuelo continuos desde la app a 20 Hz. En lugar de subir una misi\u00f3n y dejar que el dron la ejecute, la app es quien navega activamente en todo momento, calculando en cada ciclo hacia d\u00f3nde debe moverse el dron y enviando ese comando.<\/p>\n\n\n\n<p>Es m\u00e1s complejo, m\u00e1s exigente computacionalmente y m\u00e1s sensible a la latencia, pero funciona en todos los drones DJI. Y hay que decir que el resultado final es bastante m\u00e1s controlable que las misiones onboard, porque la l\u00f3gica de navegaci\u00f3n est\u00e1 completamente en tus manos.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Virtual Stick: c\u00f3mo se navega sin misiones onboard<\/h2>\n\n\n\n<p>El modo Virtual Stick que se implement\u00f3 usa el <strong>Advanced Mode<\/strong>, que ofrece bastante m\u00e1s control que el modo b\u00e1sico:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Velocidad expresada en coordenadas absolutas norte\/este (sistema GROUND), no relativas al dron.<\/li>\n\n\n\n<li>Altitud en modo POSITION: el SDK mantiene la altura objetivo sin que haya que controlarla constantemente.<\/li>\n\n\n\n<li>Yaw en modo ANGLE: se especifica un \u00e1ngulo absoluto (0\u00b0 = norte, sentido horario) en lugar de una velocidad de rotaci\u00f3n.<\/li>\n<\/ul>\n\n\n\n<p>El algoritmo de navegaci\u00f3n para cada waypoint funciona as\u00ed: leer la posici\u00f3n GPS actual del dron, calcular el bearing y la distancia hasta el objetivo, decidir el yaw apropiado, y enviar un comando de velocidad descompuesto en sus componentes norte y este. Todo esto se repite a 20 Hz hasta que la distancia al waypoint cae por debajo de 1,5 metros.<\/p>\n\n\n\n<p>Para evitar movimientos bruscos, especialmente problem\u00e1ticos con la latencia inherente a cualquier comunicaci\u00f3n inal\u00e1mbrica, se implement\u00f3 un <strong>perfil de velocidad trapezoidal<\/strong>: el dron acelera gradualmente al inicio de cada tramo, vuela a velocidad de crucero en la parte central y decelera al acercarse al waypoint, con una velocidad m\u00ednima de 0,3 m\/s para mantener el movimiento incluso muy cerca del objetivo.<\/p>\n\n\n\n<p>En los waypoints donde hay que tomar foto, la secuencia es: hover de 1,5 segundos \u2192 disparo \u2192 espera de 1 segundo. Antes de iniciar la navegaci\u00f3n, la app verifica que la c\u00e1mara est\u00e1 en modo foto (no en v\u00eddeo), un paso que parece obvio pero que si se omite provoca que los disparos fallen silenciosamente.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tres bugs de vuelo real, y c\u00f3mo se resolvieron<\/h2>\n\n\n\n<p>La depuraci\u00f3n de un sistema de navegaci\u00f3n aut\u00f3noma tiene una dificultad a\u00f1adida: los bugs se manifiestan en campo, con el dron en el aire, y no siempre hay acceso a herramientas de diagn\u00f3stico. Por eso se implement\u00f3 desde el principio el <code>FlightLogger<\/code>, que escribe un log detallado a un fichero recuperable despu\u00e9s mediante <code>adb pull<\/code>. Fue una decisi\u00f3n que result\u00f3 fundamental.<\/p>\n\n\n\n\n\n<p><strong>Bug 1: el dron volaba en direcci\u00f3n opuesta a los waypoints.<\/strong> El primer s\u00edntoma en campo fue que, tras despegar y alcanzar la altitud de misi\u00f3n, el dron se alejaba del \u00e1rea planificada en lugar de dirigirse al primer waypoint. La primera sospecha fue GPS inv\u00e1lido (coordenadas en 0,0), as\u00ed que se a\u00f1adi\u00f3 una funci\u00f3n <code>waitForValidGps()<\/code> que espera hasta 15 segundos a que el GPS sea fiable antes de empezar a navegar. El problema persisti\u00f3.<\/p>\n\n\n\n<p>Con los logs del segundo vuelo de prueba, el diagn\u00f3stico fue mucho m\u00e1s concreto. El GPS era correcto. Los bearings calculados eran correctos. Pero el movimiento real del dron no correspond\u00eda con las velocidades que se enviaban. Y ah\u00ed estaba el problema: en el DJI MSDK v5 con el sistema de coordenadas GROUND, <strong>los ejes pitch y roll est\u00e1n intercambiados<\/strong> respecto a la convenci\u00f3n aeron\u00e1utica habitual.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Lo que dice la intuici\u00f3n (y la documentaci\u00f3n):\nparam.pitch = northVelocity   \/\/ adelante\/atr\u00e1s\nparam.roll  = eastVelocity    \/\/ izquierda\/derecha\n\n\/\/ Lo que hace realmente el SDK v5 en modo GROUND:\nparam.pitch = eastVelocity    \/\/ este-oeste\nparam.roll  = northVelocity   \/\/ norte-sur<\/code><\/pre>\n\n\n\n<p>Intercambiar los dos valores en el c\u00f3digo hizo que el dron empezara a seguir los waypoints correctamente. Es el tipo de bug que no aparece en ninguna documentaci\u00f3n y que solo se descubre volando y tomando logs.<\/p>\n\n\n\n<p><strong>Bug 2: Virtual Stick falla al habilitarse.<\/strong> En algunos arranques, <code>enableVirtualStick()<\/code> devolv\u00eda un error con descripci\u00f3n nula, lo que hac\u00eda imposible iniciar la misi\u00f3n. El problema era de orden: el Advanced Mode se intentaba habilitar antes de que Virtual Stick estuviera activo. La soluci\u00f3n fue invertir el orden de las llamadas y a\u00f1adir un sistema de reintentos (tres intentos con dos segundos de espera entre cada uno), que es tambi\u00e9n lo recomendable para cualquier operaci\u00f3n que dependa de la comunicaci\u00f3n con el dron.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"461\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-195511-1024x461.png\" alt=\"Error de Virtual Stick Null\" class=\"wp-image-11698\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-195511-1024x461.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-195511-300x135.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-195511-768x346.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-195511-1536x691.png 1536w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/Screenshot_20260321-195511-2048x922.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Error de Virtual Stick Null<\/figcaption><\/figure>\n\n\n\n<p><strong>Bug 3: las fotos no se guardaban en el dron.<\/strong> El dron completaba la ruta correctamente \u2014se pod\u00eda ver en los logs que alcanzaba cada waypoint y ejecutaba el comando de disparo\u2014 pero al revisar la memoria no hab\u00eda fotos. La causa: la c\u00e1mara estaba en modo v\u00eddeo. <code>KeyStartShootPhoto<\/code> del SDK falla silenciosamente si la c\u00e1mara no est\u00e1 previamente configurada en modo foto. La soluci\u00f3n fue a\u00f1adir una llamada expl\u00edcita a <code>KeyCameraMode = PHOTO_NORMAL<\/code> antes de iniciar cualquier navegaci\u00f3n.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"768\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/IMG-20260322-WA0001-1024x768.jpg\" alt=\"La primera imagen registrada con la aplicaci\u00f3n\" class=\"wp-image-11677\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/IMG-20260322-WA0001-1024x768.jpg 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/IMG-20260322-WA0001-300x225.jpg 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/IMG-20260322-WA0001-768x576.jpg 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/IMG-20260322-WA0001-1536x1152.jpg 1536w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/03\/IMG-20260322-WA0001-2048x1536.jpg 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">La primera imagen registrada con la aplicaci\u00f3n, una vez corregido el error<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Optimizando el orden de los waypoints<\/h2>\n\n\n\n<p>Uno de los \u00faltimos refinamientos de esta fase fue la optimizaci\u00f3n del orden de recorrido de los waypoints, especialmente relevante en misiones de modelo 3D con doble rejilla (cross-hatch).<\/p>\n\n\n\n<p>El problema es que las l\u00edneas de vuelo se generan en dos grupos: primero todas las pasadas en una direcci\u00f3n, luego todas las perpendiculares. Recorrerlas en ese orden provoca un salto largo entre el \u00faltimo punto del primer grupo y el primero del segundo, lo que se traduce en distancia extra y tiempo de vuelo innecesario. Visualmente, la trayectoria parec\u00eda ca\u00f3tica.<\/p>\n\n\n\n<p>La soluci\u00f3n fue implementar un algoritmo de <strong>vecino m\u00e1s cercano<\/strong>: al terminar cada l\u00ednea de vuelo, se elige como siguiente la l\u00ednea pendiente cuyo extremo m\u00e1s cercano est\u00e9 a menor distancia, independientemente de si es una l\u00ednea primaria o perpendicular. Esto intercala naturalmente ambos grupos y minimiza las transiciones. El coste computacional para el tama\u00f1o t\u00edpico de una misi\u00f3n (menos de 100 l\u00edneas) es completamente despreciable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ajustes de UX para uso en campo<\/h2>\n\n\n\n<p>Adem\u00e1s del motor de navegaci\u00f3n, esta fase incluy\u00f3 una serie de ajustes de experiencia de usuario pensados espec\u00edficamente para el uso en campo, con el sol en la cara y el dron en el aire:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Landscape forzado<\/strong>: la app no tiene ning\u00fan sentido en modo vertical cuando el tel\u00e9fono est\u00e1 montado en el mando DJI.<\/li>\n\n\n\n<li><strong>Modo inmersivo<\/strong>: las barras del sistema Android se ocultan para maximizar el espacio de la imagen FPV.<\/li>\n\n\n\n<li><strong>Centrado de mapa en GPS real<\/strong>: en lugar de centrar en Madrid al arrancar, el mapa se posiciona en la ubicaci\u00f3n actual del dispositivo. \u00datil cuando llegas a una parcela y abres la app.<\/li>\n\n\n\n<li><strong>Persistencia de la capa de mapa<\/strong>: la selecci\u00f3n entre OSM, PNOA y Catastro se persiste en el ViewModel, as\u00ed no se resetea al navegar entre pantallas.<\/li>\n\n\n\n<li><strong>Restauraci\u00f3n de misi\u00f3n al volver al mapa<\/strong>: cuando se navega de la pantalla de vuelo al mapa y de vuelta, el pol\u00edgono y las l\u00edneas de vuelo se restauran correctamente porque el MapView se reinicializa.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">El resultado de la Fase 2<\/h2>\n\n\n\n<p>Al cierre de esta fase, la aplicaci\u00f3n era ya capaz de ejecutar misiones de fotogrametr\u00eda reales de principio a fin:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Integraci\u00f3n completa con DJI MSDK v5.17.0.<\/li>\n\n\n\n<li>Conexi\u00f3n USB con el mando y estado del dron en tiempo real (GPS, bater\u00eda, se\u00f1al, heading, altitud).<\/li>\n\n\n\n<li>Vista FPV integrada.<\/li>\n\n\n\n<li>Navegaci\u00f3n aut\u00f3noma via Virtual Stick con perfil de velocidad trapezoidal.<\/li>\n\n\n\n<li>Disparo autom\u00e1tico de c\u00e1mara en cada waypoint fotogr\u00e1fico.<\/li>\n\n\n\n<li>Control en vivo de par\u00e1metros de c\u00e1mara (ISO, obturaci\u00f3n, EV, balance de blancos).<\/li>\n\n\n\n<li>Mini-mapa con la posici\u00f3n del dron en tiempo real.<\/li>\n\n\n\n<li>Retorno autom\u00e1tico a casa al finalizar la misi\u00f3n.<\/li>\n\n\n\n<li>Parada de emergencia.<\/li>\n\n\n\n<li>Validaci\u00f3n de GPS antes de iniciar la navegaci\u00f3n.<\/li>\n\n\n\n<li>Logger a fichero para diagn\u00f3stico post-vuelo.<\/li>\n\n\n\n<li>Capas de mapa PNOA (IGN) y Catastro.<\/li>\n\n\n\n<li>Optimizaci\u00f3n de orden de waypoints por vecino m\u00e1s cercano.<\/li>\n<\/ul>\n\n\n\n<p>No fue un camino sin sobresaltos. El <em>descubrimiento<\/em> por parte de Claude Code de que el Mini 3 Pro no soporta misiones onboard le oblig\u00f3 a reescribir el motor de ejecuci\u00f3n desde cero. Los bugs de pitch\/roll intercambiados y de Virtual Stick fallando silenciosamente solo se pudieron diagnosticar con vuelos reales y logs detallados. Pero el resultado es una app que vuela misiones de fotogrametr\u00eda de forma aut\u00f3noma y que ha demostrado funcionar en campo.<\/p>\n\n\n\n<p>En el pr\u00f3ximo art\u00edculo hablar\u00e9 de la Fase 3: la correcci\u00f3n del heading del dron durante el vuelo, la visualizaci\u00f3n en tiempo real de la trayectoria sobre el mini-mapa, y una serie de mejoras de UX que marcaron la diferencia entre una app que funciona y una app que es c\u00f3moda de usar en campo.<\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"seriesmeta\">Esta entrada es la parte 3 de 12 de la serie <a href=\"https:\/\/bitacora.eniac2000.com\/?series=fotogrametria-asistida-por-ia\" class=\"series-1957\" title=\"Fotogrametr\u00eda asistida por IA\">Fotogrametr\u00eda asistida por IA<\/a><\/div><p>En el art\u00edculo anterior de esta serie describ\u00ed c\u00f3mo se<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1845,13],"tags":[106,1959,1954,537],"series":[1957],"class_list":["post-11696","post","type-post","status-publish","format-standard","hentry","category-generado-con-ia","category-informatica","tag-android","tag-claude-code","tag-dji","tag-dji-mini-3-pro","series-fotogrametria-asistida-por-ia"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11696","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=11696"}],"version-history":[{"count":1,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11696\/revisions"}],"predecessor-version":[{"id":11701,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11696\/revisions\/11701"}],"wp:attachment":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11696"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=11696"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=11696"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fseries&post=11696"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}