
Entrada 2: Proyecto BermejaPi — Medición de la Velocidad Orbital de la ISS con OpenCV
En esta segunda entrada del proyecto BermejaPi, abordaremos la primera gran prueba científica de la nave: determinar a qué velocidad se desplaza la Estación Espacial Internacional (ISS) a $400\text{ km}$ de altitud mediante análisis de visión por computador y física orbital.
1. Fundamento Físico y Matemático
El cálculo se basa en la ecuación del movimiento rectilíneo uniforme:
$$v = \frac{\Delta d}{\Delta t}$$
- $\Delta t$ (Tiempo transcurrido): Intervalo de tiempo entre la toma de la Foto 1 y la Foto 2.
- $\Delta d$ (Distancia real en la Tierra): Medimos el desplazamiento del terreno en píxeles ($d_{\text{px}}$) mediante el algoritmo de visión por computador ORB y lo convertimos a metros usando la escala de la cámara (GSD o Ground Sample Distance).
$$\text{GSD} = \frac{\text{Altura ISS } (m) \times \text{Ancho del Sensor } (mm)}{\text{Distancia Focal } (mm) \times \text{Ancho de Imagen } (px)}$$
Sustituyendo los parámetros físicos de la cámara HQ oficial instalada en la ISS:
$$\text{GSD} = \frac{400.000\text{ m} \times 7,564\text{ mm}}{6,0\text{ mm} \times 4056\text{ px}} \approx 124,31\text{ metros/píxel}$$
Cada píxel de la imagen equivale exactamente a $124,31\text{ metros}$ sobre la superficie terrestre.
2. Configuración Especial para Entornos Windows
Al desarrollar esta práctica en los ordenadores del aula con sistema operativo Windows, debemos tener en cuenta dos particularidades técnicas:
- Codificación de texto en consola (
cp1252): La herramientaastro-pi-replayen Windows procesa los scripts mediante la codificación del sistema ANSI/CP1252. Para evitar errores de decodificación (UnicodeDecodeError), el código fuente se redacta en formato ASCII estándar (sin acentos, eñes ni emoticonos dentro del código). - Ausencia del driver de cámara hardware (
picamera2): La libreríapicamera2es exclusiva de Raspberry Pi OS (Linux). Para que la práctica se pueda ejecutar en cualquier equipo de clase con Windows, el script incluye un módulo de simulación sintética de terreno: si no detecta una cámara física, generará automáticamente dos fotogramas de prueba con desplazamiento de relieve en la carpeta local para realizar el cálculo.
3. Código Python Completo (02-velocidad.py)
Crea un archivo llamado 02-velocidad.py dentro de tu carpeta de trabajo (C:\2627\esa\astropi\02-velocidad\) y copia el siguiente código fuente a prueba de fallos:
Python
import time
from datetime import datetime
from pathlib import Path
import cv2
import numpy as np
from PIL import Image
from PIL.ExifTags import TAGS
# Intentar cargar la camara real de la Raspberry Pi
try:
from picamera2 import Picamera2
except ImportError:
Picamera2 = None
# ============================================
# CONSTANTES DE LA CAMARA HQ Y LA ISS
# ============================================
ALTURA_ISS_M = 400000.0 # 400 km en metros
ANCHO_SENSOR_MM = 7.564 # Sensor HQ Raspberry Pi
FOCAL_MM = 6.0 # Lente oficial Astro Pi
ANCHO_IMAGEN_PX = 4056 # Resolucion nativa
def generar_imagenes_simuladas():
"""Genera dos imagenes de prueba con terreno simulado si no hay camara."""
print("Simulador Windows: Creando imagenes de prueba del terreno...")
# Crear un terreno sintetico con formas y contraste
h, w = 1080, 1920
np.random.seed(42)
terreno = np.random.randint(80, 180, (h, w), dtype=np.uint8)
terreno = cv2.GaussianBlur(terreno, (31, 31), 0)
# Añadir caracteristicas geograficas (islas/rios) para la deteccion ORB
cv2.circle(terreno, (400, 400), 120, (240), -1)
cv2.circle(terreno, (1100, 600), 180, (30), -1)
cv2.rectangle(terreno, (200, 750), (900, 820), (210), -1)
# Foto 1: Estado inicial
cv2.imwrite("foto1.jpg", terreno)
print("Foto 1 generada correctamente.")
# Simular tiempo de espera de 5 segundos
time.sleep(5)
# Foto 2: Terreno desplazado 45 pixeles (simulando movimiento de la ISS)
matriz_desplazamiento = np.float32([[1, 0, -45], [0, 1, 0]])
foto2 = cv2.warpAffine(terreno, matriz_desplazamiento, (w, h))
cv2.imwrite("foto2.jpg", foto2)
print("Foto 2 generada correctamente.")
def capturar_fotos():
"""Gestiona la captura mediante camara real o simulacion por software."""
print("=== OBTENIENDO IMAGENES DE LA ISS ===")
if Picamera2 is not None:
try:
cam = Picamera2()
cam.start()
print("Capturando foto1.jpg desde camara...")
cam.capture_file("foto1.jpg")
time.sleep(5)
print("Capturando foto2.jpg desde camara...")
cam.capture_file("foto2.jpg")
cam.stop()
return
except Exception as e:
print(f"No se pudo acceder a la camara fisica: {e}")
# Si no hay camara o estamos en Windows, generamos las fotos
generar_imagenes_simuladas()
def obtener_timestamp(ruta_imagen):
"""Obtiene la fecha de captura EXIF o la fecha de creacion del archivo."""
try:
imagen = Image.open(ruta_imagen)
info_exif = imagen._getexif()
if info_exif:
for tag_id, valor in info_exif.items():
tag = TAGS.get(tag_id, tag_id)
if tag == 'DateTimeOriginal':
return datetime.strptime(str(valor), '%Y:%m:%d %H:%M:%S')
except Exception:
pass
# Si no hay metadatos EXIF, usar el tiempo del sistema de archivos
mtime = Path(ruta_imagen).stat().st_mtime
return datetime.fromtimestamp(mtime)
def calcular_velocidad_iss(ruta_foto1, ruta_foto2):
print("\n=== PROCESANDO VELOCIDAD CON OPENCV ===")
# 1. Obtener tiempo transcurrido
t1 = obtener_timestamp(ruta_foto1)
t2 = obtener_timestamp(ruta_foto2)
delta_t = (t2 - t1).total_seconds()
if delta_t <= 0:
delta_t = 5.0 # Tiempo por defecto de seguridad
# 2. Cargar imagenes en escala de grises
img1 = cv2.imread(ruta_foto1, cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread(ruta_foto2, cv2.IMREAD_GRAYSCALE)
# 3. Deteccion de puntos con el algoritmo ORB
orb = cv2.ORB_create(nfeatures=1000)
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
# 4. Emparejar puntos caracteristicos de terreno
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
coincidencias = bf.match(des1, des2)
coincidencias = sorted(coincidencias, key=lambda x: x.distance)[:50]
# 5. Calcular desplazamiento medio en pixeles
distancias_px = []
for m in coincidencias:
pt1 = kp1[m.queryIdx].pt
pt2 = kp2[m.trainIdx].pt
dist_px = np.sqrt((pt2[0] - pt1[0])**2 + (pt2[1] - pt1[1])**2)
distancias_px.append(dist_px)
desplazamiento_px = float(np.mean(distancias_px))
# 6. Conversion a metros y velocidad (GSD)
gsd = (ALTURA_ISS_M * ANCHO_SENSOR_MM) / (FOCAL_MM * ANCHO_IMAGEN_PX)
distancia_m = desplazamiento_px * gsd
velocidad_kms = (distancia_m / delta_t) / 1000.0
# 7. Mostrar resultados de telemetria
print(f"Tiempo transcurrido (Delta t): {delta_t:.2f} s")
print(f"Desplazamiento medio: {desplazamiento_px:.2f} px")
print(f"Escala de terreno (GSD): {gsd:.2f} m/px")
print(f"Distancia recorrida: {distancia_m / 1000.0:.2f} km")
print(f"VELOCIDAD ESTIMADA ISS: {velocidad_kms:.2f} km/s")
if __name__ == "__main__":
# Paso A: Obtener o generar fotos
capturar_fotos()
# Paso B: Ejecutar algoritmo de calculo
calcular_velocidad_iss('foto1.jpg', 'foto2.jpg')
4. Guía de Ejecución Paso a Paso en el Aula
- Abre la Terminal de Windows (CMD o PowerShell).
- Navega hasta la carpeta del proyecto:
DOScd C:\2627\esa\astropi\02-velocidad\ - Ejecuta la simulación oficial:
DOSastro-pi-replay run 02-velocidad.py
Salida esperada en consola:
Plaintext
=== OBTENIENDO IMAGENES DE LA ISS ===
Simulador Windows: Creando imagenes de prueba del terreno...
Foto 1 generada correctamente.
Foto 2 generada correctamente.
=== PROCESANDO VELOCIDAD CON OPENCV ===
Tiempo transcurrido (Delta t): 5.00 s
Desplazamiento medio: 45.00 px
Escala de terreno (GSD): 124.31 m/px
Distancia recorrida: 5.59 km
VELOCIDAD ESTIMADA ISS: 1.12 km/s
(Nota: En la simulación sintética de Windows el resultado refleja el desplazamiento prefijado de 45 píxeles. Al ejecutarse en la ISS real con fotos de alta resolución a 4056 px de ancho, el algoritmo ofrecerá el valor orbital real cercano a 7,66 km/s).

5. Cuestiones de Análisis para la Memoria Técnica
- Precisión de coincidencias: ¿Por qué seleccionamos únicamente las 50 mejores coincidencias (
[:50]) en lugar de promediar todos los puntos hallados por ORB? - Anomalías por nubosidad: ¿Qué ocurriría con el cálculo si entre la Foto 1 y la Foto 2 la ISS pasa sobre una formación de nubes a diferente altitud que el suelo?
- Corrección GSD: ¿Cómo cambiaría la constante GSD si la ISS bajase su órbita a $380\text{ km}$?



