Hướng dẫn khởi động nhanh ROS (Robot Operating System)
Giới thiệu về ROS (có phụ đề) từ Open Robotics trên Vimeo.
ROS 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 tập hợp các thư viện và công cụ để giúp các nhà phát triển tạo ra các ứng dụng robot. ROS được thiết kế để hoạt động với nhiều nền tảng robot khác nhau, làm cho nó trở thành một công cụ linh hoạt và mạnh mẽ cho các nhà nghiên cứu robot.
Các tính năng chính của ROS
-
Kiến trúc dạng Mô-đun: ROS có kiến trúc dạng 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à các nút (nodes). Mỗi nút thường thực hiện một chức năng cụ thể và các nút giao tiếp với nhau bằng cách sử dụng tin nhắn qua các chủ đề (topics) hoặc các dịch vụ (services).
-
Phần Mềm Trung Gian Giao Tiếp: ROS cung cấp một cơ sở hạ tầng giao tiếp mạnh mẽ hỗ trợ giao tiếp giữa các tiến trình và điện 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ụ.
-
Trừu tượng hóa phần cứng: ROS cung cấp một layer trừu tượng hóa trên phần cứng, cho phép các nhà phát triển viết code không phụ thuộc vào thiết bị. Điều này cho phép sử dụng cùng một code với các thiết lập 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.
-
Công cụ và Tiện ích: ROS đi kèm với một bộ công cụ và tiện ích phong phú để 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.
-
Hệ sinh thái mở rộng: Hệ sinh thái ROS rất lớn và không ngừng phát triển, với nhiều gói có sẵn cho các ứng dụng robot khác nhau, bao gồm điều hướng, điều khiển, nhận thức và hơn thế nữa. Cộng đồng tích cực đóng góp vào việc phát triển và bảo 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 giới thiệu các tính năng và cải tiến mới để đá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 phân loại thành hai series 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, code cũng sẽ hoạt động với các phiên bản trước đó.
ROS 1 so với ROS 2
Trong khi ROS 1 cung cấp nền tảng vững chắc cho phát triển robot, ROS 2 giải quyết các thiếu sót của nó bằng cách cung cấp:
- Hiệu suất theo thời gian thực: Cải thiện hỗ trợ cho các hệ thống thời gian thực và hành vi tất định.
- An ninh: Các tính năng an ninh nâng cao để vận hành an toàn và đáng tin cậy trong nhiều 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à 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.
Các Topics và Messages của ROS
Trong ROS, giao tiếp giữa các node được tạo điều kiện thông qua messages và topics. Message là một cấu trúc dữ liệu xác định thông tin được trao đổi giữa các node, trong khi topic là một kênh được đặt tên mà qua đó các message được gửi và nhận. Các node có thể publish message đến một topic hoặc subscribe message 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à phân tách giữa các node. Mỗi cảm biến hoặc bộ truyền động trong một hệ thống robot thường publish dữ liệu đến một topic, sau đó có thể được các node khác sử dụng để xử lý hoặc điều khiển. Vì mục đích của hướng dẫn này, chúng ta sẽ tập trung vào các message Image, Depth và PointCloud và các topic camera.
Thiết lập Ultralytics YOLO với ROS
Hướng dẫn này đã được thử nghiệm bằng môi trường ROS này, đây là một nhánh của kho lưu trữ ROSbot ROS. Môi trường này bao gồm gói Ultralytics YOLO, một vùng chứa Docker để dễ dàng thiết lập, các gói ROS toàn diện và thế giới Gazebo để thử nghiệm nhanh chóng. 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 mọi môi trường ROS Noetic/Melodic, bao gồm cả mô phỏng và thế giới thực.
Cài đặt các Phần phụ thuộc
Ngoài môi trường ROS, bạn sẽ cần cài đặt các phần phụ thuộc sau:
-
Gói ROS Numpy: Điều này là cần thiết để chuyển đổi nhanh chóng giữa các tin nhắn ROS Image và mảng numpy.
pip install ros_numpy
-
Gói Ultralytics:
pip install ultralytics
Sử dụng Ultralytics với ROS sensor_msgs/Image
Hàm sensor_msgs/Image
loại thông báo thường được sử dụng trong ROS để biểu diễn dữ liệu hình ảnh. Nó chứa các trường cho mã hóa, chiều cao, chiều rộng và dữ liệu pixel, phù hợp để truyền hình ảnh được chụp bởi máy ảnh hoặc các cảm biến khác. Image messages được sử dụng rộng rãi trong các ứng dụng robot cho các tác vụ như nhận dạng hình ảnh, phát hiện đối tượng, và điều hướng.
Hướng dẫn 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 ta đăng ký một chủ đề camera, xử lý hình ảnh đầu vào bằng YOLO và xuất bản các đối tượng được phát hiện vào các chủ đề mới cho phát hiện và phân đoạn.
Đầu tiên, hãy nhập các thư viện cần thiết và khởi tạo hai mô hình: một cho phân đoạn và một cái cho phát hiện. Khởi tạo một ROS node (với tên ultralytics
) để cho phép 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 nút 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("yolo11m.pt")
segmentation_model = YOLO("yolo11m-seg.pt")
rospy.init_node("ultralytics")
time.sleep(1)
Khởi tạo hai chủ đề ROS: một cho phát hiện và một cái cho phân đoạn. Các topic này sẽ được sử dụng để xuất bản hình ảnh được chú thích, giúp chúng có thể truy cập để xử lý thêm. Giao tiếp giữa các node được tạo điều kiện bằng cách sử dụng sensor_msgs/Image
thông báo.
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 /camera/color/image_raw
topic 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 thuộc loại sensor_msgs/Image
, chuyển đổi chúng thành một mảng numpy bằng cách sử dụng ros_numpy
, xử lý hình ảnh bằng các mô hình YOLO đã được khởi tạo trước đó, chú thích hình ảnh và sau đó xuất bản chúng trở lại các topic tương ứng: /ultralytics/detection/image
cho việc 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("yolo11m.pt")
segmentation_model = YOLO("yolo11m-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
Gỡ lỗi các node ROS (Robot Operating System) có thể gặp nhiều 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:
rostopic echo <TOPIC-NAME>
: Lệnh này cho phép bạn xem các tin nhắn được xuất bản trên một chủ đề cụ thể, giúp bạn kiểm tra luồng dữ liệu.rostopic list
: Sử dụng lệnh này để liệt kê tất cả các chủ đề có sẵn trong hệ thống ROS, cung cấp cho bạn cái nhìn tổng quan về các luồng dữ liệu đang hoạt động.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.- Đối với các hình ảnh trực quan 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ụ:
sensors_msgs/Image
), trạng thái mô hình robot và nhiều loại thông tin khác, giúp bạn gỡ lỗi và hiểu hành vi của hệ thống robot dễ dàng hơn.
Xuất bản các lớp được phát hiện với std_msgs/String
Các tin nhắn ROS tiêu chuẩn cũng bao gồm std_msgs/String
thông báo. Trong nhiều ứng dụng, không cần thiết phải xuất bản lại toàn bộ hình ảnh đã chú thích; thay vào đó, chỉ cần các lớp có trong tầm nhìn của robot. Ví dụ sau đây minh họa cách sử dụng std_msgs/String
thông báo để tái xuất bản các lớp đã phát hiện trên /ultralytics/detection/classes
topic. Những tin nhắn này nhẹ hơn và cung cấp thông tin thiết yếu, làm cho chúng có giá trị cho các ứng dụng khác nhau.
Ví dụ về Trường hợp Sử dụng
Hãy xem xét một robot kho hàng được trang bị camera và đối tượng mô hình phát hiện. Thay vì gửi hình ảnh được chú thích lớn qua mạng, robot có thể xuất bản một danh sách các lớp được phát hiện dưới dạng std_msgs/String
thông báo. Ví dụ: khi robot phát hiện các đối tượng như "box", "pallet" và "forklift", nó sẽ xuất bản các lớp này vào /ultralytics/detection/classes
topic. Thông tin này sau đó có thể được sử dụng bởi một hệ thống giám sát trung tâm để theo dõi hàng tồn kho trong thời gian thực, tối ưu hóa việc lập 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 hộp được phát hiện. Cách tiếp cận này làm giảm băng thông cần thiết cho giao tiếp và tập trung vào việc truyền dữ liệu quan trọng.
Hướng dẫn Sử dụng Từng Bước dạng Chuỗi
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 ta đăng ký một chủ đề camera, xử lý hình ảnh đến bằng YOLO và xuất bản các đối tượng được phát hiện sang chủ đề mới /ultralytics/detection/classes
bằng cách sử dụng std_msgs/String
thông báo. Các ros_numpy
gói được sử dụng để chuyển đổi thông điệp ROS Image thành một 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("yolo11m.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()
Sử dụng Ultralytics với Ảnh Độ sâu ROS
Ngoài hình ảnh RGB, ROS hỗ trợ hình ảnh độ sâu, cung cấp thông tin về khoảng cách của các vật thể từ camera. Hình ảnh độ 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ị.
Ảnh chiều sâu (depth image) là ảnh mà mỗi pixel đại diện cho khoảng cách từ camera đến một vật thể. Không giống như ảnh RGB ghi lại màu sắc, ảnh chiều sâu ghi lại thông tin không gian, cho phép robot nhận biết cấu trúc 3D của môi trường xung quanh.
Thu Thập Ảnh Độ Sâu
Ảnh chiều sâu có thể được thu thập bằng nhiều loại cảm biến khác nhau:
- Camera Stereo: Sử dụng hai camera để tính toán độ sâu dựa trên sự khác biệt hình ảnh.
- Camera Time-of-Flight (ToF): Đo thời gian ánh sáng đi để trở lại từ một đối tượng.
- Cảm biến ánh sáng cấu trúc: Chiếu một mẫu và đo biến dạng của nó trên các bề mặt.
Sử dụng YOLO với Ảnh Độ Sâu
Trong ROS, ảnh độ sâu được biểu diễn bởi sensor_msgs/Image
loại thông báo, 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 ảnh độ sâu thường sử dụng định dạng như "16UC1", biểu thị một số nguyên không dấu 16 bit trên mỗi pixel, trong đó mỗi giá trị biểu thị khoảng cách đến đối tượng. Ảnh độ sâu thường được sử dụng cùng với ả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ả ảnh RGB và ảnh độ sâu. Ví dụ: YOLO có thể phát hiện các đối tượng trong ảnh RGB và thông tin phát hiện này có thể được sử dụng để xác định các vùng tương ứng trong ảnh độ 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 của robot trong việc hiểu môi trường ba chiều.
Camera RGB-D
Khi làm việc với ảnh chiều sâu, điều cần thiết là đảm bảo rằng ảnh RGB và ảnh chiều sâu được căn chỉnh chính xác. Các camera RGB-D, chẳng hạn như dòng Intel RealSense, cung cấp ảnh RGB và ảnh chiều sâu được đồng bộ hóa, giúp bạn dễ dàng kết hợp thông tin từ cả hai nguồn. Nếu sử dụng camera RGB và camera chiều sâu riêng biệt, điều quan trọng là phải hiệu chỉnh chúng để đảm bảo căn chỉnh chính xác.
Hướng dẫn từng bước sử dụng ảnh chiều sâu
Trong ví dụ này, chúng ta sử dụng YOLO để phân đoạn một hình ảnh và áp dụng mặt nạ đã trích xuất để phân đoạn đối tượng trong hình ảnh độ sâu. Điều này cho phép chúng ta xác định khoảng cách của mỗi pixel của đối tượng quan tâm từ tâm tiêu cự của máy ảnh. Bằng cách thu thập thông tin khoảng cách này, chúng ta có thể tính khoảng cách giữa máy ảnh và đối tượng cụ thể trong cảnh. Bắt đầu bằng cách nhập các thư viện cần thiết, tạo một nút ROS và khởi tạo một mô hình phân đoạn và một chủ đề 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("yolo11m-seg.pt")
classes_pub = rospy.Publisher("/ultralytics/detection/distance", String, queue_size=5)
Tiếp theo, xác định một hàm callback để xử lý thông báo hình ảnh độ sâu đến. Hàm này đợi tin nhắn hình ảnh độ sâu và hình ảnh RGB, chuyển đổi chúng thành mảng numpy và áp dụng mô hình phân đoạn cho hình ảnh RGB. Sau đó, nó trích xuất mặt nạ phân đoạn cho mỗi đối tượng được phát hiện và tính khoảng cách trung bình của đối tượng từ máy ảnh bằng cách sử dụng hình ảnh độ 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à khoảng cách cắt, vượt quá giá trị này được biểu thị là vô cực (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 một giá trị là 0
. Cuối cùng, nó công bố 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 đến /ultralytics/detection/distance
topic.
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("yolo11m-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()
Sử dụng Ultralytics với ROS sensor_msgs/PointCloud2
Hàm sensor_msgs/PointCloud2
loại thông báo là một cấu trúc dữ liệu được sử dụng trong ROS để biểu diễn dữ liệu đám mây điểm 3D. Loại tin nhắn này là không thể thiếu đối với các ứng dụng robot, cho phép các tác vụ như lập bản đồ 3D, nhận dạng đối tượng và bản địa hóa.
Đá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 chụp thông qua các công nghệ quét 3D. Mỗi điểm trong đám mây có X
, Y
, và Z
tọa độ, 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 hệ quy chiếu của cảm biến mà từ đó dữ liệu đám mây điểm (point cloud) được thu thập. Đám mây điểm ban đầu được chụp trong hệ quy chiếu của cảm biến. Bạn có thể xác định hệ quy chiếu này bằng cách lắng nghe /tf_static
topic. 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 thành một hệ quy chiếu khác. Sự chuyển đổi này có thể đạt được bằng cách sử dụng tf2_ros
gói, cung cấp các công cụ để quản lý các khung tọa độ và chuyển đổi dữ liệu giữa chúng.
Thu Thập Đám Mây Điểm
Đám mây điểm có thể thu được bằng nhiều loại cảm biến khác nhau:
- LIDAR (Phát Hiện và Đo Khoảng Cách bằng Ánh Sáng): Sử dụng xung laser để đo khoảng cách đến các vật thể và tạo bản đồ 3D có độ chính xác cao.
- Camera Chiều Sâu: Thu thập thông tin độ sâu cho mỗi pixel, cho phép tái tạo 3D cảnh.
- Camera lập thể: Sử dụng hai hoặc nhiều camera để thu thập thông tin độ sâu thông qua phép đo tam giác.
- Máy quét ánh sáng cấu trúc: Chiếu một mẫu đã biết lên bề mặt và đo độ biến dạng để tính độ sâu.
Sử dụng YOLO với Đám Mây Điểm
Để tích hợp YOLO với sensor_msgs/PointCloud2
các loại tin nhắn, chúng ta có thể sử dụng một phương pháp tương tự như phương pháp được sử dụng cho bản đồ độ sâu. Bằng cách tận dụng thông tin màu được nhúng trong đám mây điểm, chúng ta có thể trích xuất hình ảnh 2D, thực hiện phân đoạn trên hình ảnh này bằng YOLO, sau đó áp dụng mặt nạ thu được cho các điểm ba chiều để cô lập đối tượng 3D mà ta quan tâm.
Để xử lý đám mây điểm, chúng tôi khuyên bạn nên sử 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 hoạt động 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à tăng cường khả năng thao tác và phân tích đám mây điểm kết hợp với phân đoạn dựa trên YOLO.
Hướng dẫn từng bước sử dụng Đám mây điểm
Nhập các thư viện cần thiết và khởi tạo mô hình YOLO để phân đoạn.
import time
import rospy
from ultralytics import YOLO
rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolo11m-seg.pt")
Tạo một hàm pointcloud2_to_array
, chuyển đổi một sensor_msgs/PointCloud2
thông báo thành hai mảng numpy. Các sensor_msgs/PointCloud2
thông báo chứa n
các điểm dựa trên width
và height
của hình ảnh thu được. Ví dụ: 480 x 640
image sẽ có 307,200
điểm. Mỗi điểm bao gồm ba tọa độ không gian (xyz
) và màu tương ứng trong RGB
định dạng. Chúng có thể được coi là hai kênh thông tin riêng biệt.
Hàm trả về xyz
tọa độ và RGB
giá trị theo định dạng độ phân giải gốc của camera (width x height
). Hầu hết các cảm biến có khoảng cách tối đa, được gọi là khoảng cách clip, vượt quá khoảng cách này, 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 một giá trị là 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, đăng ký vào /camera/depth/points
topic để nhận tin nhắn đám mây điểm và chuyển đổi sensor_msgs/PointCloud2
thông báo thành mảng numpy chứa tọa độ XYZ và giá trị RGB (sử dụng pointcloud2_to_array
function). Xử lý ảnh RGB bằng mô hình 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, hãy trích xuất mặt nạ phân đoạn và áp dụng nó cho cả ảnh RGB và tọa độ XYZ để cô lập đối tượng trong không gian 3D.
Việc xử lý mặt nạ rất đơn giản vì nó bao gồm các giá trị nhị phân, với 1
cho biết sự hiện diện của đối tượng và 0
cho biết sự vắng mặt. Để áp dụng mặt nạ, chỉ cần nhân các kênh ban đầu với mặt nạ. Thao tác này giúp cô lập 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 các màu 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("yolo11m-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])
Câu hỏi thường gặp
Robot Operating System (ROS) là gì?
Robot Operating System (ROS) là một framework mã nguồn mở thường được sử dụng trong lĩnh vực robot để 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 tập hợ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 tin nhắn qua topics hoặc services.
Làm cách nào để tích hợp Ultralytics YOLO với ROS để phát hiện đối tượng theo thời gian thự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 dependency cần thiết như ros_numpy
và Ultralytics YOLO:
pip install ros_numpy ultralytics
Tiếp theo, tạo một nút ROS và đăng ký một topic hình ảnh để xử lý dữ liệu đến. Dưới đây là một ví dụ tối thiểu:
import ros_numpy
import rospy
from sensor_msgs.msg import Image
from ultralytics import YOLO
detection_model = YOLO("yolo11m.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()
Các ROS topic 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ạng ROS bằng cách sử dụng mô hình publish-subscribe. Một topic là một kênh được đặt tên mà các node sử dụng để gửi và nhận tin nhắn không đồng bộ. Trong bối cảnh của Ultralytics YOLO, bạn có thể tạo một node để đăng ký vào một image topic, xử lý hình ảnh bằng YOLO cho các tác vụ như phát hiện hoặc phân đoạn, và publish kết quả đến các topic mới.
Ví dụ: đăng ký một chủ đề camera và xử lý hình ảnh đến để phát hiện:
rospy.Subscriber("/camera/color/image_raw", Image, callback)
Tại sao nên sử dụng ảnh chiều sâu (depth images) với Ultralytics YOLO trong ROS?
Ảnh chiều sâu trong ROS, được biểu diễn bằng sensor_msgs/Image
, cung cấp khoảng cách của các vật thể từ camera, rất quan trọng đối với 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 độ 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 mặt nạ phân đoạn từ hình ảnh RGB và áp dụng các mặt nạ này vào hình ảnh độ sâu để có được thông tin vật thể 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.
Làm thế nào để 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:
- Chuyển đổi
sensor_msgs/PointCloud2
thông báo thành mảng numpy. - Sử dụng YOLO để phân đoạn ảnh RGB.
- Áp dụng mặt nạ 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 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("yolo11m-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 của các đối tượng đã phân đoạn, hữu ích cho các tác vụ như điều hướng và điều khiển trong các ứng dụng robot.