作為最熱門的技術(shù)領(lǐng)域,機(jī)器人技術(shù)正在徹底改變產(chǎn)業(yè),并推動(dòng)全球的創(chuàng)新。為了滿足這個(gè)快速發(fā)展的領(lǐng)域?qū)夹g(shù)人才日益增長(zhǎng)的需求,開(kāi)發(fā)了一個(gè)開(kāi)創(chuàng)性的機(jī)器人教育解決方案。這個(gè)創(chuàng)新的解決方案將自動(dòng)化水果采摘機(jī)的模擬與水果分揀和運(yùn)送的自動(dòng)化復(fù)合機(jī)器人結(jié)合起來(lái),為學(xué)生提供了一個(gè)在最受歡迎和最有趨勢(shì)的技術(shù)領(lǐng)域中的全面學(xué)習(xí)經(jīng)驗(yàn)。
在本文中我們將詳細(xì)為你介紹水果采摘和分揀機(jī)器人場(chǎng)景。我們將會(huì)從套裝的介紹和使用的場(chǎng)景介紹,到套裝功能機(jī)械臂的實(shí)現(xiàn)。
智慧農(nóng)業(yè)套裝
圖中所展示的就是我們的智慧農(nóng)業(yè)套裝,它是由以下的內(nèi)容組成。
mechArm 270 M5Stack | *2 |
---|---|
傳送帶 | *1 |
3D攝像頭/深度攝像頭 | *2 |
仿真模擬果樹 | *1 |
結(jié)構(gòu)件 | 若干 |
你肯定很好奇這個(gè)套裝是怎么運(yùn)轉(zhuǎn)的,接下來(lái)我將為你介紹這個(gè)套裝的運(yùn)作流程。
首先你可以看到我們有兩臺(tái)機(jī)械臂,他們分別執(zhí)行不同的功能,離果樹最近的那一臺(tái)機(jī)械臂是采摘機(jī)器人下面簡(jiǎn)稱R1;中間那臺(tái)機(jī)械臂是分揀機(jī)器人下面簡(jiǎn)稱是R2。從他們的名字上就可以知道它們分別做的是什么工作,R1負(fù)責(zé)將果樹上的果實(shí)采摘下來(lái)放置在傳送帶上,R2則是將傳送帶上不合格的水果分揀出來(lái)。
流程:
采摘:R1通過(guò)深度攝像頭對(duì)果樹上果實(shí)的識(shí)別然后對(duì)果子進(jìn)行定位,將果實(shí)的坐標(biāo)發(fā)送給R1去采摘。
運(yùn)輸:通過(guò)R1的采摘,果實(shí)被放置在傳送帶上。傳送帶經(jīng)過(guò)運(yùn)轉(zhuǎn)將果實(shí)運(yùn)輸?shù)絉2可識(shí)別的范圍內(nèi)進(jìn)行果實(shí)好壞的判斷。
分揀:R2上方的攝像頭將視線范圍內(nèi)的果實(shí)進(jìn)行識(shí)別算法的判斷,判斷為好果實(shí)的話,果實(shí)將隨著傳送帶傳輸?shù)焦麑?shí)收集區(qū);判斷為壞果實(shí)的話,將壞果實(shí)的坐標(biāo)信息傳遞給R2,R2將壞果實(shí)目標(biāo)進(jìn)行抓取出來(lái),放置在特定區(qū)域。
持續(xù)循環(huán)上面的流程:采摘->運(yùn)輸->分揀->采摘->運(yùn)輸
產(chǎn)品介紹
下面將簡(jiǎn)要介紹套裝中的產(chǎn)品。
Robotic Arm - mechArm 270 M5Stack
這是一款小六軸機(jī)械臂,以M5Stack-Basic為核心控制,ESP32為輔助控制,結(jié)構(gòu)是中心對(duì)稱結(jié)構(gòu)(仿工業(yè)結(jié)構(gòu))。mechArm 270-M5本體重量1kg, 負(fù)載250g,工作半徑270mm,設(shè)計(jì)緊湊便攜,小巧但功能強(qiáng)大,操作簡(jiǎn)單,能與人協(xié)同、安全工作。
傳送帶
傳送帶是一種用于運(yùn)輸物品的機(jī)械設(shè)備,通常由一個(gè)帶狀物體和一個(gè)或多個(gè)滾動(dòng)軸組成。它們可以運(yùn)輸各種物品,例如包裹、箱子、食品、礦石、建筑材料等等。傳送帶的工作原理是將物品放置在運(yùn)動(dòng)的帶子上,然后將其移動(dòng)到目標(biāo)位置。傳送帶通常由電機(jī)、傳動(dòng)系統(tǒng)、帶子和支撐結(jié)構(gòu)組成。電機(jī)提供動(dòng)力,傳動(dòng)系統(tǒng)將動(dòng)力傳遞給帶子,使其移動(dòng)。
目前市面上可以根據(jù)用戶的需求定制各種傳送帶,例如傳送帶的長(zhǎng)寬高,履帶的材質(zhì)等。
深度攝像頭
隨著使用場(chǎng)景的多樣性,普通的2D攝像頭無(wú)法滿足我們使用的需求。在場(chǎng)景中我們使用到的是深度攝像頭。深度攝像頭是一種能夠獲取場(chǎng)景深度信息的相機(jī)。它不僅能夠捕捉場(chǎng)景的顏色和亮度信息,還能夠感知物體之間的距離和深度信息。深度攝像頭通常使用紅外線或其他光源來(lái)進(jìn)行測(cè)量,以獲取物體和場(chǎng)景的深度信息。
它可以獲取很多信息,例如深度的畫面,彩色的畫面,紅外線的畫面,點(diǎn)云畫面。
有了深度相機(jī),我們就可以精準(zhǔn)的獲取到果樹上果實(shí)的位置,以及顏色信息。
自適應(yīng)夾爪-機(jī)械臂末端執(zhí)行器
自適應(yīng)夾爪是一種用來(lái)抓取、握取或夾持物體的末端執(zhí)行器,它由兩個(gè)可移動(dòng)的爪子組成,可以通過(guò)機(jī)械臂的控制系統(tǒng)來(lái)控制其開(kāi)合程度和開(kāi)合速度。
項(xiàng)目功能的實(shí)現(xiàn)
我們先要準(zhǔn)備好編譯的環(huán)境,該場(chǎng)景是用Python語(yǔ)言來(lái)進(jìn)行編寫的。在使用和學(xué)習(xí)的時(shí)候得安裝好環(huán)境。
編譯環(huán)境:
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
項(xiàng)目的功能點(diǎn)我們主要分為三部分:
● 機(jī)器視覺(jué)識(shí)別算法,深度識(shí)別算法
● 機(jī)械臂的控制,路徑的規(guī)劃
● 多臺(tái)機(jī)器之間通信和邏輯的處理
我們先從機(jī)器視覺(jué)識(shí)別算法介紹:
機(jī)器視覺(jué)識(shí)別算法
使用深度攝像頭之前需要進(jìn)行相機(jī)標(biāo)定。相機(jī)標(biāo)定的教程(https://docs.opencv.org/4.x/dc/dbb/tutorial_py_calibration.html)
相機(jī)標(biāo)定:
相機(jī)標(biāo)定是指通過(guò)對(duì)攝像機(jī)進(jìn)行一系列測(cè)量和計(jì)算,確定攝像機(jī)內(nèi)部參數(shù)和外部參數(shù)的過(guò)程。攝像機(jī)內(nèi)部參數(shù)包括焦距、主點(diǎn)位置、像素間距等,而攝像機(jī)外部參數(shù)則包括攝像機(jī)在世界坐標(biāo)系中的位置和方向等。相機(jī)標(biāo)定的目的是為了使攝像機(jī)能夠準(zhǔn)確地捕捉并記錄世界坐標(biāo)系中物體的位置、大小、形狀等信息。
我們的目標(biāo)物體是果實(shí),它顏色不一,形狀也不一定,有紅的,橙的,黃的。想要準(zhǔn)確的抓取且不傷害到果實(shí),就需要獲取果實(shí)的各個(gè)信息,寬度,厚度等,智能的進(jìn)行抓取。
我們看一下目標(biāo)果實(shí),它們目前較大的區(qū)別就是顏色的不一樣,我們?cè)O(shè)定紅色和橙色的目標(biāo)將被選中,這里就要用到HSV色域來(lái)進(jìn)行目標(biāo)的定位。下面的代碼是用來(lái)檢測(cè)目標(biāo)果實(shí)。
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"