
简介一份基于YOLOv8的武术动作识别系统完整资料包面向计算机、人工智能、电子信息等专业的在校学生与开发者尤其适合作为毕业设计或课程设计项目。压缩包共97个文件以70个Python源码为主配合12个Pyc编译文件、4个模型权重.pt、5个环境配置XML、2个使用说明TXT及1个演示MP4整体仅24.21MB其中可视化页面、模型训练、检测服务等模块划分清晰便于按需定位代码。项目代码均测试通过内置完整数据集与部署教程可一键生成混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图等关键评估图表足够支撑答辩评审简单部署即可运行也便于在此基础上二次开发拓展功能。目前已有73人学习下载对于需要快速搭建动作识别演示系统的同学来说是一份拿来即用的优质资料。1. 为什么用YOLOv8做武术动作识别从一帧到一段的跨越武术动作识别和普通的图像分类最大的不同在于一个动作往往占据十几帧甚至几十帧单看某一帧你根本分不清这是弓步冲拳还是马步架打。这个项目把YOLOv8当作姿态提取的前置引擎先拿到人体关键点再对关键点序列做时序建模最终输出动作类别。相比纯分类网络这种方式对拍摄角度、服装、背景的鲁棒性都更好训练数据也更容易获取。系统自带完整数据集、可视化界面和部署教程解压后按步骤跑通训练和推理适合做毕设、课程设计或者作为动作识别方向的研究起点。下面我按数据准备、环境搭建、训练调优、避坑、界面部署的顺序把整条链路拆开讲。2. 数据准备与标注先把动作拆成YOLO能懂的标签2.1 动作类别的标签体系设计动作识别的标签设计和目标检测不一样检测框回答的是人在哪动作标签回答的是人在做什么。我在拆这个项目时第一件事就是看它的类别定义。这套系统默认支持五个武术动作类别加上一个背景类对应关系如下。类别ID动作名称典型帧特征识别难点0punch前手冲拳手臂伸展与推掌相似1palm掌法推出掌心向前与冲拳易混淆2block格挡动作前臂横置静止帧多3kick弹腿或正踢腿部抬起与站立帧交替快4stance马步或虚步站立持续时间长5background非动作帧负样本这套标签体系是合理的因为它把动作拆成了两个层次先由YOLOv8检测人体并提取骨架再由分类头对骨架序列做判断。背景类非常重要否则系统会在没人出镜时强行归类到某个动作上。我在自己复现时还给每个动作类做了一段动作定义文档标注开始帧和结束帧的姿势特征防止不同标注人对边界理解不一致。2.2 数据集目录结构与labelme标注流程项目数据集采用的是视频片段抽帧 YOLO关键点标注的双层结构。视频片段先按动作类别保存每一段剪成2到3秒的clip然后以5fps的帧率抽帧。抽出来的图像用labelme做关键点标注YOLOv8-pose需要17个关键点对应COCO骨架格式。目录结构是这样的dataset/ ├── videos/ │ ├── punch/ │ │ ├── clip_001.mp4 │ │ └── clip_002.mp4 │ ├── palm/ │ └── block/ ├── frames/ │ ├── punch/ │ │ ├── clip_001_0001.jpg │ │ └── clip_001_0002.jpg ├── labels/ │ ├── punch/ │ │ ├── clip_001_0001.json │ │ └── clip_001_0002.json ├── train.txt ├── val.txt └── action_labels.txtframes目录和labels目录一一对应JSON文件里记录的是每个关键点的xy坐标。注意点在于labelme的JSON格式不能直接喂给YOLOv8需要转成YOLO的txt格式。项目自带了一个转换脚本我复现时还手动验证过坐标归一化的逻辑所有关键点坐标都需要除以图像宽高转为0到1的浮点数否则训练时会报shape mismatch。2.3 训练集与验证集的划分脚本划分数据集这一步看似简单但有个容易踩的坑——如果按帧随机划分同一个clip的帧会同时出现在训练集和验证集里会造成严重的数据泄漏验证集指标虚高。合理做法是按clip划分保证同一个视频片段的所有帧不会跨集合。import os import random random.seed(42) def split_by_clip(frames_dir, train_ratio0.8): clips {} for fname in os.listdir(frames_dir): clip_id fname.rsplit(_, 2)[0] clips.setdefault(clip_id, []).append(fname) clip_ids list(clips.keys()) random.shuffle(clip_ids) split_idx int(len(clip_ids) * train_ratio) train_clips set(clip_ids[:split_idx]) train_lines [] val_lines [] for fname in os.listdir(frames_dir): clip_id fname.rsplit(_, 2)[0] line fimages/train/{fname} if clip_id in train_clips else fimages/val/{fname} target train_lines if clip_id in train_clips else val_lines target.append(line) with open(dataset/train.txt, w) as f: f.write(\n.join(train_lines)) with open(dataset/val.txt, w) as f: f.write(\n.join(val_lines))这个脚本的关键逻辑是按clip_id分组clip_id取的是文件名去掉最后两段序号后的部分。这么做保证了同一个动作片段的所有帧进同一侧验证集的mAP才真实反映模型泛化能力。train_ratio参数控制划分比例默认0.8数据量小的时候可以调到0.85但不能低于0.7否则验证集噪声太大。3. 环境搭建与模型选型Ubuntu/Windows双平台的依赖清单3.1 依赖版本与硬件选型这个项目的主干是YOLOv8动作序列分类部分用的是LSTM 全连接层。整套环境在Ubuntu 20.04和Windows 10/11上都验证过关键依赖版本如下。依赖项版本要求说明Python3.8~3.103.11实测部分算子编译有问题PyTorch1.13~2.12.0以上显存占用略高但训练更快ultralytics8.0.xx8.1以后API有变动opencv-python4.8以上视频读取必备labelme5.3.x标注工具CUDA11.8或12.1只在GPU训练时需要CPU也能跑但训练速度差距明显。我用一块GTX 1660Ti6GB显存训练YOLOv8n-posebatch_size设为16时大概每轮4分钟换成纯CPU跑一轮要25分钟以上。如果你是笔记本CPU建议直接用项目给的预训练权重做迁移学习不要从零训。3.2 安装验证命令与常见报错环境安装部分项目里给的是两个脚本Windows下是.batUbuntu下是.sh。核心命令就几条conda create -n martial python3.9 conda activate martial pip install torch1.13.1 torchvision0.14.1 --index-url https://download.pytorch.org/whl/cu117 pip install ultralytics8.0.43 labelme opencv-python安装完先验证GPU是否可用不要直接跑训练。import torch print(torch.__version__) print(torch.cuda.is_available()) if torch.cuda.is_available(): print(torch.cuda.get_device_name(0))如果cuda.is_available()返回False最常见的原因是PyTorch版本和CUDA驱动不匹配。我的处理习惯是先看驱动版本再选PyTorch驱动支持CUDA 12.x就直接装cu121后缀的版本不要为了省空间装cpu版。另一个高频报错是ultralytics的API兼容问题如果你装的是8.1以上的版本yolo.PoseTrainer的初始化参数有变化建议严格按项目的requirements.txt锁版本。4. 训练与超参调优从loss曲线到mAP50的完整流程4.1 训练脚本与关键超参说明YOLOv8的pose训练入口是yolo命令但项目用的是Python脚本方式方便把数据配置和超参写在同一个文件里。数据配置走的是YAML文件# martial_pose.yaml path: D:/projects/martial/dataset train: images/train val: images/val kpt_shape: [17, 3] names: 0: personkpt_shape是必须和标注格式对齐的[17, 3]表示17个关键点每个点三个通道x, y, visible。visible标注在labelme里默认给2表示可见且未遮挡训练脚本会把它转成1。这个参数错了训练直接报维度错误。训练命令以YOLOv8n-pose为backboneyolo pose train \ modelyolov8n-pose.pt \ dataconfigs/martial_pose.yaml \ epochs80 \ imgsz640 \ batch16 \ device0 \ projectruns/pose \ namemartial_exp14.2 训练过程的观察点与loss曲线判读训练过程中不要只看总loss要分开看三个量box_loss、pose_loss、kobj_loss。我复现时发现pose_loss收敛最快前10轮就从2.1降到0.4说明关键点定位任务本身不难——毕竟武术动作的姿态变化范围有限。kobj_loss是关键点可见性分类的损失这个值如果震荡不降往往代表标注里visible字段混乱常见原因是用labelme标注时有些遮挡关键点没标导出后补了坐标但没有修正visible值。每轮训练结束后runs/pose/martial_exp1/目录下会生成confusion_matrix.png和results.png。results.png里的曲线如果出现训练loss降、验证loss反弹说明模型过拟合了。武术动作数据的背景变化不大模型很容易把背景纹理当特征这时候优先做数据增强而不是调整模型结构。4.3 推理脚本与结果验证训练完成后需要把模型导出再写一个推理脚本验证单段视频的动作识别效果from ultralytics import YOLO import cv2 import numpy as np model YOLO(runs/pose/martial_exp1/weights/best.pt) cap cv2.VideoCapture(test_clip.mp4) keypoint_buffer [] while cap.isOpened(): ret, frame cap.read() if not ret: break results model(frame, verboseFalse)[0] if results.keypoints is not None: kpts results.keypoints.data.cpu().numpy() if len(kpts) 0: keypoint_buffer.append(kpts[0]) annotated results.plot() cv2.imshow(pose, annotated) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这个脚本把每帧的17个关键点写入keypoint_buffer后续喂给LSTM分类器。注意它只取了kpts[0]也就是检测到的第一个人多人场景下需要按框的面积排序选主目标。关键点坐标在送入LSTM前要归一化以颈部或髋部为原点做相对坐标转换否则人物在画面中的位置变化会被模型误当成动作变化。4.4 时序分类器的训练关键点序列数据准备好后需要训练一个时序分类模型。我在这里使用了一个两层的LSTM 注意力机制输入是30帧关键点序列输出是6个动作类别的概率分布。import torch.nn as nn class ActionLSTM(nn.Module): def __init__(self, input_size34, hidden_size64, num_classes6): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers2, batch_firstTrue) self.attention nn.MultiheadAttention(hidden_size, num_heads4, batch_firstTrue) self.fc nn.Linear(hidden_size, num_classes) def forward(self, x): out, _ self.lstm(x) attn_out, _ self.attention(out, out, out) out attn_out.mean(dim1) return self.fc(out)input_size是34因为每个关键点有x和y两个坐标17个点共34维。序列长度采用30帧即模型每次看1秒的输入。这个长度需要覆盖动作的最小时间跨度武术动作一般0.5到2秒30帧在30fps视频里刚好1秒。训练时我使用了滑动窗口采样步长为10帧这样每条视频能产生更多训练样本同时保持数据的多样性。注意力机制在动作识别任务中很有效尤其是对于kick这种持续时间短的动作能够自动聚焦到关键帧。5. 常见问题与排查五条真实踩坑记录5.1 labelme标注导出后关键点顺序错乱现象训练开始后pose_loss一直停在1.5以上loss曲线几乎不下降。检查标注文件发现有些JSON里的关键点顺序和COCO骨架不一致比如右肩和左肩写反了。原因labelme默认按你点击点的先后顺序记录坐标如果标注时不是严格按照骨架定义的顺序点导出的坐标就是乱的。YOLOv8训练时按固定索引解析关键点顺序错了模型学到的是错误的骨架拓扑。解决写一个校验脚本加载所有JSON文件检查每个关键点的visible和坐标范围然后按COCO骨架顺序重排。从那以后我每次标注前都会先打印一张骨架点位顺序图贴在屏幕旁标注完再跑一遍校验出错率基本降为零。5.2 训练时报错KeyError: kpt_shape现象训练命令敲下去几秒钟后报错提示找不到kpt_shape。原因数据配置YAML文件里kpt_shape写到了names下面或者缩进不对ultralytics解析时把这个键忽略了。YAML对缩进极其敏感Tab和空格混用会直接解析失败。解决用统一空格缩进kpt_shape放在names之前路径用正斜杠。我习惯写完YAML先用python -c import yaml; yaml.safe_load(open(martial_pose.yaml)) 验证一下能不能被正确解析再跑训练。5.3 训练loss正常但验证集mAP接近零现象训练80轮后训练集mAP已经到0.85验证集mAP只有0.02几乎等同于随机猜测。原因数据泄漏的镜像问题——不只是训练集和验证集划分不规范还有一种情况是验证集里存在大量背景类样本而背景类在标注时使用的是负样本图像这些图像本身没有人体关键点。模型在验证时遇到无人的帧输出了空的关键点张量评估脚本按空结果计算拉低了整体mAP。解决验证集里过滤掉没有人体的帧背景类样本单独做评估不和动作类混在一起算mAP。具体做法是检查每张验证图像的标注JSON里是否有至少5个关键点的visible大于0不满足的直接从val.txt里移除。5.4 界面推理时卡顿FPS只有个位数现象可视化界面里选择视频后画面播放非常卡明显看到视频一帧一帧跳动作识别结果延迟很大。原因界面主线程在做推理视频读取、pose检测、LSTM分类全部串行执行而且每次都把整帧resize到640x640再做推理CPU和GPU都在高负载运行。解决把推理放到独立线程视频帧读取和模型推理解耦界面只负责显示最新结果。同时把推理帧率限制在15FPS以内跳过中间帧frame_count 0 while cap.isOpened(): ret, frame cap.read() if not ret: break frame_count 1 if frame_count % 2 ! 0: continue这段代码的意思是每隔一帧才做一次推理可以大大降低计算负担同时保持界面的流畅度。5.5 部署到新机器上提示缺少DLL或.so文件现象把整个项目拷到另一台电脑上运行界面脚本时提示找不到cv2或者torch的相关动态库。原因项目是用conda环境跑的环境依赖没有完整导出。很多人会直接拷贝项目文件夹但PyTorch和OpenCV的二进制库文件并没有跟着走。解决在项目根目录下执行pip freeze requirements.txt然后在新机器上重建conda环境后安装。另外OpenCV的DLL依赖VC运行库新机器需要先装Microsoft Visual C Redistributable。我的习惯是做完任何项目都会用conda env export导出一份完整的yml文件不仅包含Python包还包含conda版本的元信息这样换机器时一份文件就能恢复环境。6. 可视化界面与进阶验证把模型挂到可交互的界面上项目自带的可视化界面用的是PyQt5主界面包含三个区域左侧视频预览、右侧参数配置、底部识别结果输出。界面逻辑不复杂但有一个关键点值得学习——推理和UI交互是分离的。视频抽帧、姿态提取、动作分类都在后台线程完成通过信号槽机制把结果传回界面避免界面假死。class ActionWorker(QThread): result_ready pyqtSignal(dict) def __init__(self, model_path): super().__init__() self.model YOLO(model_path) self.running True def run(self): while self.running: frame self.get_latest_frame() if frame is None: continue results self.model(frame, verboseFalse)[0] keypoints results.keypoints.data.cpu().numpy() action self.classify(keypoints) self.result_ready.emit({action: action, frame: frame}) self.msleep(66)QThread的msleep(66)把推理频率限制在每秒15次左右这个值在实时性和流畅度之间比较平衡。进阶验证方面我建议你做一个时序一致性检查把所有测试视频按顺序播放记录模型输出的动作类别序列然后检查是否存在单帧跳变——比如前5帧都是punch第6帧突然变成kick第7帧又跳回punch。动作本身是有持续性的单帧跳变说明时序建模还不够稳。我自己的做法是把这个检查脚本化把预测结果写成一个时间线再用一个滑窗做投票平滑窗口大小设5帧多数票决定最终输出。改进后动作切换边界的准确率明显提升。最后再多提醒一句动作识别这类任务数据质量永远比模型结构重要。我在标注数据时走过的弯路比训练调参多得多从那以后我每次开始训练前都强制走一遍数据校验三板斧看标注顺序、查visible字段、按clip划分数据集。希望帮到你。本文还有配套的精品资源点击获取