Meet YOLO26: next-gen vision AI.

Link to this sectionHướng dẫn bắt đầu nhanh với ROS (Robot Operating System)#

Hướng dẫn này chỉ cho bạn cách tích hợp Ultralytics YOLO với robot đang chạy ROS Noetic để thực hiện object detectionsegmentation theo thời gian thực trên ảnh RGB, ảnh độ sâu và đám mây điểm (point cloud).

Chuyển đến phần thiết lập YOLO với ROS, sau đó làm việc với ảnh RGB, ảnh độ sâu hoặc đám mây điểm.

ROS Introduction (captioned) from Open Robotics on Vimeo.

Link to this sectionROS là gì?#

Robot Operating System (ROS) là một framework mã nguồn mở được sử dụng rộng rãi trong nghiên cứu và công nghiệp robot. ROS cung cấp một bộ các thư viện và công cụ để hỗ trợ các nhà phát triển tạo ra các ứng dụng robot. ROS được thiết kế để làm việc với nhiều nền tảng robot khác nhau, biến nó thành một công cụ linh hoạt và mạnh mẽ cho những người làm robot.

Link to this sectionCác tính năng chính của ROS#

  1. Kiến trúc mô-đun (Modular Architecture): ROS có kiến trúc mô-đun, cho phép các nhà phát triển xây dựng các hệ thống phức tạp bằng cách kết hợp các thành phần nhỏ hơn, có thể tái sử dụng được gọi là nodes. Mỗi node thường thực hiện một chức năng cụ thể và các node giao tiếp với nhau thông qua tin nhắn trên các topics hoặc services.

  2. Middleware giao tiếp: ROS cung cấp cơ sở hạ tầng giao tiếp mạnh mẽ hỗ trợ giao tiếp giữa các tiến trình (inter-process communication) và tính toán phân tán. Điều này đạt được thông qua mô hình publish-subscribe cho các luồng dữ liệu (topics) và mô hình request-reply cho các lệnh gọi dịch vụ (service calls).

  3. Trừu tượng hóa phần cứng (Hardware Abstraction): ROS cung cấp một lớp trừu tượng hóa phần cứng, cho phép các nhà phát triển viết mã không phụ thuộc vào thiết bị. Điều này cho phép cùng một đoạn mã được sử dụng với các cấu hình phần cứng khác nhau, tạo điều kiện tích hợp và thử nghiệm dễ dàng hơn.

  4. Các công cụ và tiện ích: ROS đi kèm với một bộ sưu tập phong phú các công cụ và tiện ích để trực quan hóa, gỡ lỗi và mô phỏng. Ví dụ, RViz được sử dụng để trực quan hóa dữ liệu cảm biến và thông tin trạng thái robot, trong khi Gazebo cung cấp một môi trường mô phỏng mạnh mẽ để kiểm tra các thuật toán và thiết kế robot.

  5. Hệ sinh thái mở rộng: Hệ sinh thái ROS rất rộng lớn và không ngừng phát triển, với vô số gói (packages) có sẵn cho các ứng dụng robot khác nhau, bao gồm điều hướng, điều khiển (manipulation), nhận thức, v.v. Cộng đồng đóng góp tích cực vào việc phát triển và duy trì các gói này.

Sự phát triển của các phiên bản ROS

Kể từ khi được phát triển vào năm 2007, ROS đã phát triển qua nhiều phiên bản, mỗi phiên bản đều giới thiệu các tính năng mới và cải tiến để đáp ứng nhu cầu ngày càng tăng của cộng đồng robot. Sự phát triển của ROS có thể được chia thành hai dòng chính: ROS 1 và ROS 2. Hướng dẫn này tập trung vào phiên bản Hỗ trợ dài hạn (LTS) của ROS 1, được gọi là ROS Noetic Ninjemys, mã nguồn này cũng sẽ hoạt động với các phiên bản cũ hơn.

Link to this sectionROS 1 so với ROS 2#

Mặc dù ROS 1 đã cung cấp một nền tảng vững chắc cho sự phát triển robot, ROS 2 giải quyết những thiếu sót của nó bằng cách cung cấp:

  • Hiệu suất thời gian thực: Hỗ trợ cải thiện cho các hệ thống thời gian thực và hành vi tất định.
  • Bảo mật: Các tính năng bảo mật nâng cao để vận hành an toàn và đáng tin cậy trong các môi trường khác nhau.
  • Khả năng mở rộng: Hỗ trợ tốt hơn cho các hệ thống đa robot và các triển khai quy mô lớn.
  • Hỗ trợ đa nền tảng: Mở rộng khả năng tương thích với nhiều hệ điều hành khác nhau ngoài Linux, bao gồm Windows và macOS.
  • Giao tiếp linh hoạt: Sử dụng DDS để giao tiếp giữa các tiến trình linh hoạt và hiệu quả hơn.

Link to this sectionTin nhắn và Topics trong ROS#

Trong ROS, giao tiếp giữa các node được thực hiện thông qua tin nhắntopics. Tin nhắn là cấu trúc dữ liệu xác định thông tin trao đổi giữa các node, trong khi topic là một kênh có tên mà qua đó các tin nhắn được gửi và nhận. Các node có thể publish tin nhắn lên một topic hoặc subscribe tin nhắn từ một topic, cho phép chúng giao tiếp với nhau. Mô hình publish-subscribe này cho phép giao tiếp không đồng bộ và tách rời (decoupling) giữa các node. Mỗi cảm biến hoặc bộ chấp hành trong hệ thống robot thường publish dữ liệu lên một topic, dữ liệu này sau đó có thể được các node khác tiêu thụ để xử lý hoặc điều khiển. Đối với mục đích của hướng dẫn này, chúng tôi sẽ tập trung vào các tin nhắn Image, Depth, PointCloud và các topic máy ảnh.

Link to this sectionThiết lập Ultralytics YOLO với ROS#

Hướng dẫn này đã được thử nghiệm bằng cách sử dụng môi trường ROS này, đây là một nhánh (fork) của kho lưu trữ ROSbot ROS. Môi trường này bao gồm gói Ultralytics YOLO, một Docker container để thiết lập dễ dàng, các gói ROS toàn diện và thế giới Gazebo để kiểm tra nhanh. Nó được thiết kế để hoạt động với Husarion ROSbot 2 PRO. Các ví dụ mã được cung cấp sẽ hoạt động trong bất kỳ môi trường ROS Noetic/Melodic nào, bao gồm cả mô phỏng và thực tế.

Husarion ROSbot 2 PRO autonomous robot platform

Link to this sectionCài đặt các phụ thuộc (Dependencies)#

Ngoài môi trường ROS, bạn sẽ cần cài đặt các phụ thuộc sau:

  • Gói ROS NumPy: Đây là yêu cầu bắt buộc để chuyển đổi nhanh giữa các tin nhắn ROS Image và mảng NumPy.

    pip install ros_numpy
  • Gói Ultralytics:

    pip install ultralytics

Link to this sectionSử dụng Ultralytics với sensor_msgs/Image của 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 sectionCách sử dụng hình ảnh từng bước#

Đoạn mã sau đây minh họa cách sử dụng gói Ultralytics YOLO với ROS. Trong ví dụ này, chúng tôi subscribe vào một topic máy ảnh, xử lý hình ảnh đầu vào bằng YOLO và publish các đối tượng được phát hiện lên các topic mới để phát hiệnphân đoạn.

Đầu tiên, nhập các thư viện cần thiết và khởi tạo hai model: một cho phân đoạn và một cho phát hiện. Khởi tạo một node ROS (với tên ultralytics) để kích hoạt giao tiếp với ROS master. Để đảm bảo kết nối ổn định, chúng tôi bao gồm một khoảng dừng ngắn, cho phép node có đủ thời gian để thiết lập kết nối trước khi tiếp tục.

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)

Khởi tạo hai topic ROS: một cho phát hiện và một cho phân đoạn. Các topic này sẽ được sử dụng để publish hình ảnh đã chú thích, làm cho chúng có thể truy cập được để xử lý thêm. Việc giao tiếp giữa các node được thực hiện bằng cách sử dụng các tin nhắn 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)

Cuối cùng, tạo một subscriber lắng nghe các tin nhắn trên topic /camera/color/image_raw và gọi một hàm callback cho mỗi tin nhắn mới. Hàm callback này nhận các tin nhắn loại sensor_msgs/Image, chuyển đổi chúng thành mảng NumPy bằng ros_numpy, xử lý hình ảnh với các model YOLO đã được khởi tạo trước đó, chú thích hình ảnh và sau đó publish chúng trở lại các topic tương ứng: /ultralytics/detection/image cho phát hiện và /ultralytics/segmentation/image cho phân đoạ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()
Mã hoàn chỉnh
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()
Gỡ lỗi (Debugging)

Việc gỡ lỗi các node ROS (Robot Operating System) có thể rất khó khăn do tính chất phân tán của hệ thống. Một số công cụ có thể hỗ trợ quá trình này:

  1. rostopic echo <TOPIC-NAME>: Lệnh này cho phép bạn xem các tin nhắn được publish trên một topic cụ thể, giúp bạn kiểm tra luồng dữ liệu.
  2. rostopic list: Sử dụng lệnh này để liệt kê tất cả các topic có sẵn trong hệ thống ROS, giúp bạn có cái nhìn tổng quan về các luồng dữ liệu đang hoạt động.
  3. rqt_graph: Công cụ trực quan hóa này hiển thị đồ thị giao tiếp giữa các node, cung cấp thông tin chi tiết về cách các node được kết nối và cách chúng tương tác.
  4. Để trực quan hóa phức tạp hơn, chẳng hạn như biểu diễn 3D, bạn có thể sử dụng RViz. RViz (ROS Visualization) là một công cụ trực quan hóa 3D mạnh mẽ cho ROS. Nó cho phép bạn trực quan hóa trạng thái của robot và môi trường của nó trong thời gian thực. Với RViz, bạn có thể xem dữ liệu cảm biến (ví dụ: sensor_msgs/Image), trạng thái mô hình robot và nhiều loại thông tin khác, giúp việc gỡ lỗi và hiểu hành vi của hệ thống robot của bạn trở nên dễ dàng hơn.

Link to this sectionPublish các lớp đối tượng được phát hiện với 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 sectionVí dụ về trường hợp sử dụng#

Hãy xem xét một robot kho hàng được trang bị máy ảnh và model phát hiện đối tượng. Thay vì gửi các hình ảnh chú thích lớn qua mạng, robot có thể publish danh sách các lớp đối tượng được phát hiện dưới dạng các tin nhắn std_msgs/String. Ví dụ, khi robot phát hiện các đối tượng như "box" (thùng), "pallet" (pallet) và "forklift" (xe nâng), nó sẽ publish các lớp này lên topic /ultralytics/detection/classes. Thông tin này sau đó có thể được sử dụng bởi hệ thống giám sát trung tâm để theo dõi kho hàng theo thời gian thực, tối ưu hóa kế hoạch đường đi của robot để tránh chướng ngại vật hoặc kích hoạt các hành động cụ thể như nhặt một cái thùng được phát hiện. Cách tiếp cận này giúp giảm băng thông cần thiết cho giao tiếp và tập trung vào việc truyền tải dữ liệu quan trọng.

Link to this sectionCách sử dụng chuỗi (String) từng bước#

Ví dụ này minh họa cách sử dụng gói Ultralytics YOLO với ROS. Trong ví dụ này, chúng tôi subscribe vào một topic máy ảnh, xử lý hình ảnh đầu vào bằng YOLO và publish các đối tượng được phát hiện lên topic mới /ultralytics/detection/classes bằng cách sử dụng các tin nhắn std_msgs/String. Gói ros_numpy được sử dụng để chuyển đổi tin nhắn ROS Image thành mảng NumPy để xử lý với 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 sectionSử dụng Ultralytics với hình ảnh chiều sâu (Depth Images) của ROS#

Ngoài hình ảnh RGB, ROS hỗ trợ hình ảnh chiều sâu, cung cấp thông tin về khoảng cách của các đối tượng so với máy ảnh. Hình ảnh chiều sâu rất quan trọng đối với các ứng dụng robot như tránh chướng ngại vật, lập bản đồ 3D và định vị.

Hình ảnh chiều sâu là hình ảnh trong đó mỗi pixel đại diện cho khoảng cách từ máy ảnh đến một đối tượng. Khác với hình ảnh RGB thu màu sắc, hình ảnh chiều sâu thu thông tin không gian, cho phép robot nhận thức cấu trúc 3D của môi trường xung quanh.

Lấy hình ảnh chiều sâu

Hình ảnh chiều sâu có thể thu được bằng cách sử dụng các cảm biến khác nhau:

  1. Máy ảnh Stereo: Sử dụng hai máy ảnh để tính toán độ sâu dựa trên sự khác biệt hình ảnh (disparity).
  2. Máy ảnh Time-of-Flight (ToF): Đo thời gian ánh sáng phản xạ trở lại từ một đối tượng.
  3. Cảm biến ánh sáng cấu trúc (Structured Light Sensors): Chiếu một mẫu hình và đo sự biến dạng của nó trên các bề mặt.

Link to this sectionSử dụng YOLO với hình ảnh chiều sâu#

Trong ROS, hình ảnh chiều sâu được biểu diễn bởi loại tin nhắn sensor_msgs/Image, bao gồm các trường cho mã hóa, chiều cao, chiều rộng và dữ liệu pixel. Trường mã hóa cho hình ảnh chiều sâu thường sử dụng định dạng như "16UC1", biểu thị số nguyên không dấu 16-bit cho mỗi pixel, trong đó mỗi giá trị đại diện cho khoảng cách đến đối tượng. Hình ảnh chiều sâu thường được sử dụng kết hợp với hình ảnh RGB để cung cấp cái nhìn toàn diện hơn về môi trường.

Sử dụng YOLO, có thể trích xuất và kết hợp thông tin từ cả hình ảnh RGB và hình ảnh chiều sâu. Ví dụ, YOLO có thể phát hiện các đối tượng trong hình ảnh RGB, và phát hiện này có thể được sử dụng để xác định chính xác các vùng tương ứng trong hình ảnh chiều sâu. Điều này cho phép trích xuất thông tin độ sâu chính xác cho các đối tượng được phát hiện, nâng cao khả năng hiểu môi trường của robot theo ba chiều.

Máy ảnh RGB-D

Khi làm việc với hình ảnh chiều sâu, điều cần thiết là đảm bảo rằng hình ảnh RGB và hình ảnh chiều sâu được căn chỉnh chính xác. Các máy ảnh RGB-D, chẳng hạn như dòng Intel RealSense, cung cấp hình ảnh RGB và chiều sâu đồng bộ, giúp dễ dàng kết hợp thông tin từ cả hai nguồn. Nếu sử dụng các máy ảnh RGB và máy ảnh chiều sâu riêng biệt, việc hiệu chuẩn chúng là rất quan trọng để đảm bảo căn chỉnh chính xác.

Link to this sectionCách sử dụng chiều sâu từng bước#

Trong ví dụ này, chúng tôi sử dụng YOLO để phân đoạn một hình ảnh và áp dụng mask (mặt nạ) trích xuất được để phân đoạn đối tượng trong hình ảnh chiều sâu. Điều này cho phép chúng tôi xác định khoảng cách của từng pixel của đối tượng quan tâm so với tiêu điểm của máy ảnh. Bằng cách lấy thông tin khoảng cách này, chúng tôi có thể tính toán khoảng cách giữa máy ảnh và đối tượng cụ thể trong cảnh. Bắt đầu bằng việc nhập các thư viện cần thiết, tạo một node ROS và khởi tạo một model phân đoạn cùng với một topic 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)

Tiếp theo, xác định hàm callback xử lý tin nhắn hình ảnh chiều sâu đầu vào. Hàm này chờ đợi các tin nhắn hình ảnh chiều sâu và hình ảnh RGB, chuyển đổi chúng thành mảng NumPy và áp dụng model phân đoạn lên hình ảnh RGB. Sau đó, nó trích xuất mask phân đoạn cho từng đối tượng được phát hiện và tính toán khoảng cách trung bình của đối tượng so với máy ảnh bằng cách sử dụng hình ảnh chiều sâu. Hầu hết các cảm biến đều có khoảng cách tối đa, được gọi là clip distance, mà ngoài khoảng cách đó, các giá trị được biểu diễn là inf (np.inf). Trước khi xử lý, điều quan trọng là phải lọc bỏ các giá trị null này và gán cho chúng giá trị 0. Cuối cùng, nó publish các đối tượng được phát hiện cùng với khoảng cách trung bình của chúng lên topic /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()
Mã hoàn chỉnh
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 sectionSử dụng Ultralytics với sensor_msgs/PointCloud2 của 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.

Đám mây điểm là một tập hợp các điểm dữ liệu được xác định trong một hệ tọa độ ba chiều. Các điểm dữ liệu này đại diện cho bề mặt bên ngoài của một đối tượng hoặc một cảnh, được thu thập thông qua các công nghệ quét 3D. Mỗi điểm trong đám mây có các tọa độ X, YZ, tương ứng với vị trí của nó trong không gian, và cũng có thể bao gồm thông tin bổ sung như màu sắc và cường độ.

Khung tham chiếu

Khi làm việc với sensor_msgs/PointCloud2, điều cần thiết là phải xem xét khung tham chiếu của cảm biến mà từ đó dữ liệu đám mây điểm được thu thập. Đám mây điểm ban đầu được thu thập trong khung tham chiếu của cảm biến. Bạn có thể xác định khung tham chiếu này bằng cách lắng nghe topic /tf_static. Tuy nhiên, tùy thuộc vào yêu cầu ứng dụng cụ thể của bạn, bạn có thể cần chuyển đổi đám mây điểm sang một khung tham chiếu khác. Phép biến đổi này có thể đạt được bằng cách sử dụng gói tf2_ros, cung cấp các công cụ để quản lý các khung tọa độ và biến đổi dữ liệu giữa chúng.

Lấy đám mây điểm

Đám mây điểm có thể thu được bằng cách sử dụng các cảm biến khác nhau:

  1. LIDAR (Light Detection and Ranging): Sử dụng xung laser để đo khoảng cách đến các đối tượng và tạo bản đồ 3D chính xác cao.
  2. Máy ảnh chiều sâu: Thu thông tin chiều sâu cho mỗi pixel, cho phép tái tạo 3D của cảnh.
  3. Máy ảnh Stereo: Sử dụng hai hoặc nhiều máy ảnh để lấy thông tin chiều sâu thông qua phép đạc tam giác (triangulation).
  4. Máy quét ánh sáng cấu trúc (Structured Light Scanners): Chiếu một mẫu hình đã biết lên bề mặt và đo sự biến dạng để tính toán chiều sâu.

Link to this sectionSử dụng YOLO với đám mây điểm#

Để tích hợp YOLO với các tin nhắn loại sensor_msgs/PointCloud2, chúng ta có thể áp dụng phương pháp tương tự như phương pháp dùng cho bản đồ chiều sâu. Bằng cách tận dụng thông tin màu sắc nhúng trong đám mây điểm, chúng ta có thể trích xuất một hình ảnh 2D, thực hiện phân đoạn trên hình ảnh này bằng YOLO, và sau đó áp dụng mask kết quả vào các điểm ba chiều để tách biệt đối tượng 3D quan tâm.

Để xử lý đám mây điểm, chúng tôi khuyên dùng Open3D (pip install open3d), một thư viện Python thân thiện với người dùng. Open3D cung cấp các công cụ mạnh mẽ để quản lý cấu trúc dữ liệu đám mây điểm, trực quan hóa chúng và thực hiện các thao tác phức tạp một cách liền mạch. Thư viện này có thể đơn giản hóa đáng kể quy trình và nâng cao khả năng thao tác cũng như phân tích đám mây điểm của chúng ta khi kết hợp với phân đoạn dựa trên YOLO.

Link to this sectionCách sử dụng đám mây điểm từng bước#

Nhập các thư viện cần thiết và khởi tạo model YOLO để phân đoạn.

import time

import rospy

from ultralytics import YOLO

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

Tạo hàm pointcloud2_to_array, biến đổi tin nhắn sensor_msgs/PointCloud2 thành hai mảng NumPy. Các tin nhắn sensor_msgs/PointCloud2 chứa n điểm dựa trên widthheight của hình ảnh thu được. Ví dụ, hình ảnh 480 x 640 sẽ có 307,200 điểm. Mỗi điểm bao gồm ba tọa độ không gian (xyz) và màu sắc tương ứng ở định dạng RGB. Đây có thể được coi là hai kênh thông tin riêng biệt.

Hàm trả về các tọa độ xyz và các giá trị RGB theo định dạng độ phân giải máy ảnh gốc (width x height). Hầu hết các cảm biến đều có khoảng cách tối đa, được gọi là clip distance, mà ngoài khoảng cách đó, các giá trị được biểu diễn là inf (np.inf). Trước khi xử lý, điều quan trọng là phải lọc bỏ các giá trị null này và gán cho chúng giá trị 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

Tiếp theo, subscribe vào topic /camera/depth/points để nhận tin nhắn đám mây điểm và chuyển đổi tin nhắn sensor_msgs/PointCloud2 thành các mảng NumPy chứa tọa độ XYZ và giá trị RGB (sử dụng hàm pointcloud2_to_array). Xử lý hình ảnh RGB bằng model YOLO để trích xuất các đối tượng đã phân đoạn. Đối với mỗi đối tượng được phát hiện, trích xuất mask phân đoạn và áp dụng nó vào cả hình ảnh RGB và tọa độ XYZ để tách biệt đối tượng trong không gian 3D.

Việc xử lý mask rất đơn giản vì nó bao gồm các giá trị nhị phân, với 1 biểu thị sự hiện diện của đối tượng và 0 biểu thị sự vắng mặt. Để áp dụng mask, chỉ cần nhân các kênh gốc với mask. Thao tác này tách biệt hiệu quả đối tượng quan tâm trong hình ảnh. Cuối cùng, tạo một đối tượng đám mây điểm Open3D và trực quan hóa đối tượng đã phân đoạn trong không gian 3D với màu sắc liên quan.

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])
Mã hoàn chỉnh
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 sectionKết luận#

Với Ultralytics YOLO được tích hợp vào ROS, robot của bạn có thể thực hiện object detectionsegmentation trên ảnh RGB, ảnh độ sâu và đám mây điểm, chuyển đổi luồng dữ liệu cảm biến thô thành nhận thức có thể hành động. Từ đây, hãy khám phá Predict mode để biết thêm các tùy chọn suy luận (inference), hoặc làm theo các bước của một dự án thị giác máy tính để đưa ứng dụng robot của bạn từ giai đoạn nguyên mẫu sang sản xuất.

Link to this sectionCâu hỏi thường gặp#

Link to this sectionRobot Operating System (ROS) là gì?#

Robot Operating System (ROS) là một framework mã nguồn mở thường được sử dụng trong robot học để giúp các nhà phát triển tạo ra các ứng dụng robot mạnh mẽ. Nó cung cấp một bộ sưu tập các thư viện và công cụ để xây dựng và giao tiếp với các hệ thống robot, cho phép phát triển các ứng dụng phức tạp dễ dàng hơn. ROS hỗ trợ giao tiếp giữa các node bằng cách sử dụng các tin nhắn trên topics hoặc services.

Link to this sectionLàm cách nào để tích hợp Ultralytics YOLO với ROS để phát hiện đối tượng thời gian thực?#

Việc tích hợp Ultralytics YOLO với ROS bao gồm việc thiết lập môi trường ROS và sử dụng YOLO để xử lý dữ liệu cảm biến. Bắt đầu bằng cách cài đặt các phụ thuộc cần thiết như ros_numpy và Ultralytics YOLO:

pip install ros_numpy ultralytics

Tiếp theo, hãy tạo một ROS node và đăng ký (subscribe) vào một topic hình ảnh để xử lý dữ liệu đầu vào cho tác vụ object detection. Sau đây là một ví dụ tối giản:

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 sectionROS topics là gì và chúng được sử dụng như thế nào trong Ultralytics YOLO?#

Các topic ROS tạo điều kiện giao tiếp giữa các node trong một mạng ROS bằng cách sử dụng mô hình publish-subscribe. Một topic là một kênh có tên mà các node sử dụng để gửi và nhận tin nhắn không đồng bộ. Trong ngữ cảnh của Ultralytics YOLO, bạn có thể tạo một node để đăng ký vào một topic hình ảnh, xử lý hình ảnh bằng YOLO cho các tác vụ như detection hoặc segmentation và xuất bản kết quả ra các topic mới.

Ví dụ, subscribe vào một topic máy ảnh và xử lý hình ảnh đầu vào để phát hiện:

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

Link to this sectionTại sao nên sử dụng hình ảnh chiều sâu với Ultralytics YOLO trong ROS?#

Hình ảnh chiều sâu trong ROS, được biểu diễn bởi sensor_msgs/Image, cung cấp khoảng cách của các đối tượng so với máy ảnh, rất quan trọng cho các tác vụ như tránh chướng ngại vật, lập bản đồ 3D và định vị. Bằng cách sử dụng thông tin chiều sâu cùng với hình ảnh RGB, robot có thể hiểu rõ hơn về môi trường 3D của chúng.

Với YOLO, bạn có thể trích xuất mask phân đoạn từ hình ảnh RGB và áp dụng các mask này vào hình ảnh chiều sâu để có được thông tin đối tượng 3D chính xác, cải thiện khả năng điều hướng và tương tác với môi trường xung quanh của robot.

Link to this sectionLàm cách nào tôi có thể trực quan hóa đám mây điểm 3D với YOLO trong ROS?#

Để trực quan hóa đám mây điểm 3D trong ROS với YOLO:

  1. Chuyển đổi các tin nhắn sensor_msgs/PointCloud2 thành các mảng NumPy.
  2. Sử dụng YOLO để phân đoạn hình ảnh RGB.
  3. Áp dụng mask phân đoạn vào đám mây điểm.

Đây là một ví dụ sử dụng Open3D để trực quan hóa:

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])

Phương pháp này cung cấp hình ảnh trực quan 3D về các đối tượng đã được phân đoạn, hữu ích cho các tác vụ như điều hướng và thao tác trong các ứng dụng robot.

Bình luận