識(shí)別系統(tǒng)實(shí)戰(zhàn))
各位讀者朋友大家好今天整理一篇實(shí)戰(zhàn)型的硬核內(nèi)容基于 YOLOv8 / YOLOv5 與 PySide6 構(gòu)建的摔倒檢測(cè)與跌倒識(shí)別系統(tǒng)。之前在做實(shí)驗(yàn)室安全監(jiān)控和養(yǎng)老院看護(hù)場(chǎng)景項(xiàng)目時(shí)摔倒檢測(cè)是個(gè)出現(xiàn)頻率特別高的需求。市面上很多方案要么是純算法研究、不接地氣要么是商業(yè)套殼、可定制性太差。自己動(dòng)手用 YOLOv8 PySide6 搭一套既能跑通模型訓(xùn)練閉環(huán)又能做出帶界面、帶報(bào)警、帶錄像回放的真實(shí)系統(tǒng)非常適合做畢設(shè)、課設(shè)、項(xiàng)目實(shí)戰(zhàn)或生產(chǎn)原型。這篇文章會(huì)從需求分析、技術(shù)選型、環(huán)境配置、數(shù)據(jù)集制作、模型訓(xùn)練、界面開(kāi)發(fā)、實(shí)時(shí)檢測(cè)到打包發(fā)布完整梳理一套可落地的摔倒檢測(cè)系統(tǒng)。代碼均為可復(fù)用的工程化寫(xiě)法你在自己的項(xiàng)目里可以直接改改路徑和參數(shù)運(yùn)行。1. 摔倒檢測(cè)系統(tǒng)到底在解決什么問(wèn)題1.1 什么是摔倒檢測(cè)摔倒檢測(cè)也叫跌倒識(shí)別、跌倒檢測(cè)屬于計(jì)算機(jī)視覺(jué)中目標(biāo)檢測(cè)與行為識(shí)別交叉的一個(gè)具體應(yīng)用方向。它的核心任務(wù)很簡(jiǎn)單從視頻幀或圖像序列中判斷“人是否發(fā)生了摔倒”并在發(fā)生摔倒時(shí)產(chǎn)生告警。人體的摔倒過(guò)程通常有比較明顯的空間特征人體姿態(tài)從直立迅速變?yōu)樗交蚪咏饺梭w輪廓的寬高比發(fā)生劇烈變化運(yùn)動(dòng)速度在短時(shí)間內(nèi)驟增之后長(zhǎng)時(shí)間保持靜止。這些特征在目標(biāo)檢測(cè)模型上的體現(xiàn)就是檢測(cè)框的寬高比會(huì)在很短時(shí)間內(nèi)從“高瘦”變?yōu)椤鞍帧辈⑶胰梭w檢測(cè)框的中心點(diǎn)會(huì)發(fā)生快速位移。1.2 為什么選用 YOLOv8 / YOLOv5 來(lái)做摔倒檢測(cè)摔倒檢測(cè)本質(zhì)上是一個(gè)目標(biāo)檢測(cè) 狀態(tài)分類問(wèn)題。雖然也可以用姿態(tài)估計(jì)加規(guī)則判斷但對(duì)算力要求高、部署復(fù)雜。而 YOLO 系列檢測(cè)模型結(jié)構(gòu)簡(jiǎn)單、推理速度快、訓(xùn)練生態(tài)成熟非常適合做單階段的摔倒?fàn)顟B(tài)識(shí)別。YOLOv5 的優(yōu)勢(shì)是社區(qū)資料多、穩(wěn)定性好、部署方案豐富很多老項(xiàng)目和嵌入式設(shè)備都在用。YOLOv8 在 YOLOv5 的基礎(chǔ)上進(jìn)一步改進(jìn)了骨干網(wǎng)絡(luò)和檢測(cè)頭在精度與推理速度的平衡上表現(xiàn)更好而且默認(rèn)支持旋轉(zhuǎn)目標(biāo)檢測(cè)、實(shí)例分割、姿態(tài)估計(jì)等多種任務(wù)。兩個(gè)模型的訓(xùn)練方式和代碼結(jié)構(gòu)比較接近因此我們通常把訓(xùn)練、驗(yàn)證、導(dǎo)出流程統(tǒng)一封裝模型可以隨時(shí)切換。1.3 摔倒檢測(cè)的典型應(yīng)用場(chǎng)景智慧養(yǎng)老獨(dú)居老人、養(yǎng)老院房間內(nèi)跌倒檢測(cè)發(fā)生異常時(shí)自動(dòng)通知護(hù)理人員。工業(yè)安全工廠車間、建筑工地人員跌倒檢測(cè)。醫(yī)療監(jiān)護(hù)病房、康復(fù)中心患者跌倒檢測(cè)。公共場(chǎng)所安全地鐵站、商場(chǎng)、衛(wèi)生間等區(qū)域跌倒檢測(cè)。家庭安防家用攝像頭結(jié)合本地算力盒子的跌倒報(bào)警。從這些場(chǎng)景可以看出摔倒檢測(cè)項(xiàng)目除了模型訓(xùn)練之外還要求系統(tǒng)做到本地化運(yùn)行、界面可視化、報(bào)警聯(lián)動(dòng)。這也是本文選擇 PySide6 做上位機(jī)的原因。2. 系統(tǒng)總體架構(gòu)與識(shí)別流程2.1 系統(tǒng)整體架構(gòu)整個(gè)摔倒檢測(cè)系統(tǒng)可以拆成四個(gè)層數(shù)據(jù)層、模型層、應(yīng)用層、報(bào)警層。數(shù)據(jù)層負(fù)責(zé)視頻流接入支持本地視頻文件、攝像頭 RTSP/RTMP 流、USB 攝像頭。模型層使用 YOLOv8/YOLOv5 訓(xùn)練好的摔倒檢測(cè)模型對(duì)每一幀畫(huà)面進(jìn)行推理輸出檢測(cè)框和摔倒置信度。應(yīng)用層是 PySide6 編寫(xiě)的桌面程序負(fù)責(zé)視頻解碼、模型推理調(diào)度、界面展示、檢測(cè)結(jié)果標(biāo)注。報(bào)警層在檢測(cè)到摔倒時(shí)觸發(fā)報(bào)警音、彈窗、截圖保存和錄像保存。在工程實(shí)現(xiàn)上我們把模型推理納入單獨(dú)線程避免阻塞 PySide6 的 Qt 事件循環(huán)防止界面卡死。2.2 摔倒檢測(cè)識(shí)別流程整個(gè)系統(tǒng)執(zhí)行流程如下讀取視頻幀OpenCV 的 VideoCapture 負(fù)責(zé)。對(duì)畫(huà)面做必要預(yù)處理如縮放、歸一化。送入 YOLOv8/YOLOv5 模型推理得到檢測(cè)框坐標(biāo)、類別、置信度。根據(jù)類別判斷檢測(cè)框是否屬于摔倒類別。額外驗(yàn)證檢測(cè)框的寬高比、停留時(shí)間、置信度降低誤報(bào)率。如果在連續(xù)多幀中均判定為摔倒觸發(fā)報(bào)警并保存證據(jù)圖片和錄像。在 PySide6 界面中繪制檢測(cè)框和狀態(tài)信息。2.3 為什么要加幀間判定與目標(biāo)跟蹤簡(jiǎn)單按單幀檢測(cè)框來(lái)做摔倒報(bào)警誤報(bào)率會(huì)很高。因?yàn)槿讼露住澭鼡鞏|西時(shí)人體框的寬高比也會(huì)有短時(shí)變化。更穩(wěn)的思路是加入幀間邏輯對(duì)檢測(cè)到的目標(biāo)進(jìn)行輕量級(jí)跟蹤例如基于 IoU 的簡(jiǎn)單關(guān)聯(lián)或引入 ByteTrack統(tǒng)計(jì)目標(biāo)在連續(xù) N 幀中的狀態(tài)。如果目標(biāo)框在超過(guò)一定幀數(shù)內(nèi)保持摔倒?fàn)顟B(tài)才觸發(fā)報(bào)警這樣可以過(guò)濾掉大部分瞬時(shí)姿態(tài)變化造成的誤報(bào)。3. 開(kāi)發(fā)環(huán)境準(zhǔn)備與依賴安裝3.1 環(huán)境版本說(shuō)明不同版本環(huán)境可能導(dǎo)致模型權(quán)重不兼容、算子導(dǎo)出失敗所以先做一個(gè)版本說(shuō)明。本文以 Python 3.8/3.9 環(huán)境為例PySide6 建議使用 6.x 版本YOLOv8 使用 ultralytics 庫(kù)YOLOv5 使用官方倉(cāng)庫(kù)代碼。具體版本不寫(xiě)死因?yàn)槟愕?CUDA 版本、顯卡驅(qū)動(dòng)和 Python 版本不同安裝結(jié)果會(huì)有差異。如果你的顯卡是 GTX 1660 Ti、RTX 3060 等普通消費(fèi)級(jí)顯卡建議使用 CUDA 11.8 cuDNN 8.x顯存 6G 以上可以正常訓(xùn)練 YOLOv8s、YOLOv5s 等小模型。3.2 安裝依賴建議使用虛擬環(huán)境管理依賴避免污染系統(tǒng) Python。conda create -n fall_detection python3.9 conda activate fall_detection安裝 PyTorch 時(shí)根據(jù)你的顯卡選擇對(duì)應(yīng)版本。這里以 CUDA 11.8 為例pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118安裝 YOLOv8 官方庫(kù)pip install ultralytics安裝界面開(kāi)發(fā)和視頻處理相關(guān)庫(kù)pip install pyside6 opencv-python numpy pyyaml pip install pyinstaller驗(yàn)證安裝結(jié)果python -c import torch, cv2, PySide6, ultralytics; print(env ok)如果輸出env ok說(shuō)明環(huán)境基本可用。3.3 YOLOv5 / YOLOv8 的安裝區(qū)別YOLOv8 通過(guò)ultralytics包直接調(diào)用不需要手動(dòng) clone 倉(cāng)庫(kù)代碼集成度更高。YOLOv5 是傳統(tǒng)倉(cāng)庫(kù)形式需要下載源碼git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt如果后續(xù)訓(xùn)練腳本需要兼容兩個(gè)模型建議在工程中封裝模型加載函數(shù)分別處理兩種模型的前向推理輸出格式。4. 數(shù)據(jù)集收集與標(biāo)注4.1 摔倒檢測(cè)數(shù)據(jù)集的來(lái)源方式訓(xùn)練摔倒檢測(cè)模型數(shù)據(jù)是核心。數(shù)據(jù)集通常有三種來(lái)源第一是公開(kāi)數(shù)據(jù)集常用的有 Le2i Fall Detection Dataset、UR Fall Detection Dataset、Multiple Cameras Fall Dataset很多論文和開(kāi)源項(xiàng)目都在使用。使用公開(kāi)數(shù)據(jù)集時(shí)建議先檢查標(biāo)注格式轉(zhuǎn)換為 YOLO 格式后用于訓(xùn)練。第二是自行拍攝在實(shí)驗(yàn)室或?qū)嶋H場(chǎng)景中模擬摔倒動(dòng)作用手機(jī)或監(jiān)控?cái)z像頭錄制視頻然后抽幀標(biāo)注。模擬場(chǎng)景時(shí)可以覆蓋正面摔倒、側(cè)面摔倒、從椅子上滑落等不同角度。第三是網(wǎng)絡(luò)圖片爬取但在使用時(shí)要注意版權(quán)和個(gè)人隱私不建議大規(guī)模使用。4.2 視頻抽幀生成圖片如果原始數(shù)據(jù)是視頻需要先把視頻轉(zhuǎn)成圖片。下面是一個(gè)抽幀工具腳本。# tools/extract_frames.py import cv2 import os def extract_frames(video_path, output_dir, interval10): os.makedirs(output_dir, exist_okTrue) cap cv2.VideoCapture(video_path) frame_id 0 saved_id 0 while True: ret, frame cap.read() if not ret: break if frame_id % interval 0: out_path os.path.join(output_dir, fframe_{saved_id:06d}.jpg) cv2.imwrite(out_path, frame) saved_id 1 frame_id 1 cap.release() print(ftotal frames: {frame_id}, saved images: {saved_id}) if __name__ __main__: extract_frames( video_pathdata/source_fall_video.mp4, output_dirdata/images/train, interval5, )抽幀間隔建議根據(jù)視頻幀率調(diào)整一般每秒抽 2-5 幀即可。如果抽幀太密相鄰圖片相似度太高容易造成訓(xùn)練集冗余。4.3 使用 LabelImg 或 LabelStudio 標(biāo)注YOLO 格式的標(biāo)注文件是 txt 文件每行表示一個(gè)目標(biāo)class_id center_x center_y width height其中center_x、center_y、width、height都是相對(duì)于圖片寬度和高度的歸一化值。標(biāo)注時(shí)建議定義兩類目標(biāo)normal正常站立/行走和fall摔倒。也可以只標(biāo)注摔倒目標(biāo)然后訓(xùn)練一個(gè)單類別檢測(cè)器。LabelImg 是輕量級(jí)標(biāo)注工具適合圖片標(biāo)注。LabelStudio 支持視頻標(biāo)注、多人協(xié)作適合較大的團(tuán)隊(duì)項(xiàng)目。標(biāo)注完成后一個(gè)典型的 YOLO 數(shù)據(jù)集目錄結(jié)構(gòu)如下fall_dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/在images/train和labels/train下每張圖片對(duì)應(yīng)一個(gè)同名 txt 文件。4.4 數(shù)據(jù)集增強(qiáng)摔倒檢測(cè)場(chǎng)景中正樣本摔倒往往比負(fù)樣本正常狀態(tài)少??梢酝ㄟ^(guò)以下方式擴(kuò)充數(shù)據(jù)對(duì)圖片做左右翻轉(zhuǎn)。調(diào)整亮度、對(duì)比度、飽和度。隨機(jī)裁剪。Mosaic 增強(qiáng)YOLOv5/YOLOv8 內(nèi)置支持。在訓(xùn)練配置中開(kāi)啟 Mosaic 可以顯著提升模型對(duì)遮擋和密集場(chǎng)景的魯棒性。5. YOLOv8 模型訓(xùn)練與驗(yàn)證5.1 構(gòu)建數(shù)據(jù)配置文件在 Ultralytics YOLOv8 中訓(xùn)練數(shù)據(jù)需要一個(gè) YAML 文件來(lái)定義數(shù)據(jù)集路徑和類別。# config/fall_dataset.yaml path: E:/fall_detection_project/dataset/fall_dataset train: images/train val: images/val nc: 2 names: 0: normal 1: fall注意path字段需要改成你自己的實(shí)際路徑建議使用絕對(duì)路徑避免訓(xùn)練時(shí)找不到數(shù)據(jù)。5.2 編寫(xiě)統(tǒng)一訓(xùn)練腳本為了讓 YOLOv5 和 YOLOv8 可以共用一套訓(xùn)練入口我們簡(jiǎn)單封裝一個(gè)訓(xùn)練腳本。這里先展示 YOLOv8 的訓(xùn)練方式。# tools/train_yolov8.py from ultralytics import YOLO def train_yolov8(): model YOLO(yolov8s.pt) model.train( dataconfig/fall_dataset.yaml, epochs100, imgsz640, batch16, device0, workers4, lr00.01, patience20, projectruns/train, namefall_det_yolov8s, pretrainedTrue, optimizerauto, ) if __name__ __main__: train_yolov8()訓(xùn)練時(shí)的參數(shù)說(shuō)明epochs訓(xùn)練輪數(shù)數(shù)據(jù)集在幾百?gòu)垐D片時(shí)可以設(shè)到 150-200 輪但需要配合早停機(jī)制。imgsz輸入圖片尺寸640 是默認(rèn)值小顯存可以降到 512。batch批大小GTX 1660 Ti 6G 顯存建議設(shè)為 8-16。device顯卡編號(hào)CPU 訓(xùn)練則填cpu。patience早停耐心值驗(yàn)證集指標(biāo)連續(xù)多少輪不提升就停止訓(xùn)練。5.3 使用 YOLOv5 訓(xùn)練摔倒檢測(cè)模型如果你希望使用 YOLOv5 倉(cāng)庫(kù)訓(xùn)練進(jìn)入克隆好的 yolov5 目錄后執(zhí)行python train.py \ --data config/fall_dataset.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --device 0 \ --project runs/train \ --name fall_det_yolov5sYOLOv5 的 data yaml 與 YOLOv8 略有不同可以參考yolov5/data/coco128.yaml的格式修改。5.4 YOLOv8 增量訓(xùn)練方法如果你已經(jīng)有一個(gè)訓(xùn)練好的摔倒檢測(cè)模型希望在新增數(shù)據(jù)集上繼續(xù)訓(xùn)練YOLOv8 提供了增量訓(xùn)練能力。做法是加載上一個(gè)訓(xùn)練任務(wù)產(chǎn)出的 best.pt 權(quán)重然后在舊數(shù)據(jù)和新數(shù)據(jù)的合并集上繼續(xù)訓(xùn)練。model YOLO(runs/train/fall_det_yolov8s/weights/best.pt) model.train( dataconfig/fall_dataset_v2.yaml, epochs50, imgsz640, batch16, resumeFalse, )增量訓(xùn)練是實(shí)際項(xiàng)目中非常重要的能力。因?yàn)椴杉礁嗾鎸?shí)場(chǎng)景數(shù)據(jù)后直接重新訓(xùn)練會(huì)丟失已學(xué)到的知識(shí)而在舊權(quán)重基礎(chǔ)上繼續(xù)訓(xùn)練既保留原有檢測(cè)能力又逐步適應(yīng)新場(chǎng)景。5.5 損失函數(shù)曲線與訓(xùn)練結(jié)果分析訓(xùn)練完成后在runs/train/fall_det_yolov8s/目錄下會(huì)生成results.png包含了訓(xùn)練過(guò)程中的損失曲線、精度、召回率、mAP 等指標(biāo)。針對(duì)摔倒檢測(cè)任務(wù)重點(diǎn)關(guān)注兩個(gè)指標(biāo)mAP0.5在 IoU 閾值為 0.5 時(shí)的平均精度均值如果數(shù)據(jù)集中摔倒目標(biāo)較小需要關(guān)注該值是否達(dá)到 0.85 以上。Recall摔倒樣本的召回率如果召回率過(guò)低說(shuō)明很多摔倒目標(biāo)被漏檢這在實(shí)際場(chǎng)景中非常危險(xiǎn)。還可以在訓(xùn)練工具中增加自定義繪制腳本把每輪的損失值畫(huà)成更清晰的曲線圖。5.6 模型驗(yàn)證與推理測(cè)試訓(xùn)練完成后用驗(yàn)證集評(píng)估模型效果from ultralytics import YOLO model YOLO(runs/train/fall_det_yolov8s/weights/best.pt) metrics model.val(dataconfig/fall_dataset.yaml, splitval) print(metrics.box.map50) print(metrics.box.map)然后用測(cè)試視頻做一次快速推理from ultralytics import YOLO model YOLO(runs/train/fall_det_yolov8s/weights/best.pt) results model.predict( sourcedata/test_fall_video.mp4, conf0.4, iou0.5, saveTrue, projectruns/detect, nametest_fall, )這里conf0.4的含義是只有置信度超過(guò) 0.4 的檢測(cè)框才會(huì)被保留。在實(shí)際項(xiàng)目中這個(gè)閾值需要根據(jù)誤報(bào)和漏報(bào)的接受程度進(jìn)行調(diào)優(yōu)。6. PySide6 上位機(jī)界面開(kāi)發(fā)6.1 PySide6 與 PySide 的區(qū)別PySide6 是 Qt for Python 的官方版本對(duì)應(yīng) Qt 6 版本。PySide 是老稱呼PySide2 對(duì)應(yīng) Qt 5PySide6 對(duì)應(yīng) Qt 6。兩者 API 接近但 PySide6 更新、對(duì)新 Python 版本支持更好所以新項(xiàng)目建議直接使用 PySide6。很多初學(xué)者會(huì)混淆 PySide6、PyQt5、PyQt6 這幾個(gè)庫(kù)。它們的底層都是 Qt區(qū)別在于授權(quán)協(xié)議和 API 風(fēng)格。PySide6 是 LGPL 協(xié)議相對(duì)寬松適合做工具軟件。6.2 界面功能設(shè)計(jì)摔倒檢測(cè)系統(tǒng)界面需要展示實(shí)時(shí)視頻畫(huà)面、檢測(cè)結(jié)果并提供操作按鈕。我們?cè)O(shè)計(jì)的界面包含以下功能模塊視頻顯示區(qū)域展示原始畫(huà)面和檢測(cè)框繪制結(jié)果。操作按鈕打開(kāi)視頻、打開(kāi)攝像頭、開(kāi)始檢測(cè)、停止檢測(cè)。狀態(tài)信息欄顯示當(dāng)前檢測(cè)狀態(tài)、FPS、檢測(cè)目標(biāo)數(shù)量。報(bào)警信息區(qū)顯示最近一次報(bào)警時(shí)間、保存的截圖路徑。統(tǒng)計(jì)信息當(dāng)天報(bào)警次數(shù)、累計(jì)摔倒次數(shù)。參數(shù)配置置信度閾值、報(bào)警間隔、是否保存錄像。6.3 創(chuàng)建主窗口框架下面是一個(gè)簡(jiǎn)潔但完整的 PySide6 主窗口示例。# ui/main_window.py import sys from PySide6.QtWidgets import QMainWindow, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QFileDialog, QMessageBox, QLineEdit, QSpinBox from PySide6.QtCore import Qt, QTimer from PySide6.QtGui import QImage, QPixmap class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(摔倒檢測(cè)跌倒識(shí)別系統(tǒng)) self.resize(1024, 720) self.video_label QLabel(視頻將在播放后顯示) self.video_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.video_label.setStyleSheet(background-color: #1e1e1e; color: #cccccc;) self.btn_open_video QPushButton(打開(kāi)視頻) self.btn_open_camera QPushButton(打開(kāi)攝像頭) self.btn_start QPushButton(開(kāi)始檢測(cè)) self.btn_stop QPushButton(停止檢測(cè)) layout_control QHBoxLayout() layout_control.addWidget(self.btn_open_video) layout_control.addWidget(self.btn_open_camera) layout_control.addWidget(self.btn_start) layout_control.addWidget(self.btn_stop) self.status_label QLabel(狀態(tài)未啟動(dòng)) self.fps_label QLabel(FPS0) layout_info QHBoxLayout() layout_info.addWidget(self.status_label) layout_info.addStretch() layout_info.addWidget(self.fps_label) self.msg_label QLabel(報(bào)警信息無(wú)) layout_msg QHBoxLayout() layout_msg.addWidget(self.msg_label) main_layout QVBoxLayout() main_layout.addWidget(self.video_label) main_layout.addLayout(layout_control) main_layout.addLayout(layout_info) main_layout.addLayout(layout_msg) container QWidget() container.setLayout(main_layout) self.setCentralWidget(container) self._init_components() def _init_components(self): self.video_path None self.camera_index 0 self.timer QTimer() self.timer.timeout.connect(self.update_frame) def update_frame(self): pass這是一個(gè)基礎(chǔ)窗口后續(xù)我們會(huì)把視頻讀取和模型推理集成進(jìn)來(lái)。6.4 使用 QLineEdit 配置信度閾值和報(bào)警間隔界面需要開(kāi)放參數(shù)調(diào)節(jié)功能例如置信度閾值、報(bào)警去重時(shí)間。我們可以在界面下方加入一行參數(shù)配置框。在_init_components方法中增加配置組件self.conf_label QLabel(置信度閾值:) self.conf_edit QLineEdit(0.45) self.conf_edit.setFixedWidth(80) self.interval_label QLabel(報(bào)警間隔(s):) self.interval_edit QLineEdit(5) self.interval_edit.setFixedWidth(80) layout_param QHBoxLayout() layout_param.addWidget(self.conf_label) layout_param.addWidget(self.conf_edit) layout_param.addWidget(self.interval_label) layout_param.addWidget(self.interval_edit) layout_param.addStretch()在主布局中把layout_param加入即可。這里使用QLineEdit接收用戶輸入。實(shí)際操作中我們應(yīng)該對(duì)用戶輸入做校驗(yàn)比如判斷輸入是否為浮點(diǎn)數(shù)避免程序崩潰。def get_conf_threshold(self): try: val float(self.conf_edit.text()) if 0 val 1: return val else: raise ValueError(閾值范圍必須為 (0, 1]) except ValueError as e: QMessageBox.warning(self, 參數(shù)錯(cuò)誤, str(e)) return 0.45這個(gè)組件在實(shí)際項(xiàng)目中經(jīng)常被問(wèn)到“PySide6 QLineEdit 是否輸入”的問(wèn)題本質(zhì)就是校驗(yàn)輸入內(nèi)容是否合法。7. 攝像頭實(shí)時(shí)檢測(cè)與報(bào)警聯(lián)動(dòng)7.1 視頻處理線程設(shè)計(jì)在 PySide6 中如果在主線程直接讀取攝像頭視頻流和模型推理會(huì)導(dǎo)致界面卡頓。正確做法是使用子線程完成視頻讀取與推理再通過(guò)信號(hào)把處理后的畫(huà)面?zhèn)鬟f回主線程。我們用QThread來(lái)寫(xiě)視頻處理線程。# core/detect_thread.py import cv2 import numpy as np from PySide6.QtCore import QThread, Signal from ultralytics import YOLO class DetectThread(QThread): frame_ready Signal(np.ndarray) detec_result Signal(int, float) error_msg Signal(str) def __init__(self, model_path, conf_threshold0.45, interval5, parentNone): super().__init__(parent) self.model_path model_path self.conf_threshold conf_threshold self.interval interval self.running False self.model None self.cap None def load_model(self): self.model YOLO(self.model_path) def set_video_source(self, source): self.source source def stop(self): self.running False def run(self): self.running True self.cap cv2.VideoCapture(self.source) if not self.cap.isOpened(): self.error_msg.emit(無(wú)法打開(kāi)視頻源) return while self.running: ret, frame self.cap.read() if not ret: break results self.model.predict( frame, confself.conf_threshold, imgsz640, verboseFalse, ) annotated results[0].plot() self.frame_ready.emit(annotated) fall_count 0 for r in results: boxes r.boxes if boxes is not None: for box in boxes: cls int(box.cls[0]) if self.model.names[cls] fall: fall_count 1 self.detec_result.emit(fall_count, self.conf_threshold) self.cap.release()在這個(gè)線程中frame_ready信號(hào)把繪制好的圖像傳回主界面detec_result信號(hào)把摔倒目標(biāo)數(shù)量傳給主界面更新報(bào)警信息。7.2 報(bào)警邏輯實(shí)現(xiàn)實(shí)際部署時(shí)需要把“單幀檢測(cè)到摔倒”升級(jí)為“連續(xù)幀確認(rèn)摔倒”。可以在DetectThread內(nèi)部維護(hù)一個(gè)計(jì)數(shù)器。self.fall_candidate_count 0 self.fall_trigger_frame 10如果當(dāng)前幀檢測(cè)到摔倒目標(biāo)則計(jì)數(shù)器加 1否則計(jì)數(shù)器清零或者減 1。當(dāng)計(jì)數(shù)器超過(guò)閾值就觸發(fā)一次報(bào)警。這樣可以過(guò)濾掉因?yàn)閺澭?、蹲下等?dòng)作產(chǎn)生的瞬時(shí)誤報(bào)。觸發(fā)報(bào)警后要控制報(bào)警頻率。假設(shè)用戶設(shè)置報(bào)警間隔為 5 秒那么與上次報(bào)警時(shí)間小于 5 秒時(shí)不再重復(fù)報(bào)警防止同一摔倒事件刷屏。import time self.last_alert_time 0 def maybe_alert(self): now time.time() if now - self.last_alert_time self.interval: self.last_alert_time now self.alert_signal.emit(檢測(cè)到摔倒行為!)7.3 報(bào)警截圖與錄像保存發(fā)生報(bào)警時(shí)除了界面提示外還應(yīng)該保存現(xiàn)場(chǎng)圖片和錄像片段。截圖保存比較簡(jiǎn)單保存當(dāng)前幀為 JPEG 文件即可def save_snapshot(self, frame, save_diralerts): from datetime import datetime os.makedirs(save_dir, exist_okTrue) ts datetime.now().strftime(%Y%m%d_%H%M%S) path os.path.join(save_dir, ffall_{ts}.jpg) cv2.imwrite(path, frame) return path錄像保存建議使用 OpenCV 的 VideoWriter。在報(bào)警觸發(fā)后記錄接下來(lái) 10-15 秒的視頻。fourcc cv2.VideoWriter_fourcc(*mp4v) writer cv2.VideoWriter(fall_alert.mp4, fourcc, 20.0, (width, height))注意在 PySide6 中頻繁進(jìn)行磁盤(pán)寫(xiě)入會(huì)阻塞線程建議把保存操作放到獨(dú)立隊(duì)列或使用異步方式。7.4 主界面接入檢測(cè)線程在主窗口打開(kāi)視頻時(shí)創(chuàng)建DetectThread并連接信號(hào)。# ui/main_window.py 部分代碼補(bǔ)充 from core.detect_thread import DetectThread def open_video(self): file_path, _ QFileDialog.getOpenFileName( self, 選擇視頻文件, , 視頻文件 (*.mp4 *.avi *.mov *.mkv) ) if file_path: self.thread DetectThread( model_pathmodels/best.pt, conf_thresholdself.get_conf_threshold(), intervalint(self.interval_edit.text()) ) self.thread.set_video_source(file_path) self.thread.load_model() self.thread.frame_ready.connect(self.show_frame) self.thread.detec_result.connect(self.update_status) self.thread.error_msg.connect(self.show_error) self.thread.start() self.status_label.setText(狀態(tài)檢測(cè)中) def show_frame(self, frame): frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch frame.shape bytes_per_line ch * w image QImage(frame.data, w, h, bytes_per_line, QImage.Format.Format_RGB888) self.video_label.setPixmap(QPixmap.fromImage(image).scaled( self.video_label.size(), Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation, ))這樣點(diǎn)擊“打開(kāi)視頻”后就能在界面中看到實(shí)時(shí)檢測(cè)結(jié)果。8. 項(xiàng)目啟動(dòng)入口與打包發(fā)布8.1 編寫(xiě) main.py 啟動(dòng)文件我們把模型加載、界面創(chuàng)建、線程管理統(tǒng)一在一個(gè)入口中。# main.py import sys from PySide6.QtWidgets import QApplication from ui.main_window import MainWindow def main(): app QApplication(sys.argv) window MainWindow() window.show() sys.exit(app.exec()) if __name__ __main__: main()為了減少每次啟動(dòng)加載模型的等待時(shí)間可以在MainWindow初始化時(shí)預(yù)加載模型再在打開(kāi)視頻時(shí)把模型權(quán)重傳入檢測(cè)線程。8.2 使用 PyInstaller 打包為 exePySide6 和 ultralytics 的依賴較多打包時(shí)要注意隱藏文件和資源文件。建議使用 PyInstaller 的 spec 文件配置資源pyinstaller main.spec簡(jiǎn)單時(shí)也可以直接使用命令行pyinstaller -w main.py --name FallDetect \ --hidden-import ultralytics.nn.modules \ --add-data models/best.pt;models \ --add-data config/fall_dataset.yaml;config注意--add-data把模型權(quán)重和配置文件打包進(jìn) exe 的同級(jí)目錄。-w表示不顯示控制臺(tái)窗口也就是 GUI 模式。如果在 Windows 上打包后運(yùn)行提示找不到ultralytics相關(guān)模塊需要補(bǔ)充 hidden-import??梢园颜麄€(gè)ultralytics包復(fù)制到項(xiàng)目目錄直接用相對(duì)路徑導(dǎo)入減少打包問(wèn)題。8.3 模型導(dǎo)出為 ONNX 加速推理如果部署環(huán)境沒(méi)有 PyTorch或者希望用 OpenVINO、TensorRT 加速可以將模型導(dǎo)出為 ONNX 格式model.export(formatonnx, imgsz640)導(dǎo)出完成后可以使用 OpenCV 的 DNN 模塊或 ONNX Runtime 加載模型推理速度通常比原始 PyTorch 更快適合嵌入式設(shè)備部署。import onnxruntime as ort session ort.InferenceSession(models/best.onnx, providers[CPUExecutionProvider])在 OpenVINO 環(huán)境下的 Intel CPU 推理速度提升尤其明顯。9. 常見(jiàn)問(wèn)題與排查思路9.1 環(huán)境安裝相關(guān)問(wèn)題現(xiàn)象常見(jiàn)原因解決思路PySide6 安裝失敗Python 版本過(guò)低或過(guò)高使用 Python 3.8-3.11 并重新安裝torch 安裝后 GPU 不可用CUDA 版本與 PyTorch 不匹配執(zhí)行torch.cuda.is_available()檢查按顯卡驅(qū)動(dòng)選擇合適 CUDAultralytics 導(dǎo)入報(bào)錯(cuò)依賴沖突重新創(chuàng)建虛擬環(huán)境并按順序安裝GTX 1660 Ti 運(yùn)行 YOLOv8 很慢沒(méi)有使用 GPU 推理或顯存不足設(shè)置device0調(diào)小imgsz和batch9.2 訓(xùn)練與推理相關(guān)問(wèn)題問(wèn)題現(xiàn)象常見(jiàn)原因解決思路訓(xùn)練時(shí)很快停止patience設(shè)置太小或?qū)W習(xí)率設(shè)置不合理調(diào)大patience使用自動(dòng)學(xué)習(xí)率優(yōu)化器檢測(cè)框置信度普遍偏低輸入圖像模糊或目標(biāo)太小提高標(biāo)注質(zhì)量增加樣本數(shù)量摔倒類別漏檢較多正樣本數(shù)據(jù)不充足做更多翻轉(zhuǎn)、旋轉(zhuǎn)增強(qiáng)或收集更多摔倒樣本程序界面卡頓在 Qt 主線程執(zhí)行了推理使用 QThread 子線程處理推理模型對(duì)正常彎腰誤判為摔倒只依賴單幀檢測(cè)框增加連續(xù)幀確認(rèn)邏輯和寬高比過(guò)濾9.3 PySide6 打包問(wèn)題問(wèn)題現(xiàn)象常見(jiàn)原因解決思路打包后缺少 Qt 插件hidden import 缺失使用--collect-all PySide6找不到模型文件模型權(quán)重沒(méi)有隨包復(fù)制使用--add-data攜帶模型文件打包后軟件體積過(guò)大PySide6 和 torch 體積本身就大使用 ONNX 部署模式減少 torch 依賴雙擊 exe 無(wú)反應(yīng)缺 DLL 或依賴庫(kù)在 cmd 中執(zhí)行 exe 查看報(bào)錯(cuò)日志9.4 攝像頭無(wú)法打開(kāi)攝像頭打開(kāi)失敗通常是因?yàn)闄?quán)限被占用或索引不對(duì)??梢赃@樣排查import cv2 for i in range(5): cap cv2.VideoCapture(i) if cap.isOpened(): print(fcamera index {i} opened) cap.release()如果所有索引都打不開(kāi)檢查攝像頭是否被其他軟件占用或者嘗試更換 USB 接口。10. 工程化最佳實(shí)踐與部署建議10.1 模型層面訓(xùn)練摔倒檢測(cè)模型時(shí)不要只看 mAP。建議根據(jù)實(shí)際場(chǎng)景統(tǒng)計(jì)誤報(bào)率和漏報(bào)率。可以采集一段 30 分鐘的真實(shí)監(jiān)控視頻將模型輸出結(jié)果和人工標(biāo)注結(jié)果做對(duì)比找出誤報(bào)頻發(fā)的畫(huà)面區(qū)域再有針對(duì)性地增加負(fù)樣本。在模型選擇上YOLOv8n 適合低算力設(shè)備YOLOv8s 是精度與速度均衡的選擇。YOLOv5 更適合老設(shè)備或已有工程基礎(chǔ)的項(xiàng)目。如果你的設(shè)備算力特別低也可以考慮使用 MobileNet 等輕量級(jí)骨干網(wǎng)絡(luò)配合 YOLO 檢測(cè)頭。10.2 程序?qū)用鏅z測(cè)線程要支持多路視頻流。批量部署時(shí)可以把視頻讀取和模型推理解耦讀取線程只管取幀推理線程從隊(duì)列中取幀這樣攝像頭資源不會(huì)被單個(gè)推理耗時(shí)阻塞。報(bào)警信息要記錄日志。無(wú)論是報(bào)警截圖、錄像還是系統(tǒng)運(yùn)行日志都建議按天分目錄保存便于事后追溯。日志要記錄時(shí)間、視頻源、檢測(cè)框坐標(biāo)、置信度、處理耗時(shí)。10.3 隱私與合規(guī)在真實(shí)部署場(chǎng)景中攝像頭監(jiān)控涉及個(gè)人隱私。系統(tǒng)應(yīng)該具備本地化處理能力不上傳視頻數(shù)據(jù)到云端。增加數(shù)據(jù)脫敏功能比如在畫(huà)面中檢測(cè)到非目標(biāo)區(qū)域時(shí)自動(dòng)遮擋。報(bào)警截圖與錄像只保存必要時(shí)間窗口不長(zhǎng)時(shí)間留存原始視頻。10.4 安全邊界系統(tǒng)涉及報(bào)警行為屬于安全相關(guān)場(chǎng)景不能只依賴單一模型判斷。建議疊加人員狀態(tài)跟蹤和異常閾值判斷盡量減少誤報(bào)和漏報(bào)。在生產(chǎn)環(huán)境部署前要進(jìn)行多輪真實(shí)驗(yàn)證不能因?yàn)槟P驮跍y(cè)試集上表現(xiàn)好就直接上線。對(duì) PySide6 程序建議限制只有授權(quán)用戶才能修改界面參數(shù)防止非專業(yè)人員誤調(diào)置信度閾值造成系統(tǒng)失效。10.5 性能優(yōu)化建議推理圖片尺寸從 640 降為 512可以明顯提升 FPS。使用 FP16 半精度推理減少顯存占用。使用 ONNX Runtime 的ExecutionMode.ORT_SEQUENTIAL或者 OpenVINO 插件優(yōu)化 CPU 推理速度。在 UI 中繪制檢測(cè)框時(shí)使用 OpenCV 繪制而不使用 QPaint減少圖像轉(zhuǎn)換開(kāi)銷。對(duì)于連續(xù)幀模型輸入改用 ROI 區(qū)域縮放減小輸入像素量。11. 總結(jié)與下一步學(xué)習(xí)方向摔倒檢測(cè)系統(tǒng)從技術(shù)上可以拆成數(shù)據(jù)集、模型訓(xùn)練、推理加速、桌面界面、報(bào)警聯(lián)動(dòng)五個(gè)子模塊。掌握了這些模塊你不僅能做出視頻摔倒檢測(cè)系統(tǒng)也能遷移到安全帽檢測(cè)、區(qū)域入侵檢測(cè)、離崗檢測(cè)等相似任務(wù)中。下一步建議按以下順序繼續(xù)深入閱讀 YOLOv8 官方源碼理解 C2f 模塊和 Detect 頭的設(shè)計(jì)思路。嘗試替換 YOLOv8 的骨干網(wǎng)絡(luò)比如引入 ConvNeXt V2對(duì)比不同主干對(duì)摔倒目標(biāo)的檢測(cè)效果。學(xué)習(xí) ByteTrack 等輕量級(jí)跟蹤算法為系統(tǒng)加入軌跡分析能力。在 PySide6 中加入更多自動(dòng)化控制例如聯(lián)動(dòng)門(mén)禁、光線控制。將模型導(dǎo)出為 TensorRT 或 OpenVINO 格式部署到 Jetson、樹(shù)莓派等邊緣設(shè)備。這里要提醒的是摔倒檢測(cè)領(lǐng)域最難解決的不是模型精度而是真實(shí)場(chǎng)景下的數(shù)據(jù)多樣性和誤報(bào)控制。所以實(shí)際落地時(shí)我傾向于先把數(shù)據(jù)采集和標(biāo)注流程固定下來(lái)多積累不同光線、不同角度、不同動(dòng)作的樣本再在模型優(yōu)化上投入精力。項(xiàng)目代碼架構(gòu)建議保持模塊化訓(xùn)練腳本和界面程序分開(kāi)維護(hù)換數(shù)據(jù)集、換算法模型時(shí)不用動(dòng) UI 代碼這樣后續(xù)迭代會(huì)輕松很多。如果你正在做相關(guān)畢設(shè)或工程開(kāi)發(fā)可以參考本文的流程先跑通最小閉環(huán)再逐步優(yōu)化。有問(wèn)題可以在評(píng)論區(qū)留言交流。