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