简介一套基于YOLOv8的手势识别应用工程面向计算机视觉与深度学习方向的开发者和学习者解决人机交互、虚拟现实等场景中的实时手势检测需求。压缩包共18个文件大小约11.18MB包含10张jpg手势图像样本、3个txt说明文件、2个pt模型权重、1个py主程序及配套的gitignore和md文档涵盖手势图片数据、训练指标图、yolov8n基座权重与best.pt最优权重、可直接运行的app.py以及requirements.txt、README.md等环境配置与使用指南。整体目录简洁从数据、模型到应用代码分层清晰便于理解YOLOv8从训练到部署的完整流程也可直接替换或扩充手势类别做二次开发。训练指标图可辅助分析模型收敛情况pt权重文件已训练完毕开箱即可用于推理。现有52人学习/下载适合希望快速复现手势识别项目或在此基础上完成课程设计、算法实验的读者。1. 为什么“基于YOLOv8的手势识别应用.zip”里模型代码只占三成如果你已经把这个压缩包下载到本地“解压完就能用”的想法最好先收起来。基于YOLOv8的手势识别应用真正值钱的部分不在那几行推理脚本里而在一条完整的数据闭环搭建环境只是入场券把自定义手势变成标注数据、再把 best.pt 部署到 RK3588 这类板子上才是投入时间最多的地方。它解决的是离线手势指令问题比如摄像头前比一个“OK”触发拍照、握拳暂停设备相比 MediaPipe 那种只给 21 个关键点的方案YOLOv8 的好处是手势类别完全由自己定义五类、八类都能做坏处是每加一个类别都要重新补数据。这套路径适合三类人拿手势识别做毕业设计的学生、第一次在 RK3588 上部署 YOLOv8 的嵌入式开发者以及手头只有 CPU 机器但想验证效果的工程师。2. 解压检查与 YOLOv8 环境搭建Ubuntu 20.04 CPU 版和 GTX 1660 Ti 两条路线2.1 解压前的两项检查zip 完整性和伪加密拿到 zip 包很多人第一件事是右键解压但我不这么干。先用 zipinfo 看清单、用 unzip 测试完整性这一步成本极低却能把后面几小时的排错时间省下来。项目解压到一半发现文件损坏训练时才报缺少权重文件这种翻车我见过不止一次。用 Python zipfile 打开时报 “could not find EOCD”基本可以断定文件不完整多半是网盘下载被截断。重新下载通常比尝试修复更快不用浪费时间研究所谓“zip密码移除”技巧。只有一种情况值得处理文件能正常列出、unzip 却要求输密码而 zipinfo 里的加密标志位也异常这大概率是 zip 伪加密不是真的加密包。用 7-Zip 或 Python zipfile 模块去解压多数时候能直接绕过。unzip -l YOLOv8手势识别应用.zip | head -n 30 unzip -t YOLOv8手势识别应用.zipunzip -l只列出压缩条目不落盘适合先看项目里有没有 requirements.txt、weights 目录、数据集目录结构unzip -t做 CRC 完整性测试返回错误码 1 就说明包损坏。这两条命令跑完再决定怎么解压。解压后常见项目结构是这样的train.py、detect.py、data/ 目录下分 images 和 labels 两棵子树、runs/ 目录存放训练输出。不要急着双击 train.py先确认它调用的是YOLO()新接口还是旧版detect.py脚本两者混用是后面训练报错的主要源头。提示伪加密的通用判断方法unzip -l能列出文件但unzip x.zip要求输入密码且缩略信息里出现了加密标志位。真正加密的包列表阶段通常就做了限制。2.2 Ubuntu 20.04 搭建 YOLOv8 环境CPU 版本最小命令集用 CPU 跑 YOLOv8 训练已经不算劝退yolov8n 这种轻量模型配合小数据集挂机跑一个晚上是常见操作。关键是先创建干净的 conda 环境再装纯 CPU 版 PyTorch最后装 ultralytics。顺序反了pip 会默认拉带 CUDA 的 torchCPU 机器上启动变慢、磁盘多占用几百 MB还容易在导入时报 GPU 相关错误。conda create -n yolov8-gesture python3.8 -y conda activate yolov8-gesture pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install ultralytics opencv-python pyyamlpython3.8 是 YOLOv8 兼容性比较稳妥的版本不少老项目的依赖文件也按 3.8 写先装 CPU 版 torch能避免后面装 ultralytics 时自动带一个用不到的 CUDA 版本ultralytics 会拉取 numpy、pandas、matplotlib 等依赖所以放到最后装。装完后做一次冒烟测试python -c from ultralytics import YOLO; m YOLO(yolov8n.pt); print(m.names)正常打印出 COCO 的 80 类名称说明环境和权重下载都通了。如果卡在下载权重先检查网络能访问正常的软件源再换镜像源重试不要在 torch 安装这一步省时间。2.3 GTX 1660 Ti 跑 YOLOv8Windows 下的 GPU 配置要点有独显时最容易踩的环境坑是 torch 版本与 CUDA 不匹配。对 GTX 1660 Ti 这种 6GB 显存的卡常见做法是装 CUDA 11.8 配套的 PyTorch而不是盲目追新版本。1660 Ti 跑 yolov8s 足够batch 控制在 8 到 16 之间就好别跟 24GB 显存的人学 batch 32显存溢出是必然结果。conda create -n yolov8 python3.10 -y conda activate yolov8 conda install cudatoolkit11.8 cudnn -c conda-forge pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralyticscudatoolkit 由 conda 管理系统里不需要提前装全局 CUDA省去环境变量配置cu118 后缀对应 CUDA 11.8必须和 cudatoolkit 版本对位。装完后运行python -c import torch; print(torch.cuda.is_available())返回 False 时多半是 torch 装成了 CPU 版卸载重装 cu118 版本即可。Windows 上如果 conda 创建环境很慢把 conda 的 channel 优先级调低直接走默认源一般都能解决。3. 手势数据才是这个应用的命门采集、Labelme 标注与转 YOLO 格式3.1 用公开数据集还是自采先看类别和背景网上能搜到一些手势识别数据集包括手指数 0 到 5、石头剪刀布等常见类别。但直接拿来做 YOLOv8 训练通常会遇到两个麻烦一是公开数据的拍摄角度和光照太单一背景基本是纯色训练出来的模型换到办公室环境就失灵二是类别定义不一定符合项目需求比如你想识别“OK 手势触发拍照”公开集里未必有这个类。我一般会先看类别定义类别对不上宁可自采。自采不需要专业设备手机拍一段 10 分钟视频覆盖不同角度、不同距离、不同光线然后用 OpenCV 脚本隔帧抽取通常能拿到几千张。关键是要多拍“错误样例”比如手持鼠标、拿着杯子、空桌面这些负样本能显著降低误检。import cv2 import os os.makedirs(raw, exist_okTrue) cap cv2.VideoCapture(gesture.mp4) idx 0 save_id 0 while True: ok, frame cap.read() if not ok: break if idx % 5 0: cv2.imwrite(fraw/{save_id:05d}.jpg, frame) save_id 1 idx 1 cap.release() print(f共抽取 {save_id} 帧)idx % 5是抽帧间隔手势变化快时改成 3动作慢时用 6 都可以。抽帧太密会让相邻帧高度相似模型容易过拟合到固定背景。自采数据时还要注意手部在画面里的尺寸如果手只占画面的十分之一模型很难学会手势细节拍摄时尽量让手占画面的三分之一以上。3.2 Labelme 标注后转 YOLO 格式脚本与四个边界坑Labelme 导出的是 JSONYOLO 需要的是每张图一个 TXT每行格式为类别id x_center y_center w h。转换时最容易翻车的是坐标归一化和点序问题。有人画框习惯从右下角往左上角拖如果代码直接把points[0]当成左上角计算出的宽高就是负数训练时损失值直接异常。我常用这段脚本来做转换顺便兜底点序问题import json import os from glob import glob def convert_labelme_to_yolo(json_path, out_dir, class_names): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] lines [] for shape in data[shapes]: label shape[label] if label not in class_names: continue cls_id class_names.index(label) pts shape[points] x_coords [p[0] for p in pts] y_coords [p[1] for p in pts] x1, x2 min(x_coords), max(x_coords) y1, y2 min(y_coords), max(y_coords) x_center (x1 x2) / 2.0 / img_w y_center (y1 y2) / 2.0 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h if w 0 or h 0: print(f跳过无效框: {json_path}, {label}) continue lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if lines: txt_path os.path.join(out_dir, os.path.basename(json_path).replace(.json, .txt)) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) class_names [ok, palm, fist, peace] for jp in glob(labelme_json/*.json): convert_labelme_to_yolo(jp, labels, class_names)这段代码的核心逻辑只有三句话取坐标极值保证框不反向全部除以图像宽高完成归一化过滤掉宽高为零的坏标注。class_names的顺序一旦定了后续训练和推理都不能改否则类别 id 整体漂移mAP 会突然变成 0。实际使用中还有几个边界情况需要处理。一张图里出现两个相同手势时循环 shapes 会各写一行符合 YOLO 多目标格式JSON 里记录的 imageWidth 和原图实际像素不一致时框会整体偏移转换前务必核对Labelme 的 rectangle 模式偶尔会带 rotation 字段目前转换脚本用不到忽略即可。3.3 数据集目录组织与 data.yaml 配置YOLOv8 训练要求 images 和 labels 分开存放标签与图片文件名必须一致。常见的目录组织方式dataset/ images/ train/00001.jpg val/00001.jpg labels/ train/00001.txt val/00001.txt data.yamldata.yaml 的内容path: dataset train: images/train val: images/val nc: 4 names: [ok, palm, fist, peace]最容易忽略的一点是验证集不能和训练集出现同一张图。我习惯先按文件名做 90% 和 10% 的随机划分而不是手工挑图避免把高度相似的相邻帧同时放进训练和验证导致验证指标虚高。划分脚本也很简单读取全部文件名用随机数筛一遍分别写入 train.txt 和 val.txt再按清单移动图片和标签。4. YOLOv8 手势识别训练避坑五个必调参数与三个翻车现场4.1 模型选型yolov8n 还是 yolov8s选模型不是越大越好。对 CPU 用户yolov8n.pt 是唯一不劝退的选择单帧推理在 CPU 上大约几十到一百毫秒对 GTX 1660 Ti可以上 yolov8s 提精度但更值得关注的是手部目标在画面里的占比。手往往只占图像的一小块区域这种场景下先把输入分辨率提上去比换大模型更有效。搜一下 yolov8 网络结构图就能明白输入图像经过多次下采样后小目标的特征在深层特征图上只剩几个像素。手势识别属于典型的小目标检测我一般先把 imgsz 从 640 调到 736显存不够再退回 640而不是一上来就换 yolov8m。预训练权重选择方面用 COCO 预训练的 yolov8s.pt 做起点比从零开始训练收敛快很多。4.2 五个必调参数imgsz、epochs、batch、lr0、patience训练命令里值得手动调的参数其实就五个yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns \ namegesture \ device0参数建议值作用与调整思路imgsz640 或 736输入分辨率手部目标小时提高 imgsz 收益最明显epochs100手势类别少100 轮基本够配合早停使用batch8 到 166GB 显存用 8 到 16CPU 训练用 4 到 8lr00.01使用 COCO 预训练权重时保持默认即可不必调大patience20连续 20 轮验证 mAP 不涨就停止节省时间这里重点说batch。显存不够时优先减 batch而不是减 imgsz因为 imgsz 直接影响小目标召回。CPU 训练时 batch 过大反而会让内存换页频繁损失曲线剧烈震荡。patience是个后悔药设成 20 可以让训练在验证指标不再提升时自动停下省掉大量挂机等待。lr0默认 0.01 是配合预训练权重的值。如果数据集特别小比如只有几百张把这个值降到 0.005 会更稳如果发现 loss 在 10 轮内完全不下降检查数据标注是否有空标签而不是盲目加学习率。4.3 三个翻车现场现象、原因与解决第一个典型问题是 mAP 全程为 0但训练 loss 正常下降。现象是训练曲线看着没问题验证集识别结果全是空。原因通常是类别 id 和 data.yaml 不对齐或者转换脚本把类别名字符串直接写进了 txt第一列不是 0、1、2、3 而是 ok、palm。解决方法是打开一个 label txt 看内容确认第一列是数字再用单张图片跑一次model.predict()观察输出类别编号。第二个问题是 CPU 训练时 loss 曲线震荡明显val loss 降不下来。现象是曲线像心电图收敛非常慢。原因是 batch 设置过大内存交换导致梯度更新噪声变大也可能是手部目标过小模型学不到有效特征。解决方法是 batch 降到 4 或 8imgsz 保持 640同时把 mosaic 增强关掉或调小概率小数据集上 mosaic 反而会引入太多拼接背景拖累收敛。第三个问题是推理时把背景当手误检率很高。现象是桌面上圆形物体被识别成“OK”或者空镜头前乱框。原因是训练数据里负样本不足模型学到的是“画面中央有肤色圆形区域”而不是真正的手势语义。解决方法是补充负样本把没有手势的帧也放进训练集同时在 labels 目录保留一个同名的空 txt。YOLOv8 支持空标签图参与训练它不会产生正样本 loss但能让模型学到“这里没有目标”。4.4 用 results.csv 画损失曲线别只看一条 loss每次训练结束runs/gesture/ 目录下会生成 results.csv 和 results.png。只看训练 loss 是不够的训练 loss 下降不代表模型没过拟合还要对比验证集 precision、recall 和 mAP 三条曲线。用 pandas 读一下 results.csv判断标准很简单val mAP 连续 20 轮没涨就是 patience 该发挥作用的时候。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/gesture/results.csv) plt.plot(df[epoch], df[train/box_loss], labelbox_loss) plt.plot(df[epoch], df[val/mAP50-95], labelmAP50-95) plt.legend() plt.show()这里注意列名在不同 ultralytics 版本里略有差异旧版本叫val/mAP0.5:0.95新版叫val/mAP50-95。运行前先print(df.columns)看实际列名避免复制代码直接报 KeyError。真正需要人工干预的是两种情况loss 不降但 mAP 升说明数据没喂好loss 降但 mAP 不升说明模型在过拟合训练集背景优先查验证集是否和训练集有重复帧。5. 从 best.pt 到 RK3588 板端推理导出、验证与验收习惯训练完成后runs/gesture/weights/ 下有两个文件best.pt 和 last.pt。部署到 RK3588 之前先在 PC 上做一轮批量验证拿 val 目录里最有代表性的几十张图跑一遍预测确认框的位置和类别都正常再谈导出。导出 ONNX 的命令是yolo export modelruns/gesture/weights/best.pt formatonnx imgsz640 opset12 simplifyTrueopset12是为了兼容 RKNN 工具链simplifyTrue会去掉一些冗余计算节点转换后的 ONNX 更容易被后续工具处理。这一步现在很成熟但仍有几个坑一是导出时的 imgsz 必须和训练时一致不一致会导致板端推理尺寸不匹配二是有些版本导出的 ONNX 会包含后处理建议在导出后先用onnxruntime跑一遍单张图确认输出维度和预期的 [1, 84, 8400] 一致。RK3588 部署的常见做法是分三步在 x86 主机上用 rknn-toolkit2 把 ONNX 转成 RKNN 格式、在板端加载 RKNN 模型做前处理、再解析输出框并画图。最容易忽略的是图像通道顺序。Python 用 OpenCV 读图是 BGR而模型训练时用的是 RGB如果板端直接喂 BGR 图模型不会报错但检测框会偏离真实位置。我一般会在前处理代码里加一行img cv2.cvtColor(img, cv2.COLOR_BGR2RGB)再做 resize 和归一化。现场验收时我有一个固定习惯把摄像头固定人站到 1 米、1.5 米、2 米三个距离每个手势连续比 10 次记录误检和漏检。检测框与真实手部位置的 IoU 大于 0.5 就算识别成功小于这个阈值就回去调置信度阈值或补数据而不是直接换更大的模型。每次改完模型先用一段现场视频重跑一遍确认在真实光照和背景下没有回归问题。我的教训是项目目录里长期保留 test_video/ 文件夹把每一版模型跑过的现场视频都存下来对比输出时心里才有底。希望帮到你。本文还有配套的精品资源点击获取
