AI-Підсчіт повторень вправ через камеру мобільного додатку
Підсчіт повторень—завдання, яке звучить просто, але ламається на граничних випадках. Користувач робить приседання повільно—одне повторення. Робить пульсуючі рухи наверху—модель рахує два. Телефон нахилили—координати змістилися, лічильник сбився.
Основа: Pose Estimation + аналіз часових рядів
Підхід той же, що в аналізі осанки: VNDetectHumanBodyPoseRequest (iOS) або ML Kit Pose Detection (Android) видають ключові точки скелету на кожному кадрі. Але для підсчітування повторень нас цікавить не статична поза, а рух ключевої точки по часу.
Для кожної вправи вибираємо tracking-точку:
| Вправа | Tracking-точка | Ось |
|---|---|---|
| Приседання | Бедро (leftHip / rightHip) | Y |
| Віджимання | Запястя або плечо | Y |
| Бицепс-кёрл | Запястя | Y |
| Берпі | Запястя + голова | Y комплексно |
| Выпад | Колено | Y |
class RepCounter {
private var positionHistory: [Double] = []
private var repCount: Int = 0
private var state: MovementState = .neutral
private let minAmplitude: Double = 0.08 // 8% висоти екрану
enum MovementState { case neutral, goingDown, bottom, goingUp, top }
func update(normalizedY: Double) {
positionHistory.append(normalizedY)
if positionHistory.count > 30 { positionHistory.removeFirst() }
let smoothed = positionHistory.suffix(5).reduce(0, +) / 5
detectRep(smoothedY: smoothed)
}
private func detectRep(smoothedY: Double) {
let baseline = positionHistory.prefix(10).reduce(0, +) / 10
let deviation = smoothedY - baseline
switch state {
case .neutral where deviation > minAmplitude:
state = .goingDown
case .goingDown where deviation < minAmplitude * 0.3:
state = .bottom
case .bottom where deviation > minAmplitude * 0.7:
state = .goingUp
case .goingUp where deviation < 0:
state = .top
repCount += 1
onRepCompleted?(repCount)
state = .neutral
default: break
}
}
}
Скользящее середнє по 5 кадрам (suffix(5))—сглаживання шуму pose estimation. Без нього лічильник дергається від дрожання ключевых точек між кадрами.
Калібровка під вправу
minAmplitude = 0.08—це відсоток від висоти екрану. Для приседань підходить, для бицепс-кёрла потрібно більше (рука рухається в широкому діапазоні), для віджимань—менше та інша ось.
Калібровку робимо або через аналітику (обучаємо на відео з розміченими повтореннями), або через адаптивний baseline: перші 3 секунди вправи—вимірюємо амплітуду руху, підстраховуємо пороги.
class AdaptiveRepCounter {
private var calibrationPhase = true
private var calibrationSamples: [Double] = []
private var dynamicAmplitude: Double = 0.05
func calibrate(y: Double) {
calibrationSamples.append(y)
if calibrationSamples.count >= 75 { // ~3 секунди при 25fps
let range = calibrationSamples.max()! - calibrationSamples.min()!
dynamicAmplitude = range * 0.4 // 40% від спостережуваної амплітуди
calibrationPhase = false
}
}
}
Орієнтація камери: боковой vs фронтальний вид
Одна й та же задача потребує різних рішень залежно від позиції камери:
Фронтальний вид (камера перед користувачем): видні обидва плеча, бедра, голова. Хорошо для приседань, выпадів, берпі. Координата Y бедра добре корелює з фазою приседання.
Боковой вид: видні колено, бедро, голеностоп у профіль. Краще для аналізу кута сгибання колена (техніка приседа), але вимагає додаткової підставки / штатива—неручно в реальних умовах.
Більшість додатків оптимізують під фронтальний вид з інструкцією "поставте телефон в 2 метрах перед собою на рівні талії".
Проблема: подібні вправи
Модель не знає, яку вправу виконує користувач. Якщо користувач вибрав "приседання", а робить выпад—лічильник помилиться. Рішення:
- Користувач явно вибирає вправу—просте рішення, працює
- Автоматичне розпізнавання вправи—окремої задачі класифікації
Для розпізнавання вправи користуємо time-series classifier: LSTM або 1D CNN на послідовності joint angles. Датасети: NTU RGB+D (120 класів дій), UCF101. Конвертуємо в CoreML/TFLite для on-device класифікації.
Обратна зв'язок та UI
Лічильник повторень—центральний елемент. Великий, контрастний, анімований. При кожному засчитаному повторенні: UIImpactFeedbackGenerator(style: .rigid) + візуальна вспышка лічильника.
Overlay поверх камери: показуємо skeleton (лінії між ключевыми точками). Допомагає користувачу зрозуміти, що модель його "бачить" та правильно рахує. Красний skeleton при поганій видимості (низька впевненість точок).
Процес розробки
Інтеграція pose estimation під платформу. Реалізація алгоритму детекції повторень для цільового набору вправ. Адаптивна калібровка. Camera UI з overlay skeleton. Тестування на реальних людях різної комплекції та на різних телефонах.
Орієнтири за часом
Підсчіт 3–5 базових вправ—1–2 тижні. Автоматичне розпізнавання вправи + повний трекинг тренування—3–5 тижнів.







