انتقل إلى المحتوى

دليل التشغيل السريع ROS (نظام تشغيل الروبوت)

مقدمة ROS (مصحوبة بتعليق) من Open Robotics على Vimeo.

ما هو روس روس؟

نظام تشغيل الروبوتات (ROS ) هو إطار عمل مفتوح المصدر يُستخدم على نطاق واسع في أبحاث الروبوتات والصناعة. يوفر ROS مجموعة من المكتبات والأدوات لمساعدة المطورين على إنشاء تطبيقات الروبوتات. تم تصميم ROS للعمل مع مختلف المنصات الروبوتية، مما يجعله أداة مرنة وقوية لمطوري الروبوتات.

الميزات الرئيسية ل ROS

  1. بنية معيارية: يحتوي ROS على بنية معيارية تسمح للمطورين ببناء أنظمة معقدة من خلال الجمع بين مكونات أصغر قابلة لإعادة الاستخدام تسمى العقد. تؤدي كل عقدة عادةً وظيفة محددة، وتتواصل العقد مع بعضها البعض باستخدام الرسائل عبر المواضيع أو الخدمات.

  2. البرمجيات الوسيطة للاتصالات: يوفر ROS بنية تحتية قوية للاتصالات تدعم الاتصال بين العمليات والحوسبة الموزعة. ويتحقق ذلك من خلال نموذج النشر والاشتراك لتدفقات البيانات (المواضيع) ونموذج الطلب والرد لاستدعاءات الخدمة.

  3. تجريد الأجهزة: يوفر ROS طبقة من التجريد على الأجهزة، مما يمكّن المطورين من كتابة شيفرة برمجية لا تعتمد على الجهاز. يسمح ذلك باستخدام نفس الكود البرمجي مع إعدادات أجهزة مختلفة، مما يسهل التكامل والتجريب.

  4. الأدوات والأدوات المساعدة: يأتي ROS مع مجموعة غنية من الأدوات والأدوات المساعدة للتصور والتصحيح والمحاكاة. على سبيل المثال، تُستخدم أداة RViz لتصور بيانات المستشعرات ومعلومات حالة الروبوت، بينما توفر أداة Gazebo بيئة محاكاة قوية لاختبار الخوارزميات وتصميمات الروبوت.

  5. نظام بيئي واسع النطاق: نظام ROS البيئي واسع ومتنامي باستمرار، مع وجود العديد من الحزم المتاحة للتطبيقات الروبوتية المختلفة، بما في ذلك الملاحة والمعالجة والإدراك وغيرها. يساهم المجتمع بنشاط في تطوير هذه الحزم وصيانتها.

تطور إصدارات ROS

منذ تطويره في عام 2007، تطورت ROS من خلال إصدارات متعددة، كل منها يقدم ميزات وتحسينات جديدة لتلبية الاحتياجات المتزايدة لمجتمع الروبوتات. يمكن تصنيف تطوير ROS إلى سلسلتين رئيسيتين: ROS 1 و ROS 2. يركز هذا الدليل على إصدار الدعم طويل الأمد (LTS) من ROS 1، المعروف باسم ROS Noetic Ninjemys، وينبغي أن يعمل الكود أيضاً مع الإصدارات السابقة.

روس 1 مقابل روس 2

بينما قدم ROS 1 أساسا متينا للتطوير الروبوتي ، يعالج ROS 2 أوجه القصور فيه من خلال تقديم:

  • الأداء في الوقت الحقيقي: دعم محسّن لأنظمة الوقت الحقيقي والسلوك الحتمي.
  • الأمان: ميزات أمان محسّنة للتشغيل الآمن والموثوق في بيئات مختلفة.
  • قابلية التوسع: دعم أفضل للأنظمة متعددة الروبوتات وعمليات النشر واسعة النطاق.
  • دعم متعدد المنصات: توافق موسع مع مختلف أنظمة التشغيل بخلاف لينكس، بما في ذلك ويندوز وماك أو إس.
  • اتصال مرن: استخدام DDS لتواصل أكثر مرونة وفعالية بين العمليات.

رسائل ومواضيع ROS

في ROS، يتم تسهيل الاتصال بين العقد من خلال الرسائل والمواضيع. الرسالة هي بنية بيانات تحدد المعلومات التي يتم تبادلها بين العقد، في حين أن الموضوع هو قناة مسماة يتم إرسال واستقبال الرسائل عبرها. يمكن للعقد نشر رسائل إلى موضوع أو الاشتراك في رسائل من موضوع، مما يتيح لها التواصل مع بعضها البعض. يسمح نموذج النشر والاشتراك هذا بالاتصال غير المتزامن والفصل بين العقد. يقوم كل مستشعر أو مشغل في النظام الروبوتي عادةً بنشر البيانات إلى موضوع، والتي يمكن أن تستهلكها العقد الأخرى للمعالجة أو التحكم. لغرض هذا الدليل، سنركز على رسائل الصور والعمق والنقاط السحابية وموضوعات الكاميرا.

اعداد Ultralytics YOLO مع روس

تم اختبار هذا الدليل باستخدام بيئة ROS هذه، وهي شوكة من مستودع ROSbot ROS. تتضمن هذه البيئة الحزمة Ultralytics YOLO ، وحاوية Docker لسهولة الإعداد، وحزم ROS الشاملة، وعوالم Gazebo للاختبار السريع. وهي مصممة للعمل مع Husarion ROSbot 2 PRO. ستعمل أمثلة التعليمات البرمجية المقدمة في أي بيئة ROS Noetic/Melodic، بما في ذلك المحاكاة والعالم الحقيقي.

هوساريون روزبوت 2 برو

تثبيت التبعيات

بصرف النظر عن بيئة ROS ، ستحتاج إلى تثبيت التبعيات التالية:

  • حزمة ROS Numpy: هذا مطلوب للتحويل السريع بين رسائل صور ROS ومصفوفات numpy.

    pip install ros_numpy
    
  • Ultralytics الحزمة:

    pip install ultralytics
    

استخدام Ultralytics مع روس sensor_msgs/Image

ال sensor_msgs/Image نوع الرسالة 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.

الاكتشاف والتجزئة في ROS Gazebo

استخدام الصورة خطوة بخطوة

يوضح مقتطف الكود التالي كيفية استخدام الحزمة Ultralytics YOLO مع ROS. في هذا المثال، نشترك في موضوع الكاميرا، ونعالج الصورة الواردة باستخدام YOLO ، وننشر الكائنات المكتشفة إلى مواضيع جديدة للكشف والتجزئة.

أولاً، استورد المكتبات اللازمة وأنشئ نموذجين: أحدهما ل التجزئة وواحد ل الكشف. تهيئة عقدة ROS (بالاسم ultralytics) لتمكين الاتصال مع ROS master. لضمان اتصال مستقر ، نقوم بتضمين توقف مؤقت قصير ، مما يمنح العقدة وقتا كافيا لإنشاء الاتصال قبل المتابعة.

import time

import rospy

from ultralytics import YOLO

detection_model = YOLO("yolov8m.pt")
segmentation_model = YOLO("yolov8m-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("yolov8m.pt")
segmentation_model = YOLO("yolov8m-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 (نظام تشغيل الروبوت) أمرا صعبا نظرا لطبيعة النظام الموزعة. يمكن أن تساعد العديد من الأدوات في هذه العملية:

  1. rostopic echo <TOPIC-NAME> يتيح لك هذا الأمر عرض الرسائل المنشورة حول موضوع معين ، مما يساعدك على فحص تدفق البيانات.:
  2. rostopic listاستخدم هذا الأمر لسرد جميع الموضوعات المتاحة في نظام ROS ، مما يمنحك نظرة عامة على تدفقات البيانات النشطة.:
  3. rqt_graphتعرض أداة التصور هذه الرسم البياني للاتصال بين العقد ، مما يوفر رؤى حول كيفية ترابط العقد وكيفية تفاعلها.:
  4. للحصول على تصورات أكثر تعقيدًا، مثل التمثيلات ثلاثية الأبعاد، يمكنك استخدام ريفيز. RViz (تصور ROS) هو أداة تصور ثلاثية الأبعاد قوية لـ ROS. تسمح لك بتصور حالة الروبوت الخاص بك وبيئته في الوقت الحقيقي. باستخدام RViz، يمكنك عرض بيانات المستشعر (على سبيل المثال sensors_msgs/Image) ، وحالات نموذج الروبوت ، وأنواع أخرى مختلفة من المعلومات ، مما يسهل تصحيح أخطاء وفهم سلوك نظامك الروبوتي.

نشر الفئات المكتشفة باستخدام std_msgs/String

تتضمن رسائل ROS القياسية أيضا std_msgs/String رسائل. في العديد من التطبيقات ، ليس من الضروري إعادة نشر الصورة المشروحة بالكامل ؛ بدلا من ذلك ، هناك حاجة فقط إلى الفئات الموجودة في عرض الروبوت. يوضح المثال التالي كيفية الاستخدام std_msgs/String الرسائل لإعادة نشر الأصناف المكتشفة على /ultralytics/detection/classes موضوع. هذه الرسائل خفيفة الوزن وتوفر معلومات أساسية ، مما يجعلها ذات قيمة لمختلف التطبيقات.

مثال على حالة الاستخدام

ضع في اعتبارك روبوت مستودع مزود بكاميرا وجسم نموذج الكشف. وبدلاً من إرسال صور كبيرة مشروحة عبر الشبكة، يمكن للروبوت نشر قائمة بالفئات المكتشفة على أنها std_msgs/String رسائل. على سبيل المثال ، عندما يكتشف الروبوت أشياء مثل "صندوق" و "منصة نقالة" و "رافعة شوكية" ، فإنه ينشر هذه الفئات إلى /ultralytics/detection/classes موضوع. يمكن بعد ذلك استخدام هذه المعلومات بواسطة نظام مراقبة مركزي لتتبع المخزون في الوقت الفعلي ، أو تحسين تخطيط مسار الروبوت لتجنب العقبات ، أو تشغيل إجراءات محددة مثل التقاط صندوق تم اكتشافه. يقلل هذا النهج من عرض النطاق الترددي المطلوب للاتصال ويركز على نقل البيانات الهامة.

سلسلة خطوة بخطوة الاستخدام

يوضح هذا المثال كيفية استخدام 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("yolov8m.pt")
rospy.init_node("ultralytics")
time.sleep(1)
classes_pub = rospy.Publisher("/ultralytics/detection/classes", String, queue_size=5)


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


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

استخدام Ultralytics مع صور عمق ROS

بالإضافة إلى صور RGB، يدعم ROS صور العمق التي توفر معلومات حول مسافة الأجسام من الكاميرا. تُعد صور العمق ضرورية للتطبيقات الروبوتية مثل تجنب العوائق ورسم الخرائط ثلاثية الأبعاد وتحديد المواقع.

صورة العمق هي صورة يمثل فيها كل بكسل المسافة من الكاميرا إلى كائن. على عكس صور RGB التي تلتقط الألوان ، تلتقط صور العمق المعلومات المكانية ، مما يمكن الروبوتات من إدراك بنية 3D لبيئتها.

الحصول على صور العمق

يمكن الحصول على صور العمق باستخدام أجهزة استشعار مختلفة:

  1. كاميرات مجسمة: استخدم كاميرتين لحساب العمق بناءً على تباين الصور.
  2. كاميرات زمن التحليق (ToF): قياس الوقت الذي يستغرقه الضوء للعودة من جسم ما.
  3. مستشعرات الضوء المهيكل: إسقاط نمط وقياس تشوهه على الأسطح.

استخدام YOLO مع صور العمق

في ROS ، يتم تمثيل صور العمق بواسطة sensor_msgs/Image نوع الرسالة، والذي يتضمن حقول الترميز والارتفاع والعرض وبيانات البكسل. غالبا ما يستخدم حقل الترميز لصور العمق تنسيقا مثل "16UC1" ، مما يشير إلى عدد صحيح غير موقع 16 بت لكل بكسل ، حيث تمثل كل قيمة المسافة إلى الكائن. تستخدم صور العمق بشكل شائع جنبا إلى جنب مع صور RGB لتوفير رؤية أكثر شمولا للبيئة.

استخدام YOLO، من الممكن استخراج المعلومات ودمجها من كل من RGB وصور العمق. على سبيل المثال، YOLO يمكن اكتشاف الكائنات داخل صورة RGB ، ويمكن استخدام هذا الكشف لتحديد المناطق المقابلة في صورة العمق. وهذا يسمح باستخراج معلومات عمق دقيقة للأجسام المكتشفة ، مما يعزز قدرة الروبوت على فهم بيئته في ثلاثة أبعاد.

كاميرات RGB - D

عند العمل مع صور العمق، من الضروري التأكد من محاذاة صور RGB وصور العمق بشكل صحيح. توفر كاميرات RGB-D، مثل سلسلة Intel RealSense، صور RGB وصور عمق متزامنة، مما يسهل دمج المعلومات من كلا المصدرين. إذا كنت تستخدم كاميرات RGB وكاميرات عمق منفصلة، فمن الضروري معايرتها لضمان المحاذاة الدقيقة.

عمق الاستخدام خطوة بخطوة

في هذا المثال، نستخدم YOLO لتقسيم صورة وتطبيق القناع المستخرج لتقسيم الكائن في صورة العمق. يتيح لنا ذلك تحديد مسافة كل بكسل من الكائن محل الاهتمام من المركز البؤري للكاميرا. من خلال الحصول على معلومات المسافة هذه ، يمكننا حساب المسافة بين الكاميرا والكائن المحدد في المشهد. ابدأ باستيراد المكتبات الضرورية وإنشاء عقدة ROS وإنشاء نموذج تجزئة وموضوع ROS.

import time

import rospy
from std_msgs.msg import String

from ultralytics import YOLO

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

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

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

بعد ذلك ، حدد وظيفة رد الاتصال التي تعالج رسالة صورة العمق الواردة. تنتظر الوظيفة صورة العمق ورسائل صور RGB ، وتحولها إلى صفائف رقمية ، وتطبق نموذج التجزئة على صورة RGB. ثم يستخرج قناع التجزئة لكل كائن مكتشف ويحسب متوسط مسافة الكائن من الكاميرا باستخدام صورة العمق. تحتوي معظم المستشعرات على مسافة قصوى ، تعرف باسم مسافة المقطع ، والتي يتم بعدها تمثيل القيم ك 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)

    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

    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("yolov8m-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)

    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

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


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

while True:
    rospy.spin()

استخدام Ultralytics مع روس sensor_msgs/PointCloud2

الاكتشاف والتجزئة في ROS Gazebo

ال sensor_msgs/PointCloud2 نوع الرسالة هي بنية بيانات مستخدمة في ROS لتمثيل بيانات سحابة النقاط ثلاثية الأبعاد. يعد هذا النوع من الرسائل جزءًا لا يتجزأ من التطبيقات الروبوتية، مما يتيح مهام مثل رسم الخرائط ثلاثية الأبعاد والتعرف على الأجسام وتحديد المواقع.

سحابة النقاط هي مجموعة من نقاط البيانات المحددة داخل نظام إحداثيات ثلاثي الأبعاد. تمثل نقاط البيانات هذه السطح الخارجي لكائن أو مشهد ، تم التقاطه عبر تقنيات المسح الضوئي 3D. كل نقطة في السحابة لديها X, Yو Z الإحداثيات ، والتي تتوافق مع موقعها في الفضاء ، وقد تتضمن أيضا معلومات إضافية مثل اللون والشدة.

الإطار المرجعي

عند العمل مع sensor_msgs/PointCloud2، من الضروري مراعاة الإطار المرجعي للمستشعر الذي تم الحصول منه على بيانات السحابة النقطية منه. يتم التقاط سحابة النقاط في البداية في الإطار المرجعي للمستشعر. يمكنك تحديد هذا الإطار المرجعي من خلال الاستماع إلى /tf_static موضوع. ومع ذلك ، بناء على متطلبات التطبيق المحددة الخاصة بك ، قد تحتاج إلى تحويل سحابة النقاط إلى إطار مرجعي آخر. يمكن تحقيق هذا التحول باستخدام tf2_ros الحزمة ، والتي توفر أدوات لإدارة إطارات الإحداثيات وتحويل البيانات بينها.

الحصول على السحب النقطية

يمكن الحصول على Point Clouds باستخدام أجهزة استشعار مختلفة:

  1. LIDAR (Light Detection and Ranging): Uses laser pulses to measure distances to objects and create high-precision 3D maps.
  2. كاميرات العمق: التقاط معلومات العمق لكل بكسل، مما يسمح بإعادة بناء المشهد ثلاثي الأبعاد.
  3. الكاميرات المجسمة: استخدام كاميرتين أو أكثر للحصول على معلومات العمق من خلال التثليث.
  4. الماسحات الضوئية المهيكلة: إسقاط نمط معروف على سطح ما وقياس التشوه لحساب العمق.

استخدام YOLO مع نقطة الغيوم

للتكامل YOLO مع sensor_msgs/PointCloud2 اكتب الرسائل ، يمكننا استخدام طريقة مشابهة لتلك المستخدمة في خرائط العمق. من خلال الاستفادة من معلومات اللون المضمنة في سحابة النقطة ، يمكننا استخراج صورة 2D ، وإجراء تجزئة على هذه الصورة باستخدام YOLO، ثم قم بتطبيق القناع الناتج على النقاط ثلاثية الأبعاد لعزل كائن 3D محل الاهتمام.

للتعامل مع السحب النقطية ، نوصي باستخدام Open3D (pip install open3d) ، وهو سهل الاستخدام Python مكتبة. يوفر Open3D أدوات قوية لإدارة هياكل البيانات السحابية النقطية وتصورها وتنفيذ العمليات المعقدة بسلاسة. يمكن لهذه المكتبة تبسيط العملية بشكل كبير وتعزيز قدرتنا على معالجة وتحليل السحب النقطية بالتزامن مع YOLO-التجزئة القائمة.

استخدام السحب النقطية خطوة بخطوة

استيراد المكتبات اللازمة وإنشاء مثيل YOLO نموذج للتجزئة.

import time

import rospy

from ultralytics import YOLO

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

إنشاء دالة pointcloud2_to_array، مما يحول أ sensor_msgs/PointCloud2 رسالة في صفيفين خدي. ال 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 إلى صفائف رقمية تحتوي على إحداثيات XYZ وقيم RGB (باستخدام pointcloud2_to_array وظيفة). معالجة صورة RGB باستخدام YOLO نموذج لاستخراج الكائنات المجزأة. لكل كائن تم اكتشافه ، قم باستخراج قناع التجزئة وتطبيقه على كل من صورة RGB وإحداثيات XYZ لعزل الكائن في مساحة 3D.

تعد معالجة القناع أمرا بسيطا لأنه يتكون من قيم ثنائية ، مع 1 تشير إلى وجود الكائن و 0 مما يدل على الغياب. لتطبيق القناع ، ما عليك سوى ضرب القنوات الأصلية في القناع. تعمل هذه العملية على عزل الكائن محل الاهتمام داخل الصورة بشكل فعال. أخيرا ، قم بإنشاء كائن سحابة نقطة Open3D وتصور الكائن المجزأ في مساحة ثلاثية الأبعاد مع الألوان المرتبطة به.

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 ultralytics import YOLO

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


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

    Args:
        pointcloud2 (PointCloud2): the PointCloud2 message

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


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

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

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

    obj_rgb = rgb * mask_expanded
    obj_xyz = xyz * mask_expanded

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

تجزئة السحابة النقطية باستخدام Ultralytics

الأسئلة المتداولة

ما هو نظام تشغيل الروبوت (ROS)؟

نظام تشغيل الروبوتات (ROS ) هو إطار عمل مفتوح المصدر شائع الاستخدام في مجال الروبوتات لمساعدة المطورين على إنشاء تطبيقات روبوتات قوية. وهو يوفر مجموعة من المكتبات والأدوات لبناء الأنظمة الروبوتية والتفاعل معها، مما يتيح تطوير تطبيقات معقدة بشكل أسهل. يدعم ROS التواصل بين العقد باستخدام الرسائل عبر المواضيع أو الخدمات.

كيف يمكنني دمج 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("yolov8m.pt")
rospy.init_node("ultralytics")
det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)


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


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

ما هي موضوعات ROS وكيف يتم استخدامها في Ultralytics YOLO ؟

تعمل مواضيع ROS على تسهيل الاتصال بين العُقد في شبكة ROS باستخدام نموذج النشر والاشتراك. الموضوع هو قناة مسماة تستخدمها العقد لإرسال واستقبال الرسائل بشكل غير متزامن. في سياق Ultralytics YOLO ، يمكنك جعل عقدة ما تشترك في موضوع صورة، ومعالجة الصور باستخدام YOLO لمهام مثل الكشف أو التجزئة، ونشر النتائج إلى مواضيع جديدة.

على سبيل المثال، الاشتراك في موضوع كاميرا ومعالجة الصورة الواردة للكشف عنها:

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

لماذا استخدام صور العمق مع Ultralytics YOLO في ROS؟

صور العمق في ROS، ممثلة بـ sensor_msgs/Imageتوفير مسافة الأجسام من الكاميرا، وهو أمر ضروري لمهام مثل تجنب العوائق ورسم الخرائط ثلاثية الأبعاد وتحديد المواقع. بواسطة باستخدام معلومات العمق إلى جانب صور RGB، يمكن للروبوتات فهم بيئتها ثلاثية الأبعاد بشكل أفضل.

باستخدام YOLO ، يمكنك استخراج أقنعة التجزئة من صور RGB وتطبيق هذه الأقنعة على صور العمق للحصول على معلومات دقيقة ثلاثية الأبعاد عن الأجسام، مما يحسن قدرة الروبوت على التنقل والتفاعل مع محيطه.

كيف يمكنني تصور السحب النقطية ثلاثية الأبعاد باستخدام YOLO في ROS؟

لتصور السحب النقطية ثلاثية الأبعاد في ROS باستخدام YOLO:

  1. التحويل sensor_msgs/PointCloud2 رسائل إلى مصفوفات numpy.
  2. استخدم YOLO لتجزئة صور RGB.
  3. تطبيق قناع التجزئة على السحابة النقطية.

إليك مثال باستخدام Open3D للتصور البصري:

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("yolov8m-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])

يوفر هذا النهج تصوراً ثلاثي الأبعاد للأجسام المجزأة، وهو مفيد لمهام مثل الملاحة والمعالجة.

📅 Created 4 months ago ✏️ Updated 18 days ago

التعليقات