Link to this sectionРуководство по быстрому старту ROS (Robot Operating System)#
В этом руководстве показано, как интегрировать Ultralytics YOLO с роботом, работающим на ROS Noetic, для запуска обнаружения объектов и сегментации в реальном времени на RGB-изображениях, изображениях глубины и облаках точек.
Переходи к настройке YOLO с ROS, а затем работай с RGB-изображениями, изображениями глубины или облаками точек.
ROS Introduction (captioned) from Open Robotics on Vimeo.
Link to this sectionЧто такое ROS?#
Robot Operating System (ROS) — это фреймворк с открытым исходным кодом, широко используемый в робототехнических исследованиях и индустрии. ROS предоставляет набор библиотек и инструментов для помощи разработчикам в создании приложений для роботов. ROS спроектирована для работы с различными робототехническими платформами, что делает ее гибким и мощным инструментом для робототехников.
Link to this sectionКлючевые особенности ROS#
-
Модульная архитектура: ROS обладает модульной архитектурой, позволяющей разработчикам создавать сложные системы путем объединения небольших, переиспользуемых компонентов, называемых нодами. Каждая нода обычно выполняет специфическую функцию, а ноды общаются друг с другом с помощью сообщений через топики или сервисы.
-
Связующее программное обеспечение (Middleware) для коммуникации: ROS предлагает надежную инфраструктуру обмена данными, поддерживающую межпроцессное взаимодействие и распределенные вычисления. Это достигается за счет модели «издатель-подписчик» для потоков данных (топиков) и модели «запрос-ответ» для вызовов сервисов.
-
Абстракция оборудования: ROS предоставляет уровень абстракции над аппаратным обеспечением, позволяя тебе писать код, независимый от устройств. Это позволяет использовать один и тот же код с разными конфигурациями оборудования, упрощая интеграцию и эксперименты.
-
Инструменты и утилиты: ROS поставляется с богатым набором инструментов и утилит для визуализации, отладки и симуляции. Например, RViz используется для визуализации данных сенсоров и состояния робота, в то время как Gazebo предоставляет мощную среду симуляции для тестирования алгоритмов и конструкций роботов.
-
Обширная экосистема: Экосистема ROS огромна и постоянно растет, с множеством пакетов, доступных для различных робототехнических задач, включая навигацию, манипуляции, восприятие и многое другое. Сообщество активно участвует в разработке и поддержке этих пакетов.
Эволюция версий ROS
С момента разработки в 2007 году ROS прошла путь через множество версий, каждая из которых привносила новые возможности и улучшения для удовлетворения растущих потребностей сообщества робототехников. Разработку ROS можно разделить на две основные серии: ROS 1 и ROS 2. В этом руководстве мы фокусируемся на версии ROS 1 с долгосрочной поддержкой (LTS), известной как ROS Noetic Ninjemys; код должен работать и с более ранними версиями.
Link to this sectionROS 1 против ROS 2#
Хотя ROS 1 предоставила прочный фундамент для разработки роботов, ROS 2 устраняет ее недостатки, предлагая:
- Производительность в реальном времени: Улучшенная поддержка систем реального времени и детерминированного поведения.
- Безопасность: Улучшенные функции безопасности для надежной и стабильной работы в различных условиях.
- Масштабируемость: Лучшая поддержка мультиагентных робототехнических систем и крупномасштабных развертываний.
- Кроссплатформенная поддержка: Расширенная совместимость с различными операционными системами помимо Linux, включая Windows и macOS.
- Гибкая коммуникация: Использование DDS для более гибкого и эффективного межпроцессного взаимодействия.
Link to this sectionСообщения и топики ROS#
В ROS коммуникация между нодами осуществляется через сообщения и топики. Сообщение — это структура данных, определяющая информацию, передаваемую между нодами, в то время как топик — это именованный канал, по которому отправляются и принимаются сообщения. Ноды могут публиковать сообщения в топик или подписываться на сообщения из топика, что позволяет им общаться друг с другом. Эта модель «издатель-подписчик» обеспечивает асинхронную связь и разделение (декаплинг) нод. Каждый сенсор или актуатор в робототехнической системе обычно публикует данные в топик, которые затем могут быть использованы другими нодами для обработки или управления. В рамках этого руководства мы сосредоточимся на сообщениях Image, Depth и PointCloud, а также на топиках камер.
Link to this sectionНастройка Ultralytics YOLO с ROS#
Это руководство было протестировано с использованием этой среды ROS, которая является форком репозитория ROSbot ROS. Эта среда включает пакет Ultralytics YOLO, Docker-контейнер для простой настройки, комплексные пакеты ROS и миры Gazebo для быстрой проверки. Она разработана для работы с Husarion ROSbot 2 PRO. Приведенные примеры кода будут работать в любой среде ROS Noetic/Melodic, как в симуляции, так и в реальности.
Link to this sectionУстановка зависимостей#
Помимо среды ROS, тебе потребуется установить следующие зависимости:
-
ROS NumPy package: Это необходимо для быстрого преобразования между сообщениями ROS Image и массивами NumPy.
pip install ros_numpy -
Пакет Ultralytics:
pip install ultralytics
Link to this sectionИспользование Ultralytics с sensor_msgs/Image в 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.
Link to this sectionПошаговое использование изображений#
Следующий фрагмент кода демонстрирует, как использовать пакет 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), состояния модели робота и различные другие типы информации, что облегчает отладку и понимание поведения твоей робототехнической системы.
Link to this sectionПубликация обнаруженных классов с помощью 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 sectionПример использования#
Представь складского робота, оснащенного камерой и моделью детекции объектов. Вместо отправки больших аннотированных изображений по сети, робот может публиковать список обнаруженных классов в виде сообщений std_msgs/String. Например, когда робот обнаруживает такие объекты, как «коробка», «паллета» и «вилочный погрузчик», он публикует эти классы в топик /ultralytics/detection/classes. Эта информация затем может быть использована центральной системой мониторинга для отслеживания инвентаря в реальном времени, оптимизации планирования пути робота, чтобы избежать препятствий, или активации конкретных действий, таких как захват обнаруженной коробки. Этот подход уменьшает полосу пропускания, необходимую для связи, и фокусируется на передаче критически важных данных.
Link to this sectionПошаговое использование 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()Link to this sectionИспользование Ultralytics с изображениями глубины ROS#
Помимо RGB-изображений, ROS поддерживает карты глубины, которые предоставляют информацию о расстоянии объектов от камеры. Карты глубины критически важны для таких робототехнических задач, как объезд препятствий, 3D-картирование и локализация.
Карта глубины — это изображение, где каждый пиксель представляет расстояние от камеры до объекта. В отличие от RGB-изображений, которые фиксируют цвет, карты глубины фиксируют пространственную информацию, позволяя роботам воспринимать 3D-структуру окружающей среды.
Изображения глубины могут быть получены с помощью различных сенсоров:
- Стереокамеры: используют две камеры для вычисления глубины на основе диспаратности изображений.
- Time-of-Flight (ToF) камеры: измеряют время, за которое свет возвращается от объекта.
- Сенсоры структурированного света: проецируют шаблон и измеряют его деформацию на поверхностях.
Link to this sectionИспользование YOLO с изображениями глубины#
В ROS изображения глубины представлены типом сообщения sensor_msgs/Image, который включает поля для кодировки, высоты, ширины и данных пикселей. Поле кодировки для изображений глубины часто использует формат вроде «16UC1», указывающий на 16-битное целое число без знака на пиксель, где каждое значение представляет расстояние до объекта. Изображения глубины обычно используются совместно с RGB-изображениями для получения более полного представления об окружающей среде.
С помощью YOLO можно извлекать и объединять информацию как из RGB, так и из изображений глубины. Например, YOLO может обнаруживать объекты на RGB-изображении, и это обнаружение можно использовать для определения соответствующих областей на карте глубины. Это позволяет извлекать точную информацию о глубине для обнаруженных объектов, улучшая способность робота понимать свое окружение в трех измерениях.
При работе с изображениями глубины важно убедиться, что RGB и глубинные изображения правильно выровнены. RGB-D камеры, такие как серия Intel RealSense, предоставляют синхронизированные RGB и глубинные изображения, что облегчает объединение информации из обоих источников. Если используются отдельные RGB и глубинные камеры, критически важно откалибровать их для обеспечения точного выравнивания.
Link to this sectionПошаговое использование изображений глубины#
В этом примере мы используем 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()Link to this sectionИспользование Ultralytics с sensor_msgs/PointCloud2 в ROS#
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.
Облако точек — это набор точек данных, определенных в трехмерной системе координат. Эти точки данных представляют внешнюю поверхность объекта или сцены, захваченную с помощью технологий 3D-сканирования. Каждая точка в облаке имеет координаты X, Y и Z, которые соответствуют ее положению в пространстве, и может также включать дополнительную информацию, такую как цвет и интенсивность.
При работе с sensor_msgs/PointCloud2 важно учитывать систему координат сенсора, с которого были получены данные облака точек. Облако точек изначально захватывается в системе координат сенсора. Ты можешь определить эту систему координат, прослушивая топик /tf_static. Однако, в зависимости от конкретных требований твоего приложения, тебе может потребоваться преобразовать облако точек в другую систему координат. Это преобразование может быть выполнено с помощью пакета tf2_ros, который предоставляет инструменты для управления координатными рамками и трансформации данных между ними.
Облака точек могут быть получены с помощью различных сенсоров:
- LIDAR (Light Detection and Ranging): использует лазерные импульсы для измерения расстояний до объектов и создания 3D-карт с высокой точностью.
- Камеры глубины: захватывают информацию о глубине для каждого пикселя, позволяя восстанавливать 3D-сцену.
- Стереокамеры: используют две или более камер для получения информации о глубине с помощью триангуляции.
- Сканеры структурированного света: проецируют известный шаблон на поверхность и измеряют его деформацию для вычисления глубины.
Link to this sectionИспользование YOLO с облаками точек#
Для интеграции YOLO с сообщениями типа sensor_msgs/PointCloud2 мы можем использовать метод, аналогичный тому, который применяется для карт глубины. Используя цветовую информацию, встроенную в облако точек, мы можем извлечь 2D-изображение, выполнить сегментацию на этом изображении с помощью YOLO, а затем применить полученную маску к трехмерным точкам, чтобы изолировать интересующий 3D-объект.
Для обработки облаков точек мы рекомендуем использовать Open3D (pip install open3d) — удобную Python-библиотеку. Open3D предоставляет надежные инструменты для управления структурами данных облаков точек, их визуализации и выполнения сложных операций. Эта библиотека может значительно упростить процесс и расширить твои возможности по манипуляции и анализу облаков точек в сочетании с сегментацией на базе YOLO.
Link to this sectionПошаговое использование облаков точек#
Импортируй необходимые библиотеки и создай модель 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). Большинство сенсоров имеют максимальное расстояние, называемое расстоянием отсечения, за пределами которого значения представлены как 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])
Link to this sectionЗаключение#
С интегрированным Ultralytics YOLO в ROS твой робот может выполнять обнаружение объектов и сегментацию на RGB-изображениях, изображениях глубины и облаках точек, превращая необработанные потоки данных с датчиков в полезные результаты восприятия. Отсюда изучи режим предсказания (Predict mode) для получения дополнительных вариантов вывода или следуй шагам проекта компьютерного зрения, чтобы перевести свое робототехническое приложение от прототипа к производству.
Link to this sectionFAQ#
Link to this sectionЧто такое Robot Operating System (ROS)?#
Robot Operating System (ROS) — это фреймворк с открытым исходным кодом, обычно используемый в робототехнике, чтобы помочь разработчикам создавать надежные приложения для роботов. Он предоставляет набор библиотек и инструментов для сборки робототехнических систем и взаимодействия с ними, упрощая разработку сложных приложений. ROS поддерживает связь между нодами с помощью сообщений через топики или сервисы.
Link to this sectionКак интегрировать Ultralytics YOLO с 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()Link to this sectionЧто такое ROS-топики и как они используются в Ultralytics YOLO?#
Топики ROS упрощают общение между узлами в сети ROS с помощью модели издатель-подписчик. Топик — это именованный канал, который узлы используют для отправки и получения сообщений асинхронно. В контексте Ultralytics YOLO ты можешь заставить узел подписаться на топик изображений, обрабатывать изображения с помощью YOLO для таких задач, как обнаружение или сегментация, и публиковать результаты в новые топики.
Например, подпишись на топик камеры и обработай входящее изображение для детекции:
rospy.Subscriber("/camera/color/image_raw", Image, callback)Link to this sectionЗачем использовать изображения глубины с Ultralytics YOLO в ROS?#
Изображения глубины в ROS, представленные типом sensor_msgs/Image, предоставляют информацию о расстоянии объектов от камеры, что критически важно для таких задач, как объезд препятствий, 3D-картирование и локализация. Используя информацию о глубине вместе с RGB-изображениями, роботы могут лучше понимать свою 3D-среду.
С помощью YOLO ты можешь извлекать маски сегментации из RGB-изображений и применять их к изображениям глубины, чтобы получить точную 3D-информацию об объектах, улучшая способность робота перемещаться и взаимодействовать с окружением.
Link to this sectionКак я могу визуализировать 3D-облака точек с YOLO в ROS?#
Чтобы визуализировать 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-визуализацию сегментированных объектов, полезную для таких задач, как навигация и манипуляции в робототехнических приложениях.