Cómo recoger datos en tiempo real de sensores IoT: ejemplo con aviones

El concepto de Internet de las Cosas (IoT) se refiere a la red de objetos físicos que están equipados con sensores y que se conectan a Internet para recopilar e intercambiar datos.

Los sensores recopilan información del entorno, como temperatura o movimiento, para enviarla por Internet a un centro de almacenamiento donde puede ser procesada. Una de las aplicaciones más sencillas y que tiene un importante componente geográfico es el seguimiento de vehículos en movimiento, como las flotas de aviones, barcos o camiones. Aquí vamos a ver un ejemplo de seguimiento de aviones.

Sensores

Para poder obtener datos que puedan ser útiles en el trabajo de seguimiento de vehiculos, necesitamos que se cumplan tres requisitos:

  • 1º. Necesitamos obtener los datos de la posición a intervalo de tiempo regulares, es decir necesitamos que el dispositivo nos proporcione su posición periodicamente.
  • 2º. Además necesitamos disponer de un identificador único que nos permita hacer el seguimiento de ese dispositivo concreto.
  • 3º. Tenemos que poder almacenar esos datos para luego poder analizarlos.

El dispositivo de localización más conocido es el GPS. Se basa en el empleo de satélites (trilateración) para obtener la posición exacta en tiempo real de un vehículo. Estos dispositivos transmiten datos (ubicación, velocidad, histórico de rutas) a una plataforma web o app móvil mediante redes celulares, permitiendo monitorizar y proteger el vehículo.

Aunque el sistema de posicionamiento más conocido es GPS no es el único. Por ejemplo, en Europa está Galileo, que es el Sistema Global de Navegación por Satélite (GNSS) propio de la Unión Europea, operativo desde 2017 y diseñado para ofrecer posicionamiento, navegación y tiempo con mayor precisión que el GPS.

Sistemas de posicionamiento

Los sistemas de posicionamiento permiten determinar la ubicación de un dispositivo (vehículo, teléfono, dron, sensor IoT) y se pueden clasificar según la infraestructura que utilizan:

  • Basados en satélites. Aquí el dispositivo recibe señales de varios satélites y calcula su posición mediante triangulación. Este sistema es el que utilizan GPS, GLONASS, Galileo y BeiDou. Tienen cobertura global y alta precisión en exteriores, pero también presentan limitaciones: puede fallar en interiores, túneles o zonas urbanas densas.
  • Basados en redes terrestres. Utilizan triangulación celular usando torres de telefonía móvil, Wi-Fi y Bluetooth. El dispositivo estima su posición midiendo la intensidad o el tiempo de llegada de señales de antenas o puntos de acceso cercanos. Son útiles en interiores o donde el GPS no llega, pero tienen una precisión menor que los sistemas satelitales y dependen de la infraestructura disponible.

Obtener los datos de los “sensores” (aviones)

Para obtener la localización de los aviones usamos la API de ADS-B (api.adsb.lol) que recibe información de los transpondedores de los aviones. Cada avión emite datos como:

  • lat y lon → ubicación.
  • alt_baro → altitud.
  • gs → velocidad sobre el suelo.
  • track → rumbo.
  • hex → identificador único del avión.

Vamos a utilizar un script de Python para obtener los datos de posición de los aviones y dibujarlos sobre un mapa empleando Folium. Para más comodidad, lo haremos desde Google Colab:

!pip install folium requests
import requests
import folium

Comenzamos instalando las librerías requests y folium.

Ahora vamos a obtener los datos y los añadimos a un mapa en el que cada avión se posiciona mediante un marcador:

# Centro aproximado de la Península Ibérica
lat = 40
lon = -3
dist = 350  # km

url = f"https://api.adsb.lol/v2/lat/{lat}/lon/{lon}/dist/{dist}"

data = requests.get(url).json()

vuelos = data["ac"]

# Crear mapa centrado en la península
m = folium.Map(
    location=[40, -3],
    zoom_start=6,
    tiles="CartoDB dark_matter"
)

for a in vuelos[:500]:   # limitar para que sea rápido
    if "lat" in a and "lon" in a:
        folium.Marker(
            location=[a["lat"], a["lon"]],
            popup=a.get("flight","alt"),
            icon=folium.Icon(color="red", icon="plane", prefix="fa")
        ).add_to(m)

m

 

En la primera parte de este código estamos construyendo una URL que llama a la API ADS-B, indicando las coordenadas del centro del área sobre la que queremos obtener los datos y la distancia. De este modo, definimos los límites de nuestra zona de búsqueda de aviones.

Mediante requests.get(url) se hace una petición HTTP a la API y luego se convierte la respuesta JSON en un diccionario de Python.

Obtenemos una lista de los aviones donde en la estructura JSON «ac» significa aircraft (aviones).

A continuación, creamos el mapa con Folium. Para no sobrecargar demasiado la descarga de datos la limitamos a 500 aviones y a partir de esos datos insertamos un marcador para cada avión y un popup con el código del avión.

Seguimiento de un elemento móvil

Hasta ahora nos hemos limitado a obtener las posiciones de un vehículo. Se trataba de hacer una «instantánea» de la posición de los aviones, pero hemos indicado que nuestro objetivo era realizar el seguimiento, es decir obtener su ruta. Esto lo hacemos obteniendo las posiciones a intervalos regulares de tiempo. Por ejemplo de esta forma.

import requests
import time

lat = 40
lon = -3
dist = 350

track_points = []

# Número de muestras y intervalo
num_muestras = 20
intervalo_segundos = 60

plane_hex = None  # para almacenar el identificador del avión

for i in range(num_muestras):
    try:
        url = f"https://api.adsb.lol/v2/lat/{lat}/lon/{lon}/dist/{dist}"
        data = requests.get(url, timeout=10).json()
        aircraft = data["ac"]

        if not aircraft:
            print(f"Muestra {i+1}: No hay aviones")
            time.sleep(intervalo_segundos)
            continue

        # Si es la primera lectura, elegimos un avión cualquiera
        if plane_hex is None:
            plane = aircraft[0]
            plane_hex = plane["hex"]
        else:
            # buscamos el avión con el mismo hex
            plane = next((a for a in aircraft if a["hex"] == plane_hex), None)

        if plane and "lat" in plane and "lon" in plane:
            pos = {
                "lat": plane["lat"],
                "lon": plane["lon"],
                "flight": plane.get("flight","N/A"),
                "alt": plane.get("alt_baro","N/A"),
                "speed": plane.get("gs","N/A"),
                "track": plane.get("track","N/A")
            }
            track_points.append(pos)
            print(f"Muestra {i+1}: {pos}")
        else:
            print(f"Muestra {i+1}: avión no disponible o sin posición")

    except Exception as e:
        print(f"Error en muestra {i+1}: {e}")

    # espera antes de la siguiente lectura
    if i < num_muestras - 1:
        time.sleep(intervalo_segundos)

Aquí ya no se trata de buscar las posiciones de todos los aviones, sino de elegir uno y tomar su posición en intervalos de tiempo. Para que puedas replicar este código, hemos optado por elegir un avión cualquiera, como es el primero de la lista aircraft[0], aunque podíamos haber elegido un vuelo concreto.

Como se puede ver se ha importado time, que se utiliza para introducir pausas entre las consultas.

Una vez elegido el vuelo se hace su seguimiento mediante el identificador «hex«. Se comprueba que el avión sigue ahí:

if plane and «lat» in plane and «lon» in plane:

y se hace la lectura de su posición (pos). Se toman los datos de posición, código de vuelo, altitud, velocidad y rumbo.

Almacenamos la trayectoria del avión:

track_points.append(pos)

y se muestra el resultado:

print(f»Muestra {i+1}: {pos}»)

Al final, se indica que espere el intervalo indicado (intervalo_segundos), que en este caso es un minuto, para hacer la siguiente lectura. El resultado es una lista de datos con las posiciones del avión en el intervalo seleccionado.

Muestra 1: {'lat': 39.096059, 'lon': -10.417969, 'flight': 'EZY62AL ', 'alt': 36000, 'speed': 425.4, 'track': 21.8}
Muestra 2: {'lat': 39.207001, 'lon': -10.361003, 'flight': 'EZY62AL ', 'alt': 36000, 'speed': 424.5, 'track': 21.85}
Muestra 3: {'lat': 39.317276, 'lon': -10.304161, 'flight': 'EZY62AL ', 'alt': 36000, 'speed': 423.6, 'track': 21.9}
Muestra 4: {'lat': 39.427551, 'lon': -10.24696, 'flight': 'EZY62AL ', 'alt': 36000, 'speed': 422.6, 'track': 21.95}
Muestra 5: {'lat': 39.537186, 'lon': -10.189939, 'flight': 'EZY62AL ', 'alt': 36000, 'speed': 421.7, 'track': 22.0}
Muestra 6: {'lat': 39.646446, 'lon': -10.132812, 'flight': 'EZY62AL ', 'alt': 36000, 'speed': 420.8, 'track': 22.05}
Muestra 7: {'lat': 39.757427, 'lon': -10.074707, 'flight': 'EZY62AL ', 'alt': 35975, 'speed': 422.1, 'track': 22.13}
Muestra 8: {'lat': 39.867197, 'lon': -10.01709, 'flight': 'EZY62AL ', 'alt': 36000, 'speed': 423.9, 'track': 22.03}
...

Visualizar la ruta en un mapa

Podemos visualizar las posiciones con la ubicación del avión que hemos obtenido nuevamente en folium.

import folium

m = folium.Map(location=[lat, lon], zoom_start=6, tiles="CartoDB dark_matter")

for p in track_points:
    folium.CircleMarker(
        location=[p["lat"], p["lon"]],
        radius=4,
        popup=f"{p['flight']} | alt {p['alt']} ft | {p['speed']} kt"
    ).add_to(m)

# Línea de trayectoria
folium.PolyLine([(p["lat"], p["lon"]) for p in track_points], color="yellow").add_to(m)

m

En el ejemplo anterior se diseña un mapa estático que indica las posiciones obtenidas del vuelo. Pero también podemos hacer una animación insertando un controlador de tiempo como se observa en este video donde se dibuja la trayectoria de dos aviones:

Deja un comentario