OpenStrap/analytics
GitHub: OpenStrap/analytics
基于已发表文献算法的可穿戴设备健康指标计算引擎,提供运动强度、睡眠、恢复等纯函数式确定性计算。
Stars: 1 | Forks: 3
# OpenStrap 分析
这就是具体的计算逻辑。给定一段按分钟记录的心率、运动和佩戴数据,它会
计算出你所关心的指标:你今天的运动强度有多大、睡眠质量如何、是否已经
恢复、训练负荷趋势如何。[后端](https://github.com/OpenStrap/backend) 会导入并在 cron 任务中运行它。就其本身而言,它只是一堆函数。
我想直接跟你说清楚它能做什么和不能做什么,因为它很容易被夸大。
这里的每一项内容都是已发表、经过同行评审的方法。使用 Banister 的 TRIMP 计算
运动强度。使用 Cole-Kripke 体动记录仪分析睡眠。使用 Keytel 方程式计算卡路里。还有睡眠
规律性指数。使用 ACWR 评估负荷。这些全都不是我自己发明的,没有使用任何神经网络,也不是
我在瞎猜 WHOOP 是怎么做的。我选择了文献中已有的方法,这样你
就可以去阅读相关论文,自己决定是否相信这些数值。
这就引出了最实在的一点:**这和 WHOOP 提供给你的数据一样吗?不。差得远。**
WHOOP 花费了数年和大量资金,在其云端和研究团队
的支持下,将传感器数据转化为恢复和
强度评分。而我只有每分钟的心率和一些教科书上的方程式。我计算出的结果是一个基于手环
实际输出数据构建的可靠近似值。它很有用,能正确反映趋势,并且会在你
恢复不足时提醒你。它不是他们的核心机密,我也不会假装它是。
## 一个数值如何知道该对自身赋予多少信任度
所有结果都返回相同的数据结构:
```
type Metric = T & {
confidence: number; // 0 to 1
tier: 'AUTH' | 'HIGH' | 'ESTIMATE' | 'RELATIVE';
inputs_used: string[]; // which inputs actually fed this
}
```
层级(tier)会告诉你这是一个什么样的数值。`AUTH` 表示它直接来自
设备。`HIGH` 表示它是经过测量并套用了可靠的已发表方法计算得出的。`ESTIMATE`
表示它是建模估算出来的,你应该把它当成一个大概的范围。`RELATIVE` 表示它只有在
与你的自身基准线相比时才有意义,皮温就是一个例子,它的绝对
数值毫无意义,但变化量是有意义的。
置信度是计算出来的。它主要取决于覆盖率(我是否获得了
足够的佩戴时长?)和完整度(我需要的输入项是否实际上都存在?)。如果你
只戴了四个小时而不是一整晚,置信度就会下降。如果某个指标需要
三个输入项但只得到了两个,它也会下降。
整个代码库遵循这样一条规则:**如果没有输入数据,结果就是 `null`,
并且置信度为 `0`。** 我绝不会用一个看似合理的猜测来填补数据空白。缺失的
数值依然保持缺失。一旦它开始捏造数据,其他所有结果的
可信度也就荡然无存了,所以它干脆不这么干。
## 每个文件计算的内容
| 函数 | 文件 | 功能描述 |
|----------|------|--------------|
| `calcRestingHR` | `resting.ts` | 整个睡眠窗口内心率的第 5 百分位数。如果尚未入睡,则回退到最安静的 30 分钟心率。 |
| `calcStrain` | `strain.ts` | 基于心率储备的 Banister TRIMP,将 `ratio·0.64·e^(1.92·ratio)` 按分钟求和,并压缩到 0–21 的范围内。 |
| `calcHrZones` | `zones.ts` | 根据最大心率的百分比,统计在五个心率区间内停留的时间(分钟)。 |
| `calcCalories` | `calories.ts` | Keytel (2005) 提出的每分钟活动卡路里方程式求和。男女使用不同的计算公式;如果在性别未知时,则取两者的平均值。 |
| `calcSleep` | `sleep.ts` | Cole-Kripke 根据体动情况对每个 Epoch(睡眠分段)进行清醒或睡眠评分,然后我结合夜间心率的下降趋势对其进行微调。得出入睡时间、醒来时间、睡眠效率以及 Beta 睡眠阶段的估算值。 |
| `calcSleepRegularity` | `regularity.ts` | 睡眠规律性指数,范围 0–100,根据你每天晚上就寝和起床时间的波动程度计算得出。 |
| `detectSessions` | `sessions.ts` | 检测锻炼时段:心率储备持续超过 40% 的时间段,然后将其大致分类为有氧运动、力量训练或步行。 |
| `calcHrRecovery` | `recovery.ts` | HRR60,即你在达到峰值后的一分钟内心率下降了多少。这是一个真正的健康指标。 |
| `calcLoad`, `calcFitnessTrend` | `trends.ts` | 使用 ACWR(近 7 天数据与近 28 天数据的比值)评估负荷,并通过静息心率和 HRR 的回归斜率来判断你的体能是否在提升。 |
| `calcReadiness`, `calcAnomaly` | `readiness.ts` | 根据静息心率偏差、睡眠负债和睡眠质量计算准备程度。此外还带有一个预警标志:“你的静息心率已经连续两天偏高,你是不是快生病了?” |
| `calcBaselines` | `baselines.ts` | 滚动计算的 30 天中位数,这是其他所有指标进行对比的基准锚点。 |
| `calcStress`, `classifyArousal` | `stress.ts` | 根据你在静止状态下心率高于静息心率的情况来评估唤醒度。如果你在运动,那就是锻炼而不是压力,因此系统会对运动状态进行屏蔽排除。 |
| `calcNocturnalHeart` | `nocturnal.ts` | 你的睡眠心率、最低点、相比白天心率的下降幅度,以及心率偏高时的预警标志。 |
| `buildCoach` | `coach.ts` | 一个简单的规则引擎。读取恢复情况和负荷,设定强度目标,并对一些建议进行排名。没有 AI,只是基于写死阈值的“如果发生 A 则执行 B”逻辑。 |
| `buildNotifications` | `notify.ts` | 决定哪些信息值得推送给你。最多不超过六条,按优先级排序,每条信息都有一个固定的 ID,确保你不会收到重复的提醒。 |
有几件事需要特别说明一下,以免你到处去找:
**这里没有 HRV,以后也不会有。** WHOOP 的恢复机制是基于心率变异性,即
心跳之间的时间间隔建立的。手环并没有通过网络将这项数据传给我们,至少
我目前无法通过任何方式恢复提取它。因此,这里的准备程度是基于静息
心率、睡眠负债和睡眠质量构建的,并且标签上已经明确说明了这一点。
`hrv.ts` 文件是故意留空的,以此来提醒我们所缺失的数据。
**最大心率**会进行平滑降级回退:如果我见过你在锻炼中的真实
峰值心率就用它;否则使用我观察到的最高心率;再不行就用 `220 − 年龄`;最后实在不行则默认 190。数据
来源越不可靠,依赖它的任何指标的置信度就越低。
## 测试
全都是纯函数。没有时钟、没有随机性、没有网络请求、没有数据库。相同的输入,
每次都会得到相同的输出。这意味着测试仅仅是输入预设数据然后断言输出结果,
完全不需要 mock 任何东西。
```
npm test # runs every module's test block
npm run typecheck
```
## 如果你想添加新的指标
编写一个函数,接收分钟数据(或历史记录)以及基准线和用户配置(profile),
然后返回一个 `Metric`。保持纯函数特性,不要产生副作用。像其他
指标一样,根据覆盖率和完整度推导置信度,当缺少输入数据时返回 `null` 且置信度为 `0`。在代码注释中写明你所使用的方法
名称,以便下一个人能够验证你的逻辑。另外,如果你的想法需要用到 HRV 或手环无法提供的
某些信号,那它就不属于这里,这就是底线。
标签:MITM代理, TypeScript, 健康监测, 可穿戴设备, 安全插件, 生物统计学, 睡眠分析, 纯函数, 自动化攻击, 运动数据分析