文章目錄

  • 系列文章目錄
  • 前言
  • 一、YOLO3算法簡介
  • 二、基於TensorFlow 2.x的輕量級YOLO3模型(YOLO3 Nano)簡介
  • 三、需要文件的下載安裝
  • 三、打標教程及技巧:為訓練準備高質量數據
  • 工具準備:LabelImg
  • 打標步驟
  • 打標技巧
  • 三、訓練模型教程:從數據到可用模型
  • 環境準備
  • 步驟1:配置數據集路徑
  • 步驟2:轉換數據集
  • 步驟3:生成錨框(Anchors)
  • 步驟4:訓練模型
  • 步驟5:評估模型
  • 四、部署模型到OpenART
  • 步驟1:導出TFLite模型
  • 步驟2:部署到OpenART
  • 步驟3:驗證部署效果
  • 總結

前言

上一篇博客 我們在講色塊檢測的時候有提到,很多情況下我們也會選擇yolo目標檢測作為一種更加穩定的方案,他不需要現場調節閾值,可以很穩定的實現對選定目標的框取定位。
而在比賽之外,在嵌入式設備上實現高效的目標檢測,一直是計算機視覺領域的熱門需求。無論是智能小車避障、無人機巡檢,還是便攜式安防設備,都需要輕量級、低功耗且精度足夠的模型。今天,我們就來聊聊如何利用YOLO3 Nano——一個基於TensorFlow 2.x的輕量級YOLO3實現,完成從數據打標、模型訓練到部署到OpenART硬件的全流程。

本文適合對目標檢測感興趣的初學者,無需深厚的深度學習理論基礎,跟着步驟操作就能上手。讓我們一步步把“目標檢測”搬進openart中吧

一、YOLO3算法簡介

在聊YOLO3 Nano之前,我們先簡單瞭解下它的“前輩”——YOLO3算法。

YOLO(You Only Look Once)是經典的單階段目標檢測算法,核心優勢是“快”。與兩階段算法(如Faster R-CNN)相比,YOLO將目標檢測視為一個端到端的迴歸問題,直接從圖像中預測目標的邊界框和類別,省去了複雜的區域提案(Region Proposal)步驟,因此推理速度極快,適合實時場景。

YOLO3在YOLOv1、v2的基礎上做了多項改進:

  1. 多尺度檢測:通過3個不同尺度的特徵圖(13×13、26×26、52×52),分別檢測大、中、小目標,提升了對小目標的識別能力;
  2. 特徵融合:使用跳躍連接(Skip Connection)融合不同層級的特徵(淺層特徵含細節,深層特徵含語義),讓檢測更精準;
  3. 骨幹網絡升級:採用Darknet-53作為特徵提取網絡,兼顧精度和速度;
  4. 錨框機制:預設9種不同尺寸的錨框(Anchors),通過聚類數據集目標尺寸生成,提升邊界框預測效率。

不過,YOLO3的Darknet-53網絡仍有較多參數,在嵌入式設備上(比如説本次智能車大賽指定的openmv視覺模塊或者是算力稍微好一點樹莓派、Nano)運行時會面臨算力和內存限制。因此,輕量級版本——YOLO3 Nano應運而生。

二、基於TensorFlow 2.x的輕量級YOLO3模型(YOLO3 Nano)簡介

YOLO3 Nano是針對資源受限設備設計的輕量化YOLO3實現,核心特點是“輕量”和“易用”:

  • 輕量級設計:簡化了YOLO3的骨幹網絡,減少卷積層數量和通道數,大幅降低模型參數(通常只有原YOLO3的1/10左右),適合在嵌入式設備(如OpenART)上運行;
  • TensorFlow 2.x支持:基於TensorFlow 2.x框架實現,兼容Keras高階API,訓練和部署更靈活,且支持導出為TFLite格式(輕量級推理格式);
  • 完整工具鏈:配套提供了數據集轉換、錨框生成、訓練、評估、推理的全套腳本,無需從零搭建流程;
  • OpenART適配:針對嵌入式硬件優化,可直接部署到OpenART設備(一種面向邊緣計算的智能硬件),滿足實時檢測需求。

三、需要文件的下載安裝

下載解壓之後是包含着四個文件或文件夾的,其具體作用在下面會詳細介紹

小白筆記:動手做目標檢測 --> YOLO v3(二)_幀率

三、打標教程及技巧:為訓練準備高質量數據

模型訓練的效果,很大程度上取決於數據集的質量。YOLO3 Nano支持VOC格式數據集,因此我們需要用工具標註圖像,並生成VOC格式的標註文件。 打標的意思就是為數據添加標籤,簡單來説就是告訴模型這個圖片裏面的哪些區域是我要檢測的東西,其屬於哪個類別(不過在這邊我們用的只有一個類別,因為我們暫時只需要進行目標檢測)

工具準備:LabelImg

LabelImg是一款開源的圖像標註工具,支持生成VOC格式(XML文件)的標註結果,操作簡單,適合初學者。

安裝命令(Windows/Mac/Linux通用):

pip install labelimg

安裝完成後,在終端輸入labelimg即可啓動。

或者是這個壓縮文件裏面其實是提供了完整的打標工具的,進入label_img目錄下直接運行mainUI.exe這個可執行文件既可進行打標

小白筆記:動手做目標檢測 --> YOLO v3(二)_數據集_02

打標步驟

  1. 準備圖像
    收集需要檢測的目標圖像,建議至少收集500張以上(數量越多,模型泛化能力越強)注意不可以直接用比賽官方提供的數據集進行標註,一定要把數據集全部打印出來用攝像頭一張一張的拍下在真實環境中的真實圖片(不過去年逐飛官方有提到一個p圖的方法,也可以參考參考),在目標檢測的時候倒是不需要拍下所有類別的所有照片,大概拍一部分500張以上就可以了,實測數據集在5000張以上的時候定位效果是非常穩定的。這個數據集也可以按拍照圖片的類別保存後,後期經過yolo模型處理之後可以作為訓練分類模型的數據集,就可以少拍一些之後的分類模型數據集了(這個才是最折磨人的)
  2. 啓動標註環境 導入圖片(以逐飛官方提供的labe_img為例):
    按上述步驟進入mainUI.exe之後界面如下,需要按圖片步驟導入拍攝的圖片路徑:
  3. 小白筆記:動手做目標檢測 --> YOLO v3(二)_幀率_03

  4. 點開導入之後有一個打開圖片文件夾的選項,選中你存放拍攝的圖片的路徑下就可以了,效果如下:
  5. 小白筆記:動手做目標檢測 --> YOLO v3(二)_目標檢測_04

  6. 新建工程
    點擊左上角的文件——然後選擇新建工程——在彈出的界面中選擇User Define Objects——在Input Types裏面輸入object(注意一定要是object 不可以是別的否則在訓練的時候會報錯,除非你的代碼基礎比較好可以直接嘗試修改備註文件或者標註工程文件)
  7. 標註目標
    這裏面的標註框選工具是默認喚醒的,直接進行框選就好,框選之後會出現類別的選擇項,由於這邊使用的是單目標檢測,所以直接選擇object_0就可以了
  8. 小白筆記:動手做目標檢測 --> YOLO v3(二)_目標檢測_05

  9. 打完這張圖的標記之後可以按Next進入下一張的標註
  10. 小白筆記:動手做目標檢測 --> YOLO v3(二)_目標檢測_06

  11. 導出數據集
    按導出數據集——保存voc文件既可進行打標結果的保存,這個保存的數據集是直接適配於文件中的yolo3Nano的訓練代碼的,不需要再進行格式調整什麼的。
    保存之後的是一個tar壓縮包,解壓之後數據集格式如下:

    JPEGImage就是原始的圖片數據

    Annotations文件夾是與圖片一一對應的打標數據(文件名相同,猴嘴不同為xml文件)

    文件打開之後格式如下:
data000716.jpgC:\Users\Z\Downloads\data\000716.jpgUnknown64048030
        objectUnspecified00191135449428

標註文件為 Pascal VOC 格式(常用於 YOLO 等模型訓練),主要信息如下:
標註圖像:位於 “data” 文件夾,文件名為 “000716.jpg”,路徑為 “C:\Users\Z\Downloads\data\000716.jpg”;
圖像尺寸:寬 640、高 480、深度 3(RGB 格式),無分割標註(segmented=0);
標註物體:1 個類別為 “object” 的物體,姿態未指定,未截斷(truncated=0),不難識別(difficult=0),邊界框座標為 xmin=191、ymin=135、xmax=449、ymax=428。

打標技巧

1、進入下一張的時候可以直接按空格復刻上一次的按鈕操作(Prev 或者 Next) 就不需要數據在框選和Next那邊來回點擊了
2、建議打一部分就按一次導出數據集——保存voc文件,不然萬一這個程序抽風崩潰或者卡死前面的標就全部白打了

三、訓練模型教程:從數據到可用模型

有了標註好的數據集,接下來我們用YOLO3 Nano的工具鏈訓練模型。

環境準備

首先安裝依賴庫(建議用虛擬環境隔離):

pip install tensorflow==2.11.0 numpy==1.23.4 pillow opencv-python configparser argparse matplotlib

步驟1:配置數據集路徑

  1. 打開剛才下載的文件夾裏面yolo3_smartcar路徑,找到項目根目錄下的config.cfg文件,用記事本或VS Code打開。
  2. 找到[dataset]部分的voc_folder參數,填寫VOC格式數據集的根目錄路徑:

    修改為剛才標註導出的tar文件的解壓之後的路徑
    數據集目錄結構需符合VOC格式(如下),其中JPEGImages放圖像,Annotations放XML標註文件,ImageSets/Main下放train.txt和val.txt(記錄訓練/驗證圖像的文件名):
VOC2007/
├─ JPEGImages/       # 圖像文件(.jpg)
├─ Annotations/      # 標註文件(.xml)
└─ ImageSets/
    └─ Main/
        ├─ train.txt  # 訓練集圖像文件名(無後綴)
        └─ val.txt    # 驗證集圖像文件名(無後綴)

步驟2:轉換數據集

YOLO3 Nano需要將VOC格式的XML文件轉換為模型可直接讀取的TFRecord格式(高效存儲數據)。運行以下命令:

python voc_convertor.py

腳本會自動讀取config.cfg中的數據集路徑,生成train.tfrecordval.tfrecord文件(保存在項目根目錄)。

小白筆記:動手做目標檢測 --> YOLO v3(二)_目標檢測_07

步驟3:生成錨框(Anchors)

錨框(Anchor Box) 是一種預先設定的、具有固定寬高比例的邊界框,用於輔助模型更高效地預測圖像中目標的位置和大小。它的核心作用是 “降低預測難度”—— 通過預設的 “模板框”,讓模型只需學習 “如何調整模板”,而不是從零開始預測目標的邊界框。

YOLO算法依賴錨框預測目標邊界框,錨框的尺寸需要根據數據集的目標尺寸通過K-means聚類生成(更貼合數據分佈)。運行:

python kmeans.py

腳本會輸出9個錨框的尺寸(如[[10,13], [16,30], ...]),並自動更新到config.cfganchors參數中。

小白筆記:動手做目標檢測 --> YOLO v3(二)_數據集_08


輸出結果解析:

1、發現了 2 個無效標註框(需檢查並修正,避免影響模型訓練);

2、聚類得到 3 個錨框(9×4、13×7、24×14),這些錨框會被用於 YOLO3 Nano 模型的邊界框預測;

3、錨框與真實目標的平均匹配度為 74.98%,聚類效果合格,可用於後續訓練

步驟4:訓練模型

運行訓練腳本,開始模型訓練:

python train.py

訓練過程中,腳本會自動加載數據集、初始化模型、計算損失並更新參數。關鍵參數可在config.cfg中調整:

  • batch_size:批次大小(根據顯卡顯存調整,建議8-32);
  • epochs:訓練輪數(建議100-300輪,根據驗證集精度調整);
  • learning_rate:學習率(初始建議0.001,後期可衰減)。

訓練過程中,模型會定期保存到checkpoints目錄,同時在logs目錄生成TensorBoard日誌(可通過tensorboard --logdir=logs查看損失和精度曲線)。

小白筆記:動手做目標檢測 --> YOLO v3(二)_幀率_09


這樣就是正確開始訓練了,耗時不會很長,具體根據你的batch_size和epochs等決定

步驟5:評估模型

訓練完成後,用驗證集評估模型性能(主要看mAP:平均精度均值):

python evaluate.py

輸出結果中,mAP值越高(接近1),模型性能越好。如果精度較低,可增加數據集數量、調整訓練參數或優化標註質量。

四、部署模型到OpenART

訓練好的模型需要轉換為嵌入式設備支持的格式,才能部署到OpenART。YOLO3 Nano已支持導出TFLite格式(輕量級推理格式),並附帶後處理邏輯(簡化部署流程)。

步驟1:導出TFLite模型

訓練完成後,項目會自動生成包含後處理的TFLite模型(yolo3_iou_smartcar_final_with_post_processing.tflite),無需額外轉換。

步驟2:部署到OpenART

OpenART是一款支持TFLite推理的嵌入式硬件,部署步驟超簡單:

  1. yolo3_iou_smartcar_final_with_post_processing.tflite複製到SD卡中;
  2. 將SD卡插入OpenART硬件的SD卡槽;
  3. 啓動OpenART,運行以下官方提供的代碼(我已加上詳細註釋很好理解)。硬件會自動加載TFLite模型並運行目標檢測:
# 導入必要的庫
# seekfree/pyb:OpenART硬件專用庫,用於控制底層硬件(如攝像頭、IO口)
# sensor:攝像頭傳感器控制庫,負責圖像採集
# image:圖像處理庫,用於繪製檢測框、圖像預處理等
# time:時間工具庫,用於計時和幀率計算
# tf:TensorFlow Lite推理庫,用於加載模型並執行目標檢測
# gc:垃圾回收庫,嵌入式設備內存有限,用於主動釋放內存避免溢出
import seekfree, pyb
import sensor, image, time, tf, gc
# ----------------------------
# 1. 攝像頭傳感器初始化配置
# ----------------------------
# 重置攝像頭傳感器(類似重啓,確保初始狀態正確)
sensor.reset()
# 設置像素格式:RGB565(每個像素佔2字節,兼顧色彩顯示和內存佔用)
# 可選格式:GRAYSCALE(灰度圖,1字節/像素,更省內存但無色彩)
sensor.set_pixformat(sensor.RGB565)
# 設置圖像分辨率:QVGA(320x240)
# 選擇原因:嵌入式設備算力有限,低分辨率可提升幀率(實時性更優);若需更高精度,可嘗試VGA(640x480)但幀率會下降
sensor.set_framesize(sensor.QVGA)
# 跳過初始2000ms的圖像幀
# 原因:攝像頭剛啓動時,曝光、白平衡等參數未穩定,前幾幀圖像可能偏暗/偏色,跳過可保證後續檢測穩定性
sensor.skip_frames(time=2000)
# 創建時鐘對象,用於計算實時幀率(FPS)
clock = time.clock()
# ----------------------------
# 2. 加載YOLO3 Nano模型
# ----------------------------
# 模型路徑:TFLite模型存儲在SD卡中(OpenART通常通過SD卡讀取外部文件)
# 此處路徑對應之前部署的"帶後處理的模型"(已集成NMS等後處理邏輯,直接輸出最終檢測結果)
model_path = '/sd/yolo3_iou_smartcar_final_with_post_processing.tflite'
# 加載模型:通過tf.load()方法將模型從SD卡加載到內存
# 注意:若模型加載失敗,可能是路徑錯誤或模型文件損壞,需檢查SD卡中的文件
net = tf.load(model_path)
# 主動觸發一次垃圾回收,釋放模型加載過程中可能產生的臨時內存佔用
gc.collect()
# ----------------------------
# 3. 實時目標檢測主循環
# ----------------------------
while True:
# 記錄當前時間(用於後續計算幀率)
clock.tick()
# 從攝像頭獲取一幀圖像(snapshot()返回image對象,可直接用於檢測和繪製)
img = sensor.snapshot()
# ----------------------------
# 3.1 執行目標檢測
# ----------------------------
# 使用加載的模型檢測圖像中的目標
# tf.detect(net, img)返回檢測結果列表:每個元素是一個目標的信息(座標、類別、置信度)
# 注:模型輸出的座標是"歸一化座標"(範圍0~1),需後續轉換為實際像素座標
detect_results = tf.detect(net, img)
# ----------------------------
# 3.2 處理檢測結果
# ----------------------------
for obj in detect_results:
# 解析單個目標的信息:
# x1, y1:目標左上角座標(歸一化,0~1)
# x2, y2:目標右下角座標(歸一化,0~1)
# label:目標類別ID(對應訓練時的類別,如0代表"車")
# scores:目標置信度(0~1,值越高,模型對該目標的判斷越可靠)
x1, y1, x2, y2, label, scores = obj
# 過濾低置信度目標:只保留置信度>70%的結果
# 目的:減少誤檢(模型可能把背景誤判為目標),提升檢測可靠性
if scores > 0.7:
# 打印目標信息(調試用,可查看檢測到的目標細節)
print(f"檢測到目標:類別={label},置信度={scores:.2f},座標=({x1:.2f},{y1:.2f})-({x2:.2f},{y2:.2f})")
# ----------------------------
# 3.3 座標轉換:歸一化座標 → 實際像素座標
# ----------------------------
# 模型輸出的x1,y1,x2,y2是相對於圖像寬高的比例(0~1),需轉換為實際像素值
# 圖像實際寬高:img.width()=320,img.height()=240(對應QVGA分辨率)
img_width = img.width()   # 圖像寬度(像素)
img_height = img.height() # 圖像高度(像素)
# 計算目標寬度和高度(歸一化)
w_norm = x2 - x1  # 歸一化寬度
h_norm = y2 - y1  # 歸一化高度
# 轉換為實際像素座標(x1_offset是可選的校準參數,若檢測框整體偏右,可減偏移量)
x1_pixel = int((x1 - 0.05) * img_width)  # 左上角x像素(-0.05為示例校準值,可根據實際偏移調整)
y1_pixel = int(y1 * img_height)          # 左上角y像素
w_pixel = int(w_norm * img_width)        # 寬度像素
h_pixel = int(h_norm * img_height)       # 高度像素
# 確保座標在圖像範圍內(避免超出圖像邊界導致繪製錯誤)
x1_pixel = max(0, min(x1_pixel, img_width))
y1_pixel = max(0, min(y1_pixel, img_height))
w_pixel = max(1, min(w_pixel, img_width - x1_pixel))  # 寬度至少為1,避免無效框
h_pixel = max(1, min(h_pixel, img_height - y1_pixel))
# ----------------------------
# 3.4 繪製檢測框
# ----------------------------
# 在圖像上繪製矩形框標記目標,線寬為2像素,顏色默認紅色(RGB565格式下可自定義)
img.draw_rectangle((x1_pixel, y1_pixel, w_pixel, h_pixel), thickness=2)
# 可選:在框上方繪製類別和置信度文本(增強可視化效果)
# 文本內容:類別ID + 置信度(保留2位小數)
text = f"cls{label}: {scores:.2f}"
# 繪製文本:位置在框左上角上方,字體大小1(最小),顏色紅色
img.draw_string(x1_pixel, y1_pixel - 10, text, color=(255, 0, 0), scale=1)
# ----------------------------
# 3.5 輸出實時幀率
# ----------------------------
# 計算並打印當前幀率(FPS = 每秒處理的圖像幀數)
# 嵌入式設備中,FPS越高説明實時性越好(通常需≥10FPS才流暢)
print(f"幀率:{clock.fps():.1f} FPS")
# 定期觸發垃圾回收,釋放循環中產生的臨時內存(避免內存泄漏)
gc.collect()

步驟3:驗證部署效果

如果需要在PC上提前驗證TFLite模型的效果,可運行項目提供的檢測腳本:

腳本會讀取圖像並輸出檢測結果(目標位置和類別),效果如下(由於openart都給下一屆學弟拿去學習了,現在手上沒有openart 這還是從以前視頻截的照片):

小白筆記:動手做目標檢測 --> YOLO v3(二)_數據集_10

總結

通過本文,我們完成了從數據打標、模型訓練到部署到OpenART的全流程。YOLO3 Nano的輕量級設計讓它在openart設備上“跑得動”,而TensorFlow 2.x的生態則簡化了訓練和部署流程。

回顧整個過程,高質量的數據集是模型效果的基礎,合理的錨框和訓練參數是模型精度的保障,而TFLite格式則是連接訓練與部署的橋樑。如果想進一步提升性能,可以嘗試:

  • 增加數據增強(如旋轉、裁剪、亮度調整);
  • 微調模型結構(如增加註意力機制);
  • 量化模型(TFLite支持INT8量化,進一步降低推理耗時)。