Otro de los proyectos en los que he estado trabajando con IA generativa como elemento de apoyo ha sido en aspectos avanzados de control de la domótica. Llevaba mucho tiempo queriendo llevar a cabo un proyecto para poner en valor un Kinect v1 que había adquirido como hardware para realizar modelado 3D de figuras pero que, viendo la capacidad que tenía para obtener imágenes a color, captura de imágenes con detección de profundidad, captura infrarrojos y generación de nube de puntos, pensaba que se le podía sacar mucho partido en el ámbito de la domótica.

En su momento había estado investigando sobre las posibilidades de uso de Libfreenect para sacar adelante este proyecto, pero no tardaba mucho en encontrarme con problemas de compatibilidad de librerías con los sistemas operativos que tenía disponible en mi entorno, por lo que no pude avanzar mucho en este proyecto. Pero, de nuevo, el hecho de disponer del nuevo servidor de virtualización me permitía poder tener múltiples sistemas en paralelo con los que jugar. Había llegado la hora de avanzar con esta idea.
Para poder evaluar la viabilidad del proyecto, empecé por hacer una búsqueda prospectiva apoyada por IA. Utilicé como motor de IA Perplexity. En seguida me dio algunas ideas interesantes, algunas de ellas con las que ya contaba (en concreto, con el uso de Libfreenect), pero ya desde el primer momento me dio un punto de partida muy interesante, y que yo no había contemplado: el uso de ROS, plataforma de software para robótica, como elemento central de la arquitectura. En concreto, proponía:
- Uso OpenNI como elemento de para la gestión de la visión artificial, con acceso a los flujos de datos en bruto del Kinect. OpenNI ofrece una capa más alta de abstracción y procesamiento, con middleware para reconocimiento de movimiento, detección de esqueleto, reconocimiento de gestos, y soporta múltiples dispositivos además de Kinect.
- Libfreenect: Drivers para proporcionar acceso básico a los sensores Kinect.
- ROS como plataforma de gestión de robótica, que proporciona las herramientas y librerías para el manejo de datos del Kinect proporcionados por OpenNI, y que permite su explotación mediante la integración con otros algoritmos y sistemas robóticos. Una parte muy interesante es que se apoya en el contecto de topics para el acceso a estos datos y su integración, un concepto muy similar al que se maneja con MQTT.
- Pyfreenect, como librería de acceso en python, para la explotación de los datos.

Como este punto de partida parecía muy prometedor, en seguida me puse a ver cómo llevarlo a cabo. Y pronto volví a encontrarme con un problema habitual: la obsolescencia del Kinect. La mayoría de los desarrollos para este hardware llevaban al menos 5 años abandonados. Y esto era un problema importante por varios motivos:
- Existen varias versiones de ROS; actualmente la versión predominante es ROS 2, pero no existe un driver oficial, mantenido y bien documentado para Kinect v1 en ROS 2 como sí lo hubo en ROS 1 (openni_camera, freenect_launch, etc.).
- Repositorios como
kinect_ros2existen pero son no oficiales, con problemas de dependencia (rosdep no encuentra libfreenect) y mantenimiento limitado. - OpenNI/OpenNI2 ya no se mantiene activamente, y la integración “oficial” con Kinect se hacía a través de drivers adicionales (OpenNI-FreenectDriver o SDK de Microsoft), no de forma directa.
- En ROS 1, los paquetes openni_* funcionaban razonablemente con Ubuntu/ROS antiguos (p. ej. Kinetic, 16.04), pero esa pila no se ha portado ni actualizado a ROS 2 de forma completa.
- libfreenect como biblioteca es “agnóstica a ROS”, pero su integración exige compilarla a mano, instalarla en rutas de sistema y luego escribir o adaptar un nodo ROS 2 que publique las imágenes y percepción de la profundidad.
- El seguimiento esquelético con Kinect v1 sobre OpenNI se apoya en NiTE, un middleware binario de PrimeSense que proporciona skeleton tracking, gestos y manos, no en OpenNI “puro”.
- NiTE está discontinuado, es cerrado y no se distribuye ni mantiene oficialmente; muchas guías se limitan a ROS 1 (openni_tracker) en Ubuntu antiguos y ya no tienen soporte activo.
- El paquete clásico
openni_trackerque usa NiTE para publicar esqueletos vía tf existe solo para ROS 1; no hay equivalente mantenido para ROS 2, por lo que no dispones de un nodo listo para usar que publique joints esqueléticos. - La mayoría de hilos recientes sobre Kinect + ROS 2 se centran en sacar color y profundidad (RGB-D) con libfreenect/OpenNI, pero no tratan ni ofrecen soluciones maduras para skeleton tracking en ROS 2.
Es decir, me estaba encontrando con un problema de obsolescencia, tanto en la parte hardware como en la parte software, sobre todo si quería hacer uso de las vías que más trabajadas han estado de manera habitual. En condiciones normales, a estas alturas habría abandonado el proyecto, pero valía la pena intentar sacarle partido a este hardware tan interesante.
Divide et impera. Cómo abordar el problema
Es decir, llegados a este punto, me tenía que enfrentar con tres problemas: obsolescencia del hardware, obsolescencia del software de adquisición de datos y de control y obsolescencia del software de explotación:
- Obsolescencia del hardware: Directamente aquí no había nada que hacer. Quería utilizar este hardware. Pero sí nos obligaba a tomar una serie de decisiones. Si quería hacer uso del mismo, tocaba prescindir de ROS 2, y hacer uso de ROS 1.
- Obsolescencia del software de control: El camino venía dado por lo anterior. Si iba a usar ROS 1, necesitaba encontrar un sistema operativo sobre el que lo pudiera desplegar y, sobre todo, que fuera compatible con Kinect v1. En este caso, vino a salvarme un repositorio de GitHub en el que daban instrucciones precisas de cómo configurar Kinect v1 para su correcto funcionamiento con ROS Noetic: Setup Kinect in ROS Noetic. Ya tenía mi punto de partida desde el punto de vista del software de control. Con ROS Noetic desplegado sobre una Ubuntu 20.04, que aún cuenta con soporte a largo plazo, tenía un sistema lo suficientemente sólido como para plantearme emprender el sistema. Eso sí, teniendo claro que es una solución viable sólo a medio plazo.
- Obsolescencia del software de explotación: Aquí sí que iba a tener que enfrentar un problema inmediato. Daba igual las vueltas que le diera, que OpenNI y NiTE no funcionaban: no existe una versión 2 compatible con Kinect v1, y la 1.5 daba problemas de todo tipo, algo de esperar, debido a que lleva un tiempo abandonado. Era preciso abordar el problema de una manera diferente. Y aquí, la IA generativa tuvo algo nuevo que decir.
Puesta en marcha de Kinect v1 en ROS Noetic
La guía anterior explica, de manera detallada, cómo poner en marcha un Kinect v1 en ROS Noetic sobre Ubuntu 20.04, desde las dependencias hasta la visualización en RViz:
- El tutorial parte de un sistema con Ubuntu 20.04 y ROS Noetic ya instalado, verificando la versión de Ubuntu con
lsb_release -ay la distro de ROS en/opt/ros. - Primero se actualiza el sistema con
sudo apt updateysudo apt upgradepara tener todos los paquetes al día. - Después se instalan los metapaquetes
ros-noetic-rgbd-launch,ros-noetic-openni-launchy la librería de desarrollolibfreenect-dev, que proporcionan la base para trabajar con sensores RGB-D y el Kinect. - Como el paquete
freenect_stackno está disponible precompilado para Noetic, la guía indica crear un workspacecatkin_ws, clonar el repositorio oficialros-drivers/freenect_stackdentro desrcy compilarlo concatkin_make. - Una vez construido, se ejecuta
source ~/catkin_ws/devel/setup.bashpara que ROS detecte los nuevos paquetes en el entorno de trabajo. - Con el workspace cargado, se vuelve a hacer
sourcedel setup, se conecta el Kinect y se comprueba que el sistema lo detecta conlsusb. - A continuación se lanza
roslaunch freenect_launch freenect.launch depth_registration:=truepara obtener una nube de puntos RGB-D y, en otra terminal, se abrerviz, configurando elFixed Frameencamera_linky añadiendo unPointCloud2en el tópico/camera/depth_registered/pointspara ver los datos en 3D.
Con ello, ya teníamos solucionados los problemas de obsolescencia del hardware y del software de control. Quedaba el problema de cómo explotar los datos.
MediaPipe como solución de reconocimiento de pose
De nuevo apoyándome en Perplexity, busqué soluciones alternativas, viables en 2025, para reemplazar a NiTE como solución de reconocimiento esquelético, y entre las distintas posibilidades que me ofrecía, una de ellas llamó pronto mi atención: MediaPipe de Google:
- MediaPipe es una solución avanzada y moderna de reconocimiento de pose y skeleton tracking basada en machine learning.
- Funciona con cámaras RGB normales y puede adaptarse con ajustes para Kinect v1.
- Existe un wrapper para ROS y puedes integrar fácilmente sus datos para control domótico.
- Es multiplataforma, con soporte para Python, C++, y buena documentación.

Es decir, una solución moderna, activa, con soporte, y que integra capacidades de ML para el reconocimiento de poses. Y que es una solución que, si en el futuro decido prescindir del Kinect v1, podré seguir usando con hardware más moderno. Lo tenía todo.
Poniéndolo todo junto
Es decir, teníamos un proyecto con los siguientes puntales:
- Kinect (Sensor): Es el hardware, una cámara de detección de profundidad (RGB-D) desarrollada por Microsoft. El Kinect v1, en particular, es compatible con el estándar OpenNI y es ampliamente utilizado con ROS para obtener datos de color, profundidad y nubes de puntos.
- OpenNI (Framework/Controlador): Es un framework de interacción natural (NITE) y un conjunto de controladores (drivers) de código abierto que permiten la comunicación entre el software y el hardware compatible, como el Kinect y las cámaras PrimeSense. Proporciona acceso a flujos de datos brutos (imágenes RGB e IR, mapas de profundidad) y, en versiones anteriores, a funciones de seguimiento esquelético. En ROS, los paquetes openni_camera y openni_launch utilizan estos controladores.
- ROS (Robot Operating System): Es una meta-plataforma de software para robótica. Proporciona las herramientas y librerías para manejar los datos del Kinect (publicados como tópicos de ROS mediante OpenNI) y permite la integración con otros algoritmos y sistemas robóticos (como la navegación o manipulación). Permite la comunicación entre diferentes módulos de software, por ejemplo, pasar la imagen de la cámara a un nodo de procesamiento de MediaPipe.
- MediaPipe (Librería de visión por computadora): Es una librería de Google (ahora parte de TensorFlow) que proporciona soluciones de visión por computadora listas para usar, como seguimiento de manos, rostros y poses (esqueletos). MediaPipe opera sobre las imágenes (normalmente RGB) obtenidas del sensor (Kinect) y procesadas a través de ROS. Existen paquetes en ROS 2 (y probablemente adaptaciones en ROS 1) que integran directamente MediaPipe para publicar los datos de seguimiento como tópicos de ROS.
- MQTT: Plataforma de mensajería para la integración con el sistema de domótica.
Una propuesta muy, muy potente. Y era hora de avanzar con ella.
- Sensor Kinect v1 conectado.
- Driver libre que publique RGB:
freenect_launchdisponible en ROS para Kinect. - Instalar MediaPipe y OpenCV para Python:
pip3 install mediapipe opencv-python
sudo apt install ros-noetic-cv-bridge ros-noetic-image-transport
- Compilar el workspace catkin y fuente el entorno:
chmod +x mediapipe_pose_node.py
cd ~/catkin_ws
catkin_make
source devel/setup.bash
Tras ello, ya era posible desarrollar un aplicativo que explotara ROS, que procesara los movimientos con MediaPipe, y que volcara los hallazgos a un topic MQTT.
Reconocimiento de gestos. MediaPipe y Python
Este es el punto donde entraba juego MediaPipe Pose. Este software permite detectar 33 puntos de referencia en el cuerpo, lo que hace posible reconocer diversas posturas y gestos complejos, como por ejemplo:
- Posturas básicas: De pie, sentado, agachado, acostado.
- Gestos de brazos y manos:
- Brazo levantado (derecho o izquierdo).
- Mano abierta o cerrada.
- Dedo índice apuntando hacia arriba.
- Pulgar arriba o abajo.
- Señal de «victoria» (dos dedos en V).
- Gestos personalizados definidos por posición relativa de puntos clave.
- Movimientos de piernas:
- Levantar una pierna.
- Caminar, correr, salto.
- Inclinación o giro del torso y cabeza:
- Inclinarse hacia adelante o atrás.
- Girar la cabeza hacia un lado.
- Análisis de postura para ergonomía o rehabilitación:
- Detectar encorvamiento o postura erguida.
- Seguimiento y corrección de ejercicios.
- Gestos comunes predefinidos en MediaPipe Hands:
- Puño cerrado, palma abierta, señal de amor, etc.

Estos gestos y posturas se pueden analizar con reglas geométricas, diferencias de ángulos entre articulaciones o mediante modelos de aprendizaje automático que clasifiquen patrones en los 33 puntos 3D. Más que de sobra para mi proyecto. En mi caso, me centré en reconocer gestos básicos como la presencia o ausencia de una persona, levantar los brazos, o unir las manos por encima de la cabeza. Y la idea sería volcar a un topic MQTT, para utilizarlos posteriormente para controlar la domótica.
Para ello, pedí a Perplexity que desarrollara un código básico con estos detalles. Y el resultado, tras refinarlo un poco, empezó a funcionar extraordinariamente bien. Adjunto el código en python desarrollado:
#!/usr/bin/env python3
import rospy
from sensor_msgs.msg import Image
from geometry_msgs.msg import PointStamped
from cv_bridge import CvBridge
import mediapipe as mp
import cv2
import paho.mqtt.client as mqtt
import time
import threading
class MediaPipePoseNode:
def __init__(self):
rospy.init_node('mediapipe_pose_node', anonymous=True)
self.bridge = CvBridge()
self.pose = mp.solutions.pose.Pose(min_detection_confidence=0.5)
self.pub_right_hand = rospy.Publisher('/mediapipe/right_hand', PointStamped, queue_size=10)
self.pub_left_hand = rospy.Publisher('/mediapipe/left_hand', PointStamped, queue_size=10)
rospy.Subscriber('/camera/rgb/image_raw', Image, self.image_callback)
self.mqtt_client = mqtt.Client()
self.mqtt_client.connect("localhost")
# Inicia loop no bloqueante MQTT en segundo plano
self.mqtt_client.loop_start()
self.mqtt_topic = "domotica/brazo_presencia"
self.threshold_y = 0.5 # Umbral para brazo levantado
self.hand_join_threshold = 0.1 # Distancia XY para manos juntas
self.last_publish_time = 0
self.publish_interval = 1.0 # mínimo 1 segundo entre mensajes
self.last_processed_time = 0
self.process_interval = 0.1 # Procesar máximo 10 FPS
self.person_detected = False
rospy.loginfo("MediaPipe Pose Node with MQTT started")
rospy.spin()
def distance_xy(self, lm1, lm2):
return ((lm1.x - lm2.x)**2 + (lm1.y - lm2.y)**2)**0.5
def image_callback(self, msg):
try:
current_time = time.time()
# Limita tasa de procesamiento
if current_time - self.last_processed_time < self.process_interval:
return
self.last_processed_time = current_time
cv_image = self.bridge.imgmsg_to_cv2(msg, desired_encoding='bgr8')
results = self.pose.process(cv_image)
if results.pose_landmarks:
if not self.person_detected:
rospy.loginfo("Persona detectada")
self.mqtt_client.publish(self.mqtt_topic, "PERSONA_DETECTADA")
self.person_detected = True
self.last_publish_time = current_time
right_hand = results.pose_landmarks.landmark[mp.solutions.pose.PoseLandmark.RIGHT_WRIST]
left_hand = results.pose_landmarks.landmark[mp.solutions.pose.PoseLandmark.LEFT_WRIST]
nose = results.pose_landmarks.landmark[mp.solutions.pose.PoseLandmark.NOSE]
point_right = PointStamped(header=msg.header)
point_right.point.x = right_hand.x
point_right.point.y = right_hand.y
point_right.point.z = right_hand.z
self.pub_right_hand.publish(point_right)
point_left = PointStamped(header=msg.header)
point_left.point.x = left_hand.x
point_left.point.y = left_hand.y
point_left.point.z = left_hand.z
self.pub_left_hand.publish(point_left)
right_up = right_hand.y < self.threshold_y
left_up = left_hand.y < self.threshold_y
hands_together_above_head = (self.distance_xy(right_hand, left_hand) < self.hand_join_threshold) and \
(right_hand.y < nose.y) and (left_hand.y < nose.y)
if current_time - self.last_publish_time > self.publish_interval:
if right_up and left_up:
rospy.loginfo("Ambos brazos levantados")
self.mqtt_client.publish(self.mqtt_topic, "AMBOS_BRAZOS_LEVANTADOS")
self.last_publish_time = current_time
elif hands_together_above_head:
rospy.loginfo("Manos unidas encima cabeza")
self.mqtt_client.publish(self.mqtt_topic, "MANOS_UNIDAS_ENCIMA_CABEZA")
self.last_publish_time = current_time
elif right_up:
rospy.loginfo("Brazo derecho levantado")
self.mqtt_client.publish(self.mqtt_topic, "BRAZO_DERECHO_LEVANTADO")
self.last_publish_time = current_time
elif left_up:
rospy.loginfo("Brazo izquierdo levantado")
self.mqtt_client.publish(self.mqtt_topic, "BRAZO_IZQUIERDO_LEVANTADO")
self.last_publish_time = current_time
else:
if self.person_detected:
rospy.loginfo("Fin de detección persona")
self.mqtt_client.publish(self.mqtt_topic, "FIN_DETECCION_PERSONA")
self.person_detected = False
self.last_publish_time = current_time
except Exception as e:
rospy.logwarn(f"Error en procesamiento: {e}")
if __name__ == '__main__':
try:
MediaPipePoseNode()
except rospy.ROSInterruptException:
pass
Automatización de la plataforma
El siguiente paso era automatizar el inicio del software. Para ello, opté por utilizar systemd, pero desplegando dos servicios separados:
- Un servicio para el componente ROS que lance
roslaunch freenect_launch freenect.launch depth_registration:=true. - Otro servicio para el nodo Python
mediapipe_pose_node.pyque asume que ROS ya está corriendo.
…el primero con el siguiente servicio:
[Unit]
Description=ROS Freenect Launch
After=network.target
[Service]
Type=simple
User=tu_usuario
Environment="HOME=/home/tu_usuario"
ExecStart=/bin/bash -c 'source /home/tu_usuario/catkin_ws/devel/setup.bash && roslaunch freenect_launch freenect.launch depth_registration:=true'
Restart=on-failure
RestartSec=5s
WorkingDirectory=/home/tu_usuario
[Install]
WantedBy=multi-user.target
…y el segundo con este otro:
[Unit]
Description=MediaPipe Pose ROS Node
After=freenect.launch.service
[Service]
Type=simple
User=tu_usuario
Environment="HOME=/home/tu_usuario"
ExecStart=/bin/bash -c 'source /home/tu_usuario/catkin_ws/devel/setup.bash && rosrun mediapipe_pose mediapipe_pose_node.py'
Restart=on-failure
RestartSec=5s
WorkingDirectory=/home/tu_usuario/catkin_ws
[Install]
WantedBy=multi-user.target

Siguientes pasos
El proyecto, hasta ahora, está muy bien, pero tiene una serie de puntos de mejora a tratar:
- Todo el procesado se realiza en una máquina virtual en mi servidor de virtualización, por lo que el Kinect tiene que estar conectado a este último. Como éste es un ProLiant DL360, no es que pueda tenerlo precisamente en el salón de casa, que es lo ideal para poder hacer detección de movimientos y control domótico. La idea pasa por conectar el Kinect a un sistema más sencillo, que contenga la parte básica de ROS, y crear un sistema maestro/esclavo, algo que ROS soporta perfectamente.
- Ampliar el tipo de gestos reconocidos por el sistema. He empezado por algunos básicos, pero las posibilidades son inmensas.
- Utilizar otros canales para el reconocimiento de imagen. Actualmente hago uso del RGB, pero sería muy interesante poder usar el espectro infrarrojo, o el reconocimiento de puntos.
- Explorar otro tipo de hardware más moderno.
- Explorar la utilización de ROS 2, para evitar la obsolescencia de ROS 1.

Como se puede ver, hay ramificaciones para rato.