2. 基于颜色识别的工件分拣

任务目标

本案例实现一个基于颜色识别的工件分拣系统:通过摄像头获取图像,利用HSV颜色阈值分割识别红、绿、蓝三种颜色的工件,计算工件中心坐标,并将分拣指令发送给机械臂,使其将工件抓取至对应颜色的放置区域。

环境与依赖

  • Python 3.8+,OpenCV(opencv-python)、NumPy
  • 机械臂通信:通过串口发送坐标指令(本案例以模拟串口输出演示,实际接入时替换为机械臂SDK调用)
  • 摄像头或静态图片输入(本案例使用静态图片路径,便于验证)

核心实现

以下代码完成从图像读取、HSV阈值分割、轮廓提取、颜色分类到输出抓取指令的完整流程。HSV阈值范围针对标准光照下的红、绿、蓝工件设定,实际使用时需根据现场光照微调。

import cv2
import numpy as np

# HSV颜色阈值范围(H: 0-180, S: 0-255, V: 0-255)
COLOR_RANGES = {
    'red':   [(0, 120, 70), (10, 255, 255)],    # 红色(低H段)
    'green': [(35, 100, 60), (85, 255, 255)],
    'blue':  [(100, 120, 60), (130, 255, 255)]
}

# 放置区域坐标(机械臂工作台坐标系,单位mm)
PLACE_ZONES = {
    'red':   (150, 200),
    'green': (350, 200),
    'blue':  (550, 200)
}

def detect_colored_objects(image_path):
    """识别图像中红/绿/蓝工件,返回[(color, cx, cy, area), ...]"""
    img = cv2.imread(image_path)
    if img is None:
        raise FileNotFoundError(f"无法读取图像: {image_path}")
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    results = []

    for color, (lower, upper) in COLOR_RANGES.items():
        mask = cv2.inRange(hsv, np.array(lower), np.array(upper))
        # 形态学去噪
        mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, np.ones((5,5), np.uint8))
        contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        for cnt in contours:
            area = cv2.contourArea(cnt)
            if area < 500:   # 过滤噪声小区域
                continue
            M = cv2.moments(cnt)
            if M["m00"] == 0:
                continue
            cx = int(M["m10"] / M["m00"])
            cy = int(M["m01"] / M["m00"])
            results.append((color, cx, cy, area))

    return results

def pixel_to_robot_coord(px, py, img_w=640, img_h=480):
    """像素坐标转机械臂基座坐标(简化线性映射,需现场标定)"""
    # 假设相机视野对应工作台范围:x: 0~700mm, y: 0~400mm
    robot_x = px * 700.0 / img_w
    robot_y = (img_h - py) * 400.0 / img_h   # 图像y轴翻转
    return round(robot_x, 1), round(robot_y, 1)

def generate_pick_commands(image_path):
    """主流程:识别工件并生成抓取指令"""
    objects = detect_colored_objects(image_path)
    if not objects:
        print("未检测到任何目标工件")
        return []

    commands = []
    for color, px, py, area in objects:
        rx, ry = pixel_to_robot_coord(px, py)
        zx, zy = PLACE_ZONES[color]
        # 指令格式: PICK x,y,zx,zy,color
        cmd = f"PICK {rx},{ry},{zx},{zy},{color}"
        commands.append(cmd)
        print(f"[{color}] 像素中心=({px},{py}) 面积={area} → 机械臂坐标=({rx},{ry}) → 放置区=({zx},{zy})")
    return commands

if __name__ == "__main__":
    # 验证用:生成一张含三色圆形的测试图
    test_img = np.zeros((480, 640, 3), dtype=np.uint8)
    cv2.circle(test_img, (160, 240), 40, (0, 0, 255), -1)    # 红色BGR
    cv2.circle(test_img, (320, 240), 40, (0, 255, 0), -1)    # 绿色
    cv2.circle(test_img, (480, 240), 40, (255, 0, 0), -1)    # 蓝色
    cv2.imwrite("test_objects.png", test_img)

    cmds = generate_pick_commands("test_objects.png")
    print(f"\n共生成 {len(cmds)} 条指令:")
    for c in cmds:
        print(c)

运行与验证

  1. 安装依赖:pip install opencv-python numpy
  2. 运行脚本:python color_sorting.py
  3. 程序自动生成含红、绿、蓝三个圆形的测试图并执行识别

通过条件:控制台输出三条指令,分别对应红色(像素中心约160,240)、绿色(约320,240)、蓝色(约480,240),且每条指令的放置区坐标与PLACE_ZONES中对应颜色一致。若输出坐标与上述预期偏差超过±10像素,需检查HSV阈值或图像输入。

关键说明

  • HSV阈值中红色因色相环跨越0°/180°需分段处理,本案例仅使用低H段(0-10),若工件红色偏紫可补充高H段(170-180)掩码合并。
  • pixel_to_robot_coord为简化线性映射,实际部署需通过手眼标定获取变换矩阵。
  • 机械臂指令输出到串口时,将print(cmd)替换为ser.write(cmd.encode())即可,串口初始化代码未包含在本案例中。