跳转到内容

它如何看见

PhysiClaw 从不把原始像素丢给大脑去琢磨——它递给大脑的是一份清单,其中每个按钮、图标和 文字行都已经被找到、贴上标签、框好了。两个模型在每一帧上完成这项工作:一个 OCR 阅读器负责 文字,一个小神经网络负责图标。本页讲的就是一帧画面如何变成那份清单,以及清单为什么长成那样。

手机屏幕上有两类可点的东西——文字和图形——PhysiClaw 为每一类各跑一个专门的模型,再把结果合并。

文字 → OCR

RapidOCR(跑在 ONNX Runtime 上的 PaddleOCR 模型,中英文,不依赖 PyTorch)。它读出 每一块文字区域,返回字符串外加一个像素框。这就给了每个文字元素一个真正的 label—— "Settings""$29.9"——于是大脑可以按名字瞄准。

图标 → ONNX 检测器

OmniParser V2 图标检测(一个由 Microsoft 微调的 YOLO11m 模型,导出为 ONNX, 经 OpenCV 的 DNN 模块运行——同样不依赖 PyTorch)。它找出那些可交互的图形——App 图标、 图像按钮、开关——这些都没有文字可供 OCR 去读。

两者都完全在本地设备上运行。任何一帧都不会离开你的机器去做分析,这也是为什么 安装会把模型下载到本地。两路输出会被合并、清理(丢掉太小的框和 低置信度的命中、按重叠去掉近重复项),从上到下、再从左到右排序,然后重新编号为 0, 1, 2, …

合并后的结果就是大脑对屏幕的全部视野——每个元素占一行:

id [kind] "label" [left,top,right,bottom] conf
0 [icon] "" [0.020,0.060,0.110,0.100] 0.64
1 [text] "Settings" [0.510,0.550,0.590,0.630] 0.96
2 [text] "Wednesday 14" [0.300,0.080,0.700,0.130] 0.99

五个字段,大脑拿到的就这些:

字段它是什么
id该元素在本份清单里的稳定句柄(每帧重新编号)。
kindtexticon——哪个检测器找到了它。
label文字的 OCR 字符串;图标则为"")——它那张图没有名字。
bbox那个矩形,屏幕的四个分数 [left, top, right, bottom]
conf检测器的置信度,01。文字低于 0.7、图标低于 0.3 的会被丢掉。

图标的标签为空是有意为之——检测器找到的是这里有个能点的东西,而不是它是什么。 大脑会从位置和周围的文字推断出一个没有标签的图标是什么,就像一个人扫一眼一格格 App 图标, 就知道哪个是哪个。

bbox(边界框)是围住某个元素的矩形,给出四个数,每个都在 01 之间—— 是屏幕宽和高的分数,绝不是像素。

(0,0) ┌───────────────────────┐
│ [0.51, 0.55, 0.59, 0.63]
│ ┌──────┐ ← left=0.51 (51% across)
│ │ Set… │ top=0.55 (55% down)
│ └──────┘ right=0.59, bottom=0.63
│ center = (0.55, 0.59)
└───────────────────────┘ (1,1)

用分数而非像素,正是清单可移植的原因。同一个 [0.51, 0.55, 0.59, 0.63] 指向同一个按钮, 无论底层那帧是 1024 像素的相机裁切还是 1170 像素的手机截图——也无论你的手机是小号 SE 还是大号 Pro Max。大脑只在一个干净的 01 空间里思考;所有到像素和毫米的来回换算都由 服务器处理(那是标定的活)。

当大脑想动手时,它说出一个边界框和一个手势——tap [0.51, 0.55, 0.59, 0.63]。服务器取这个框的 中心 ((left+right)/2, (top+bottom)/2),朝那里瞄。所以只要框的正中落在目标上,松一点的 框完全没问题;你是在指,不是在描边。

peekscreenshot:两个来源,同一份清单

Section titled “peek 对 screenshot:两个来源,同一份清单”

清单格式从不变——但它可以由两张不同的图像构建,而在两者之间做选择,是「看」里唯一真正的取舍。

peekscreenshot
图像来源头顶相机手机自己的截图
怎么做拍快照 → 裁到屏幕 → 检测点 AssistiveTouch → iOS Shortcut 上传像素 → 检测
速度~4 s~12 s
清晰度对多数目标够用像素级精确
副作用App 可能察觉到截图(分享面板、水印)

peek 是默认项,也是主力。它把触控笔停到视野外,拍下屏幕,裁到只剩手机屏幕那块区域 (裁切让每个图标多出 2–3 倍像素,让检测更锐利),再跑同样那两个检测器。如果第一帧回来是糊的 ——对焦或运动问题——它会等上几秒,再抓一帧才肯放弃。

screenshot 是升级手段。它不拍相机照片,而是通过 桥接触发手机自己的截图:机械臂点 AssistiveTouch,一个 iOS Shortcut 抓取并上传像素级精确的字节,vision 在那些像素上运行。 结果更锐利但更慢,而且它是会改变状态的——截图是一个真实的 iOS 事件,有些 App 会对它做出 反应。所以大脑只在以下情况才动用它:

  • 目标太小,相机分辨不出(它在 peek 清单里缺失),
  • 眩光或运动模糊让相机画面读不清,或者
  • 它需要读相机看不清的小字。

因为一次截图可能改变 App 状态,规矩是:screenshot 之后、点击之前,永远要再 peek 一次 ——先读到鲜活的屏幕,然后再动手。

把一份解析好的清单(而不是一张图像)递给大脑,一举做成了三件事:它把每个目标都锚定在屏幕上一个 真实的矩形里(无需猜像素坐标),它把「屏幕变了吗?」变成两份清单之间一次廉价的文本对比, 并且它让大脑的词汇表保持极小——挑一个框,挑一个手势。正是这个小而稳定的接口,让 PhysiClaw 能靠简简单单再看一眼,就从意外中恢复过来。