Meet YOLO26: next-gen vision AI.

Link to this sectionGuía de inicio rápido de ROS (Robot Operating System)#

Esta guía te muestra cómo integrar Ultralytics YOLO con un robot que utiliza ROS Noetic para ejecutar detección de objetos y segmentación en tiempo real en imágenes RGB, imágenes de profundidad y nubes de puntos.

Salta a configurar YOLO con ROS y luego trabaja con imágenes RGB, imágenes de profundidad o nubes de puntos.

ROS Introduction (captioned) from Open Robotics on Vimeo.

Link to this section¿Qué es ROS?#

El Robot Operating System (ROS) es un framework de código abierto muy utilizado en la investigación y la industria de la robótica. ROS proporciona una colección de bibliotecas y herramientas para ayudar a los desarrolladores a crear aplicaciones para robots. ROS está diseñado para funcionar con diversas plataformas robóticas, lo que lo convierte en una herramienta flexible y potente para los roboticistas.

Link to this sectionCaracterísticas clave de ROS#

  1. Arquitectura modular: ROS tiene una arquitectura modular que permite a los desarrolladores construir sistemas complejos combinando componentes más pequeños y reutilizables llamados nodos. Cada nodo suele realizar una función específica, y los nodos se comunican entre sí mediante mensajes a través de temas o servicios.

  2. Middleware de comunicación: ROS ofrece una infraestructura de comunicación robusta que admite la comunicación entre procesos y la computación distribuida. Esto se logra a través de un modelo de publicación-suscripción para flujos de datos (temas) y un modelo de solicitud-respuesta para llamadas de servicio.

  3. Abstracción de hardware: ROS proporciona una capa de abstracción sobre el hardware, lo que permite a los desarrolladores escribir código independiente del dispositivo. Esto permite utilizar el mismo código con diferentes configuraciones de hardware, facilitando la integración y la experimentación.

  4. Herramientas y utilidades: ROS viene con un amplio conjunto de herramientas y utilidades para la visualización, la depuración y la simulación. Por ejemplo, RViz se utiliza para visualizar datos de sensores e información del estado del robot, mientras que Gazebo proporciona un potente entorno de simulación para probar algoritmos y diseños de robots.

  5. Ecosistema extenso: El ecosistema de ROS es vasto y sigue creciendo, con numerosos paquetes disponibles para diferentes aplicaciones robóticas, incluyendo navegación, manipulación, percepción y más. La comunidad contribuye activamente al desarrollo y mantenimiento de estos paquetes.

Evolución de las versiones de ROS

Desde su desarrollo en 2007, ROS ha evolucionado a través de múltiples versiones, cada una introduciendo nuevas funcionalidades y mejoras para satisfacer las crecientes necesidades de la comunidad robótica. El desarrollo de ROS puede categorizarse en dos series principales: ROS 1 y ROS 2. Esta guía se centra en la versión de Soporte a Largo Plazo (LTS) de ROS 1, conocida como ROS Noetic Ninjemys, aunque el código también debería funcionar con versiones anteriores.

Link to this sectionROS 1 frente a ROS 2#

Aunque ROS 1 proporcionó una base sólida para el desarrollo robótico, ROS 2 soluciona sus deficiencias ofreciendo:

  • Rendimiento en tiempo real: Soporte mejorado para sistemas en tiempo real y comportamiento determinista.
  • Seguridad: Características de seguridad mejoradas para un funcionamiento seguro y fiable en diversos entornos.
  • Escalabilidad: Mejor soporte para sistemas multi-robot y despliegues a gran escala.
  • Soporte multiplataforma: Compatibilidad ampliada con varios sistemas operativos más allá de Linux, incluyendo Windows y macOS.
  • Comunicación flexible: Uso de DDS para una comunicación entre procesos más flexible y eficiente.

Link to this sectionMensajes y temas de ROS#

En ROS, la comunicación entre nodos se facilita a través de mensajes y temas. Un mensaje es una estructura de datos que define la información intercambiada entre nodos, mientras que un tema es un canal con nombre a través del cual se envían y reciben mensajes. Los nodos pueden publicar mensajes en un tema o suscribirse a mensajes de un tema, lo que les permite comunicarse entre sí. Este modelo de publicación-suscripción permite una comunicación asíncrona y el desacoplamiento entre nodos. Cada sensor o actuador en un sistema robótico suele publicar datos en un tema, que luego pueden ser consumidos por otros nodos para su procesamiento o control. Para el propósito de esta guía, nos centraremos en los mensajes de imagen (Image), profundidad (Depth) y nube de puntos (PointCloud), así como en los temas de cámara.

Link to this sectionConfiguración de Ultralytics YOLO con ROS#

Esta guía ha sido probada utilizando este entorno ROS, que es una bifurcación del repositorio ROSbot ROS. Este entorno incluye el paquete de Ultralytics YOLO, un contenedor Docker para una fácil configuración, paquetes ROS completos y entornos Gazebo para pruebas rápidas. Está diseñado para funcionar con el Husarion ROSbot 2 PRO. Los ejemplos de código proporcionados funcionarán en cualquier entorno ROS Noetic/Melodic, tanto en simulación como en el mundo real.

Husarion ROSbot 2 PRO autonomous robot platform

Link to this sectionInstalación de dependencias#

Además del entorno ROS, necesitarás instalar las siguientes dependencias:

  • Paquete ROS NumPy: Es necesario para la conversión rápida entre mensajes de imagen de ROS y matrices NumPy.

    pip install ros_numpy
  • Paquete Ultralytics:

    pip install ultralytics

Link to this sectionUsa Ultralytics con sensor_msgs/Image de ROS#

The sensor_msgs/Image message type is commonly used in ROS for representing image data. It contains fields for encoding, height, width, and pixel data, making it suitable for transmitting images captured by cameras or other sensors. Image messages are widely used in robotic applications for tasks such as visual perception, object detection, and navigation.

Detection and Segmentation in ROS Gazebo

Link to this sectionUso paso a paso de imágenes#

El siguiente fragmento de código demuestra cómo usar el paquete de Ultralytics YOLO con ROS. En este ejemplo, nos suscribimos a un tema de cámara, procesamos la imagen entrante usando YOLO y publicamos los objetos detectados en nuevos temas para detección y segmentación.

Primero, importa las bibliotecas necesarias e instancia dos modelos: uno para segmentación y otro para detección. Inicializa un nodo de ROS (con el nombre ultralytics) para permitir la comunicación con el maestro de ROS. Para garantizar una conexión estable, incluimos una breve pausa, dando al nodo tiempo suficiente para establecer la conexión antes de continuar.

import time

import rospy

from ultralytics import YOLO

detection_model = YOLO("yolo26m.pt")
segmentation_model = YOLO("yolo26m-seg.pt")
rospy.init_node("ultralytics")
time.sleep(1)

Inicializa dos temas de ROS: uno para detección y otro para segmentación. Estos temas se utilizarán para publicar las imágenes anotadas, haciéndolas accesibles para un procesamiento posterior. La comunicación entre nodos se facilita mediante mensajes sensor_msgs/Image.

from sensor_msgs.msg import Image

det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)
seg_image_pub = rospy.Publisher("/ultralytics/segmentation/image", Image, queue_size=5)

Finalmente, crea un suscriptor que escuche los mensajes en el tema /camera/color/image_raw y llame a una función de callback para cada mensaje nuevo. Esta función de callback recibe mensajes de tipo sensor_msgs/Image, los convierte en una matriz NumPy usando ros_numpy, procesa las imágenes con los modelos YOLO previamente instanciados, anota las imágenes y luego las publica de nuevo en los temas respectivos: /ultralytics/detection/image para la detección y /ultralytics/segmentation/image para la segmentación.

import ros_numpy

def callback(data):
    """Callback function to process image and publish annotated images."""
    array = ros_numpy.numpify(data)
    if det_image_pub.get_num_connections():
        det_result = detection_model(array)
        det_annotated = det_result[0].plot(show=False)
        det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))

    if seg_image_pub.get_num_connections():
        seg_result = segmentation_model(array)
        seg_annotated = seg_result[0].plot(show=False)
        seg_image_pub.publish(ros_numpy.msgify(Image, seg_annotated, encoding="rgb8"))

rospy.Subscriber("/camera/color/image_raw", Image, callback)

while True:
    rospy.spin()
Código completo
import time

import ros_numpy
import rospy
from sensor_msgs.msg import Image

from ultralytics import YOLO

detection_model = YOLO("yolo26m.pt")
segmentation_model = YOLO("yolo26m-seg.pt")
rospy.init_node("ultralytics")
time.sleep(1)

det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)
seg_image_pub = rospy.Publisher("/ultralytics/segmentation/image", Image, queue_size=5)

def callback(data):
    """Callback function to process image and publish annotated images."""
    array = ros_numpy.numpify(data)
    if det_image_pub.get_num_connections():
        det_result = detection_model(array)
        det_annotated = det_result[0].plot(show=False)
        det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))

    if seg_image_pub.get_num_connections():
        seg_result = segmentation_model(array)
        seg_annotated = seg_result[0].plot(show=False)
        seg_image_pub.publish(ros_numpy.msgify(Image, seg_annotated, encoding="rgb8"))

rospy.Subscriber("/camera/color/image_raw", Image, callback)

while True:
    rospy.spin()
Depuración

La depuración de nodos en ROS (Robot Operating System) puede ser un desafío debido a la naturaleza distribuida del sistema. Varias herramientas pueden ayudar en este proceso:

  1. rostopic echo <TOPIC-NAME>: Este comando te permite ver los mensajes publicados en un tema específico, ayudándote a inspeccionar el flujo de datos.
  2. rostopic list: Usa este comando para listar todos los temas disponibles en el sistema ROS, dándote una visión general de los flujos de datos activos.
  3. rqt_graph: Esta herramienta de visualización muestra el gráfico de comunicación entre nodos, proporcionando información sobre cómo los nodos están interconectados y cómo interactúan.
  4. Para visualizaciones más complejas, como representaciones en 3D, puedes usar RViz. RViz (ROS Visualization) es una potente herramienta de visualización 3D para ROS. Te permite visualizar el estado de tu robot y su entorno en tiempo real. Con RViz, puedes ver datos de sensores (por ejemplo, sensor_msgs/Image), estados del modelo del robot y otros diversos tipos de información, facilitando la depuración y la comprensión del comportamiento de tu sistema robótico.

Link to this sectionPublicación de clases detectadas con std_msgs/String#

Standard ROS messages also include std_msgs/String messages. In many applications, it is not necessary to republish the entire annotated image; instead, only the classes present in the robot's view are needed. The following example demonstrates how to use std_msgs/String messages to republish the detected classes on the /ultralytics/detection/classes topic. These messages are more lightweight and provide essential information, making them valuable for various applications.

Link to this sectionCaso de uso de ejemplo#

Imagina un robot de almacén equipado con una cámara y un modelo de detección de objetos. En lugar de enviar imágenes grandes anotadas a través de la red, el robot puede publicar una lista de las clases detectadas como mensajes std_msgs/String. Por ejemplo, cuando el robot detecta objetos como "caja", "palet" y "carretilla elevadora", publica estas clases en el tema /ultralytics/detection/classes. Esta información puede ser utilizada por un sistema de monitorización central para realizar un seguimiento del inventario en tiempo real, optimizar la planificación de rutas del robot para evitar obstáculos o activar acciones específicas, como recoger una caja detectada. Este enfoque reduce el ancho de banda necesario para la comunicación y se centra en la transmisión de datos críticos.

Link to this sectionUso paso a paso de cadenas (String)#

Este ejemplo demuestra cómo usar el paquete de Ultralytics YOLO con ROS. En este ejemplo, nos suscribimos a un tema de cámara, procesamos la imagen entrante usando YOLO y publicamos los objetos detectados en un nuevo tema /ultralytics/detection/classes usando mensajes std_msgs/String. El paquete ros_numpy se utiliza para convertir el mensaje de imagen de ROS en una matriz NumPy para su procesamiento con YOLO.

import time

import ros_numpy
import rospy
from sensor_msgs.msg import Image
from std_msgs.msg import String

from ultralytics import YOLO

detection_model = YOLO("yolo26m.pt")
rospy.init_node("ultralytics")
time.sleep(1)
classes_pub = rospy.Publisher("/ultralytics/detection/classes", String, queue_size=5)

def callback(data):
    """Callback function to process image and publish detected classes."""
    array = ros_numpy.numpify(data)
    if classes_pub.get_num_connections():
        det_result = detection_model(array)
        classes = det_result[0].boxes.cls.cpu().numpy().astype(int)
        names = [det_result[0].names[i] for i in classes]
        classes_pub.publish(String(data=str(names)))

rospy.Subscriber("/camera/color/image_raw", Image, callback)
while True:
    rospy.spin()

Link to this sectionUsa Ultralytics con imágenes de profundidad en ROS#

Además de las imágenes RGB, ROS admite imágenes de profundidad, que proporcionan información sobre la distancia de los objetos a la cámara. Las imágenes de profundidad son cruciales para aplicaciones robóticas como la evitación de obstáculos, el mapeo 3D y la localización.

Una imagen de profundidad es una imagen donde cada píxel representa la distancia desde la cámara a un objeto. A diferencia de las imágenes RGB que capturan el color, las imágenes de profundidad capturan información espacial, permitiendo a los robots percibir la estructura 3D de su entorno.

Obtención de imágenes de profundidad

Las imágenes de profundidad pueden obtenerse utilizando varios sensores:

  1. Cámaras estéreo: Utilizan dos cámaras para calcular la profundidad basándose en la disparidad de la imagen.
  2. Cámaras de tiempo de vuelo (ToF): Miden el tiempo que tarda la luz en regresar desde un objeto.
  3. Sensores de luz estructurada: Proyectan un patrón y miden su deformación en las superficies.

Link to this sectionUso de YOLO con imágenes de profundidad#

En ROS, las imágenes de profundidad están representadas por el tipo de mensaje sensor_msgs/Image, que incluye campos para la codificación, altura, anchura y datos de píxeles. El campo de codificación para las imágenes de profundidad a menudo utiliza un formato como "16UC1", que indica un entero de 16 bits sin signo por píxel, donde cada valor representa la distancia al objeto. Las imágenes de profundidad se utilizan comúnmente junto con las imágenes RGB para proporcionar una visión más completa del entorno.

Usando YOLO, es posible extraer y combinar información tanto de imágenes RGB como de profundidad. Por ejemplo, YOLO puede detectar objetos dentro de una imagen RGB, y esta detección puede utilizarse para señalar regiones correspondientes en la imagen de profundidad. Esto permite la extracción de información de profundidad precisa para los objetos detectados, mejorando la capacidad del robot para comprender su entorno en tres dimensiones.

Cámaras RGB-D

Cuando se trabaja con imágenes de profundidad, es esencial asegurarse de que las imágenes RGB y de profundidad estén correctamente alineadas. Las cámaras RGB-D, como la serie Intel RealSense, proporcionan imágenes RGB y de profundidad sincronizadas, lo que facilita la combinación de información de ambas fuentes. Si se utilizan cámaras RGB y de profundidad separadas, es fundamental calibrarlas para garantizar una alineación precisa.

Link to this sectionUso paso a paso de profundidad#

En este ejemplo, usamos YOLO para segmentar una imagen y aplicar la máscara extraída para segmentar el objeto en la imagen de profundidad. Esto nos permite determinar la distancia de cada píxel del objeto de interés desde el centro focal de la cámara. Al obtener esta información de distancia, podemos calcular la distancia entre la cámara y el objeto específico en la escena. Comienza importando las bibliotecas necesarias, creando un nodo de ROS e instanciando un modelo de segmentación y un tema de ROS.

import time

import rospy
from std_msgs.msg import String

from ultralytics import YOLO

rospy.init_node("ultralytics")
time.sleep(1)

segmentation_model = YOLO("yolo26m-seg.pt")

classes_pub = rospy.Publisher("/ultralytics/detection/distance", String, queue_size=5)

A continuación, define una función de callback que procese el mensaje de imagen de profundidad entrante. La función espera los mensajes de imagen de profundidad y de imagen RGB, los convierte en matrices NumPy y aplica el modelo de segmentación a la imagen RGB. Luego, extrae la máscara de segmentación para cada objeto detectado y calcula la distancia promedio del objeto a la cámara usando la imagen de profundidad. La mayoría de los sensores tienen una distancia máxima, conocida como distancia de recorte, más allá de la cual los valores se representan como infinito (np.inf). Antes de procesar, es importante filtrar estos valores nulos y asignarles un valor de 0. Finalmente, publica los objetos detectados junto con sus distancias promedio en el tema /ultralytics/detection/distance.

import numpy as np
import ros_numpy
from sensor_msgs.msg import Image

def callback(data):
    """Callback function to process depth image and RGB image."""
    image = rospy.wait_for_message("/camera/color/image_raw", Image)
    image = ros_numpy.numpify(image)
    depth = ros_numpy.numpify(data)
    result = segmentation_model(image)

    all_objects = []
    for index, cls in enumerate(result[0].boxes.cls):
        class_index = int(cls.cpu().numpy())
        name = result[0].names[class_index]
        mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
        obj = depth[mask == 1]
        obj = obj[~np.isnan(obj)]
        avg_distance = np.mean(obj) if len(obj) else np.inf
        all_objects.append(f"{name}: {avg_distance:.2f}m")

    classes_pub.publish(String(data=str(all_objects)))

rospy.Subscriber("/camera/depth/image_raw", Image, callback)

while True:
    rospy.spin()
Código completo
import time

import numpy as np
import ros_numpy
import rospy
from sensor_msgs.msg import Image
from std_msgs.msg import String

from ultralytics import YOLO

rospy.init_node("ultralytics")
time.sleep(1)

segmentation_model = YOLO("yolo26m-seg.pt")

classes_pub = rospy.Publisher("/ultralytics/detection/distance", String, queue_size=5)

def callback(data):
    """Callback function to process depth image and RGB image."""
    image = rospy.wait_for_message("/camera/color/image_raw", Image)
    image = ros_numpy.numpify(image)
    depth = ros_numpy.numpify(data)
    result = segmentation_model(image)

    all_objects = []
    for index, cls in enumerate(result[0].boxes.cls):
        class_index = int(cls.cpu().numpy())
        name = result[0].names[class_index]
        mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
        obj = depth[mask == 1]
        obj = obj[~np.isnan(obj)]
        avg_distance = np.mean(obj) if len(obj) else np.inf
        all_objects.append(f"{name}: {avg_distance:.2f}m")

    classes_pub.publish(String(data=str(all_objects)))

rospy.Subscriber("/camera/depth/image_raw", Image, callback)

while True:
    rospy.spin()

Link to this sectionUsa Ultralytics con sensor_msgs/PointCloud2 de ROS#

Detection and Segmentation in ROS Gazebo

The sensor_msgs/PointCloud2 message type is a data structure used in ROS to represent 3D point cloud data. This message type is integral to robotic applications, enabling tasks such as 3D mapping, object recognition, and localization.

Una nube de puntos es una colección de puntos de datos definidos dentro de un sistema de coordenadas tridimensional. Estos puntos de datos representan la superficie externa de un objeto o una escena, capturada mediante tecnologías de escaneo 3D. Cada punto en la nube tiene coordenadas X, Y y Z, que corresponden a su posición en el espacio, y también puede incluir información adicional como color e intensidad.

Marco de referencia

Cuando se trabaja con sensor_msgs/PointCloud2, es esencial considerar el marco de referencia del sensor desde el cual se adquirieron los datos de la nube de puntos. La nube de puntos se captura inicialmente en el marco de referencia del sensor. Puedes determinar este marco de referencia escuchando el tema /tf_static. Sin embargo, dependiendo de los requisitos específicos de tu aplicación, es posible que necesites convertir la nube de puntos a otro marco de referencia. Esta transformación puede lograrse utilizando el paquete tf2_ros, que proporciona herramientas para gestionar marcos de coordenadas y transformar datos entre ellos.

Obtención de nubes de puntos

Las nubes de puntos pueden obtenerse utilizando varios sensores:

  1. LIDAR (Light Detection and Ranging): Utiliza pulsos láser para medir distancias a los objetos y crear mapas 3D de alta precisión.
  2. Cámaras de profundidad: Capturan información de profundidad para cada píxel, permitiendo la reconstrucción 3D de la escena.
  3. Cámaras estéreo: Utilizan dos o más cámaras para obtener información de profundidad mediante triangulación.
  4. Escáneres de luz estructurada: Proyectan un patrón conocido sobre una superficie y miden la deformación para calcular la profundidad.

Link to this sectionUso de YOLO con nubes de puntos#

Para integrar YOLO con mensajes de tipo sensor_msgs/PointCloud2, podemos emplear un método similar al utilizado para los mapas de profundidad. Aprovechando la información de color incrustada en la nube de puntos, podemos extraer una imagen 2D, realizar la segmentación en esta imagen usando YOLO y luego aplicar la máscara resultante a los puntos tridimensionales para aislar el objeto 3D de interés.

Para manejar nubes de puntos, recomendamos usar Open3D (pip install open3d), una biblioteca de Python fácil de usar. Open3D proporciona herramientas robustas para gestionar estructuras de datos de nubes de puntos, visualizarlas y ejecutar operaciones complejas sin problemas. Esta biblioteca puede simplificar significativamente el proceso y mejorar nuestra capacidad para manipular y analizar nubes de puntos junto con la segmentación basada en YOLO.

Link to this sectionUso paso a paso de nubes de puntos#

Importa las bibliotecas necesarias e instancia el modelo YOLO para la segmentación.

import time

import rospy

from ultralytics import YOLO

rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolo26m-seg.pt")

Create a function pointcloud2_to_array, which transforms a sensor_msgs/PointCloud2 message into two NumPy arrays. The sensor_msgs/PointCloud2 messages contain n points based on the width and height of the acquired image. For instance, a 480 x 640 image will have 307,200 points. Each point includes three spatial coordinates (xyz) and the corresponding color in RGB format. These can be considered as two separate channels of information.

La función devuelve las coordenadas xyz y los valores RGB en el formato de la resolución original de la cámara (width x height). La mayoría de los sensores tienen una distancia máxima, conocida como distancia de recorte, más allá de la cual los valores se representan como inf (np.inf). Antes de procesarlos, es importante filtrar estos valores nulos y asignarles un valor de 0.

import numpy as np
import ros_numpy

def pointcloud2_to_array(pointcloud2: PointCloud2) -> tuple:
    """Convert a ROS PointCloud2 message to a numpy array.

    Args:
        pointcloud2 (PointCloud2): the PointCloud2 message

    Returns:
        (tuple): tuple containing (xyz, rgb)
    """
    pc_array = ros_numpy.point_cloud2.pointcloud2_to_array(pointcloud2)
    split = ros_numpy.point_cloud2.split_rgb_field(pc_array)
    rgb = np.stack([split["b"], split["g"], split["r"]], axis=2)
    xyz = ros_numpy.point_cloud2.get_xyz_points(pc_array, remove_nans=False)
    xyz = np.array(xyz).reshape((pointcloud2.height, pointcloud2.width, 3))
    nan_rows = np.isnan(xyz).all(axis=2)
    xyz[nan_rows] = [0, 0, 0]
    rgb[nan_rows] = [0, 0, 0]
    return xyz, rgb

A continuación, suscríbete al tema /camera/depth/points para recibir el mensaje de nube de puntos y convierte el mensaje sensor_msgs/PointCloud2 en matrices NumPy que contengan las coordenadas XYZ y los valores RGB (usando la función pointcloud2_to_array). Procesa la imagen RGB usando el modelo YOLO para extraer objetos segmentados. Para cada objeto detectado, extrae la máscara de segmentación y aplícala tanto a la imagen RGB como a las coordenadas XYZ para aislar el objeto en el espacio 3D.

El procesamiento de la máscara es directo ya que consiste en valores binarios, donde 1 indica la presencia del objeto y 0 indica la ausencia. Para aplicar la máscara, simplemente multiplica los canales originales por la máscara. Esta operación aísla efectivamente el objeto de interés dentro de la imagen. Finalmente, crea un objeto de nube de puntos de Open3D y visualiza el objeto segmentado en el espacio 3D con los colores asociados.

import sys

import open3d as o3d

ros_cloud = rospy.wait_for_message("/camera/depth/points", PointCloud2)
xyz, rgb = pointcloud2_to_array(ros_cloud)
result = segmentation_model(rgb)

if not len(result[0].boxes.cls):
    print("No objects detected")
    sys.exit()

classes = result[0].boxes.cls.cpu().numpy().astype(int)
for index, class_id in enumerate(classes):
    mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
    mask_expanded = np.stack([mask, mask, mask], axis=2)

    obj_rgb = rgb * mask_expanded
    obj_xyz = xyz * mask_expanded

    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(obj_xyz.reshape((ros_cloud.height * ros_cloud.width, 3)))
    pcd.colors = o3d.utility.Vector3dVector(obj_rgb.reshape((ros_cloud.height * ros_cloud.width, 3)) / 255)
    o3d.visualization.draw_geometries([pcd])
Código completo
import sys
import time

import numpy as np
import open3d as o3d
import ros_numpy
import rospy
from sensor_msgs.msg import PointCloud2

from ultralytics import YOLO

rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolo26m-seg.pt")

def pointcloud2_to_array(pointcloud2: PointCloud2) -> tuple:
    """Convert a ROS PointCloud2 message to a numpy array.

    Args:
        pointcloud2 (PointCloud2): the PointCloud2 message

    Returns:
        (tuple): tuple containing (xyz, rgb)
    """
    pc_array = ros_numpy.point_cloud2.pointcloud2_to_array(pointcloud2)
    split = ros_numpy.point_cloud2.split_rgb_field(pc_array)
    rgb = np.stack([split["b"], split["g"], split["r"]], axis=2)
    xyz = ros_numpy.point_cloud2.get_xyz_points(pc_array, remove_nans=False)
    xyz = np.array(xyz).reshape((pointcloud2.height, pointcloud2.width, 3))
    nan_rows = np.isnan(xyz).all(axis=2)
    xyz[nan_rows] = [0, 0, 0]
    rgb[nan_rows] = [0, 0, 0]
    return xyz, rgb

ros_cloud = rospy.wait_for_message("/camera/depth/points", PointCloud2)
xyz, rgb = pointcloud2_to_array(ros_cloud)
result = segmentation_model(rgb)

if not len(result[0].boxes.cls):
    print("No objects detected")
    sys.exit()

classes = result[0].boxes.cls.cpu().numpy().astype(int)
for index, class_id in enumerate(classes):
    mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
    mask_expanded = np.stack([mask, mask, mask], axis=2)

    obj_rgb = rgb * mask_expanded
    obj_xyz = xyz * mask_expanded

    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(obj_xyz.reshape((ros_cloud.height * ros_cloud.width, 3)))
    pcd.colors = o3d.utility.Vector3dVector(obj_rgb.reshape((ros_cloud.height * ros_cloud.width, 3)) / 255)
    o3d.visualization.draw_geometries([pcd])

Point Cloud Segmentation with Ultralytics

Link to this sectionConclusión#

Con Ultralytics YOLO integrado en ROS, tu robot puede ejecutar detección de objetos y segmentación a través de imágenes RGB, imágenes de profundidad y nubes de puntos, convirtiendo flujos de sensores brutos en percepción procesable. A partir de aquí, explora el modo Predict para obtener más opciones de inferencia, o sigue los pasos de un proyecto de visión artificial para llevar tu aplicación robótica del prototipo a la producción.

Link to this sectionFAQ#

Link to this section¿Qué es el Robot Operating System (ROS)?#

El Robot Operating System (ROS) es un framework de código abierto utilizado habitualmente en robótica para ayudar a los desarrolladores a crear aplicaciones robóticas robustas. Proporciona una colección de bibliotecas y herramientas para construir e interconectar sistemas robóticos, facilitando el desarrollo de aplicaciones complejas. ROS admite la comunicación entre nodos mediante mensajes a través de temas o servicios.

Link to this section¿Cómo integro Ultralytics YOLO con ROS para la detección de objetos en tiempo real?#

La integración de Ultralytics YOLO con ROS implica configurar un entorno ROS y utilizar YOLO para procesar los datos de los sensores. Comienza instalando las dependencias necesarias como ros_numpy y Ultralytics YOLO:

pip install ros_numpy ultralytics

A continuación, crea un nodo de ROS y suscríbete a un tema de imagen para procesar los datos entrantes para la detección de objetos. Aquí tienes un ejemplo mínimo:

import ros_numpy
import rospy
from sensor_msgs.msg import Image

from ultralytics import YOLO

detection_model = YOLO("yolo26m.pt")
rospy.init_node("ultralytics")
det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)

def callback(data):
    array = ros_numpy.numpify(data)
    det_result = detection_model(array)
    det_annotated = det_result[0].plot(show=False)
    det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))

rospy.Subscriber("/camera/color/image_raw", Image, callback)
rospy.spin()

Link to this section¿Qué son los temas de ROS y cómo se utilizan en Ultralytics YOLO?#

Los temas de ROS facilitan la comunicación entre nodos en una red ROS mediante el uso de un modelo de publicación-suscripción. Un tema es un canal con nombre que los nodos utilizan para enviar y recibir mensajes de forma asíncrona. En el contexto de Ultralytics YOLO, puedes hacer que un nodo se suscriba a un tema de imagen, procese las imágenes usando YOLO para tareas como detección o segmentación y publique los resultados en nuevos temas.

Por ejemplo, suscríbete a un tema de cámara y procesa la imagen entrante para la detección:

rospy.Subscriber("/camera/color/image_raw", Image, callback)

Link to this section¿Por qué usar imágenes de profundidad con Ultralytics YOLO en ROS?#

Las imágenes de profundidad en ROS, representadas por sensor_msgs/Image, proporcionan la distancia de los objetos a la cámara, lo cual es crucial para tareas como la evitación de obstáculos, el mapeo 3D y la localización. Al usar información de profundidad junto con imágenes RGB, los robots pueden comprender mejor su entorno 3D.

Con YOLO, puedes extraer máscaras de segmentación de imágenes RGB y aplicar estas máscaras a las imágenes de profundidad para obtener información precisa de los objetos en 3D, mejorando la capacidad del robot para navegar e interactuar con su entorno.

Link to this section¿Cómo puedo visualizar nubes de puntos 3D con YOLO en ROS?#

Para visualizar nubes de puntos 3D en ROS con YOLO:

  1. Convierte los mensajes sensor_msgs/PointCloud2 en matrices NumPy.
  2. Usa YOLO para segmentar imágenes RGB.
  3. Aplica la máscara de segmentación a la nube de puntos.

Aquí tienes un ejemplo usando Open3D para la visualización:

import sys

import numpy as np
import open3d as o3d
import ros_numpy
import rospy
from sensor_msgs.msg import PointCloud2

from ultralytics import YOLO

rospy.init_node("ultralytics")
segmentation_model = YOLO("yolo26m-seg.pt")

def pointcloud2_to_array(pointcloud2):
    pc_array = ros_numpy.point_cloud2.pointcloud2_to_array(pointcloud2)
    split = ros_numpy.point_cloud2.split_rgb_field(pc_array)
    rgb = np.stack([split["b"], split["g"], split["r"]], axis=2)
    xyz = ros_numpy.point_cloud2.get_xyz_points(pc_array, remove_nans=False)
    xyz = np.array(xyz).reshape((pointcloud2.height, pointcloud2.width, 3))
    return xyz, rgb

ros_cloud = rospy.wait_for_message("/camera/depth/points", PointCloud2)
xyz, rgb = pointcloud2_to_array(ros_cloud)
result = segmentation_model(rgb)

if not len(result[0].boxes.cls):
    print("No objects detected")
    sys.exit()

classes = result[0].boxes.cls.cpu().numpy().astype(int)
for index, class_id in enumerate(classes):
    mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
    mask_expanded = np.stack([mask, mask, mask], axis=2)

    obj_rgb = rgb * mask_expanded
    obj_xyz = xyz * mask_expanded

    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(obj_xyz.reshape((-1, 3)))
    pcd.colors = o3d.utility.Vector3dVector(obj_rgb.reshape((-1, 3)) / 255)
    o3d.visualization.draw_geometries([pcd])

Este enfoque proporciona una visualización 3D de objetos segmentados, útil para tareas como la navegación y la manipulación en aplicaciones robóticas.

Comentarios