YOLO Vision 2026:

ROS (Robot Operating System) クイックスタートガイド#

このガイドでは、Ultralytics YOLOをROS1 (rospy) またはROS2 (rclpy) と統合して、RGB画像、深度画像、ポイントクラウドに対してリアルタイムの物体検出セグメンテーションを実行する方法を説明します。

ROSでのYOLOの設定に移動し、RGB画像深度画像、またはポイントクラウドを扱います。

ROS Introduction (captioned) from Open Robotics on Vimeo.

ROSとは何ですか?#

Robot Operating System (ROS)は、ロボット工学の研究や産業で広く使用されているオープンソースのフレームワークです。ROSは、開発者がロボットアプリケーションを作成するのに役立つライブラリとツールのコレクションを提供します。ROSはさまざまなロボットプラットフォームと連携できるように設計されており、ロボット工学者にとって柔軟で強力なツールとなっています。

ROSの主な機能#

  1. モジュール型アーキテクチャ: ROSにはモジュール型アーキテクチャがあり、開発者はノードと呼ばれる小さく再利用可能なコンポーネントを組み合わせて複雑なシステムを構築できます。通常、各ノードは特定の機能を実行し、ノード間はトピックまたはサービス上のメッセージを使用して通信します。

  2. 通信ミドルウェア: ROSは、プロセス間通信と分散コンピューティングをサポートする堅牢な通信インフラストラクチャを提供します。これは、データストリーム(トピック)のためのパブリッシュ・サブスクライブモデルと、サービスコールのためのリクエスト・リプライモデルによって実現されます。

  3. ハードウェア抽象化: ROSはハードウェアに対する抽象化層を提供し、開発者がデバイスに依存しないコードを書くことを可能にします。これにより、同じコードを異なるハードウェア構成で使用でき、統合や実験が容易になります。

  4. ツールとユーティリティ: ROSには、視覚化、デバッグ、シミュレーションのための豊富なツールやユーティリティが付属しています。例えば、RVizはセンサーデータやロボットの状態情報の視覚化に使用され、Gazeboはアルゴリズムやロボット設計をテストするための強力なシミュレーション環境を提供します。

  5. 広範なエコシステム: ROSのエコシステムは広大で絶えず成長しており、ナビゲーション、操作、知覚など、様々なロボットアプリケーションに対応する多数のパッケージが利用可能です。コミュニティはこれらのパッケージの開発とメンテナンスに積極的に貢献しています。

ROSバージョンの進化

2007年の開発以来、ROSはROS 1とROS 2に分かれ、複数のバージョンを経て進化してきました。以下の既存の例ではROS1 Noeticを使用しています。ROS2の使用にあるコンパクトなアダプターは、現在のROS2リリースに対応する rclpy インターフェースを示しています。

ROS 1 と ROS 2 の比較#

ROS 1 はロボット開発の強固な基盤を提供しましたが、ROS 2 はその欠点を解決し、以下を提供します。

  • リアルタイム性能: リアルタイムシステムおよび決定論的動作のサポート向上。
  • セキュリティ: 様々な環境での安全かつ信頼性の高い運用のための強化されたセキュリティ機能。
  • スケーラビリティ: マルチロボットシステムや大規模導入へのより優れたサポート。
  • クロスプラットフォームサポート: Linux以外の様々なオペレーティングシステム(WindowsやmacOSなど)への互換性拡大。
  • 柔軟な通信: DDSの使用により、より柔軟で効率的なプロセス間通信を実現。

ROSのメッセージとトピック#

ROSでは、ノード間の通信はメッセージトピックを通じて行われます。メッセージはノード間で交換される情報を定義するデータ構造であり、トピックはメッセージが送受信される名前付きチャネルです。ノードはトピックにメッセージをパブリッシュしたり、トピックからメッセージをサブスクライブしたりすることで、相互に通信できます。このパブリッシュ・サブスクライブモデルにより、非同期通信とノード間の疎結合が可能になります。ロボットシステムの各センサーやアクチュエータは通常、データをトピックにパブリッシュし、そのデータを他のノードが処理や制御のために消費します。このガイドでは、Image、Depth、PointCloudのメッセージとカメラトピックに焦点を当てます。

ROSでの Ultralytics YOLO のセットアップ#

ROS1 の例は、ROSbot ROS repository のフォークである this ROS environment を使用してテストされています。ROS2 でも同様の YOLO および NumPy 処理が適用されますが、異なるのはノードのライフサイクルとメッセージ変換のみです。

Husarion ROSbot 2 PRO autonomous robot platform

依存関係のインストール#

ROS環境以外に、以下の依存関係をインストールする必要があります。

  • ROS NumPyパッケージ: これは、ROS ImageメッセージとNumPy配列の間で高速に変換するために必要です。

    pip install ros_numpy
  • Ultralytics パッケージ:

    pip install ultralytics

ROS2の使用#

ROS2では、rospyrclpy に置き換えられ、画像変換の ros_numpycv_bridge に置き換えられます。次のノードは、以下のRGB検出フローの完全なROS2 equivalenteです。モデルを一度インスタンス化し、コールバック間で再利用します。

import cv_bridge
import rclpy
from rclpy.node import Node
from rclpy.qos import qos_profile_sensor_data
from sensor_msgs.msg import Image

from ultralytics import YOLO

class UltralyticsNode(Node):
    """Run YOLO detection on ROS2 image messages."""

    def __init__(self):
        """Initialize the ROS2 node, model, and image interfaces."""
        super().__init__("ultralytics")
        self.bridge = cv_bridge.CvBridge()
        self.model = YOLO("yolo26m.pt")
        self.publisher = self.create_publisher(Image, "/ultralytics/detection/image", 5)
        self.create_subscription(Image, "/camera/color/image_raw", self.callback, qos_profile_sensor_data)

    def callback(self, message):
        """Publish the annotated camera frame."""
        image = self.bridge.imgmsg_to_cv2(message, desired_encoding="bgr8")
        annotated = self.model(image)[0].plot(show=False)
        self.publisher.publish(self.bridge.cv2_to_imgmsg(annotated, encoding="bgr8"))

def main(args=None):
    """Start the ROS2 node."""
    rclpy.init(args=args)
    node = UltralyticsNode()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == "__main__":
    main()

深度画像については、以下の深度処理コードを再利用し、メッセージの取得と変換部分のみを置き換えてください:

self.create_subscription(Image, "/camera/color/image_raw", self.rgb_callback, qos_profile_sensor_data)
self.create_subscription(Image, "/camera/depth/image_raw", self.depth_callback, qos_profile_sensor_data)

def rgb_callback(self, message):
    self.rgb_image = self.bridge.imgmsg_to_cv2(message, desired_encoding="bgr8")

def depth_callback(self, message):
    depth_image = self.bridge.imgmsg_to_cv2(message, desired_encoding="passthrough")
    # Apply the NumPy mask and distance calculation from the depth example below.

ポイントクラウドの場合、ROS2は sensor_msgs_py.point_cloud2 を提供します。整理されたクラウドを一度変換し、以下のNumPyセグメンテーションと3Dマッピングを再利用します。

from sensor_msgs_py import point_cloud2

points = point_cloud2.read_points_numpy(message, field_names=("x", "y", "z", "rgb"))
points = points.reshape(message.height, message.width, 4)

UltralyticsをROS sensor_msgs/Image とともに使用する#

sensor_msgs/Image メッセージタイプは、画像データを表現するためにROSで一般的に使用されます。エンコーディング、高さ、幅、ピクセルデータのフィールドが含まれており、カメラやその他のセンサーでキャプチャした画像を送信するのに適しています。画像メッセージは、視覚的認識、物体検出、ナビゲーションなどのタスクでロボットアプリケーションに広く使用されています。

Detection and Segmentation in ROS Gazebo

画像使用のステップバイステップ#

次のコードスニペットは、Ultralytics YOLOパッケージをROSで使用する方法を示しています。この例では、カメラトピックをサブスクライブし、YOLOを使用して受信画像を処理し、検出されたオブジェクトを新しいトピックにパブリッシュして検出セグメンテーションを行います。

まず、必要なライブラリをインポートし、2つのモデル(セグメンテーション用と検出用)をインスタンス化します。ROSマスターとの通信を有効にするために、ROSノード(名前は ultralytics)を初期化します。安定した接続を確保するために、短い一時停止を挟み、処理を進める前にノードが接続を確立するのに十分な時間を与えます。

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)

2つの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 型のメッセージを受信し、ros_numpy を使用して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) ノードのデバッグは、システムの分散型の性質上、困難な場合があります。このプロセスを支援するいくつかのツールがあります。

  1. rostopic echo <TOPIC-NAME> : このコマンドを使用すると、特定のトピックでパブリッシュされたメッセージを表示し、データの流れを検査することができます。
  2. rostopic list: このコマンドを使用してROSシステム内の利用可能なすべてのトピックを一覧表示し、アクティブなデータストリームの概要を把握します。
  3. rqt_graph: この視覚化ツールはノード間の通信グラフを表示し、ノードがどのように相互接続され、どのように相互作用しているかについての洞察を提供します。
  4. 3D表現などのより複雑な視覚化には、RVizを使用できます。RViz (ROS Visualization) は、ROS向けの強力な3D視覚化ツールです。ロボットとその環境の状態をリアルタイムで視覚化できます。RVizを使用すると、センサーデータ(例: sensor_msgs/Image)、ロボットモデルの状態、およびその他さまざまなタイプの情報を表示できるため、ロボットシステムの動作のデバッグと理解が容易になります。

std_msgs/String で検出されたクラスをパブリッシュする#

標準のROSメッセージには、std_msgs/String メッセージも含まれます。多くのアプリケーションでは、アノテーション付きの画像全体を再パブリッシュする必要はなく、ロボットの視界にあるクラスのみが必要になります。次の例は、std_msgs/String メッセージを使用して、検出されたクラスを /ultralytics/detection/classes トピックに再パブリッシュする方法を示しています。これらのメッセージはより軽量で不可欠な情報を提供するため、さまざまなアプリケーションで価値があります。

使用事例#

カメラと物体検出モデルを搭載した倉庫のロボットを考えてみてください。ネットワーク経由で大きなおアノテーション付き画像を送信する代わりに、ロボットは検出されたクラスのリストを std_msgs/String メッセージとしてパブリッシュできます。たとえば、ロボットが「box」、「pallet」、「forklift」などのオブジェクトを検出すると、これらのクラスを /ultralytics/detection/classes トピックにパブリッシュします。この情報は、中央の監視システムがリアルタイムで在庫を追跡したり、障害物を回避するためのロボットの経路計画を最適化したり、検出された箱を拾い上げるなどの特定のアクションをトリガーしたりするために使用できます。このアプローチにより、通信に必要な帯域幅が削減され、重要なデータの送信に集中できます。

文字列使用のステップバイステップ#

この例では、Ultralytics YOLO パッケージを ROS と共に使用する方法を示します。この例では、カメラトピックをサブスクライブし、YOLO を使用して受信画像を処理し、std_msgs/String メッセージを使用して検出されたオブジェクトを新しいトピック /ultralytics/detection/classes にパブリッシュします。ros_numpy パッケージは、ROS Image メッセージを YOLO で処理するための NumPy 配列に変換するために使用されます。

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

ROS深度画像で Ultralytics を使用する#

RGB画像に加えて、ROSは深度画像をサポートしています。深度画像は、カメラからのオブジェクトの距離に関する情報を提供します。深度画像は、障害物回避、3Dマッピング、ローカライゼーションなどのロボットアプリケーションに不可欠です。

深度画像は、各ピクセルがカメラから物体までの距離を表す画像です。色をキャプチャするRGB画像とは異なり、深度画像は空間情報をキャプチャし、ロボットが周囲の環境の3D構造を認識できるようにします。

深度画像の取得

深度画像は様々なセンサーを使用して取得できます。

  1. ステレオカメラ: 2台のカメラを使用して、画像の視差に基づいて深度を計算します。
  2. Time-of-Flight (ToF) カメラ: 光がオブジェクトから戻ってくるまでの時間を測定します。
  3. 構造化光センサー: パターンを投影し、表面上のその変形を測定します。

YOLOと深度画像の使用#

ROSでは、深度画像は sensor_msgs/Image メッセージタイプで表現され、エンコーディング、高さ、幅、ピクセルデータのフィールドが含まれます。深度画像のエンコーディングフィールドには、「16UC1」のような形式がよく使用されます。これは、ピクセルあたり16ビットの符号なし整数を示し、各値がオブジェクトまでの距離を表します。深度画像は、環境のより包括的なビューを提供するために、RGB画像と組み合わせて一般的に使用されます。

YOLOを使用すると、RGB画像と深度画像の両方から情報を抽出して結合することが可能です。例えば、YOLOはRGB画像内の物体を検出でき、この検出結果を使用して深度画像内の対応する領域を特定できます。これにより、検出された物体の正確な深度情報を抽出でき、ロボットが3次元で環境を理解する能力が向上します。

RGB-Dカメラ

深度画像を扱うときは、RGB画像と深度画像が正しく整合していることを確認することが重要です。Intel RealSenseシリーズなどのRGB-Dカメラは、同期されたRGB画像と深度画像を提供するため、両方のソースからの情報を簡単に結合できます。別々のRGBカメラと深度カメラを使用する場合は、正確な整合を確保するためにそれらを校正することが不可欠です。

深度使用のステップバイステップ#

この例では、YOLOを使用して画像をセグメント化し、抽出されたマスクを適用して深度画像内の物体をセグメント化します。これにより、対象物体の各ピクセルがカメラの焦点中心からどれだけ離れているかを判断できます。この距離情報を取得することで、カメラとシーン内の特定の物体との間の距離を計算できます。まず、必要なライブラリをインポートし、ROSノードを作成し、セグメンテーションモデルとROSトピックをインスタンス化することから始めます。

import time

import rospy
from std_msgs.msg import String

from ultralytics import YOLO

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

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

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

次に、受信した深度画像メッセージを処理するコールバック関数を定義します。この関数は、深度画像とRGB画像のメッセージを待機し、それらをNumPy配列に変換し、セグメンテーションモデルをRGB画像に適用します。次に、検出された各オブジェクトのセグメンテーションマスクを抽出し、深度画像を使用してカメラからのオブジェクトの平均距離を計算します。ほとんどのセンサーには、クリップ距離と呼ばれる最大距離があり、これを超える値は inf (np.inf) として表現されます。処理の前に、これらのnull値をフィルタリングして除外官し、0 の値を割り当てることが重要です。最後に、検出されたオブジェクトとそれらの平均距離を /ultralytics/detection/distance トピックにパブリッシュします。

import numpy as np
import ros_numpy
from sensor_msgs.msg import Image

def callback(data):
    """Callback function to process depth image and RGB image."""
    image = rospy.wait_for_message("/camera/color/image_raw", Image)
    image = ros_numpy.numpify(image)
    depth = ros_numpy.numpify(data)
    result = segmentation_model(image)

    all_objects = []
    for index, cls in enumerate(result[0].boxes.cls):
        class_index = int(cls.cpu().numpy())
        name = result[0].names[class_index]
        mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
        obj = depth[mask == 1]
        obj = obj[~np.isnan(obj)]
        avg_distance = np.mean(obj) if len(obj) else np.inf
        all_objects.append(f"{name}: {avg_distance:.2f}m")

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

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

while True:
    rospy.spin()
完全なコード
import time

import numpy as np
import ros_numpy
import rospy
from sensor_msgs.msg import Image
from std_msgs.msg import String

from ultralytics import YOLO

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

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

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

def callback(data):
    """Callback function to process depth image and RGB image."""
    image = rospy.wait_for_message("/camera/color/image_raw", Image)
    image = ros_numpy.numpify(image)
    depth = ros_numpy.numpify(data)
    result = segmentation_model(image)

    all_objects = []
    for index, cls in enumerate(result[0].boxes.cls):
        class_index = int(cls.cpu().numpy())
        name = result[0].names[class_index]
        mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
        obj = depth[mask == 1]
        obj = obj[~np.isnan(obj)]
        avg_distance = np.mean(obj) if len(obj) else np.inf
        all_objects.append(f"{name}: {avg_distance:.2f}m")

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

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

while True:
    rospy.spin()

UltralyticsをROS sensor_msgs/PointCloud2 とともに使用する#

Detection and Segmentation in ROS Gazebo

sensor_msgs/PointCloud2 メッセージタイプは、3Dポイントクラウドデータを表現するためにROSで使用されるデータ構造です。このメッセージタイプはロボットアプリケーションに不可欠であり、3Dマッピング、オブジェクト認識、ローカライゼーションなどのタスクを可能にします。

ポイントクラウドは、3次元座標系内で定義されたデータポイントの集まりです。これらのデータポイントは、3Dスキャンテクノロジーを介してキャプチャされたオブジェクトまたはシーンの外表面を表します。クラウド内の各ポイントには、空間内の位置に対応する XY、および Z 座標があり、色や強度などの追加情報が含まれている場合もあります。

参照フレーム

sensor_msgs/PointCloud2 を扱うときは、ポイントクラウドデータが取得されたセンサーの参照フレームを考慮することが不可欠です。ポイントクラウドは最初、センサーの参照フレームでキャプチャされます。この参照フレームは、/tf_static トピックをリスンすることで確認できます。ただし、特定のアプリケーションの要件によっては、ポイントクラウドを別の参照フレームに変換する必要がある場合があります。この変換は、座標フレームを管理し、それらの間でデータを変換するためのツールを提供する tf2_ros パッケージを使用して実現できます。

点群の取得

点群は様々なセンサーを使用して取得できます。

  1. LIDAR (光検出と測距): レーザーパルスを使用してオブジェクトまでの距離を測定し、高精度な3Dマップを作成します。
  2. 深度カメラ: 各ピクセルの深度情報をキャプチャし、シーンの3D再構築を可能にします。
  3. ステレオカメラ: 2台以上のカメラを利用して三角測量により深度情報を取得します。
  4. 構造化光スキャナー: 表面に既知のパターンを投影し、変形を測定して深度を計算します。

点群とYOLOの使用#

YOLOを sensor_msgs/PointCloud2 タイプのメッセージと統合するために、深度マップで使用される方法と同様の方法を採用できます。ポイントクラウドに埋め込まれた色情報を活用することで、2D画像を抽出し、YOLOを使用してこの画像でセグメンテーションを実行し、結果のマスクを3次元ポイントに適用して関心のある3Dオブジェクトを分離することができます。

ポイントクラウドを処理するには、使いやすいPythonライブラリであるOpen3D (pip install open3d) を使用することをお勧めします。Open3Dは、ポイントクラウドデータ構造の管理、視覚化、および複雑な操作のシームレスな実行のための堅牢なツールを提供します。このライブラリは、プロセスを大幅に簡素化し、YOLOベースのセグメンテーションと組み合わせてポイントクラウドを操作および分析する能力を向上させることができます。

点群使用のステップバイステップ#

必要なライブラリをインポートし、セグメンテーション用のYOLOモデルをインスタンス化します。

import time

import rospy

from ultralytics import YOLO

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

pointcloud2_to_array という関数を作成します。これは、sensor_msgs/PointCloud2 メッセージを 2 つの NumPy 配列に変換します。sensor_msgs/PointCloud2 メッセージには、取得された画像の width および height に基づく n ポイントが含まれています。たとえば、480 x 640 画像には 307,200 ポイントが含まれます。各ポイントには、3 つの空間座標 (xyz) と RGB 形式の対応するカラーが含まれます。これらは、2 つの独立した情報チャネルと見なすことができます。

この関数は、元のカメラ解像度 (width x height) の形式で xyz 座標と RGB 値を返します。ほとんどのセンサーには最大距離 (クリップ距離と呼ばれます) があり、これを超えると値は 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値を含むNumPy配列に変換します(pointcloud2_to_array 関数を使用)。YOLOモデルを使用してRGB画像を処理し、セグメント化されたオブジェクトを抽出します。検出されたオブジェクトごとにセグメンテーションマスクを抽出し、それを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])

Point Cloud Segmentation with Ultralytics

結論#

Ultralytics YOLOがROSに統合されると、ロボットはRGB画像、深度画像、ポイントクラウド全体で物体検出セグメンテーションを実行できるようになり、生のセンサーデータストリームを用途のある認識に変換できます。ここから、予測モードを探索して推論オプションを増やすか、コンピュータービジョンプロジェクトの手順に従ってロボットアプリケーションをプロトタイプから本番環境へと進めてください。

よくある質問 (FAQ)#

  • Robot Operating System (ROS)は、ロボット工学で一般的に使用されるオープンソースのフレームワークであり、開発者が堅牢なロボットアプリケーションを作成するのに役立ちます。ロボットシステムの構築とインターフェースのためのライブラリとツールのコレクションを提供し、複雑なアプリケーションの開発を容易にします。ROSは、トピックまたはサービス上のメッセージを使用したノード間の通信をサポートします。

  • Ultralytics YOLOをROSと統合するには、ROS環境を設定し、YOLOを使用してセンサーデータを処理します。まず、ros_numpy やUltralytics YOLOなどの必要な依存関係をインストールします。

    pip install ros_numpy ultralytics

    次に、ROSノードを作成し、画像トピックをサブスクライブして、受信データを物体検出用に処理します。最小限の例は次のとおりです。

    import ros_numpy
    import rospy
    from sensor_msgs.msg import Image
    
    from ultralytics import YOLO
    
    detection_model = YOLO("yolo26m.pt")
    rospy.init_node("ultralytics")
    det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)
    
    def callback(data):
        array = ros_numpy.numpify(data)
        det_result = detection_model(array)
        det_annotated = det_result[0].plot(show=False)
        det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))
    
    rospy.Subscriber("/camera/color/image_raw", Image, callback)
    rospy.spin()
  • ROSトピックは、パブリッシュ・サブスクライブモデルを使用して、ROSネットワーク内のノード間の通信を容易にします。トピックは、ノードが非同期でメッセージを送受信するために使用する名前付きチャネルです。Ultralytics YOLOのコンテキストでは、ノードに画像トピックをサブスクライブさせ、検出セグメンテーションなどのタスクにYOLOを使用して画像を処理し、結果を新しいトピックにパブリッシュさせることができます。

    例えば、カメラトピックをサブスクライブし、検出のために受信画像を処理するには以下のようにします。

    rospy.Subscriber("/camera/color/image_raw", Image, callback)
  • sensor_msgs/Image で表されるROSの深度画像は、カメラからのオブジェクトの距離を提供します。これは、障害物回避、3Dマッピング、ローカライゼーションなどのタスクに不可欠です。深度情報を使用することで、RGB画像と合わせて、ロボットは3D環境をよりよく理解できるようになります。

    YOLOを使用すると、RGB画像からセグメンテーションマスクを抽出し、これらのマスクを深度画像に適用して正確な3Dオブジェクト情報を取得できるため、ロボットのナビゲーションおよび周囲環境との対話能力が向上します。

  • ROSでYOLOを使用して3D点群を視覚化するには、以下の手順に従います。

    1. sensor_msgs/PointCloud2 メッセージをNumPy配列に変換します。
    2. YOLOを使用してRGB画像をセグメント化します。
    3. セグメンテーションマスクを点群に適用します。

    視覚化に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視覚化を提供します。これは、ロボットアプリケーションでのナビゲーションやマニピュレーションなどのタスクに役立ちます。

コメント