Руководство по быстрому старту ROS (Robot Operating System)#
Это руководство показывает, как интегрировать Ultralytics YOLO с ROS1 (rospy) или ROS2 (rclpy) для запуска обнаружения объектов и сегментации в реальном времени на изображениях RGB, изображениях глубины и облаках точек.
Перейди к настройке YOLO с ROS, а затем работай с изображениями RGB, изображениями глубины или облаками точек.
ROS Introduction (captioned) from Open Robotics on Vimeo.
Что такое ROS?#
Robot Operating System (ROS) — это фреймворк с открытым исходным кодом, широко используемый в робототехнических исследованиях и промышленности. ROS предоставляет набор библиотек и инструментов, помогающих разработчикам создавать приложения для роботов. ROS разработана для работы с различными робототехническими платформами, что делает её гибким и мощным инструментом для робототехников.
Ключевые особенности ROS#
-
Модульная архитектура: ROS имеет модульную архитектуру, позволяющую разработчикам создавать сложные системы путем объединения более мелких, многоразовых компонентов, называемых узлами. Каждый узел обычно выполняет определенную функцию, и узлы общаются друг с другом с помощью сообщений через топики или сервисы.
-
Связующее программное обеспечение (Middleware) для коммуникации: ROS предлагает надежную инфраструктуру обмена данными, поддерживающую межпроцессное взаимодействие и распределенные вычисления. Это достигается за счет модели «издатель-подписчик» для потоков данных (топиков) и модели «запрос-ответ» для вызовов сервисов.
-
Абстракция оборудования: ROS предоставляет уровень абстракции над аппаратным обеспечением, позволяя тебе писать код, независимый от устройств. Это позволяет использовать один и тот же код с разными конфигурациями оборудования, упрощая интеграцию и эксперименты.
-
Инструменты и утилиты: ROS поставляется с богатым набором инструментов и утилит для визуализации, отладки и симуляции. Например, RViz используется для визуализации данных сенсоров и состояния робота, в то время как Gazebo предоставляет мощную среду симуляции для тестирования алгоритмов и конструкций роботов.
-
Обширная экосистема: Экосистема ROS огромна и постоянно растет, с множеством пакетов, доступных для различных робототехнических задач, включая навигацию, манипуляции, восприятие и многое другое. Сообщество активно участвует в разработке и поддержке этих пакетов.
Эволюция версий ROS
С момента своего создания в 2007 году ROS развивалась через множество версий, разделенных на ROS 1 и ROS 2. Существующие примеры ниже используют ROS1 Noetic; компактные адаптеры в разделе Использование ROS2 показывают соответствующие интерфейсы rclpy для текущих релизов ROS2.
ROS 1 против ROS 2#
Хотя ROS 1 предоставила прочный фундамент для разработки роботов, ROS 2 устраняет ее недостатки, предлагая:
- Производительность в реальном времени: Улучшенная поддержка систем реального времени и детерминированного поведения.
- Безопасность: Улучшенные функции безопасности для надежной и стабильной работы в различных условиях.
- Масштабируемость: Лучшая поддержка мультиагентных робототехнических систем и крупномасштабных развертываний.
- Кроссплатформенная поддержка: Расширенная совместимость с различными операционными системами помимо Linux, включая Windows и macOS.
- Гибкая коммуникация: Использование DDS для более гибкого и эффективного межпроцессного взаимодействия.
Сообщения и топики ROS#
В ROS связь между узлами осуществляется через сообщения и топики. Сообщение — это структура данных, определяющая информацию, которой обмениваются узлы, а топик — это именованный канал, через который отправляются и принимаются сообщения. Узлы могут публиковать сообщения в топик или подписываться на сообщения из топика, что позволяет им общаться друг с другом. Эта модель издатель-подписчик обеспечивает асинхронную связь и разделение между узлами. Каждый датчик или исполнительный механизм в робототехнической системе обычно публикует данные в топик, которые затем могут использоваться другими узлами для обработки или управления. Для целей этого руководства мы сосредоточимся на сообщениях Image, Depth и PointCloud и топиках камер.
Настройка Ultralytics YOLO с ROS#
Примеры для ROS1 были протестированы с использованием этой среды ROS, форка репозитория ROSbot ROS. Та же обработка YOLO и NumPy применима и в ROS2; отличаются только жизненный цикл узла и преобразование сообщений.
Установка зависимостей#
Помимо среды ROS, тебе потребуется установить следующие зависимости:
-
Пакет ROS NumPy: Это необходимо для быстрого преобразования между сообщениями ROS Image и массивами NumPy.
pip install ros_numpy -
Пакет Ultralytics:
pip install ultralytics
Использование ROS2#
ROS2 заменяет rospy на rclpy, а преобразование изображений ros_numpy на cv_bridge. Следующий узел является полным эквивалентом ROS2 для приведенного ниже процесса обнаружения RGB; создавай экземпляры моделей один раз и используй их повторно в обратных вызовах.
import cv_bridge
import rclpy
from rclpy.node import Node
from rclpy.qos import qos_profile_sensor_data
from sensor_msgs.msg import Image
from ultralytics import YOLO
class UltralyticsNode(Node):
"""Run YOLO detection on ROS2 image messages."""
def __init__(self):
"""Initialize the ROS2 node, model, and image interfaces."""
super().__init__("ultralytics")
self.bridge = cv_bridge.CvBridge()
self.model = YOLO("yolo26m.pt")
self.publisher = self.create_publisher(Image, "/ultralytics/detection/image", 5)
self.create_subscription(Image, "/camera/color/image_raw", self.callback, qos_profile_sensor_data)
def callback(self, message):
"""Publish the annotated camera frame."""
image = self.bridge.imgmsg_to_cv2(message, desired_encoding="bgr8")
annotated = self.model(image)[0].plot(show=False)
self.publisher.publish(self.bridge.cv2_to_imgmsg(annotated, encoding="bgr8"))
def main(args=None):
"""Start the ROS2 node."""
rclpy.init(args=args)
node = UltralyticsNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == "__main__":
main()Для карт глубины повторно используй приведенный ниже код обработки глубины, заменив только получение и преобразование сообщений:
self.create_subscription(Image, "/camera/color/image_raw", self.rgb_callback, qos_profile_sensor_data)
self.create_subscription(Image, "/camera/depth/image_raw", self.depth_callback, qos_profile_sensor_data)
def rgb_callback(self, message):
self.rgb_image = self.bridge.imgmsg_to_cv2(message, desired_encoding="bgr8")
def depth_callback(self, message):
depth_image = self.bridge.imgmsg_to_cv2(message, desired_encoding="passthrough")
# Apply the NumPy mask and distance calculation from the depth example below.Для облаков точек ROS2 предоставляет sensor_msgs_py.point_cloud2; преобразуй организованное облако один раз, а затем используй сегментацию NumPy и 3D-картирование ниже:
from sensor_msgs_py import point_cloud2
points = point_cloud2.read_points_numpy(message, field_names=("x", "y", "z", "rgb"))
points = points.reshape(message.height, message.width, 4)Использование Ultralytics с ROS sensor_msgs/Image#
Тип сообщения sensor_msgs/Image message type обычно используется в ROS для представления данных изображений. Он содержит поля для кодировки, высоты, ширины и данных пикселей, что делает его подходящим для передачи изображений, снятых камерами или другими датчиками. Сообщения изображений широко применяются в робототехнических задачах для визуального восприятия, обнаружения объектов и навигации.
Пошаговое использование изображений#
Следующий фрагмент кода демонстрирует, как использовать пакет Ultralytics YOLO с ROS. В этом примере мы подписываемся на топик камеры, обрабатываем входящее изображение с помощью YOLO и публикуем обнаруженные объекты в новые топики для обнаружения и сегментации.
Сначала импортируй необходимые библиотеки и создай две модели: одну для сегментации и одну для обнаружения. Инициализируй узел ROS (с именем ultralytics), чтобы обеспечить связь с мастером ROS. Чтобы обеспечить стабильное соединение, мы включаем короткую паузу, давая узлу достаточно времени для установления соединения перед продолжением.
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)Инициализируй два топика ROS: один для обнаружения и один для сегментации. Эти топики будут использоваться для публикации размеченных изображений, делая их доступными для дальнейшей обработки. Связь между узлами осуществляется с помощью сообщений 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)Наконец, создай подписчика, который прослушивает сообщения в топике /camera/color/image_raw и вызывает функцию обратного вызова для каждого нового сообщения. Эта функция обратного вызова получает сообщения типа sensor_msgs/Image, преобразует их в массив NumPy с помощью ros_numpy, обрабатывает изображения с помощью ранее созданных моделей YOLO, размечает изображения, а затем публикует их обратно в соответствующие топики: /ultralytics/detection/image для обнаружения и /ultralytics/segmentation/image для сегментации.
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()Полный код
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()Отладка
Отладка нод ROS (Robot Operating System) может быть сложной задачей из-за распределенной природы системы. Существует несколько инструментов, которые помогут в этом процессе:
rostopic echo <TOPIC-NAME>: Эта команда позволяет просматривать сообщения, опубликованные в определенном топике, помогая тебе проверять поток данных.rostopic list: Используй эту команду для вывода списка всех доступных топиков в системе ROS, что дает тебе общее представление об активных потоках данных.rqt_graph: Этот инструмент визуализации отображает граф связи между узлами, предоставляя информацию о том, как узлы связаны друг с другом и как они взаимодействуют.- Для более сложных визуализаций, таких как 3D-представления, ты можешь использовать RViz. RViz (ROS Visualization) — это мощный инструмент 3D-визуализации для ROS. Он позволяет визуализировать состояние твоего робота и его окружения в реальном времени. С помощью RViz ты можешь просматривать данные датчиков (например,
sensor_msgs/Image), состояния модели робота и различные другие типы информации, что облегчает отладку и понимание поведения твоей робототехнической системы.
Публикация обнаруженных классов с помощью std_msgs/String#
Стандартные сообщения ROS также включают сообщения std_msgs/String. Во многих приложениях нет необходимости перепубликовывать все аннотированное изображение; вместо этого нужны только классы, присутствующие в поле зрения робота. Следующий пример демонстрирует, как использовать сообщения std_msgs/String для перепубликации обнаруженных классов в топике /ultralytics/detection/classes. Эти сообщения более легковесны и содержат самую важную информацию, что делает их полезными для различных сценариев.
Пример использования#
Рассмотрим складского робота, оснащенного камерой и моделью обнаружения объектов. Вместо отправки больших размеченных изображений по сети робот может опубликовать список обнаруженных классов в виде сообщений std_msgs/String. Например, когда робот обнаруживает такие объекты, как «коробка», «поддон» и «погрузчик», он публикует эти классы в топик /ultralytics/detection/classes. Эта информация затем может использоваться центральной системой мониторинга для отслеживания инвентаря в реальном времени, оптимизации планирования пути робота для избежания препятствий или запуска определенных действий, таких как подъем обнаруженной коробки. Этот подход снижает пропускную способность, необходимую для связи, и фокусируется на передаче критически важных данных.
Пошаговое использование String#
Этот пример демонстрирует, как использовать пакет Ultralytics YOLO с ROS. В этом примере мы подписываемся на топик камеры, обрабатываем входящее изображение с помощью YOLO и публикуем обнаруженные объекты в новый топик /ultralytics/detection/classes, используя сообщения std_msgs/String. Пакет ros_numpy используется для преобразования сообщения ROS Image в массив NumPy для обработки с помощью 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()Использование Ultralytics с изображениями глубины ROS#
В дополнение к изображениям RGB, ROS поддерживает изображения глубины, которые предоставляют информацию о расстоянии до объектов от камеры. Изображения глубины имеют решающее значение для таких робототехнических задач, как обход препятствий, 3D-картирование и локализация.
Карта глубины — это изображение, где каждый пиксель представляет расстояние от камеры до объекта. В отличие от RGB-изображений, которые фиксируют цвет, карты глубины фиксируют пространственную информацию, позволяя роботам воспринимать 3D-структуру окружающей среды.
Изображения глубины могут быть получены с помощью различных сенсоров:
- Стереокамеры: Используют две камеры для расчета глубины на основе диспаратности изображений.
- Камеры времени пролета (ToF): Измеряют время, которое требуется свету для возвращения от объекта.
- Датчики структурированного света: Проецируют паттерн и измеряют его деформацию на поверхностях.
Использование YOLO с изображениями глубины#
В ROS изображения глубины представлены типом сообщения sensor_msgs/Image, который включает поля для кодирования, высоты, ширины и данных пикселей. Поле кодирования для изображений глубины часто использует формат вроде "16UC1", указывающий на 16-битное беззнаковое целое число на пиксель, где каждое значение представляет расстояние до объекта. Изображения глубины обычно используются в сочетании с изображениями RGB для обеспечения более полного представления об окружающей среде.
С помощью YOLO можно извлекать и объединять информацию как из RGB, так и из изображений глубины. Например, YOLO может обнаруживать объекты на RGB-изображении, и это обнаружение можно использовать для определения соответствующих областей на карте глубины. Это позволяет извлекать точную информацию о глубине для обнаруженных объектов, улучшая способность робота понимать свое окружение в трех измерениях.
При работе с изображениями глубины важно убедиться, что RGB-изображения и изображения глубины правильно выровнены. RGB-D камеры, такие как серия Intel RealSense, предоставляют синхронизированные RGB-изображения и изображения глубины, что облегчает объединение информации из обоих источников. Если используются раздельные RGB-камеры и камеры глубины, крайне важно откалибровать их для обеспечения точного выравнивания.
Пошаговое использование изображений глубины#
В этом примере мы используем YOLO для сегментации изображения и применяем извлеченную маску для сегментации объекта на карте глубины. Это позволяет нам определить расстояние каждого пикселя интересующего объекта от фокального центра камеры. Получив эту информацию о расстоянии, мы можем вычислить дистанцию между камерой и конкретным объектом в сцене. Начни с импорта необходимых библиотек, создания ROS-ноды, а также инициализации модели сегментации и 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)Затем определи функцию обратного вызова, которая обрабатывает входящее сообщение изображения глубины. Функция ожидает сообщения изображения глубины и изображения RGB, преобразует их в массивы NumPy и применяет модель сегментации к изображению RGB. Затем она извлекает маску сегментации для каждого обнаруженного объекта и вычисляет среднее расстояние объекта от камеры с помощью изображения глубины. Большинство датчиков имеют максимальное расстояние, известное как расстояние обрезки (clip distance), за пределами которого значения представляются как inf (np.inf). Перед обработкой важно отфильтровать эти нулевые значения и присвоить им значение 0. Наконец, она публикует обнаруженные объекты вместе с их средними расстояниями в топик /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()Полный код
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()Использование Ultralytics с ROS sensor_msgs/PointCloud2#
Тип сообщения sensor_msgs/PointCloud2 message type представляет собой структуру данных, используемую в ROS для представления данных трехмерного облака точек. Этот тип сообщения является неотъемлемой частью робототехнических приложений и позволяет решать такие задачи, как трехмерное картирование, распознавание объектов и локализация.
Облако точек — это набор точек данных, определенных в трехмерной системе координат. Эти точки данных представляют внешнюю поверхность объекта или сцены, полученную с помощью технологий 3D-сканирования. Каждая точка в облаке имеет координаты X, Y и Z, которые соответствуют ее положению в пространстве, а также может включать дополнительную информацию, такую как цвет и интенсивность.
При работе с sensor_msgs/PointCloud2 важно учитывать систему отсчета датчика, от которого были получены данные облака точек. Облако точек изначально захватывается в системе отсчета датчика. Ты можешь определить эту систему отсчета, прослушивая топик /tf_static. Однако в зависимости от требований твоего конкретного приложения тебе может потребоваться преобразовать облако точек в другую систему отсчета. Это преобразование может быть достигнуто с помощью пакета tf2_ros, который предоставляет инструменты для управления системами координат и преобразования данных между ними.
Облака точек могут быть получены с помощью различных сенсоров:
- LIDAR (обнаружение и дальнометрия с помощью света): Использует лазерные импульсы для измерения расстояний до объектов и создания высокоточных 3D-карт.
- Камеры глубины: захватывают информацию о глубине для каждого пикселя, позволяя восстанавливать 3D-сцену.
- Стереокамеры: используют две или более камер для получения информации о глубине с помощью триангуляции.
- Сканеры структурированного света: проецируют известный шаблон на поверхность и измеряют его деформацию для вычисления глубины.
Использование YOLO с облаками точек#
Для интеграции YOLO с сообщениями типа sensor_msgs/PointCloud2 мы можем использовать метод, аналогичный методу, используемому для карт глубины. Используя цветовую информацию, внедренную в облако точек, мы можем извлечь 2D-изображение, выполнить сегментацию на этом изображении с помощью YOLO, а затем применить полученную маску к трехмерным точкам для выделения интересующего нас 3D-объекта.
Для работы с облаками точек мы рекомендуем использовать Open3D (pip install open3d), удобную библиотеку Python. Open3D предоставляет надежные инструменты для управления структурами данных облаков точек, их визуализации и беспрепятственного выполнения сложных операций. Эта библиотека может значительно упростить процесс и расширить наши возможности по манипулированию и анализу облаков точек в сочетании с сегментацией на основе YOLO.
Пошаговое использование облаков точек#
Импортируй необходимые библиотеки и создай модель YOLO для сегментации.
import time
import rospy
from ultralytics import YOLO
rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolo26m-seg.pt")Создай функцию pointcloud2_to_array, которая преобразует сообщение sensor_msgs/PointCloud2 в два массива NumPy. Сообщения sensor_msgs/PointCloud2 содержат точки n на основе width и height полученного изображения. Например, изображение 480 x 640 будет иметь точки 307,200. Каждая точка включает три пространственные координаты (xyz) и соответствующий цвет в формате RGB. Их можно рассматривать как два отдельных канала информации.
Функция возвращает координаты xyz и значения RGB в формате исходного разрешения камеры (width x height). Большинство датчиков имеют максимальное расстояние, известное как расстояние обрезки (clip distance), за пределами которого значения представляются как inf (np.inf). Перед обработкой важно отфильтровать эти нулевые значения и присвоить им значение 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Затем подпишись на топик /camera/depth/points, чтобы получить сообщение облака точек, и преобразуй сообщение sensor_msgs/PointCloud2 в массивы NumPy, содержащие координаты XYZ и значения RGB (используя функцию pointcloud2_to_array). Обработай изображение RGB с помощью модели YOLO для извлечения сегментированных объектов. Для каждого обнаруженного объекта извлеки маску сегментации и примени ее как к изображению RGB, так и к координатам XYZ, чтобы изолировать объект в 3D-пространстве.
Обработка маски проста, так как она состоит из двоичных значений, где 1 указывает на присутствие объекта, а 0 — на его отсутствие. Чтобы применить маску, просто умножь исходные каналы на маску. Эта операция эффективно изолирует интересующий объект внутри изображения. Наконец, создай объект облака точек Open3D и визуализируй сегментированный объект в 3D-пространстве с сопутствующими цветами.
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])Полный код
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])
Заключение#
Благодаря интеграции Ultralytics YOLO в ROS твой робот может выполнять обнаружение объектов и сегментацию на изображениях RGB, изображениях глубины и облаках точек, превращая сырые потоки с датчиков в полезные данные для восприятия. Отсюда изучи режим Предсказания (Predict mode) для получения дополнительных параметров инференса или следуй этапам проекта компьютерного зрения, чтобы перенести свое робототехническое приложение от прототипа к производству.
FAQ#
Robot Operating System (ROS) — это фреймворк с открытым исходным кодом, обычно используемый в робототехнике, чтобы помочь разработчикам создавать надежные приложения для роботов. Он предоставляет набор библиотек и инструментов для создания систем роботов и взаимодействия с ними, обеспечивая более легкую разработку сложных приложений. ROS поддерживает связь между узлами с помощью сообщений через топики или сервисы.
Интеграция Ultralytics YOLO с ROS включает настройку среды ROS и использование YOLO для обработки данных датчиков. Начни с установки необходимых зависимостей, таких как
ros_numpyи Ultralytics YOLO:pip install ros_numpy ultralyticsЗатем создай узел ROS и подпишись на топик изображений для обработки входящих данных для обнаружения объектов. Вот минимальный пример:
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()Топики ROS облегчают связь между узлами в сети ROS с помощью модели издатель-подписчик. Топик — это именованный канал, который узлы используют для отправки и получения сообщений асинхронно. В контексте Ultralytics YOLO ты можешь заставить узел подписаться на топик изображений, обрабатывать изображения с помощью YOLO для таких задач, как обнаружение или сегментация, и публиковать результаты в новые топики.
Например, подпишись на топик камеры и обработай входящее изображение для детекции:
rospy.Subscriber("/camera/color/image_raw", Image, callback)Изображения глубины в ROS, представленные
sensor_msgs/Image, предоставляют расстояние от объектов до камеры, что крайне важно для таких задач, как обход препятствий, 3D-картирование и локализация. Используя информацию о глубине вместе с изображениями RGB, роботы могут лучше понимать свое 3D-окружение.С помощью YOLO ты можешь извлекать маски сегментации из изображений RGB и применять эти маски к изображениям глубины для получения точной информации о 3D-объектах, улучшая способность робота ориентироваться и взаимодействовать с окружающим пространством.
Чтобы визуализировать 3D-облака точек в ROS с помощью YOLO:
- Преобразуй сообщения
sensor_msgs/PointCloud2в массивы NumPy. - Используй YOLO для сегментации RGB-изображений.
- Примени маску сегментации к облаку точек.
Вот пример использования Open3D для визуализации:
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])Этот подход обеспечивает 3D-визуализацию сегментированных объектов, полезную для таких задач, как навигация и манипуляции в робототехнических приложениях.
- Преобразуй сообщения