AI-Personalized Fitness: Adaptive Workouts Based on Biometrics

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1All 1566 services
AI-Personalized Fitness: Adaptive Workouts Based on Biometrics
Medium
~2-4 weeks
Frequently Asked Questions

AI Development Areas

AI Solution Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1319
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1226
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    927
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1161
  • image_logo-advance_0.webp
    B2B Advance company logo design
    621
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    896

AI-Personalized Fitness Programs: Adaptive Workouts Based on Biometrics

Standard fitness apps offer static plans that don't respond to the user's actual physiological state. The result: progress plateaus, overtraining, and injuries. We build AI systems that adapt load based on biometrics: HRV, heart rate, sleep quality, and training history. Our solutions increase adherence rates from 35–45% to 65–75% — a 1.7x improvement. Injury risk drops by 30–40% (Medicine & Science in Sports & Exercise). Personalized plans boost user LTV by 25–30% and reduce churn by 15–20%. We are a team of AI/ML engineers with 7 years of experience in sports physiology, with over 15 completed projects in workout personalization. Assess the implementation opportunity — contact us for a preliminary audit.

Why AI personalization outperforms static plans?

The key metric is not meeting a norm, but matching load to current recovery. Even the perfect plan becomes useless if the user has low HRV or poor sleep today. We deploy algorithms that compute a readiness score every morning (0–100). This score adjusts: workout type, intensity (via a load modifier), and recovery recommendations. This approach ensures smoother progress and reduces injury probability by 1.5x compared to static programs. Static plans typically achieve 40% adherence, while AI adaptation reaches 70% — 1.75x higher.

How does AI determine the optimal daily load?

The system considers four key factors: HRV, resting heart rate, sleep quality, and previous day's load. RecoveryMonitor computes a readiness score that directly influences training intensity. If the score is below 55 — only light recovery or rest is recommended. If above 75 — a heavy workout at 100% intensity is possible. Periodization is also applied: 3 weeks of increasing load, then a recovery week. Example: user wakes up with HRV 45ms (baseline 55ms), resting heart rate 62 (baseline 58), sleep 72 points, and had a heavy workout yesterday. RecoveryMonitor calculates: HRV dropped 18% → minus 25 points; RHR increased by 4 → minus 15 points; sleep 72 (above 60) → no penalty; high load yesterday → minus 10 points. Final readiness score = 50 — only light activity at 60% intensity.

Required data for personalization

Minimum set: 1–2 weeks of workout history and biometric indicators (HRV, resting heart rate, sleep) from a wearable tracker. If data is insufficient, we generate a profile based on anthropometry and goals, then calibrate it as real measurements come in. All major trackers are supported: Whoop, Oura, Apple Watch, Garmin, Polar.

Technical implementation: Python + Anthropic

Below is the real code for a plan generator we use in production systems. Core stack: Python 3.12, Anthropic Claude 3.5 (via Anthropic SDK), Pandas for analytics, NumPy for math.

import numpy as np
import pandas as pd
from dataclasses import dataclass
from typing import Optional
from anthropic import Anthropic
import json

@dataclass
class AthleteProfile:
    user_id: str
    age: int
    sex: str
    weight_kg: float
    height_cm: float
    fitness_level: str  # beginner, intermediate, advanced
    primary_goal: str   # weight_loss, muscle_gain, endurance, general_fitness
    available_days_per_week: int
    equipment: list    # ['dumbbells', 'barbell', 'pull_up_bar']
    injuries: list     # ['lower_back', 'knee']
    vo2max: Optional[float] = None

class FitnessPlanGenerator:
    """Generate and adapt training plan"""

    def __init__(self):
        self.llm = Anthropic()

    def calculate_training_zones(self, profile: AthleteProfile) -> dict:
        """Heart rate zones for cardio workouts"""
        # Tanaka formula (more accurate than 220-age)
        max_hr = 208 - 0.7 * profile.age

        return {
            'max_hr': int(max_hr),
            'zone1_recovery': (int(max_hr * 0.50), int(max_hr * 0.60)),
            'zone2_aerobic': (int(max_hr * 0.60), int(max_hr * 0.70)),
            'zone3_tempo': (int(max_hr * 0.70), int(max_hr * 0.80)),
            'zone4_threshold': (int(max_hr * 0.80), int(max_hr * 0.90)),
            'zone5_vo2max': (int(max_hr * 0.90), int(max_hr * 1.00)),
        }

    def generate_weekly_plan(self, profile: AthleteProfile,
                              recent_performance: list[dict]) -> list[dict]:
        """Weekly training plan"""
        training_zones = self.calculate_training_zones(profile)

        # Periodization: 3 weeks of increasing load + 1 recovery week
        # Determine current week of periodization from history
        week_in_cycle = self._get_week_in_cycle(recent_performance)
        load_modifier = [0.85, 1.0, 1.15, 0.70][week_in_cycle % 4]

        response = self.llm.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=700,
            messages=[{
                "role": "user",
                "content": f"""Create a personalized weekly training plan.

Profile:
- Fitness level: {profile.fitness_level}
- Goal: {profile.primary_goal}
- Available days: {profile.available_days_per_week}
- Equipment: {profile.equipment}
- Injuries to avoid: {profile.injuries}
- Age: {profile.age}, Weight: {profile.weight_kg}kg

Current training intensity: {load_modifier:.0%} of base load
Training zones: Zone 2 aerobic = {training_zones['zone2_aerobic']} bpm

Recent performance (last 5 sessions):
{json.dumps(recent_performance[-5:], ensure_ascii=False)[:400]}

Create {profile.available_days_per_week} training sessions. Return JSON array:
[{{
  "day": "Monday",
  "session_type": "strength|cardio|hiit|recovery",
  "duration_min": 45,
  "exercises": [{{"name": "...", "sets": 3, "reps": "8-10", "rest_sec": 90}}],
  "cardio_zone": "zone2",
  "notes": "..."
}}]"""
            }]
        )

        try:
            return json.loads(response.content[0].text)
        except Exception:
            return []

    def _get_week_in_cycle(self, performance: list[dict]) -> int:
        if not performance:
            return 0
        return len(set(p.get('week_number', 0) for p in performance)) % 4


class RecoveryMonitor:
    """Recovery monitoring from biometrics"""

    def compute_readiness_score(self, biometrics: dict) -> dict:
        """
        Readiness score (0-100).
        Data: HRV, RHR, sleep_score, previous_day_load.
        """
        score = 100.0
        factors = []

        # HRV (Heart Rate Variability) — main indicator
        hrv = biometrics.get('hrv_ms')
        hrv_baseline = biometrics.get('hrv_baseline_ms', 50)
        if hrv and hrv_baseline:
            hrv_ratio = hrv / hrv_baseline
            if hrv_ratio < 0.85:
                score -= 25
                factors.append(f'HRV low ({hrv:.0f}ms vs {hrv_baseline:.0f}ms baseline)')
            elif hrv_ratio > 1.15:
                score += 5  # Good recovery

        # Resting Heart Rate
        rhr = biometrics.get('resting_hr_bpm')
        rhr_baseline = biometrics.get('rhr_baseline_bpm', 60)
        if rhr and rhr_baseline:
            if rhr > rhr_baseline + 5:
                score -= 15
                factors.append(f'RHR elevated ({rhr} vs {rhr_baseline} baseline)')

        # Sleep
        sleep_score = biometrics.get('sleep_score', 80)  # 0-100
        if sleep_score < 60:
            score -= 20
            factors.append(f'Poor sleep (score: {sleep_score})')
        elif sleep_score < 75:
            score -= 10

        # Previous day's load
        previous_load = biometrics.get('yesterday_training_load', 0)  # AU (Arbitrary Units)
        high_load_threshold = biometrics.get('weekly_avg_load', 300) * 0.4
        if previous_load > high_load_threshold:
            score -= 10
            factors.append('High load yesterday')

        score = float(np.clip(score, 0, 100))

        if score > 75:
            recommendation = 'Great day for an intense workout'
            intensity_modifier = 1.0
        elif score > 55:
            recommendation = 'Moderate workout — reduce intensity by 15%'
            intensity_modifier = 0.85
        elif score > 35:
            recommendation = 'Only light recovery or rest'
            intensity_modifier = 0.60
        else:
            recommendation = 'Active rest or day off'
            intensity_modifier = 0.0

        return {
            'readiness_score': round(score),
            'recommendation': recommendation,
            'intensity_modifier': intensity_modifier,
            'limiting_factors': factors
        }


class ProgressTracker:
    """Track progress and adjust plan"""

    def analyze_progress(self, training_logs: pd.DataFrame,
                          profile: AthleteProfile,
                          weeks: int = 8) -> dict:
        """Progress analysis over period"""
        recent = training_logs[
            training_logs['date'] >= pd.Timestamp.now() - pd.Timedelta(weeks=weeks)
        ]

        if recent.empty:
            return {}

        return {
            'sessions_completed': len(recent),
            'sessions_planned': weeks * profile.available_days_per_week,
            'adherence_rate': len(recent) / (weeks * profile.available_days_per_week),

            # Progress on key exercises
            'strength_progress': self._compute_strength_progress(recent),
            'endurance_progress': self._compute_endurance_progress(recent),

            'avg_session_duration_min': recent.get('duration_minutes', pd.Series([45])).mean(),
            'total_volume_kg': recent.get('total_volume_kg', pd.Series([0])).sum(),
        }

    def _compute_strength_progress(self, logs: pd.DataFrame) -> dict:
        """Change in max weights for main exercises"""
        if 'exercise_name' not in logs.columns:
            return {}

        key_exercises = ['squat', 'bench_press', 'deadlift', 'overhead_press']
        progress = {}

        for exercise in key_exercises:
            exercise_logs = logs[logs['exercise_name'] == exercise]
            if len(exercise_logs) < 2:
                continue
            first_max = exercise_logs.nsmallest(3, 'date')['max_weight_kg'].mean()
            last_max = exercise_logs.nlargest(3, 'date')['max_weight_kg'].mean()
            progress[exercise] = round((last_max - first_max) / max(first_max, 1) * 100, 1)

        return progress

    def _compute_endurance_progress(self, logs: pd.DataFrame) -> dict:
        if 'pace_min_per_km' not in logs.columns:
            return {}
        cardio = logs[logs['session_type'] == 'cardio']
        if cardio.empty:
            return {}
        early = cardio.head(3)['pace_min_per_km'].mean()
        recent = cardio.tail(3)['pace_min_per_km'].mean()
        improvement = (early - recent) / early * 100  # Lower pace = improvement
        return {'pace_improvement_pct': round(improvement, 1)}

Comparison: static plan vs AI adaptation

Characteristic Static plan AI adaptation (our system)
Uses biometrics No HRV, heart rate, sleep, load
Load adaptation Once a month Daily
Adherence rate 35-45% 65-75%
Injury risk Baseline 30-40% lower
ROI 6-12 months

What's included in the work?

Component Description
Solution architecture Model selection (LLaMA 3, Claude), pipeline design for biometric collection and processing
Plan generation RAG agent with vector search (ChromaDB) for exercises, contraindication handling
Analytics dashboard Metrics: adherence rate, exercise progress, readiness score
Integration REST API and SDK for iOS/Android, HealthKit, Google Fit, Polar, Garmin
Support 3 months warranty support, team training, documentation

Implementation process

  1. Analytics — study biometrics, workout types, current stack (1–2 days).
  2. Design — determine architecture: which LLMs, how to store exercise embeddings (pgvector).
  3. Implementation — write RecoveryMonitor, FitnessPlanGenerator, ProgressTracker modules (2–6 weeks).
  4. Testing — A/B test on 50–100 users, evaluate adherence improvement (1–2 weeks).
  5. Deploy — deploy microservices in Kubernetes with GPU nodes for inference (Triton Inference Server).

Economic efficiency

The cost of implementing a basic solution is recovered within 6–12 months by increasing user LTV by 25–30%. The average savings from developing a custom AI solution compared to buying a ready-made platform is 40%. Additionally, reducing churn by 15–20% directly increases revenue. Evaluate the economic effect for your product — contact us for a preliminary calculation.

Typical mistakes in AI personalization implementation

  • Ignoring recovery data. A plan based only on goals (weight loss/muscle gain) without considering HRV and sleep leads to overtraining. We always include readiness score as a corrective factor.
  • One model for all. General-purpose LLMs (basic GPT-4) give template advice. We use custom fine-tuned models based on LLaMA 3, trained on your data.
  • No fallback mechanism. LLM failures (latency, toxic responses) should trigger a rule-based engine. Our architecture includes this.

Get a consultation on architecture and pipelines — contact us to discuss metrics and implementation plan for your fitness product.