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)
运行与验证
- 安装依赖:
pip install opencv-python numpy - 运行脚本:
python color_sorting.py - 程序自动生成含红、绿、蓝三个圆形的测试图并执行识别
通过条件:控制台输出三条指令,分别对应红色(像素中心约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())即可,串口初始化代码未包含在本案例中。