Files
gravl/.planning/research/11-progressive-overload.md
T

518 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 🐝*