手動(dòng)分類項(xiàng)目是最費(fèi)力和最耗時(shí)的任務(wù)之一。手動(dòng)分揀,無(wú)論是水果還是蔬菜或其他任何東西,都需要大量的人力和時(shí)間。因此,在本教程中,我們嘗試構(gòu)建一個(gè)能夠區(qū)分紅番茄和綠番茄的番茄分選機(jī)。
分揀機(jī)所需組件
硬件
Pi 相機(jī)模塊
2×伺服電機(jī)
軟件
邊緣脈沖工作室
Edge Impulse 入門
要使用Edge Impulse Raspberry Pi訓(xùn)練機(jī)器學(xué)習(xí)模型,請(qǐng)創(chuàng)建一個(gè) Edge Impulse 帳戶,驗(yàn)證您的帳戶,然后開始一個(gè)新項(xiàng)目。
在 Raspberry Pi 上安裝 Edge Impulse
現(xiàn)在要在 Raspberry Pi 上使用 Edge Impulse,您首先必須在 Raspberry Pi 上安裝 Edge Impulse 及其依賴項(xiàng)。使用以下命令在 Raspberry 上安裝 Edge Impulse:
curl -sL https://deb.nodesource.com/setup_12.x | 須藤重?fù)?-
sudo apt install -y gcc g++ make build-essential nodejs sox gstreamer1.0-tools gstreamer1.0-plugins-good gstreamer1.0-plugins-base gstreamer1.0-plugins-base-apps
sudo npm install edge-impulse-linux -g --unsafe-perm
現(xiàn)在使用以下命令運(yùn)行 Edge Impulse:
邊緣脈沖Linux
您將被要求登錄您的 Edge Impulse 帳戶。然后系統(tǒng)會(huì)要求您選擇一個(gè)項(xiàng)目,最后選擇一個(gè)麥克風(fēng)和攝像頭以連接到該項(xiàng)目。
現(xiàn)在由于 Edge Impulse 在 Raspberry Pi 上運(yùn)行,我們必須將 Pi 相機(jī)模型與 Pi 連接以進(jìn)行圖像采集。如下圖所示連接 Pi 相機(jī):
創(chuàng)建數(shù)據(jù)集
如前所述,我們使用 Edge Impulse Studio 來訓(xùn)練我們的圖像分類模型。為此,我們必須收集一個(gè)數(shù)據(jù)集,其中包含我們希望使用 Pi 相機(jī)進(jìn)行分類的對(duì)象樣本。由于目標(biāo)是對(duì)紅番茄和綠番茄進(jìn)行分類,因此您需要收集一些紅番茄和綠番茄的樣本圖像,以便區(qū)分兩者。
您可以通過手機(jī)、樹莓派板子采集樣本,也可以將數(shù)據(jù)集導(dǎo)入邊緣脈沖賬戶。將樣本加載到 Edge Impulse 中的最簡(jiǎn)單方法是使用您的手機(jī)。為此,您必須將您的手機(jī)與 Edge Impulse 連接。
要連接您的手機(jī),請(qǐng)單擊“設(shè)備”,然后單擊“連接新設(shè)備”。
現(xiàn)在在下一個(gè)窗口中單擊“使用您的手機(jī)”,將出現(xiàn)一個(gè)二維碼。使用您的手機(jī)使用 Google Lens 或其他 QR 碼掃描儀應(yīng)用程序掃描 QR 碼。這會(huì)將您的手機(jī)與 Edge Impulse studio 連接起來。
將手機(jī)與 Edge Impulse Studio 連接后,您現(xiàn)在可以加載樣本。要加載樣本,請(qǐng)單擊“數(shù)據(jù)采集”。現(xiàn)在在數(shù)據(jù)采集頁(yè)面上輸入標(biāo)簽名稱并選擇“相機(jī)”作為傳感器。點(diǎn)擊“開始采樣”。
這會(huì)將番茄圖像保存到 Edge Impulse 云中。從不同角度拍攝 50 到 60 張圖像。上傳樣本后,現(xiàn)在將標(biāo)簽設(shè)置為“Green Tomato”并收集另外 50 到 60 張圖像。除了綠色和紅色番茄的樣本外,還要收集一些不確定情況的樣本,以防框架中沒有任何東西。
這些樣本用于訓(xùn)練模塊,在接下來的步驟中,我們將收集測(cè)試數(shù)據(jù)。測(cè)試數(shù)據(jù)應(yīng)至少占訓(xùn)練數(shù)據(jù)的 20%。
訓(xùn)練模型
當(dāng)我們的數(shù)據(jù)集準(zhǔn)備好后,現(xiàn)在我們將為我們的數(shù)據(jù)創(chuàng)建一個(gè)脈沖。為此,請(qǐng)?jiān)L問“創(chuàng)造沖動(dòng)”頁(yè)面。
現(xiàn)在在“創(chuàng)建脈沖”頁(yè)面上,單擊“添加處理塊”,然后單擊“圖像”塊旁邊的“添加”按鈕添加一個(gè)處理塊,該處理塊將標(biāo)準(zhǔn)化圖像數(shù)據(jù)并減少顏色深度。之后,單擊“遷移學(xué)習(xí)(圖像) ”塊以獲取用于圖像分類的預(yù)訓(xùn)練模型,我們將在該模型上執(zhí)行遷移學(xué)習(xí)以針對(duì)我們的番茄識(shí)別任務(wù)對(duì)其進(jìn)行調(diào)整。然后點(diǎn)擊“保存沖動(dòng)”。
接下來,轉(zhuǎn)到“脈沖設(shè)計(jì)”菜單項(xiàng)下的“圖像”子項(xiàng),然后單擊“生成特征”選項(xiàng)卡,然后點(diǎn)擊綠色的“生成特征”按鈕。
之后,點(diǎn)擊“Impulse design”菜單項(xiàng)下的“Transfer learning”子項(xiàng),點(diǎn)擊頁(yè)面底部的“Start training”按鈕。這里我們使用了默認(rèn)的 MobileNetV2。如果需要,您可以使用不同的訓(xùn)練模型。
訓(xùn)練模型需要一些時(shí)間。訓(xùn)練模型后,它將顯示訓(xùn)練性能。對(duì)我來說,準(zhǔn)確率是 75%,損失是 0.58。我們現(xiàn)在可以測(cè)試我們訓(xùn)練好的模型。為此,單擊左側(cè)菜單中的“實(shí)時(shí)分類”選項(xiàng)卡,然后您可以使用 Raspberry Pi 相機(jī)拍攝樣本圖像。
在 Raspberry Pi 上部署經(jīng)過訓(xùn)練的模型
訓(xùn)練過程完成后,我們可以將訓(xùn)練好的 Edge 脈沖圖像分類模型部署到 Raspberry Pi。有兩種方法可以做到這一點(diǎn),一種是使用邊緣脈沖 linux runner 命令。這將通過全硬件加速自動(dòng)編譯訓(xùn)練好的模型,將模型下載到樹莓派,然后開始分類,無(wú)需編寫任何代碼,另一種方法是下載模型文件,然后使用 python SDK 示例進(jìn)行圖像分類。
第一種方法很簡(jiǎn)單,進(jìn)入終端窗口輸入以下命令:
edge-impulse-linux-runner
如果 edge-impulse-linux 命令已在運(yùn)行,則按 Control-C 將其停止,然后輸入上述命令。如果您已經(jīng)分配了一個(gè)項(xiàng)目并想清除它以啟動(dòng)一個(gè)新項(xiàng)目,請(qǐng)使用以下命令:
邊緣脈沖跑步者--清潔
這會(huì)將 Raspberry Pi 連接到 Edge Impulse 云并下載最近訓(xùn)練的模型,并啟動(dòng)視頻流。結(jié)果將顯示在終端窗口中。
您還可以使用 Raspberry Pi IP 地址在瀏覽器上打開視頻流。但由于我們的目標(biāo)是構(gòu)建一個(gè)紅綠番茄分揀機(jī),我們必須使用第二種方法,即使用 python SDK 示例。通過以下方式下載模型文件:
edge-impulse-linux-runner --下載modelfile.eim
現(xiàn)在克隆此存儲(chǔ)庫(kù)以獲取用于對(duì)象分類、語(yǔ)音識(shí)別等的 python 示例:
git 克隆 https://github.com/edgeimpulse/linux-sdk-python
在這里,我們將對(duì)紅色和綠色西紅柿進(jìn)行分類,因此我們將使用此存儲(chǔ)庫(kù)的示例文件夾中的分類.py 示例。使用以下命令運(yùn)行此代碼:
python3 分類.py 模型文件.eim
其中modefile.eim是經(jīng)過訓(xùn)練的模型文件名。確保此文件與代碼位于同一文件夾中。
運(yùn)行代碼后,它將打印檢測(cè)到的對(duì)象的概率,如下圖所示:
現(xiàn)在我們必須對(duì)代碼進(jìn)行一些調(diào)整,以便我們可以根據(jù)檢測(cè)移動(dòng)伺服系統(tǒng)。預(yù)測(cè)的標(biāo)簽和分?jǐn)?shù)存儲(chǔ)在標(biāo)簽和分?jǐn)?shù)變量中,因此我們將這些分?jǐn)?shù)值存儲(chǔ)在一個(gè)數(shù)組中,然后將這三個(gè)值分配給三個(gè)不同的值,以便我們可以輕松地比較它們并相應(yīng)地移動(dòng)舵機(jī)。文檔末尾還提供了包含所有更改的完整代碼。
對(duì)于標(biāo)簽中的標(biāo)簽:
score = res[‘result’][‘classification’][label]
print(‘%s: %.2f\t’ % (label, score), end=‘’)
data.append(分?jǐn)?shù))
打印(‘’,沖洗=真)
綠色=圓形(數(shù)據(jù)[0],2)
紅色 = 圓形(數(shù)據(jù) [1],2)
不確定=輪(數(shù)據(jù)[2],2)
如果(綠色 》=0.45 和 framee_count%10 ==0):
而(綠色》 = 0.35):
pwm1.ChangeDutyCycle(12.0)
time.sleep(0.500)
pwm1.ChangeDutyCycle(2.0) #close
時(shí)間。睡眠(0.250)
pwm.ChangeDutyCycle(7.0)
time.sleep(0.450)
pwm.ChangeDutyCycle(2.0)
綠色=0.01
如果(紅色 》=0.50 和 framee_count%10 ==0):
而(紅色》 = 0.50):
pwm1.ChangeDutyCycle(7.0)
time.sleep(0.500)
pwm1.ChangeDutyCycle(2.0)
時(shí)間。睡眠(0.250)
pwm.ChangeDutyCycle(7.0)
time.sleep(0.450)
pwm.ChangeDutyCycle(2.0)
紅色=0.01
樹莓派分揀機(jī)電路圖
為了移動(dòng)西紅柿,我們將兩個(gè)伺服電機(jī)連接到 Raspberry Pi。一個(gè)伺服器用于一個(gè)接一個(gè)地移動(dòng)西紅柿,第二個(gè)伺服器用于將西紅柿放入各自的盒子中。
如電路圖所示,第一個(gè)舵機(jī)連接到 GPIO 25,第二個(gè)舵機(jī)連接到 Raspberry Pi 的 GPIO 17。兩個(gè)舵機(jī)均由 Raspberry Pi 的 5V 和 GND 引腳供電。
構(gòu)建分揀機(jī)設(shè)置
現(xiàn)在,隨著訓(xùn)練和編碼部分的完成,讓我們進(jìn)入下一個(gè)部分,即為西紅柿分類進(jìn)行完整設(shè)置。我們使用了 2mm 厚的白色 Sunboard 和兩個(gè)伺服電機(jī)。第一個(gè)伺服電機(jī)用于一個(gè)接一個(gè)地移動(dòng)西紅柿,第二個(gè)伺服電機(jī)用于根據(jù)顏色將西紅柿放入盒子中。將所有部件連接在一起后,這臺(tái)分揀機(jī)將如下所示:
現(xiàn)在要測(cè)試設(shè)置,將一些西紅柿放入托盤中,在攝像頭下方放一個(gè)西紅柿,然后在 Raspberry Pi 上啟動(dòng)代碼。
該項(xiàng)目的完整工作顯示在下面給出的視頻中。除了根據(jù)顏色對(duì)番茄進(jìn)行分類外,我們還可以根據(jù)番茄是否腐爛的狀態(tài)對(duì)其進(jìn)行分類。如果您有任何問題,請(qǐng)將它們放在評(píng)論部分,或者您可以使用我們的論壇開始討論。
代碼
#!/usr/bin/env python
導(dǎo)入簡(jiǎn)歷2
導(dǎo)入操作系統(tǒng)
導(dǎo)入系統(tǒng),getopt
進(jìn)口信號(hào)
進(jìn)口時(shí)間
從 edge_impulse_linux.image 導(dǎo)入 ImageImpulseRunner
導(dǎo)入 RPi.GPIO 作為 GPIO
跑步者=無(wú)
show_camera = 假
framee_count=0
伺服銷 = 25
伺服1 = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(servo_pin,GPIO.OUT)
GPIO.setup(servo1, GPIO.OUT)
# 設(shè)置 PWM 進(jìn)程
pwm = GPIO.PWM(servo_pin,50) # 50 Hz(20 ms PWM 周期)
pwm1 = GPIO.PWM(servo1,50)
pwm.start(7) # 旋轉(zhuǎn) 90 度啟動(dòng) PWM
pwm1.start(7)
pwm1.ChangeDutyCycle(2.0)
pwm.ChangeDutyCycle(2.0) #close
現(xiàn)在定義():
返回回合(時(shí)間。時(shí)間()* 1000)
def get_webcams():
port_ids = []
對(duì)于范圍內(nèi)的端口(5):
print(“正在端口 %s 中尋找攝像頭:” %port)
相機(jī) = cv2.VideoCapture(端口)
如果 camera.isOpened():
ret = camera.read()
如果重新:
backendName =camera.getBackendName()
w = camera.get(3)
h = camera.get(4)
print(“在端口 %s 中找到相機(jī) %s (%sx %s) ” %(backendName,h,w, port))
port_ids.append(端口)
相機(jī).release()
返回 port_ids
def sigint_handler(sig, frame):
打印(‘中斷’)
如果(跑步者):
runner.stop()
系統(tǒng)退出(0)
signal.signal(signal.SIGINT, sigint_handler)
定義幫助():
print(‘python分類.py 《path_to_model.eim》 《攝像頭端口ID,僅當(dāng)存在多于1個(gè)攝像頭時(shí)才需要》’)
定義主(argv):
framee_count=0
嘗試:
opts, args = getopt.getopt(argv, “h”, [“--help”])
除了 getopt.GetoptError:
幫助()
系統(tǒng)退出(2)
對(duì)于 opt,在 opts 中的 arg:
如果選擇加入(‘-h’,‘--help’):
幫助()
sys.exit()
如果 len(args) == 0:
幫助()
系統(tǒng)退出(2)
模型 = 參數(shù) [0]
dir_path = os.path.dirname(os.path.realpath(__file__))
modelfile = os.path.join(dir_path, 模型)
打印(‘模型:’ + 模型文件)
以 ImageImpulseRunner(modelfile) 作為跑步者:
嘗試:
model_info = runner.init()
print(‘為 “’ + model_info[‘project’][‘owner’] + ‘ / ’ + model_info[‘project’][‘name’] + ‘”’ 加載了跑步者
標(biāo)簽 = model_info[‘model_parameters’][‘labels’]
如果 len(args)》= 2:
videoCaptureDeviceId = int(args[1])
別的:
port_ids = get_webcams()
如果 len(port_ids) == 0:
raise Exception(‘找不到任何網(wǎng)絡(luò)攝像頭’)
如果 len(args)《= 1 和 len(port_ids)》 1:
raise Exception(“找到多個(gè)攝像頭。將攝像頭端口 ID 作為第二個(gè)參數(shù)添加到此腳本中”)
videoCaptureDeviceId = int(port_ids[0])
相機(jī) = cv2.VideoCapture(videoCaptureDeviceId)
ret = camera.read()[0]
如果重新:
backendName = camera.getBackendName()
w = camera.get(3)
h = camera.get(4)
print(“已選擇端口 %s 中的攝像機(jī) %s (%sx %s)。” %(backendName,h,w, videoCaptureDeviceId))
相機(jī).release()
別的:
raise Exception(“無(wú)法初始化選定的相機(jī)。”)
next_frame = 0 # 此處限制為 ~10 fps
對(duì)于 runner.classifier(videoCaptureDeviceId) 中的 res、img:
如果(下一個(gè)幀》現(xiàn)在()):
time.sleep((next_frame - now()) / 1000)
# print(‘分類運(yùn)行響應(yīng)’, res)
數(shù)據(jù) = []
幀數(shù) = 幀數(shù) +1
print(“幀數(shù):”, framee_count)
如果 res[“result”].keys() 中的“分類”:
print(‘結(jié)果 (%d ms.) ’ % (res[‘timing’][‘dsp’] + res[‘timing’][‘classification’]), end=‘’)
對(duì)于標(biāo)簽中的標(biāo)簽:
score = res[‘result’][‘classification’][label]
# 打印(分?jǐn)?shù))
print(‘%s: %.2f\t’ % (label, score), end=‘’)
data.append(分?jǐn)?shù))
打印(‘’,沖洗=真)
綠色=圓形(數(shù)據(jù)[0],2)
紅色 = 圓形(數(shù)據(jù) [1],2)
不確定=輪(數(shù)據(jù)[2],2)
打印(綠色,紅色,不確定)
如果(綠色 》=0.25 和 framee_count%10 ==0):
而(綠色》 = 0.25):
pwm1.ChangeDutyCycle(12.0)
print(“檢測(cè)到綠色番茄”)
time.sleep(0.500)
pwm1.ChangeDutyCycle(2.0) #close
時(shí)間。睡眠(0.250)
pwm.ChangeDutyCycle(7.0)
time.sleep(0.450)
pwm.ChangeDutyCycle(2.0)
綠色=0.01
# time.sleep(2)
如果(紅色 》=0.50 和 framee_count%10 ==0):
而(紅色》 = 0.50):
pwm1.ChangeDutyCycle(7.0)
print(“檢測(cè)到紅番茄”)
time.sleep(0.500)
pwm1.ChangeDutyCycle(2.0)
時(shí)間。睡眠(0.250)
pwm.ChangeDutyCycle(7.0)
time.sleep(0.450)
pwm.ChangeDutyCycle(2.0)
紅色=0.01
# time.sleep(2)
別的:
time.sleep(0.01)
# print(‘%s: %.2f\t’ % (Green,Red,Uncertain), end =‘’)
如果(show_camera):
cv2.imshow(‘edgeimpulse’, img)
如果 cv2.waitKey(1) == ord(‘q’):
休息
res[“result”].keys() 中的 elif “bounding_boxes”:
print(‘找到 %d 個(gè)邊界框 (%d ms.)’ % (len(res[“result”][“bounding_boxes”]), res[‘timing’][‘dsp’] + res[‘timing’] [‘分類’]))
對(duì)于 res[“result”][“bounding_boxes”] 中的 bb:
print(‘\t%s (%.2f): x=%dy=%dw=%dh=%d’ % (bb[‘label’], bb[‘value’], bb[‘x’], bb[‘y’], bb[‘width’], bb[‘height’]))
next_frame = now() + 100
最后:
如果(跑步者):
runner.stop()
# framee_count=0
如果 __name__ == “__main__”:
主要(sys.argv[1:])
帽釋放()
cv2.destroyAllWindows()
-
分類機(jī)
+關(guān)注
關(guān)注
0文章
2瀏覽量
5941
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論