Wieler Vermogensformules

Wiskundige Fundering van Bike Analytics Metrieken

Implementatiegids

Deze pagina biedt copy-paste formules en stapsgewijze berekeningsmethoden voor alle Bike Analytics metrieken. Gebruik deze voor eigen implementaties, verificatie of dieper begrip van op vermogen gebaseerde training.

⚠️ Implementatie Notities

  • Alle vermogenswaarden in watts (W), tijd in seconden tenzij anders aangegeven
  • FTP en CP zijn individuele drempelwaarden—geen universele waarden
  • Valideer invoer altijd op redelijke bereiken (0-2000W typisch)
  • Handel randgevallen af (delen door nul, negatief vermogen)
  • Vermogensdata vereist 1-seconde registratie-intervallen voor nauwkeurigheid

Kern Prestatie Metrieken

1. Training Stress Score (TSS)

Formule:

TSS = (duur_seconden × NP × IF) / (FTP × 3600) × 100
waar IF = NP / FTP

Uitgewerkt Voorbeeld:

Scenario: 2 uur rit, NP = 235W, FTP = 250W

  1. Bereken IF: IF = 235 / 250 = 0.94
  2. Duur in seconden: 2 uur × 3600 = 7200 seconden
  3. TSS = (7200 × 235 × 0.94) / (250 × 3600) × 100
  4. TSS = 1,590,720 / 900,000 × 100 = 176.7 TSS

Interpretatie: Zware trainingsrit (>150 TSS), verwacht 2-3 dagen herstel

JavaScript Implementatie:

function calculateTSS(durationSeconds, normalizedPower, ftp) {
  const intensityFactor = normalizedPower / ftp;
  const tss = (durationSeconds * normalizedPower * intensityFactor) / (ftp * 3600) * 100;
  return Math.round(tss);
}

// Voorbeeld gebruik:
const tss = calculateTSS(7200, 235, 250);
// Geeft: 177

2. Normalized Power (NP)

Algoritme (30-seconden voortschrijdend gemiddelde):

1. Bereken 30-seconden voortschrijdend gemiddelde vermogen voor de hele rit
2. Verhef elke 30-sec waarde tot de 4e macht
3. Neem het gemiddelde van al deze ^4 waarden
4. Neem de 4e machtswortel van dat gemiddelde
NP = ⁴√(gemiddelde van [30s_avg^4])

Waarom de 4e Macht?

De kwartische (4e macht) relatie weerspiegelt de niet-lineaire fysiologische kosten van variabele inspanningen. Een rit met surges en herstel kost meer energie dan steady vermogen bij hetzelfde gemiddelde.

Voorbeeld:

  • Steady rit: 200W voor 1 uur → NP = 200W, Gemiddelde = 200W
  • Variabele rit: Afwisselend 300W/100W → Gemiddelde = 200W, NP = 225W

Zelfde gemiddelde vermogen, maar variabele rit heeft 12% hogere NP door fysiologische kosten van surges

JavaScript Implementatie:

function calculateNormalizedPower(powerData) {
  // powerData is array van 1-seconde vermogenswaarden

  // Stap 1: Bereken 30-seconden voortschrijdende gemiddelden
  const rollingAvgs = [];
  for (let i = 29; i < powerData.length; i++) {
    const window = powerData.slice(i - 29, i + 1);
    const avg = window.reduce((sum, p) => sum + p, 0) / 30;
    rollingAvgs.push(avg);
  }

  // Stap 2: Verhef tot 4e macht
  const powered = rollingAvgs.map(p => Math.pow(p, 4));

  // Stap 3: Gemiddelde van 4e machten
  const avgPowered = powered.reduce((sum, p) => sum + p, 0) / powered.length;

  // Stap 4: Neem 4e machtswortel
  const np = Math.pow(avgPowered, 0.25);

  return Math.round(np);
}

// Voorbeeld gebruik:
const powerData = [/* 1-seconde vermogensarray */];
const np = calculateNormalizedPower(powerData);
// Geeft: NP in watts

3. Intensity Factor (IF)

Formule:

IF = NP / FTP

Interpretatie Bereiken:

IF Bereik Inspanningsniveau Voorbeeld Workout
< 0.75 Herstel / Gemakkelijk Actieve herstelrit, Zone 1-2
0.75 - 0.85 Duur Lange rustige rit, aerobe basis
0.85 - 0.95 Tempo Sweet spot training, tempo intervallen
0.95 - 1.05 Drempel FTP intervallen, tijdrit inspanning
1.05 - 1.15 VO₂max 5-minuten intervallen, criterium race
> 1.15 Anaeroob Korte sprints, aanvallen, MTB bursts

Voorbeeld Berekening:

Scenario: NP = 235W, FTP = 250W

IF = 235 / 250 = 0.94

Interpretatie: Hoog tempo / sub-drempel inspanning, vol te houden voor 2-3 uur

function calculateIF(normalizedPower, ftp) {
  return (normalizedPower / ftp).toFixed(2);
}

// Voorbeeld:
const if_value = calculateIF(235, 250);
// Geeft: 0.94

4. Variability Index (VI)

Formule:

VI = NP / Gemiddeld Vermogen

Interpretatie per Discipline:

Discipline Typische VI Betekenis
Weg Tijdrit / Vlakke Rit 1.00 - 1.05 Zeer consistent vermogen, optimale pacing
Wegwedstrijd 1.05 - 1.10 Enige surges, over het algemeen stabiel
Criterium 1.10 - 1.20 Frequente acceleraties en aanvallen
Mountainbike XC 1.15 - 1.30+ Zeer variabel, constante surges

Voorbeeld Berekening:

Wegwedstrijd: NP = 240W, Gem. Vermogen = 230W

VI = 240 / 230 = 1.04 (stabiele pacing)

MTB Race: NP = 285W, Gem. Vermogen = 235W

VI = 285 / 235 = 1.21 (zeer variabel, burst inspanningen)

function calculateVI(normalizedPower, averagePower) {
  return (normalizedPower / averagePower).toFixed(2);
}

// Voorbeeld:
const vi_road = calculateVI(240, 230);  // Geeft: 1.04
const vi_mtb = calculateVI(285, 235);   // Geeft: 1.21

Critical Power & W' (Anaerobe Capaciteit)

5. Critical Power (CP) - Lineair Model

Formule:

Tijd = W' / (Vermogen - CP)
Herschikt: Vermogen × Tijd = CP × Tijd + W'

Berekening uit Meerdere Inspanningen:

Vereist 2-4 maximale inspanningen op verschillende duren (bijv. 3, 5, 12, 20 minuten)

Voorbeeld Data:

Duur Vermogen (W) Totaal Werk (kJ)
3 min (180s) 400W 72 kJ
5 min (300s) 365W 109.5 kJ
12 min (720s) 310W 223.2 kJ
20 min (1200s) 285W 342 kJ

Gebruik lineaire regressie (Werk = CP × Tijd + W'):

  • CP = 270W (helling van regressielijn)
  • W' = 18.5 kJ (y-as afsnijding)

JavaScript Implementatie:

function calculateCP_Linear(efforts) {
  // efforts = [{duration: seconds, power: watts}, ...]

  const times = efforts.map(e => e.duration);
  const work = efforts.map(e => e.power * e.duration / 1000); // kJ

  // Lineaire regressie: werk = CP * tijd + W'
  const n = efforts.length;
  const sumT = times.reduce((a, b) => a + b, 0);
  const sumW = work.reduce((a, b) => a + b, 0);
  const sumTW = times.reduce((sum, t, i) => sum + t * work[i], 0);
  const sumTT = times.reduce((sum, t) => sum + t * t, 0);

  const CP = (n * sumTW - sumT * sumW) / (n * sumTT - sumT * sumT);
  const Wprime = (sumW - CP * sumT) / n;

  return {
    CP: Math.round(CP * 10) / 10,      // watts
    Wprime: Math.round(Wprime * 10) / 10  // kJ
  };
}

// Voorbeeld gebruik:
const efforts = [
  {duration: 180, power: 400},
  {duration: 300, power: 365},
  {duration: 720, power: 310},
  {duration: 1200, power: 285}
];

const result = calculateCP_Linear(efforts);
// Geeft: { CP: 270.0, Wprime: 18.5 }

6. W' Balans (W'bal) - Differentiaalvergelijking Model

Formules:

Verbruik (wanneer P > CP):
W'exp(t) = ∫(P(t) - CP) dt
Herstel (wanneer P < CP):
W'rec(t) = W' × (1 - e^(-t/τ))
waar τ = 546 × e^(-0.01 × ΔCP) + 316
en ΔCP = (CP - P(t))

Praktijkvoorbeeld:

Renner specificaties: CP = 270W, W' = 18.5 kJ

Scenario 1 - Harde Aanval:

  • Renner sprint naar 400W voor 30 seconden
  • W' verbruik: (400 - 270) × 30 = 3,900 J = 3.9 kJ
  • W'bal resterend: 18.5 - 3.9 = 14.6 kJ

Scenario 2 - Herstel:

  • Na aanval, zakt naar 200W (70W onder CP) voor 2 minuten
  • ΔCP = 270 - 200 = 70W
  • τ = 546 × e^(-0.01 × 70) + 316 = 588 seconden
  • Herstel in 120s: 18.5 × (1 - e^(-120/588)) = 3.5 kJ hersteld
  • Nieuwe W'bal: 14.6 + 3.5 = 18.1 kJ

JavaScript Implementatie:

function calculateWbalance(powerData, CP, Wprime) {
  // powerData = array van {time: seconds, power: watts}
  let wbal = Wprime * 1000; // Converteer naar joules
  const wbalHistory = [];

  for (let i = 1; i < powerData.length; i++) {
    const dt = powerData[i].time - powerData[i-1].time;
    const power = powerData[i].power;

    if (power > CP) {
      // Verbruik boven CP
      const expenditure = (power - CP) * dt;
      wbal -= expenditure;
    } else {
      // Herstel onder CP
      const deltaCP = CP - power;
      const tau = 546 * Math.exp(-0.01 * deltaCP) + 316;
      const recovery = (Wprime * 1000 - wbal) * (1 - Math.exp(-dt / tau));
      wbal += recovery;
    }

    // Zorg dat W'bal niet boven max of onder nul komt
    wbal = Math.max(0, Math.min(wbal, Wprime * 1000));

    wbalHistory.push({
      time: powerData[i].time,
      wbal: wbal / 1000, // kJ
      percent: (wbal / (Wprime * 1000)) * 100
    });
  }

  return wbalHistory;
}

// Voorbeeld gebruik:
const powerData = [
  {time: 0, power: 200},
  {time: 1, power: 210},
  // ... rest van ritdata
];

const wbalHistory = calculateWbalance(powerData, 270, 18.5);
// Geeft array van W'bal waarden over tijd

Prestatie Management Grafiek (PMC)

7. CTL, ATL, TSB Berekeningen

Formules (Exponentieel Gewogen Voortschrijdende Gemiddelden):

CTL_vandaag = CTL_gisteren + (TSS_vandaag - CTL_gisteren) / 42
ATL_vandaag = ATL_gisteren + (TSS_vandaag - ATL_gisteren) / 7
TSB_vandaag = CTL_gisteren - ATL_gisteren

Metriek Definities:

  • CTL (Chronische Trainingsbelasting): 42-daags exponentieel gemiddelde - vertegenwoordigt conditie
  • ATL (Acute Trainingsbelasting): 7-daags exponentieel gemiddelde - vertegenwoordigt vermoeidheid
  • TSB (Training Stress Balans): Vorm = Conditie - Vermoeidheid

Uitgewerkt Voorbeeld (7-Daags Trainingsblok):

Dag TSS CTL ATL TSB Status
Ma 100 75.0 80.0 -5.0 Trainen
Di 50 74.4 75.7 -1.3 Herstel
Wo 120 75.5 82.0 -6.5 Harde Training
Do 0 73.7 70.3 +3.4 Rustdag
Vr 80 73.8 71.7 +2.1 Gemiddeld
Za 150 75.6 82.9 -7.3 Lange Rit
Zo 40 74.8 76.8 -2.0 Herstel

Vermogen-Gewicht & Klim Metrieken

8. Vermogen-Gewicht Ratio

Formule:

W/kg = Vermogen (watts) / Lichaamsgewicht (kg)

FTP W/kg Benchmarks:

Niveau Man W/kg Vrouw W/kg Categorie
Recreatief 2.5 - 3.5 2.0 - 3.0 Fitness fietser
Competitief 3.5 - 4.5 3.0 - 4.0 Cat 3-4, amateur racer
Geavanceerd 4.5 - 5.5 4.0 - 5.0 Cat 1-2, sterke amateur
Elite Amateur 5.5 - 6.0 5.0 - 5.5 Nationaal niveau
Professioneel 6.0 - 7.0+ 5.5 - 6.5+ World Tour, Grand Tour GC

9. VAM (Velocità Ascensionale Media)

Formule:

VAM (m/u) = Hoogogtewinst (m) / Tijd (uur)

VAM Benchmarks:

VAM (m/u) Niveau Voorbeeld
600 - 900 Recreatief Clubfietser op lokale klim
900 - 1200 Competitief Goede amateur op Alpe d'Huez
1200 - 1500 Elite Amateur Nationale klimmer
1500 - 1800 Professioneel World Tour knecht
> 1800 Grand Tour Winnaar Pogačar, Vingegaard op sleutelritten