跳转到内容

系统架构

PhysiClaw 就是一个 Python 服务器:一头连着硬件,另一头连着大脑。大脑负责决策,MCP 服务器把这些决策变成相机画面和机械臂动作,而一个小巧的手机端桥接则让服务器能伸进 iOS,去做那几件触控笔做不到的事。下面要讲的,正是夹在「点那个按钮」和触控笔真正触到玻璃之间的全部环节。

┌──────────────────────────────────────────────────────────────┐
│ BRAIN — sees a screen, picks a bbox + a gesture │
│ external MCP client (Claude Desktop, …) OR built-in agent │
└───────────────────────────────┬──────────────────────────────┘
│ MCP (12 tools: peek, tap, swipe, …)
┌──────────────────────────────────────────────────────────────┐
│ PhysiClaw MCP SERVER (Python · core/) │
│ orchestrator · vision (OCR + icon detect) · calibration math │
└───────┬───────────────────────┬───────────────────┬──────────┘
│ USB-UVC │ USB-serial │ HTTP over Wi-Fi (LAN)
▼ ▼ ▼
┌─────────────┐ ┌────────────────┐ ┌──────────────────────┐
│ ONE overhead│ │ GRBL / FluidNC │ │ PHONE-SIDE BRIDGE │
│ camera │ │ controller │ │ • bridge web page │
└──────┬──────┘ │ X·Y gantry + │ │ (Safari, /bridge) │
│ looks down │ Z SOLENOID │ │ • AssistiveTouch │
│ └───────┬────────┘ │ • 3 iOS Shortcuts │
│ │ stylus tip └──────────┬───────────┘
│ ▼ │ runs on
└─────────────► ┌──────────────┐ ◄──────────────┘
│ PHONE │
│ (unlocked) │
└──────────────┘

本页接下来会逐一拆解每个方框,再讲它们之间的连线。

PhysiClaw 把它的硬件暴露成一组 MCP 工具——也就是 Model Context Protocol,Claude Desktop 和其他客户端早已通用的那套标准。 任何能讲 MCP 的东西都能驱动机械臂。这里支持两种大脑,而它们用的是完全相同的工具接口:

外部 MCP 客户端

把 Claude Desktop(或任意 MCP 客户端)指向服务器,给它一个目标,它就会直接调用 peek / tap / swipe。模型和对话循环都由你自己带来。

内置 agent

PhysiClaw 在 agent/ 下自带了一个大脑——一套原生的工具循环,带记忆、技能和 cron/轮询触发器,传承自 OpenClaw。它调用的是同样的 12 个工具,只是回路里 少了一个桌面应用。

无论哪种方式,大脑要干的活都一样:读一张被标注好的屏幕,挑出一个边界框一个手势,剩下的交给服务器。它从不会看到电机坐标,也看不到像素。

一个单独的 Python 进程,拆成几个职责明确的模块。编排器 (core/orchestration/orchestrator.py)是脊梁——它掌管机械臂、相机、标定, 以及一把忙碌锁,确保两次工具调用永远不会同时让机械臂动起来。每个工具都会拿到这把锁, 干完自己的活,然后在退出时把触控笔停到屏幕外。

  • 文件夹src/physiclaw/
    • 文件夹core/ the robot — hardware as MCP tools
      • 文件夹server/ FastMCP instance + the 12 tool definitions
      • 文件夹orchestration/ the orchestrator: lifecycle, lock, gestures
      • 文件夹vision/ OCR + ONNX icon detection → element listing
      • 文件夹calibration/ the affine transforms learned during setup
      • 文件夹hardware/ arm, camera, GRBL/FluidNC, solenoid
      • 文件夹bridge/ LAN bridge: phone page, screenshot upload, clipboard
    • 文件夹agent/ the built-in brain (one MCP client among many)

你最常打交道的几块:

  • vision —— 把一帧相机画面(或一张手机截图)变成一份整洁的元素清单: 每个按钮、图标和文字行,都列成 id · kind · label · bbox · conf。详见 它如何看见
  • calibration —— 持有两张仿射映射,把三个坐标系(屏幕分数、相机像素、机械臂毫米) 连接起来。详见 标定数学
  • hardware —— 封装了 GRBL/FluidNC 控制器。GRBL 是驱动 X/Y 龙门架的开源 G-code 固件;FluidNC 是 PhysiClaw 烧录的 ESP32 变体。Z 轴不是电机—— 它是一个 solenoid(电磁铁),一块快速电磁铁,会把触控笔尖猛地按下去,再让它弹回来。
overhead camera (ONE)
│ looks straight down, ~25 cm up
┌───────────┐
stylus tip ───►│ PHONE │ X·Y gantry slides the tip across the glass;
on a solenoid │ screen │ the solenoid drops it to register a touch
└───────────┘

只有一台相机,架在头顶笔直朝下俯视屏幕——没有第二台相机,也没有深度传感器。 可靠性来自每动一次就重新看一眼,而不是堆更多硬件。机械臂是一套 X/Y 龙门架, 能把触控笔定位到屏幕上任意一点;笔尖上的 solenoid 就是 Z 轴。一次点击就是一记 干脆的下压再抬起的脉冲(固件先猛击线圈,再以较低占空比保持,这样长按时不会掉下来); 长按则只是把笔尖按住久一点。对手机而言,这个笔尖和指尖毫无分别,所以任何 App 都能用, 无需逐个 App 配置。

触控笔几乎能做手指能做的一切——但不完全是一切。它没法向 iOS 索要一张像素级精确的截图, 也没法把内容打进系统剪贴板。桥接填的正是这几道缝,多一点都没有。它有三个部分, 都在准备手机时一次性配好:

  1. 一张桥接网页。 手机在 Safari 里打开 http://<server-ip>:8048/bridge(你扫一个 二维码来配对——两台设备只要在同一个 Wi-Fi 下即可)。这个页面每秒轮询服务器四次, 并把服务器要它显示的东西渲染出来:配置阶段的标定靶点,或运行时排队的剪贴板文本。

  2. AssistiveTouch。 iOS 那个悬浮的辅助功能按钮。机械臂会物理地点击 AssistiveTouch 按钮,去触发触控笔本来够不着的系统动作——单击截图,双击和长按则运行 Shortcuts。

  3. 三个 iOS Shortcuts。 绑定到那些 AssistiveTouch 手势上: 截一张图把最新的截图上传到服务器,以及把排队的文本取进剪贴板。

所以当大脑调用 screenshot 时,服务器并不自己抓像素——它驱动机械臂去点 AssistiveTouch, iOS Shortcut 抓下手机自己的截图,把字节通过 Wi-Fi POST 回来,服务器再对那些像素跑 vision。剪贴板也是同样的路子:服务器把文本排进队列,机械臂长按 AssistiveTouch, Shortcut 把文本拉进来。桥接是手机上唯一存在的东西,而它存在的全部理由,就是截图、剪贴板, 以及显示标定标记。

跳点传输载荷
大脑 ↔ 服务器MCP工具调用 + 图像/清单结果
服务器 ↔ 相机USB (UVC)JPEG 帧
服务器 ↔ 控制器USB 串口G-code(GRBL 方言)
服务器 ↔ 手机桥接HTTP over Wi-Fi (LAN)轮询状态、截图上传、剪贴板文本

其中三条是显而易见的线缆;第四条才是当年那套「手机原封不动」说法漏掉的那根。手机从不 通过 USB 连服务器——它是通过你的局域网够到服务器的,这也是为什么两者必须共享一个 Wi-Fi。 四条线都接好后,一个完整动作看起来是这样:

brain: peek → server crops camera frame, runs OCR+icons, returns listing
brain: tap [bbox] → server maps bbox → arm mm, moves, fires solenoid, parks off-screen
brain: peek → server re-photographs, returns a fresh listing to compare

这个「看 → 决定 → 动 → 再查」的节奏,就是整个控制回路——而正因为每一步的结尾都是看一眼, 任何意外都不过是下一件要应对的事而已。