ビジョンによる色認識の実装
実験の目的
- ROS2 に基づいて画像情報を取得し、トピックを配信できるようになる。2. 色認識の原理を理解する。3. 物体の色を認識できるようになる。
色認識の原理
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、Bluetooth キーボード・マウス * 1、メインコンソール * 1、カメラ 3.0*1
回路接続
ハードウェアモジュール
ヒント:ハードウェアモジュールの使い方
ハードウェアシステムの組み立て
組み込み AI 開発ボード RDK_X5
Uni mega2560 拡張ボード【V1.1】使用説明書
電子モジュールの紹介
物理配線
具体的な配線・取り付け方法については、ハードウェアシステムの組み立て を参照してください。
ソフトウェア設計
設計フロー
ビジョンによる色認識の設計
ソースコードのダウンロード
資料のダウンロード・技術サポートについては、HumansX株式会社までお問い合わせください。
プログラムコード
コードの関係:
camera_node.py:リアルタイムに画像を取得して配信し、カメラのトピックを配信します
color_detection.launch.py:複数色の対象を検出・認識し、画像の購読と結果の配信を行います
具体的なコード:
color_detection.launch.py は色認識の処理プログラムです。プロジェクトの完全なコードはソースコードのリンクからダウンロードしてください。
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import cv2
import numpy as np
class ColorDetector(Node):
def __init__(self):
super().__init__('color_detector')
# 声明 ROI 参数(可动态调整)
self.declare_parameters(
namespace='',
parameters=[
('roi_x', 155),
('roi_y', 120),
('roi_width', 385),
('roi_height', 250),
('min_area', 500)
]
)
self.roi_x = self.get_parameter('roi_x').value
self.roi_y = self.get_parameter('roi_y').value
self.roi_w = self.get_parameter('roi_width').value
self.roi_h = self.get_parameter('roi_height').value
self.min_area = self.get_parameter('min_area').value
self.get_logger().info(f"ROI 设置: x={self.roi_x}, y={self.roi_y}, w={self.roi_w}, h={self.roi_h}")
# 订阅原始图像
self.subscription = self.create_subscription(
Image,
'/p4/img_raw',
self.image_callback,
10
)
self.subscription
# 发布检测结果图像
self.image_pub = self.create_publisher(Image, '/p4/color_detection', 10)
self.bridge = CvBridge()
# HSV 颜色范围
self.color_ranges = {
'red': [
(np.array([0, 43, 46]), np.array([10, 255, 255])),
(np.array([156, 43, 46]), np.array([180, 255, 255]))
],
'green': [
(np.array([35, 100, 100]), np.array([85, 255, 255]))
],
'blue': [
(np.array([90, 100, 100]), np.array([130, 255, 255]))
]
}
self.draw_colors = {
'red': (0, 0, 255),
'green': (0, 255, 0),
'blue': (255, 0, 0)
}
self.center_color = (0, 165, 255) # Orange in BGR
self.get_logger().info("颜色检测节点已启动,使用 ROI 检测红/绿/蓝,并发布结果到 /p4/color_detection")
def image_callback(self, msg):
try:
# 转换为 OpenCV 图像
cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")
original_h, original_w = cv_image.shape[:2]
# 校验 ROI 是否越界
x1 = max(0, self.roi_x)
y1 = max(0, self.roi_y)
x2 = min(original_w, self.roi_x + self.roi_w)
y2 = min(original_h, self.roi_y + self.roi_h)
if x2 <= x1 or y2 <= y1:
self.get_logger().warn("ROI 设置无效,跳过处理")
return
# 提取 ROI
roi = cv_image[y1:y2, x1:x2]
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
cv2.rectangle(cv_image, (x1, y1), (x2, y2), (0, 255, 255), 5) # 黄色 ROI 框
# 在原图副本上绘制(保持原始尺寸)
output = cv_image.copy()
# 可选:在 output 上画出 ROI 边框(调试用)
# cv2.rectangle(output, (x1, y1), (x2, y2), (255, 255, 0), 2)
for color_name in ['red', 'green', 'blue']:
mask = None
for lower, upper in self.color_ranges[color_name]:
temp_mask = cv2.inRange(hsv_roi, lower, upper)
if mask is None:
mask = temp_mask
else:
mask = cv2.bitwise_or(mask, temp_mask)
if mask is None:
continue
# 形态学去噪
kernel = np.ones((5, 5), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# 查找轮廓
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area = cv2.contourArea(cnt)
if area < self.min_area:
continue
# 轮廓坐标是相对于 ROI 的,需转换回原图坐标
cnt_global = cnt + np.array([x1, y1]) # 平移
# 绘制边界框(在原图坐标)
x, y, w, h = cv2.boundingRect(cnt_global)
cv2.rectangle(output, (x, y), (x + w, y + h), self.draw_colors[color_name], 2)
# 计算中心点
M = cv2.moments(cnt_global)
if M["m00"] != 0:
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
cv2.circle(output, (cx, cy), 7, self.center_color, -1)
cv2.putText(output, color_name, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, self.draw_colors[color_name], 2)
# # 显示结果(可选)
# cv2.imshow('Color Detection', output)
# cv2.waitKey(1)
# 转换为 ROS Image 消息并发布
img_msg = self.bridge.cv2_to_imgmsg(output, "bgr8")
img_msg.header = msg.header # 保留原始时间戳和 frame_id
self.image_pub.publish(img_msg)
except Exception as e:
self.get_logger().error(f"处理图像时出错: {e}")
def destroy_node(self):
cv2.destroyAllWindows()
super().destroy_node()
def main(args=None):
rclpy.init(args=args)
node = ColorDetector()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
except Exception as e:
print(f"节点运行出错: {str(e)}")
finally:
try:
rclpy.shutdown()
except RuntimeError as e:
error_msg = str(e)
if "already called" in error_msg or "already shutdown" in error_msg:
pass
else:
print(f"Unexpected error during shutdown: {e}", file=sys.stderr)
if __name__ == '__main__':
main()
操作手順
- 192.168.8.111 にリモート接続します。次のコマンドを入力して Enter キーを押します。
ssh sunrise@192.168.8.111
パスワード sunrise を入力します。下図を参照してください:

- カメラを起動し、カメラのトピックを配信します。次のコマンドを入力して Enter キーを押します。
ros2 run opencv_camera_pkg camera_node
下図を参照してください:

- ローカル端末を開き、色認識プログラムを起動します。次のコマンドを入力します。
ros2 launch color_detection color_detection.launch.py
下図を参照してください:

Enter キーを押すと RVIZ 画面が起動します。起動後、取得した画像と認識結果を確認できます。
実験結果
さまざまな色の物体をカメラの下に置くと、物体の色を認識できます。下図のように、青・緑・赤の物体が認識されています。

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