Esta entrada es la parte 5 de 6 de la serie Fotogrametría asistida por IA
Imagen destacada

Hasta ahora los artículos de esta serie se han centrado en la aplicación Android: cómo planifica la misión, cómo hace volar al dron, cómo controla el heading durante el vuelo. Pero una misión de fotogrametría no termina cuando el dron aterriza. Las imágenes capturadas son la materia prima; lo que convierte esas fotos en un ortomosaico, una nube de puntos o un modelo 3D es el procesado fotogramétrico, y eso requiere su propia infraestructura. Este artículo cuenta cómo se diseñó y desplegó esa infraestructura, y en él Claude Code tiene un papel protagonista diferente al habitual: no como desarrollador de código, sino como ingeniero de sistemas.

El software: OpenDroneMap

La elección del motor de procesado fotogramétrico fue OpenDroneMap (ODM), y la razón principal es la misma que guía muchas otras decisiones de este proyecto: es completamente open source, sin licencias ni costes recurrentes, y produce resultados comparables a soluciones comerciales como Pix4D o Agisoft Metashape para los casos de uso habituales con drones de consumo.

ODM no es un programa con interfaz gráfica, sino un motor de procesado por línea de comandos. Para hacerlo manejable existen dos capas adicionales que forman el stack completo:

  • NodeODM: expone el motor de ODM como una API REST, permitiendo subir imágenes, lanzar tareas y consultar su estado de forma programática.
  • WebODM: interfaz web completa y API REST de alto nivel sobre NodeODM. Gestiona proyectos, usuarios, tareas y resultados. Es el punto de entrada principal, tanto para un humano desde el navegador como para la app Android en la Fase 4.
  • ClusterODM: capa de orquestación que distribuye tareas entre múltiples nodos NodeODM. Para un servidor único actúa principalmente como gestor de cola.

Para la visualización de nubes de puntos se eligió Potree, la referencia open source para renderizar ficheros .las/.laz con millones de puntos directamente en el navegador, sin plugins ni software adicional. Potree permite medir distancias, hacer secciones transversales y navegar por la nube de puntos de forma fluida incluso en conjuntos de datos grandes.

La arquitectura de máximos: cuatro VMs en Proxmox

El servidor disponible para el procesado es un HP ProLiant DL360 Gen8, virtualizado con Proxmox VE. La primera propuesta de arquitectura que surgió en la sesión de diseño con Claude era ambiciosa: cuatro máquinas virtuales separadas, cada una con un rol específico, comunicadas a través de una red interna y expuestas mediante un nginx actuando como reverse proxy centralizado.

┌─────────────────────────────────────────────────────┐
│                   Proxmox VE                         │
│                                                      │
│  ┌──────────────┐    ┌──────────────────────────┐   │
│  │  vm-nginx    │    │       vm-webodm           │   │
│  │ Reverse proxy│───▶│  WebODM + NodeODM         │   │
│  │ 192.168.x.10 │    │  :8000  192.168.x.11      │   │
│  └──────┬───────┘    └──────────────────────────┘   │
│         │                                            │
│         │            ┌──────────────────────────┐   │
│         ├───────────▶│     vm-clusterodm         │   │
│         │            │  ClusterODM orquestador   │   │
│         │            │  :3000/:8080 192.168.x.12 │   │
│         │            └──────────────────────────┘   │
│         │                                            │
│         │            ┌──────────────────────────┐   │
│         └───────────▶│       vm-potree           │   │
│                      │  Potree + PDAL visor 3D   │   │
│                      │  :8080  192.168.x.13      │   │
│                      └──────────────────────────┘   │
│                                                      │
│         Almacenamiento NFS compartido entre VMs      │
└─────────────────────────────────────────────────────┘

La arquitectura era coherente desde el punto de vista de la separación de responsabilidades, pero tenía un problema práctico evidente: cuatro VMs implican cuatro sistemas operativos que mantener, cuatro puntos de gestión, overhead de red entre VMs para operaciones que podrían ser locales, y un consumo de recursos significativamente mayor. Para un proyecto personal en un servidor que también hace otras cosas, era sobredimensionado.

Diagrama de arquitectura inicial. Cuatro máquinas virtuales desplegadas en Proxmox. Elegante, pero demasiado complejo
Diagrama de arquitectura inicial. Cuatro máquinas virtuales desplegadas en Proxmox. Elegante, pero demasiado complejo

La arquitectura real: todo en una VM con Docker

La simplificación fue inmediata: consolidar todo en una única VM Debian 12, con todos los servicios corriendo como contenedores Docker orquestados por un único docker-compose.yml. Las ventajas son claras frente a la arquitectura multi-VM: menos overhead de red (los contenedores se comunican por la red interna de Docker), un único punto de gestión, y Docker Compose como herramienta de orquestación ya conocida.

┌──────────────────────────────────────────────────────────┐
│              VM Debian 12 — Proxmox VE                    │
│                                                           │
│  Puerto 80 expuesto al exterior                           │
│         │                                                 │
│         ▼                                                 │
│  ┌─────────────┐   Red interna Docker: odm-net            │
│  │ odm-nginx   │   (172.20.0.0/16)                        │
│  │ Reverse     │                                          │
│  │ proxy       ├──────────────────────────────┐           │
│  └──────┬──────┘                              │           │
│         │                                     │           │
│    /odm/│              /cluster/              │/3d/       │
│         ▼                    ▼                ▼           │
│  ┌─────────────┐  ┌─────────────────┐  ┌──────────────┐  │
│  │ odm-webodm  │  │ odm-clusterodm  │  │  odm-potree  │  │
│  │ UI + REST   │  │ Orquestador     │  │  Visor 3D    │  │
│  │ API :8000   │  │ :3001/:8080     │  │  :8081       │  │
│  └──────┬──────┘  └────────┬────────┘  └──────────────┘  │
│         │                  │                              │
│         ▼                  ▼                              │
│  ┌─────────────┐  ┌─────────────────┐                    │
│  │ odm-postgres│  │  odm-nodeodm    │                    │
│  │ BD WebODM   │  │  Motor ODM      │                    │
│  │ :5432       │  │  :3000          │                    │
│  └─────────────┘  └─────────────────┘                    │
│                                                           │
│  /odm-data  (disco virtual dedicado)                      │
│  ├── projects/        ← imágenes e outputs de ODM         │
│  ├── potree-output/   ← nubes de puntos para Potree       │
│  └── postgres/        ← datos de WebODM                   │
└──────────────────────────────────────────────────────────┘

Los recursos asignados a la VM para un procesado cómodo son 10–12 vCores (ODM es muy intensivo en CPU), 28–32 GB de RAM y dos discos virtuales: uno para el sistema y Docker, otro dedicado a /odm-data donde viven todos los proyectos y resultados.

Diagrama de arquitectura final. Máquina virtual única con docker
Diagrama de arquitectura final. Máquina virtual única con docker

El flujo completo de una misión, desde el dron hasta el modelo 3D, queda así:

DJI Mini 3 Pro
     │ captura imágenes georreferenciadas
     ▼
App Android
     │ upload vía REST API (Fase 4 — pendiente)
     ▼
WebODM API ──▶ ClusterODM ──▶ NodeODM (motor ODM)
                                    │
                                    ▼
                         .laz / .ply / .tiff
                                    │
                         PotreeConverter (host)
                                    │
                                    ▼
                         Potree Viewer (navegador)
                                    │
                                    ▼
                         Modelo 3D navegable en web
Diagrama de procesado, desde el dron hasta la imagen procesada
Diagrama de procesado, desde el dron hasta la imagen procesada

El tiempo estimado de procesado en el DL360 Gen8 es de 30 a 120 minutos, dependiendo del área cubierta y la resolución de las imágenes. No es instantáneo, pero es perfectamente asumible para un flujo de trabajo de campo donde el procesado se lanza al llegar a casa.

Claude Code como ingeniero de sistemas

Aquí es donde este artículo se separa de los anteriores de la serie. En las fases anteriores, Claude Code actuaba como arquitecto y co-desarrollador de la aplicación Android: diseñaba módulos, generaba código Kotlin, depuraba errores de compilación. Para el despliegue de la infraestructura, el rol es completamente diferente: ingeniero de sistemas autónomo.

La aproximación fue instalar Claude Code directamente en la VM Debian donde se iba a desplegar el stack, y darle un único prompt detallado con todo lo que tenía que hacer, dejándolo ejecutar de forma autónoma:

# Instalación de Claude Code en la VM
curl -fsSL https://claude.ai/install.sh | bash

# Ejecución en modo autónomo
claude --dangerously-skip-permissions --model claude-sonnet-4-6

El flag --dangerously-skip-permissions merece una mención. En el uso habitual de Claude Code, cada operación que modifica el sistema de ficheros o ejecuta comandos requiere confirmación explícita del usuario. Con este flag se elimina esa fricción y Claude Code opera con autonomía completa. En un entorno productivo o compartido sería una decisión arriesgada; en un servidor personal controlado, con una tarea bien definida, es exactamente lo que se necesita para un despliegue desatendido.

El prompt que se le entregó estructuraba el trabajo en ocho fases secuenciales, con criterios de éxito explícitos para cada una y la instrucción de no pasar a la siguiente si la anterior había fallado:

  1. Verificación del sistema: comprobar disco montado, conectividad a internet, Docker instalado. Si Docker no estaba presente, instalarlo desde el repositorio oficial (no el paquete docker.io de Debian, que es más antiguo).
  2. Preparación de directorios: crear la estructura de /odm-data y /opt/odm-stack.
  3. Crear ficheros de configuración: escribir el docker-compose.yml completo con los seis servicios y el nginx.conf con las tres rutas y los timeouts largos necesarios para el polling de tareas ODM.
  4. Instalar PotreeConverter en el host: el conversor de nubes de puntos corre directamente en la VM, no en Docker, porque necesita acceso directo a los ficheros de salida de ODM.
  5. Arrancar el stack: docker compose up -d, esperar 60 segundos y verificar que todos los contenedores estaban en estado running.
  6. Verificación funcional: una batería de curl contra cada ruta del reverse proxy y comprobaciones internas entre contenedores.
  7. Configuración post-despliegue: mostrar las URLs de acceso, recordar los pasos manuales que quedan (crear la cuenta de administrador en WebODM, añadir los nodos de procesado, obtener el token de API).
  8. Arranque automático: crear un servicio systemd que levante el stack Docker al arrancar la VM.

Lo que hace interesante esta aproximación es la naturaleza del trabajo. No es generar código de aplicación, sino que es hacer exactamente lo que haría un administrador de sistemas experimentado: verificar el entorno, instalar dependencias, escribir ficheros de configuración, arrancar servicios, validar que funcionan y dejar el sistema listo para producción. Y hacerlo en el orden correcto, con manejo de errores en cada paso.

Un detalle que ilustra bien la capacidad de razonamiento en este contexto: el prompt especificaba instalar Docker desde el repositorio oficial, no desde el paquete docker.io del repositorio de Debian. La diferencia es que el paquete oficial de Docker suele estar varias versiones por delante del que empaqueta Debian, y algunas funcionalidades de Docker Compose v2 requieren versiones recientes. Claude Code entiende esta distinción y ejecuta los pasos correctos sin necesidad de explicarlos en detalle.

Algunos detalles de configuración que importan

El docker-compose.yml tiene algunas decisiones de diseño que vale la pena mencionar. La base de datos PostgreSQL incluye un healthcheck que verifica que acepta conexiones antes de que WebODM intente conectarse, evitando la carrera de arranque habitual entre aplicación y base de datos. WebODM tiene una condición depends_on con condition: service_healthy sobre Postgres, lo que garantiza el orden correcto sin necesidad de scripts de espera artificiales.

El nginx merece especial atención en los timeouts. WebODM hace polling de larga duración para seguir el estado de las tareas de procesado y, sin timeouts configurados explícitamente, nginx cortaría esas conexiones antes de que ODM terminara. Se configuraron 3600 segundos (una hora) para proxy_read_timeout, proxy_send_timeout y proxy_connect_timeout en la ruta /odm/, y un client_max_body_size de 10 GB para permitir el upload de conjuntos de imágenes grandes.

PotreeConverter se instala directamente en el host de la VM, fuera de Docker, porque necesita leer los ficheros .laz que produce ODM y escribir los resultados en el directorio que sirve el contenedor de Potree. Un script de conversión de una línea automatiza el proceso una vez que un proyecto ODM ha terminado:

/opt/odm-stack/convert-to-potree.sh nombre-del-proyecto

El resultado queda accesible en http://[IP-servidor]/3d/nombre-del-proyecto/index.html — una nube de puntos navegable directamente en el navegador, sin software adicional.

El resultado: tres URLs y un flujo de trabajo completo

Con el stack desplegado, los puntos de acceso al entorno de procesado son:

ServicioURLPara qué sirve
WebODMhttp://[IP]/Interfaz web completa y API REST para la app Android
ClusterODMhttp://[IP]/cluster/Panel de administración del orquestador de tareas
Potreehttp://[IP]/3d/<proyecto>/index.htmlVisor 3D de la nube de puntos en el navegador

Una vez se accede a la aplicación, ya es posible hacer procesado de imágenes de manera manual, creando las tareas, asociando las imágenes, y escogiendo el modelo de procesado. De salida genera unas ortofotografías y modelos en 3D, tanto de nube de puntos como de texturas, de calidad nada desdeñable.

Primera misión generada en ODM
Primera misión generada en ODM

El único paso que queda por automatizar es la integración directa desde la app Android, prevista en la Fase 4: que al terminar una misión de vuelo, la app suba automáticamente las imágenes a WebODM, lance el procesado y notifique cuando el modelo esté listo. La API REST de WebODM ya está disponible y documentada; es cuestión de implementar el cliente en la app.

Lo que me parece más destacable de este proceso es la demostración de que Claude Code no es solo una herramienta de desarrollo de software. En este caso no se escribió prácticamente ningún código de aplicación: se diseñó una arquitectura, se tomaron decisiones de infraestructura, se redactó una guía de despliegue completa y se automatizó la ejecución de ese despliegue en un servidor real. Eso es integración de sistemas, y funciona igual de bien que en el desarrollo de la app Android.

Navegación de la serie<< Aplicación Android para fotogrametría con IA. Fase 3: Corrección de heading, visualización en vuelo y ajustes de UXAplicación Android para fotogrametría con IA. Fase 4: Integración cloud, exportación KMZ y cierre del ciclo completo >>

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.