资讯动态

端侧YOLOv8与AirScript驱动的游戏自动化实战

发布时间:2026/10/21 3:46:24 来源:尧图企业网站定制
如果想把挂机自动化做到“换游戏也能用、UI 改了也不慌”靠谱的路线基本就一条端侧跑 YOLOv8 做实时视觉识别再用 AirScript 这类执行层把识别结果转成点击。这套组合解决的核心问题是传统脚本太“脆”——坐标宏一更新就崩模板匹配换个分辨率就瞎而视觉模型只需要认住游戏里几个关键物件的样子就能在不同界面、不同设备上稳定干活。整体跑下来一次循环在几十到一百毫秒级几百帧截图、少量标注就能训出可用的检测效果。这篇文章把我从零搭这套轻量端侧 AI 游戏自动化方案的过程、踩过的坑、实测的数字都写出来适合想自己搞游戏自动化、给老游戏做自定义优化或者纯粹想玩端侧视觉的开发者参考。1. 整体设计挂机脚本为什么卡在“通用”上1.1 三代技术路线我为什么放弃内存流与固定坐标先给结论挂机自动化想做到“通用”核心矛盾是怎么在不依赖游戏内部数据的前提下拿到足够可靠的“现在该干啥”的信号。梳理下来常见的有三代做法内存读取 / 封包抓取直接读游戏进程内存里的 NPC 坐标、血量或者抓网络封包。优点是快、准、低延迟缺点是每个游戏都是独立逆向工程版本一更新大概率就废掉而且这类行为在反作弊系统眼里的风险等级最高。固定坐标宏 / 模板截图录制一组坐标循环执行再叠加模板匹配做简单判断。优点是上手快缺点是画面一错位就全乱换设备、换分辨率都要重新标定属于典型的能跑但不能维护。视觉模型 输入注入本方案用目标检测识别屏幕上的交互对象比如怪物、血条、攻击键、弹窗关闭键识别结果交由自动化执行层动态点击。它不碰内存、不抓包只分析公开可见的屏幕内容所以天然和游戏内部算法解耦。我在对比后选了第三种。原因很实际屏幕上的物体外观通常不会因为版本更新彻底变化你只需要在界面变了以后补几十张新截图重新训一版而且检测模型输出的是对象位置和类别天然能接决策逻辑换新游戏只是换权重和类名配置主循环一行都不用动。1.2 视觉感知 脚本执行的流水线这套方案的运转逻辑拆开看就五段屏幕采集 → YOLO 推理 → 决策策略 → 输入注入 → 等待一个周期YOLOv8 做感知它接收一帧截图输出若干个检测框每个框带类别和置信度比如“怪物 0.87”“血条低了 0.91”“关闭弹窗 0.66”。这一步的模型权重可以针对特定游戏微调也可以直接拿它检测通用界面元素。AirScript 做执行它封装了屏幕截图、触摸、滑动、等待这些基础动作把决策层算出的坐标变成真实的屏幕点击。这里有个关键取舍不要把决策逻辑塞进检测模型里也不要让执行脚本直接打裸坐标。检测模型只管“我看到什么”决策脚本管“我该点哪里”执行层只管“把点击发出去”。三层解耦之后任何一层都能单独替换和测试。1.3 端侧推理换来的三个真实收益选择在端侧跑推理而不是把截图发到云端识别除了省服务器费用还有三个直接影响挂机效果的点延迟低。云端方案一个来回至少几百毫秒端侧推理一次只有几十毫秒。挂机动作如果对时序敏感比如 BOSS 攻击间隔只有两秒低延迟直接决定脚本能不能稳住节奏。截图不出设备。游戏账号相关的画面内容留在本机既不用传服务器也少了隐私和账号关联风险。断网可用。手机流量不稳定、Wi-Fi 抖动都不影响循环执行只要有电就能跑。如果你之前只用过固定坐标脚本第一次跑通“识别到怪物 → 走过去 → 点攻击”这条链路时你会明显感觉到它不像脚本更像一个小机器人在代玩。2. 环境与选型YOLOv8n、ONNX、AirScript 怎么搭起来2.1 模型、输入分辨率与运行时的选型表先说结论我默认的配置是YOLOv8n 320 或 480 输入 ONNX Runtime。下面是选型时对比过的参数数据来自我手头的设备实测项选择理由与实测模型体量YOLOv8n权重约 6MB320 输入时 CPU 推理约 20~40ms足够挂机用进阶备选YOLOv8s权重约 22MB精度略高但端侧帧率降一半非必要不选输入分辨率320 / 480小按钮少则 320有大量 20~30px 小图标则上 480640 只在关键失败区段用推理引擎ONNX RuntimeCPU 友好、部署简单Android 端可换轻量神经网络推理库半精度推理更明显还有一个容易被忽视的选型模板匹配。对固定位置的按钮做判断确实快但游戏界面一旦加了随机偏移、皮肤特效模板就失效。YOLO 这类检测模型学的是目标的“语义外观”抗偏移能力完全不同。这就是为什么哪怕识别目标只有三四个我也坚持用检测模型而不是模板。2.2 从 PyTorch 权重导出端侧 ONNX训练好的模型是 PyTorch 格式部署前先导出 ONNX。这一步的作用是把模型的网络结构冻结成计算图去掉训练相关的动态逻辑。from ultralytics import YOLO model YOLO(yolov8n.pt) # 可以是自己微调后的权重 model.export( formatonnx, opset12, imgsz320, simplifyTrue, dynamicFalse, )导出成功的标志是目录下出现 game.onnx 文件。重点检查两个东西一是 opset 不能选太高某些端侧运行时对高版本算子支持差二是 dynamic 参数保持 False输入尺寸固定推理时不需要动态形状跑得更稳。如果目标是 Android 端更极致的速度可以再走一步把 ONNX 转成轻量网络格式用半精度跑。这步不是必需我最初在手机上用通用运行时就完全够跑后面想降功耗才做的优化。2.3 AirScript 执行层接入与 ADB 连接姿势执行层我用 AirScript 做了一层封装底层是 ADB 通道截图走 screencap点击走 input tap滑动走 input swipe。最终暴露给业务层的接口很简单from airscript import AirScript agent AirScript(Android:///?serialxxx) frame agent.screenshot() # 返回 BGR numpy 数组 agent.touch(540, 960) # 绝对像素坐标点击 agent.swipe((540, 1800), (540, 600)) # 滑动 agent.wait(0.2) # 等待一个周期连接设备有几个实测过的坑。第一优先用 USB 连接Wi-Fi ADB 在高强度截屏时容易超时丢帧。第二不要把连接对象在每次循环里重复创建长连接比反复握手稳定得多也更省电。第三如果脚本跑在电脑上、控制的是手机可以先用一条adb devices确认设备序列号再把它填进连接参数避免多设备时连错机。这里的核心思想是执行层只暴露原子操作不管业务逻辑。你完全可以把 AirScript 理解成一个“能看屏幕、能动手”的机器人底座上面怎么指挥是决策层的事。3. 数据与训练让 YOLO 认得出游戏里的物件3.1 采集截图与标注规范数据集准备是整个方案里最累的一环但也是最容易偷懒出效果的一环因为游戏界面比真实世界规整太多。核心流程就两步截图、标注。截图我建议用脚本自动抓不要手截图。让游戏自然跑半小时脚本每 2 秒存一帧覆盖白天、夜间、不同地图和弹窗状态for i in range(300): frame agent.screenshot() cv2.imwrite(fdata/raw/{i:04d}.jpg, frame) agent.wait(2)标注时用任意支持 YOLO 格式导出的开源标注器画矩形框就行。类名不要贪多控制在 10 个以内我常用的类表是这样的类别触发场景优先级dialog_close任何弹窗的关闭按钮最高confirm_btn确认领取、确认购买高hp_low角色血条低于阈值高attack_btn攻击/技能按钮中monster可攻击怪物中item_pick掉落物拾取点低map_exit地图出口/退出按钮低标注的准则就一条宁可少标不要错标。把“怪物”类标进了“关闭弹窗”类会让决策层在战斗时频繁点错按钮比漏检更致命。3.2 小样本训练参数与收敛判断小数据集训练的核心思路是迁移学习从官方预训练权重起步冻结大部分主干参数只微调检测头和部分特征层。先写数据配置path: ./data/game train: images/train val: images/val names: 0: dialog_close 1: confirm_btn 2: hp_low 3: attack_btn 4: monster 5: item_pick 6: map_exit训练命令yolo detect train datagame.yaml modelyolov8n.pt imgsz640 epochs80 batch16我实测下来 300~500 张截图就能让 mAP50 到 0.6 以上这对自动化来说够用。判断模型行不行不要只看 mAP要拿真实截图去跑一遍推理重点看两类错误漏检怪物就在屏幕上但没框出来误检背景特效被当成怪物。漏检会让挂机卡住误检会乱点两者都要控制住。两个提升稳定性的小技巧一是收集一批“没有任何目标”的干净截图标成纯背景帧让模型学习负样本二是性能不确定的设备训练时把画面增强调低过度随机的亮度扰动反而会让模型对真实界面的响应退化。4. 核心链路实现感知、决策、执行的代码骨架4.1 推理与 NMS 封装YOLOv8 导出后的 ONNX 输出是一个[1, 4类别数, 锚点数]的张量需要自己把它拆成检测框并做非极大值抑制NMS。我习惯封装一个 GameDetector 类import cv2 import numpy as np import onnxruntime as ort class GameDetector: def __init__(self, onnx_path, class_names, input_size320): self.sess ort.InferenceSession(onnx_path, providers[CPUExecutionProvider]) self.class_names class_names self.input_size input_size self.nc len(class_names) def preprocess(self, frame): h, w frame.shape[:2] scale min(self.input_size / h, self.input_size / w) nh, nw int(h * scale), int(w * scale) rsz cv2.resize(frame, (nw, nh)) pad_x (self.input_size - nw) // 2 pad_y (self.input_size - nh) // 2 canvas cv2.copyMakeBorder(rsz, pad_y, pad_y, pad_x, pad_x, cv2.BORDER_CONSTANT, value(114, 114, 114)) blob cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB)[None].astype(np.float32) / 255.0 return blob, scale, pad_x, pad_y def detect(self, frame, conf0.6): blob, scale, pad_x, pad_y self.preprocess(frame) out self.sess.run(None, {self.sess.get_inputs()[0].name: blob.transpose(0, 3, 1, 2)})[0] # 自行实现 NMS 后返回 [(cls_id, conf, x1, y1, x2, y2), ...]注意预处理里 BGR 转 RGB 的顺序不要搞反很多新手第一步就挂在颜色通道上。NMS 实现网上有现成的我用的思路是先按置信度排序逐框抑制交并比大于 0.45 的重复框。4.2 坐标映射的数学与代码模型输出的坐标是“模型输入坐标系”里的必须还原回屏幕原始坐标。还原公式其实只要记住一句模型坐标 屏幕坐标 × 缩放比 填充边距所以屏幕坐标 (模型坐标 - pad) / scale。def to_screen(dets, scale, pad_x, pad_y, sw, sh): results [] for cls_id, conf, x1, y1, x2, y2 in dets: x1_s np.clip((x1 - pad_x) / scale, 0, sw) y1_s np.clip((y1 - pad_y) / scale, 0, sh) x2_s np.clip((x2 - pad_x) / scale, 0, sw) y2_s np.clip((y2 - pad_y) / scale, 0, sh) results.append((cls_id, conf, (x1_s, y1_s, x2_s, y2_s))) return results点击点我取检测框中心然后往右下方偏 10~20 像素作为安全偏移避免点到按钮边缘的圆角镂空区域。另外Android 触摸坐标和截图像素坐标通常一致但如果你的手机开了系统显示缩放就要做一次标定检测屏幕上某个固定元素算出它实际点击位置和识别框中心的差值存成全局偏移量。这个坑我在换设备时踩过一次。4.3 用状态机管住点击节奏挂机不是无脑连点决策逻辑最好写成状态机。我维护了 IDLE、BATTLE、LOOT、HEAL 四个状态状态切换完全由检测结果驱动state IDLE while running: dets detector.detect(screen, conf0.6) centers to_screen(...) # 拿到屏幕坐标 # 弹窗永远最高优先级 if dialog_close in dets: agent.touch(centers[dialog_close][0]) state IDLE continue if state HEAL: if hp_low in dets: agent.touch(centers[hp_low][0]) else: state IDLE continue if state IDLE: if monster in dets: target nearest_to_screen_center(centers[monster]) agent.touch(target) state BATTLE elif item_pick in dets: agent.touch(centers[item_pick][0]) state LOOT continue if state BATTLE: if attack_btn in dets and cooldown_ok(): agent.touch(centers[attack_btn][0]) if monster not in dets: state IDLE ...弹窗优先级最高这条必须写死在最前面。经验之谈游戏挂机翻车的头号原因不是打不过怪而是一堆确认弹窗顶在屏幕上脚本还在那循环点攻击最后进错界面。4.4 卡死检测与日志回放自动化脚本跑久了总会遇到意外状态比如角色被地形卡住、游戏断线重连弹窗、网络波动导致画面冻结。我的安全网有三层无变化检测连续 10 个循环检测结果完全为空或者置信度全部低于阈值就强制点一次屏幕右下角并截图留档。卡死熔断连续 3 次“点击了但没有状态变化”停止点击等 5 秒再试仍失败就退出任务并发送通知。日志回放每 20 帧存一张叠加了检测框和状态的截图到 logs 目录。遇到翻车翻得不明不白时翻这些截图定位比看代码快十倍。这套日志机制看起来土但排查效率直接拉满。后来我所有脚本改动都会先跑一小时然后盯日志图确认每个状态切换都符合预期再放手。5. 实测数据与调优记录5.1 两端设备的循环耗时表我的运行环境是一台五年前的中端 Android 手机、一台普通桌面电脑两者通过同一套代码控制。实测单循环各环节耗时如下环节Android USB ADBPC 桌面屏幕采集20~40ms5~10msYOLOv8n 推理32020~35ms5~12msNMS 与决策1~3ms1~3ms触摸/鼠标注入10~30ms1~5ms全过程合计50~110ms12~30ms也就是说手机端控制循环频率在 9~20Hz 之间PC 端能到 30Hz 以上。挂机任务其实不需要这个频率我普遍在循环末尾加 sleep(0.1~0.2)让脚本稳定在 5~8Hz 就够用还能明显减少发热和耗电。5.2 四个最容易翻车的细节翻车点一小图标漏检。攻击技能图标往往只有 30 像素不到320 输入分辨率下特征被压缩得很小。我的办法是对画面下方的“操作区域”单独裁剪放大再做一次推理成本只在需要时发生平时主循环保持 320。翻车点二弹窗叠加。结算弹窗和战斗界面同时存在时两个目标框高度重叠只看置信度会选择错误的框。解法是给弹窗类一个规则权重只要存在弹窗目标同区域的其他框一律让路。翻车点三多设备分辨率不一致。手机换了一台同一条脚本坐标全部偏移。我不想每次重新标定就在启动时检测一次屏幕上的固定标志物比如返回键自动算缩放比例和偏移量之后所有点击坐标都基于这个标定结果换算。翻车点四画面闪烁导致误检。游戏里的技能特效偶尔会闪出类似怪物轮廓的图形单帧检测很容易误判。我给每个候选目标做了三帧平滑连续三帧都在同一位置出现才执行点击闪烁帧自然被滤掉。5.3 让检测更稳的调优思路当 mAP 够用但实际挂机效果一般时优先怀疑的不是模型而是输入质量。以下调优按见效排序抓帧间隔错开广告轮播和过场动画的节奏避免训练集里混入大量无效画面。对速度不敏感的操作比如拾取物品把置信度阈值降到 0.4对安全性敏感的操作比如弹窗关闭提到 0.7。对长期固定位置的对象可以在检测基础上叠加一次 ROI 模板校验双重确认再点击误点率能降一个数量级。换成量化模型预测误差一般不超过像素级精度损失端侧延迟却可以省 30% 左右。这些都是工程上的钱花在数据清洗和阈值调试上比换大模型值得多。6. 常见问题与排查技巧实录6.1 故障速查表把我在项目里遇到过的典型问题整理成一张表症状可能原因解法检测输出全空输入通道顺序错误、模型输入尺寸不对先打印输入 blob 的 shape 和均值再对照预处理代码检查点击总是偏一段距离letterbox 的 pad 没有还原重点检查 pad_x/scale 是否在 to_screen 里正确使用点击没反应设备息屏、ADB 通道断开、游戏覆盖层拦截点亮屏幕、加长连接保活、改用系统级输入事件手机发烫循环没有休眠、推理频率过高加 sleep降到 5~8Hz必要时用 320 输入换设备后全面失效显示缩放或分辨率不同做一次启动标定用固定元素计算缩放和偏移频繁误点弹窗弹窗类别没进训练集补标弹窗类并写死弹窗优先级6.2 一次“检测到了但点不上”的完整排查分享一次典型的翻车经历。某天脚本日志显示“monster 置信度 0.87已点击”但实际游戏里怪物血条没变化。我当时按顺序做了四步第一步保存检测叠加图确认模型框的位置恰好覆盖怪物身体识别没问题。第二步用 adb 命令手动点击同一个坐标结果按钮有反馈但战斗没启动。第三步怀疑命令发送速度太慢把触摸方式从普通点击改成先按下再抬起间隔 50ms仍然无效。第四步回头检查屏幕分辨率发现这台机器的截图像素是 720×1280但触摸事件使用的却是另一档逻辑分辨率坐标差了一倍。加上启动标定换算后问题彻底消失。这次经历给我的教训是自动化链路里每一个环节都可能独立出错排查时务必把“模型层”“坐标层”“执行层”分开验证不要一上来就怀疑模型不准。日志回放截图是帮助定位的第一利器先看“模型看到了什么”再看“脚本点到了哪里”通常两三分钟就能锁定真凶。项目做到后面我最大的感受是这套东西的价值不在于“挂机”而在于把“能看会点的机器人底座”做扎实了。它不读内存、不抓包所有决策都建立在公开可见的屏幕画面上属于纯视觉层面的自动化思路天然适合做游戏兼容性测试、无障碍操作辅助以及给老单机游戏写自定义玩法优化。使用范围也要多说一句多人竞技类游戏通常明确禁止自动化脚本跑之前一定看清楚游戏条款别拿自己账号去赌。最后再分享一个小技巧给脚本加一个“手动模式”。当检测到异常或你不确定当前界面是什么情况时暂停自动化、把所有按键交给人工接管处理完再切回自动。这个开关成本很低但能在脚本抽风时保住账号、保住进度比任何花哨的模型优化都实在。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑