Перейти к содержимому

Формулы метрик

Версия: 0.1 (v0) Дата: 2026-05-04 Аудитория: внутренний документ команды (продукт + разработка). Не предназначен для пользователя.

Этот документ — единая точка входа в логику расчётов Lumo. Сами формулы живут в screens/ рядом со своими экранами; здесь — рамка, которая их связывает: какие данные у нас уже есть, что требует персонального baseline, а что работает с первого дня, и откуда взяты веса.


1. Главный принцип — ярусы персонализации

Заголовок раздела «1. Главный принцип — ярусы персонализации»

В wearable-алгоритмах нет «правильного абсолютного значения HRV». RMSSD у разных людей отличается в 10 раз (20–200 мс — обе нормы), и интерпретация без личного baseline бессмысленна. Поэтому каждая метрика принадлежит к одному из трёх ярусов:

ЯрусКогда работаетЧто туда попадаетЧто показываем до
0. Абсолютные нормыДень 1HR-зоны (220 − возраст), SpO2 < 90% тревожно, температура > 37.5°C лихорадка, сон 7–9ч adult, цель 8000 шаговРаботает сразу
1. Персональный baseline14 дней ношенияHRV (RMSSD), RHR, кожная температура (отклонение), фоновый стресс, время отхода ко сну (хронотип)Плашка «Накапливаем данные» + абсолютная норма как fallback (см. §3)
2. Долгосрочный тренд30–90 днейЗдоровье, Lumo Age, кардио-трендыСкрыто полностью

Правило: каждая метрика в screens/ должна явно указывать свой ярус. Если ярус 1 или 2, обязателен cold-start UX — что показываем, пока baseline не сформирован.


Окна:

  • Recent window — 7 дней (свежее состояние)
  • Baseline window — 14 дней (личная норма)
  • Long-term window — 60 дней (для медленных метрик: Здоровье, Lumo Age)

Большинство «балансовых» метрик (HRV Balance, RHR vs baseline) сравнивают recent с baseline.

Минимум данных для запуска:

  • Ярус 1 (Energy, Stress, Battery): 14 ночей ношения с покрытием ≥ 70% времени сна
  • Ярус 2 (Здоровье): 10 полных дней для индикатора, 30 дней для Lumo Age

Что делаем при перерывах ношения:

  • < 3 дня без данных — продолжаем считать на старом baseline
  • ≥ 3 дня без данных — фризим обновление baseline до накопления свежих 7 дней
  • Полный сброс кольца / новый пользователь — пересчёт с нуля, плашка «Накапливаем данные, точность улучшится через N дней»

Fallback на ярус 0 для ярусов 1/2 в cold-start: используем популяционные нормы по возрасту/полу. Например, RHR ожидаемый = 60–80 для 25–45 лет; HRV-нормы по age-band. Это прокси-baseline на первые 14 дней — точность ниже, но лучше пустого экрана.


Расширение таблицы из product-spec §5 колонкой «вытащено в RingDebug». Источник реальности — RingDebugView.swift.

МетрикаSDK объектРежимВ RingDebug
Фазы снаQCSleepModelНочной batch✅ 3 дня
Пульс scheduledQCSchedualHeartRateModelКаждые 5 мин✅ 3 дня + расписание
Пульс real-timerealTimeHeartRateСтриминг✅ HR raw + processed
RHR (производная)из QCSchedualHeartRateModel ночьюНочной batch⚠️ данные есть, расчёт RHR — нет
HRV scheduledQCHRVModelДневной batch✅ 7 дней
HRV + RR on-demandQCRealOneKeyMeasureHeartRateModelПо запросу❌ не подключено
SpO2 scheduledQCBloodOxygenModelНочью + manual✅ raw + scheduled + 3 дня
ТемператураQCThreeValueTemperatureModelИнтервально✅ 3 датчика + расписание + 3 дня
Стресс (raw)QCStressModelИнтервально✅ 7 дней
Шаги / активностьQCSportModelBatch❌ не подключено
Шаги real-timecurrentStepInfoСтриминг❌ не подключено
ТренировкиQCExerciseModelПо событию❌ не подключено
СидячестьQCSedentaryModelПо событию❌ не подключено
ДавлениеQCBloodPressureModelScheduled + manual❌ не подключено
ГлюкозаQCBloodGlucoseModelScheduled + manual❌ не подключено

Следствие для v1: половина композитных формул упирается не в код, а в источник данных. Activity-компоненты в Energy / Battery (вес 0.05 / drain) и любые «вы много сидите» — сейчас не считаются. Это не блокер запуска (5% в Energy не критично), но в доке метрики должно стоять ⚠️ компонент недоступен — пересчитываем веса оставшихся к 1.0.


Все формулы держим у соответствующих экранов, чтобы не было двух источников правды. Здесь — обзор: ярус, источник весов, ссылка.

МетрикаГде формулаЯрусБазовая модель
Sleep Score06-sleep-detail.md0 + 1 (timing — 1)Oura Sleep Score (см. §8)
Energy07-energy-detail.md1 (требует HRV/RHR baseline)Oura Readiness contributors
Stress (фоновый)08-stress-detail.md1Garmin Body Battery / Polar Stress
Stress (on-demand)08-stress-detail.md0Baevsky SI (классика)
Battery (Lumo)04-battery-detail.md1 (через Energy + Stress)Garmin Body Battery
Здоровье05-health-detail.md2Oura Resilience
Lumo Age05-health-detail.md2Композит, см. §8

Что отдаём на экране 11-raw-metrics.md и какие пороги используем для алертов / окрашивания.

МетрикаЯрусАбсолютный порог (ярус 0)Персональный baseline (ярус 1)
HR (день)0< 40 brady, > 100 покой — алерт
HR (тренировка)0HR-зоны от max HR = 220 − возрастУточняем измеренным max за 60 дней
RHR (ночной)0 + 1< 40 / > 90 — алертΔ vs personal baseline (±5 bpm заметно)
HRV (RMSSD)1— (нет универсального порога)Δ vs personal 14-day baseline в %
SpO20< 90% тревожно, < 95% во сне — насторожитьсяΔ vs personal ночной baseline
Температура (кожа)1> 37.5°C — fever-flagΔ vs personal night baseline (±0.5°C — паттерн)

Правило подачи raw: для ярус-0 показываем абсолютное значение + цвет по порогу. Для ярус-1 показываем абсолютное значение, но интерпретация («норма / выше / ниже») — только после baseline.


Пользователь может вручную измерить через детальные экраны. Вопрос: использовать ли эти точки в композитных формулах или они только для самоощущения?

ИзмерениеИсточникИспользование в формулахИспользование в UI
Manual HRrealTimeHeartRate❌ нет (формулы используют scheduled и ночные)Точечный snapshot в карточке
Manual SpO2QCBloodOxygenModel (manual)❌ нет (Sleep Score использует ночной)Снапшот + история ручных замеров
Manual Stress (Baevsky)QCRealOneKeyMeasureHeartRateModel❌ не входит в фоновый Stress score (другая шкала)Отдельный snapshot «стресс прямо сейчас»
Manual HRV (RR)QCRealOneKeyMeasureHeartRateModel⚠️ кандидат: использовать как замену scheduled HRV в этот деньSnapshot + потенциальное обновление дневной HRV

Решение для v1: ручные измерения — отдельный канал, не вмешиваются в композитные скоры. Иначе один невозможный замер посреди дня сломает Energy/Battery.

Открыто: Manual HRV (RR) — единственный кандидат на интеграцию в формулы, потому что time-domain HRV из RR точнее scheduled. Решаем после первых пользователей.


Что видит пользователь на разных стадиях накопления данных:

ДеньЧто доступноЧто скрыто / fallback
1Sleep Score (если поспал в кольце), HR, SpO2, Температура с абсолютными порогамиEnergy / Stress / Battery / Здоровье — плашка «Накапливаем данные»
3+ первые тренды HR / SpO2 / SleepТе же
7+ ранний Sleep Score уверенный, ранний proxy Energy на population baselineStress / Battery — всё ещё плашка
14✅ Полный набор: Energy, Stress, Battery, baseline-зависимые rawЗдоровье — ещё нет
30✅ ЗдоровьеLumo Age — ещё нет
90✅ Lumo Age

Это в т.ч. ответ на вопрос продаж: «А когда уже будет польза от кольца?» — конкретные ответы по дням.


8. Веса формул — обоснование на референсах

Заголовок раздела «8. Веса формул — обоснование на референсах»

Текущие веса в формулах (Energy, Sleep Score, Здоровье, Lumo Age) — округлённые, требуют валидации на ≥100 пользователях после запуска. Базируются на следующих публичных решениях:

Sleep Score (наша формула — 7 компонентов). Опирается на Oura Sleep Score: те же 7 компонентов (Total / Efficiency / REM / Deep / Restfulness / Latency / Timing). Точные веса Oura не публикует, но в их whitepaper и help-центре указано, что Total Sleep и Efficiency — доминирующие. Наши 0.25 / 0.15 для них консистентны. WASO выделили отдельным компонентом (0.10) — клинический маркер фрагментации, отсутствует у Oura, но есть у медицинских стандартов (AASM).

Energy (наша формула — 5 компонентов: Sleep / HRV / RHR / Temp / Activity). Аналог Oura Readiness. Oura использует 8 contributors, мы свернули в 5 (объединили sleep score / sleep balance, выкинули recovery index, требующий ночных RR). Наш вес Sleep 0.40 близок к доминирующему вкладу сна у Oura (по их breakdown — крупнейший контрибьютор). HRV 0.30 + RHR 0.15 = 0.45 на сердечные сигналы — близко к Whoop Recovery, где HRV — основной фактор (~50%+).

Stress (фоновый). Путь Garmin Body Battery / Polar Stress: эвристика на HRV vs personal baseline + HR + исключение активного движения. Точные формулы Garmin/Polar закрыты (Firstbeat — проприетарный алгоритм), но принцип «HRV ниже baseline → стресс выше» документирован в их whitepapers. Наша реализация = тот же принцип.

Stress on-demand (Baevsky SI). Академическая формула из российской/советской космической медицины (Баевский Р.М., 1979–2000е). Используется в Polar OwnRelax, упоминается в современных исследованиях по HRV. Норма 80–150, рост ×1.5–10 при стрессе. Главное отличие от фонового: даёт точечный snapshot за 2–3 минуты, а не балансное значение.

Battery (Lumo). Аналог Garmin Body Battery: стартовое значение = утренний Energy, в течение дня drain от стресса/нагрузки, charge от отдыха. Наша формула проще (нет Firstbeat-математики), но архитектура совпадает.

Здоровье (14 дней). Аналог Oura Resilience, Whoop Recovery Trend, Polar Sleep Plus 7-day trend. Все строятся на 14–28-дневной агрегации тех же базовых сигналов. Наши веса (0.40 / 0.30 / 0.20 / 0.10) — приоритет Восстановления (HRV+RHR) > Стресса > Сна > Активности — соответствует логике Oura: устойчивость = в первую очередь автономная нервная система.

Lumo Age. Композит без прямого аналога. Reverse-fit к паспортному возрасту по 5 сигналам (см. 05-health-detail.md). Веса доминируют RHR (0.40) — научно обоснованно: RHR — самый сильный одиночный предиктор сердечно-сосудистого возраста (см. Cooper Clinic Longitudinal Study). HRV-trend (0.20) — второй валидированный сигнал. Остальные веса — рабочие, требуют калибровки на референсной когорте.


В формуле Батарейки drain(t) стоит «Стресс(t) высокий». Чтобы избежать рекурсии, фиксируем направление потока:

QCStressModel (raw, интервальный ряд из SDK)
HRV (RMSSD) baseline + HR + sedentary penalty
Stress score (0–100, карточка) ← это вход в Battery.drain
Battery (0–100)

Stress score (карточка, 0–100) — производная: пересчитывается из raw QCStressModel + HRV baseline + контекста. Battery использует уже готовый Stress score, не raw. Stress score в свою очередь не использует Battery — петли нет.

«Стресс(t) высокий» в формуле Батарейки = Stress score > 50 (порог = граница «Повышенный» из 08-stress-detail.md).


Сводка TBD из всех экранов, чтобы не искать по 7 файлам:

  • Веса формул — все текущие веса (0.40 / 0.30 / …) требуют калибровки на ≥100 пользователях после запуска. До этого — round-numbers по аналогии с Oura/Whoop.
  • Cold-start fallback на популяционные нормы — нужна таблица RHR/HRV ожидаемых по возрасту/полу. Источник: NHANES / Garmin user data tables / академические работы. Не сделано.
  • RHR-расчёт — сырые ночные HR в SDK есть, но extract минимума устойчивого окна (5+ мин низкого HR в глубоком сне) не реализован.
  • Manual HRV (RR) → замена дневной scheduled HRV — оставлено на v1.1 после первых пользователей.
  • PPG sample rate — для Cardio Age на морфологии waveform нужно ≥100 Hz. Запрос вендору не отправлен (см. 05-health-detail.md).
  • Dynamic порог «высокого стресса» для Battery — сейчас фиксируем 50 (граница «Повышенный»). Возможно стоит делать персональным (75-й перцентиль личного распределения за 14 дней).
  • Stress: активный vs сидячий — карточка различает их, но фоновый расчёт ещё не имеет источника движения (QCSportModel / QCSedentaryModel не подключены).

Версия: 0.1 Последнее обновление: 2026-05-04