# Progressive Overload-algoritmer — Research för Gravl ## Vad är Progressive Overload? > "Progressive overload is the gradual increase of stress placed on the body during training. To continue building strength and muscle, you must progressively increase the demands on your musculoskeletal system." **Grundprincipen:** Om du gör samma träning med samma vikter, reps och sets vecka efter vecka har kroppen ingen anledning att anpassa sig. --- ## Progressionsmetoder ### 1. Vikt-progression (Linear) **Enklast och mest effektiv för nybörjare/intermediates** ``` Vecka 1: Bänkpress 60kg x 8,8,8 Vecka 2: Bänkpress 62.5kg x 8,8,8 Vecka 3: Bänkpress 65kg x 8,8,8 ... ``` **Typiska ökningar:** | Övning | Ökning per pass | |--------|-----------------| | Squat/Deadlift | +2.5-5 kg | | Bench/Row/OHP | +1.25-2.5 kg | | Isolation (curls, etc.) | +1-2 kg | ### 2. Rep-progression (Double Progression) **När du inte kan öka vikt varje vecka** ``` Mål: 3x8-12 reps Vecka 1: 60kg x 8,8,8 (låg end) Vecka 2: 60kg x 9,9,8 Vecka 3: 60kg x 10,10,10 Vecka 4: 60kg x 12,11,11 Vecka 5: 62.5kg x 8,8,8 (öka vikt, börja om) ``` **Regel:** Öka vikt när alla sets når övre rep-gränsen. ### 3. Set-progression ``` Vecka 1: 60kg x 8,8,8 (3 sets) Vecka 2: 60kg x 8,8,8,8 (4 sets) Vecka 3: 62.5kg x 8,8,8 (tillbaka till 3 sets, ny vikt) ``` ### 4. RPE/RIR-baserad Autoregulation **RPE = Rate of Perceived Exertion (1-10)** **RIR = Reps in Reserve** | RPE | RIR | Beskrivning | |-----|-----|-------------| | 10 | 0 | Failure (kunde inte gjort fler) | | 9.5 | 0.5 | Kanske 1 till med dålig form | | 9 | 1 | 1 rep kvar | | 8.5 | 1.5 | 1-2 reps kvar | | 8 | 2 | 2 reps kvar | | 7 | 3 | 3 reps kvar | | 6 | 4 | Uppvärmning | **Konvertering:** `RPE = 10 - RIR` **Användning:** ``` Målsättning: 3x8 @ RPE 8 Set 1: 80kg x 8 @ RPE 7 → för lätt, öka Set 2: 82.5kg x 8 @ RPE 8 → perfekt Set 3: 82.5kg x 8 @ RPE 9 → trötthet, behåll vikt ``` --- ## 1RM-beräkning ### Populära formler #### Epley Formula (mest använd) ``` 1RM = weight × (1 + reps/30) ``` **Exempel:** 80kg × 10 reps ``` 1RM = 80 × (1 + 10/30) = 80 × 1.333 = 106.7 kg ``` #### Brzycki Formula ``` 1RM = weight × (36 / (37 - reps)) ``` **Exempel:** 80kg × 10 reps ``` 1RM = 80 × (36 / (37 - 10)) = 80 × 1.333 = 106.7 kg ``` #### Lander Formula ``` 1RM = weight × (100 / (101.3 - 2.67 × reps)) ``` ### Rep Max Tabell (% av 1RM) | Reps | % av 1RM | Vikt (om 1RM = 100kg) | |------|----------|----------------------| | 1 | 100% | 100 kg | | 2 | 94% | 94 kg | | 3 | 91% | 91 kg | | 4 | 88% | 88 kg | | 5 | 86% | 86 kg | | 6 | 83% | 83 kg | | 7 | 81% | 81 kg | | 8 | 79% | 79 kg | | 9 | 77% | 77 kg | | 10 | 75% | 75 kg | | 12 | 70% | 70 kg | | 15 | 65% | 65 kg | --- ## Progressionsalgoritmer för Gravl ### Algoritm 1: Simple Linear (Nybörjare) ```python def calculate_next_weight(exercise, last_workout): """ Enkel linjär progression. Om alla sets klarades → öka vikt. """ target_reps = exercise.target_reps # ex: 8 achieved_reps = last_workout.reps # ex: [8, 8, 8] # Alla sets klarade? if all(r >= target_reps for r in achieved_reps): increment = get_increment(exercise.type) return last_workout.weight + increment else: return last_workout.weight # Repetera samma vikt def get_increment(exercise_type): """Standardökningar baserat på övningstyp.""" increments = { 'compound_lower': 2.5, # Squat, Deadlift 'compound_upper': 1.25, # Bench, OHP, Row 'isolation': 1.0, # Curls, Extensions } return increments.get(exercise_type, 1.25) ``` ### Algoritm 2: Double Progression (Rep Range) ```python def calculate_next_weight_double(exercise, last_workout): """ Double progression med rep range (ex: 8-12 reps). Öka vikt när alla sets når övre gränsen. """ min_reps = exercise.min_reps # ex: 8 max_reps = exercise.max_reps # ex: 12 achieved_reps = last_workout.reps # Alla sets på max reps? if all(r >= max_reps for r in achieved_reps): increment = get_increment(exercise.type) return { 'weight': last_workout.weight + increment, 'target_reps': min_reps # Börja om på min_reps } # Alla sets klarade min_reps? elif all(r >= min_reps for r in achieved_reps): return { 'weight': last_workout.weight, 'target_reps': min(max(achieved_reps) + 1, max_reps) } else: # Missade reps, behåll allt return { 'weight': last_workout.weight, 'target_reps': min_reps } ``` ### Algoritm 3: RPE-baserad Autoregulation ```python def calculate_next_weight_rpe(exercise, last_workout): """ RPE-baserad progression. Justerar vikt baserat på hur hårt det kändes. """ target_rpe = exercise.target_rpe # ex: 8 achieved_rpe = last_workout.rpe # ex: [7, 8, 9] avg_rpe = sum(achieved_rpe) / len(achieved_rpe) # Under target RPE → för lätt, öka if avg_rpe < target_rpe - 0.5: adjustment = (target_rpe - avg_rpe) * 2.5 # ~2.5kg per RPE return last_workout.weight + adjustment # Över target RPE → för tungt, minska elif avg_rpe > target_rpe + 0.5: adjustment = (avg_rpe - target_rpe) * 2.5 return last_workout.weight - adjustment # Inom range → perfekt, små ökning else: return last_workout.weight + get_increment(exercise.type) ``` ### Algoritm 4: Hybrid (Gravl Recommendation) ```python def calculate_progression(exercise, history, user): """ Hybrid-algoritm som kombinerar flera metoder. 1. Nybörjare: Linear progression 2. Intermediate: Double progression 3. Avancerad: RPE-baserad Med säkerhetschecks och platå-hantering. """ last_workout = history[-1] if history else None if not last_workout: return estimate_starting_weight(exercise, user) # Välj metod baserat på erfarenhet if user.experience == 'beginner': return linear_progression(exercise, last_workout) elif user.experience == 'intermediate': return double_progression(exercise, last_workout) else: return rpe_progression(exercise, last_workout) def estimate_starting_weight(exercise, user): """ Estimera startvikt för ny användare. Baserat på kroppsvikt och erfarenhet. """ bodyweight = user.weight_kg # Typiska ratio för 1RM baserat på erfarenhet ratios = { 'beginner': { 'squat': 0.5, 'bench': 0.4, 'deadlift': 0.6, 'ohp': 0.25, 'row': 0.35, }, 'intermediate': { 'squat': 1.0, 'bench': 0.75, 'deadlift': 1.25, 'ohp': 0.5, 'row': 0.6, } } ratio = ratios.get(user.experience, ratios['beginner']) estimated_1rm = bodyweight * ratio.get(exercise.base_type, 0.5) # Börja på ~65% av estimated 1RM (för 10 reps) starting_weight = estimated_1rm * 0.65 # Avrunda till närmaste 2.5kg return round(starting_weight / 2.5) * 2.5 ``` --- ## Platå-hantering ### Detektera platå ```python def detect_plateau(history, window=4): """ Platå = ingen progress under [window] pass. """ if len(history) < window: return False recent = history[-window:] weights = [w.weight for w in recent] # Ingen viktökning? if max(weights) <= min(weights): # Kolla även reps total_reps = [sum(w.reps) for w in recent] if max(total_reps) <= min(total_reps): return True return False ``` ### Platå-strategier ```python def handle_plateau(exercise, history, strategy='deload'): """ Hantera platå med olika strategier. """ last_weight = history[-1].weight if strategy == 'deload': # Sänk vikt med 10-15%, bygg upp igen return { 'weight': last_weight * 0.85, 'reason': 'Deload: Sänker vikt för att bygga upp igen' } elif strategy == 'rep_change': # Byt rep-range (ex: 5x5 → 3x8) return { 'weight': last_weight * 0.9, 'reps': 8, 'sets': 3, 'reason': 'Ny rep-range för att bryta platå' } elif strategy == 'exercise_swap': # Byt övning temporärt alternatives = get_alternatives(exercise) return { 'exercise': alternatives[0], 'reason': 'Byter övning för variation' } ``` --- ## Deload-strategier ### Vad är Deload? En planerad period med reducerad intensitet för recovery. ### Typer av Deload | Typ | Vikt | Volym | När | |-----|------|-------|-----| | **Light Deload** | -10% | Same | Var 4:e vecka | | **Volume Deload** | Same | -40% | Vid trött | | **Full Deload** | -20% | -50% | Efter tuffa block | ### Automatisk Deload ```python def should_deload(user, history): """ Avgör om deload behövs. """ weeks_since_deload = user.weeks_since_deload # Schemalagd deload var 4-6 vecka if weeks_since_deload >= 5: return True # RPE konsekvent hög recent_rpe = [h.avg_rpe for h in history[-4:]] if len(recent_rpe) >= 4 and all(r >= 9 for r in recent_rpe): return True # Missade reps ökar recent_misses = count_missed_reps(history[-4:]) if recent_misses > 5: return True return False ``` --- ## UX för Progression ### Visa progression transparent ``` ┌────────────────────────────────────────────────┐ │ Bänkpress Nästa: 85kg │ ├────────────────────────────────────────────────┤ │ │ │ Förra passet: 82.5kg x 8, 8, 8 │ │ Alla sets klarade! → Ökar med 2.5kg │ │ │ │ ┌──────────────────────────────────────────┐ │ │ │ [Progressionsgraf senaste 8 veckor] │ │ │ │ 85 ─ ● │ │ │ │ 80 ─ ● ● │ │ │ │ 75 ─ ● ● │ │ │ │ 70 ─ ● ● │ │ │ │ W1 W2 W3 W4 W5 W6 W7 W8 │ │ │ └──────────────────────────────────────────┘ │ │ │ │ [Godkänn 85kg] [Justera manuellt] │ └────────────────────────────────────────────────┘ ``` ### Förklara logiken ``` 💡 Varför ökar vikten? ─────────────────────── Du tog 82.5kg x 8, 8, 8 förra passet. Mål var 8-10 reps. → Alla sets klarade → Dags att öka! → +2.5kg är standard för överkropps-compound. ``` --- ## Implementation för Gravl ### Database Schema ```sql CREATE TABLE progression_settings ( id SERIAL PRIMARY KEY, user_id INT REFERENCES users(id), exercise_id INT REFERENCES exercises(id), -- Progression method method VARCHAR(20) DEFAULT 'double', -- 'linear', 'double', 'rpe' -- Rep range min_reps INT DEFAULT 8, max_reps INT DEFAULT 12, target_sets INT DEFAULT 3, -- Increments weight_increment DECIMAL(4,2) DEFAULT 2.5, -- Deload settings deload_frequency_weeks INT DEFAULT 5, deload_percentage DECIMAL(3,2) DEFAULT 0.85, -- RPE settings target_rpe DECIMAL(3,1) DEFAULT 8.0, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE progression_history ( id SERIAL PRIMARY KEY, user_id INT REFERENCES users(id), exercise_id INT REFERENCES exercises(id), workout_id INT REFERENCES workouts(id), weight DECIMAL(6,2), reps INT[], rpe DECIMAL(3,1)[], -- Computed estimated_1rm DECIMAL(6,2), total_volume DECIMAL(10,2), -- weight × total_reps performed_at TIMESTAMPTZ DEFAULT NOW() ); ``` ### API Endpoint ```python @app.get("/api/exercises/{exercise_id}/next-weight") def get_next_weight(exercise_id: int, user: User): """ Returnerar nästa rekommenderade vikt för en övning. """ history = get_exercise_history(user.id, exercise_id) settings = get_progression_settings(user.id, exercise_id) next_weight = calculate_progression( exercise=get_exercise(exercise_id), history=history, settings=settings, user=user ) return { "exercise_id": exercise_id, "recommended_weight": next_weight.weight, "recommended_reps": next_weight.reps, "reason": next_weight.reason, "previous": history[-1] if history else None, "progression_graph": get_progression_graph(history) } ``` --- ## Källor - Setgraph, Zing Coach, FitnessAI — Progressive overload calculators - JEFIT, RippedBody — RPE/RIR guides - Stronglifts — Increment settings - NASM, VBTCoach — 1RM formulas - Alpha Progression, StrengthLog — Rep max tables --- *Sammanställt 2026-02-15 av Bumblebee 🐝*