尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

YOLOv8+NCNN+AutoJS:端侧视觉赋能手游自动化实战

YOLOv8+NCNN+AutoJS:端侧视觉赋能手游自动化实战 1. 从“手动肝”到“端侧视觉”这套方案到底在解决什么问题如果你玩过手游尤其是那种需要反复刷日常、看广告领奖励、挂机刷资源的类型大概都经历过一种疲惫明明游戏本身挺有意思但每天重复点击几百次之后乐趣全被消磨干净了。更烦的是很多游戏把“看广告”做成了获取资源的必经之路30秒一条一天下来光广告就能看掉一两个小时。于是“自动化”就成了一个很自然的需求——让机器替我去点、替我去滑、替我去判断什么时候该关广告。但传统的自动化方案有个致命短板它太“瞎”了。基于坐标点击的脚本换个分辨率就废了基于固定颜色判断的脚本游戏一改UI就失灵基于无障碍服务的方案遇到游戏自绘控件直接抓瞎。真正靠谱的做法是让脚本“长眼睛”——用视觉模型实时识别屏幕上的关键目标再驱动点击和滑动。这就是端侧视觉赋能手游自动化的核心思路。我这次要拆解的方案技术栈是YOLOv8 NCNN AutoJS。YOLOv8 负责训练一个轻量目标检测模型能识别游戏界面里的“关闭广告按钮”“领取奖励按钮”“跳过按钮”等关键元素NCNN 负责把这个模型塞进手机端做高效推理不依赖网络、不上传截图、延迟低AutoJS 负责截屏、调用推理、执行点击和滑动。整套东西跑在手机本地不碰账号数据、不修改游戏内存、不注入进程属于“看屏幕、动手点”的外部自动化风险相对可控。这套内容适合谁看如果你是有一定编程基础的手游玩家想自己折腾一套挂机方案如果你是做自动化测试的工程师想了解端侧视觉在移动端的落地方式或者你只是对 YOLOv8 和 NCNN 的移动端部署感兴趣这套流程都能给你一个完整的参考。我会从模型训练、模型转换、端侧推理、AutoJS 集成、广告拦截逻辑、常见坑排查这几个维度把整个链路讲透。需要提前说明的是本文讨论的是技术实现路径不鼓励在任何违反游戏用户协议的场景下使用。自动化脚本的使用边界请自行判断和承担。我们聚焦在“端侧视觉推理”这个技术本身它同样适用于自动化测试、辅助工具、无障碍交互等正当场景。2. 整体方案设计与技术选型为什么是 YOLOv8 NCNN AutoJS2.1 为什么不用云端推理非要端侧先说一个最容易被忽略的问题延迟。手游自动化的核心诉求是“看到即点到”从截屏到执行动作理想情况下要在 100ms 到 300ms 内完成。如果走云端推理截图上传、服务器排队、模型推理、结果回传这一圈下来网络好的时候 200ms网络差的时候直接上秒。等你收到“关闭广告按钮在 (850, 1200)”这个结果时广告可能已经播完自动关了或者游戏已经进入了下一个界面。端侧推理把这一整圈压缩到本地截屏 → 预处理 → 模型推理 → 后处理 → 输出坐标全程在手机内部完成。以中端骁龙芯片为例一个 320×320 输入的 YOLOv8n 模型NCNN 推理耗时大概在 15ms 到 40ms 之间加上截屏和预处理整体可以控制在 80ms 以内。这个延迟水平才真正具备“实时视觉”的可用性。另一个原因是隐私和账号安全。截图里可能包含聊天记录、好友列表、充值界面等敏感信息。端侧推理意味着这些画面永远不会离开你的设备不存在上传到某个服务器再被留存的风险。对于在意账号安全的玩家来说这是底线。2.2 YOLOv8 在游戏目标检测里的优势目标检测模型的选择很多SSD、Faster R-CNN、YOLO 系列、DETR 等等。为什么偏偏选 YOLOv8我实际对比过几个方案结论很明确YOLOv8n 足够小nano 版本参数量约 3.2M权重文件 6MB 左右转成 NCNN 后进一步压缩塞进 APK 毫无压力。训练成本低游戏界面元素通常类别少5 到 15 类、样本获取容易自己截屏标注就行YOLOv8 在几百张图上就能收敛出可用模型。后处理简单YOLOv8 的输出是标准的检测框格式NMS 之后直接拿到类别和坐标不需要复杂的解码逻辑。生态成熟Ultralytics 的框架文档齐全训练、验证、导出一条龙遇到问题社区里基本都能搜到答案。相比之下DETR 系列虽然精度不错但端侧部署麻烦后处理复杂SSD 对小目标的检测效果在游戏 UI 场景下不如 YOLOv8。所以 YOLOv8n 是这个场景下的甜点选择。2.3 NCNN 为什么比 ONNX Runtime 更适合这个场景端侧推理框架的选择常见的有 NCNN、MNN、TFLite、ONNX Runtime Mobile、Paddle Lite 等。我最终选 NCNN主要基于几个实际考量体积和依赖。NCNN 是腾讯开源的纯 C 推理框架无第三方依赖编译出来的库非常小。ONNX Runtime Mobile 虽然功能强大但库体积偏大集成到 AutoJS 这类环境里比较笨重。NCNN 的轻量特性让它更容易嵌入到各种宿主程序中。ARM 优化。NCNN 从设计之初就是为 ARM 平台优化的对 NEON 指令集、FP16 推理、多线程调度的支持非常成熟。在同样的骁龙 778G 上我实测 YOLOv8n 的 NCNN 版本比 ONNX Runtime 版本快大约 20% 到 30%。量化支持。NCNN 支持 INT8 量化可以把模型进一步压缩推理速度再提升一截。虽然量化会带来一定精度损失但在游戏 UI 这种高对比度、目标清晰的场景下INT8 量化的精度损失几乎可以忽略。与 AutoJS 的集成便利性。AutoJS 本身支持调用本地 so 库NCNN 编译成动态库后通过 JNI 接口暴露推理函数AutoJS 侧用 Java 反射或者封装好的接口调用即可。整个链路清晰可控。2.4 AutoJS 在链路中的角色定位AutoJS 在这个方案里承担的是“调度中枢”的角色定时截屏、把截图传给推理模块、拿到检测结果、根据业务逻辑决定点击哪里、执行点击或滑动。它不负责推理本身只负责“看”和“动”。选择 AutoJS 而不是 Appium 或者原生开发原因很实际AutoJS 的脚本编写效率极高改一行代码就能立刻跑起来看效果调试成本低。Appium 更适合做跨应用的自动化测试配置复杂、启动慢不适合这种需要快速迭代的游戏挂机场景。原生开发虽然性能最好但开发周期长不适合个人玩家折腾。AutoJS 的截屏能力是这套方案的基础。它可以通过requestScreenCapture()获取屏幕截图返回一个 Image 对象再转成 Bitmap 或者直接读取像素数据。这个截屏频率可以控制在每秒 5 到 10 次配合端侧推理的延迟整体响应足够流畅。3. 从零训练一个游戏 UI 检测模型数据、标注与训练参数3.1 数据采集截屏、筛选与增强训练模型的第一步是搞数据。游戏 UI 检测的数据获取其实很简单打开游戏在各种界面下截屏保存成 PNG 或 JPG。但有几个细节需要注意。截屏分辨率要统一。不同手机的屏幕分辨率不同如果你打算在多个设备上跑最好统一缩放到一个固定尺寸比如 720×1280 或者 1080×1920。YOLOv8 训练时会做 letterbox 缩放但原始数据的分辨率差异太大会影响模型泛化。我的做法是用主力设备截屏统一缩放到 720×1280训练出来的模型在 1080P 设备上推理时把输入图缩放到模型输入尺寸即可。场景覆盖要全面。以“广告拦截”为例你需要覆盖广告播放界面有关闭按钮、跳过按钮、广告倒计时界面、广告结束后的奖励领取界面、游戏主界面、各种弹窗界面。每个界面至少截 30 到 50 张确保不同背景、不同广告内容、不同按钮位置都有样本。负样本不能少。所谓负样本就是“没有目标但容易被误检”的界面。比如游戏主界面里也有各种按钮但你不希望模型把“开始游戏”按钮误判成“关闭广告”按钮。所以主界面、设置界面、背包界面这些都要截一些标注时全部标为背景。数据量方面我的经验是5 到 8 个类别每个类别 200 到 400 个实例总共 1500 到 3000 张图就足够训练出一个可用的模型。如果某些类别样本少可以用旋转、亮度调整、对比度调整做数据增强但不要过度否则容易过拟合。3.2 标注工具与标注规范标注工具我用的是 LabelImg 和 Roboflow 的组合。LabelImg 本地标注免费、离线、操作简单Roboflow 用来做在线标注和格式转换方便团队协作。如果只是个人用LabelImg 足够了。标注规范这块有几个坑我踩过框要贴紧目标检测框不要留太多空白否则模型学到的特征会包含大量背景噪声。但也不要太紧留 2 到 3 个像素的边距比较合适。类别命名要统一比如“关闭广告按钮”和“广告关闭”要统一成一个名字不要混用。类别名建议用英文避免中文编码问题。遮挡目标要标注如果按钮被部分遮挡只要还能看出是那个按钮就要标注。模型需要学会处理遮挡情况。小目标要特别注意游戏 UI 里的关闭按钮有时候很小比如 30×30 像素。标注时一定要放大仔细标训练时也要确保输入分辨率足够。标注完成后导出成 YOLO 格式每张图对应一个 txt 文件每行是类别id 中心x 中心y 宽 高坐标都归一化到 0 到 1 之间。3.3 YOLOv8 训练参数配置与调优数据准备好之后训练本身反而简单了。Ultralytics 的 YOLOv8 训练命令非常简洁yolo detect train datagame_ui.yaml modelyolov8n.pt epochs150 imgsz640 batch16 patience30但参数背后的逻辑值得说清楚modelyolov8n.pt选 nano 版本因为端侧推理对模型大小和速度敏感。如果精度不够可以尝试 yolov8s但推理耗时会翻倍。imgsz640训练输入尺寸。游戏 UI 检测的目标通常不会特别小640 足够。如果关闭按钮特别小可以提到 960但训练和推理成本都会增加。epochs150训练轮数。太少欠拟合太多过拟合。配合 patience30如果 30 轮验证集指标不提升就提前停止。batch16批次大小。显存不够就降到 8 或 4但太小会影响 BatchNorm 的效果。训练过程中要盯住几个指标mAP50、mAP50-95、precision、recall。对于游戏 UI 检测mAP50达到 0.9 以上基本就够用了。如果precision高但recall低说明模型太保守漏检多反之则是误检多。根据实际需求调整置信度阈值来平衡。训练完成后用yolo detect val在验证集上跑一遍看看混淆矩阵确认没有类别混淆的问题。如果某个类别总是被误判成另一个说明这两个类别的视觉特征太接近需要增加区分度或者合并类别。3.4 模型导出从 PyTorch 到 ONNX 再到 NCNN训练出来的是 PyTorch 的 .pt 文件要部署到手机端需要经过两次转换先转 ONNX再转 NCNN。转 ONNXyolo export modelbest.pt formatonnx imgsz640 opset12 simplifyTrue这里opset12是兼容性比较好的版本simplifyTrue会做图优化去掉冗余节点。转 NCNNonnx2ncnn best.onnx best.param best.binonnx2ncnn是 NCNN 自带的转换工具编译 NCNN 时会一起生成。转换完成后会得到两个文件.param是网络结构描述.bin是权重数据。转换过程中常见的问题是算子不支持。YOLOv8 的某些算子比如 SiLU 激活函数在旧版 NCNN 里可能没有需要升级 NCNN 到较新版本或者手动替换算子。我建议直接用 NCNN 的最新 release 版本兼容性最好。转换完成后可以用 NCNN 自带的benchncnn工具在 PC 上跑一下确认模型能正常加载和推理再往手机端部署。4. 端侧推理落地NCNN 集成与 AutoJS 调用4.1 NCNN 动态库编译与 JNI 接口封装NCNN 在 Android 上的集成核心是编译出一个.so动态库然后通过 JNI 暴露推理接口给 Java 层AutoJS 再通过 Java 反射调用。编译 NCNN 的流程大致是git clone https://github.com/Tencent/ncnn.git cd ncnn mkdir build-android cd build-android cmake -DCMAKE_TOOLCHAIN_FILE$ANDROID_NDK/build/cmake/android.toolchain.cmake \ -DANDROID_ABIarm64-v8a \ -DANDROID_PLATFORMandroid-24 \ -DNCNN_VULKANOFF \ -DNCNN_BUILD_BENCHMARKOFF \ .. make -j8这里ANDROID_ABIarm64-v8a是针对现代手机的 64 位架构ANDROID_PLATFORMandroid-24对应 Android 7.0兼容性足够。NCNN_VULKANOFF是因为 Vulkan 在部分手机上兼容性不稳定纯 CPU 推理更可靠。编译完成后你会得到libncnn.so。接下来需要写一个 JNI 封装层把 NCNN 的推理流程包装成 Java 可调用的函数。核心接口大概长这样extern C JNIEXPORT jfloatArray JNICALL Java_com_example_YoloDetector_detect(JNIEnv* env, jobject thiz, jbyteArray image_data, jint width, jint height) { // 1. 把 byte array 转成 ncnn::Mat // 2. 预处理归一化、letterbox // 3. 加载模型执行推理 // 4. 后处理解码、NMS // 5. 返回检测结果数组 [x1, y1, x2, y2, score, class_id, ...] }这个封装层是整个链路里最需要仔细写的部分。预处理要和训练时保持一致后处理的 NMS 阈值要和验证时一致否则推理结果会偏差很大。4.2 AutoJS 截屏与图像预处理AutoJS 侧的截屏代码很直接if (!requestScreenCapture()) { toast(请求截屏权限失败); exit(); } var img captureScreen(); var bitmap img.getBitmap();拿到 Bitmap 之后需要转成模型需要的输入格式。YOLOv8 的输入是 640×640 的 RGB 图像归一化到 0 到 1。AutoJS 里可以用images.resize()做缩放然后读取像素数据var resized images.resize(img, 640, 640); var pixels resized.getPixels();但 AutoJS 的像素读取效率一般如果每帧都做全图缩放和像素读取可能会成为瓶颈。我的优化做法是只截取屏幕的特定区域比如广告界面通常在上半部分减少处理面积或者用 Java 层的 Bitmap 操作替代 AutoJS 的 images 模块速度会快不少。4.3 推理结果解析与坐标映射模型输出的坐标是相对于 640×640 输入图的需要映射回原始屏幕坐标。映射逻辑要考虑 letterbox 的缩放比例和填充偏移// 假设原始屏幕 1080x1920模型输入 640x640 var scale Math.min(640 / 1080, 640 / 1920); var newW 1080 * scale; var newH 1920 * scale; var padX (640 - newW) / 2; var padY (640 - newH) / 2; // 模型输出的 x1,y1,x2,y2 需要先减去 pad再除以 scale var realX1 (x1 - padX) / scale; var realY1 (y1 - padY) / scale;这一步很容易出错尤其是 pad 的计算。如果映射错了点击位置就会偏移表现为“明明检测到了按钮但点不中”。我建议在调试阶段把检测框画到截图上肉眼确认映射是否正确。4.4 推理性能实测与优化我在几台设备上实测了 YOLOv8n NCNN 的推理性能设备芯片推理耗时640输入推理耗时320输入小米 13骁龙 8 Gen 218ms8msRedmi Note 12骁龙 4 Gen 165ms28ms旧款旗舰骁龙 85535ms15ms可以看到320 输入比 640 输入快一倍以上。如果游戏 UI 的目标不是特别小把输入降到 320 或 416能显著提升帧率。代价是小目标检测精度下降需要根据实际情况权衡。另一个优化点是多线程。NCNN 支持设置线程数一般设为 2 到 4 个线程比较合适。线程太多会导致 CPU 抢占反而变慢。在 AutoJS 里推理调用是阻塞的所以截屏和推理最好放在不同的线程里避免卡住主线程。5. 广告拦截与挂机逻辑业务层的设计细节5.1 广告识别与自动关闭的状态机设计广告拦截的核心逻辑是一个状态机检测当前界面处于什么状态然后执行对应的动作。状态 A广告播放中。检测到“关闭按钮”或“跳过按钮”执行点击。状态 B广告倒计时。检测到倒计时数字等待倒计时结束。状态 C奖励领取界面。检测到“领取”按钮执行点击。状态 D游戏主界面。没有检测到广告相关元素执行预设的挂机操作。状态之间的转换靠检测结果驱动。如果连续多帧检测到“关闭按钮”就触发点击如果点击后界面没变化说明点击没生效需要重试或者调整点击坐标。这里有个细节点击坐标不要直接用检测框的中心而是加一点随机偏移。因为有些游戏会检测“完美中心点击”来判断是否脚本。随机偏移范围控制在检测框内 20% 到 30% 的区域既保证点中又增加一点自然性。5.2 滑动翻页与防检测策略挂机场景里经常需要滑动翻页比如刷视频、刷列表。AutoJS 的滑动用swipe()函数swipe(x1, y1, x2, y2, duration);滑动的关键是随机化。固定起点、固定终点、固定时长的滑动很容易被识别为脚本。我的做法是起点和终点在合理范围内随机偏移比如 ±50 像素。滑动时长在 300ms 到 800ms 之间随机。滑动轨迹加一点弧度不要是完美直线。可以用gesture()函数自定义路径。滑动间隔随机不要固定每 2 秒滑一次改成 1.5 到 3.5 秒之间随机。这些策略不能保证 100% 不被检测但能显著降低被判定为脚本的概率。5.3 多分辨率适配与鲁棒性处理不同手机的屏幕分辨率、刘海位置、导航栏高度都不同。如果脚本写死了坐标换台手机就废了。视觉方案的好处就在这里模型检测的是“按钮长什么样”而不是“按钮在哪个坐标”。只要按钮的视觉特征一致模型就能检测到坐标映射会自动适配当前分辨率。但有几个地方需要额外处理刘海屏检测区域要避开刘海否则截屏里刘海部分是黑的可能干扰检测。导航栏底部导航栏会占据屏幕空间滑动时要避开。横竖屏切换如果游戏支持横竖屏模型需要能处理两种方向或者脚本检测到方向变化时重新加载对应的模型。鲁棒性方面我建议加一个“置信度过滤”只处理置信度高于 0.6 的检测结果低于这个阈值的忽略。这样可以减少误检导致的误操作。5.4 资源占用与发热控制端侧推理虽然不依赖网络但持续运行会占用 CPU 和内存导致手机发热、耗电加快。控制资源占用的几个手段降低推理频率不需要每帧都推理可以每 200ms 推理一次中间用轻量级的颜色检测做快速筛选。动态调整输入尺寸检测到广告界面时用 640 输入保证精度挂机界面用 320 输入保证速度。及时释放资源每次推理完成后释放 Mat 和 Bitmap避免内存泄漏。温控策略如果检测到设备温度过高自动降低推理频率或者暂停脚本。实测下来一台骁龙 778G 手机连续跑 2 小时电量消耗约 25%机身温度在 40 度左右属于可接受范围。6. 常见问题与排查技巧实录6.1 模型训练阶段的典型问题问题一训练 loss 不下降。最常见的原因是学习率太大或者数据标注有问题。先检查标注文件有没有格式错误比如坐标超出 0 到 1 范围、类别 id 从 1 开始而不是 0。如果标注没问题把学习率降到 0.001 再试。问题二验证集 mAP 很高但实际推理效果差。这通常是过拟合或者数据分布不一致。检查训练集和验证集是不是来自同一批截屏如果是验证集指标会虚高。解决办法是留出一些完全没参与训练的界面截图做测试。问题三某些类别总是检测不到。可能是样本太少或者目标太小。增加该类别的样本数量或者提高训练输入尺寸。如果目标确实很小考虑在标注时把框放大一点让模型更容易学到特征。6.2 NCNN 转换与推理的坑坑一onnx2ncnn 报错“Unsupported operator”。这是 NCNN 版本太旧不支持 YOLOv8 的某些算子。升级 NCNN 到最新版或者用onnxsim先简化模型。坑二推理结果全是乱码。检查预处理是否和训练时一致。YOLOv8 训练时用的是 RGB、归一化到 0 到 1、letterbox 填充。如果推理时用了 BGR 或者没归一化结果会完全错误。坑三推理速度比预期慢很多。检查是否开启了 FP16 推理是否设置了合适的线程数。另外确认编译时开了 NEON 优化。如果用的是 debug 版本的 NCNN速度会慢很多一定要用 release 版本。6.3 AutoJS 集成中的常见故障故障一截屏返回 null。通常是截屏权限没给或者 AutoJS 版本太旧。确保在脚本开头调用了requestScreenCapture()并且用户点了允许。故障二点击位置偏移。九成是坐标映射算错了。在截图上画出检测框和点击点肉眼对比一下。另外注意 AutoJS 的坐标原点在左上角和模型输出一致不需要翻转。故障三脚本运行一段时间后卡死。大概率是内存泄漏。检查每次推理后有没有释放 Bitmap 和 MatAutoJS 的垃圾回收有时候不及时可以手动调用java.lang.System.gc()。6.4 常见问题速查表现象可能原因排查方向模型检测不到目标置信度阈值太高降低阈值到 0.3 试试检测框位置偏移坐标映射错误检查 letterbox 的 pad 计算推理速度慢输入尺寸太大降到 320 或 416点击没反应点击坐标被遮挡检查是否有悬浮窗遮挡脚本被检测操作太规律增加随机化和延迟手机发热严重推理频率太高降低频率或暂停脚本6.5 独家避坑经验最后分享几个我在实际项目中总结的经验都是文档里不会写的经验一模型不是越大越好。我一开始用 yolov8s精度确实高一点但推理耗时翻倍实际挂机时帧率跟不上反而漏检更多。换成 yolov8n 之后虽然单帧精度略降但帧率上去了整体检出率反而更高。端侧场景下速度往往比精度更重要。经验二标注质量比数据量重要。我试过用 5000 张标注粗糙的图训练效果不如 1500 张精标图。标注时多花点时间把框贴紧、类别分清楚比盲目堆数据量有效得多。经验三留一个“紧急停止”入口。脚本跑起来之后有时候会陷入死循环比如一直点某个按钮但界面没反应。一定要在脚本里加一个音量键或者悬浮窗按钮作为紧急停止否则只能强制重启手机。经验四不要追求 100% 自动化。完全无人值守的脚本一旦出问题就是大问题。我的做法是加一个“异常上报”机制如果连续 10 次操作都没有检测到预期目标就暂停脚本并发送通知让人来介入。这样既省心又不会失控。经验五定期更新模型。游戏 UI 会随着版本更新变化上个月训练的模型这个月可能就失效了。建议每次游戏大版本更新后重新截一批图微调一下模型。微调比从头训练快得多几十张图跑几十轮就能恢复可用状态。这套方案我从头到尾跑通了大概三四个游戏场景从广告拦截到日常任务挂机整体稳定性还不错。核心的难点不在模型训练而在端侧推理的工程化落地和业务逻辑的鲁棒性设计。如果你也在折腾类似的东西希望这些经验能帮你少走点弯路。
返回列表