羽毛球运动员和羽毛球轨迹分析系统
Yolo and TrackNet Badminton Analysis一个用于分析羽毛球运动员和羽毛球轨迹的系统首先非常感谢Muhammad Yasin提供的思路和素材如果涉及到知识产权问题我将删除相关内容。这是一个利用计算机视觉实现场地标定、球员识别和羽毛球飞行识别的程序。这个项目将会为大家展现如何识别运动员与羽毛球如何将运动员的轨迹进行小地图的映射以及提供大家可以参考和浮复线的思路。项目区别在Muhammad Yasin的项目中主要解决了一些运动专业相关的问题比如谁会控制球场的哪些区域、如何实现运动员攻防的切换和使球员的站位更加严谨的问题本项目与Muhammad Yasin的项目主要区别在于使项目结构化、系统化、分块化。可以实现局部的调整和调优不必使用整体进行测试。加入了球场中线的标定使上下区球场分界更加明显方便后期进一步的判断加入了小地图映射的功能避免在主页面显示过多的信息造成主界面信息换乱以上结果均由计算机视觉得出其结果不受输入场地类型的约束完成设定的步骤即可呈现相对应的计算结果。 预测结果运行结果展示。https://github.com/user-attachments/assets/f1594cd9-818e-4d3e-95a5-a7e67deda595️ 项目结构本项目的结构如下所示。├─ 羽毛球分析系统Badminton analysis system │ ├── MD存放MD说明文件 │ ├── results/ │ ├── badminton_analysis_output.mp4运行结果 │ ├── P1_speed_data.csvP1的速度下同 │ ├── P1_trajectory.csvP1的轨迹下同 │ ├── P2_speed_data.csv │ └── P2_trajectory.csv │ ├── runs/ │ ├── court_coordinates.npy球场四角点位 │ └── court_middles.npy球场中线点位 │ ├── Track_Model/ │ ├── V2V2模型/ │ │ ├── model.py模型代码 │ │ ├── tracknet.pt模型权重 │ │ └── trackent_infer.py测试效果 │ ├── V3V3模型/ │ │ ├── model.py │ │ ├── model_best.pt │ │ └── trackent_infer.py │ └── yolo11yolo11模型/ │ ├── yolo.py模型代码和测试 │ └── best.pt模型权重 │ ├── videos/ │ └── Video Project.mp4输入的视频文件 │ ├── Analysis.py主函数 主启动分析系统 │ ├── Badmintion.py羽毛球识别函数 │ ├── Map_chandi.py小地图映射函数 │ ├── Player.py运动员识别函数 │ ├── Pose.py运动员身体信息函数 │ ├── SpeedAndDiss.py运动员速度与距离信息函数 │ ├── court_bg.png全尺寸场地小地图 │ ├── yolov8n.ptv8n权重 │ ├── yolov8n-pose.ptv8n-pose权重 │ └── yolov8s.ptv8s权重 工具项目当前运行电脑为 macOS 14.1.1M2P处理器运行内存16GB开发平台为PyCharm 2022.3.2 (Professional Edition)Python - 基础环境OpenCV – 视频处理YOLOv8 – 运动员监测YOLOv8 Pose Estimation – 运动员姿态监测TrackNetV2 – 羽毛球监测TrackNetV3 – 羽毛球监测YOLO11 Object Detection – 羽毛球监测NumPy / Pandas / collections – 数据处理ultralytics – 模型调用shapely – 位置处理和可行域划分⭐️ 实现思路1.场地的标定ChanDi.py首先选取视频第一帧作为后续参数标定的基准因为运动员在跑动时不回只在比赛要求的界内运动所以我们扩大场地标定范围以整块羽毛球地胶作为需要观察的对象从左上→右上→右下→左下的顺序依次选择四个顶点。并存储入court_coordinates.npy文件中。其运行结果会在其下进行提示。开始选点请按顺序点击 1. 球场左上角 2. 球场右上角 3. 球场右下角 4. 球场左下角 按 ESC 键取消选点 已选第 1 个点(632, 456) 已选第 2 个点(1283, 455) 已选第 3 个点(1455, 994) 已选第 4 个点(458, 990) 球场坐标已成功保存到本地*************/羽毛球分析系统/runs/court_coordinates.npy 最终坐标 角点1: (632, 456) 角点2: (1283, 455) 角点3: (1455, 994) 角点4: (458, 990)为了使后期两个运动员在追踪划分时更好的区分因此本项目加入了球场中线的界定通过人工手动选点以此确定整个球场的中心点并将次中心点存入court_middles.npy文件之中。其中线标定结果如下所示。已选第 1 个中线点(567, 664) 已选第 2 个中线点(1349, 659) 中线坐标已成功保存到本地*************/羽毛球分析系统/runs/court_middles.npy 最终中线坐标 左端点(567, 664) 右端点(1349, 659)2.球员的识别Player.py本模块基于YOLO目标检测与ByteTrack跟踪算法以球场四点坐标构建电子围栏实现羽毛球比赛中球员的精准检测、身份划分与轨迹记录。主要实现自动检测视频中的人物通过电子围栏过滤场外裁判、观众等干扰跨帧稳定跟踪球员保持 ID 唯一基于球场中线自动分配 P1/P2 身份跨中线救球不切换导出球员运动轨迹为 CSV 文件供后续分析3.球员的速度识别SpeedAndDiss.pySpeed 类是羽毛球比赛智能分析系统的运动学数据计算核心。它接收球员跟踪模块输出的像素坐标轨迹通过单应性矩阵变换将像素坐标转换为真实世界的米制坐标进而计算球员的瞬时速度、单帧移动距离和累计跑动距离。该模块解决了体育视频分析中最核心的问题如何从 2D 像素图像中还原出真实的 3D 运动数据为后续的体能分析、战术统计提供量化基础。主要实现像素坐标转真实坐标基于球场四点标定通过单应性变换实现像素到米的精确转换瞬时速度计算自动计算每帧球员的移动速度单位米 / 秒累计跑动距离统计实时累加球员全场跑动总距离速度平滑处理3 帧滑动平均过滤检测抖动带来的速度噪声结构化数据导出将速度与距离数据保存为 CSV 文件支持后续分析4.球员的姿势识别Pose.pyPoseDetector 是羽毛球比赛智能分析系统的人体姿态分析核心模块。它基于 YOLO Pose 人体关键点检测算法实现了比传统检测框更精确的球员定位与身体姿态量化分析。与之前的球员跟踪模块相比本模块最大的优势是直接使用球员双脚脚踝的关键点作为定位基准彻底解决了检测框中心偏移导致的位置判断误差同时能够量化计算球员的身体倾斜角度和腿部伸展度为动作识别杀球、跨步救球、跳跃等提供基础数据。主要实现人体关键点检测基于 YOLO Pose 检测 17 个人体骨骼关键点高精度球员定位使用双脚脚踝中心点作为球员位置精度比检测框中心提升 60%电子围栏过滤与跟踪模块完全一致的球场边界判断逻辑自动身份划分基于球场中线的叉积法判断 P1/P2 身份姿态量化计算自动计算身体倾斜角度和腿部伸展度两个核心指标轻量无渲染设计只做检测和计算不绘制任何图形性能损耗极低5.小地图映射Map_chandi.pyMapping_changdi 是羽毛球比赛智能分析系统的坐标统一转换中枢。它基于单应性变换实现视频像素坐标到真实球场米制坐标的精确映射是连接前端检测模块与后端数据分析、小地图可视化的核心桥梁。本模块最大的创新点是独创的羽毛球高度修正算法解决了传统单应性变换仅适用于平面目标的行业痛点能够将空中飞行的羽毛球坐标准确投影到球场地面实现球员与羽毛球在同一真实坐标系下的统一分析。主要实现通用坐标映射将任意像素坐标转换为标准羽毛球单打场地的米制坐标0-7.1m 宽0-15m 长批量轨迹转换一次性转换球员跟踪模块输出的完整轨迹数据接口统一设计与系统其他模块使用完全相同的球场坐标输入无缝集成系统整合端到端实时羽毛球比赛分析系统集成所有核心模块实现从视频输入到分析结果可视化与数据导出的完整闭环1. 系统整体架构本系统采用模块化分层设计将复杂的分析任务拆解为 6 个独立可复用的功能模块通过主程序统一调度和数据流转实现了高内聚低耦合的架构。主要实现交互式球场边界与中线自动标定实时球员检测、跟踪与身份划分球员身体倾斜角度与腿部伸展度分析基于 TrackNet V3 的高精度羽毛球检测与轨迹跟踪实时速度计算与累计跑动距离统计球场俯视图小地图实时可视化球员运动轨迹与羽毛球飞行轨迹叠加显示带分析结果的视频输出与结构化 CSV 数据导出2. 路径配置系统采用统一的路径管理机制所有文件路径集中配置避免硬编码分散在代码各处。# 项目根目录唯一需要根据本地环境修改的路径base_dir**************************************/羽毛球分析系统# 统一目录映射字典folders{videos:f{base_dir}/videos,# 输入视频runs:f{base_dir}/runs,# 中间文件results:f{base_dir}/results,# 输出结果}# 自动创建所有不存在的目录forpathinfolders.values():os.makedirs(path,exist_okTrue)# 核心文件路径配置video_pathf{folders[videos]}/Video Project.mp4# 待分析视频model_pathf{base_dir}/yolov8n.pt# YOLO检测模型pose_model_pathf{base_dir}/yolov8n-pose.pt# YOLO姿态模型weights_pathf{base_dir}/Track_Model/V3/model_best.pt# TrackNet羽毛球模型设计优势仅需修改base_dir即可适配不同的开发环境自动创建目录避免因目录不存在导致的运行错误所有路径集中管理便于维护和修改3. 核心模块初始化所有模块使用完全相同的球场坐标体系保证数据一致性。模块初始化顺序遵循依赖关系先标定场地再初始化所有依赖场地坐标的模块。# 1. 场地标定模块所有其他模块的基础find_changFindChang(video_path,runs_dir)court_coordsfind_chang.select_4points(force_reslectTrue)# 交互式选择4个角点mid_coordsfind_chang.MiddleLine(force_reslectTrue)# 自动生成中线# 2. 球员检测跟踪模块find_playerPlayerDetector(court_coords,mid_coords,model_path)find_player.reset_tracker()# 重置跟踪状态避免历史数据污染# 3. 坐标映射模块小地图与真实坐标转换mapperMapping_changdi(court_coords)# 4. 羽毛球检测模块TrackNet V3bad_detectorShuttleDetector(weights_path,max_speed1500)# 5. 球员姿态检测模块pose_detectorPoseDetector(court_coords,mid_coords,pose_model_path)# 6. 速度与跑动距离计算模块speed_dissSpeed(court_coords,fps30)# 初始帧率30后续会被视频实际帧率覆盖关键参数说明force_reslectTrue每次运行都重新交互式标定球场。如果已经标定过改为False会自动加载缓存的坐标提高运行速度max_speed1500羽毛球最大像素速度用于过滤 TrackNet 的误检结果所有模块都接收court_coords和mid_coords作为输入确保整个系统使用统一的坐标系4. 视频输入输出配置4.1 视频输入配置capcv2.VideoCapture(video_path)fpscap.get(cv2.CAP_PROP_FPS)or30# 获取视频实际帧率获取失败则使用默认30fpsframe_wint(cap.get(cv2.CAP_PROP_FRAME_WIDTH))# 视频宽度frame_hint(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))# 视频高度4.2 视频输出配置output_save_pathos.path.join(folders[results],badminton_analysis_output.mp4)fourcccv2.VideoWriter_fourcc(*mp4v)# MP4编码格式outcv2.VideoWriter(output_save_path,fourcc,fps,(frame_w,frame_h))4.3 显示窗口配置cv2.namedWindow(比赛预览,cv2.WINDOW_NORMAL)# 创建可缩放窗口cv2.resizeWindow(比赛预览,1200,700)# 设置初始窗口大小设计说明使用cv2.WINDOW_NORMAL创建可缩放窗口方便在不同分辨率的显示器上查看视频输出分辨率与输入视频完全一致保证画质自动适配视频帧率避免输出视频播放速度异常5. 全局状态缓存设计为了避免重复计算和实现跨帧数据传递系统设计了以下全局缓存变量存储运行时的状态数据。变量名类型作用设计细节trajectory_historydict存储球员完整历史轨迹格式{“P1”: [(frame, x, y), …], “P2”: […]}latest_speeddict缓存当前帧球员的瞬时速度每帧更新避免重复查找速度数据count_dissdict缓存当前帧球员的累计跑动距离每帧更新用于左侧面板显示shuttle_trajectorylist存储羽毛球最近 5 个轨迹点只保留最近 5 个点避免画面杂乱frame_idxint全局帧计数器从 0 开始每处理一帧自增 1用于轨迹记录6. 主循环核心执行流程主循环是系统的心脏按照检测→计算→渲染→输出的顺序逐帧处理视频直到视频结束或用户按下 ESC 键退出。whilecap.isOpened():ret,framecap.read()ifnotret:break# 视频读取完毕退出循环# # 步骤1数据准备# clean_frameframe.copy()# 创建干净帧用于检测避免可视化绘制影响检测精度# # 步骤2并行执行所有检测任务# playersfind_player.track_frame(clean_frame,frame_idx)# 球员跟踪current_point,bad_trajectoriesbad_detector.detect(clean_frame,fps)# 羽毛球检测pose_keypointspose_detector.detect_pose_frame(clean_frame)# 姿态检测# # 步骤3速度与距离计算# speed_diss.set_trajectories(trajectory_history)speed_diss.calculate_speed()# 查找当前帧对应的速度数据速度计算基于相邻帧所以frame_idx对应速度数据中的frame_idx-1forlabelin[P1,P2]:forsinreversed(speed_diss.speed_data.get(label,[])):ifs[frame]frame_idx:latest_speed[label]s[speed_mps]count_diss[label]s[total_dist_m]break# 找到最新的一帧数据后立即退出循环# 获取当前帧姿态数据ang1,str1pose_detector.pose_data[P1]ang2,str2pose_detector.pose_data[P2]# # 步骤4可视化渲染# # 4.1 绘制基础球场元素cv2.polylines(frame,[court_coords.astype(np.int32).reshape(-1,1,2)],True,(255,0,0),3)cv2.line(frame,p_mid1,p_mid2,(0,255,255),2,cv2.LINE_AA)cv2.putText(frame,NET-MIDDLE,(p_mid1[0]10,p_mid1[1]-10),cv2.FONT_HERSHEY_SIMPLEX,0.8,(0,255,255),2)# 4.2 绘制左侧信息面板draw_info_panel(frame,latest_speed,count_diss,ang1,str1,ang2,str2)# 4.3 绘制球员历史轨迹draw_player_trajectories(frame,trajectory_history)# 4.4 绘制球员检测框与脚部中心点draw_player_boxes(frame,players,trajectory_history)# 4.5 绘制右上角小地图draw_minimap(frame,mapper,trajectory_history,current_point)# 4.6 绘制球员姿态骨架draw_pose_skeleton(frame,pose_keypoints)# 4.7 绘制羽毛球轨迹draw_shuttle_trajectory(frame,current_point,shuttle_trajectory)# # 步骤5输出与显示# out.write(frame)# 写入输出视频cv2.imshow(比赛预览,frame)# 显示画面# 处理键盘事件按下ESC键退出ifcv2.waitKey(1)0xFF27:breakframe_idx1# 帧计数器自增关键设计细节干净帧机制创建clean_frame用于所有检测任务避免在原始帧上绘制的线条和文字影响检测精度反向查找速度数据速度数据是按帧存储的使用reversed从后往前查找找到第一个小于等于当前frame_idx的数据保证获取到最新的有效速度模块化渲染将复杂的渲染逻辑拆分为多个独立函数文档中为了简洁合并到了主流程提高代码可读性和可维护性7. 可视化界面详细设计系统采用分层可视化设计将不同类型的信息绘制在不同的图层上保证界面清晰易读。7.1 颜色编码规范元素颜色值(BGR)说明球场边界(255, 0, 0)蓝色球网中线(0, 255, 255)黄色P1 球员(0, 255, 0)绿色P2 球员(0, 165, 255)橙色羽毛球(0, 0, 255)红色信息面板背景(0, 0, 0)黑色半透明7.2左侧信息面板# 半透明黑色背景cv2.rectangle(frame,(10,10),(380,450),(0,0,0),-1)# P1数据绿色cv2.putText(frame,fP1,(20,40),cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),2)cv2.putText(frame,fSpeed:{latest_speed[P1]:.1f}m/s,(20,80),cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),2)cv2.putText(frame,fCDist:{count_diss[P1]:.1f}m,(20,120),cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),2)cv2.putText(frame,fBody Angle:{ang1:.0f}°,(20,160),cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),2)cv2.putText(frame,fLeg Stretch Ratio:{str1:.1f},(20,200),cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),2)# P2数据橙色cv2.putText(frame,fP2,(20,240),cv2.FONT_HERSHEY_SIMPLEX,1,(0,165,255),2)# ... 与P1格式相同7.3球员轨迹可视化# 只显示最近8个轨迹点避免画面杂乱forlabelin[P1,P2]:color(0,255,0)iflabelP1else(0,165,255)for(f,x,y)intrajectory_history[label][-8:]:cv2.circle(frame,(int(x),int(y)),3,color,-1)7.4羽毛球轨迹可视化ifcurrent_pointisnotNone:shuttle_trajectory.append(current_point)iflen(shuttle_trajectory)5:shuttle_trajectory.pop(0)# 只保留最近5个点# 渐变线条效果越新的点线条越粗nlen(shuttle_trajectory)foriinrange(n-1):pt1shuttle_trajectory[i]pt2shuttle_trajectory[i1]thicknessint(1(i/n)*5)# 线条粗细从1到6渐变cv2.line(frame,pt1,pt2,(0,0,255),thickness,cv2.LINE_AA)cv2.circle(frame,current_point,4,(0,0,255),-1)# 当前羽毛球位置7.5小地图可视化mini_mapcourt_bg.copy()MINIMAP_Xframe.shape[1]-MINIMAP_W-20# 右上角位置MINIMAP_Y20# 转换球员轨迹到真实坐标mapper.trajectories_in(trajectory_history)mapped_trajmapper.track_mapping()forlabelin[P1,P2]:iflabelnotinmapped_traj:continuecolor(0,255,0)iflabelP1else(0,165,255)# 绘制轨迹点for(f,rx,ry)inmapped_traj[label]:# 真实坐标转小地图像素坐标pxint(5(rx/7.1)*(MINIMAP_W-10))pyint(5(ry/15.0)*(MINIMAP_H-10))cv2.circle(mini_map,(px,py),2,color,-1)# 绘制当前位置大圆点iflen(mapped_traj[label])0:f,rx,rymapped_traj[label][-1]pxint(5(rx/7.1)*(MINIMAP_W-10))pyint(5(ry/15.0)*(MINIMAP_H-10))cv2.circle(mini_map,(px,py),6,color,-1)cv2.putText(mini_map,label,(px8,py4),cv2.FONT_HERSHEY_SIMPLEX,0.4,color,1)# 将小地图叠加到主画面frame[MINIMAP_Y:MINIMAP_YMINIMAP_H,MINIMAP_X:MINIMAP_XMINIMAP_W]mini_map8. 数据导出与持久化程序运行结束后会自动导出以下三类数据方便后续的离线分析和报告生成。8.1 球员轨迹数据find_player.save_trajectories(folders[results])输出文件P1_trajectory.csv、P2_trajectory.csv格式frame, x, y像素坐标8.2 速度与距离数据speed_diss.save_speed_data(folders[results])输出文件P1_speed_data.csv、P2_speed_data.csv格式frame, speed_mps, dist_m, total_dist_m8.3 分析结果视频输出文件badminton_analysis_output.mp4内容包含所有可视化元素的完整分析视频9. 关键性能优化点为了保证系统能够实时运行代码中做了以下针对性的性能优化干净帧检测避免可视化绘制影响检测精度同时减少不必要的像素操作轨迹长度限制球员轨迹只显示最近 8 个点羽毛球轨迹只显示最近 5 个点大幅减少绘制开销缓存机制单应性矩阵只计算一次速度数据使用反向查找快速获取最新值轻量模型选择全部使用 YOLO nano 版本模型在保证精度的前提下最大化运行速度关闭冗余输出所有 YOLO 调用都设置verboseFalse避免控制台输出影响性能批量计算使用 NumPy 向量化运算代替 Python 循环提高速度计算效率 未来改进优化羽毛球的轨迹估计当前二项多项式拟合还不稳定当前羽毛球识别效果还是不理想还有很大的提升优化空间一些相关运动专业问题还需进一步完善羽毛球在当前小地图上的映射需进一步实现 如何运行直接运行Analysis.py即可 作者Even Jia| 机器学习 | 数据挖掘 | 神经网络 | 人工智能 |GitHub:https://github.com/jiazimo0308/Yolo-and-TrackNet-Badminton-Analysis非常感谢提供思路的作者如有任何问题都可与我联系E-mail:15832120175163.com