@windsland52/maafw-live 0.0.0-stage → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +293 -2
- package/bin/maafw-live.mjs +25 -0
- package/docs/daemon-protocol.md +121 -0
- package/docs/keyframe-retention-contract-v0.md +225 -0
- package/docs/session-unify-v0.md +115 -0
- package/lib/client/daemon.js +276 -0
- package/lib/commands/annotate.js +179 -0
- package/lib/commands/calibrate.js +61 -0
- package/lib/commands/color.js +73 -0
- package/lib/commands/crop.js +228 -0
- package/lib/commands/dispatch.js +121 -0
- package/lib/commands/env.js +409 -0
- package/lib/commands/index.js +70 -0
- package/lib/commands/input.js +257 -0
- package/lib/commands/kf.js +167 -0
- package/lib/commands/probe.js +70 -0
- package/lib/commands/reco.js +440 -0
- package/lib/commands/repl.js +1501 -0
- package/lib/commands/roi-check.js +273 -0
- package/lib/commands/roi-compare.js +388 -0
- package/lib/commands/runtime.js +718 -0
- package/lib/commands/skill.js +169 -0
- package/lib/commands/timing.js +247 -0
- package/lib/commands/version.js +66 -0
- package/lib/daemon/framed.mjs +3680 -0
- package/lib/daemon/reco_child.mjs +287 -0
- package/lib/exec.js +96 -0
- package/lib/flags.js +44 -0
- package/lib/image.js +90 -0
- package/lib/index.js +143 -0
- package/lib/interface/hotkey.js +142 -0
- package/lib/interface/index.js +12 -0
- package/lib/interface/jsonc.js +95 -0
- package/lib/interface/load.js +397 -0
- package/lib/interface/override.js +287 -0
- package/lib/interface/plan.js +279 -0
- package/lib/option-args.js +29 -0
- package/lib/pkg.js +54 -0
- package/lib/protocol.js +94 -0
- package/lib/runtime/actions.js +225 -0
- package/lib/runtime/keyframes.js +73 -0
- package/lib/runtime/log-watch.js +160 -0
- package/lib/runtime/node-source.js +100 -0
- package/lib/runtime/ocr-model.js +45 -0
- package/lib/runtime/pi-env.js +34 -0
- package/lib/runtime/pretask.js +217 -0
- package/lib/runtime/prune.js +28 -0
- package/lib/runtime/receipts.js +57 -0
- package/lib/runtime/reco-args.js +38 -0
- package/lib/runtime/reco-verdict.js +82 -0
- package/lib/runtime/roi-compare.js +198 -0
- package/lib/runtime/session-forward.js +79 -0
- package/lib/runtime/session-ipc.js +471 -0
- package/lib/runtime/session.js +135 -0
- package/lib/runtime/signal-guard.js +35 -0
- package/lib/types/client/daemon.d.ts +86 -0
- package/lib/types/commands/annotate.d.ts +3 -0
- package/lib/types/commands/calibrate.d.ts +7 -0
- package/lib/types/commands/color.d.ts +9 -0
- package/lib/types/commands/crop.d.ts +3 -0
- package/lib/types/commands/dispatch.d.ts +22 -0
- package/lib/types/commands/env.d.ts +21 -0
- package/lib/types/commands/index.d.ts +13 -0
- package/lib/types/commands/input.d.ts +17 -0
- package/lib/types/commands/kf.d.ts +10 -0
- package/lib/types/commands/probe.d.ts +15 -0
- package/lib/types/commands/reco.d.ts +27 -0
- package/lib/types/commands/repl.d.ts +10 -0
- package/lib/types/commands/roi-check.d.ts +3 -0
- package/lib/types/commands/roi-compare.d.ts +3 -0
- package/lib/types/commands/runtime.d.ts +66 -0
- package/lib/types/commands/skill.d.ts +17 -0
- package/lib/types/commands/timing.d.ts +64 -0
- package/lib/types/commands/version.d.ts +3 -0
- package/lib/types/exec.d.ts +31 -0
- package/lib/types/flags.d.ts +62 -0
- package/lib/types/image.d.ts +27 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/interface/hotkey.d.ts +36 -0
- package/lib/types/interface/index.d.ts +11 -0
- package/lib/types/interface/jsonc.d.ts +9 -0
- package/lib/types/interface/load.d.ts +190 -0
- package/lib/types/interface/override.d.ts +78 -0
- package/lib/types/interface/plan.d.ts +68 -0
- package/lib/types/option-args.d.ts +19 -0
- package/lib/types/pkg.d.ts +14 -0
- package/lib/types/protocol.d.ts +124 -0
- package/lib/types/runtime/actions.d.ts +248 -0
- package/lib/types/runtime/keyframes.d.ts +43 -0
- package/lib/types/runtime/log-watch.d.ts +28 -0
- package/lib/types/runtime/node-source.d.ts +25 -0
- package/lib/types/runtime/ocr-model.d.ts +8 -0
- package/lib/types/runtime/pi-env.d.ts +24 -0
- package/lib/types/runtime/pretask.d.ts +53 -0
- package/lib/types/runtime/prune.d.ts +4 -0
- package/lib/types/runtime/receipts.d.ts +7 -0
- package/lib/types/runtime/reco-args.d.ts +18 -0
- package/lib/types/runtime/reco-verdict.d.ts +50 -0
- package/lib/types/runtime/roi-compare.d.ts +117 -0
- package/lib/types/runtime/session-forward.d.ts +10 -0
- package/lib/types/runtime/session-ipc.d.ts +117 -0
- package/lib/types/runtime/session.d.ts +77 -0
- package/lib/types/runtime/signal-guard.d.ts +19 -0
- package/lib/types/update-check.d.ts +20 -0
- package/lib/types/usage.d.ts +3 -0
- package/lib/update-check.js +97 -0
- package/lib/usage.js +50 -0
- package/package.json +94 -4
- package/skills/README.md +51 -0
- package/skills/maafw-live/SKILL.md +293 -0
- package/skills/maafw-live/references/examples.md +238 -0
- package/skills/maafw-live/references/pitfalls.md +211 -0
- package/skills/maafw-live/references/workflows.md +164 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: maafw-live
|
|
3
|
+
description: 用 maafw-live 做 MaaFramework 应用的实时观测、识别实测、模板资产制作与状态证据留存——常驻 daemon 帧流、变化/稳定事件、历史帧按 seq/ROI 取用、reco 识别单测与阈值扫描、crop 模板裁剪(含从关键帧库留存帧离线裁剪)、kf 升格与离线解析、color 探色、annotate 候选区域编号、calibrate 噪声地板、timing 时长反推。Use when working on a MaaFW application with a device attached for deterministic observation, recognition measurement, template assets, or retention of a state that cannot be reproduced (including cropping templates offline from a keyframe library frame), or when running pipeline tasks and needing per-node evidence.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# maafw-live 使用动线
|
|
7
|
+
|
|
8
|
+
定位:**活设备上的动词与观测证据**——连接、截图、输入、运行任务、识别实测、留存关键帧。
|
|
9
|
+
不判断业务上完成了哪个状态;事后日志/诊断证据归 [MaaEvidenceKit](https://github.com/Windsland52/MaaEvidenceKit)(`maa-evidence`);
|
|
10
|
+
字段与协议语义归 MaaLLMWiki 的版本锁定原文。
|
|
11
|
+
|
|
12
|
+
本事只写**动线、纪律、判据与反模式**:命令面与返回字段查
|
|
13
|
+
[daemon-protocol.md](https://github.com/Windsland52/maafw-live/blob/main/docs/daemon-protocol.md)、
|
|
14
|
+
留存与引用语义查[留存契约](https://github.com/Windsland52/maafw-live/blob/main/docs/keyframe-retention-contract-v0.md)、
|
|
15
|
+
CLI 命令与退出码查 [README](https://github.com/Windsland52/maafw-live/blob/main/README.md)
|
|
16
|
+
(npm 包内这三份就在 `skills/` 旁边的 `docs/` 与 `README.md`)。
|
|
17
|
+
三处是唯一出处,本 skill 不复述——复述出来的第二份迟早对不上。
|
|
18
|
+
|
|
19
|
+
## 硬护栏
|
|
20
|
+
|
|
21
|
+
1. **单会话单控制器**(红线)。一次性命令每次都 spawn 自己的 daemon,连接是进程态;它与你已开的面板/REPL
|
|
22
|
+
并存 = 同一台设备上两个控制器互扰。交互探索用 `repl`(连一次、命令复用),验证用一次性命令,**不并发**。
|
|
23
|
+
2. **走框架链路**。识别输入与点击都必须来自框架截图链路(本工具 / 框架 API),不用 adb、外部截图工具或手工
|
|
24
|
+
截屏——分辨率与像素空间一旦不同源,模板与 roi 全部作废。
|
|
25
|
+
3. **声明图像空间**。`roi`、模板图、`click` 坐标都在**控制器分辨率空间**(按项目 `display_short_side` 缩放,
|
|
26
|
+
缺省短边 720)。历史帧按**该帧捕获时尺寸**换算,不按当前全局尺寸。源帧空间 ≠ 当前控制器空间时必须当回事:
|
|
27
|
+
工具会警告(退出 3),不要拿这种模板去写 pipeline。
|
|
28
|
+
4. **判定分级**。调用成功(命令跑完)/ 任务成功(`data.record.ok`,**不是**退出码、也不是信封外层 `ok`)/
|
|
29
|
+
业务验收(行为符合预期)。三层互不顶替;一次跑通 ≠ 稳定,验收要重复并含冷启动。
|
|
30
|
+
读 `record.nodes[]` 认 `nameSource`:`node_details` 是框架给的权威节点名,可以据此判断走了哪条分支;
|
|
31
|
+
`entry` 是顶层兜底(框架在节点完成前只报入口名),**未确认**——这种条目要用屏幕或留存帧复核。
|
|
32
|
+
5. **看到即升格**。L0 原图是进程态有界缓存,FIFO 淘汰,**淘汰即不可升格**。不可复现状态(一次性弹窗、
|
|
33
|
+
当日签到、活动入口)必须在看到的那一刻 `kf promote`,攒到最后只会剩下没有证据。
|
|
34
|
+
6. **实测优先、一次一个变量**。参数、ROI、阈值、模板都实测(`reco` / `crop` / `color` / `timing`);
|
|
35
|
+
识别未命中**不是**调低阈值的理由——得分 < 0.5 说明模板与画面不匹配,该换模板/换识别方式。
|
|
36
|
+
7. **像素默认不出 daemon**。先拿元数据(`seq` / `hash` / `diff` / `w/h`),需要画面时再按 `seq` / `roi`
|
|
37
|
+
精确取。这是为了让模型上下文不被像素挤爆——不要把整帧读进上下文当习惯。
|
|
38
|
+
8. **边界外的事交出去**。静态知识/校验归 MaaLLMWiki 技能;pipeline 改写归 maafw-pipeline;
|
|
39
|
+
修复性诊断归 maafw-debug;事后日志证据归 maa-evidence。
|
|
40
|
+
9. **同帧纪律**。测得的坐标与要执行的动作必须共享帧身份:界面元素会漂移(实测同一棋盘在相邻帧之间
|
|
41
|
+
整体位移 18px),拿旧帧的坐标去点,表现与"目标不认这个手势"**一模一样**。规矩是
|
|
42
|
+
**先抓帧 → 同帧量 → 立刻动 → 隔几秒复核**。工具会为取帧回执标注来源与有效分辨率
|
|
43
|
+
(`source` / `sourceResolution`),别把降级小图上的坐标当原图的用。
|
|
44
|
+
10. **动作后必须复核,别拿旧截图当现场**。`screencap` 不是"等画面稳定",它只是"再抓一帧"——点完立刻抓
|
|
45
|
+
常抓到过渡帧。要读**动作回执自带的 `retention.before/after`**,或用 `wait stable` 等条件成立再下结论。
|
|
46
|
+
把"我想做什么"与"实际发生了什么"**分开记录**。
|
|
47
|
+
11. **"没反应"不等于"不支持"**。目标系统对非法输入的默认反应常常是什么都不发生(静默忽略)。
|
|
48
|
+
先排除两条:坐标是否过期、前置状态是否满足(实测:已有连线时新笔画被忽略,我据此误判了规则)。
|
|
49
|
+
**对照实验前必须把被测系统恢复到已声明的初态**,否则假阴性会被当成规则写下来。
|
|
50
|
+
12. **候选表要读可用性,不能只读条数**。`annotate` 的 `availability`(截断 / 剔除 / 分带 / 检测面与回画
|
|
51
|
+
分辨率)是"看起来有结果 ≠ 有结果"的护栏:30 条候选、编号整齐、格式正确,也可能**恰好漏掉唯一
|
|
52
|
+
重要的那个目标**(实测该目标的候选覆盖率为 0)。目标明确时用 `--roi` 收窄搜索面——
|
|
53
|
+
这是截断的正解,而不是把上限调大。
|
|
54
|
+
|
|
55
|
+
## 0. 首次上手:一条最短路径
|
|
56
|
+
|
|
57
|
+
动线 A–D 是**按能力**分的(看见 / 实测 / 裁 / 留档),第一次上手一台设备或一个项目时按下面这条
|
|
58
|
+
**按顺序**走,每一步都带一条"过不了就别往下走"的判据。判据不成立就先修那一步——带着不确定往下走,
|
|
59
|
+
后面得到的结论无法归因(是模板不行、空间不对,还是根本没连对?)。
|
|
60
|
+
|
|
61
|
+
| # | 这一步做什么 | 命令 | 过这一步的判据 |
|
|
62
|
+
| :- | :--- | :--- | :--- |
|
|
63
|
+
| 1 | 认清环境与目标 | `version` / `env` / `probe` / `device --kind adb` | 本次要用到的链路不在 `env` 的 warnings 里(缺依赖是**探测结果**,不是命令失败);`device` 列得出目标 |
|
|
64
|
+
| 2 | 连**一次** | `repl --project <dir>`(手连才 `--kind adb --target`) | 回执里控制器类型与识别空间尺寸符合预期;此刻起这台设备只有这一条链路(硬护栏 1) |
|
|
65
|
+
| 3 | 先看见(**并先把尺子造出来**) | `stream start` → `frame get --roi --src full` / `annotate` / `wait stable` | 拿到的是**本会话**的帧(`seq` 在动),不是"上次那一帧";不认识画面就先要候选编号,别猜坐标;**动手前先有一个可复验的"屏幕像素 ↔ 控制器坐标"换算**(见下) |
|
|
66
|
+
| 4 | 实测再下结论 | `reco --seq` / `--image` / `--act`、`color`、`calibrate` | 识别只认 `hit`(miss 也有 detail);阈值起点 = 实测最高分 − 0.1;静止画面先 `calibrate` 定噪声地板 |
|
|
67
|
+
| 5 | 裁资产 | `crop --roi` / `--point`(库帧走 `--from-kf`) | **位置正确**(落回裁剪处)优先于得分;框贴合与否用"再裁一遍比 `box`"验;源帧空间 ≠ 当前空间就别用 |
|
|
68
|
+
| 6 | 看到就留档 | `kf promote latest --note "…"` → `kf list` / `kf resolve` | 不可复现的状态**当场**升格(热缓存淘汰就没了);出处侧车随产物一起提交 |
|
|
69
|
+
|
|
70
|
+
**这条路径的意义在第 6 步之后**:3→4→5 都能重做,唯独"当时那一帧"只有升格过才重做得了——
|
|
71
|
+
所以一次完整的闭环是「看见(3)→ 留档(6)→ 事后离线裁(5 的 `--from-kf`)→ 写进项目资源 → `run` 实测」。
|
|
72
|
+
时间紧的时候最容易省掉的是第 6 步,而它是唯一无法补救的一步。
|
|
73
|
+
|
|
74
|
+
每一步的能力细节、参数选择与反模式:第 3 步见动线 A,第 4 步见动线 B,第 5 步见动线 C,
|
|
75
|
+
第 6 步与离线裁剪见动线 D。
|
|
76
|
+
|
|
77
|
+
**第 3 步之前先造尺子(动手前的强制前置)。** 从零案例里最贵的教训:我 33 次读整帧**目测**坐标、
|
|
78
|
+
手算像素→控制器换算,比例还搞错了(按 1/3,实际 0.375),于是连续在 5 个错误坐标上盲试。
|
|
79
|
+
正确顺序是**先建立"屏幕像素 ↔ 控制器坐标"的可靠换算,再动手**:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
maa> frame get --roi "400,20,200,60" --src full --out probe.png # 验证 ROI 能 1:1 取回、sourceResolution=1
|
|
83
|
+
maa> annotate --roi "…" --out som.png # 用编号候选反查坐标,而不是目测
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`frame get` 回执的 `sourceResolution` 就是尺子的刻度:**它不等于 1 时,图上量出来的坐标就不是
|
|
87
|
+
控制器坐标**。降级成小图时工具会警告(退出 3)——那不是噪音,是"你正在用一把缩过的尺子"。
|
|
88
|
+
|
|
89
|
+
### 第 1–2 步的细节
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
maafw-live version && maafw-live env && maafw-live probe # 版本 / 环境 / 运行时与设备发现数
|
|
93
|
+
maafw-live device --kind adb # 目标在不在
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`env` 自身**永远不因环境残缺而失败**:缺依赖是探测结果,不是错误——读它的 warnings,别把它当门禁用。
|
|
97
|
+
|
|
98
|
+
连接优先 `--project <dir>`:控制器类型、窗口正则、截图/输入方法、识别缩放全跟项目声明走。**不猜设备**:
|
|
99
|
+
猜错不只是连错窗口,还会把 roi 与模板的坐标系一起猜错。手动连法(`--kind adb --target 127.0.0.1:16384`)
|
|
100
|
+
只在项目不可用或调试连接本身时用。
|
|
101
|
+
|
|
102
|
+
> **项目从哪来不影响本工具**:它只读 `interface.json` 里的控制器条目与 `resource[].path`——`resource/`
|
|
103
|
+
> 与 `resource/base/` 这类布局差异都实测能连、能跑(资源根是项目自己的约定,不是本工具的要求)。
|
|
104
|
+
> 识别空间由控制器条目(如 `display_short_side`)决定,与目录布局无关;**唯一要守的是别把两套坐标系混起来**。
|
|
105
|
+
> 自己新建项目时,跟随该项目生态的既定布局即可,别自创一套——偏离布局后 roi、模板与文档里的路径都要跟着改。
|
|
106
|
+
|
|
107
|
+
> 注意:`--short-side` / `--long-side` / `--raw` 只在**手动**连法下生效;它们改变识别空间,
|
|
108
|
+
> 意味着项目里所有 roi 与模板都要按新尺寸重算。
|
|
109
|
+
|
|
110
|
+
## 1. 动线 A:先看见(实时观测)
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
maafw-live repl --project ./my-maa-project # 多步操作用它;也能被管道驱动
|
|
114
|
+
maa> stream start --fps 10 # 帧流常驻:元数据 + 变化/稳定事件 + 环形缓冲
|
|
115
|
+
maa> stream start --fps 10 --l0-roll 64 # 回看窗口不够就调它(缺省 16 帧 ≈ 1.6 秒 @10fps)
|
|
116
|
+
maa> frame get --roi "640,300,220,80" --src full --out roi.png # 控制器分辨率 1:1;带引号防 shell 拆参数
|
|
117
|
+
maa> wait stable --timeout 5000 --quiet 600 # 等画面静下来再读,别硬睡
|
|
118
|
+
maa> wait change --roi "…" --threshold 3 # 等某块区域动起来
|
|
119
|
+
maa> frame diff --a 120 --b 135 --rois "x,y,w,h;x,y,w,h" # 这一步到底改了哪几块/哪几格
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- **事件告诉你"何时变",不保证"都采到"**:低频阶段的一次性弹窗仍可能漏采。漏采的状态就是没有证据,
|
|
123
|
+
按未知处理,不要用"现在的画面"冒充"当时的画面"。
|
|
124
|
+
- 变化检测阈值先实测:静止画面 `calibrate` 定噪声地板,再按它给的 `blockThresh` / `changeGlobal` 起流。
|
|
125
|
+
- **读帧先看 `source` / `sourceResolution`**:`--src full` 要控制器分辨率原图(L0 淘汰就报错,
|
|
126
|
+
不拿缩小图冒充);缺省 `auto` 在 L0 已淘汰时会降级成小图并**警告 + 退出 3**——用降级图量出来的
|
|
127
|
+
坐标不是控制器坐标。
|
|
128
|
+
- 没有模板、不知坐标时用 `annotate`:OCR/diff/连通域/边缘候选 + 编号回画,选号即得控制器坐标;
|
|
129
|
+
它和 `crop --point`(点选路径)互为降级。**读表先读 `availability`,不是先读条数**:
|
|
130
|
+
`mergedTotal` vs `count`(不等 = 被上限截断)、`dropped.tooBig`(巨框已剔除)、`byBand`
|
|
131
|
+
(某带 inTable 为 0 时看它的 inPool —— 是供给真没有还是被门槛/上限拿掉了)、`detectionResolution`
|
|
132
|
+
(检测面越小,小目标越可能根本探不到)。
|
|
133
|
+
**目标明确时用 `--roi` 收窄**:区域内的候选只跟自己竞争,这比把 `--som-limit` 调大更对症
|
|
134
|
+
(实测同一帧同一目标:整帧默认覆盖率 0% → 加 `--roi` 后 62.1%)。
|
|
135
|
+
候选按源理解:OCR 贴文字(带 `score`)、连通域偏亮连通区、边缘密度看梯度——都没有就走点选。
|
|
136
|
+
- 事后(没设备 / 状态已消失)仍可在**库帧**上跑:`annotate --from-kf <完整 ID>`。该路径没有会话事件,
|
|
137
|
+
**diff 源不可用**(工具如实报 `sourcesUnavailable`);要看"哪里变了"得用实时会话。
|
|
138
|
+
- `annotate` 的 OCR 候选只加载 `resource.path[0]`(或你显式给的 `--resource-dir`)。`reco --project` /
|
|
139
|
+
`--node-name` 才加载整条链,含后面的 bundle 和 `attach_resource_path`。模型不在第一段时,候选表会像
|
|
140
|
+
画面上没有字;验文字用 `reco`,或把 `--resource-dir` 指到模型所在的那一段。
|
|
141
|
+
- 探色用 `color`(ROI 实测均值/HSV/主色),选完色再 `reco --type ColorMatch` 出框——不要让模型"报色值"。
|
|
142
|
+
- **动手时**:坐标必须来自**同一次测量**(硬护栏 9)。点完/滑完立刻用动作回执自带的
|
|
143
|
+
`retention.before/after` 或 `wait stable` 复核,**不要拿旧截图当现场**(硬护栏 10)。
|
|
144
|
+
需要"按住 → 经过若干点 → 抬起"的折线手势(连线 / 谱曲 / 拖拽排序)用
|
|
145
|
+
`swipe x1 y1 x2 y2 --via "x,y;x,y"`——**一条笔画、中途不抬手**;要跨多条命令维持同一次按压,
|
|
146
|
+
用裸原语 `touchdown` / `touchmove` / `touchup` 自己拼序列。
|
|
147
|
+
**手势无效时先怀疑坐标过期与前置状态,不要先怀疑"不支持"**(硬护栏 11)。
|
|
148
|
+
|
|
149
|
+
## 2. 动线 B:识别实测(别猜参数)
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# 整节点透传(V1 扁平 / V2 嵌套都原样交给框架解析)
|
|
153
|
+
maafw-live reco --node '{"recognition":"TemplateMatch","template":"tpl.png","roi":[640,300,220,80]}' \
|
|
154
|
+
--resource-dir ./resource/base --image frame.png
|
|
155
|
+
|
|
156
|
+
# 节点/参数走文件(JSONC,可带注释):argv 会被 cmd.exe 重写,`^`、`"` 这类字符会变形
|
|
157
|
+
maafw-live reco --node-file node.json --resource-dir ./resource/base --image frame.png
|
|
158
|
+
cat node.json | maafw-live reco --node-file - --resource-dir ./resource/base --image frame.png
|
|
159
|
+
|
|
160
|
+
# 测"项目里那个节点":按资源里的原名跑。整条资源链(resource.path[] + attach)都会加载
|
|
161
|
+
maafw-live reco --project ./my-maa-project --node-name AP_NavUnlockButton --image frame.png
|
|
162
|
+
|
|
163
|
+
maafw-live reco --type TemplateMatch --image frame.png --resource-dir ./resource/base \
|
|
164
|
+
--sweep '{"key":"threshold","min":0.6,"max":0.95,"step":0.05}'
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
- 图像来源三条:`--image <png>`(离线文件,**不连设备**)、`--seq`(缓冲帧,需 `repl` 里先 `stream start`)、
|
|
168
|
+
`--act`(当前画面 + 真机执行动作半)。
|
|
169
|
+
- **读结果只认 `hit`**:miss 时框架仍会回 detail 对象,别把"有 detail"当命中。
|
|
170
|
+
- miss 先读回执 `missReason`,别一律归到"模板没读到":OCR 是 `roi 内无文字` / `expected 无匹配`
|
|
171
|
+
(**空结果还会先查 `model/ocr` 的 `det.onnx` + `rec.onnx` + `keys.txt` 在不在链上**——模型没加载时
|
|
172
|
+
框架不报错、只回空结果,以前会被当成"roi 里没有字"去改 roi);模板匹配是
|
|
173
|
+
`低于阈值(最高置信度 x)` / `模板与画面不匹配(最高置信度 x)`。`rawCount` / `filteredCount`
|
|
174
|
+
是 `all` 与 `expected` 过滤后的条数。
|
|
175
|
+
- 阈值起点 = 实测最高置信度 − 0.1(留余量);得分随场景会掉,换场景要复测。
|
|
176
|
+
- `--node` / `--node-file` / `--node-name` 三者互斥;`--param`、`--param-file`、`--sweep` 只作用于
|
|
177
|
+
`--type` 路径,与任一节点来源同给会直接报参数错误(不再静默忽略)。节点名拼错会给**近邻建议**
|
|
178
|
+
(按资源里真实节点名的编辑距离),别对着资源目录逐个比对。
|
|
179
|
+
- `repl` 里的 `reco` 是同一组能力(`--node-file` / `--node-name` / `--param-file`,资源取
|
|
180
|
+
`connect project` 的整条链),miss 原因与计数同样给;`-`(stdin)只在一次性命令里有意义,
|
|
181
|
+
REPL 里会直接拒绝(它的 stdin 是命令流)。
|
|
182
|
+
- `--node-name` 的 `layer` 恒为 `resource`(资源合并结果):`run` 还会再叠 interface 的
|
|
183
|
+
四级 `pipeline_override`,GUI 选项改过同一字段时两者对不上是**预期行为**。`connect project`
|
|
184
|
+
之后,REPL 里的 `run` 与一次性 `run --project` 是同一条装配(入口、四级覆盖、`--preset`)。
|
|
185
|
+
`--resource-dir` 只换资源来源;和 `--preset` 同给时 preset 没有可应用的四级链,REPL 打警告、
|
|
186
|
+
CLI 写进 `warnings`。Custom / agent 节点不桥接,回执直接给 `code: RECO_CUSTOM_NODE`——不是 miss,
|
|
187
|
+
改用 `run` 验。
|
|
188
|
+
- `--act` 只接受 `--node` / `--node-file`(节点要带 action),不接受 `--node-name`。它是
|
|
189
|
+
"识别拿框 → 用框真机执行动作"的半链路,会**真的动设备**。
|
|
190
|
+
判据两层:识别 `hit` + 动作 `success`,且**落点在命中框内**;动作边界帧在 `retention.before/after`,
|
|
191
|
+
取"点之前/点之后"的画面用它,不要事后重截冒充。识别未命中时动作半不执行(`stage=recognition`)。
|
|
192
|
+
- 一次只改一个变量,改完立刻复测并留记录;多处同改后结果变化无法归因。
|
|
193
|
+
|
|
194
|
+
## 3. 动线 C:模板资产(crop)
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
maafw-live crop --roi 640,300,220,80 --project ./my-maa-project --out tpl.png # 宽松框
|
|
198
|
+
maafw-live crop --point 700,340 --pad 24 --project ./my-maa-project --out tpl.png # 点 + 外扩
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
输入可以粗糙(宽松框或一个点),工具做三件事:**snap 收紧** → **从控制器分辨率原图裁** → **同帧自匹配
|
|
202
|
+
逐边 ±2px 精修**。产物是 L2 派生图,旁边写 `<out>.prov.json` 出处(来源帧身份 + 裁剪变换 + 自匹配结论)。
|
|
203
|
+
|
|
204
|
+
判据(不合就换框,别硬交):
|
|
205
|
+
|
|
206
|
+
- **位置正确优先于得分**:模板在同帧上都找不到自己(best 落在别处)说明它不独特——纯色/大面积同色区域的
|
|
207
|
+
CCOEFF_NORMED 得分是噪声,得分高也可能位置错。
|
|
208
|
+
- 连接真机时会自动做**跨帧验证**(另抓一帧再匹配);得分大幅衰减或位置漂移 → 模板对动态区域敏感,慎用。
|
|
209
|
+
- 只从**控制器分辨率原图**裁;绝不从降采样小图裁,也绝不上采样冒充原图。
|
|
210
|
+
- 自匹配用的资源同样只取 `resource.path[0]`(或显式 `--resource-dir`),不走 `reco` 那条整链。
|
|
211
|
+
第一段没有可加载的 pipeline 时,自匹配起不来:回执是「没跑起来」,不是「模板不独特」。
|
|
212
|
+
|
|
213
|
+
## 4. 动线 D:不可复现状态 → 留存 → 离线裁模板
|
|
214
|
+
|
|
215
|
+
这是留存契约要支撑的核心动线:**观察到的那一刻留档,事后(换天、没连设备、状态已消失)还能裁出模板**。
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
# ① 看到状态:当场升格(repl 里 stream/输入/run 之后立刻做)
|
|
219
|
+
maa> screencap /tmp/state.png # 或任意一次输入/任务边界帧,都会进 L0
|
|
220
|
+
maa> kf promote latest --note "活动弹窗"
|
|
221
|
+
|
|
222
|
+
# ② 事后(可以完全离线,不需要设备)
|
|
223
|
+
maafw-live kf list # 看库里有什么、完整 ID
|
|
224
|
+
maafw-live crop --from-kf kf:<库UUID>:0142 --roi 570,118,140,58 \
|
|
225
|
+
--project ./my-maa-project --out assets/tpl_popup.png # → L2 + .prov.json
|
|
226
|
+
maafw-live kf resolve kf:<库UUID>:0142 # available / missing / corrupt / unsupported
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- 库帧就是 **L0 原图本体**(控制器分辨率、捕获时刻整帧),不是预览小图:升级的只是存在状态
|
|
230
|
+
(进程态热缓存 → 库内文件),图的级别不变。
|
|
231
|
+
- `--from-kf` 缺省**离线**(不连设备)、缺省**不做**跨帧验证:留存帧对应的状态通常已不在画面上,
|
|
232
|
+
拿当前帧比只会得到假警告;状态可复现时显式给 `--cross`。
|
|
233
|
+
- 裁剪前工具会校 sha256 与像素尺寸,不符即拒——不许把"另一个字节"当依据。
|
|
234
|
+
- 出处侧车随模板一起提交进仓库;**设备地址与目标不落进 L2**,它们留在本地 manifest。
|
|
235
|
+
模板要进资源包时用 `crop --prov-out <dir>` 把侧车写到**包外**——资源包(`resource[].path`)会被整目录打包
|
|
236
|
+
发给用户,而出处是给开发者的过程资产;此时不变量从"同一目录"变成**同一相对路径**(建议与 `image/` 同构,
|
|
237
|
+
例如 `state-plan/provenance/`),并加一条对账检查防止漂移。
|
|
238
|
+
- 裁出的模板就是 pipeline 素材:PNG 放进项目资源的 `image/`,节点写 `template` + `roi`,`run` 一发看
|
|
239
|
+
`record.ok` 与节点状态(样例见 [references/examples.md](references/examples.md) 样例 5)。
|
|
240
|
+
- `missing` 只表示**本机**不可复核,不表示历史从未观测;L0 已淘汰时只能导 L1 参考帧,
|
|
241
|
+
按契约它**不作 state 证据本体**。
|
|
242
|
+
|
|
243
|
+
## 5. 结果怎么读
|
|
244
|
+
|
|
245
|
+
| 退出码 | 含义 | 典型 |
|
|
246
|
+
| :--- | :--- | :--- |
|
|
247
|
+
| 0 | 正常完成,未发现问题 | 识别命中、裁剪通过 |
|
|
248
|
+
| 1 | 命令自身失败 / **run 的任务失败** | `data.record.ok=false`(含超时被停止) |
|
|
249
|
+
| 2 | 参数、用法错误 | 缺 `--roi`、`--from-kf` 与 `--seq` 同给 |
|
|
250
|
+
| 3 | **跑成了,但发现了问题** | 识别未命中、裁剪警告(低纹理/跨帧不稳/空间不一致)、kf 帧不可解析 |
|
|
251
|
+
| 4 | 前置环境缺失 | 缺 maa-node、缺设备、资源未解析 |
|
|
252
|
+
|
|
253
|
+
`--json` 时 stdout 只有信封(字段只增不改)。**业务失败不一定让调用失败**:外层 `ok` 只代表调用完成,
|
|
254
|
+
任务级结果看 `data.record.ok`。逐条消费注意见 [references/pitfalls.md](references/pitfalls.md)。
|
|
255
|
+
|
|
256
|
+
## 6. 反模式(照着犯的代价都写在 references/pitfalls.md)
|
|
257
|
+
|
|
258
|
+
- 边看边猜坐标:截一帧 → 猜 roi → 再截一帧。有帧流与编号候选,坐标应当**测**出来。
|
|
259
|
+
- 攒证据:等这一段做完再升格关键帧——那时 L0 早已淘汰。
|
|
260
|
+
- 拿 L1 小图裁模板或当状态证据。
|
|
261
|
+
- 用当前画面冒充"当时那次捕获"(升格已淘汰的 seq 必须**如实失败**,工具会拒绝帮你作弊)。
|
|
262
|
+
- 同一台设备上并发跑两个控制器(面板 + 一次性命令)。
|
|
263
|
+
- 把 `reco` 未命中当"阈值太高"直接调低,而不是先问"目标在不在帧里"。
|
|
264
|
+
- 用退出码 0 判定任务成功。
|
|
265
|
+
- 在节点事件回调里发任何控制器任务(截图/点击)——与框架识别回路抢控制器会触发原生崩溃。
|
|
266
|
+
|
|
267
|
+
## 7. 故障与降级
|
|
268
|
+
|
|
269
|
+
| 情形 | 处置 |
|
|
270
|
+
| :--- | :--- |
|
|
271
|
+
| 连不上 / 找不到设备 | `device` 看目标是否在列表;项目模式检查 `interface.json` 的 controller 声明与窗口正则 |
|
|
272
|
+
| 一次性命令的缓冲是空的 | `seq` 恒为 0、环内无历史:帧序对齐与历史帧只在 `repl` / 客户端订阅路径有效 |
|
|
273
|
+
| 重连后帧流不再更新 | `connect` 会重建控制器,**旧流随之失效**(回执 `streamStopped:true`):重新 `stream start` 即可,别按"设备卡了"处理 |
|
|
274
|
+
| 资源未解析 / 缺模板 | `--resource-dir` 显式给,或修项目的 `resource[]` 声明 |
|
|
275
|
+
| 工具不可用 | **硬要求不随工具变**:像素空间一致 + 裁后自匹配复核 + 不猜参数;换工具要在结论里说明缺了哪项能力 |
|
|
276
|
+
|
|
277
|
+
## 参考
|
|
278
|
+
|
|
279
|
+
- [references/workflows.md](references/workflows.md):四条动线的命令级细节、参数选择与实测经验值
|
|
280
|
+
(末尾 **Z. 经验值速查**:框余量、判据链、耗时、候选上限/IoU/edge 的默认值与"改没改、为什么")。
|
|
281
|
+
- [references/pitfalls.md](references/pitfalls.md):真实教训与消费注意(含 daemon 协议易踩点)。
|
|
282
|
+
- [references/examples.md](references/examples.md):真机样例——一次性状态留档后离线裁模板、分辨率空间守卫、
|
|
283
|
+
两节点流程与 `record` 读法、带 `[JumpBack]` 的分支在 `nextCandidates` 里怎么读。
|
|
284
|
+
|
|
285
|
+
两件与本 skill 自身有关的事:
|
|
286
|
+
|
|
287
|
+
- **副本是否与 CLI 同源**:`maafw-live skill --check <安装根目录>` 逐文件比对(`same` / `different` /
|
|
288
|
+
`missing` / `extra`,有漂移退出 `FINDINGS(3)`);怀疑手里的 skill 比 CLI 旧就先跑它。
|
|
289
|
+
- **要动工具里的常数**:先量再改——`npm run survey:crop`(裁剪:snap 行为、位置正确率、成本)与
|
|
290
|
+
`npm run survey:som`(候选:各源数量、上限截断、分带、覆盖率),都离线、不需要设备。
|
|
291
|
+
注意前者的「位置正确率」是**自洽**指标(模板在同帧上找得到自己),模板把背景连元素一起裁进去也照样为真;
|
|
292
|
+
要算**误报/漏报**得配人工真值:`node scripts/survey-crop.mjs --truth scripts/truth/<集>.json`
|
|
293
|
+
(判定口径与加标注流程见 `scripts/truth/README.md`,种子集要长)。
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# 真机样例
|
|
2
|
+
|
|
3
|
+
环境:MuMu Player v5(Android,控制器 1280×720)+ `@maaxyz/maa-node` 5.14.2,
|
|
4
|
+
被测项目 = M9A(`interface.json` 默认控制器 ADB、资源 `resource/base`),游戏《重返未来:1999》。
|
|
5
|
+
数字都是实测值;命令里的路径换成你自己的。
|
|
6
|
+
|
|
7
|
+
## 样例 1:一次性状态留档 → 关掉 → 离线裁出状态标记
|
|
8
|
+
|
|
9
|
+
**为什么要这条动线**:每日签到弹窗一天只出现一次。看到它的时候不留档,回头就没有像素可裁——
|
|
10
|
+
这正是"捕获时刻的原图往往已经不存在"(留存契约开篇的需求来源)。
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# ① 看到状态:当场升格(必须在捕获它的那个 daemon 会话里)
|
|
14
|
+
printf 'connect project ./M9A\nscreencap /tmp/state.png\nkf promote latest --note "daily signin popup"\nquit\n' \
|
|
15
|
+
| maafw-live repl
|
|
16
|
+
# → 已升格 kf:<库UUID>:0005(1280x720)
|
|
17
|
+
|
|
18
|
+
# ② 关掉弹窗(点一下),该状态当天再也不可能出现
|
|
19
|
+
|
|
20
|
+
# ③ 事后,离线(不连设备)从留存帧裁状态标记
|
|
21
|
+
maafw-live crop --from-kf kf:<库UUID>:0005 --roi 570,118,140,58 \
|
|
22
|
+
--project ./M9A --out tpl_obtained.png
|
|
23
|
+
# 离线裁剪:未连接设备(留存帧是本机持久文件,裁剪不需要设备)
|
|
24
|
+
# 宽松框 [570,118,140,58] → 收紧 [589,124,101,23] ← snap 把标题文字收出来了
|
|
25
|
+
# 自匹配 位置正确,得分 1(9 次评估)
|
|
26
|
+
# 空间核对:未连接设备,未与当前控制器尺寸比对(模板空间 = 源帧捕获时尺寸 1280x720)
|
|
27
|
+
# L2 出处:tpl_obtained.png.prov.json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
复核(判据是"只在留存帧上命中"):
|
|
31
|
+
|
|
32
|
+
| 被检图像 | 结果 |
|
|
33
|
+
| :--- | :--- |
|
|
34
|
+
| 留存帧(库内 L0) | `hit=true score=1 box=[589,124,101,23]` |
|
|
35
|
+
| 当前实时画面(弹窗已关) | `hit=false`(同帧另一张物品卡片模板同样 miss) |
|
|
36
|
+
|
|
37
|
+
**结论**:这张留存帧是该状态唯一的证据;模板从它离线裁出且出处可追溯。这是 `--from-kf` 存在的理由。
|
|
38
|
+
|
|
39
|
+
## 样例 2:离线裁出的模板在实时帧上仍然命中(像素空间一致)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
maafw-live crop --from-kf kf:<库UUID>:0003 --roi 925,138,68,70 --project ./M9A --out tpl_icon.png
|
|
43
|
+
maafw-live screencap --project ./M9A --out live.png # 另取一帧实时画面
|
|
44
|
+
maafw-live reco --node '{"recognition":"TemplateMatch","template":"<abs>/tpl_icon.png","roi":[880,110,180,140]}' \
|
|
45
|
+
--resource-dir ./M9A/resource/base --image live.png
|
|
46
|
+
# [命中] ... 实测最高置信度 1
|
|
47
|
+
# best.box = [925,138,68,70]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**结论**:库帧与实时帧同为 1280×720 控制器空间,1:1 裁出的 L2 可以直接用于实时识别——
|
|
51
|
+
"从留存帧裁"不等于"只能事后看看"。
|
|
52
|
+
|
|
53
|
+
## 样例 3:分辨率空间守卫(别让错空间的模板流进 pipeline)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 用 1080 短边连接(控制器 1920x1080),却拿 1280x720 的库帧裁模板 + 显式跨帧验证
|
|
57
|
+
maafw-live crop --from-kf kf:<库UUID>:0003 --roi 925,138,68,70 \
|
|
58
|
+
--kind adb --target 127.0.0.1:16384 --short-side 1080 --cross \
|
|
59
|
+
--resource-dir ./M9A/resource/base --out tpl_bad.png
|
|
60
|
+
# 自匹配 位置正确,得分 1(同帧仍然对——模板与源帧当然一致)
|
|
61
|
+
# 跨帧验证 seq=1:得分 0,位置漂移
|
|
62
|
+
# 空间核对:源 1280x720 ≠ 当前控制器 1920x1080
|
|
63
|
+
# 警告:… 源帧空间与当前控制器不一致(见下条),该比对不成立,先在当前空间重采一帧再验
|
|
64
|
+
# 警告:模板按源帧空间裁出,与当前识别空间不一致(改过 shortSide / 换过设备?)
|
|
65
|
+
# exit=3
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**结论**:同帧自匹配 1.0 **不能**证明模板可跨空间使用。工具在这里不静默产出"看起来很好"的模板,
|
|
69
|
+
而是给出空间不一致的判定与退出 3;跨帧失败也**不会**被误归因成"模板对动态区域敏感"。
|
|
70
|
+
|
|
71
|
+
## 样例 4:热缓存路径对照(同一条动线,源不同)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
maafw-live crop --roi 925,138,68,70 --project ./M9A --out tpl_live.png
|
|
75
|
+
# 源帧 seq=1(1280x720,L0 热缓存)
|
|
76
|
+
# 自匹配 位置正确,得分 1(9 次评估)
|
|
77
|
+
# 跨帧验证 seq=2:得分 1,位置正确 ← 热缓存路径自动跨帧(这次状态还在画面上)
|
|
78
|
+
# 空间核对:源 1280x720 = 当前控制器 1280x720
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
出处里的 `derivedFrom.kind` 是 `l0-cache`(带热缓存 `seq`),样例 1–3 是 `kf`(带完整 `kf:` ID 与 sha256)。
|
|
82
|
+
两种来源把同一套契约字段填满,消费方不必分支处理。
|
|
83
|
+
|
|
84
|
+
## 样例 5:离线裁出的模板直接进 pipeline 节点(闭环)
|
|
85
|
+
|
|
86
|
+
承接样例 1 的做法:签到月历页留档为库帧 → 离线裁出「今日奖励」标签(`556,356,120,44` → 收紧
|
|
87
|
+
`556,357,85,25`,自匹配 1.0)→ 放进项目资源 → 跑节点。
|
|
88
|
+
|
|
89
|
+
> 事后用人工真值量过这一例:该框面积是「今日奖励」文字的 **2.3 倍**、与真值框 IoU **0.39**——
|
|
90
|
+
> 当时的「位置正确 + 自匹配 1.0」看不出这件事。它当模板能用(节点确实命中),但**不算贴合的框**;
|
|
91
|
+
> 这类"够用但不贴合"只能靠外部真值发现(`scripts/truth/`,见 SKILL.md 末条)。
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# 项目侧(模板是资源的一部分,和别的素材一样提交)
|
|
95
|
+
mkdir -p proj/res/image && cp tpl_signin_label.png proj/res/image/signin_label.png
|
|
96
|
+
cat proj/res/pipeline/demo.json
|
|
97
|
+
# { "SigninCalendar": { "recognition": "TemplateMatch", "template": "signin_label.png",
|
|
98
|
+
# "roi": [520,330,200,100], "threshold": 0.8, "action": "DoNothing" } }
|
|
99
|
+
|
|
100
|
+
maafw-live run --entry SigninCalendar --project ./proj --timeout 30000
|
|
101
|
+
# 任务 SigninCalendar:完成(status=3000,651ms,帧序 1..3)
|
|
102
|
+
# [ok] SigninCalendar 415ms
|
|
103
|
+
# exit=0
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**结论**:`--from-kf` 裁出的 L2 不是"事后看一眼"的产物——它就是能写进 pipeline、被框架识别回路命中的模板。
|
|
107
|
+
判据落在任务级:`record.ok=true` 且节点 `[ok]`(不是"命令退出 0")。阈值起点按实测(本例自匹配 1.0,
|
|
108
|
+
节点处取 0.8 留余量)。
|
|
109
|
+
|
|
110
|
+
## 样例 6:离线裁的模板做真机落点验证(`reco --act`,识别成功 ≠ 点击生效)
|
|
111
|
+
|
|
112
|
+
承接样例 2:留存帧里大厅的「图鉴」图标 → 离线裁模板(`52,466,78,76` → 收紧 `60,467,69,62`,自匹配 1.0)
|
|
113
|
+
→ 用同一个模板真机点它。
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
maafw-live reco --act --node '{"recognition":"TemplateMatch","template":"<abs>/tpl_gallery.png",
|
|
117
|
+
"roi":[40,450,180,120],"threshold":0.8,"action":"Click"}' \
|
|
118
|
+
--project ./M9A --json
|
|
119
|
+
# stage=action
|
|
120
|
+
# reco: {"algorithm":"TemplateMatch","hit":true,"box":[60,467,69,62], "best":{"score":0.990205}}
|
|
121
|
+
# action: {"action":"Click","box":[60,467,69,62],"success":true,"detail":{"point":[118,470]}}
|
|
122
|
+
# retention: before.seq=1 after.seq=2
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
随后截图确认:画面已从大厅切到图鉴页——**动作真的生效了**。
|
|
126
|
+
|
|
127
|
+
要读的三件事:
|
|
128
|
+
|
|
129
|
+
1. **`stage`**:`action` 表示识别命中且动作已执行;停在 `recognition` 说明动作半没跑(识别未命中/报错)。
|
|
130
|
+
2. **命中框 vs 落点**:`reco.box=[60,467,69,62]`,实际点击点 `[118,470]` 落在框内——这是 Click 的
|
|
131
|
+
「框内随机点」语义(避免每次都点同一个像素、也避免脚本特征)。落点在框外才是问题。
|
|
132
|
+
3. **`retention.before/after`**:动作边界帧已进 L0 锚区,可直接 `frame get --seq` 取"点之前/点之后"的画面
|
|
133
|
+
取证——不要用"现在再截一帧"冒充动作后的那一刻。
|
|
134
|
+
|
|
135
|
+
**结论**:`--act` 是"识别拿框 → 真机执行动作"的半链路,专门用来验证 target 框语义与落点。
|
|
136
|
+
它会**真的动设备**,所以:一次一个变量、先确认目标在帧里、动作有可见效果时再下结论(本例效果 = 页面切换)。
|
|
137
|
+
|
|
138
|
+
## 样例 7:两个模板全部离线裁自留存帧,跑通两节点流程
|
|
139
|
+
|
|
140
|
+
这是"素材从哪来"的完整答案:**两个节点用的模板都不是现场截的**,一个来自几天前的大厅留存帧,
|
|
141
|
+
一个来自本次会话刚升格的图鉴页留存帧;目标位置也不是猜的,是 `annotate --from-kf` 的候选表给的。
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# ① 节点 2 的目标:在留存帧上离线取候选(OCR 源要资源目录),按表选号
|
|
145
|
+
maafw-live annotate --from-kf kf:<库UUID>:0007 --resource-dir ./M9A/resource/base --out som.png
|
|
146
|
+
# SoM 候选 30 个(ocr 15 / conn 15)… #5 [ocr] ctrl=116,403,118,43 "以影像之"
|
|
147
|
+
|
|
148
|
+
# ② 离线裁两个模板(分别源自大厅留存帧与图鉴页留存帧)
|
|
149
|
+
maafw-live crop --from-kf kf:<库UUID>:0004 --roi 52,466,78,76 --project ./M9A --out gallery_icon.png
|
|
150
|
+
maafw-live crop --from-kf kf:<库UUID>:0007 --roi 110,398,132,54 --project ./M9A --out story_title.png
|
|
151
|
+
# 收紧 60,467,69,62(1.0) 收紧 126,416,101,23(1.0)
|
|
152
|
+
|
|
153
|
+
# ③ 放进项目资源,写 next 链,跑
|
|
154
|
+
# EnterGallery : TemplateMatch gallery_icon.png roi[40,450,180,120] → Click, next: [GalleryOpened]
|
|
155
|
+
# GalleryOpened : TemplateMatch story_title.png roi[80,370,200,90] → DoNothing
|
|
156
|
+
maafw-live run --entry EnterGallery --project ./proj2 --timeout 40000
|
|
157
|
+
# 任务 EnterGallery:完成(status=3000,2223ms,帧序 1..4)
|
|
158
|
+
# [ok] EnterGallery 469ms
|
|
159
|
+
# [ok] GalleryOpened 1414ms
|
|
160
|
+
# exit=0
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**屏幕状态独立复核**(不要只看退出码):跑完截图 → 图鉴页模板命中 1.0、大厅图标 miss
|
|
164
|
+
→ 点击与页面切换都真的发生了。
|
|
165
|
+
|
|
166
|
+
两个必须知道的读表细节:
|
|
167
|
+
|
|
168
|
+
1. **认 `nameSource`**:`node_details` 是框架给的权威节点名(上面两条都是),可以据此判断走了哪条分支;
|
|
169
|
+
`entry` 是顶层兜底值,出现在节点完成之前(框架在 `PipelineNode.*` 顶层填的是任务入口名),**未确认**。
|
|
170
|
+
另外 `record.nextCandidates` 给的是**候选序列**:本例读作 `EnterGallery×1 → GalleryOpened×3`
|
|
171
|
+
——"×3"是页面切换动画期间框架反复轮询同一候选,属于正常现象,也顺带告诉你那一步等了多久才命中。
|
|
172
|
+
端到端结论始终靠 `record.ok` + 屏幕或留存帧复核。
|
|
173
|
+
2. **素材链可以完全离线**:留存帧 → `annotate --from-kf` 选候选 → `crop --from-kf` 裁模板 → 写进项目 →
|
|
174
|
+
`run` 时设备才需要出现。设备在不在那个画面上,与素材制作无关。
|
|
175
|
+
|
|
176
|
+
## 样例 8:带 `[JumpBack]` 的分支——`nextCandidates` 在分支场景里怎么读
|
|
177
|
+
|
|
178
|
+
样例 7 的 `next` 只有一个候选(线性)。真实流程里 `next` 常常是"一个可能出现的一次性弹窗 + 真正的目标",
|
|
179
|
+
这次把 `next` 换成两个候选、**一对一错**,其中一个带 `[JumpBack]`(进图鉴后可能弹一次奖励弹窗):
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
cat proj3/res/pipeline/branch.json
|
|
183
|
+
# { "EnterGalleryBranch": { "recognition": "TemplateMatch", "template": "gallery_icon.png",
|
|
184
|
+
# "roi": [40,440,140,110], "threshold": 0.8, "action": "Click",
|
|
185
|
+
# "next": ["[JumpBack]RewardPopupGuard", "StoryEntry"] },
|
|
186
|
+
# "RewardPopupGuard": { "recognition": "TemplateMatch", "template": "obtained_title.png",
|
|
187
|
+
# "threshold": 0.8, "action": "DoNothing", "next": ["StoryEntry"] },
|
|
188
|
+
# "StoryEntry": { "recognition": "TemplateMatch", "template": "story_title.png",
|
|
189
|
+
# "roi": [80,380,200,80], "threshold": 0.8, "action": "DoNothing" } }
|
|
190
|
+
|
|
191
|
+
maafw-live run --entry EnterGalleryBranch --project ./proj3 --timeout 40000
|
|
192
|
+
# 任务 EnterGalleryBranch:完成(status=3000,2278ms,帧序 1..2)
|
|
193
|
+
# [ok] EnterGalleryBranch 467ms
|
|
194
|
+
# [ok] StoryEntry 1487ms
|
|
195
|
+
# exit=0
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
三个模板的关系:`gallery_icon`(大厅「图鉴」入口,留存帧 0004 离线裁)、`story_title`(图鉴页「以影像之」,
|
|
199
|
+
留存帧 0007 离线裁)都实测命中(0.998 / 1.0);`obtained_title`(签到弹窗的「获得物品」标题,留存帧 0005)在这个
|
|
200
|
+
流程里**不该出现**——它就是那个错的候选,实测在跑前的大厅帧上 0.227、跑后的图鉴页帧上也 miss(不是"擦线未中",
|
|
201
|
+
是真的不在画面上)。
|
|
202
|
+
|
|
203
|
+
**候选序列只在 `--json` 的 `data.record` 里**(纯文本摘要只列跑过的节点),两次复跑读出来逐条一致:
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
nextCandidateCount=7
|
|
207
|
+
EnterGalleryBranch×1 → RewardPopupGuard[JumpBack]×1 → StoryEntry×1
|
|
208
|
+
→ RewardPopupGuard[JumpBack]×1 → StoryEntry×1
|
|
209
|
+
→ RewardPopupGuard[JumpBack]×1 → StoryEntry×1
|
|
210
|
+
# 三轮公告的时间戳(相对第一条公告):+541ms / +622ms / +1541ms(两次跑同一形状,只有毫秒不同)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
四个读法,不加区分就会读错:
|
|
214
|
+
|
|
215
|
+
1. **第一条是任务入口本身**,不是一次轮询——框架起手就把"下一步 = 入口节点"当候选公告出来。数轮次要**从第二条起**。
|
|
216
|
+
2. **分支里 `polls` 恒为 1,改成"数条数"**:`polls` 折叠只发生在**同一候选连续重复**时(样例 7 的单候选
|
|
217
|
+
`GalleryOpened×3` 就是这么折出来的)。`next` 有两个候选时公告是 (A, B) 交替的,谁也不与上一条相邻 →
|
|
218
|
+
每条各占一行、`polls` 全是 1。本例"每个候选 3 条 = 轮询了 3 轮",第 3 轮才命中(页面切换约 1.5s)。
|
|
219
|
+
3. **`[JumpBack]` 候选带 `jumpBack: true`,而且从不折叠**(实现上明确跳过它)——它是"回头点"标记,不改变
|
|
220
|
+
轮询顺序:公告顺序就是 `next` 的书写顺序,**排在前面不代表先被选中**。
|
|
221
|
+
4. **走的是哪条分支看紧接着的节点记录,不看出现在候选里的名字**:本例两条节点记录是
|
|
222
|
+
`EnterGalleryBranch → StoryEntry`,而 `RewardPopupGuard` **一条节点记录都没有**——没命中的候选不产生节点记录。
|
|
223
|
+
所以"候选轮询过哪些"只能从 `nextCandidates` 读,"实际走了哪条"从 `nodes` 读,两者合起来才是这次分支的完整故事。
|
|
224
|
+
|
|
225
|
+
**屏幕状态独立复核**(判据不止退出码):跑完截图 → `story_title` 命中 1.0、`gallery_icon` miss(0.126)
|
|
226
|
+
→ 页面确实切到了图鉴页,"记录里看着走了"之外还有画面为证。
|
|
227
|
+
|
|
228
|
+
**结论**:带 `[JumpBack]` 的分支在 `nextCandidates` 里读起来是**交替出现的候选序列**——用条数数轮次、
|
|
229
|
+
用 `jumpBack` 认回头点、用 `nodes` 认最终走的那条;`polls` 只在单候选被反复公告时才大于 1,别当成统一的"次数"。
|
|
230
|
+
|
|
231
|
+
## 这几条样例共同说明的事
|
|
232
|
+
|
|
233
|
+
1. **源帧身份决定能不能复核**:热缓存 seq 只在本会话有意义;库帧 ID + sha256 才是跨会话可复核的引用。
|
|
234
|
+
2. **同一个判据链**:位置正确 → 跨帧稳定 → 空间一致。三段都过,模板才值得写进 pipeline。
|
|
235
|
+
3. **失败要如实**:状态没了就是没了(miss 是结论,不是要调参);空间不同源就说不一致,
|
|
236
|
+
不产出"得分很高但用不了"的模板。
|
|
237
|
+
4. **记录要读两层**:`nodes` 说"实际走了哪条路"(只有命中的节点才有记录),`nextCandidates` 说"当时轮询过哪些出口"
|
|
238
|
+
(包括没走的那条腿)——分支结论必须两层都读,并在有可见效果时用屏幕/留存帧复核。
|