电子说
作为最热门的技术领域,机器人技术正在彻底改变产业,并推动全球的创新。为了满足这个快速发展的领域对技术人才日益增长的需求,开发了一个开创性的机器人教育解决方案。这个创新的解决方案将自动化水果采摘机的模拟与水果分拣和运送的自动化复合机器人结合起来,为学生提供了一个在最受欢迎和最有趋势的技术领域中的全面学习经验。
在本文中我们将详细为你介绍水果采摘和分拣机器人场景。我们将会从套装的介绍和使用的场景介绍,到套装功能机械臂的实现。
图中所展示的就是我们的智慧农业套装,它是由以下的内容组成。
mechArm 270 M5Stack | *2 |
---|---|
传送带 | *1 |
3D摄像头/深度摄像头 | *2 |
仿真模拟果树 | *1 |
结构件 | 若干 |
你肯定很好奇这个套装是怎么运转的,接下来我将为你介绍这个套装的运作流程。
首先你可以看到我们有两台机械臂,他们分别执行不同的功能,离果树最近的那一台机械臂是采摘机器人下面简称R1;中间那台机械臂是分拣机器人下面简称是R2。从他们的名字上就可以知道它们分别做的是什么工作,R1负责将果树上的果实采摘下来放置在传送带上,R2则是将传送带上不合格的水果分拣出来。
流程:
采摘:R1通过深度摄像头对果树上果实的识别然后对果子进行定位,将果实的坐标发送给R1去采摘。
运输:通过R1的采摘,果实被放置在传送带上。传送带经过运转将果实运输到R2可识别的范围内进行果实好坏的判断。
分拣:R2上方的摄像头将视线范围内的果实进行识别算法的判断,判断为好果实的话,果实将随着传送带传输到果实收集区;判断为坏果实的话,将坏果实的坐标信息传递给R2,R2将坏果实目标进行抓取出来,放置在特定区域。
持续循环上面的流程:采摘->运输->分拣->采摘->运输
下面将简要介绍套装中的产品。
这是一款小六轴机械臂,以M5Stack-Basic为核心控制,ESP32为辅助控制,结构是中心对称结构(仿工业结构)。mechArm 270-M5本体重量1kg, 负载250g,工作半径270mm,设计紧凑便携,小巧但功能强大,操作简单,能与人协同、安全工作。
传送带是一种用于运输物品的机械设备,通常由一个带状物体和一个或多个滚动轴组成。它们可以运输各种物品,例如包裹、箱子、食品、矿石、建筑材料等等。传送带的工作原理是将物品放置在运动的带子上,然后将其移动到目标位置。传送带通常由电机、传动系统、带子和支撑结构组成。电机提供动力,传动系统将动力传递给带子,使其移动。
目前市面上可以根据用户的需求定制各种传送带,例如传送带的长宽高,履带的材质等。
随着使用场景的多样性,普通的2D摄像头无法满足我们使用的需求。在场景中我们使用到的是深度摄像头。深度摄像头是一种能够获取场景深度信息的相机。它不仅能够捕捉场景的颜色和亮度信息,还能够感知物体之间的距离和深度信息。深度摄像头通常使用红外线或其他光源来进行测量,以获取物体和场景的深度信息。
它可以获取很多信息,例如深度的画面,彩色的画面,红外线的画面,点云画面。
有了深度相机,我们就可以精准的获取到果树上果实的位置,以及颜色信息。
自适应夹爪是一种用来抓取、握取或夹持物体的末端执行器,它由两个可移动的爪子组成,可以通过机械臂的控制系统来控制其开合程度和开合速度。
我们先要准备好编译的环境,该场景是用Python语言来进行编写的。在使用和学习的时候得安装好环境。
编译环境:
numpy==1.24.3
opencv-contrib-python==4.6.0.66
openni==2.3.0
pymycobot==3.1.2
PyQt5==5.15.9
PyQt5-Qt5==5.15.2
PyQt5-sip==12.12.1
pyserial==3.5
项目的功能点我们主要分为三部分:
● 机器视觉识别算法,深度识别算法
● 机械臂的控制,路径的规划
● 多台机器之间通信和逻辑的处理
我们先从机器视觉识别算法介绍:
使用深度摄像头之前需要进行相机标定。相机标定的教程(https://docs.opencv.org/4.x/dc/dbb/tutorial_py_calibration.html)
相机标定:
相机标定是指通过对摄像机进行一系列测量和计算,确定摄像机内部参数和外部参数的过程。摄像机内部参数包括焦距、主点位置、像素间距等,而摄像机外部参数则包括摄像机在世界坐标系中的位置和方向等。相机标定的目的是为了使摄像机能够准确地捕捉并记录世界坐标系中物体的位置、大小、形状等信息。
我们的目标物体是果实,它颜色不一,形状也不一定,有红的,橙的,黄的。想要准确的抓取且不伤害到果实,就需要获取果实的各个信息,宽度,厚度等,智能的进行抓取。
我们看一下目标果实,它们目前较大的区别就是颜色的不一样,我们设定红色和橙色的目标将被选中,这里就要用到HSV色域来进行目标的定位。下面的代码是用来检测目标果实。
Code:
class Detector:
class FetchType(Enum):
FETCH = False
FETCH_ALL = True
"""
Detection and identification class
"""
HSV_DIST = {
# "redA": (np.array([0, 120, 50]), np.array([3, 255, 255])),
# "redB": (np.array([176, 120, 50]), np.array([179, 255, 255])),
"redA": (np.array([0, 120, 50]), np.array([3, 255, 255])),
"redB": (np.array([118, 120, 50]), np.array([179, 255, 255])),
# "orange": (np.array([10, 120, 120]), np.array([15, 255, 255])),
"orange": (np.array([8, 150, 150]), np.array([20, 255, 255])),
"yellow": (np.array([28, 100, 150]), np.array([35, 255, 255])), # old
# "yellow": (np.array([31, 246, 227]), np.array([35, 255, 255])), # new
}
default_hough_params = {
"method": cv2.HOUGH_GRADIENT_ALT,
"dp": 1.5,
"minDist": 20,
"param2": 0.6,
"minRadius": 15,
"maxRadius": 40,
}
def __init__(self, target):
self.bucket = TargetBucket()
self.detect_target = target
def get_target(self):
return self.detect_target
def set_target(self, target):
if self.detect_target == target:
return
self.detect_target = target
if target == "apple":
self.bucket = TargetBucket(adj_tolerance=25, expire_time=0.2)
elif target == "orange":
self.bucket = TargetBucket()
elif target == "pear":
self.bucket = TargetBucket(adj_tolerance=35)
def detect(self, rgb_data):
if self.detect_target == "apple":
self.__detect_apple(rgb_data)
elif self.detect_target == "orange":
self.__detect_orange(rgb_data)
elif self.detect_target == "pear":
self.__detect_pear(rgb_data)
def __detect_apple(self, rgb_data):
maskA = color_detect(rgb_data, *self.HSV_DIST["redA"])
maskB = color_detect(rgb_data, *self.HSV_DIST["redB"])
mask = maskA + maskB
kernelA = cv2.getStructuringElement(cv2.MORPH_RECT, (8, 8))
kernelB = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
mask = cv2.erode(mask, kernelA)
mask = cv2.dilate(mask, kernelA)
targets = circle_detect(
mask, {"minDist": 15, "param2": 0.5,
"minRadius": 10, "maxRadius": 50}
)
self.bucket.add_all(targets)
self.bucket.update()
def __detect_orange(self, rgb_data):
mask = color_detect(rgb_data, *self.HSV_DIST["orange"])
targets = circle_detect(
mask, {"minDist": 15, "param2": 0.1,
"minRadius": 7, "maxRadius": 30}
)
self.bucket.add_all(targets)
self.bucket.update()
def __detect_pear(self, rgb_data):
mask = color_detect(rgb_data, *self.HSV_DIST["yellow"])
kernelA = cv2.getStructuringElement(cv2.MORPH_RECT, (