HumansXドキュメントセンター 公式サイトへ ↗

視覚カラートラッキングの実装

実験の目的:

  1. ROS を用いて画像情報を取得し、トピックとして配信できるようになります。2. 色認識の原理を理解します。3. 物体の色を認識できるようになります。4. カラートラッキングを実現します。

色認識の原理:

RGB と HSV のカラーモデル: デジタル画像処理では、一般に RGB(赤・緑・青)と HSV(色相・彩度・明度)の2つのカラーモデルが使用されます。RGB は比較的直感的ですが、R・G・B の値と色との間に直接的な関係はありません。このモデルのチャンネルでは物体の具体的な色情報をうまく表現できないのに対し、HSV モデルは人が色を記述・説明する方法に近いため、HSV による色の記述のほうが直感的です。

RGB と HSV の違い:

(1) RGB モデル:3次元座標

RGB:三原色(Red、Green、Blue)

原点から白の頂点までの中心軸がグレーの線で、この線上では r・g・b の3成分が等しくなります。強度は3成分からなるベクトルで表すことができます。

RGB で色・濃淡・明暗の変化を理解すると、次のようになります。

色の変化:3つの座標軸上の RGB 各最大成分の頂点と、黄・マゼンタ・シアン(YMC)の色頂点とを結ぶ線

濃淡の変化:RGB 頂点および CMY 頂点から、原点と白の頂点を結ぶ中心軸までの距離

明暗の変化:中心軸上の点の位置で、原点に近いほど暗く、白の頂点に近いほど明るくなります。

(2) HSV モデル:逆円錐モデル

このモデルは、色・濃淡・明暗によって色を表します。

H は色(色相)です。

S は濃淡(彩度)で、S = 0 のときはグレーのみになります。

V は明暗(明度)で、色の明るさを表しますが、光の強度とは直接的な関係はありません(多少の関係はあります)。

(3) RGB と HSV の関係

上記の直感的な理解から、RGB の3次元座標の中心軸を垂直に立てて押し広げると、HSV の円錐モデルになります。

ただし、V は RGB の最大成分の1つだけを採用するため、強度とは直接関係しません。一方、RGB は光の強度(またはグレースケール)の変化を反映できます。

v = max(r, g, B)

RGB から HSV への変換:

(4) HSV の色範囲

​

想定される活用シーン

物品の搬送やターゲット追従など、移動ロボットの応用シーンです。

機能要件:

車体が特定の色の物体(例:赤い物体)を追跡できるようにします。

カメラで画像を取得して赤い物体を認識し、赤い物体が動くと、シャーシカーがそれを追跡します。

ハードウェアの準備:

ナビゲーションロボット * 1、HDカメラ * 1、Type-C ケーブル * 1

回路接続:

カメラモジュール Zero との接続

ソフトウェア設計:

設計プロセス

視覚カラートラッキングの設計

ソースコードのダウンロード

ソースコードの入手先:

資料のダウンロード・技術サポートについては、HumansX株式会社までお問い合わせください。

ソースファイルのディレクトリ構成:

プログラムコード:

color_detect.py は色を認識して車体の動きを制御するプログラムです。プロジェクトの完全なコードはソースコードリンクからダウンロードできます。

#!/usr/bin/env python3
import rospy
import cv2
import numpy as np
from sensor_msgs.msg import Image
from geometry_msgs.msg import Twist
from cv_bridge import CvBridge

class RedObjectDetector:
    def __init__(self):
        rospy.init_node('red_object_detector', anonymous=True)
        self.bridge = CvBridge()
        self.car_turn = False
        self.car_turn_speed = 0.08
        self.car_linear_speed = 0.08

        # 定义HSV红色范围(两个区间)
        self.lower_red1 = np.array([0, 43, 46])
        self.upper_red1 = np.array([10, 255, 255])
        self.lower_red2 = np.array([160, 200, 220])
        self.upper_red2 = np.array([180, 255, 255])
        # 形态学操作核
        self.kernel = np.ones((5,5), np.uint8)

        # 图像尺寸(假设为320x320)
        self.image_width = 160
        self.image_height = 160

        # 订阅摄像头图像
        self.image_sub = rospy.Subscriber('/camera/image_decompressed', 
                                        Image, self.image_callback)

        # 发布/cmd_vel控制指令
        self.cmd_vel_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)

        # 初始化Twist消息
        self.cmd_vel_msg = Twist()

    def image_callback(self, msg):
        try:
            # 转换ROS图像到OpenCV格式(BGR)
            cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")

            # 翻转图像(解决上下颠倒问题)
            cv_image = cv2.flip(cv_image, 0)  # 0 表示垂直翻转(上下翻转)

        except Exception as e:
            rospy.logerr("CV Bridge error: %s" % e)
            return

        # 转换为HSV颜色空间
        hsv = cv2.cvtColor(cv_image, cv2.COLOR_BGR2HSV)

        # 创建红色掩膜
        mask1 = cv2.inRange(hsv, self.lower_red1, self.upper_red1)
        mask2 = cv2.inRange(hsv, self.lower_red2, self.upper_red2)
        combined_mask = cv2.bitwise_or(mask1, mask2)

        # # 形态学处理(去噪+填充)
        # processed_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_CLOSE, self.kernel)
        # processed_mask = cv2.morphologyEx(processed_mask, cv2.MORPH_OPEN, self.kernel)

        # 查找轮廓
        # contours, _ = cv2.findContours(processed_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        contours, _ = cv2.findContours(combined_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # 寻找最大面积的红色物体
        max_area = 0
        largest_contour = None

        for cnt in contours:
            area = cv2.contourArea(cnt)
            if area < 10000 and area > 1500:  # 过滤小面积噪点
                max_area = area
                largest_contour = cnt

        # 如果找到最大红色物体
        if largest_contour is not None:
            # 获取边界矩形
            x, y, w, h = cv2.boundingRect(largest_contour)

            # 计算中心坐标
            M = cv2.moments(largest_contour)
            if M["m00"] != 0:
                cx = int(M["m10"] / M["m00"])
                cy = int(M["m01"] / M["m00"])
            else:
                cx, cy = x + w//2, y + h//2

            # 打印检测信息
            # rospy.loginfo(f"Largest red object detected - Position: ({cx}, {cy}), Size: {w}x{h}, Area: {max_area}")

            # 绘制检测结果
            # 1. 绘制矩形框(绿色)
            cv2.rectangle(cv_image, (x, y), (x+w, y+h), (0, 255, 0), 2)
            # 2. 绘制中心点(蓝色)
            cv2.circle(cv_image, (cx, cy), 5, (255, 0, 0), -1)
            # 3. 在中心点旁边显示坐标
            cv2.putText(cv_image, f"({cx}, {cy})", (cx+10, cy), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)

            # 根据中心位置控制小车运动
            self.control_car(cx, max_area)

        else:
            # 如果没有检测到红色物体,停止小车
            self.stop_car()

        # 显示结果(需要OpenCV GUI支持)
        cv2.imshow("Largest Red Object Detection", cv_image)
        cv2.waitKey(1)

    def control_car(self, cx, area):
        """根据红色物体中心位置控制小车运动"""
        # 计算图像的三分区域
        # left_bound = self.image_width // 3
        # right_bound = 2 * self.image_width // 3
        left_bound = 130 #60
        right_bound = 190 #100        
        if cx < left_bound:
            # 物体在左侧,左转
            self.car_turn = False
            self.cmd_vel_msg.linear.x = 0.0  # 低速前进
            self.cmd_vel_msg.angular.z = self.car_turn_speed  # 左转
            # rospy.loginfo("Turning LEFT")
        elif cx > right_bound:
            self.car_turn = False
            # 物体在右侧,右转
            self.cmd_vel_msg.linear.x = 0.0  # 低速前进
            self.cmd_vel_msg.angular.z = -self.car_turn_speed  # 右转
        else:
            self.cmd_vel_msg.linear.x = 0.0
            self.cmd_vel_msg.angular.z = 0.0
            self.car_turn = True

        if self.car_turn == True:
            if area <= 5000:
                self.cmd_vel_msg.linear.x = self.car_linear_speed
                self.cmd_vel_msg.angular.z = 0.0
            elif area > 5000 and area <= 6000:
                self.cmd_vel_msg.linear.x = 0.0
                self.cmd_vel_msg.angular.z = 0.0
            else:
                self.cmd_vel_msg.linear.x = -self.car_linear_speed
                self.cmd_vel_msg.angular.z = 0.0
        # else:
        #     self.cmd_vel_msg.linear.x = 0.0
        #     self.cmd_vel_msg.angular.z = 0.0

        # if area <= 3000:
        #     self.cmd_vel_msg.linear.x = 0.1
        #     self.cmd_vel_msg.angular.z = 0.0
        # elif area > 3000 and area <= 4000:
        #     self.cmd_vel_msg.linear.x = 0.0
        #     self.cmd_vel_msg.angular.z = 0.0
        # else:
        #     self.cmd_vel_msg.linear.x = -0.1
        #     self.cmd_vel_msg.angular.z = 0.0

        # 发布控制指令
        self.cmd_vel_pub.publish(self.cmd_vel_msg)

    def stop_car(self):
        """停止小车"""
        self.cmd_vel_msg.linear.x = 0.0
        self.cmd_vel_msg.angular.z = 0.0
        self.cmd_vel_pub.publish(self.cmd_vel_msg)
        # rospy.loginfo("No red object detected - STOP")

if __name__ == '__main__':
    try:
        detector = RedObjectDetector()
        rospy.spin()
    except rospy.ROSInterruptException:
        detector.stop_car()  # 确保程序退出时小车停止
        pass

操作手順:

  1. リモート端末にアクセスします。ターミナルを開き、次のコマンドを入力します。
 ssh  uni@192.168.8.207

Enterキーを押してから、パスワード uni を入力します。下図を参考にしてください。

  1. 車体の画像配信プログラムをリモートで起動します。引き続きターミナルで次のコマンドを入力します。
roslaunch color_tracking color_tracking.launch
  1. ローカルの画像購読と RViz 表示を起動します。Ctrl + Shift + T で新しいターミナルタブを開き、次のコマンドを入力します。
 roslaunch vision_test color_tracking.launch

Enterキーを押すと、取得した画像情報が RViz に表示されます。赤い物体を動かすと、その中心座標がリアルタイムで表示されます。同時に、車体が赤い物体を追跡して移動することを確認できます。

サポート窓口

資料のダウンロード・技術サポートについては、HumansX株式会社までお問い合わせください。