﻿---
title: "智能眼镜能看出瞳孔大小吗"
description: "研究用的眼动眼镜已经能按毫米导出瞳孔直径。顺着这个数往下追：镜框上的摄像头怎么看见瞳孔，斜着拍的像素怎么换成毫米，瞳孔为什么会变、哪一部分才和“想事”有关，走到街上怎么扣掉光照，以及为什么我们手上的 AI 眼镜拿不到这个数。"
pubDate: 2026-10-19
tags: ["智能眼镜", "眼动追踪", "瞳孔", "注意力", "可穿戴"]
hero: "./hero.svg"
refs:
  - id: neon-format
    title: Neon Data Format
    url: https://docs.pupil-labs.com/neon/data-collection/data-format/
    collect: "pupil-labs-neon-data-collection-data-format"
    site: Pupil Labs Docs
    note: "`3d_eye_states.csv` 的字段：瞳孔直径、眼球中心、光轴方向、眼睑开度"
  - id: neon-report
    title: "Neon Pupillometry Test Report: an evaluation of Neon's pupillometry feature"
    url: https://zenodo.org/records/10057185
    collect: "zenodo-records-10057185"
    author: Pfeffer T, Dierkes K
    site: Pupil Labs · Zenodo
    date: 2024-06-05
    note: "厂商白皮书，未经同行评审。每只眼 192×192 像素、200 Hz 红外摄像头；视角依赖 <5%，镜架位移 5 mm 几乎无影响"
  - id: invisible
    title: "Pupil Labs community chat: #invisible, September 2022"
    url: https://chat-archive.pupil-labs.com/logs/invisible/2022/09/
    collect: "pupil-labs-chat-archive-invisible-2022-09"
    site: Pupil Labs
    date: 2022-09-01
    note: "工作人员答复：Pupil Invisible 不输出瞳孔数据，因为摄像头角度太斜，2D 瞳孔检测不够稳定"
  - id: petersch
    title: Gaze-angle dependency of pupil-size measurements in head-mounted eye tracking
    url: https://pmc.ncbi.nlm.nih.gov/articles/PMC9046372/
    collect: "nih-pmc9046372"
    author: Petersch B, Dierkes K
    site: Behavior Research Methods 54(2)
    date: "2022"
    note: "长轴法、Świrski 3D 模型、带角膜折射的 3D 模型三种算法的视角误差对比"
  - id: hayes
    title: Mapping and correcting the influence of gaze position on pupil size measurements
    url: https://pmc.ncbi.nlm.nih.gov/articles/PMC4637269/
    collect: "nih-pmc4637269"
    author: Hayes TR, Petrov AA
    site: Behavior Research Methods 48(2)
    date: "2016"
    note: "桌面眼动仪 + 人造眼：注视位置带来的瞳孔大小误差，用余弦几何模型修正"
  - id: mathot
    title: "Pupillometry: psychology, physiology, and function"
    url: https://journalofcognition.org/articles/10.5334/joc.18
    collect: "journalofcognition-articles-10-5334-joc-18"
    author: Mathôt S
    site: Journal of Cognition 1(1)
    date: "2018"
  - id: marquart
    title: Workload assessment for mental arithmetic tasks using the task-evoked pupillary response
    url: https://peerj.com/articles/cs-16/
    collect: "peerj-articles-cs-16"
    author: Marquart G, de Winter J
    site: PeerJ Computer Science 1
    date: "2015"
    note: "引言：认知任务让瞳孔扩张最多约 0.5 mm，光照引起的最大变化约 6 mm（引自 Beatty & Lucero-Wagoner 2000）"
  - id: kahneman
    title: Attention and Effort
    author: Kahneman D
    site: Prentice-Hall
    date: "1973"
  - id: watson
    title: A unified formula for light-adapted pupil size
    author: Watson AB, Yellott JI
    site: Journal of Vision 12(10):12
    date: "2012"
    note: "综合七个旧公式，输入亮度、视场大小、年龄、单眼或双眼"
  - id: pignoni
    title: Accounting for effects of variation in luminance in pupillometry for field measurements of cognitive workload
    author: Pignoni G, Komandur S, Volden F
    site: IEEE Sensors Journal
    date: "2020"
    note: "场景视频 + 头戴亮度传感器 + 注视点，预测光照下的瞳孔，作为认知负荷的基线"
  - id: eckert
    title: Pupillary light reflex correction for robust pupillometry in virtual reality
    author: Eckert M, Robotham T, Habets E, Rummukainen OS
    site: Proceedings of the ACM on Computer Graphics and Interactive Techniques 5(2)
    date: "2022"
    note: "VR 里的个人亮度→瞳孔映射；注视区与背景加权最好，6 秒一档的校准序列最稳"
  - id: aria
    title: "Inside Aria Gen 2: explore the cutting-edge tech behind the device"
    url: https://www.meta.com/blog/aria-gen-2-research-glasses-under-the-hood-reality-labs/
    collect: "meta-blog-aria-gen-2-research-glasses-under-th"
    site: Meta
    date: 2025-06-04
  - id: raybandisplay
    title: Meta's new smart glasses come with a screen and AI
    url: https://www.ubergizmo.com/2025/09/meta-smart-glasses-screen-ai
    collect: "ubergizmo-2025-09-meta-smart-glasses-screen-ai"
    author: Paulo Montenegro
    site: Ubergizmo
    date: 2025-09-18
    note: "Meta Ray-Ban Display：单眼显示、一颗朝外的 12MP 摄像头、Neural Band 腕带操控"
  - id: apple
    title: Eyes, Hands & Privacy
    url: https://www.apple.com/legal/privacy/data/en/eyes-hands/
    collect: "apple-legal-privacy-data-en-eyes-hands"
    site: Apple
  - id: vive
    title: "Vizard reference: Vive Pro"
    url: https://docs.worldviz.com/vizard/latest/VivePro.htm
    collect: "worldviz-vizard-latest-vivepro-htm"
    site: WorldViz
    note: "Vive Pro Eye 的 `getPupilDiameter()` 返回每只眼的瞳孔直径（毫米）"
  - id: tobii
    title: Pupil diameter
    url: https://developer.tobiipro.com/commonconcepts/pupildiameter.html
    collect: "tobiipro-commonconcepts-pupildiameter-html"
    site: Tobii Pro Developer
    note: Tobii 报告的是瞳孔的物理大小（毫米），不是从外面看到的表观大小
---

下面是一个 CSV 文件的表头，来自 Pupil Labs 的眼动眼镜 Neon[^neon-format]：

```text
timestamp [ns], pupil diameter left [mm], pupil diameter right [mm],
eye ball center left x [mm], ..., eyelid aperture left [mm], eyelid aperture right [mm]
```

戴上它走一圈，导出的 `3d_eye_states.csv` 里每秒有 200 行，每一行都有左右两只眼的瞳孔直径，单位是毫米。

这让人有点意外。一副眼镜上的摄像头离眼睛只有两三厘米，还是从镜框边上斜着拍的，它怎么量得出毫米？我们平时说的“智能眼镜”，比如能拍照、能和 AI 说话的 Ray-Ban Meta，也能做到吗？就算量出来了，走在街上忽明忽暗，这个数又能说明什么？

上一篇讲注意力的文章最后提到，想像 HRV 一样戴着就测注意力，瞳孔是最好的候选，只是光照干扰让它离日常使用还远（见《[心跳越不规律越健康，那注意力呢](/blog/attention-elasticity/)》）。这一次我们就拿着这副眼镜，看看“远”到底远在哪里。

先交代几个后文反复用到的词。**瞳孔**是虹膜（眼睛里有颜色的那一圈）中间的圆孔，光从这里进入眼睛；它的大小由两块肌肉控制，一块收缩、一块扩张，都不受意识支配。**眼动追踪**是用摄像头算出眼睛在看哪里，**瞳孔测量**（pupillometry）则是量瞳孔有多大。两件事用的是同一套摄像头，但难度不一样。

## 一、眼镜怎样看见瞳孔

### 现象：大多数智能眼镜根本没有看眼睛的摄像头

先回答最直接的问题。Ray-Ban Meta 这一类 AI 眼镜，摄像头都朝外，用来拍照和让 AI “看见”你面前的东西。2025 年发布的 Meta Ray-Ban Display 加了一块单眼显示屏，公开的规格里仍然只有一颗朝外的 1200 万像素摄像头，操控靠手腕上读肌电的 Neural Band，而不是眼睛[^raybandisplay]。没有朝内的摄像头，就谈不上看瞳孔。

能看瞳孔的，是另一类设备：研究用的眼动眼镜（Pupil Labs Neon、Tobii Pro Glasses 3、Meta 的 Aria Gen 2），以及带眼动追踪的头显（Apple Vision Pro、HTC Vive Pro Eye）。它们的共同点是，镜框或镜片周围装着朝内的**红外摄像头**和**红外 LED**。

### 原理：在红外图像里找最暗的一块

为什么用红外？一是人眼看不见红外光，LED 一直亮着也不晃眼，暗处也能拍；二是在红外下，虹膜不管是什么颜色都比较亮，而瞳孔是一个通往眼底的洞，光照进去几乎不反射出来，于是成了整张图里**最暗的一块**。算法要做的，就是把这块暗区分割出来，沿边缘拟合一个椭圆。图像里另外还有一两个小亮点，那是 LED 在角膜表面的反光，追踪视线时会用到。

以 Neon 为例，每只眼一个红外摄像头，分辨率只有 192×192 像素，每秒 200 帧，每只眼配一个红外 LED[^neon-report]。

<figure class="dd dd-figure">
<div class="dd-scroll">
<svg class="dd-svg" viewBox="0 0 800 408" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="pg-cam-title pg-cam-desc">
<title id="pg-cam-title">眼镜上的眼摄像头怎样拍到瞳孔</title>
<desc id="pg-cam-desc">左边是侧面示意：眼摄像头和红外 LED 装在镜框下沿，LED 照亮眼睛，摄像头从下方斜着往上拍。右边是摄像头拍到的一帧 192×192 像素的红外图像，瞳孔是其中最暗的椭圆；下面列出 Neon 每只眼的摄像头参数和输出的瞳孔直径字段。</desc>
<defs>
<marker id="pg-cam-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head" d="M1 1 L7 4 L1 7"/></marker>
<marker id="pg-cam-arrow-acc" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head-acc" d="M1 1 L7 4 L1 7"/></marker>
</defs>
<rect class="dd-frame" width="800" height="408" rx="10"/>
<rect class="dd-zone" x="16" y="16" width="400" height="376" rx="8"/>
<text class="t-zone" x="32" y="44">侧面 · 镜框下沿的眼摄像头</text>
<rect class="dd-zone" x="432" y="16" width="352" height="376" rx="8"/>
<text class="t-zone" x="448" y="44">摄像头拍到的一帧</text>
<path class="dd-edge dd-dash" d="M348 304 L238 206" marker-end="url(#pg-cam-arrow)"/>
<path class="dd-edge-acc" d="M234 210 L300 298" marker-end="url(#pg-cam-arrow-acc)"/>
<circle class="dd-card" cx="160" cy="196" r="72"/>
<path class="dd-edge" d="M214 148 Q252 196 214 244"/>
<line class="dd-edge" x1="226" y1="170" x2="226" y2="222"/>
<line style="stroke:var(--dd-accent);stroke-width:5" x1="226" y1="186" x2="226" y2="206"/>
<text class="t-note" x="112" y="200">眼球</text>
<text class="t-note" x="236" y="160">瞳孔</text>
<rect class="dd-card" x="324" y="72" width="8" height="196" rx="3"/>
<text class="t-note" x="340" y="88">镜片</text>
<rect class="dd-card" x="272" y="300" width="56" height="28" rx="4"/>
<circle class="dd-edge" cx="300" cy="314" r="7"/>
<rect class="dd-key" x="340" y="304" width="24" height="20" rx="3"/>
<text class="t-note" x="264" y="348">眼摄像头</text>
<text class="t-note" x="336" y="348">红外 LED</text>
<text class="t-say" x="32" y="300">摄像头在下方斜着往上拍，</text>
<text class="t-say" x="32" y="320">平视时拍到的瞳孔也是椭圆</text>
<rect class="dd-card" x="448" y="64" width="160" height="160" rx="4"/>
<ellipse cx="528" cy="148" rx="54" ry="42" style="fill:var(--dd-line);fill-opacity:0.22"/>
<ellipse cx="528" cy="148" rx="22" ry="16" style="fill:var(--dd-accent);fill-opacity:0.85"/>
<ellipse class="dd-edge-acc dd-dash" cx="528" cy="148" rx="27" ry="21"/>
<circle cx="537" cy="141" r="3" style="fill:var(--dd-node)"/>
<text class="t-note" x="448" y="244">192 × 192 像素 · 红外</text>
<line class="dd-edge" x1="560" y1="148" x2="624" y2="148"/>
<text class="t-say" x="632" y="140">最暗的一块是瞳孔，</text>
<text class="t-say" x="632" y="160">虚线是拟合的椭圆</text>
<rect class="dd-card" x="448" y="268" width="320" height="110" rx="6"/>
<path class="dd-card-head" d="M448.6 300 V274 Q448.6 268.6 454 268.6 H762 Q767.4 268.6 767.4 274 V300 Z"/>
<line class="dd-edge" x1="448" y1="300" x2="768" y2="300"/>
<text class="t-name" x="464" y="289">Neon 眼摄像头</text>
<text class="t-mono" x="752" y="289" text-anchor="end">每只眼一套</text>
<text class="t-mono" x="464" y="317">分辨率 · 帧率</text><text class="t-val" x="752" y="317" text-anchor="end">192×192 px · 200 Hz</text>
<line class="dd-hair" x1="464" y1="326" x2="752" y2="326"/>
<text class="t-mono" x="464" y="343">照明</text><text class="t-val" x="752" y="343" text-anchor="end">红外 LED × 1</text>
<line class="dd-hair" x1="464" y1="352" x2="752" y2="352"/>
<text class="t-mono" x="464" y="369">输出字段</text><text class="t-val t-acc" x="752" y="369" text-anchor="end">pupil diameter left [mm]</text>
</svg>
</div>
<figcaption>图 1 · 眼镜看瞳孔靠的是朝内的红外摄像头：瞳孔在红外图像里是最暗的一块，先拟合出椭圆，再换算成毫米</figcaption>
</figure>

注意摄像头的位置：它装在镜框下沿，从下往上斜着拍。眼镜不可能把摄像头放在眼睛正前方，否则会挡住视线。所以即使我们平视前方，摄像头拍到的瞳孔也不是正圆，而是一个椭圆。

**拍到的是一个几十像素宽的椭圆，那它是怎么变成“4.6 毫米”的？**

## 二、从像素到毫米

### 现象：瞳孔没变，图像里的大小却变了

最直觉的办法是数像素：椭圆越大，瞳孔越大。可是同一个瞳孔，在图像里的大小会因为跟瞳孔无关的原因而变化。

- **眼球一转**，瞳孔这个圆片相对摄像头倾斜，投影出来的椭圆变扁。只按几何算，视线偏离摄像头轴 40° 时，短轴缩到 cos 40° ≈ 0.77 倍。
- **眼镜一滑**，摄像头离眼睛远了一点，整个瞳孔看上去就小了一圈。Neon 白皮书里，镜架沿视线方向移开 5 毫米，按像素算的瞳孔大小就下降了一成以上[^neon-report]。
- **角膜是一块透镜**。我们从外面看到的瞳孔，其实是隔着角膜看到的放大像，放大多少又跟观察角度有关。

这几种效应合称**瞳孔投影缩短**（pupil foreshortening）。它在桌面眼动仪上就已经很明显[^hayes]，在眼镜上更麻烦：摄像头离眼睛近、角度斜，人又在走动，眼镜会顺着鼻梁慢慢往下滑。白皮书举了一个例子：做一个持续几小时的疲劳实验，眼镜慢慢滑下去，表观瞳孔跟着变小，很容易被误读成“人疲劳了”[^neon-report]。

<figure class="dd dd-figure">
<div class="dd-scroll">
<svg class="dd-svg" viewBox="0 0 800 420" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="pg-fs-title pg-fs-desc">
<title id="pg-fs-title">同一个瞳孔在眼摄像头里的三种样子</title>
<desc id="pg-fs-desc">三栏都是直径 4.6 毫米的同一个瞳孔。视线正对摄像头时拍到正圆；视线偏离摄像头轴 40 度时，短轴缩到约 0.77 倍，变成椭圆；镜架沿视线方向外移 5 毫米时，整体缩小一成以上。</desc>
<defs>
<marker id="pg-fs-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head" d="M1 1 L7 4 L1 7"/></marker>
<marker id="pg-fs-arrow-acc" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head-acc" d="M1 1 L7 4 L1 7"/></marker>
</defs>
<rect class="dd-frame" width="800" height="420" rx="10"/>
<rect class="dd-zone" x="16" y="16" width="248" height="356" rx="8"/>
<text class="t-zone" x="32" y="44">视线正对摄像头</text>
<rect class="dd-card" x="60" y="64" width="160" height="160" rx="4"/>
<ellipse cx="140" cy="144" rx="54.0" ry="54.0" style="fill:var(--dd-line);fill-opacity:0.22"/>
<ellipse cx="140" cy="144" rx="24" ry="24" style="fill:var(--dd-accent);fill-opacity:0.85"/>
<circle class="dd-edge dd-dash" cx="140" cy="144" r="24"/>
<rect class="dd-card" x="32" y="244" width="216" height="110" rx="6"/>
<path class="dd-card-head" d="M32.6 276 V250 Q32.6 244.6 38 244.6 H242 Q247.4 244.6 247.4 250 V276 Z"/>
<line class="dd-edge" x1="32" y1="276" x2="248" y2="276"/>
<text class="t-name" x="48" y="265">拍到的瞳孔</text>
<text class="t-mono" x="48" y="293">真实直径</text><text class="t-val t-acc" x="232" y="293" text-anchor="end">4.6 mm</text>
<line class="dd-hair" x1="48" y1="302" x2="232" y2="302"/>
<text class="t-mono" x="48" y="319">形状</text><text class="t-val" x="232" y="319" text-anchor="end">圆</text>
<line class="dd-hair" x1="48" y1="328" x2="232" y2="328"/>
<text class="t-mono" x="48" y="345">表观大小</text><text class="t-val" x="232" y="345" text-anchor="end">基准</text>
<rect class="dd-zone" x="276" y="16" width="248" height="356" rx="8"/>
<text class="t-zone" x="292" y="44">视线偏离摄像头 40°</text>
<rect class="dd-card" x="320" y="64" width="160" height="160" rx="4"/>
<ellipse cx="400" cy="144" rx="54.0" ry="41.4" style="fill:var(--dd-line);fill-opacity:0.22"/>
<ellipse cx="400" cy="144" rx="24" ry="18.4" style="fill:var(--dd-accent);fill-opacity:0.85"/>
<circle class="dd-edge dd-dash" cx="400" cy="144" r="24"/>
<rect class="dd-card" x="292" y="244" width="216" height="110" rx="6"/>
<path class="dd-card-head" d="M292.6 276 V250 Q292.6 244.6 298 244.6 H502 Q507.4 244.6 507.4 250 V276 Z"/>
<line class="dd-edge" x1="292" y1="276" x2="508" y2="276"/>
<text class="t-name" x="308" y="265">拍到的瞳孔</text>
<text class="t-mono" x="308" y="293">真实直径</text><text class="t-val t-acc" x="492" y="293" text-anchor="end">4.6 mm</text>
<line class="dd-hair" x1="308" y1="302" x2="492" y2="302"/>
<text class="t-mono" x="308" y="319">形状</text><text class="t-val" x="492" y="319" text-anchor="end">椭圆</text>
<line class="dd-hair" x1="308" y1="328" x2="492" y2="328"/>
<text class="t-mono" x="308" y="345">表观大小</text><text class="t-val" x="492" y="345" text-anchor="end">短轴 ≈ 0.77 倍</text>
<rect class="dd-zone" x="536" y="16" width="248" height="356" rx="8"/>
<text class="t-zone" x="552" y="44">镜架往外滑 5 mm</text>
<rect class="dd-card" x="580" y="64" width="160" height="160" rx="4"/>
<ellipse cx="660" cy="144" rx="47.2" ry="47.2" style="fill:var(--dd-line);fill-opacity:0.22"/>
<ellipse cx="660" cy="144" rx="21" ry="21" style="fill:var(--dd-accent);fill-opacity:0.85"/>
<circle class="dd-edge dd-dash" cx="660" cy="144" r="24"/>
<rect class="dd-card" x="552" y="244" width="216" height="110" rx="6"/>
<path class="dd-card-head" d="M552.6 276 V250 Q552.6 244.6 558 244.6 H762 Q767.4 244.6 767.4 250 V276 Z"/>
<line class="dd-edge" x1="552" y1="276" x2="768" y2="276"/>
<text class="t-name" x="568" y="265">拍到的瞳孔</text>
<text class="t-mono" x="568" y="293">真实直径</text><text class="t-val t-acc" x="752" y="293" text-anchor="end">4.6 mm</text>
<line class="dd-hair" x1="568" y1="302" x2="752" y2="302"/>
<text class="t-mono" x="568" y="319">形状</text><text class="t-val" x="752" y="319" text-anchor="end">小一圈的圆</text>
<line class="dd-hair" x1="568" y1="328" x2="752" y2="328"/>
<text class="t-mono" x="568" y="345">表观大小</text><text class="t-val" x="752" y="345" text-anchor="end">缩小一成以上</text>
<text class="t-say" x="32" y="400">虚线圈是视线正对摄像头时的大小。瞳孔本身没变，变的是它投到图像上的影子。</text>
</svg>
</div>
<figcaption>图 2 · 眼球一转、眼镜一滑，同一个瞳孔在图像里就变了样；第二栏是按几何算的，第三栏取自 Neon 白皮书的实测</figcaption>
</figure>

### 第一次尝试：量椭圆的长轴

既然眼球转动只会把圆压扁，那么取椭圆的**长轴**，不就绕开了吗？圆片倾斜时，和转轴平行的那条直径长度不变。

可惜这只在没有角膜、摄像头离得很远的理想情况下成立。Pupil Labs 的 Petersch 和 Dierkes 用光线追踪模拟了头戴式眼动仪的成像：视线偏离到约 50° 时，长轴法会把瞳孔低估最多 7.5%[^petersch]。

### 第二次尝试：先重建眼球，再求瞳孔

更进一步的办法是**3D 眼球模型**。眼球在转，但眼球中心在眼眶里基本不动；从一段视频里的许多个椭圆，可以反推出眼球中心的位置，再把每一帧的椭圆还原成三维空间里一个真实的圆，它的直径就是瞳孔大小。这是 Świrski 提出的方法，开源的 Pupil Core 一直在用。

但它忽略了角膜折射，结果反而**高估**，视角越大高估越多。把角膜折射也算进去（用标准眼模型的四个参数：眼球半径、虹膜半径、角膜曲率半径和折射率），人群平均的误差能压到 ±1%；落到具体某个人身上，仍然有 2.5%–4% 的偏差，因为每个人的眼睛和“标准眼”不完全一样[^petersch]。

<figure class="dd dd-figure dt-figure">
<div class="dt-panel">
<div class="dt-title" id="pg-algo-title">三种算法在视角变大时的偏差（头戴式眼动仪，光线追踪仿真）</div>
<div class="dt-scroll">
<table class="dt" aria-labelledby="pg-algo-title">
<colgroup><col style="width:24%"><col><col class="hl"></colgroup>
<thead>
<tr><th scope="col">算法</th><th scope="col">怎么算</th><th scope="col" class="acc">视线偏到约 50° 时</th></tr>
</thead>
<tbody>
<tr><th scope="row">椭圆长轴</th><td>直接取图像里椭圆的长轴</td><td class="bad">低估，最多 7.5%</td></tr>
<tr><th scope="row">3D 眼球模型</th><td>从多帧椭圆反推眼球中心，再还原成三维的圆</td><td class="bad">高估，个体 3%–7.5%</td></tr>
<tr><th scope="row">3D 模型 + 角膜折射</th><td>再用标准眼的四个参数修正折射</td><td class="good">人群平均 ±1%，个体仍差 2.5%–4%</td></tr>
</tbody>
</table>
</div>
<p class="dt-note">数据来自 Petersch 与 Dierkes 2022，视角用瞳孔椭圆的圆度 0.4 换算。</p>
</div>
<figcaption>图 3 · 不把角膜折射算进去，瞳孔大小会随视线方向系统性地偏</figcaption>
</figure>

### 解决：让网络直接学一个“个人化”的眼球

Neon 在这条路上又走了一步：用一个叫 NeonNet 的神经网络直接从眼图估计三维眼球状态，并约束它和一个个人化的、带光学参数的眼球模型一致，不需要用户先校准[^neon-report]。按白皮书的测试，水平方向 ±40° 内，人群平均的视角误差小于 5%；垂直方向 ±15° 内也小于 5%；镜架移开 5 毫米，几乎没有影响。

这也解释了为什么上一代产品 Pupil Invisible 干脆**不输出瞳孔大小**。它的摄像头装在镜腿根部，角度更斜，Pupil Labs 的工作人员在社区里的答复是：“因为摄像头角度，2D 瞳孔检测不够稳定，建不出好的 3D 眼模型”[^invisible]。同一家公司、同样是眼镜，测视线可以，测瞳孔不行，难点就在这一步。

需要说明的是，Neon 的数字来自厂商自己的白皮书，没有经过同行评审。Tobii Pro Glasses 3 也说自己报告的是瞳孔的物理大小（毫米），而不是从外面看到的表观大小[^tobii]，但没有公开同样细的误差数据。

**误差压到 5% 以内，对 4.6 毫米的瞳孔来说约是 0.2 毫米。这够不够用？要看我们想测的那个变化有多大。**

## 三、瞳孔为什么会变

### 光照：瞳孔最主要的工作

瞳孔首先是一个光圈。它的直径大约在 2 到 8 毫米之间变化，进光量相差十几倍[^mathot]。从暗处走到太阳下，瞳孔会在一秒左右缩到很小，这叫**瞳孔光反射**。光照能带来的变化最大约 6 毫米[^marquart]。

除此之外还有两种跟“想事”无关的变化。一是**近反射**：看近处时，双眼向内聚、晶状体变凸，瞳孔也会跟着缩小，让近处的东西看得更清楚。二是**自发波动**：即使光照不变、人不动，瞳孔也在一直轻微地变，其中有一个约每 3–5 秒一次的节律，叫瞳孔震颤（hippus）。

### 想事：小，但很稳定

在光照不变的条件下，瞳孔还会随“心里的事”变化。Kahneman 在 1973 年的《注意与努力》里就拿瞳孔扩张当作“心理努力”的主要指标：记的数字越多，瞳孔越大[^kahneman]。上一篇文章里讲过，这背后是脑干里的蓝斑：它释放去甲肾上腺素调节警觉程度，瞳孔大小和它的活动同步。

但这个变化很小。做心算这类费脑的任务时，瞳孔最多扩张约 0.5 毫米[^marquart]。把几种变化放在同一把尺子上，就能看出问题在哪：

<figure class="dd dd-figure">
<div class="dd-scroll">
<svg class="dd-svg" viewBox="0 0 800 300" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="pg-mag-title pg-mag-desc">
<title id="pg-mag-title">瞳孔直径几种变化的量级</title>
<desc id="pg-mag-desc">同一把毫米尺上：瞳孔能在 2 到 8 毫米之间变化；光照最多能让它变约 6 毫米；想事带来的扩张最多约 0.5 毫米；测量误差按 5% 算约 0.2 毫米。</desc>
<defs>
<marker id="pg-mag-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head" d="M1 1 L7 4 L1 7"/></marker>
<marker id="pg-mag-arrow-acc" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head-acc" d="M1 1 L7 4 L1 7"/></marker>
</defs>
<rect class="dd-frame" width="800" height="300" rx="10"/>
<rect class="dd-zone" x="16" y="16" width="768" height="268" rx="8"/>
<text class="t-zone" x="32" y="44">瞳孔直径的几种变化 · 同一把毫米尺</text>
<line class="dd-edge" x1="168" y1="236" x2="760" y2="236"/>
<line class="dd-edge" x1="168" y1="236" x2="168" y2="242"/>
<text class="t-note" x="168" y="258" text-anchor="middle">0</text>
<line class="dd-edge" x1="242" y1="236" x2="242" y2="242"/>
<text class="t-note" x="242" y="258" text-anchor="middle">1</text>
<line class="dd-edge" x1="316" y1="236" x2="316" y2="242"/>
<text class="t-note" x="316" y="258" text-anchor="middle">2</text>
<line class="dd-edge" x1="390" y1="236" x2="390" y2="242"/>
<text class="t-note" x="390" y="258" text-anchor="middle">3</text>
<line class="dd-edge" x1="464" y1="236" x2="464" y2="242"/>
<text class="t-note" x="464" y="258" text-anchor="middle">4</text>
<line class="dd-edge" x1="538" y1="236" x2="538" y2="242"/>
<text class="t-note" x="538" y="258" text-anchor="middle">5</text>
<line class="dd-edge" x1="612" y1="236" x2="612" y2="242"/>
<text class="t-note" x="612" y="258" text-anchor="middle">6</text>
<line class="dd-edge" x1="686" y1="236" x2="686" y2="242"/>
<text class="t-note" x="686" y="258" text-anchor="middle">7</text>
<line class="dd-edge" x1="760" y1="236" x2="760" y2="242"/>
<text class="t-note" x="760" y="258" text-anchor="middle">8</text>
<text class="t-note" x="760" y="274" text-anchor="end">毫米</text>
<text class="t-name" x="32" y="79">瞳孔能变的范围</text>
<rect class="dd-card" x="316" y="64" width="444" height="20" rx="3"/>
<text class="t-val" x="328" y="79">2–8 mm</text>
<text class="t-name" x="32" y="119">光照最多带来</text>
<rect class="dd-card" x="168" y="104" width="444" height="20" rx="3"/>
<text class="t-val" x="180" y="119">约 6 mm</text>
<text class="t-name" x="32" y="159">想事最多带来</text>
<rect style="fill:var(--dd-accent);fill-opacity:0.85" x="168" y="144" width="37.0" height="20" rx="3"/>
<text class="t-val t-acc" x="215.0" y="159">约 0.5 mm</text>
<text class="t-name" x="32" y="199">测量误差 5%</text>
<rect class="dd-derived" x="168" y="184" width="17.0" height="20" rx="3"/>
<text class="t-val" x="195.02" y="199">约 0.2 mm</text>
<text class="t-say" x="432" y="164">暗室里光照不变，0.5 mm 比误差大一倍；</text>
<text class="t-say" x="432" y="184">走到室外，光照带来的变化是它的十几倍。</text>
<text class="t-note" x="432" y="204">虚线框：Neon 白皮书测得的视角误差上限</text>
</svg>
</div>
<figcaption>图 4 · 想事让瞳孔变的那一点，比测量误差大，却远小于光照带来的变化</figcaption>
</figure>

### 暗室里，眼镜已经测得出来

所以第二节的问题有了答案：在光照不变的暗室里，约 0.2 毫米的误差比 0.5 毫米的信号小，眼镜够用。Neon 白皮书在暗室里复现了三个经典结果：瞳孔震颤的频率在 0.18–0.26 Hz 之间；屏幕闪亮后约 0.22–0.27 秒瞳孔开始收缩；做 n-back 记忆任务（看一串字母，判断当前字母是否和 N 个之前的相同，N 越大越费脑）时，8 名受试者全都在最难的 2-back 时瞳孔最大[^neon-report]。

可是，一旦走出暗室，光照的变化就是这个信号的十几倍。走过一片树荫、抬头看一眼窗外、手机屏幕亮一下，都足以淹没“想事”带来的那一点扩张。

**那能不能把光照的那一部分算出来，从测得的瞳孔里减掉？**

## 四、扣除光照的影响

### 第一次尝试：只在光照稳定时用

最简单的办法是绕开问题：只在光照不变的时候看瞳孔。实验室就是这么做的，暗室、固定屏幕亮度、下巴托。可如果只能在暗室里用，眼镜就失去了“戴着就测”的意义。

### 第二次尝试：先预测光照该让瞳孔多大

更积极的思路是：既然光照对瞳孔的影响有规律，就先根据看到的亮度**预测**一个瞳孔大小，再看实际测得的比它大了多少。

预测的公式早就有人总结过。Watson 和 Yellott 2012 年综合了七个旧公式，给出一个统一公式，输入亮度、视野里亮区的大小、年龄、单眼还是双眼受光，输出瞳孔直径[^watson]。问题在于眼镜上怎么知道“看到的亮度”。

- **用场景摄像头和亮度传感器。** 挪威的 Pignoni 等人在眼动仪上加了一个头戴亮度传感器，结合第一视角视频和注视点，估计“光照下应有的瞳孔”，把它当作认知负荷的基线[^pignoni]。这套方法在实验室的受控画面里验证过，后来被同一团队用在直升机驾驶和舰船航行演练中。
- **用渲染出来的画面。** 在 VR 头显里，画面每个像素有多亮是已知的。Eckert 等人在 VR 里给每个人做一条“亮度→瞳孔”的个人曲线：先用 6 秒一档、顺序打乱的灰屏校准，再用注视区和背景的加权亮度去查这条曲线，效果最好[^eckert]。

把这些拼起来，就是眼镜上扣除光照的大致流程：

<figure class="dd dd-figure">
<div class="dd-scroll">
<svg class="dd-svg" viewBox="0 0 800 448" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="pg-pipe-title pg-pipe-desc">
<title id="pg-pipe-title">从测得的瞳孔里扣掉光照</title>
<desc id="pg-pipe-desc">左边是眼镜上的传感器：场景摄像头和环境光传感器估出注视点的亮度，再经过每个人校准过的亮度到瞳孔的映射，得到光照该有的直径；眼摄像头经过 3D 眼模型得到实际测得的直径。两者相减的残差，才留给认知、唤醒和困倦。看近处带来的瞳孔收缩要靠双眼辐辏估出距离再修正，这一路在眼镜上还没有验证。</desc>
<defs>
<marker id="pg-pipe-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head" d="M1 1 L7 4 L1 7"/></marker>
<marker id="pg-pipe-arrow-acc" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head-acc" d="M1 1 L7 4 L1 7"/></marker>
</defs>
<rect class="dd-frame" width="800" height="448" rx="10"/>
<rect class="dd-zone" x="16" y="16" width="200" height="416" rx="8"/>
<text class="t-zone" x="32" y="44">眼镜上的传感器</text>
<rect class="dd-zone" x="232" y="16" width="328" height="416" rx="8"/>
<text class="t-zone" x="248" y="44">估算</text>
<rect class="dd-zone" x="576" y="16" width="208" height="416" rx="8"/>
<text class="t-zone" x="592" y="44">结果</text>
<path class="dd-edge" d="M200 90 H216 a8 8 0 0 1 8 8 V104 a8 8 0 0 0 8 8 H248" marker-end="url(#pg-pipe-arrow)"/>
<path class="dd-edge" d="M200 146 H216 a8 8 0 0 0 8 -8 V132 a8 8 0 0 1 8 -8 H248" marker-end="url(#pg-pipe-arrow)"/>
<path class="dd-edge" d="M396 136 V178" marker-end="url(#pg-pipe-arrow)"/>
<path class="dd-edge" d="M544 196 H592" marker-end="url(#pg-pipe-arrow)"/>
<path class="dd-edge" d="M200 272 H248" marker-end="url(#pg-pipe-arrow)"/>
<path class="dd-edge" d="M544 272 H592" marker-end="url(#pg-pipe-arrow)"/>
<path class="dd-edge dd-dash" d="M116 290 V326 a8 8 0 0 0 8 8 H248" marker-end="url(#pg-pipe-arrow)"/>
<path class="dd-edge dd-dash" d="M544 334 H632 a8 8 0 0 0 8 -8 V290" marker-end="url(#pg-pipe-arrow)"/>
<path class="dd-edge-acc" d="M768 196 H772 a8 8 0 0 1 8 8 V384 a8 8 0 0 1 -8 8 H768" marker-end="url(#pg-pipe-arrow-acc)"/>
<path class="dd-edge-acc" d="M712 290 V364" marker-end="url(#pg-pipe-arrow-acc)"/>
<rect class="dd-card" x="32" y="72" width="168" height="36" rx="6"/>
<text class="t-name" x="48" y="95">场景摄像头</text>
<text class="t-mono" x="184" y="95" text-anchor="end">30 Hz</text>
<rect class="dd-card" x="32" y="128" width="168" height="36" rx="6"/>
<text class="t-name" x="48" y="151">环境光传感器</text>
<rect class="dd-card" x="32" y="254" width="168" height="36" rx="6"/>
<text class="t-name" x="48" y="277">眼摄像头 ×2</text>
<text class="t-mono" x="184" y="277" text-anchor="end">200 Hz</text>
<rect class="dd-card" x="248" y="100" width="296" height="36" rx="6"/>
<text class="t-name" x="264" y="123">注视点亮度</text>
<text class="t-mono" x="528" y="123" text-anchor="end">注视区 + 背景加权</text>
<rect class="dd-card" x="248" y="178" width="296" height="36" rx="6"/>
<text class="t-name" x="264" y="201">亮度→瞳孔映射</text>
<text class="t-mono" x="528" y="201" text-anchor="end">每人校准一次</text>
<rect class="dd-card" x="248" y="254" width="296" height="36" rx="6"/>
<text class="t-name" x="264" y="277">3D 眼模型</text>
<text class="t-mono" x="528" y="277" text-anchor="end">带角膜折射</text>
<rect class="dd-derived" x="248" y="316" width="296" height="36" rx="6"/>
<text class="t-name" x="264" y="339">注视距离</text>
<text class="t-mono" x="528" y="339" text-anchor="end">双眼辐辏</text>
<rect class="dd-card" x="592" y="178" width="176" height="36" rx="6"/>
<text class="t-name" x="608" y="201">光照该有的</text>
<text class="t-mono" x="752" y="201" text-anchor="end">d_light</text>
<rect class="dd-card" x="592" y="254" width="176" height="36" rx="6"/>
<text class="t-name" x="608" y="277">实际测得</text>
<text class="t-mono" x="752" y="277" text-anchor="end">d_meas</text>
<rect class="dd-card" x="592" y="364" width="176" height="56" rx="6"/>
<text class="t-mono t-acc" x="608" y="386">d_meas − d_light</text>
<text class="t-note" x="608" y="408">留给认知、唤醒、困倦</text>
<text class="t-say" x="248" y="384">虚线：看近处瞳孔也会缩小，要先估出</text>
<text class="t-say" x="248" y="404">看多远再修正，这一路还没人在眼镜上验证</text>
</svg>
</div>
<figcaption>图 5 · 想从瞳孔读出“心里的事”，先要算出光照本该让它多大，再看测得的比它大了多少</figcaption>
</figure>

### 眼镜上难在哪里

这条流程在 VR 里比在眼镜上容易，原因是 VR 的光源完全由程序控制。换到眼镜上，至少有三处麻烦（下面三条是根据上述研究推断的，没有找到专门的验证）：

1. **场景摄像头不是测光表。** 它会自动调曝光，拍出来的明暗不等于真实亮度；要么锁定曝光、做亮度标定，要么另加一个校准过的环境光传感器。Aria Gen 2 就带了这样一个传感器，还能用紫外通道分辨室内和室外[^aria]。
2. **瞳孔看的是注视点附近。** 同一个场景，盯着窗户和盯着桌面，进眼的光差得很远，所以必须用眼动追踪的注视点去取亮度，而不是整张画面的平均值。
3. **看远近也要扣。** 近反射会让瞳孔缩小，需要用两只眼的视线交点（辐辏点）估出注视距离再修正。Aria Gen 2 的眼动输出里正好有辐辏点[^aria]，但还没见到有人把它用于瞳孔校正。

所以这个问题目前只有一个保守的答案：技术路线是清楚的，从暗室走到真实街道的那一步，目前还停在实验室和 VR 里。

**就算这些都能做，为什么我们手上的眼镜还是拿不到这个数？**

## 五、手上的眼镜为什么拿不到这个数

把前面提到的设备排在一起，答案分成两类：要么没有看眼睛的摄像头，要么有，但数据不给你。

<figure class="dd dd-figure dt-figure">
<div class="dt-panel">
<div class="dt-title" id="pg-dev-title">几类设备能不能给出瞳孔直径</div>
<div class="dt-scroll">
<table class="dt" aria-labelledby="pg-dev-title">
<colgroup><col style="width:28%"><col><col><col class="hl"></colgroup>
<thead>
<tr><th scope="col">设备</th><th scope="col">朝内的眼摄像头</th><th scope="col">瞳孔直径</th><th scope="col" class="acc">谁能拿到</th></tr>
</thead>
<tbody>
<tr class="dt-group"><th colspan="4" scope="rowgroup">消费级 AI 眼镜</th></tr>
<tr><th scope="row">Ray-Ban Meta、Meta Ray-Ban Display</th><td class="bad">没有</td><td colspan="2">无从谈起</td></tr>
<tr class="dt-group"><th colspan="4" scope="rowgroup">研究用眼动眼镜</th></tr>
<tr><th scope="row">Pupil Labs Neon</th><td>每只眼 1 个，200 Hz</td><td class="good">毫米</td><td>买设备的研究者</td></tr>
<tr><th scope="row">Pupil Labs Invisible</th><td>有，角度很斜</td><td class="bad">不输出</td><td>—</td></tr>
<tr><th scope="row">Tobii Pro Glasses 3</th><td>有</td><td class="good">毫米</td><td>买设备的研究者</td></tr>
<tr><th scope="row">Meta Aria Gen 2</th><td>有</td><td class="good">输出</td><td>申请通过的研究者</td></tr>
<tr class="dt-group"><th colspan="4" scope="rowgroup">头显</th></tr>
<tr><th scope="row">HTC Vive Pro Eye</th><td>有</td><td class="good">毫米</td><td>开发者（SDK 接口）</td></tr>
<tr><th scope="row">Apple Vision Pro</th><td>有</td><td>系统内部使用</td><td class="bad">不给 App</td></tr>
</tbody>
</table>
</div>
<p class="dt-note">Ray-Ban 两款按公开规格整理，规格里只有朝外的摄像头。</p>
</div>
<figcaption>图 6 · 能测瞳孔的设备都在研究者手里，消费级产品要么没装摄像头，要么不开放数据</figcaption>
</figure>

**为什么消费级眼镜不装？** 公开资料里没有厂商的解释，以下是推断。一副要整天戴的眼镜，重量、续航和外观都很紧张；朝内的摄像头和 LED 要一直工作，还要实时跑眼模型，耗电不小。对这类眼镜现在的主要用途（拍照、听歌、问 AI），看视线和瞳孔的收益不足以抵消这些成本。

**为什么装了也不给？** Vision Pro 有一整套 LED 和红外摄像头盯着眼睛，但 Apple 的隐私说明写得很直白：“你的眼睛输入不会分享给 App、网站或 Apple”[^apple]，App 只知道你最后用手指捏合选中了什么。这和本文讲的瞳孔直接相关：如果瞳孔能反映认知负荷和唤醒程度，那么一个能实时读瞳孔的广告或应用，就能知道你看到什么时心里一动。反过来，Vive Pro Eye 这类面向开发者的头显，SDK 里直接有返回瞳孔直径的接口[^vive]。

## 六、回到注意力

现在可以回答上一篇留下的那个“远”了。想用眼镜像 HRV 一样被动地看注意力，要过三关：

- **有没有摄像头。** 消费级 AI 眼镜现在没有，研究用眼镜有。
- **量不量得准。** 带角膜折射的 3D 眼模型已经能把误差压到 5% 以内，暗室里足以测出 n-back 级别的认知负荷。这一关基本过了。
- **能不能扣掉光照。** 这是真正的距离。光照带来的变化是认知信号的十几倍，扣除的方法在 VR 和受控实验里可行，在真实街道上还没有被验证。

一个折中的想法是，像 HRV 的晨测一样，不追求全天连续，而是每天在同一个光照稳定的场景（比如固定的书桌前）测几分钟，比较自己不同日子、不同时段的变化。上一篇提到的瞳孔困倦测试就是这样做的：暗室里盯着看 11 分钟，困了以后瞳孔会出现缓慢的大幅波动。这只是推断，还没有见到有人用眼镜这样做。

## 小结

1. **智能眼镜能看到瞳孔吗？** 要有朝内的红外摄像头。研究用眼动眼镜和部分头显有，Ray-Ban Meta 这类消费级 AI 眼镜没有。
2. **斜着拍的像素怎么变毫米？** 眼球转动、眼镜滑动和角膜折射都会改变图像里的瞳孔大小。量长轴、3D 眼球模型都不够，要把角膜折射算进去，人群平均误差才压到约 ±1%，Neon 在真实佩戴下做到 5% 以内。
3. **瞳孔为什么会变？** 主要是光照（最多约 6 mm），还有看远近和自发波动；想事带来的扩张最多约 0.5 mm。暗室里误差比它小，眼镜已经测得出来。
4. **街上能不能扣掉光照？** 思路是先按注视点亮度预测光照该有的瞳孔，再看残差。VR 和实验室里可行，眼镜上还缺测光、注视点取亮度和近反射修正的验证。
5. **为什么拿不到？** 消费级眼镜因为重量、续航没装；装了的头显出于隐私不开放；能拿到数据的设备都在研究者手里。

## 参考资料
