基于MediaPipe姿态估计的引体向上自动计数系统实现
简介本资源是一套基于Python的引体向上动作自动计数算法实现方案面向计算机科学、电子工程及应用数学等相关专业学生解决健身动作识别与运动次数统计的技术实践问题适用于课程设计、学期项目或毕业设计等中阶编程与CV综合应用场景。压缩包共8个文件含2个演示视频chinup.mp4与output.mp4用于效果验证2个核心Python脚本det_keypoint_unite_infer.py与common.py实现姿态检测与轨迹分析逻辑另有说明文档、备份文件及Git配置文件整体大小为47.26MB结构简洁、开箱即用。已有34人学习下载资源突出提供完整可运行代码链路与真实动作示例视频涵盖关键模块如关键点检测、运动周期判定与计数状态机设计便于读者理解算法原理、调试参数并拓展至其他健身动作识别任务。1. 项目概述当计算机视觉遇上健身计数最近在折腾一个挺有意思的小项目用Python写一个能自动数引体向上的程序。起因很简单我自己在家练引体每次做到力竭脑子里还得记着数经常做着做着就忘了刚才到底是第8个还是第9个。网上那些健身APP要么需要手动点击屏幕要么就是付费功能体验并不纯粹。作为一个喜欢动手的程序员我琢磨着能不能用摄像头对着自己让电脑自动帮我数这个想法听起来有点“极客”但实现起来其实涉及了计算机视觉和机器学习里几个非常经典且实用的技术点。它本质上是一个动作识别与计数问题。我们不是要识别复杂的手语或者舞蹈而是聚焦在一个周期性、规律性很强的健身动作上。通过摄像头捕捉人体关键点比如肩膀、手肘、手腕的坐标分析这些点在垂直方向上的周期性运动就能在动作达到标准时自动累加计数。这个项目非常适合作为Python和OpenCV的进阶练手项目。它不像人脸识别或者物体检测那样有现成的、精度极高的模型直接套用需要我们根据具体业务逻辑引体向上的动作标准去设计算法。你会接触到视频流处理、姿态估计、数据滤波、峰值检测等一系列知识点最终得到一个能实际运行、解决真实需求的小工具。无论你是健身爱好者想做个实用工具还是计算机视觉的初学者想找个有明确目标的项目来深化理解这个“引体向上计数器”都是一个绝佳的起点。接下来我会详细拆解整个项目的实现思路、核心代码以及如何一步步把它搭建起来。你会发现抛开复杂的理论用大约200行Python代码你就能拥有一个属于自己的“智能健身教练”。2. 核心思路与技术选型实现自动计数核心是量化“一次完整的引体向上”。一个标准的引体向上身体会经历“起始悬挂 - 上拉至下巴过杠 - 下放回起始位置”的过程。对应到计算机视觉里就是人体某个关键点的纵坐标y坐标会呈现出一个完整的“波谷-波峰-波谷”的周期性变化。2.1 技术路径对比要实现这个目标有几种主流的技术路径传统图像处理通过背景减除、轮廓检测来粗略定位人体然后计算人体轮廓最高点的运动轨迹。这种方法计算量小但对光照、背景变化敏感且无法精确区分手臂、躯干的相对运动在引体向上这种手臂与身体相对位置变化剧烈的场景下鲁棒性很差。基于深度学习的人体姿态估计这是当前的主流和首选方案。它可以直接输出人体数十个关键关节点的二维或三维坐标。对于引体向上我们只需要关注左右手腕、左右手肘、左右肩膀这几个点就足够了。通过分析手腕或肩膀相对于固定参照物如单杠的运动轨迹可以非常精准地判断动作是否完成。毫无疑问方案2是更优解。它精度高、抗干扰能力强。在Python生态中我们有多个优秀的姿态估计库可以选择OpenPose老牌、功能强大的库精度高但配置复杂速度较慢更适合研究或对精度要求极高的离线场景。MediaPipe Pose由Google开发轻量级、速度快、跨平台、易于集成。它提供了预训练的模型能够以接近实时的速度在普通CPU上运行非常适合我们这种需要实时处理视频流的应用。MMPoseOpenMMLab旗下的开源工具箱模型库丰富但更偏向研究和定制化开发。我们的选择是 MediaPipe。理由很充分它无需复杂的环境配置pip install mediapipe即可提供了简洁易用的Python API并且在速度和精度之间取得了很好的平衡完全满足我们实时计数的需求。2.2 算法流程设计确定了核心技术MediaPipe Pose后整个程序的逻辑流程图就清晰了视频输入打开摄像头或读取本地视频文件。姿态估计对每一帧图像使用MediaPipe Pose模型检测人体并提取所需关键点的坐标。数据提取与预处理提取关键点如左肩或双肩中点的纵坐标y。由于视频抖动和检测噪声原始的y坐标序列会像心电图一样毛刺很多。我们需要进行平滑滤波如移动平均或低通滤波得到一个平滑的运动轨迹曲线。峰值/谷值检测与计数逻辑在平滑后的曲线上寻找局部波峰最高点对应身体最低点/起始悬挂点和局部波谷最低点对应身体最高点/下巴过杠点。设计计数规则例如当检测到一个波谷身体上拉到顶并且其与前一个波峰的幅度差超过某个阈值确保动作幅度达标且当前状态不是“已经计数”的状态则计数值加1。可视化与输出在视频帧上绘制关键点、骨架连线、实时显示计数值并输出处理后的视频。注意这里有一个关键设计点选择哪个关键点作为计数的依据手腕点运动范围最大但可能在动作不标准如耸肩时产生误导。肩膀点运动相对稳定更能代表躯干的整体升降。我个人实践下来使用双肩连线的中点的纵坐标稳定性最好受手臂局部动作的影响最小。3. 环境搭建与依赖安装工欲善其事必先利其器。这个项目对环境要求很宽松但正确的安装能避免后续很多奇怪的错误。3.1 创建并激活虚拟环境强烈推荐这是一个好习惯能为项目创建一个独立的Python环境避免包版本冲突。# 打开终端Windows用CMD/PowerShell Mac/Linux用Terminal # 创建名为 pullup_counter 的虚拟环境 python -m venv pullup_counter_env # 激活虚拟环境 # Windows: pullup_counter_env\Scripts\activate # Mac/Linux: source pullup_counter_env/bin/activate激活后你的命令行提示符前面通常会显示环境名(pullup_counter_env)。3.2 安装核心库在激活的虚拟环境中运行以下命令pip install opencv-python mediapipe numpyopencv-python (cv2)计算机视觉的瑞士军刀用于视频捕获、图像处理、绘制图形和文字。mediapipe核心提供人体姿态估计模型。numpy进行高效的数组运算我们的坐标数据、滤波计算都依赖它。通常这些安装会很顺利。如果遇到网络问题可以考虑使用国内镜像源例如pip install opencv-python mediapipe numpy -i https://pypi.tuna.tsinghua.edu.cn/simple3.3 验证安装创建一个简单的测试脚本test_env.pyimport cv2 import mediapipe as mp import numpy as np print(fOpenCV version: {cv2.__version__}) print(fMediaPipe version: {mp.__version__}) print(fNumPy version: {np.__version__}) print(环境检查完毕如果无报错则说明安装成功。)在终端运行python test_env.py如果能看到版本号输出且没有错误恭喜你环境准备就绪。4. 代码实现与核心模块解析让我们开始编写核心代码。我将代码分成几个功能模块来讲解你可以将它们整合到一个.py文件中。4.1 初始化与视频流捕获首先导入必要的库并初始化MediaPipe Pose模型。import cv2 import mediapipe as mp import numpy as np from collections import deque import time class PullUpCounter: def __init__(self, smooth_window5, pull_threshold0.08): 初始化引体向上计数器 :param smooth_window: 平滑滤波的窗口大小越大曲线越平滑但延迟越高 :param pull_threshold: 判定为一次有效引体向上的相对幅度阈值 # 初始化MediaPipe Pose self.mp_pose mp.solutions.pose self.pose self.mp_pose.Pose( static_image_modeFalse, # 视频流模式 model_complexity1, # 模型复杂度 (0,1,2)。1是精度和速度的平衡点。 smooth_landmarksTrue, # 平滑关键点减少抖动 min_detection_confidence0.5, # 检测置信度阈值 min_tracking_confidence0.5 # 跟踪置信度阈值 ) self.mp_drawing mp.solutions.drawing_utils # 计数相关变量 self.count 0 self.is_counted False # 防止一次动作重复计数 self.smooth_window smooth_window self.pull_threshold pull_threshold # 用于存储历史y坐标进行平滑和峰值检测 self.y_history deque(maxlen30) # 限制历史长度避免内存无限增长 self.smoothed_y_history [] # 状态跟踪 self.direction None # up or down self.last_state down # 假设起始状态为悬挂 def process_frame(self, frame): 处理单帧图像返回绘制后的帧和当前计数 # 将BGR图像转换为RGB因为MediaPipe需要RGB输入 image_rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) image_rgb.flags.writeable False # 为了提升性能标记图像为不可写 # 进行姿态估计 results self.pose.process(image_rgb) # 转换回BGR用于OpenCV显示 image_rgb.flags.writeable True annotated_image cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR) current_y None if results.pose_landmarks: # 绘制姿态骨架和关键点 self.mp_drawing.draw_landmarks( annotated_image, results.pose_landmarks, self.mp_pose.POSE_CONNECTIONS, self.mp_drawing.DrawingSpec(color(0, 255, 0), thickness2, circle_radius2), # 点 self.mp_drawing.DrawingSpec(color(255, 0, 0), thickness2) # 线 ) # 获取关键点坐标 (图像尺寸归一化到[0,1]) h, w, _ annotated_image.shape landmarks results.pose_landmarks.landmark # 选择计数参考点双肩中点 left_shoulder landmarks[self.mp_pose.PoseLandmark.LEFT_SHOULDER] right_shoulder landmarks[self.mp_pose.PoseLandmark.RIGHT_SHOULDER] mid_shoulder_y (left_shoulder.y right_shoulder.y) / 2.0 current_y mid_shoulder_y # 归一化的y坐标 # 将归一化坐标转换为像素坐标用于绘制可选 pixel_y int(mid_shoulder_y * h) # 在肩膀上画一个醒目的点 cv2.circle(annotated_image, (int((left_shoulder.xright_shoulder.x)/2 * w), pixel_y), 8, (0, 0, 255), -1) return annotated_image, current_y代码解析我们创建了一个PullUpCounter类来封装所有功能这样代码更整洁也便于管理状态。static_image_modeFalse是关键它告诉MediaPipe我们处理的是视频流它会利用帧间相关性进行跟踪大幅提升速度。我们选择model_complexity1在速度和精度间取得平衡。如果追求更高精度且电脑性能好可以设为2。smooth_landmarksTrue是MediaPipe内置的平滑滤波器能初步减少关键点抖动。我们使用双肩中点的y坐标作为计数依据并将其归一化到[0, 1]这样算法不受人距离摄像头远近的影响。4.2 数据平滑与滤波直接从模型获取的y坐标是有噪声的。我们需要自己再加一道平滑处理。在PullUpCounter类中添加方法def smooth_data(self, y_value): 使用简单移动平均进行数据平滑 if y_value is not None: self.y_history.append(y_value) if len(self.y_history) self.smooth_window: smoothed np.mean(list(self.y_history)[-self.smooth_window:]) self.smoothed_y_history.append(smoothed) return smoothed return None移动平均是最简单的滤波方法。smooth_window5意味着用最近5个历史值的平均值作为当前平滑值。窗口越大曲线越平滑但对动作变化的响应也越慢延迟越高。对于引体向上这种频率不高的运动5是个不错的起点。4.3 峰值检测与计数逻辑这是整个项目的“大脑”。我们需要从平滑后的y坐标序列中识别出波峰和波谷。在PullUpCounter类中添加核心计数方法def count_logic(self, smoothed_y): 基于平滑后的y坐标序列进行计数判断 if smoothed_y is None or len(self.smoothed_y_history) 10: return # 数据不足不进行判断 # 1. 判断当前运动方向 if len(self.smoothed_y_history) 1: delta self.smoothed_y_history[-1] - self.smoothed_y_history[-2] if delta -0.001: # y值减小身体上升 self.direction up self.is_counted False # 进入上升阶段重置计数标志 elif delta 0.001: # y值增大身体下降 self.direction down # 2. 简单的局部极值检测更稳健的方法可使用scipy.signal.find_peaks # 我们检测从‘下降’转为‘上升’的拐点作为波谷身体最高点 if self.direction up and self.last_state down: # 可能找到了一个波谷最低y值点 # 我们需要检查这个波谷的“深度”是否足够 # 计算最近一段历史数据中的粗略幅度 recent_history list(self.smoothed_y_history)[-10:] if len(recent_history) 5: local_max max(recent_history[:5]) # 前一段的局部高点身体低位 local_min min(recent_history[-5:]) # 当前的局部低点身体高位 amplitude local_max - local_min # 关键判断幅度超过阈值且本次波谷尚未被计数 if amplitude self.pull_threshold and not self.is_counted: self.count 1 self.is_counted True print(f计数当前总数{self.count} 检测幅度{amplitude:.4f}) # 更新上一帧状态 self.last_state self.direction if self.direction else self.last_state逻辑精髓方向判断通过比较当前帧与上一帧平滑后的y值判断身体是在向上 (up) 还是向下 (down) 运动。状态机我们维护一个简单的状态机。一次完整的计数发生在检测到从down(下降) 状态切换到up(上升) 状态的时刻并且这个切换点是一个“足够深”的波谷。幅度阈值 (pull_threshold)这是防止误判的关键。y坐标是归一化的0.08意味着肩膀需要移动超过画面高度8%的距离才被认为是一次有效的引体向上。这个值需要根据你的摄像头角度、人与摄像头的距离进行实地校准。如果计数太灵敏小幅度晃动就计数就调高这个值如果计数不灵敏做了完整的动作但不计数就调低这个值。防重判 (is_counted)确保在一个上升阶段只计数一次避免在波谷点附近连续多帧触发计数。4.4 主循环与可视化最后我们将所有模块串联起来形成主程序循环。def main(): counter PullUpCounter(smooth_window5, pull_threshold0.07) # 打开摄像头 cap cv2.VideoCapture(0) # 参数0表示默认摄像头。如果是视频文件改为文件路径。 if not cap.isOpened(): print(无法打开摄像头) return print(开始检测引体向上...) print(调整姿势确保全身在画面中。) print(按 q 键退出程序。) while cap.isOpened(): success, frame cap.read() if not success: print(无法读取视频帧。) break # 水平翻转图像获得镜像视图更像照镜子 frame cv2.flip(frame, 1) # 处理帧获取绘制后的图像和当前y坐标 processed_frame, current_y counter.process_frame(frame) # 平滑数据 smoothed_y counter.smooth_data(current_y) # 执行计数逻辑 if smoothed_y is not None: counter.count_logic(smoothed_y) # 在画面上绘制信息 # 1. 显示计数 cv2.putText(processed_frame, fCount: {counter.count}, (50, 80), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 255), 4) # 2. 显示实时y坐标调试用 if current_y is not None: cv2.putText(processed_frame, fY: {current_y:.3f}, (50, 140), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 0), 2) if smoothed_y is not None: cv2.putText(processed_frame, fSmoothed Y: {smoothed_y:.3f}, (50, 180), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2) # 3. 显示方向状态 status_text fState: {counter.last_state} cv2.putText(processed_frame, status_text, (50, 220), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2) # 4. 绘制简单的参考线可选 h, w, _ processed_frame.shape cv2.line(processed_frame, (0, h//3), (w, h//3), (100, 100, 100), 1) # 上参考线 cv2.line(processed_frame, (0, 2*h//3), (w, 2*h//3), (100, 100, 100), 1) # 下参考线 # 显示画面 cv2.imshow(Pull-up Counter, processed_frame) # 退出条件 if cv2.waitKey(5) 0xFF ord(q): break # 释放资源 cap.release() cv2.destroyAllWindows() counter.pose.close() print(f最终计数{counter.count}) if __name__ __main__: main()5. 参数调优与实战技巧代码跑起来只是第一步让它准确工作才是挑战。以下是我在多次调试中总结的经验5.1 关键参数调优指南程序中有几个“旋钮”需要你根据实际环境进行调整参数所在位置作用调优建议smooth_windowPullUpCounter.__init__()移动平均滤波的窗口大小。值越大曲线越平滑抗抖动能力越强但延迟也越高。引体向上动作较慢建议从5开始。如果画面抖动大可以尝试7或9。观察Smoothed Y的曲线应该是一个清晰的波浪线没有太多毛刺。pull_thresholdPullUpCounter.__init__()判定一次有效引体向上的最小幅度。这是最重要的参数默认0.077%的画面高度。调参方法做几个标准引体向上观察控制台打印的检测幅度。确保标准动作的幅度远大于阈值而身体小幅晃动或调整手势的幅度远小于阈值。例如标准动作幅度在0.15-0.25那么阈值设为0.10会比较安全。min_detection_confidencemp_pose.Pose()姿态检测的置信度阈值。低于此值MediaPipe会重新检测而不是跟踪。默认0.5一般够用。如果人部分出框导致检测不稳定可以适当降低到0.4。如果背景复杂导致误检可以提高到0.6。min_tracking_confidencemp_pose.Pose()姿态跟踪的置信度阈值。低于此值会认为跟踪丢失。通常和min_detection_confidence设成一样即可。5.2 拍摄与姿势建议算法再强也需要好的输入。以下几点能极大提升计数准确率摄像头位置侧面拍摄是最佳角度。能清晰看到身体在垂直方向的上下运动。正面拍摄时身体前后倾会被误判为上下运动。背景尽量选择简洁、静态的背景。避免背景中有频繁移动的物体如走过的人、飘动的窗帘它们可能干扰姿态估计。光照保证光线充足、均匀。避免逆光人变成剪影或强烈的侧光造成阴影这会导致关键点检测不准。着装穿紧身或与背景对比度高的衣服。MediaPipe是基于颜色和纹理估计关键点的穿宽松的黑衣在暗背景下效果会打折扣。全身入镜确保头、肩、髋、脚都在画面内。MediaPipe需要看到足够多的身体部分来建立完整的姿态模型。5.3 计数逻辑的增强思路基础版本可能在某些边界情况下出错。这里提供两个进阶优化思路使用更稳健的峰值检测库我们手写的极值检测逻辑比较简单。可以使用scipy.signal.find_peaks函数它能更专业地查找波峰波谷并允许你设置峰值高度、距离、宽度等约束条件抗噪能力更强。from scipy.signal import find_peaks # ... 在count_logic中当有足够历史数据时... # 寻找波谷 (因为y坐标在顶部时值最小) valleys, _ find_peaks(-np.array(self.smoothed_y_history), distance10, prominencepull_threshold) # distance10 避免在同一个波谷附近重复检测 # prominence 是峰值突出度相当于我们的幅度阈值多关键点验证除了肩膀中点可以同时监测左右手腕的y坐标。只有当肩膀和手腕的运动轨迹都满足“达到阈值幅度”的条件时才计数。这能有效过滤掉“只动肩膀不动手”的无效动作。6. 常见问题排查与解决方案在实际运行中你可能会遇到以下问题。别慌大部分都有解决办法。6.1 程序运行类问题问题现象可能原因解决方案导入mediapipe报错1. 未在虚拟环境中安装。2. Python版本不兼容MediaPipe需要Python 3.7-3.10。3. 操作系统缺少依赖Linux常见。1. 确认终端前有(your_env_name)并用pip list检查。2. 使用python --version检查建议使用Python 3.8或3.9。3. 对于Linux尝试安装libopencv-core-dev和libopencv-highgui-dev。摄像头打不开黑屏1. 摄像头被其他程序占用。2.cv2.VideoCapture(0)中的索引不对。3. 权限问题Mac/Linux。1. 关闭其他可能使用摄像头的软件微信、Zoom等。2. 尝试1,2等其他索引或使用视频文件路径测试。3. 检查系统隐私设置中的摄像头权限。程序卡顿帧率很低1. 默认摄像头分辨率太高。2.model_complexity设置过高。3. 电脑性能不足。1. 在cv2.VideoCapture(0)后添加cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)和cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)降低分辨率。2. 将model_complexity从1改为0轻量模式。3. 关闭不必要的后台程序。6.2 算法与计数类问题问题现象可能原因解决方案完全检测不到人体无骨架绘制1. 人不在画面中或距离太远。2. 光照太暗或逆光。3.min_detection_confidence设置过高。1. 调整位置确保全身在画面中。2. 改善光照避免背对光源。3. 暂时将min_detection_confidence和min_tracking_confidence降低到0.4试试。骨架抖动严重Y值跳动大1. 摄像头本身抖动或分辨率低。2. 背景杂乱。3. 平滑窗口smooth_window太小。1. 固定好摄像头。2. 简化背景。3. 增大smooth_window参数如从5调到9。计数过多太灵敏1.pull_threshold阈值设置过低。2. 小幅度的身体晃动如调整握姿被误判。这是最常见的问题。1.逐步提高pull_threshold每次增加0.02直到小幅晃动不触发计数但标准动作仍能触发。2. 结合“多关键点验证”思路要求手腕和肩膀同时达到阈值。计数过少漏计1.pull_threshold阈值设置过高。2. 动作幅度不够下巴未过杠。3. 摄像头角度问题侧面运动在画面中垂直位移小。1.逐步降低pull_threshold。2. 确保动作标准。可以用程序显示的Y值反馈来指导动作幅度。3. 确保摄像头从正侧面拍摄使上下运动在画面中投影的位移最大。计数延迟动作做完后才显示smooth_window设置过大或峰值检测逻辑有延迟。适当减小smooth_window。如果使用find_peaks检查distance参数是否过大。同一个引体向上被计为2次防重判逻辑is_counted失效或在波谷点附近方向频繁切换。1. 检查count_logic中is_counted的重置逻辑应在检测到up方向时重置。2. 增加方向判断的“死区”将if delta -0.001:中的0.001绝对值调大避免噪声引起的方向误判。6.3 调试技巧当计数不准时不要盲目调参要学会观察数据开启调试信息主循环中我们已经将current_y和smoothed_y打印在画面上了。观察做动作时这两个值的变化范围是否稳定。打印幅度在count_logic函数中将计算出的amplitude也打印或显示到画面上。这能直观告诉你当前动作被算法“认为”有多大。录制视频调试将cv2.VideoCapture(0)改为cv2.VideoCapture(your_pullup_video.mp4)使用一段预先录好的、包含正确和错误计数的视频进行调试。这样可以反复测试而不需要每次都实地做引体向上。7. 项目扩展与进阶方向这个基础版本已经可以工作了但你可以把它变得更有趣、更强大保存训练数据与回放将每次训练的日期、时间、计数保存到CSV文件或数据库。可以增加一个模式回放历史训练视频并叠加当时的计数曲线进行动作分析。动作标准度评估不仅计数还能评估质量。例如计算节奏分析“上拉”和“下放”阶段各自所用的时间判断是否做得太快借力或太慢。检测身体摆动分析髋关节或脚踝关键点的横向位移如果摆动过大提示“避免借力摆动”。检测下巴过杠除了肩膀还可以检测鼻子或下巴的关键点是否在某一时刻高于手腕需正面或斜侧面角度。图形化界面 (GUI)使用tkinter或PyQt为程序做一个简单的界面包含“开始计数”、“结束”、“重置计数”、“参数设置滑块”等按钮更用户友好。迁移到其他运动这套框架姿态估计 - 关键点轨迹分析 - 峰值检测计数具有很强的通用性。你可以尝试修改关键点和计数逻辑用于深蹲计数分析髋关节的垂直运动、俯卧撑计数分析肩膀或髋关节的垂直运动、跳绳计数分析手腕的周期性画圈运动等。部署为Web应用使用Flask或FastAPI搭建一个后端服务通过网页上传视频文件后端处理完成后将带计数结果的视频返回给用户。或者使用WebRTC实现网页端的实时计数。这个项目从想法到实现贯穿了问题定义、技术选型、代码实现、调试优化和功能扩展的全过程。它最宝贵的价值不在于代码本身而在于解决一个具体问题时你所经历的思考、试错和调整的完整闭环。希望这份超详细的拆解能帮你不仅做出这个计数器更能理解背后“为什么这么做”从而具备去解决下一个更酷问题的能力。本文还有配套的精品资源点击获取