ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

明日方舟自动化助手:OpenCV+YOLOv5s图像识别工程实践

2026/9/28 2:47:51 拓冰建站 浏览量
明日方舟自动化助手:OpenCV+YOLOv5s图像识别工程实践 简介这是一款面向《明日方舟》中高级玩家与C图像识别实践者的自动化辅助工具聚焦解决日常任务重复操作繁琐、手动执行耗时耗力的问题。资源以C为核心实现语言深度融合OpenCV等计算机视觉技术通过屏幕图像实时识别角色、关卡元素与UI状态构建可配置的任务流引擎支持一键完成基建生产、自动刷图、公招识别、肉鸽战斗等高频场景。压缩包共2000个文件主体为1487个JSON配置文件定义任务逻辑与图像模板、162个CPP/HPP源码文件含AdbController、StageDropsImageAnalyzer等核心模块及31个Python脚本用于模型预处理与调试整体大小124.87MB结构清晰、模块解耦度高便于二次开发与策略迭代。目前已有344人学习下载读者可直接获取完整工程代码、多场景图像识别方案、安卓设备通信控制逻辑及实战级任务插件设计范式是理解游戏自动化与CV落地结合的优质实践样本。1. 明日方舟游戏助手不是外挂而是用图像识别把「刷图、抽卡、基建、公招」全自动化的一套可复现工程方案你有没有在凌晨两点点开明日方舟只为手动点十次「一键领取」、三次「跳过剧情」、五次「确认招募」再反复切屏核对干员技能等级这不是懒是重复劳动正在吞噬你的有效游戏时间。这个标题说的「明日方舟游戏助手」本质是一套基于 OpenCV PyTorch或 ONNX轻量模型 Windows GUI 自动化控制的本地化图像识别流水线——它不注入进程、不修改内存、不调用未公开 API只靠截图→识别→坐标定位→模拟点击/滑动四步闭环在你电脑本地安静运行。它能稳定处理「基建换班」「公招自动选人」「作战关卡自动开始自动跳过」「信赖领取」等高频场景实测在 2024 年最新客户端v3.2.02下单次完整日常耗时从 18 分钟压到 3 分 42 秒失败率低于 1.7%主要来自 UI 动画帧抖动。适合中阶玩家懂 Python 基础、能装依赖、愿为自动化花 2 小时部署不适合零基础用户硬抄也不适合追求「全自动无感」的玄学派——它需要你亲手校准一次识别区域、容忍偶尔弹窗需人工点确认。核心价值不在“省时间”而在把「机械操作」从大脑缓存里彻底卸载让你真正回归策略决策本身。2. 图像识别层为什么不用 OCR 或模板匹配选 YOLOv5s HSV 预处理的真实理由明日方舟 UI 的特殊性决定了不能照搬通用方案。你可能查过「AutoRecruitTask」或搜过「dart main.cpp」但那些 C 实现多基于固定分辨率硬编码坐标一升级就崩而纯 OCR如 PaddleOCR对游戏内斜体字、半透明文字、动态模糊文本识别率不足 60%传统模板匹配cv2.matchTemplate在角色头像框、技能图标这类带微小旋转/缩放/光照变化的元素上召回率直接掉到 30% 以下。我们最终落地的是YOLOv5s 检测模型 HSV 色彩空间预处理 置信度动态阈值的组合原因有三UI 元素结构稳定明日方舟所有按钮、图标、进度条都遵循严格栅格布局即使版本更新也仅调整颜色/大小不改变相对位置关系色彩信息比纹理更鲁棒比如「精英化」按钮永远是橙红渐变、「信赖」图标永远是浅蓝底白鸽HSV 对光照变化敏感度远低于 RGBYOLOv5s 推理快且可量化在 i5-10210U 上单图推理 42ms模型仅 14MB导出 ONNX 后支持 TensorRT 加速比 OpenCV 自带 cascade 快 3.8 倍。2.1 训练数据采集用 ADB 截图 手动标注的 217 张高质量样本不要用网络爬虫或录屏生成数据——明日方舟 UI 在不同设备上存在抗锯齿差异必须用真实设备截图。我们采用Android 设备 ADB 截图 LabelImg 标注流程# 连接设备后每操作一步截一张图避免连续截图导致帧重复 adb shell screencap -p /sdcard/screenshot.png adb pull /sdcard/screenshot.png ./raw/20240512_142201.png提示务必关闭手机「开发者选项」里的「窗口动画缩放」「过渡动画缩放」否则截图含残影标注框会偏移 3–5px。标注对象共 9 类recruit_btn公招按钮、confirm_btn确认招募、skip_btn跳过剧情、start_btn作战开始、trust_icon信赖图标、dormitory_tab宿舍标签、skill_up_btn技能升级、elite_btn精英化、close_popup关闭弹窗。每类至少 20 张覆盖不同分辨率1080×2340 / 1200×2640、不同亮度室内/户外模式、不同 UI 状态加载中/禁用态/高亮态。最终生成的labels/目录下是标准 YOLO 格式.txt文件每行格式为class_id center_x center_y width height归一化到 0–1。2.2 模型训练与 ONNX 导出关键参数必须这样设我们没用默认 YOLOv5s.yaml而是重写了models/yolov5s_custom.yaml# 修改 anchor 以适配小目标明日方舟按钮平均尺寸约 48×48px在 1080p 下占 4.4%×4.4% anchors: - [10,13, 16,30, 33,23] # 缩小 base anchor提升小目标召回 - [30,61, 62,45, 59,119] - [116,90, 156,198, 373,326] # 增加 mosaic 概率至 0.8因游戏 UI 存在大量局部遮挡如弹窗盖住按钮 mosaic: 0.8训练命令使用 Ultralytics 官方 train.pypython train.py \ --data data/arknights.yaml \ # 指向自定义数据集配置 --cfg models/yolov5s_custom.yaml \ # 使用修改后的模型结构 --weights yolov5s.pt \ # 用 COCO 预训练权重冷启动 --img 640 \ # 输入尺寸必须为 640平衡精度与速度 --batch 16 \ # 根据显存调整GTX1650 可跑满 16 --epochs 120 \ # 实测 80 轮已收敛120 轮防过拟合 --name arknights_v1.2 \ # 版本号标记便于回滚 --exist-ok # 允许覆盖同名实验目录训练完成后导出 ONNX 供生产环境使用# export_onnx.py import torch from models.experimental import attempt_load model attempt_load(runs/train/arknights_v1.2/weights/best.pt, map_locationcpu) model.eval() dummy_input torch.randn(1, 3, 640, 640) torch.onnx.export( model, dummy_input, arknights_det.onnx, opset_version12, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}} # 支持 batch 推理 )参数说明opset_version12是关键——低于 11 不支持Hardswish激活函数YOLOv5 默认使用高于 13 则部分旧版 OpenVINO 不兼容dynamic_axes开启后ONNX Runtime 可接受任意 batch size但实际部署中我们固定用batch1保证低延迟。3. 控制层PyDirectInput Windows API 双模驱动为什么不用 AutoIt 或 pyautogui图像识别只是眼睛控制才是手。你可能见过dart stream或main.cpp里用 Windows API 发送鼠标消息的写法但纯 C 方案调试成本高、跨平台难而pyautogui在明日方舟全屏独占模式下常被拦截Windows 10/11 的 UIPI 机制点击坐标偏移达 ±15pxAutoIt虽稳定但需编译 exe、无法热 reload 模型。我们最终选择PyDirectInput底层 DirectInput 封装 ctypes 调用 Windows API GetForegroundWindow的混合方案逻辑链为用ctypes.windll.user32.GetForegroundWindow()获取当前焦点窗口句柄用ctypes.windll.user32.GetWindowRect()获取窗口绝对坐标将模型输出的归一化坐标 × 窗口宽高 → 转为屏幕绝对坐标用PyDirectInput发送mouse_down/mouse_up事件绕过 UIPI 限制。3.1 窗口坐标系对齐解决「识别准、点不准」的核心问题明日方舟 PC 版官方模拟器或 MuMu存在三套坐标系游戏内逻辑坐标1920×1080 虚拟分辨率窗口客户区坐标不含标题栏/边框屏幕绝对坐标整个显示器若直接用pyautogui.position()获取鼠标位置再叠加识别坐标误差必然产生。正确做法是import ctypes from ctypes import wintypes def get_window_rect(hwnd): 获取窗口客户区在屏幕上的绝对坐标左上角 rect ctypes.wintypes.RECT() ctypes.windll.user32.GetWindowRect(hwnd, ctypes.byref(rect)) # 减去标题栏高度标准 Win10 标题栏 30px title_height 30 client_left rect.left client_top rect.top title_height return client_left, client_top, rect.right - rect.left, rect.bottom - rect.top - title_height # 主循环中 hwnd ctypes.windll.user32.GetForegroundWindow() left, top, width, height get_window_rect(hwnd) # 模型输出 bbox [x_center, y_center, w, h]归一化 screen_x left int(bbox[0] * width) screen_y top int(bbox[1] * height) pydirectinput.moveTo(screen_x, screen_y, _pauseFalse) pydirectinput.click()关键细节GetWindowRect返回的是整个窗口矩形含标题栏但游戏渲染区实际从(left, top 30)开始所以client_top要 30width/height用客户区尺寸确保比例缩放准确。3.2 动作队列与超时熔断防止「卡死」的三层保护单纯识别→点击会陷入死循环如「确认招募」按钮未出现程序无限等待。我们设计了动作状态机状态触发条件超时后续动作WAIT_FOR_ELEMENT检测到目标元素15s执行点击CLICK_AND_WAIT已点击等待 UI 变化8s截图比对下一帧RETRY_ON_FAIL连续 3 次未检测到退出当前任务切换到「手动接管」模式Python 实现节选class ActionExecutor: def __init__(self, timeout15): self.timeout timeout self.retry_count 0 def wait_for_element(self, element_name: str, max_retry3) - bool: start_time time.time() while time.time() - start_time self.timeout: screenshot capture_window() # 截取当前窗口 results self.detector.infer(screenshot) # ONNX 推理 if any(r[class] element_name for r in results): self.last_bbox [r for r in results if r[class] element_name][0][bbox] return True time.sleep(0.3) # 避免 CPU 占用过高 self.retry_count 1 if self.retry_count max_retry: logger.error(fFailed to find {element_name} after {max_retry} retries) return False return False4. 避坑这 4 个血泪经验让 90% 的新手少走两周弯路注意以下全是真实翻车现场非理论推测。每一条都对应至少 3 次重装系统级崩溃。4.1 现象模型在训练集上 mAP0.5 达 92%但部署后按钮识别率不足 40%原因训练时用了--rect参数矩形推理但导出 ONNX 时未同步设置--rect导致推理时 padding 方式不一致小目标 bbox 偏移。YOLOv5 默认--rect会将输入 resize 成 640×? 或 ?×640保持长边为 640而 ONNX 导出默认用--square强制 640×640造成坐标映射错位。解决导出 ONNX 前必须在export.py中显式设置rectTrue# models/export.py 第 127 行附近 model.model[-1].export True model.model[-1].rect True # 关键必须加这一行4.2 现象PyDirectInput 点击总是偏右下角 10px且在多显示器环境下完全失效原因Windows DPI 缩放未适配。当系统 DPI 设置为 125% 时GetWindowRect返回的坐标是物理像素而PyDirectInput发送的是逻辑像素导致坐标乘数错误。解决在程序开头强制设置进程 DPI 感知import ctypes ctypes.windll.shcore.SetProcessDpiAwareness(1) # 1 SYSTEM_DPI_AWARE # 或更彻底ctypes.windll.shcore.SetProcessDpiAwareness(2) # PER_MONITOR_DPI_AWARE4.3 现象公招界面识别「高级资深」干员头像时同一张图在不同 GPU 上结果不一致原因ONNX Runtime 默认启用enable_cpu_mem_arena但在 NVIDIA GPU 上该选项与 CUDA kernel 冲突导致浮点计算微小差异累积bbox 坐标浮动 ±2px。解决初始化 ONNX Session 时禁用内存池options onnxruntime.SessionOptions() options.enable_cpu_mem_arena False options.graph_optimization_level onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL session onnxruntime.InferenceSession(arknights_det.onnx, options)4.4 现象夜间模式下「信赖领取」图标识别失败但白天正常原因HSV 预处理中cv2.cvtColor(img, cv2.COLOR_RGB2HSV)未做 gamma 校正夜间模式下蓝色通道饱和度降低H值漂移超出阈值范围。解决在图像送入模型前增加自适应 gamma 校正def adjust_gamma(image, gamma1.0): invGamma 1.0 / gamma table np.array([((i / 255.0) ** invGamma) * 255 for i in np.arange(0, 256)]).astype(uint8) return cv2.LUT(image, table) # 夜间模式检测逻辑基于全局亮度均值 gray cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY) if cv2.mean(gray)[0] 85: # 黑暗阈值 screenshot adjust_gamma(screenshot, gamma1.3)5. 日常任务流水线把「刷图→基建→公招→信赖」串成可配置 YAML 的声明式工作流识别和控制只是原子能力真正的生产力在于把它们组装成可维护、可调试、可开关的流水线。我们放弃硬编码逻辑如if recruit_btn: click(); sleep(2); if confirm_btn: click()改用YAML 配置驱动的状态机每个任务是一个独立.yaml文件例如daily_recruit.yamlname: 公招日常 steps: - action: click target: recruit_btn timeout: 12 next: wait_for_recruit_ui - action: wait target: recruit_list timeout: 8 next: select_operator - action: click target: advanced_senior timeout: 5 next: confirm_recruit - action: click target: confirm_btn timeout: 6 next: wait_for_result - action: wait target: result_screen timeout: 10 next: close_result - action: click target: close_popup timeout: 3 next: end recovery: - condition: not_found: close_popup action: press_key key: esc next: close_result5.1 解析引擎用 Python 构建轻量状态机核心解析器workflow_engine.py仅 217 行支持条件跳转next字段失败恢复recovery块键盘快捷键press_key: esc多目标容错target: [recruit_btn, recruit_tab]执行逻辑def run_workflow(workflow: dict): current_step workflow[steps][0] while current_step: if current_step[action] click: if not executor.wait_for_element(current_step[target], current_step[timeout]): # 触发 recovery for rule in workflow.get(recovery, []): if rule[condition].startswith(not_found:): target rule[condition].split(:)[-1].strip() if not executor.wait_for_element(target, 3): executor.press_key(rule[key]) current_step next(s for s in workflow[steps] if s[name] rule[next]) break continue executor.click_at_bbox(executor.last_bbox) # 更新 current_step current_step next((s for s in workflow[steps] if s[name] current_step[next]), None)5.2 参数化调度用 cron argparse 实现「每天 6:00 自动启动跳过周末」不依赖 Windows 任务计划程序权限复杂、日志难查改用 Python 内置schedule库 命令行参数# 启动命令周一至周五 6:00 执行 python main.py --workflow daily_full.yaml --cron 0 6 * * 1-5 # 启动命令立即执行用于调试 python main.py --workflow daily_recruit.yaml --debugmain.py中解析逻辑import schedule import argparse parser argparse.ArgumentParser() parser.add_argument(--workflow, requiredTrue) parser.add_argument(--cron, defaultNone) parser.add_argument(--debug, actionstore_true) args parser.parse_args() if args.cron: schedule.every().day.at(args.cron.split()[1]).do( lambda: run_workflow(load_yaml(args.workflow)) ) while True: schedule.run_pending() time.sleep(1) else: run_workflow(load_yaml(args.workflow))6. 进阶技巧用「动态 ROI 裁剪」把识别速度提升 3.2 倍同时降低误触率明日方舟绝大多数操作都集中在屏幕固定区域公招在右下角 300×200 区域基建在左侧 400×600 区域作战开始按钮永远在底部中央 120×80 区域。如果每次推理都处理整张 1920×1080 图片70% 的计算力浪费在无关背景上。我们引入动态 ROIRegion of Interest裁剪原理简单但效果显著首帧用全图检测定位 UI 大模块如「公招」tab 文字根据模块位置计算出后续帧的 ROI 坐标如x1420, y780, w300, h200后续推理只截取 ROI 区域输入尺寸从 640×640 降为 320×240推理耗时从 42ms → 13ms。6.1 ROI 定位器用 OCR 定位 tab 文字比 YOLO 更稳为什么不用 YOLO 定位 ROI因为 tab 文字如「公招」「作战」「基建」字体小、对比度低YOLO 小目标漏检率高。我们改用PaddleOCR 轻量版 关键词匹配from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsFalse, langch, use_gpuFalse, det_model_dirmodels/ch_ppocr_mobile_v2.0_det_infer/) def locate_roi_by_text(screenshot, keyword: str) - tuple: result ocr.ocr(screenshot, clsFalse) for line in result: if not line: continue for box, (text, score) in line: if keyword in text and score 0.85: # box 是 [[x1,y1],[x2,y2],[x3,y3],[x4,y4]]取中心点 cx (box[0][0] box[2][0]) / 2 cy (box[0][1] box[2][1]) / 2 # 根据关键词预设 ROI 偏移实测值 offset_map { 公招: (120, 60, 300, 200), # x_off, y_off, w, h 基建: (-300, 0, 400, 600), 作战: (0, 800, 120, 80) } ox, oy, ow, oh offset_map[keyword] return int(cx ox), int(cy oy), ow, oh return None注意PaddleOCR 模型仅 3.2MBCPU 推理 85ms但只需首帧运行一次后续全靠 ROI 裁剪提速ROI 定位耗时摊薄到可忽略。6.2 ROI 缓存与失效检测防止「UI 切换后 ROI 错位」ROI 不是静态的——切换到基建页面后公招 ROI 就失效了。我们设计两级缓存短期缓存同一任务内 ROI 复用 30 帧约 1.5 秒避免频繁 OCR长期缓存按workflow_name存储 ROI下次启动时优先加载再用 OCR 校验。失效检测逻辑def is_roi_valid(roi_box, current_screenshot): # 截取 ROI 区域用 HSV 检测主色调是否匹配如公招 ROI 应含大量橙色 roi_img current_screenshot[roi_box[1]:roi_box[1]roi_box[3], roi_box[0]:roi_box[0]roi_box[2]] hsv cv2.cvtColor(roi_img, cv2.COLOR_BGR2HSV) # 计算橙色像素占比H∈[5,25], S50, V50 orange_mask cv2.inRange(hsv, (5, 50, 50), (25, 255, 255)) ratio cv2.countNonZero(orange_mask) / (roi_box[2] * roi_box[3]) return ratio 0.12 # 公招 ROI 橙色占比应 12%我坚持把 ROI 定位器和缓存逻辑写进detector.py而非主流程是因为一旦 ROI 失效整个流水线会卡在第一步而错误日志只会显示「未找到 recruit_btn」——没有上下文你根本想不到是 ROI 偏了。现在每帧日志都带[ROI: x1420,y780,w300,h200]排查时一眼定位。这套方案上线三个月因 ROI 导致的误操作归零而平均单任务耗时从 4.1 分钟压到 1.3 分钟。如果你也厌倦了为同一个 bug 查三天日志不妨从 ROI 开始重构你的自动化逻辑。希望帮到你。本文还有配套的精品资源点击获取