把 AirPods 变成“头部鼠标”

耳机里有运动传感器,不等于第三方程序就能随意读取。先确认公开能力,再用六次真实反馈把运动数据变成可以开关的光标控制。

佩戴白色无线耳机的线框头部通过运动轨迹控制屏幕指针

一副每天戴在耳朵里的耳机,除了播放声音,还能做什么?

当我们转头时,AirPods 里的声音会跟着空间改变。这让我产生了一个好奇的念头:既然耳机知道头正在移动,它能不能把这种理解交给电脑?也许我们不必再增加一个新设备,只要轻轻抬头、低头或转头,就能表达自己的意图。

于是我们做了一个实验。它没有从代码开始,而是从一连串问题开始:耳机究竟知道什么?Apple 愿意向第三方开放什么?别人做到过什么?我们又能把它变成怎样的新体验?

借助 VibeCoding,这些问题一步步变成了可以亲手体验的软件。本文要分享的不是一堆技术名词,而是这段从好奇、调研到真实产品的过程,以及一套零基础用户也能复用的方法:

产生创意 → 调研硬件与开源项目 → 制定阶段目标 → 目标、AI 实现、灰测 → 根据真实反馈进入下一轮。

读完后,你不一定会立刻变成程序员,但应该能够带着一副兼容的 AirPods 和一台 Mac,指挥 AI 复刻出同类项目。

一、最后做出了什么

我们把它叫作“头部鼠标”。

戴上 AirPods,打开 Mac 上的应用,屏幕中央会出现一个十字和一颗小球。你向哪边转头,小球就向哪边移动;动作幅度还可以按照自己的习惯调节。轻轻打开一个开关,小球的控制就会延伸到整个电脑:你的头部开始带动鼠标光标,回到正前方,光标便安静地停下来。摘下耳机,控制会自动关闭。

它不要求用户再购买一件陌生的外设,也不要求先学会一种复杂的操作语言。戴上原本就在使用的耳机,头部就多了一种与电脑交流的能力。

今天,它还是一个内部测试原型;但我们已经能看到它可能走向的地方。

在游戏里,转头不再只是现实中的动作:它可以让玩家探身观察、操纵飞行,或与视线和手柄协作,创造前所未有的游戏体验。在日常工作中,它可以和语音、眼动或 AI 助手配合,用一个很轻的动作完成选择与确认。对于瘫痪、上肢活动受限,或难以长期使用传统鼠标的人,它也可能成为另一条操作电脑的通道。

这让项目同时拥有两种价值:在技术上,它探索一种随身、自然的新输入方式;在社会层面,它让“操作电脑”这件事有机会适应更多不同的身体,而不是要求所有人都去适应同一种鼠标和键盘。

当前版本已经可以显示头部响应、调节灵敏度、控制系统光标、在耳机断开时安全停止,并打包给其他 Mac 用户测试。它还没有完成点击、离散点头和摇头指令,也不是医疗器械,但最关键的一步已经发生:一个好奇的想法,真的变成了可以体验的产品。

二、方法的外环:从创意走到阶段路线

整个项目有一个外层流程,也有一个不断重复的内层循环。

流程概览
  1. 创意:耳机能否成为 AI 输入设备?
  2. 硬件与平台能力调研
  3. 行业与开源项目调研
  4. 制定分阶段目标
  5. 目标
  6. AI 实现
  7. 真实硬件灰测

↻ 反馈形成下一轮目标

外环负责避免方向错误:这个创意是否可行,硬件到底有什么,公开软件接口允许做什么,别人已经走到哪里。

内环负责让产品真正长出来:每一轮只确定一个可观察目标,让 AI 做出最小版本,再用真实硬件和真实用户去灰测。

没有外环,AI 很可能迅速实现一个建立在错误假设上的产品;没有内环,调研就会变成一堆永远不会运行的文档。

三、创意阶段:先问“为什么”,不要先问“怎么写代码”

AirPods 支线来自“手柄 VB”项目的第一性原理:我们希望提高人与 AI 之间的交互效率。PS5 手柄是一种输入设备,耳机同样也可能成为输入设备。真正重要的不是把功能绑定在哪个按钮或动作上,而是能否降低用户表达意图、确认操作和感知 AI 状态的成本。

所以最初的问题不是:

“请帮我写一个读取陀螺仪的 Swift 程序。”

而是:

“我可不可以写一个第三方 Mac 程序,捕捉 AirPods 的摇头动作?如果可行,它是否能成为一种更高效的人机交互方式?”

这两个问题看起来接近,方向却完全不同。第一个问题已经默认了技术方案和数据一定存在;第二个问题先允许答案是“不行”,也允许调研后改变实现路线。

对零基础用户来说,这是第一条重要经验:

你不需要先知道技术答案,但要先说清楚自己想改变哪一种用户体验。

四、调研阶段:硬件存在,不等于第三方程序能够读取

硬件项目最容易掉进一个陷阱:产品页面写着“有传感器”,我们就自然认为程序可以读取它。

实际至少要连续问四个问题:

  1. 这项硬件能力是否真的存在?
  2. 操作系统是否使用了它?
  3. Apple 是否提供第三方公开接口?
  4. 公开接口给的是原始数据、处理后的数据,还是只有系统内部功能?

围绕这四个问题,我们先做了一轮硬件、平台接口与开源项目调研。

4.1 我们真正确认了什么

Apple 提供了公开的 CMHeadphoneMotionManager。对于支持动态头部追踪的耳机,macOS 14 及以后可以取得经过 Core Motion 处理的运动对象。应用可以关注姿态、旋转速率、重力、用户加速度和数据来源等信息。

但我们没有把它描述成“读取 AirPods 全部传感器”。原因很简单:

  • 姿态和角速度输出不等于直接读到了某个陀螺仪芯片的原始寄存器。
  • 耳机里的皮肤检测、光学检测、触控、麦克风和其他部件,并不都向第三方开放原始数据。
  • Apple 系统能够识别某些点头、摇头交互,不代表存在一个通用的第三方“摇头事件”接口。
  • 公开接口一次表现为一条运动数据流;左右耳由系统选择,并通过数据帧中的位置标记告诉应用当前来源。

这一步让项目从“我要读取所有传感器”收敛成一个可以验证的目标:

先取得公开运动数据,再判断它是否足以支撑头部交互。

4.2 心率支线为什么没有硬塞进 Mac 版本

后来我们发现新款 AirPods 具备心率能力,于是自然产生了新需求:“把心率也读出来。”

调研后的结论不是继续向当前 Mac 程序堆代码,而是先停下来确认平台边界:心率面向 iPhone/iPad 的 HealthKit 训练场景,Apple 的 HealthKit 设置文档明确标注该框架不适用于 macOS。于是我们只开始了 iPhone/iPad 伴侣应用原型,设想在移动端取得心率后通过本地连接发送给 Mac;它目前还没有完成真机集成,也没有进入 v0.2.0

这个看似“没有完成”的结果,恰好体现了方法论:

调研的价值不只是证明能做,也包括及时证明当前路线不该继续做。

五、开源项目调研:不是找一个仓库照抄

我们使用的关键词不是一个,而是按问题分组组合:

CMHeadphoneMotionManager macOS
AirPods head motion macOS
AirPods motion sensor GitHub
AirPods head tracker open source
CMDeviceMotion headphone sample
AirPods gesture recognition
head motion control desktop macOS

搜索结果中,最有价值的项目包括:

左右滑动查看完整表格

项目 它证明了什么 我们怎样使用
AirPodsPro-Motion-Sampler 运动数据可以展示、驱动界面并导出 下载源码,学习 API 生命周期和字段基线
HeadphoneMotion 最小的耳机运动采集链路可以成立 用来理解最小结构
AirPosture 运动数据可以进一步转化为行为判断和反馈 学习“数据→判断→反馈”的产品闭环
airtracker macOS 头部追踪可以桥接给其他应用 参考桌面事件桥接方向,不直接假设许可证和兼容性

我们最终实际下载并阅读了 AirPodsPro-Motion-Sampler。它帮助 AI 核实 CMHeadphoneMotionManager 的启动、权限说明、数据字段和用运动数据驱动 UI 的方式,但 EXP-004 的 macOS 界面、相对姿态、灵敏度、连接恢复和打包流程仍然重新实现。

开源调研的正确目标不是“找到代码复制”,而是回答三件事:

  • 这条路是否已经有人走通?
  • 哪些接口和工程结构可以复用认知?
  • 我们真正要解决的空白是什么?

对本项目来说,开源项目证明了采集可行;我们的空白是把它变成一个普通用户能看懂、能调节、能灰测并能真正控制电脑的头部输入工具。

六、把远目标拆成看得见的阶段目标

“用 AirPods 提高人与 AI 的交互效率”太大,不能直接交给 AI 一次完成。我们把它拆成五个阶段,每个阶段只回答一个主要问题。

左右滑动查看完整表格

阶段 当时的目标 用户能看到的验收结果
0. 可行性调研 确认公开接口、硬件边界和开源参考 得到有来源、有边界的调研结论
1. 数据可见 读取运动数据,并映射到十字靶小球 转头时数字刷新,小球跟着移动
2. 输入可用 修正方向、灵敏度、窗口布局和左右耳切换 动作方向符合直觉,小动作可调,窗口可用
3. 系统控制 增加“头部鼠标” 开关打开后能用头部移动系统光标
4. 内部交付 让同事能解压、打开、测试并反馈 Universal 2 ZIP、测试说明、真实解压复验

我们还没有进入的下一阶段,是录制动作样本、识别离散点头/摇头、建立置信度和冷却机制,再把经过验证的动作接入 AI 意图层。

阶段目标有一个共同特点:它们都能被普通用户直接观察。不要写“完成运动数据抽象层”,而要写“我向右转头时,小球向右走”;不要写“建立分发链路”,而要写“同事在另一台电脑解压后,能看到窗口并按说明反馈”。

七、方法的内环:目标、AI 实现、灰测

项目真正推进,靠的是不断重复下面三件事。

7.1 目标:每次只定义一个可观察结果

目标不是功能名,而是用户动作和系统结果。

一个合格的目标可以写成:

当我佩戴 AirPods 并启动程序时,屏幕显示实时运动数据。
以当前姿态归零后,向右转头时小球向右移动,抬头时小球向上移动。
X、Y 灵敏度可以分别调整,小幅动作也能让小球移动到十字边沿。

它同时包含了前提、动作、结果和验收方式。AI 不必猜“做完”是什么意思。

7.2 AI 实现:不只是生成代码,还要启动和验证

在我们的项目里,“AI 实现”至少包括:

  1. 阅读项目日志和已有文件。
  2. 根据目标提出最小技术方案。
  3. 参考官方接口和合适的开源项目。
  4. 修改代码和文档。
  5. 编译、启动程序。
  6. 检查窗口、关键控件、设备状态和实时数据。
  7. 增加能够防止问题复发的自动测试。
  8. 把真实结果和未完成事项写入项目日志。

如果 AI 只说“代码已经写好”,却没有启动、没有真实设备状态、没有窗口证据,那还不能算完成。

7.3 灰测:把真实人的一句话当成下一轮输入

灰测不是等所有功能完成后才进行的大测试。它发生在每一轮,而且往往只需要一句非常具体的反馈:

我向右转头,小球却向左移动。

好的灰测反馈最好包含四项:

我的动作:向右转头。
我预期看到:小球向右移动。
实际看到:小球向左移动。
环境:真实 AirPods 已连接,数据约 50 Hz,其他数值持续刷新。

这比“程序不好用”更能帮助 AI 定位问题。AI 可以据此判断是耳机坐标系与屏幕坐标方向相反,而不是传感器断开或界面卡住。

灰测不是项目的最后一道门;灰测是下一轮需求的生成器。

八、六次真实反馈,软件是怎样长出来的

下面这些不是虚构的产品故事,而是项目中实际发生过的反馈。它们展示了 VibeCoding 最有临场感的部分:用户说出现象,AI 根据证据修改,下一版立刻运行。

第一次:传感器真的有数据吗?

目标: 先看到数据,不先做动作分类模型。

AI 实现: 构建原生 macOS 应用,显示姿态、四元数、角速度、重力、用户加速度和十字靶;加入当前姿态归零与 X/Y 灵敏度。

灰测: 真实 AirPods 持续输出约 50 Hz 数据。X 轴灵敏度调到 后,大约 2.2° 的小幅偏转就能让圆球接近边沿。

结论: 第一阶段最重要的假设成立——公开运动流足以支撑连续头部控制。此时没有必要先训练机器学习模型。

第二次:“左右是反的”

用户反馈非常直接:

“我向右转头的时候,小球却往左侧移动。”

AI 检查后确认,当前 AirPods 的 yaw 符号与屏幕 X 方向相反。下一版只修改映射方向,并增加回归测试:AirPods 的负 yaw 应该变成屏幕正 X。

这一轮没有大改架构,却极其重要。硬件坐标系没有“符合人类直觉”的义务,方向必须通过真实动作校准。

第三次:“窗口放大,组件没有跟着放大”

程序能运行,不代表界面已经可用。用户把窗口放大后发现,左侧十字靶仍然维持固定大小。

AI 把固定高度改成响应式布局:左侧主要交互区域跟随窗口扩大,右侧数据和底部控制保持原尺寸。然后实际检查默认窗口和放大窗口,不只看代码约束。

这一轮说明,灰测不仅验证“功能有没有”,也验证信息是否以用户期望的方式呈现。

第四次:“为什么只采一只耳朵?左耳单独戴不能控制”

最初我们以为程序似乎只用了右耳。检查代码和数据后才发现,应用没有固定选择右耳;公开接口本来就只给出一条系统当前运动流,并在每一帧标记数据来自左耳还是右耳。

于是 AI 增加了耳侧显示和切换自动归零。但继续灰测时,只戴左耳仍然不能控制。真实界面显示系统仍把右耳当作当前来源。最终结论不是“继续写代码强制左耳”,因为公开 API 没有这个能力;正确恢复方法是开启 AirPods 自动入耳检测,把右耳放回充电盒并合盖,让系统把运动流切到左耳。

这是一轮非常典型的边界判断:有些问题应该改代码,有些问题应该修改设备设置,还有些问题公开接口根本不能绕过。

第五次:“同事电脑有进程,但没有窗口”

同事发来的 Console 日志里没有崩溃、权限拒绝或签名错误,反而显示进程已经运行、WindowServer 已建立绘制状态,但随后变成不可见状态。

AI 没有继续把问题归因于“同事不会打开”,而是回到窗口生命周期:旧版只在启动完成时显示一次窗口,没有处理重复打开、点击 Dock、最小化恢复、隐藏状态和不同桌面空间。

下一版统一了窗口显示流程,并为 Console 增加 EXP004_WINDOW_PRESENTED 诊断记录。真实界面检查确认窗口获得焦点,而不是只根据进程存在就宣布成功。

第六次:从“会动的小球”到“头部鼠标”

当连续二维控制稳定后,目标自然升级:增加一个开关,真正用头部移动鼠标。

AI 没有把头部角度直接硬映射到屏幕绝对位置,而是采用更容易控制的速率方式:回正停止,偏转越大移动越快;加入死区抑制自然抖动;每次开启自动归零;耳机断开时自动关闭。

测试不只检查开关文字。我们在正常用户会话里让光标移动 4 px,确认它到达目标后再恢复原位置;最终 ZIP 解压出的 Universal 2 应用又重复了一次测试。

随后 AI 生成原创图标、写入 .icns、构建 arm64 + x86_64 应用、打包 ZIP、重新解压并验证窗口、图标、数据、签名结构和测试说明。

九、一张“失败状态”截图,为什么也值得放进文章

AirPods 头部动作实验界面显示设备已断开,头部鼠标随连接中断自动关闭
AirPods 断开连接后,头部鼠标自动停止,避免指针继续漂移。

分享项目时,人们很容易只保留最漂亮、最成功的画面。但真实项目的可信度,往往来自它怎样处理失败:断开是否可见,危险功能是否自动停止,用户是否知道下一步做什么。

十、零基础复刻路线

下面的路线不要求你先学会 Objective-C。你需要做的是准备环境、把目标说清楚、允许 AI 操作项目文件,并亲自完成真实耳机测试。

10.1 准备条件

  • 一台运行 macOS 14 或更高版本的 Mac。
  • 一副支持动态头部追踪、能够连接这台 Mac 的 AirPods 或兼容 Beats 耳机。
  • Xcode Command Line Tools,或完整 Xcode。
  • 一个能够读取和修改本地项目、执行终端命令的 AI 编程工具。
  • 一个独立项目目录,以及持续记录过程的项目日志。

不要在一开始要求 AI 使用蓝牙逆向或私有协议。先坚持公开 Core Motion 路线,这更稳定,也更容易让其他人复刻。

如果 Mac 尚未安装 Command Line Tools,可以先在“终端”中执行:

xcode-select --install

系统会弹出安装窗口。你不需要理解编译器的细节,只需要知道后面的 AI 会用这些官方工具把源码构建成 .app

10.2 第一步:让 AI 先调研,不写代码

可以直接使用下面的提示词:

我想探索:能否在 macOS 上读取已佩戴 Apple 耳机的运动数据,识别头部动作,
并把它发展为控制鼠标或与 AI 交互的输入方式。

本轮只调研,不写程序。请完成:
1. 调研 Apple 耳机公开的硬件传感器和不同型号差异。
2. 区分“硬件存在”“系统内部使用”“第三方公开接口可读取”三种状态。
3. 核实 macOS 的公开 API、最低系统版本、权限和数据字段。
4. 搜索类似开源项目,记录平台、许可证、可复用部分和风险。
5. 明确哪些是处理后的运动数据,哪些原始数据拿不到。
6. 给出分阶段实验路线、成功标准和停止条件。

要求优先引用 Apple 官方资料和原始开源仓库,不要把推测写成事实。

10.3 第二步:只做第一阶段可视化原型

根据已经完成的调研,制作第一阶段 macOS 原型。

目标:佩戴兼容 AirPods 后,窗口实时显示 Core Motion 提供的姿态、旋转速率、
重力、用户加速度、数据来源和采样率。屏幕中央显示十字靶和圆球:左右转头控制 X,
抬头低头控制 Y;X/Y 灵敏度可以分别调整,并支持以当前姿态归零。

约束:
1. 使用 Apple 公开 API,不使用私有蓝牙协议。
2. 第一阶段不做机器学习和离散动作分类。
3. 必须包含 NSMotionUsageDescription、连接/断开状态和无设备提示。
4. 完成后自动编译、启动并检查真实窗口,不得只说代码已生成。
5. 增加方向、灵敏度和角度环绕的自动测试。
6. 区分自动测试结果与真实 AirPods 实测结果。

如果你想严格复刻本项目,可让 AI 采用原生 AppKit + Core Motion;但对初学者来说,技术名不是重点,重点是不要扩大第一阶段范围。

10.4 第三步:使用统一灰测反馈模板

每次体验后,把反馈写成下面的形式:

测试环境:Mac 型号、macOS 版本、耳机型号、连接方式。
我的动作:我实际做了什么。
预期结果:我认为软件应该怎样反应。
实际结果:屏幕、鼠标或耳机实际发生了什么。
可见证据:界面状态、数据来源、采样率、截图或日志。
请先判断根因,再决定需要改代码、改设备设置,还是接受平台边界;
修复后增加回归测试并重新启动让我验证。

不要只说“有 Bug”。真实动作和实际结果,是你提供给 AI 最有价值的数据。

10.5 第四步:再增加头部鼠标

在连续头部姿态控制已经稳定后,增加开关型“头部鼠标”。

要求:
1. 默认关闭,不保存开启状态。
2. 开启时按当前头部姿态自动归零。
3. 回正停止,偏转越大鼠标移动越快,并设置死区避免自然抖动造成漂移。
4. 左右和上下方向必须符合用户直觉。
5. AirPods 断开、重新检测或移动失败时自动关闭。
6. 不模拟点击,只使用公开接口移动光标。
7. 用数学单元测试验证死区、速度和方向;再做一次小距离真实光标移动并恢复原位的烟雾测试。

10.6 第五步:让 AI 按同事真实路径打包测试

完成代码后制作内部 macOS 测试包。

要求:
1. 同时构建 arm64 和 x86_64,生成 Universal 2 应用。
2. 生成原创应用图标并写入应用包。
3. ZIP 内同时放入应用和零基础同事测试说明。
4. 打包后重新解压到新目录,不要只验证打包前的应用。
5. 检查 ZIP 完整性、Info.plist、图标、两种架构、签名结构和内置测试。
6. 从解压目录实际启动应用,确认窗口、设备状态、关键控件和断开保护。
7. 明确区分 ad-hoc 内部签名与 Developer ID / Apple 公证;不要声称未公证应用可以普通双击无警告分发。
8. 输出 ZIP 路径、大小、SHA-256、已验证结果和未解决边界。

10.7 你怎样判断自己真的复刻成功

不要用“AI 说完成了”作为标准。至少亲自检查:

10.8 如果你直接拿到本项目源码

当前实验已经把构建和测试收敛成两个脚本。进入实验目录后执行:

cd "$PROJECT_DIR"
./tests/run_tests.sh
./build.sh --zip

第一个命令会编译程序并检查姿态映射、头部鼠标曲线、图标、两种 Mac 架构和签名结构;第二个命令生成内部测试 ZIP。

脚本通过仍然不能替代佩戴测试。构建完成后,继续按照上一节的清单戴上耳机、检查方向、开关、断开保护和解压后的程序。

十一、零基础用户在 VibeCoding 中真正负责什么

完全没有编程基础,不意味着你在项目里没有不可替代的职责。恰恰相反,你负责的几件事决定了项目会不会走向正确方向。

1. 定义体验,而不是假装懂技术

你可以不知道 yaw、四元数或 Core Graphics,但你知道“向右转头,鼠标应该向右”。把这个体验讲清楚,比随便指定一个技术方案更重要。

2. 提供真实世界

AI 能写测试,却不能替你戴上耳机。单耳佩戴、摘下、重新连接、窗口放大、换一台同事电脑,这些都属于真实世界输入。

3. 要求证据层级

同一句“通过”,可能代表完全不同的事情:

官方文档说明支持
  ≠ 开源项目代码看起来可行
  ≠ 自动测试通过
  ≠ 本机真实硬件通过
  ≠ 同事电脑解压后通过
  ≠ 可以正式公开分发

让 AI 每次说明证据来自哪一层,可以显著减少“看起来已经完成”的错觉。

4. 决定什么时候停止扩展

心率能力没有因为“耳机里有传感器”就被硬塞进 Mac 版本;离散动作识别没有因为“听起来更 AI”就抢在稳定运动采集之前实现。每轮只解决一个主要假设,项目反而走得更快。

十二、把这套方法带到其他硬件项目

AirPods 只是一个例子。这套方法同样适用于手柄、摄像头、麦克风、智能戒指、手表、机器人或任何新型输入设备:

  1. 从用户体验创意出发。
  2. 调研硬件能力、平台接口和开源行业现状。
  3. 把“存在”“可访问”“已验证”分开。
  4. 制定每轮都能被普通用户观察的阶段目标。
  5. 让 AI 完成实现、启动、自动检查和记录。
  6. 用真实硬件和真实用户灰测。
  7. 把反馈变成下一轮目标,而不是一次性堆功能。

最终,我们的方法可以再次压缩成一句话:

先产生创意,用调研把创意变成正确的阶段路线;再在每一个阶段里重复“目标、AI 实现、灰测”,直到真实世界愿意承认它可用。

这就是我们做出“头部鼠标”的过程。代码当然重要,但真正让项目从想法走到可运行软件的,是目标、证据和一轮又一轮真实反馈。