OpenMV 從入手到跑TensorFlow Lite神經網路進行垃圾分類
- 一、了解OpenMV 4 plus的構成
- 1.OpenMV 4 plus 的特點
- 1.1 硬體資源
- 1.2 引腳圖
- 2.可用的學習資源
- 二、建議的學習路線
- 1.學習python基礎語法
- 2.練習OpenMV的基礎實驗
- 2.1 安裝軟體
- 2.2 了解基礎環境使用方法并連接設備
- 2.3 常用基礎例程
- 2.3.1 查找例程方法
- 三、一點教程
- 1.常用的延時寫法
- 2.如何 點亮or熄滅 一個LED燈
- 3.控制IO口輸入輸出
- 4.控制串口引腳輸出資訊
- 5.第一個影像例程HelloWord
- 6.單顏色識別例程
- 7.多顏色識別例程
- 8.【串口】多顏色識別-回傳色塊中心坐標
- 四、開啟TensorFlow Lite神經網路之旅
- 1.TensorFlow Lite介紹
- 2.使用支持
- 3.使用OpenMV采集訓練資料
- 4.上傳Edge Impulse進行訓練
- 4.1注冊賬號并創建Edge Impulse工程
- 4.2 上傳訓練集
- 4.3 生成特征
- 4.4 進行訓練
- 5.在OpenMV上部署
- 五、錯誤情況問題 【更新時間2021-8-04】
- 1.“OSError” 錯誤
- 2.運行速度慢
- 3.“MemoryError” 記憶體不足
- 4.其他問題
- 5.解決問題一般思路
一、了解OpenMV 4 plus的構成
??OpenMV 是一個開源專案,其使用stm32系列單片機作為主控單元,搭載攝像頭,將python語言決議器移植到openMV的主控芯片運行,使其可以使用python語言做一些影像處理相關的作業,最常見的從 OpenMV 2 搭載的 stm32f4 到 OpenMV 3 的 stm32f7,再到 OpenMV 4 的 stm32H7 ,主頻從180MHz一路升級到480MHz,記憶體也從256KB升級到1MB,而本次介紹的 OpenMV 4 plus版本在OpenMV 4的基礎上外掛了32 MB 的 SRAM 和 32 MB 的 Flash,將可用性再次提升一個大的臺階,

1.OpenMV 4 plus 的特點
1.1 硬體資源
| 資源 | 性能 |
|---|---|
| 處理器 | STM32H743II ARM Cortex M7 處理器,480 MHz ,1MB RAM,2 MB flash. |
| 介面 | 全速 USB (12Mbs) 介面,連接到電腦,當插入OpenMV攝像頭后,你的電腦會出現一個虛擬COM埠和一個“U盤” |
| 介面 | 一個μSD卡槽擁有100Mbs讀寫,這允許你的OpenMV攝像頭錄制視頻和把機器視覺的素材從SD卡提取出來 |
| 介面 | 一個SPI總線高達100Mbs速度,允許你簡單的把影像流資料傳給LCD擴展板,WiFi擴展板,或者其他控制器 |
| 介面 | 一個 I2C總線,CAN總線, 和2兩個異步串口總線 (TX/RX) ,用來鏈接其他控制器或者傳感器 |
| 介面 | 一個12-bit ADC 和一個12-bit DAC |
| 介面 | 2個 I/O 引腳用于舵機控制 |
| 介面 | 一個RGB LED(三色), 兩個高亮的 850nm IR LED(紅外) |
| 介面 | 所有的IO口都可以用于,中斷和PWM(板子上有10個I/O引腳) |
| 擴展 | 32 MB 外置的 32-bit SDRAM ,100 MHz的時鐘,達到 400 MB/s 的帶寬 |
| 擴展 | 32 MB 外置的 quadspi flash, 100 MHz的時鐘,4-bit DDR模式達到 100 MB/s 的帶寬 |
| 攝像頭 | 默認安裝 OV5640 感光元件的攝像頭,可處理2592×1944 (5MP)影像,并帶有M12標準的2.8mm焦距鏡頭 |
1.2 引腳圖

2.可用的學習資源
| 名稱 | 網址 | 備注 |
|---|---|---|
| 外文官網 | https://openmv.io/ | 唯一原版官網 |
| 外文檔案教程 | https://docs.openmv.io/openmvcam/quickref.html | 強調基礎控制 |
| 中國代理官網 | https://singtown.com/openmv/ | 星瞳科技 |
| 中文檔案教程 | https://docs.singtown.com/micropython/zh/latest/openmvcam/openmvcam/quickref.html# | 外文檔案教程的翻譯版(星瞳科技) |
| 中文檔案教程 | https://book.openmv.cc/quick-starter.html | 入門推薦 ☆ 星瞳科技 |
| 中文視頻教程 | https://singtown.com/learn/ | 入門推薦 ☆ 星瞳科技 |
| 開源專案地址 | https://github.com/openmv/openmv | github 韌體原始碼 |
二、建議的學習路線
1.學習python基礎語法
??python的學習資源非常豐富,但為了使用OpenMv并最終實作跑TensorFlow Lite神經網路進行垃圾分類,僅會使用其基礎語法和格式即可,以下為免費的入門教程,自己有其他的教程資源亦可,其他教程推薦去bilibili搜索python,大把的免費視頻,
| 名稱 | 網址 | 備注 |
|---|---|---|
| 中文檔案簡稱 | https://www.runoob.com/python3/python3-tutorial.html | 免費簡潔的學習網站 |
| 視頻教程 | https://www.bilibili.com/video/av27789609 | 著名小甲魚教程(幽默有趣,但是有點污) |
??前期學習python無需學得很精,如果有其它語言基礎如C語言,則只需要看python的基礎資料型別、基礎條件陳述句以及基礎回圈陳述句等與自己掌握語言的不同之處即可,如沒有語言基礎,則需要將以上內容在例程的基礎上訓練幾遍,讓自己熟悉編程規則,
2.練習OpenMV的基礎實驗
2.1 安裝軟體
??在開始使用OpenMV之前,您需要下載并安裝 OpenMV IDE(點擊藍字即可跳轉官網下載),如下圖,在Windows下,OpenMV IDE進入安裝程式后,它將自動安裝IDE以及OpenMV和MicroPython pyboard的驅動程式,只需按照默認的安裝程式提示即可,一般情況下一直點擊下一步就正常安裝完成了,要啟動OpenMV IDE,只需點擊開始選單中的快捷方式即可,【安裝有問題的點此】

2.2 了解基礎環境使用方法并連接設備
??通過USB資料線將OpenMV連接至電腦USB口,然后打開資源管理器,找到OpenMv生成的U盤,打開會發現包含一個main.py檔案,此檔案就是OpenMV在離線(未連接OpenMvIDE)上電時自動執行的第一個代碼檔案,

??之后打開剛安裝好的OpenMV IDE,將OpenMV生成的U盤內的main.py檔案拖至OpenMV IDE編輯器處打開該檔案,

??打開檔案后,首先點擊編輯器下方的連接圖示,之后再點擊編輯器下方運行按鈕,OpenMV就會在OpenMV IDE的控制下執行編輯器打開的main.py檔案,并在右側顯示攝像頭傳輸的影像流,

??點擊下方的串行終端部分可看到OpenMV列印的提示資訊,在默認例程中輸出為影像流的幀率,

2.3 常用基礎例程
2.3.1 查找例程方法
①在OpenMV IDE的 【檔案->示例->openMV】中可找到帶有的所有官方例程,

②在星瞳給出的教程檔案中,有對官方例程的翻譯和解釋,【點此前往】

③當然是根據自己的需求去萬能的百度了,,,
三、一點教程
在學習影像處理操作前,先學習一點基礎操作知識,
1.常用的延時寫法
??延時函式依賴于time模塊,所以在使用前應引入time模塊,
import time # 匯入庫檔案
time.sleep(1) # 延時1s
time.sleep(0.5) # 延時0.5s [引數以秒為單位,可傳入小數或整數]
time.sleep_ms(500) # 延時500ms
time.sleep_us(10) # 延時10us
2.如何 點亮or熄滅 一個LED燈
??首先OpenMV 4 plus上搭載了一個全彩RGB燈和兩個紅外LED,一個RGB LED內部包含3個LED,但是兩個紅外LED是受一個埠控制的,那么OpenMV 4 plus上就總共有4個控制LED的埠,如下表所示,
| LED | 埠編號 | 定義寫法 |
|---|---|---|
| 紅色LED | 1 | led_R = pyb.LED(1) |
| 紅色LED | 2 | led_G = pyb.LED(2) |
| 紅色LED | 3 | led_B = pyb.LED(3) |
| 兩個紅外LED | 4 | led_IR = pyb.LED(4) |
??在上表中可以看到在寫法中都為“y = pyb.LED(x)”,可知LED的控制所依賴pyb模塊,所以在程式的開始應先將pyb模塊引入,控制LED燈點亮的方法為led_R.on(),熄滅的方法為led_R.off(),相應的使用示例如下,
import pyb,time # 匯入庫檔案
led_R = pyb.LED(1) # 定義紅色LED控制
led_G = pyb.LED(2) # 定義綠色LED控制
led_B = pyb.LED(3) # 定義藍色LED控制
led_IR = pyb.LED(4) # 定義紅外LED控制
while(True): # 死回圈
led_R.on() # 紅色LED點亮
led_G.on() # 綠色LED點亮
led_B.on() # 藍色LED點亮
led_IR.on() # 紅外LED點亮
time.sleep_ms(500) # 延時0.5S
led_R.off() # 紅色LED熄滅
led_G.off() # 綠色LED熄滅
led_B.off() # 藍色LED熄滅
led_IR.off() # 紅外LED熄滅
time.sleep_ms(500) # 延時0.5S
3.控制IO口輸入輸出
??在上一個控制LED示例中并未通過直接控制IO口的方式去控制LED的狀態,而是呼叫了pyb庫中已經寫好的介面實作控制目的,此講來說一下如何直接控制一個IO口,首先IO的控制依賴Pin模塊,而Pin模塊又包含在pyb模塊中,所以在程式開始要提前添加Pin模塊【from pyb import Pin】,
??學習過stm32單片機的同學都知道,在初始化一個IO口時,需要配置IO口的引腳編號、輸入輸出模式以及上下拉模式等,在使用STM32為主控單元的OpenMV中也是如此,如下表所示,
| 配置模式 | 輸入or輸出模式代碼 | 上下拉模式代碼 |
|---|---|---|
| 下拉輸入 | Pin.IN | Pin.PULL_DOWN |
| 上拉輸入 | Pin.IN | Pin.PULL_UP |
| 浮空輸入 | Pin.IN | Pin.PULL_NONE |
| 開漏輸出 | Pin.OUT_OD | 無 |
| 推挽輸出 | Pin.OUT_PP | 無 |
??實際使用的例程如下:
from pyb import Pin # 引入Pin庫
p0_out = Pin('P0', Pin.OUT_PP) # 設定P0埠為推挽輸出模式
p0_out.high() # 設定P0埠輸出高電平
p0_out.low() # 設定P0埠輸出低電平
p1_in = Pin('P1', Pin.IN, Pin.PULL_UP) # 設定P1埠為上拉輸入模式
p1_value = p1_in.value() # 獲取P1埠的值,0或1,高電平為1,低電平為0
4.控制串口引腳輸出資訊
??在OpenMV 4 Plus中,有兩個串口可以使用,分別為串口1和串口三,引腳對應關系如下表所示,
| 串口 | 埠號 | 對應功能 |
|---|---|---|
| UART 1 | P5 | RX |
| UART 1 | P4 | TX |
| UART 3 | P0 | RX |
| UART 3 | P1 | TX |
??常用的串口相關函式如下表,
| 型別 | 函式名 | 示例 | 實作功能 |
|---|---|---|---|
| 配置 | UART() | uart = UART(3, 9600) | 創建串口3物件,并初始化串口3,波特率9600,其他引數默認 |
| 配置 | uart.init() | uart.init(9600, bits=8, parity=None, stop=1) | 重新配置串口引數【接UART()后使用】 波特率9600,8位資料長度,無校驗位,1位停止位 |
| 讀取 | uart.read() | str = uart.read() | 讀取所有可用字符 |
| 讀取 | uart.read() | str = uart.read(x) | 讀取x位字符 |
| 讀取 | uart.readchar() | num = uart.readchar() | 讀取一個字符,并回傳其整數形式 |
| 讀取 | uart.readline() | str = uart.read(x) | 讀取一行【到回車換行結束】 |
| 讀取 | uart.readinto() | uart.readinto(buf) | 讀取并存入緩沖區buf內 |
| 讀取狀態 | uart.any() | num = uart.any() | 回傳等待的字符數量,用于檢查是否有串口資料接收到 |
| 發送 | uart.write() | uart.write(‘abc’) | 發送字串‘abc’ |
| 發送 | uart.writechar() | uart.writechar(42) | 發送一個字符’B’ ,數值42對應ASCLL碼的符號B |
對串口功能的使用也是依賴與pyb模塊,在程式開始要提前添加pyb模塊,使用示例如下,
from pyb import UART # 匯入串口支持庫檔案
uart = UART(3, 115200) # 創建串口3設備,并設定波特率為115200
num = 132 # 隨便定義一個數值變數
num_str = "[%d]" % num # 將資料轉化為字串用于輸出
uart.write(fps_str+"\r\n") # 串口3輸出【通過 P4埠】
while(True):
if uart.any()>0: # 如果串口接收到資料【通過 P5埠】
str = uart.read() # 讀取接收到的字符
uart.write("redata:"+ str+ "\r\n") # 將收到的資料通過串口3輸出【通過 P4埠】
5.第一個影像例程HelloWord
??此例程為最基礎的HelloWord級例程,內部包含了相關模塊的依賴、攝像頭的初始化、拍照并存放至快取變數中以及列印影像的獲取幀率,以后的所有有關影像處理的程式都將以此程式為基礎進行撰寫,
import sensor, image, time # 引入此例程依賴的模塊,sensor是與攝像頭引數設定相關的模塊,image是影像處理相關的模塊,time時鐘控制相關的模塊,import相當于c語言的#include <>,模塊相當于c語言的庫,
sensor.reset() # 復位并初始化攝像頭,reset()是sensor模塊里面的函式
sensor.set_pixformat(sensor.RGB565) # 設定影像色彩格式,有RGB565色彩圖和GRAYSCALE灰度圖兩種
sensor.set_framesize(sensor.QVGA) # 將影像大小設定為QVGA (320x240)
# 設定影像像素大小,QQVGA: 160x120,QQVGA2: 128x160,QVGA: 320x240,VGA: 640x480, QQCIF: 88x72,QCIF: 176x144,CIF: 352x288
sensor.skip_frames(time = 2000) # 等待設定生效,
clock = time.clock() # 初始化時鐘,并創建一個時鐘物件來跟蹤FPS幀率,
while(True): # python while回圈,一定不要忘記加冒號“:”
clock.tick() # 更新FPS幀率時鐘,
img = sensor.snapshot() # 拍一張照片并回傳影像,截取當前影像,存放于變數img中,注意python中的變數是動態型別,不需要宣告定義,直接用即可,
print(clock.fps()) # 列印當前的幀率,注意: 當連接電腦后,OpenMV會變成一半的速度,當不連接電腦,幀率會增加,
6.單顏色識別例程
??在OpenMV IDE的選單【檔案->示例->OpenMV-ColotTracking】中依次找到例程,即為單顏色識別程式,

??單顏色識別的程式如下所示,可知此程式就是在上一個HelloWord例程的基礎上使用了find_blobs()方法對拍攝到的圖片進行顏色的識別,
import sensor, image, time
green_threshold = ( 0, 80, -70, -10, -0, 30) # 要識別的顏色LAB值
sensor.reset() # 初始化sensor
sensor.set_pixformat(sensor.RGB565) # 設定影像色彩格式,有RGB565色彩圖和GRAYSCALE灰度圖兩種
sensor.set_framesize(sensor.QVGA) # 使用QVGA的解析度
sensor.skip_frames(10) # 等待設定生效,
sensor.set_auto_whitebal(False) # 關閉白平衡,白平衡是默認開啟的,在顏色識別中,建議關閉白平衡,
while(True):
img = sensor.snapshot() # 拍一張照片并回傳影像,
blobs = img.find_blobs([green_threshold]) # 識別并回傳結果【回傳一個串列】
if blobs: # 如果找到了目標顏色【即串列不為空】
for b in blobs: # 遍歷找到的目標顏色區域【因為可能存在不只一個顏色塊】
img.draw_rectangle(b[0:4]) # 用矩形標記出目標顏色區域
img.draw_cross(b[5], b[6]) # 在目標顏色區域的中心畫十字形標記
print(b[5], b[6]) # 輸出目標物體中心坐標【通過串行終端 USB設備】
??其中find_blobs()方法的輸入引數是一個LAB格式的顏色閾值,作為欲識別的顏色,回傳值是一個包括識別到的每個色塊物件的串列,回傳值具體是個什么意思呢,首先是個串列,串列內的每個一條資訊都是找到的一個色塊的資訊,此資訊中又包含10個值,分別代表的意義如下表所示【假設串列中某條資訊為b】,串列內資訊值的使用除了可以用下標的方式取出,還可以通過[.變數名]的方式使用,如下表所示【假設串列中某條資訊為b】:
| 標號方式使用 | 內部方法方式使用 | 意義 |
|---|---|---|
| b[0] | b.x() | 識別到的目標顏色區域左上頂點的x坐標 |
| b[1] | b.y() | 識別到的目標顏色區域左上頂點的y坐標 |
| b[2] | b.w() | 識別到的目標顏色區域的寬 |
| b[3] | b.h() | 識別到的目標顏色區域的高 |
| b[4] | b.pixels() | 識別到的目標顏色區域的像素點的個數 |
| b[5] | b.cx() | 識別到的目標顏色區域的中心點x坐標 |
| b[6] | b.cy() | 識別到的目標顏色區域的中心點y坐標 |
| b[7] | b.rotation() | 識別到的目標顏色區域的旋轉角度(是弧度值,浮點型,串列其他元素是整型) |
| b[8] | b.count() | 識別到的所有顏色區域與此目標區域交叉的目標個數 |
| b[9] | b.code() | 識別到的目標顏色區域顏色的編號(它可以用來分辨這個區域是用哪個顏色閾值識別出來的) |
| 無 | blob.rect() | 回傳一個矩形元組(x, y, w, h) ,用于如色塊邊界框的 image.draw_rectangle 等 其他的 image 方法 |
??那么具體的待識別LAB顏色閾值如何設定呢,可以使用OpenMV IDE中的“閾值編輯器”工具,對攝像頭中拍攝的待識別影像進行LAB顏色閾值的調整,

??選擇幀緩沖區

??比如說要識別圖片中紅色的顏色,那么拖動下面的LAB最大最小值,使右側的串口中僅剩紅色部分為白色,此時視窗下方的LAB閾值即為需要識別的顏色的LAB閾值,此處我調整的經驗是,先將全部的最小值拖至最左側,全部的最大值拖至最右側,然后依次拖動LAB值,僅需一次完整程序即可將右側影像僅剩待處理顏色塊,【注意,白色部分是待識別的顏色】

??將獲得的LAB值復制,并粘貼值代碼中的green_threshold=()括號內,即完成顏色的識別設定,此時運行程式,即可準確的將攝像頭捕獲的影像中的紅色標注出來,

??如果欲讓OpenMV在不連接OpenMV IDE時也可以進行此程式功能,則可以在編輯器的選單欄【工具->將打開的腳本檔案保存到OpenMV Cam】中將程式代碼保存到OpenMV中去,

7.多顏色識別例程
??多顏色識別與單顏色識別基本上是一致的,都是使用了同一個方法find_blobs(),只不過將輸入的閾值從一個增加至了多個,閾值的設定也與單顏色的設定相同,使用“閾值編輯器”依次將每種顏色的影像進行處理即可,調整后的識別效果如下,

??多顏色識別代碼,其中find_blobs()方法比單顏色識別程式中多了兩個引數,pixels_threshold和area_threshold,其中pixels_threshold是設定邊界框區域閾值,若一個色塊的邊界框區域小于 area_threshold ,則會被過濾掉, pixel_threshold是設定像素數閾值,若一個色塊的像素數小于 pixel_threshold ,則會被過濾掉,
import sensor, image, time, math
# 多顏色識別最多可同時識別16種顏色
thresholds = [(0, 100, 13, 127, 34, 67), # 待識別顏色LAB閾值1
(81, 100, -128, -13, 13, 53), # 待識別顏色LAB閾值2
(38, 100, -87, 22, -98, -33)] # 待識別顏色LAB閾值3
sensor.reset() # 初始化攝像頭,reset()是sensor模塊里面的函式
sensor.set_pixformat(sensor.RGB565) # 設定影像色彩格式,有RGB565色彩圖和GRAYSCALE灰度圖兩種
sensor.set_framesize(sensor.QVGA) # 設定影像像素大小302*240
sensor.skip_frames(time = 2000) # 等待設定生效
sensor.set_auto_gain(False) # 顏色跟蹤建議關閉自動增益
sensor.set_auto_whitebal(False) # 顏色跟蹤建議關閉白平衡
while(True):
img = sensor.snapshot() # 拍照并獲取影像
for blob in img.find_blobs(thresholds, pixels_threshold=200, area_threshold=200): # 進行多顏色識別
img.draw_rectangle(blob.rect()) # 框出識別到的色塊
img.draw_cross(blob.cx(), blob.cy()) # 標出色塊中心點
8.【串口】多顏色識別-回傳色塊中心坐標
??在上個示例中已實作顏色的識別和色塊的框選,在教程的開始處還講解了串口的使用方法,本示例將其組合至一起,實作將識別到每個顏色色塊中心點的坐標通過串口發送出去,代碼如下:
import sensor, image, time, math # 匯入串口支持庫檔案
from pyb import UART # 匯入串口支持庫檔案
# 多顏色識別最多可同時識別16種顏色
thresholds = [(0, 100, 13, 127, 34, 67), # 待識別顏色LAB閾值1
(81, 100, -128, -13, 13, 53), # 待識別顏色LAB閾值2
(38, 100, -87, 22, -98, -33)] # 待識別顏色LAB閾值3
sensor.reset() # 初始化攝像頭,reset()是sensor模塊里面的函式
sensor.set_pixformat(sensor.RGB565) # 設定影像色彩格式,有RGB565色彩圖和GRAYSCALE灰度圖兩種
sensor.set_framesize(sensor.QVGA) # 設定影像像素大小302*240
sensor.skip_frames(time = 2000) # 等待設定生效
sensor.set_auto_gain(False) # 顏色跟蹤建議關閉自動增益
sensor.set_auto_whitebal(False) # 顏色跟蹤建議關閉白平衡
uart = UART(3, 115200) # 創建串口3設備,并設定波特率為115200
while(True):
uart.write("-------------------------\r\n") # 串口3輸出【通過 P4埠】
img = sensor.snapshot() # 拍照并獲取影像
for blob in img.find_blobs(thresholds, pixels_threshold=200, area_threshold=200): # 進行多顏色識別
img.draw_rectangle(blob.rect()) # 框出識別到的色塊
img.draw_cross(blob.cx(), blob.cy()) # 標出色塊中心點
Str_x = "[%3d]" % blob.cx() # 將中心點X坐標轉為長度為3的字串
Str_y = "[%3d]" % blob.cy() # 將中心點y坐標轉為長度為3的字串
if blob.code()==1: # 如果識別
uart.write("red X:"+Str_x+" Y:"+Str_y+"\r\n") # 串口3輸出【通過 P4埠】
if blob.code()==2:
uart.write("green X:"+Str_x+" Y:"+Str_y+"\r\n") # 串口3輸出【通過 P4埠】
if blob.code()==4:
uart.write("blue X:"+Str_x+" Y:"+Str_y+"\r\n") # 串口3輸出【通過 P4埠】
uart.write("-------------------------\r\n\r\n") # 串口3輸出【通過 P4埠】
??在以上程式中,對于當前資訊是什么顏色的,使用code()方法配合判斷,code()方法回傳的是用于識別的閾值串列中的顏色位置,即,本次識別使用的LAB顏色閾值在上面程式中thresholds串列中是第幾個顏色,在上面程式中thresholds串列中有三個顏色,第一個是紅色(0, 100, 13, 127, 34, 67),第二個是綠色(81, 100, -128, -13, 13, 53),第三個是藍色(38, 100, -87, 22, -98, -33),【(感謝網友的指導)所以,當code()方法回傳1證明本次識別到的是紅色,回傳2證明本次識別到的是綠色,回傳3證明本次識別到的是藍色,但是在實際的使用中發現,識別藍色的時候回傳值為4,此處本人尚未查明原因,若有朋友知道誠邀指導,】
??所以,當code()方法回傳二進制00000001->十進制1,證明本次識別到的是紅色;回傳00000010->2,證明本次識別到的是綠色;回傳00000100->4,證明本次識別到的是藍色,
??識別后在串口軟體中列印的效果如下:

四、開啟TensorFlow Lite神經網路之旅
本章對應星瞳視頻教程:OpenMV 4 Plus 訓練神經網路進行口罩識別
1.TensorFlow Lite介紹

??人工智能、神經網路以及機器學習等應用皆需要大量的計算資源以及高性能的處理器加成,動輒就是上GPU群或者云服務器集群來處理,想要在一般的嵌入式芯片或者單片機上執行是想都不要想的事情,
??但是當前的邊緣處理場景越來越多,需求也越來越大,所以一些針對IOT設備和嵌入式設備的遷移學習網路應運而生,而Tensorflow Lite就是在這樣的情境下誕生的,是在完整的Tensorflow上進行裁剪、優化以及量身定做的,是針對移動設備和嵌入式設備的專用輕量化解決方案,其占用空間小且延遲低,
①主要特點有:
???支持浮點運算和量化模型,并已針對移動平臺進行優化,可以用來創建和運行自定義模型,開發者也可以在模型中添加自定義操作,
???FlatBuffer格式
???具有在移動設備運行更快的內核解釋器
???支持通過Tensorflow訓練好的模型轉換為Tensorflow Lite格式(pd,h5等都可以)
???當支持所有優化操作時,模型小于300k,當僅支持inception v3和mobilenet模型優化時,模型小于200k
②預訓練模型:
???inception v3:用于目標檢測
???MobileNets:專門針對移動端的模型,具有低延遲,高速度,低記憶體,可用于影像識別,目標檢測,影像分割,但是精度小于inception v3
???量化版本的MobileNets,通過將float-32轉為int-8,在CPU上擁有更快的速度
???支持java,c++API
??以上談到的預訓練模型基于ImageNet資料集訓練,支持1000種類別,如果此資料集不能滿足你的專案需要,你需要準備自己的資料集和標簽,使用遷移學習重新訓練模型,
2.使用支持
??但是盡管Tensorflow Lite已經足夠小、足夠快速,但是作為資源非常緊俏的單片機來說,尤其是作影像處理,還是有點力不從心,好在OpenMV 4 Plus使用的單片機是當前stm32單片機中性能最強的H7系列,而且外擴了較大的SDRAM作為資料處理記憶體使用,已基本滿足進行一些資料量小、影像像素低的識別任務要求,
??但是滿足了基本的硬體資源支持,進行一款神經網路的搭建、訓練和除錯,還是需要付出極大的學習成本的,如果讓一個單片機從業者為了產品需求,進軍人工智能方面的學習,無論從產品周期來說、還是學習能力的考驗方面來說,都不是個好的方法,
??為了進一步降低神經網路模型在嵌入式設備部署的難度,在線訓練模型網站Edge Impulse誕生了,此網站專門針對各種設備創建了各種的訓練場景,以創建工程、設定引數的方式完成網路模型的訓練,基本步驟:添加訓練資料->選擇模型->設定訓練引數->點擊開始訓練->訓練結果準確度達到要求->生成指定設備的可執行代碼->直接將代碼下載到設備運行,直達網址:https://studio.edgeimpulse.com/login

3.使用OpenMV采集訓練資料
??在進行神經網路模型訓練前需要先將訓練用的資料準備好,本次訓練目的為垃圾分類,那么訓練用的資料為圖片,打開OpenMV IDE,在選單處選擇【工具->資料集編輯器->新資料集】,

??自己隨便選擇一個位置創建檔案夾命名為【資料集】(隨便),并點擊選擇檔案夾,

??之后會進入如下界面,首先可以創建你想要分類的所有檔案夾(也稱為標簽),之后依次在每個標簽下拍下此類物品的各角度照片,建議在100張左右即可,

??具體的操作如下(動圖,因為有5MB大小限制,動圖較模糊):

??根據自己的所需,創建對應的標簽并采集完足夠的素材,展現效果如下:

4.上傳Edge Impulse進行訓練
4.1注冊賬號并創建Edge Impulse工程
??打開Edge Impulse網站,注冊就是一般流程沒有什么特殊性,就不描述了,登陸成功后會跳轉到創建工程頁面,按下圖步驟創建工程,

??之后會進入指引頁面,因為是外文網站,所以整個網頁內容都是英文的,英語不好的小伙伴建議使用帶網頁翻譯功能的瀏覽器為好,我用的是Google Chrome瀏覽器自帶翻譯插件,翻譯后頁面效果如下,

4.2 上傳訓練集
??雖然OpenMV IDE帶有直接上傳至Edge Impulse工程的功能,但是經過實測,并不是那么好用,會因為各種原因如防火墻等影響導致無法上傳,所以建議直接手動上傳就可以了,點擊左側“資料采集”導航欄,進入上傳資料頁面,點擊上傳圖示進入上傳頁面,

??按照以下步驟,依次選擇檔案->設定輸入標簽->點擊上傳,完成一組標簽內容的上傳,重復此步驟,直至將采集到的全部的照片上傳完畢,

??上傳完之后,再次點擊左側“資料采集”導航欄,重新進入上傳資料頁面,將會看到已上傳的資料詳情,

4.3 生成特征
??首先點擊左側“創建沖動”導航按鈕,進行輸入資料物件、訓練模型的選擇,并點擊保存,如下動圖所示,【為方便處理,一般建議影像大小為96x96或160x160】

??點擊右側影像導航欄按鈕,選擇RGB模式,點擊保存,保存后會自動跳轉至生成特征界面,

??點擊生成特征按鈕,等待生成完畢,會顯示右側的生成特征的3維影像,如果顯示失敗,則證明采集的影像特征不明顯,導致特征有重疊部分,則需要重新對訓練的影像進行篩選后上傳訓練,

4.4 進行訓練
??至此萬事具備只欠東風,進入遷移學習訓練界面,依次設定訓練的引數,如訓練周數:越大訓練次數越多、時間越長,相應的結果準確率也越有可能高,學習率:此值決定了訓練的速度,如果過擬合過快,則要降低此值,最低置信度:比較好理解,就是準確率達到百分之多少,標記為識別成功,然后選擇神經網路架構,可根據自己設定沖動是設定的圖片大小進行選擇,也可以使用默認的,每個神經網路架構訓練出來的模型結果也是不同的,可以自己實驗最符合自己要求的結構,之后點擊開始訓練,等待訓練完成即可,

??如果最后的結果準確率不滿意,可嘗試更改引數重新訓練,如果一直不夠理想,可回傳之前的步驟重新設定圖片大小以及訓練引數等,甚至進一步考慮自己采集的訓練集影像是不是特征不明顯,導致的準確率低,在結果準確率滿意的情況下,可進行下一步,
5.在OpenMV上部署
??進入部署界面,選擇生成OpenMV韌體,點擊構建,會自動下載生成的OpenMV代碼和模型,

??下載下來的壓縮包內包含訓練完的模型“trained.tflite”,OpemMV執行模型的代碼“ei_image_classification.py”,以及模型內部包含的所有分類標簽“labels.txt”,將OpenMV生成的U盤內的檔案全部洗掉掉,然后將下載下來的壓縮包內的檔案全部復制進去,為保證OpenMV離線狀態下可自動執行識別程式,將復制進去后的“ei_image_classification.py”重命名為“main.py”,

??之后將U盤內的main.py檔案在OpenMV IDE內打開,并運行,此時觀察串口終端內會輸出識別結果以及準確率,輸出的標簽后面都跟著一個數值,此數值代表識別結果認為是此標簽的可能性,=1表示識別程式認為100%是此標簽的物品,

??如果想讓其輸出的結果為中文,則可打開labels.txt檔案,并將其內部的英文標簽對應位置翻譯為中文保存即可,修改完畢后效果如下,

??識別出結果后,可修改代碼通過串口輸出識別到的結果給其他單片機使用,串口相關的具體代碼已在文中有過說明,自己修改吧,😄😄😄
五、錯誤情況問題 【更新時間2021-8-04】
??這段時間以來,很多小伙伴在實踐此文章的神經網路部署方面出現了各種各樣的問題,再此做一個匯總和解決方案,
1.“OSError” 錯誤
??出現最多的無非是OSError:,如下圖樣式,翻譯過來呢是說:目前,僅支持float32輸入型別,當然也有出現float16錯誤的,

??最早出現此情況的一個朋友在我們共同的努力下,成功解決了此問題,雖然解決方法比較玄學,但是最終鎖定了原因出在訓練模型的引數配置上,下圖是問他最終什么原因他說的原話,他說把所有圖片放到了open mv 的flash中,不過我覺得這個應該是不需要的,根本原因應該是在訓練的配置上,

??解決思路如下:
??①首先要查看自己的open MV是否為4Plus,若不是,則無法使用此例程,
??②之后連接open MV IDE進行韌體升級,確保為最新的韌體,
??③下載我提供的,我訓練好并且經過驗證沒有任何問題的韌體【點此下載-gitee】,部署到自己的open MV上運行,看是否還有例外問題,
??④-1如果運行還有例外,說明您的open MV有問題,請更換官方正版open MV 4Plus再次嘗試,
??④-2如果運行無例外,則說明您訓練的模型有問題,請重新訓練,
??⑤如果鎖定是訓練問題,請仔細檢查自己采集的訓練影像,看特征是否明顯,請重新創建一個新的訓練工程,按照步驟重新配置,可以盡量使用默認引數,這里因為本人并不是專業的人工智能人員,故這里的引數也無法給您提供建議,自己多嘗試嘗試吧,只要思路正確,找到問題所在,總能夠解決,
2.運行速度慢
??其實本來作為openMV來說,運行此類程式就是比較勉強的,所以出現幀率很低的情況也屬正常,但是在注意一些細節的情況下也有優化的空間,比如調低影像的生成特征的解析度,適當調整訓練引數,以及更改為更適合的模型結構等,自己多試試總結總結吧,

3.“MemoryError” 記憶體不足
??還有小伙伴出現了以下的錯誤,提示說記憶體不足,注意這里的記憶體指的是RAM,是運行記憶體空間,而不是flash或者SD大小,出現這種情況,一般表明您的open MV不是4Plus,所以是無法運行此垃圾識別的程式的,

4.其他問題
??如出現“trained.tflite - 無法讀取壓縮包資料”,這樣的問題,請仔細檢查是否將模型放置到了openMV的代碼空間內,或者查看名稱是否有寫錯誤等,
??其實除了上述3個問題外,其他問題基本都屬于細節性問題了,自己靜下心來再仔細檢查檢查吧,
5.解決問題一般思路
??其實如何解決問題,如何找到問題的關鍵所在,是一種非常重要的能力,在自己的專案出現問題時,首先要將思路理清,一般首先將專案分為幾個大的部分,通過控制變數法依次排除掉可以確定沒問題的部分,將問題鎖定在某一個范圍內,再次重復此程序直至找到問題的根源所在,
??比如咱們這個專案,可以首先將其分為兩大塊:①open MV ②訓練的模型,
??接下來就要大致鎖定問題出在①部分還是②部分,在這里有兩個思路:
??(1)將自己訓練好的模型放到其他運行過此類代碼并且確認完好的openMV上去執行,這樣可以確認自己訓練的模型是否出現問題;
??(2)去下載別人訓練好并且經過驗證的模型,然后在自己的openMV上執行,這樣可以查看自己openMV是否出現問題,
??如果是模型出現問題,則要考慮是采集的訓練集有問題?特征生成配置有問題?訓練引數有問題?還是最后的模型結構不匹配?
??如果是openMV有問題,則考慮韌體是否為最新版?是否硬體出了問題?是否為正版設備?還是說型號搞錯了?等等,
??當然,本專案比較簡單,可以比較容易的鎖定問題所在,如果是比較龐大的專案,則可能需要更多的排除次數,但是思路是一致的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/348541.html
標籤:AI
