1. 基于颜色分割的简单物体抓取
任务目标
构建一个最小可复现的视觉抓取系统:通过摄像头(或静态图像)识别红色方块,计算其中心在图像中的像素坐标,并转换为机器人基座坐标系下的抓取位置,最后输出该位置供机器人执行抓取。本案例聚焦视觉识别与坐标转换核心链路,不涉及具体机器人运动学求解。
环境与依赖
- Python 3.8+
- OpenCV(
pip install opencv-python) - NumPy(
pip install numpy) - 若使用真实相机:任意USB摄像头;若使用静态图:任意包含红色方块的图片
实现思路
系统分为三个步骤:颜色分割(将RGB转HSV,用阈值提取红色区域)、轮廓提取与中心计算(找到最大红色轮廓并求质心)、坐标转换(利用相机内参和已知高度的单应性矩阵,将像素坐标映射到机器人平面坐标)。为简化且保证可复现,本案例使用静态图像输入,并假设相机光轴垂直于抓取平面(即平面单应关系)。
关键完整代码
import cv2
import numpy as np
def detect_red_block(image_path):
"""读取图像,返回红色方块中心像素坐标及掩膜"""
img = cv2.imread(image_path)
if img is None:
raise FileNotFoundError(f"无法读取图像: {image_path}")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 红色在HSV中有两个区间(0°附近),分别提取后合并
lower_red1 = np.array([0, 120, 70])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([170, 120, 70])
upper_red2 = np.array([180, 255, 255])
mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
mask = cv2.bitwise_or(mask1, mask2)
# 形态学去噪
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)
if not contours:
return None, None, mask
# 取最大轮廓(假设目标为最大红色区域)
largest = max(contours, key=cv2.contourArea)
M = cv2.moments(largest)
if M["m00"] == 0:
return None, None, mask
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
return (cx, cy), largest, mask
def pixel_to_robot(pixel_point, homography):
"""将像素坐标通过单应矩阵转换为机器人平面坐标(单位:mm)"""
pt = np.array([pixel_point[0], pixel_point[1], 1.0])
robot = homography @ pt
robot /= robot[2]
return robot[0], robot[1]
if __name__ == "__main__":
# ===== 配置 =====
IMAGE_PATH = "red_block.jpg" # 输入图像
# 单应矩阵:通过4组像素-机器人坐标对标定获得(此处为示例值)
# 假设图像640x480,工作区域300mm x 200mm,原点在图像中心
H = np.array([
[0.5, 0, -160],
[0, -0.5, 120],
[0, 0, 1]
], dtype=float)
# ===== 执行 =====
center, contour, mask = detect_red_block(IMAGE_PATH)
if center is None:
print("未检测到红色方块")
else:
robot_x, robot_y = pixel_to_robot(center, H)
print(f"像素中心: ({center[0]}, {center[1]})")
print(f"机器人抓取位置: ({robot_x:.1f} mm, {robot_y:.1f} mm)")
# 可视化(可选)
img = cv2.imread(IMAGE_PATH)
cv2.drawContours(img, [contour], -1, (0, 255, 0), 2)
cv2.circle(img, center, 5, (255, 0, 0), -1)
cv2.imshow("Detection", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行与验证
- 准备一张包含红色方块的图像(如
red_block.jpg),背景尽量简单,红色区域为画面中最大红色连通域。 - 运行脚本:
python grasp_detection.py - 通过条件:终端输出像素中心坐标和机器人坐标;可视化窗口中红色方块被绿色轮廓框住,中心有蓝色圆点标记。
- 坐标验证:将图像中心(320,240)代入单应矩阵,应得到(0,0)——即机器人原点对应图像中心。若红色方块位于图像中心偏右,则
robot_x应为正值,偏上则robot_y为正值(因y轴翻转)。
常见问题
- 检测不到红色:调整HSV阈值,特别是光照变化时需重新标定;可先输出mask图观察阈值是否合适。
- 单应矩阵如何获得:实际使用时需用棋盘格等标定板,取4个以上角点的像素坐标与机器人实际坐标,用
cv2.findHomography计算。本案例的H为演示值,仅用于验证流程。 - 多目标场景:当前实现取最大红色区域,若需抓取指定目标,需增加尺寸或位置筛选逻辑。