用Python实现跳一跳自动瞄准:OpenCV图像识别与ADB自动化完整拆解

发布时间:2026/10/20 4:49:25
用Python实现跳一跳自动瞄准:OpenCV图像识别与ADB自动化完整拆解
简介一份面向微信小游戏爱好者的 Python 自动跳一跳破解源码用于替代手动点击、突破分数瓶颈。程序基于 ADB 桥接手机与 Windows 64 位电脑通过截图识别方块位置、计算距离并模拟按压时长自动完成起跳适合有一定 Python 基础、希望研究图像识别与自动化脚本或单纯想高效体验跳一跳的开发者。资源包共 55 个文件总大小 9.68MB包含可直接阅读修改的 Python 源码.py、编译后的运行缓存.pyc、免配置启动的 exe 程序、支撑 ADB 通信的 dll 动态库以及覆盖小米、华为、iPhone、三星等多款手机的分辨率 json 配置和运行截图备份 png另附步骤清晰的使用说明。json 配置可按机型直接选用png 截图便于比对调试识别效果整体结构紧凑、上手门槛不高。目前已有 4544 人学习是同类跳一跳破解中较受关注的一份。对照说明连接手机后既可深入源码学习识别与计算的实现思路也可直接运行 exe 快速体验预置的多分辨率配置与调试截图能有效帮助读者规避连接失败、识别不准等常见问题减少自行摸索的成本。1. 项目定位用Python给微信跳一跳装一个自动瞄准的手有一次朋友聚会上桌上的话题被一款叫跳一跳的微信小游戏带走了。有人晒出上万分战绩其他人研究各种手法我在想的却是另一件事这种靠手势与节奏堆出来的分数能不能由程序自动完成于是有了这份源码——一套用Python实现的跳一跳自动跳跃脚本。它不修改游戏文件也不碰网络协议只是把“看屏幕、估距离、按下去”三个人工动作翻译成“截屏、识别、模拟触摸”的程序循环ADB截取手机画面OpenCV找到棋子和棋盘算出距离后换算按压时长再模拟触摸完成跳跃。所谓破解本质是视觉闭环的自动化。源码核心逻辑不到两百行适合想入门OpenCV和移动端自动化的开发者也适合研究“图像识别驱动操作”完整链路的人。2. 技术栈与工程结构ADB、OpenCV与一份能直接跑的源码跳一跳的渲染发生在OpenGL画布上启动之后系统UI里拿不到任何可用的控件节点传统UI自动化在这种场景下基本是黑匣子。想让它自动跳只能回到像素层面读屏幕图像在图像里找棋子、找落点最后模拟手指触摸。这套方案需要的技术栈因此非常明确ADB做手机通信OpenCV做图像理解Python做流程串联。2.1 选型逻辑为什么是这三样ADB是整个项目的地基。脚本要完成两件最基本的操作拿到屏幕画面和模拟一次触摸。这两件事在ADB里各有一条现成命令截屏用screencap触摸用input swipe组合起来正好覆盖游戏自动化的全部交互需求。相比在手机上装一个后台服务ADB方案零侵入、无残留手机上只要打开USB调试电脑端脚本随时可以连、随时可以杀跑挂了也不影响手机本身。OpenCV负责把截到的画面变成可计算的数据。棋子是什么颜色、棋盘边缘在哪里、目标点中心坐标是多少这些全要从像素里找。选择OpenCV而不是自己写一堆嵌套for循环去遍历像素是因为它提供了HSV颜色空间转换、形态学操作、轮廓查找和霍夫变换这些现成算法识别稳定性和抗干扰能力远高于手写扫描。尤其在不同背景下找深色棋子这类任务用颜色特征配合形态学去噪比逐像素比对可靠得多。Python在这里是胶水层。subprocess调ADB命令、OpenCV处理图像、numpy算距离最后再按参数模拟触摸全部代码用Python串起来不过两百来行。这套源码很适合作为图像识别入门的练习素材因为它用到的都是视觉处理里的高频手段ROI裁剪、HSV阈值、颜色过滤、边缘检测、坐标还原。每读一段代码你都会觉得这些名词从文档里的概念变成了手上能用的工具。2.2 目录结构与文件职责源码按功能拆成几个独立模块每个文件只负责一件事。这样你在排错的时候不需要在几百行代码里大海捞针哪一步出了问题就直奔哪个文件。文件职责说明main.py主循环串联截屏、识别、按压三个环节adb_helper.py封装ADB命令对外提供截屏和触摸两个接口detector.py图像识别包含棋子定位与落点检测两部分touch.py按压参数换算执行模拟触摸命令config.py集中存放按压系数、颜色阈值、调试开关requirements.txtPython依赖清单main.py里的主循环是一个典型的生产者消费者链adb_helper取出屏幕帧detector识别出两个关键坐标touch根据距离执行跳跃然后休眠一会儿等待动画结束再进入下一轮。每一轮之间必须留出足够的间隔因为跳一跳里上一次跳跃的起跳和落地动画加起来接近一秒程序跑得太快反而会打断游戏节奏。config.py放的都是那种“调参玄学”数值按压系数、棋子HSV范围、搜索区域比例全在这里。把它们从主逻辑里抽出来的原因很简单这部分数值在不同手机上表现差异明显放在单独文件里调试时改配置就能重跑不需要动核心代码。下一章讲核心实现时你会频繁回看这个文件。2.3 依赖清单与环境要求requirements.txt内容很精简opencv-python4.5.0 numpy1.21.0opencv-python是OpenCV为Python提供的发行版所有图像识别算法都从这里导入numpy则承担底层矩阵运算因为OpenCV读入的图像本质上就是一个BGR三通道的numpy数组后面所有坐标计算也都建立在numpy数组的索引和运算之上。安装依赖只需要一条命令pip install -r requirements.txt。环境硬性要求不多Python 3.8以上电脑能运行adb命令一部开启了USB调试的安卓手机。不需要root不需要模拟器也不需要安装任何手机端App。如果你手头只有模拟器也可以跑但模拟器的触摸模拟延迟比真机高按压系数需要单独调。提示这套方案对设备唯一的硬性要求是能开启USB调试其余全部在电脑端完成。3. 核心代码拆解截屏、识别到跳跃的完整链路这一章按照一次跳跃的完整流程走一遍代码。从获取屏幕帧开始到拿到棋子坐标、目标落点最后换算按压时间并执行触摸每个环节贴出核心实现并解释每一行在干什么。读过这章你再看主循环就会觉得每一段都很顺。3.1 屏幕采集exec-out直达内存截屏是第一环负责把手机当前画面取到电脑内存里。这里的关键是让截屏数据和ADB调用之间尽量减少中间步骤避免把文件先存到手机存储再拉取那样每轮会多花几百毫秒。adb_helper.py import subprocess import numpy as np import cv2 def get_screen(): # exec-out 让截屏数据直接通过标准输出流返回 raw subprocess.check_output( [adb, exec-out, screencap, -p] ) # 把字节流解码成 OpenCV 的 BGR 图像 arr np.frombuffer(raw, dtypenp.uint8) img cv2.imdecode(arr, cv2.IMREAD_COLOR) if img is None: raise RuntimeError(截屏数据为空请先检查 adb 连接) return imgexec-out参数的含义是让命令结果直接输出到标准输出而不是写入设备文件配合screencap -p拿到PNG格式的原始字节流。check_output把字节流收进内存后先用numpy把它转成一维数组再用imdecode解码成OpenCV的BGR图像矩阵。这里的BGR顺序需要注意往屏幕上画调试框时用的是BGR值换算HSV时OpenCV会自己处理通道顺序。截屏失败时函数会抛出一个明确的RuntimeError而不是让后续图像处理拿到一个空对象继续崩。实际调试中这种情况经常发生手机锁屏、USB授权弹窗遮挡、数据线松动都可能让截屏返回空数据提前暴露问题比在识别阶段报一堆莫名字段错误友好得多。3.2 棋子定位HSV色域里的深色块拿到屏幕帧后第一步是找到当前棋子的位置。跳一跳的棋子是一个深色小人与周围颜色鲜艳的棋盘背景差异明显用HSV颜色空间划定一个“低饱和度、低亮度”的范围就能把它捞出来。detector.py import cv2 import numpy as np def find_piece(img, hsv_range, roi_ratio0.55): h, w img.shape[:2] # 棋子只会出现在屏幕下半部分裁剪掉上半部分缩小搜索范围 start_y int(h * (1 - roi_ratio)) roi img[start_y:h, :] hsv cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) lower np.array(hsv_range[lower], dtypenp.uint8) upper np.array(hsv_range[upper], dtypenp.uint8) mask cv2.inRange(hsv, lower, upper) # 闭运算消除细碎噪点让棋子区域连成完整块 mask cv2.morphologyEx( mask, cv2.MORPH_CLOSE, np.ones((5, 5), np.uint8) ) contours, _ cv2.findContours( mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) if not contours: return None # 棋子占屏幕比例不小取面积最大的轮廓计算质心 c max(contours, keycv2.contourArea) M cv2.moments(c) if M[m00] 100: return None cx int(M[m10] / M[m00]) cy int(M[m01] / M[m00]) start_y return cx, cy这里有两个容易忽略的参数。roi_ratio控制搜索区域占屏幕的比例默认0.55表示只搜索屏幕下方55%的区域因为棋子永远不会出现在上半屏这个裁剪能明显减少轮廓检测的噪点。hsv_range是config.py里的颜色配置典型值是lower(0, 0, 50)和upper(180, 80, 150)含义是色相不限饱和度80以内亮度50到150之间专门用来抓那种偏灰偏深的棋子颜色同时避开亮色的棋盘。形态学闭运算在这里的作用是把棋子身上细碎的暗色噪点连成一片避免轮廓被分割成多个小块。findContours找到所有符合颜色的区域后用moments计算质心时M[m00]表示该轮廓的像素面积面积太小说明可能是误检直接返回None让主循环进入下一轮。最后cy加回start_y是因为前面裁剪过ROI轮廓返回的坐标是相对ROI的不补回偏移量的话棋子中心会整体上移按压落点全部偏短。3.3 落点识别水平边缘就是下一个棋盘目标点的识别比棋子定位麻烦一些因为棋盘颜色变化很大有深有浅但所有棋盘都有一个稳定的几何特征顶部是一条接近水平的边缘线。用Canny提取边缘再用霍夫变换找出这条线它的中点就是下一个落点。def find_target(img, piece_pos, offset50): h, w img.shape[:2] px, py piece_pos # 只保留棋子右上方的区域棋盘只会出现在跳动方向前方 roi img[int(h * 0.15):int(h * 0.55), px offset:w] gray cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) edges cv2.Canny(gray, 60, 160) lines cv2.HoughLinesP( edges, 1, np.pi / 180, threshold70, minLineLength80, maxLineGap15 ) if lines is None: return None candidates [] for line in lines: x1, y1, x2, y2 line[0] # 棋盘顶面的线段接近水平过滤掉竖线干扰 if abs(y2 - y1) 8: candidates.append(((x1 x2) // 2, (y1 y2) // 2)) if not candidates: return None # 取离棋子最近的水平线段中点避免被远处背景误检 candidates.sort( keylambda p: (p[0] - px) ** 2 (p[1] - py) ** 2 ) tx, ty candidates[0] return tx px offset, ty int(h * 0.15)ROI裁剪这一步同时做了两件事排除棋子左后方区域因为跳跃方向永远朝右上方排除屏幕顶部和最底部因为那些区域的边缘线大概率是其他物体的装饰。Canny的双阈值60和160控制边缘敏感度阈值太低会检出大量纹理噪点太高会把浅色棋盘的边缘滤掉。霍夫变换的参数里minLineLength80保证太短的杂线不会被采纳maxLineGap15允许棋盘顶部边缘中间有细小断点。水平过滤条件abs(y2-y1)8把竖线和斜线排除因为棋盘顶边在跳一跳里基本是水平的。最后按距离排序取最近的一个候选点这样即使画面右侧出现远处背景的干扰边缘也不会被当成目标。返回坐标时同样要补回ROI的偏移量。3.4 按压换算与模拟触摸距离和按压时间之间近似线性关系这是整套脚本的核心假设。距离越长按压时间越长。换算公式简单到不能再简单难点在于系数标定这个后面单独讲。touch.py import subprocess import time # 系数在这里只给默认值真机跑之前务必标定 DEFAULT_COEF 1.39 def press(x, y, distance, coefDEFAULT_COEF): duration int(distance * coef) subprocess.run( [adb, shell, input, swipe, str(x), str(y), str(x), str(y), str(duration)] ) def wait_animation(): # 等待上一次跳跃的动画结束避免操作堆积 time.sleep(1.2)input swipe命令的第五个参数是按压毫秒数起点和终点坐标相同系统就会把它识别成长按。这里直接复用棋子的坐标当起点等于程序用手指按在棋子所在位置并按住对应时长。duration由distance乘以coef得到coef的单位是“毫秒每像素”它近似反映触摸屏的线性响应特性。等待动画的sleep时间值得说两句。跳一跳从起跳到落地大约一秒钟主循环跑完一整轮后如果不等待下一轮截屏时棋子还在半空中识别出的位置会错得离谱。1.2秒这个值是经验值在大多数分辨率下够用程序跑得慢一点但稳定追求速度的话可以缩到1.0秒再短就容易翻车。主循环的串联逻辑在main.py里是这样的main.py import time from adb_helper import get_screen from detector import find_piece, find_target from touch import press, wait_animation from config import COEF def run(): while True: img get_screen() piece find_piece(img) if piece is None: wait_animation() continue target find_target(img, piece) if target is None: wait_animation() continue dx piece[0] - target[0] dy piece[1] - target[1] dist (dx * dx dy * dy) ** 0.5 press(piece[0], piece[1], dist, COEF) wait_animation() if __name__ __main__: run()识别失败时不直接退出而是跳过这一轮继续截屏。游戏过程中偶尔会有棋子卡在中心、画面看不清的情况这种容错让脚本可以被直接挂着跑而不用人盯。到这里主链路已经完整接下来就是把代码真正跑起来。4. 运行与调试把脚本在安卓真机上跑起来的完整步骤代码读完了接下来是动手环节。这一章按顺序给出从环境准备到首次跑通的全过程每一步都说明目的和验证方法避免你照着敲完命令却不知道哪一步出了问题。4.1 环境准备与ADB连接先把电脑端环境补齐。安装Python 3.8以上版本再安装Android平台工具让adb命令在终端里可用。这一步做完后执行adb version能看到版本号说明命令路径没问题再看下一步。手机端准备工作进入开发者选项打开USB调试。部分新机型第一次插线时会弹“是否允许USB调试”的授权框必须手动点允许否则后续所有命令都会报设备离线。用数据线连接电脑后执行adb devices健康的输出是设备列表里有一行状态为device的条目而不是unauthorized或offline。到这里先验证截屏链路再进入游戏。打开跳一跳的游戏界面随便停在一局进行中在终端执行adb exec-out screencap -p test.png然后用图片查看器打开test.png确认画面内容正常。能看到清晰的游戏画面说明ADB链路完全可用如果文件是黑的或打不开先回到授权和设备状态去排查。4.2 首次运行与系数标定直接运行python main.py通常会快速翻车原因几乎都在按压系数上。系数偏大时棋子会越过目标棋盘偏小时跳不到目标上。这两种失败的表现区别非常明显跳过头会落到棋盘外侧直接掉地上跳不满则会落在当前棋盘边缘。标定系数有一个笨但可靠的方法。先打开游戏找一个相邻两棋盘距离适中的画面让程序暂停自动循环在调试模式下单次执行一次跳跃。观察落点位置如果超出目标棋盘中心说明当前系数大了按超出的比例下调落在目标盘子边缘或不足说明系数小了。反复三四次就能收敛到一个可用范围1920x1080分辨率下经验值在1.35到1.45之间但不同系统的触摸上报频率会让这个值漂移换手机必须重新标。如果你不想靠肉眼估可以借用任意一张截图量出两棋盘中心的真实像素距离再用调试模式打印实际按压时长两者相除就得到一个更精确的初值。这个初值作为coef写入config.py后再进行一到两次微调。4.3 可视化调试把识别结果画出来识别模块的中间结果光靠打印坐标很难判断对错最直观的方式是把识别到的点和区域画到截图上保存下来。很多调试视频里那种带红圈绿圈的画面就是加了这样一个调试输出。detector.py 调试开关 DEBUG True def debug_annotate(img, piece, target, save_pathdebug/result.png): debug img.copy() if piece is not None: cv2.circle(debug, piece, 12, (0, 0, 255), 3) if target is not None: cv2.circle(debug, target, 12, (0, 255, 0), 3) cv2.imwrite(save_path, debug) print(fpiece{piece}, target{target})红圈代表识别到的棋子位置绿圈代表目标落点。如果红圈每次都能稳定套在棋子中心绿圈指向的位置和实际棋盘中心一致识别链路就是健康的有一方偏移优先检查对应函数的ROI坐标有没有补回偏移量。把debug_annotate插到主循环里跑一轮后打开保存的图片对比实际画面通常一眼就能定位问题在哪一环。这套方法在后续改识别算法时也会一直用到可以说是我调试这类视觉项目的主力手段。4.4 运行中的停止与恢复脚本进入循环后不会自己停下来想中止直接在终端按CtrlC。但要注意如果正赶上脚本执行input swipe那几毫秒中断可能会让adb进程残留下次再跑时先adb kill-server再重新连接。长时间挂机时把屏幕亮度调到最低但别锁屏锁屏会让下一次截屏拿到黑帧脚本就会卡在识别失败的分支里反复重试。正确做法是让屏幕保持常亮同时关闭游戏内可能弹出的对话框或网络提示保证画面一直处于可识别状态。5. 常见问题与避坑跑不起来时的排查清单代码本身不复杂问题几乎都出在环境和参数上。这一章把常见的几类失败列出来每条都是实际踩过的坑按“现象、原因、解决”的方式展开照着对号入座能省下不少排查时间。5.1 adb devices能看到设备但截屏全黑现象执行adb devices时设备在线但截屏测试生成的图片全黑偶尔还伴随明显卡顿。原因这种情况大部分是USB调试授权被系统拦截或设备处于锁屏状态。跳一跳要求屏幕保持常亮手机一旦锁屏screencap返回的就是黑屏数据。解决先设置屏幕常亮模式跳一跳运行期间不让设备休眠。再检查授权在手机上重新打开开发者选项撤销旧授权后重新授权最后adb kill-server重启ADB服务并重新连接设备。这两步做完截屏基本恢复正常不用怀疑是代码问题。5.2 棋子识别中心整体偏上现象红圈始终压在棋子顶部而不是正中间导致每次跳跃距离都偏短分数慢慢下降。原因find_piece裁剪了ROI后轮廓计算的cy没有加回start_y偏移量或者加了但偏移量计算错误。这个坑在换分辨率之后最容易出现因为ROI比例变化偏移量也跟着变。解决在debug模式下打印piece坐标和图像上实际位置比对。找出偏移差把start_y的补偿加对。每次修改ROI比例时务必同步检查坐标还原逻辑这是图像识别项目里最常见也最隐蔽的一类问题。5.3 按压系数标定后仍然不成线性现象近距离跳跃很准远距离总是跳过头或跳不满而且误差方向一致不是随机抖动。原因跳一跳的按压响应并非全程完美线性屏幕边缘区域和中央区域的触摸响应存在细微差异。用一个固定系数覆盖全距离自然在远端偏差越来越大。解决给系数加上距离修正项让coef随距离微调比如近距离用1.37、远距离用1.42。config.py里放一个系数表主循环根据distance区间选择对应系数比单一系数稳定得多。这种分段标定的思路在很多触摸类自动化项目里都适用。5.4 游戏更新后棋子颜色失效现象某次游戏更新后find_piece完全识别不到棋子mask显示一片空白或全是噪点。原因游戏角色换皮肤或改了贴图配色HSV阈值范围不再覆盖新颜色。颜色阈值方案最脆弱的地方就在这里它强依赖画面的像素分布。解决在config.py里把hsv_range的下限和上限适当放大重新跑debug模式观察mask。更稳的办法是升级识别策略把颜色阈值换成模板匹配或颜色直方图比对后者对新配色天然免疫缺点是初始实现成本高一些。5.5 部分手机截屏偶尔卡出高延迟现象运行过程中偶尔某轮耗时特别长游戏画面明显卡顿识别结果开始漂移。原因exec-out截屏在部分设备上偶发高延迟可能是系统截屏通道被后台任务占用也可能是USB传输速率波动。解决在get_screen里套一层超时保护截屏超过2秒就重试一次避免把一次偶发延迟变成持续的失败循环。代码里合理的超时和重试逻辑比盲目延长sleep更有效也更接近真实项目的健壮性要求。6. 二次开发从能跑到跑得稳的三个改造方向代码把流程跑通只是第一步真正常跑稳定还需要做很多工程化打磨。这一章给出三个可落地的改造方向难度递进你可以按自己的需求选一个动手。6.1 自动校准系数替代手工标定把手工标定变成程序自己做的事每跳一次截屏记录落点与目标中心的偏差偏差有方向就反向调整系数。连续几跳都在可接受范围内就认为系数收敛。实现上就是在主循环里多记录一次落点坐标在wait_animation之后补一次识别与比对。自动校准最大的好处是换手机不用再手动折腾我给朋友跑这套脚本的时候每台新设备都是靠它在二三十跳内自己找到合适系数的。6.2 失败检测与自动重试跳失败了程序是无感的它会继续傻傻地跳跃在已经结束的画面上乱按。给循环加一个状态判断跳跃后截屏如果棋子坐标没有变化或找不到目标就认为这一轮失败暂停几秒等待画面回到开始状态。这样脚本不会在失败画面上空转也不会因连续误判把情况越搞越糟。6.3 识别引擎从颜色阈值升级到模板匹配颜色阈值方案最脆弱换个配色就要调参。入门的升级是用模板匹配先存一张当前版本的棋子模板图用matchTemplate在画面里搜索。更进一步可以把棋子检测和落点检测换成轻量的图像分类模型但那样就需要引入标注数据和推理框架复杂度上一个台阶适合想做视觉算法的同学练手。有一次我给一台新手机跑这套脚本没做设备适配就直接上了默认系数结果棋子像喝了酒一样一跳一个歪连续七八跳全部掉地上。从那以后我每次拿到新设备都会强制走一遍标定流程先把系数校准到位再谈跳跃策略这个习惯也让我后面调其他视觉自动化项目少踩了很多坑。希望这份拆解能帮到想自己动手跑一遍跳一跳自动化的你。本文还有配套的精品资源点击获取