news 2026/9/27 1:16:47

基于MediaPipe的动作识别Python毕业设计源码实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于MediaPipe的动作识别Python毕业设计源码实战指南

简介:这份资源是基于Mediapipe实现动作识别的Python毕业设计源码包,面向计算机视觉、机器学习方向的高校学生与自学者,可用于智能家居、健身指导、游戏交互等场景的动作识别课题实践。压缩包共7个文件,约97KB,以3个py脚本为核心,配合2个csvi与2个csv动作样本数据,覆盖视频抽帧、姿态关键点采集与动作分类等环节,代码结构清晰便于二次开发。项目围绕Mediapipe的Pose模块展开,可实时检测并跟踪头部、肩部、肘部、手腕、髋部、膝盖、脚踝等关键关节,再结合SVM、随机森林或CNN等模型完成动作类别判定,同时涉及OpenCV视频流处理与matplotlib结果可视化,完整呈现数据预处理、模型训练、实时检测与结果展示的工程流程。目前已有242人学习,适合作为毕业设计参考方案,帮助读者快速理解Mediapipe管道机制、姿态估计与动作识别整合思路,并锻炼Python编程与问题解决能力。

1. 从一份「基于 MediaPipe 实现的动作识别 Python 毕业设计源码」说起

如果你正在搜「基于 MediaPipe 实现的动作识别 Python 毕业设计源码」,大概率是三种人之一:要交毕设的本科生、想快速跑通一个能演示的 CV 小项目、或者想拿它当自己产品原型的开发者。这个标题拆开看其实就三件事:MediaPipe 负责从摄像头里把人体的关键点抠出来,动作识别负责把这些关键点序列翻译成「挥手 / 深蹲 / 举手」这类语义标签,Python 负责把整条链路串起来。它不需要训练大模型,不需要 GPU 服务器,一台普通笔记本的摄像头就能跑,这正是它适合当毕业设计和入门实战的原因。

但「源码」两个字最容易骗人。网上流传的压缩包里,很多只是把 MediaPipe 官方示例改了个文件名,动作识别部分要么是写死的 if-else 阈值判断,要么干脆没实现,只画了骨架。真正能用的方案,核心不在 MediaPipe 本身——它已经把检测做到开箱即用——而在于你拿到 33 个关键点之后,怎么构造特征、怎么定义动作、怎么让识别不抖。这篇就按「先跑通骨架 → 再构造特征 → 再做分类 → 再避坑 → 再进阶」的顺序,把这条链路讲透,让你拿到任何一份类似源码都能看懂、改得动、演示得出来。

2. MediaPipe 姿态估计跑通:从摄像头到 33 个关键点

2.1 为什么选 MediaPipe 而不是自己训检测模型

做动作识别,第一步永远是「人现在是什么姿势」。传统路线是 YOLO 检测人 + 姿态估计网络回归关键点,两步走,模型加起来几百 MB,还要配环境、下权重、调 NMS。MediaPipe 的 Pose 方案把检测和关键点回归压进一个轻量管线,CPU 上单帧几毫秒,模型文件随包安装,pip install完就能用。对毕业设计这种「要演示、要答辩、时间有限」的场景,选它是理性的:你省下的环境调试时间,可以全部投到动作逻辑上。

代价也要说清楚。MediaPipe Pose 输出的是 33 个关键点的归一化坐标(x、y 在 0~1,z 是相对深度),它不直接告诉你「这个人在干嘛」。而且它对遮挡、快速运动、多人场景的处理有限——默认只跟踪画面里置信度最高的那个人。所以它适合单人、正面或侧面对着摄像头、光照正常的场景,这也正是毕设演示的标准环境。

2.2 最小可运行代码:打开摄像头画出骨架

先别管动作识别,把骨架跑出来,确认环境和摄像头没问题。下面这段是能直接抄的最小版本:

import cv2 import mediapipe as mp # 初始化 MediaPipe Pose 模块 mp_pose = mp.solutions.pose mp_draw = mp.solutions.drawing_utils # static_image_mode=False 表示走视频流模式,会做帧间跟踪,更快更稳 # model_complexity=1 是精度和速度的平衡点,0 最快、2 最准 pose = mp_pose.Pose( static_image_mode=False, model_complexity=1, smooth_landmarks=True, # 关键点平滑,减少抖动 min_detection_confidence=0.5, min_tracking_confidence=0.5 ) cap = cv2.VideoCapture(0) # 0 是默认摄像头,外接摄像头可试 1 while cap.isOpened(): ret, frame = cap.read() if not ret: break frame = cv2.flip(frame, 1) # 镜像,符合照镜子的直觉 rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # MediaPipe 要 RGB result = pose.process(rgb) if result.pose_landmarks: mp_draw.draw_landmarks( frame, result.pose_landmarks, mp_pose.POSE_CONNECTIONS) cv2.imshow('pose', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()

逻辑说明:pose.process()接收 RGB 帧,返回pose_landmarks,里面landmark是一个长度 33 的列表,每个元素有x, y, z, visibility。draw_landmarks只是可视化,真正做识别时你要的是那 33 组坐标,不是画出来的图。

参数说明:model_complexity是第一个该调的参数,笔记本卡就设 0,追求精度设 2;smooth_landmarks=True会让关键点在时间上做平滑,代价是动作切换时有一点点延迟,做快速动作识别时可以关掉对比效果;两个 confidence 阈值低于 0.5 会引入大量误检,高于 0.7 又容易在侧身时丢点,0.5 是常用起点。

2.3 关键点索引:动作识别只用到其中一部分

33 个点不用全用。做上肢动作(挥手、举手、比心)主要看 11~22 号点(肩、肘、腕、手);做下肢动作(深蹲、抬腿)看 23~28 号点(髋、膝、踝)。记住几个关键索引能省很多事:

索引部位索引部位
11 / 12左 / 右肩23 / 24左 / 右髋
13 / 14左 / 右肘25 / 26左 / 右膝
15 / 16左 / 右腕27 / 28左 / 右踝

提示:不要用绝对坐标做判断。人站得离摄像头远近不同,x、y 的绝对值会变,但关节之间的相对角度不变。这是后面特征工程的核心。

3. 从关键点到动作标签:特征构造与分类器选型

3.1 用关节角度代替原始坐标

直接把 33×2 个坐标丢给分类器,模型会学到「人站在画面左边还是右边」这种和动作无关的信息,换个位置就翻车。稳妥做法是算角度。以肘关节为例,用肩、肘、腕三点算夹角:

import numpy as np def calc_angle(a, b, c): """计算 b 点处的夹角,a/b/c 为 (x, y) 坐标""" a, b, c = np.array(a), np.array(b), np.array(c) ba = a - b bc = c - b # 点积公式求夹角,加 1e-6 防止除零 cosine = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc) + 1e-6) return np.degrees(np.arccos(np.clip(cosine, -1.0, 1.0))) def extract_features(landmarks): """从 33 个关键点里抽出对动作敏感的角度特征""" lm = [(p.x, p.y) for p in landmarks.landmark] feats = [] # 左右肘角度 feats.append(calc_angle(lm[11], lm[13], lm[15])) feats.append(calc_angle(lm[12], lm[14], lm[16])) # 左右肩角度(躯干与上臂) feats.append(calc_angle(lm[23], lm[11], lm[13])) feats.append(calc_angle(lm[24], lm[12], lm[14])) # 左右膝角度 feats.append(calc_angle(lm[23], lm[25], lm[27])) feats.append(calc_angle(lm[24], lm[26], lm[28])) return np.array(feats)

逻辑说明:calc_angle用向量点积求夹角,返回 0~180 度。extract_features把 6 个角度拼成一个 6 维向量,这就是每一帧的特征。相比 66 维原始坐标,6 维角度对位置、体型、距离都不敏感,泛化好得多。

参数说明:np.clip把余弦值夹到 [-1, 1],避免浮点误差导致arccos出 NaN,这是血泪经验,不加偶尔会崩。角度特征的数量可以按动作扩展,比如加手腕相对肩膀的高度差来判断「举手」。

3.2 时序窗口:单帧不够,要攒一段

动作是随时间变化的,单帧角度只能说明「此刻姿势」,说明不了「正在做动作」。常见做法是滑动窗口:维护一个长度为 N 的队列,每帧算完特征就入队,队列满了就取出来做一次分类。N 一般取 15~30,对应 0.5~1 秒。

from collections import deque WINDOW = 20 buffer = deque(maxlen=WINDOW) def on_frame(landmarks): feats = extract_features(landmarks) buffer.append(feats) if len(buffer) < WINDOW: return None # 窗口没满,先不判断 # 把 20 帧 × 6 维拼成 120 维,或者做统计量 seq = np.array(buffer) # shape: (20, 6) mean = seq.mean(axis=0) # 均值反映整体姿态 std = seq.std(axis=0) # 标准差反映动作幅度 return np.concatenate([mean, std])

逻辑说明:均值 + 标准差是性价比最高的时序压缩方式。均值告诉你「这段时间平均什么姿势」,标准差告诉你「动得厉害不厉害」。挥手和静止举手的均值可能接近,但挥手的标准差明显更大,这一维就能区分开。

参数说明:WINDOW太小(<10)动作没做完就判断,容易误触发;太大(>40)延迟明显,演示时感觉卡顿。20 帧在 30fps 下约 0.67 秒,是常用值。如果摄像头帧率不稳,建议按时间而不是帧数切窗口。

3.3 分类器:从阈值规则到轻量模型

特征有了,分类器有三档选择,按你的时间和数据量挑:

第一档是阈值规则,比如「肘角 < 90 且腕高于肩 → 举手」。优点是零训练、可解释、答辩好讲;缺点是动作一多就写成一堆 if-else,调参调到怀疑人生。适合只做 2~3 个动作的毕设。

第二档是 scikit-learn 的 SVM 或随机森林。你手动采集每个动作几十段样本,每段算一个特征向量,训练几秒钟就完事,准确率对 5~8 个动作通常能到 90% 以上。这是我最推荐的毕设方案,代码量小、效果稳、能讲清楚。

from sklearn.ensemble import RandomForestClassifier import joblib # X: (样本数, 特征维度) y: 对应动作标签 0/1/2... clf = RandomForestClassifier(n_estimators=100, max_depth=10, random_state=42) clf.fit(X_train, y_train) joblib.dump(clf, 'action_model.pkl') # 存下来,演示时直接加载

第三档是 LSTM 或 MediaPipe Model Maker 自定义训练。前者要 PyTorch/TensorFlow 环境,后者能直接吃视频片段训练,但环境配置和训练时间对毕设来说偏重。除非导师明确要求深度学习,否则没必要。

注意:采集训练数据时,每个动作至少 30 段,每段之间要有明确的「回到中立姿势」的间隔,否则窗口会跨动作,标签就脏了。

4. 避坑与排查:动作识别跑起来后最容易翻车的五件事

4.1 现象:识别结果疯狂跳变,同一动作一会儿 A 一会儿 B

原因:单窗口独立判断,没有做时间上的平滑。相邻两个窗口特征接近,分类器边界附近就会来回横跳。

解决:加一个投票或计数机制。维护最近 K 次预测结果,取众数;或者要求同一标签连续出现 M 次才确认输出。M 取 3~5 效果明显,代价是响应慢一点点。

from collections import Counter history = deque(maxlen=5) def stable_predict(label): history.append(label) return Counter(history).most_common(1)[0][0]

4.2 现象:换个房间、换个人,准确率断崖下跌

原因:用了绝对坐标或像素距离做特征,训练时人在画面中央,测试时人偏左,特征分布就变了。

解决:坚持用角度和相对量。如果已经用了坐标,做归一化:以髋部中心为原点,用肩宽做尺度归一。这一步不做,模型就是「认环境」而不是「认动作」。

4.3 现象:MediaPipe 频繁丢点,骨架一闪一闪

原因:光照不足、背景杂乱、人离摄像头太远或太近,导致检测置信度低于阈值。

解决:先调min_detection_confidence到 0.4 试试;改善正面光照;让人占画面高度的 1/2 到 2/3。如果还丢,在丢点帧用上一帧的关键点做插值,别让特征向量出现空值。

4.4 现象:程序跑几分钟越来越卡,内存一直涨

原因:每帧都新建Pose对象,或者把每帧图像存进列表没释放。

解决:Pose对象在循环外初始化一次,全程复用;不要缓存原始帧,只缓存特征向量;cap.release()和destroyAllWindows()一定要在 finally 里执行。

4.5 现象:答辩现场摄像头打不开或画面全黑

原因:摄像头被其他程序占用,或者VideoCapture(0)的索引不对,或者权限没给。

解决:提前用系统相机确认设备正常;代码里加cap.isOpened()判断并打印提示;准备一段录好的视频文件作为兜底输入,VideoCapture('demo.mp4')一样能跑,现场翻车时能救场。

5. 进阶技巧:让毕设从「能跑」变成「能打」

5.1 用状态机管住动作的起止

纯分类器输出的是「当前像哪个动作」,但真实动作有开始和结束。加一个简单状态机:中立态 → 检测到动作特征持续 N 帧 → 进入动作态并记录 → 特征回落到中立 → 输出一次完整动作。这样输出的是「完成了一次挥手」而不是「连续 30 帧都在挥手」,演示效果和逻辑清晰度都上一个台阶。

5.2 用 MediaPipe Model Maker 做自定义动作

如果你确实需要更多动作、又不想自己搭训练框架,Model Maker 支持喂视频片段训练自定义分类头。流程是:每个动作录 20~30 段短视频,按文件夹分好,调用训练脚本,导出 tflite 模型,再用 MediaPipe 的 Tasks API 加载。它对环境要求比手写 LSTM 低,但训练时间取决于数据量,毕设周期内要提前排期,别拖到最后一周。

5.3 验证方法:别只看准确率

答辩时导师最爱问「你怎么证明它真的行」。准备三样东西:一是混淆矩阵,看哪些动作容易混;二是实时演示,现场做动作看响应;三是边界测试,比如快速做、慢速做、背对摄像头做,主动说出局限在哪。能讲清楚「它在什么条件下不可靠」,比吹准确率 99% 更让人信服。

验证项方法合格线
单动作准确率每动作 20 段测试样本> 85%
响应延迟从动作开始到输出标签< 1 秒
抗抖动静止时误触发次数每分钟 < 2 次
鲁棒性换人 / 换位置重测下降 < 15%

5.4 我自己的习惯

我做完任何一个动作识别 demo,第一件事不是加动作,而是先录一段自己「乱动」的视频跑一遍,看它会不会乱报。乱报的根源八成在窗口边界和阈值,不在分类器。把误触发压下去,再往上加功能,顺序反了会一直返工。这套东西不难,难的是耐心调那几个参数,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 1:16:45

一周AI大模型与Agent进展:机器人推理、视频生成与部署实战盘点

周五下午&#xff0c;我把这周社区里刷屏的AI大模型和Agent相关讨论又翻了一遍&#xff0c;挑出16个值得看一眼的新进展。它们从机器人推理一路铺到视频生成&#xff0c;跨度不小&#xff0c;但有一个共同点&#xff1a;都在解决“从演示到能用”之间的那一段距离。不管是做算法…

作者头像 李华
网站建设 2026/9/27 1:16:26

嵌入式偶发故障排查三道防线:换机、录屏、固件对照

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/27 1:16:14

STM32CubeMX 6.14核心升级与工程配置实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/27 1:15:47

随机微分方程从直觉到数值求解:Ito积分与Euler-Maruyama方法详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/27 1:14:30

TMDS181中继器:HDMI 2.0信号完整性设计与调试实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/27 1:14:24

FM17580寄存器深度解析:掌控ISO14443-A物理层通信

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华