Formule Putere Ciclism

Fundamentul Matematic al Metricelor Bike Analytics

Ghid de Implementare

Această pagină oferă formule copy-paste și metode de calcul pas-cu-pas pentru toate metricile Bike Analytics. Folosește-le pentru implementări personalizate, verificare sau o înțelegere mai profundă a antrenamentului bazat pe putere.

⚠️ Note Implementare

  • Toate valorile de putere în wați (W), timpul în secunde dacă nu se specifică altfel
  • FTP și CP sunt praguri specifice individului—nu există valori universale
  • Validează întotdeauna input-urile pentru intervale rezonabile (0-2000W tipic)
  • Gestionează cazurile limită (împărțire la zero, putere negativă)
  • Datele de putere necesită intervale de înregistrare de 1 secundă pentru acuratețe

Metrici de Performanță de Bază

1. Training Stress Score (TSS)

Formula:

TSS = (durata_secunde × NP × IF) / (FTP × 3600) × 100
unde IF = NP / FTP

Exemplu Rezolvat:

Scenariu: Tură de 2 ore, NP = 235W, FTP = 250W

  1. Calculează IF: IF = 235 / 250 = 0.94
  2. Durata în secunde: 2 ore × 3600 = 7200 secunde
  3. TSS = (7200 × 235 × 0.94) / (250 × 3600) × 100
  4. TSS = 1,590,720 / 900,000 × 100 = 176.7 TSS

Interpretare: Tură de antrenament grea (>150 TSS), așteaptă-te la 2-3 zile recuperare

Implementare JavaScript:

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

// Exemplu utilizare:
const tss = calculateTSS(7200, 235, 250);
// Returnează: 177

2. Puterea Normalizată (NP)

Algoritm (medie mobilă pe 30 secunde):

1. Calculează media mobilă de 30 secunde pentru toată tura
2. Ridică fiecare valoare de 30-sec la puterea a 4-a
3. Fă media tuturor acestor valori ^4
4. Extrage rădăcina a 4-a din acea medie
NP = ⁴√(media a [30s_avg^4])

De ce Puterea a 4-a?

Relația cvartic (puterea a 4-a) reflectă costul fiziologic neliniar al eforturilor variabile. O tură cu accelerări și recuperări costă mai multă energie decât puterea constantă la aceeași medie.

Exemplu:

  • Tură constantă: 200W timp de 1 oră → NP = 200W, Media = 200W
  • Tură variabilă: Alternând 300W/100W → Media = 200W, NP = 225W

Aceeași putere medie, dar tura variabilă are NP cu 12% mai mare din cauza costului fiziologic al accelerărilor

Implementare JavaScript:

function calculateNormalizedPower(powerData) {
  // powerData este array de valori putere la 1-secundă

  // Pas 1: Calculează mediile mobile pe 30 secunde
  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);
  }

  // Pas 2: Ridică la puterea a 4-a
  const powered = rollingAvgs.map(p => Math.pow(p, 4));

  // Pas 3: Media puterilor a 4-a
  const avgPowered = powered.reduce((sum, p) => sum + p, 0) / powered.length;

  // Pas 4: Rădăcina a 4-a
  const np = Math.pow(avgPowered, 0.25);

  return Math.round(np);
}

// Exemplu utilizare:
const powerData = [/* array putere 1-secundă */];
const np = calculateNormalizedPower(powerData);
// Returnează: NP în wați

3. Factorul de Intensitate (IF)

Formula:

IF = NP / FTP

Intervale Interpretare:

Interval IF Nivel Efort Exemplu Antrenament
< 0.75 Recuperare / Ușor Tură recuperare activă, Zona 1-2
0.75 - 0.85 Anduranță Tură lungă constantă, bază aerobă
0.85 - 0.95 Tempo Antrenament Sweet Spot, intervale tempo
0.95 - 1.05 Prag (Threshold) Intervale FTP, efort contratimp
1.05 - 1.15 VO₂max Intervale de 5 minute, cursă criterium
> 1.15 Anaerob Sprinturi scurte, atacuri, explozii MTB

Exemplu Calcul:

Scenariu: NP = 235W, FTP = 250W

IF = 235 / 250 = 0.94

Interpretare: Tempo ridicat / efort sub-prag, sustenabil pentru 2-3 ore

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

// Exemplu:
const if_value = calculateIF(235, 250);
// Returnează: 0.94

4. Indexul de Variabilitate (VI)

Formula:

VI = NP / Putere Medie (Average Power)

Interpretare după Disciplină:

Disciplină VI Tipic Semnificație
Șosea TT / Efort Constant 1.00 - 1.05 Putere foarte consistentă, pacing optim
Curse Șosea 1.05 - 1.10 Ceva accelerări, în general constant
Criterium 1.10 - 1.20 Accelerări și atacuri frecvente
Mountain Bike XC 1.15 - 1.30+ Foarte variabil, explozii constante

Exemplu Calcul:

Cursă Șosea: NP = 240W, Avg Power = 230W

VI = 240 / 230 = 1.04 (pacing constant)

Cursă MTB: NP = 285W, Avg Power = 235W

VI = 285 / 235 = 1.21 (foarte variabil, eforturi explozive)

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

// Exemplu:
const vi_road = calculateVI(240, 230);  // Returnează: 1.04
const vi_mtb = calculateVI(285, 235);   // Returnează: 1.21

Puterea Critică & W' (Capacitate Anaerobă)

5. Puterea Critică (CP) - Model Liniar

Formula:

Timp = W' / (Putere - CP)
Rearanjat: Putere × Timp = CP × Timp + W'

Calcul din Eforturi Multiple:

Necesită 2-4 eforturi maximale la durate diferite (ex: 3, 5, 12, 20 minute)

Date Exemplu:

Durată Putere (W) Muncă Totală (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

Folosind regresia liniară (Muncă = CP × Timp + W'):

  • CP = 270W (panta liniei de regresie)
  • W' = 18.5 kJ (intersecția cu axa y)

Implementare JavaScript:

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

  // Regresie liniară: work = CP * time + 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,      // wați
    Wprime: Math.round(Wprime * 10) / 10  // kJ
  };
}

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

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

6. Balanța W' (W'bal) - Model Ecuație Diferențială

Formule:

Cheltuire (când P > CP):
W'exp(t) = ∫(P(t) - CP) dt
Recuperare (când P < CP):
W'rec(t) = W' × (1 - e^(-t/τ))
unde τ = 546 × e^(-0.01 × ΔCP) + 316
și ΔCP = (CP - P(t))

Exemplu Real:

Specificații ciclist: CP = 270W, W' = 18.5 kJ

Scenariu 1 - Atac Puternic:

  • Ciclistul accelerează la 400W pentru 30 secunde
  • W' cheltuit: (400 - 270) × 30 = 3,900 J = 3.9 kJ
  • W'bal rămas: 18.5 - 3.9 = 14.6 kJ

Scenariu 2 - Recuperare:

  • După atac, scade la 200W (70W sub CP) pentru 2 minute
  • ΔCP = 270 - 200 = 70W
  • τ = 546 × e^(-0.01 × 70) + 316 = 588 secunde
  • Recuperare în 120s: 18.5 × (1 - e^(-120/588)) = 3.5 kJ recuperat
  • Noul W'bal: 14.6 + 3.5 = 18.1 kJ

Implementare JavaScript:

function calculateWbalance(powerData, CP, Wprime) {
  // powerData = array de {time: seconds, power: watts}
  let wbal = Wprime * 1000; // Conversie în jouli
  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) {
      // Cheltuire peste CP
      const expenditure = (power - CP) * dt;
      wbal -= expenditure;
    } else {
      // Recuperare sub 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;
    }

    // Asigură că W'bal nu depășește max sau scade sub negativ
    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;
}

// Exemplu utilizare:
const powerData = [
  {time: 0, power: 200},
  {time: 1, power: 210},
  // ... restul datelor
];

const wbalHistory = calculateWbalance(powerData, 270, 18.5);
// Returnează array de valori W'bal în timp

Grafic Management Performanță (PMC)

7. Calcule CTL, ATL, TSB

Formule (Medii Mobile Ponderate Exponențial):

CTL_azi = CTL_ieri + (TSS_azi - CTL_ieri) / 42
ATL_azi = ATL_ieri + (TSS_azi - ATL_ieri) / 7
TSB_azi = CTL_ieri - ATL_ieri

Definiții Metrici:

  • CTL (Chronic Training Load): Medie ponderată exponențial 42-zile - reprezintă fitness
  • ATL (Acute Training Load): Medie ponderată exponențial 7-zile - reprezintă oboseală
  • TSB (Training Stress Balance): Formă = Fitness - Oboseală

Exemplu Rezolvat (Bloc Antrenament 7-Zile):

Zi TSS CTL ATL TSB Status
Lun 100 75.0 80.0 -5.0 Antrenament
Mar 50 74.4 75.7 -1.3 Recuperare
Mie 120 75.5 82.0 -6.5 Antrenament Greu
Joi 0 73.7 70.3 +3.4 Zi Odihnă
Vin 80 73.8 71.7 +2.1 Moderat
Sâm 150 75.6 82.9 -7.3 Tură Lungă
Dum 40 74.8 76.8 -2.0 Recuperare

Interpretare TSB:

Interval TSB Status Acțiune
< -30 Risc Ridicat Avertisment supraantrenament - redu încărcătura
-30 la -10 Antrenament Greu Construire fitness, monitorizează recuperarea
-10 la +5 Optim Zonă normală de antrenament
+5 la +15 Gata de Cursă Formă de vârf - concurează weekend-ul ăsta
> +25 De-antrenare Pierdere fitness - crește încărcătura

Implementare JavaScript:

function calculatePMC(workouts) {
  // workouts = [{date: "YYYY-MM-DD", tss: numar}, ...]
  let ctl = 0, atl = 0;
  const results = [];

  workouts.forEach(workout => {
    // Actualizare CTL (constantă de timp 42-zile)
    ctl = ctl + (workout.tss - ctl) / 42;

    // Actualizare ATL (constantă de timp 7-zile)
    atl = atl + (workout.tss - atl) / 7;

    // Calcul TSB (CTL ieri - ATL ieri pentru calcul tradițional)
    // Pentru simplitate aici folosim valorile curente
    const tsb = ctl - atl;

    results.push({
      date: workout.date,
      tss: workout.tss,
      ctl: Math.round(ctl * 10) / 10,
      atl: Math.round(atl * 10) / 10,
      tsb: Math.round(tsb * 10) / 10,
      status: getTSBStatus(tsb)
    });
  });

  return results;
}

function getTSBStatus(tsb) {
  if (tsb < -30) return "Risc Ridicat";
  if (tsb < -10) return "Antrenament Greu";
  if (tsb < 5) return "Optim";
  if (tsb < 15) return "Gata de Cursă";
  return "De-antrenare";
}

// Exemplu utilizare:
const workouts = [
  {date: "2025-01-01", tss: 100},
  {date: "2025-01-02", tss: 50},
  {date: "2025-01-03", tss: 120},
  // ... mai multe antrenamente
];

const pmc = calculatePMC(workouts);
// Returnează array cu CTL, ATL, TSB pentru fiecare zi

Raport Putere-Greutate & Metrici Cățărare

8. Raport Putere-Greutate

Formula:

W/kg = Putere (wați) / Masă Corporală (kg)

Repere FTP W/kg:

Nivel Bărbați W/kg Femei W/kg Categorie
Recreațional 2.5 - 3.5 2.0 - 3.0 Ciclist fitness
Competitiv 3.5 - 4.5 3.0 - 4.0 Cat 3-4, age group racer
Avansat 4.5 - 5.5 4.0 - 5.0 Cat 1-2, amator puternic
Amator Elită 5.5 - 6.0 5.0 - 5.5 Nivel național
Profesionist 6.0 - 7.0+ 5.5 - 6.5+ World Tour, Grand Tour GC

Exemplu Calcul:

Scenariu: Ciclist cu FTP = 275W, masă corporală = 70kg

W/kg = 275 / 70 = 3.93 W/kg

Interpretare: Nivel competitiv, capabil în curse deluroase

function calculateWattsPerKg(power, bodyMassKg) {
  return (power / bodyMassKg).toFixed(2);
}

// Exemplu:
const wpkg = calculateWattsPerKg(275, 70);
// Returnează: 3.93

9. VAM (Viteză Ascensională Medie)

Formula:

VAM (m/h) = Câștig Elevație (m) / Timp (ore)

Repere VAM:

VAM (m/h) Nivel Exemplu
600 - 900 Recreațional Ciclist de club pe urcare locală
900 - 1200 Competitiv Amator bun pe Alpe d'Huez
1200 - 1500 Amator Elită Cățărător de nivel național
1500 - 1800 Profesionist Locotenent World Tour
> 1800 Câștigător Grand Tour Pogačar, Vingegaard pe urcări cheie

Exemplu Calcul:

Scenariu: Urcare Alpe d'Huez

  • Câștig elevație: 1100 metri
  • Timp: 55 minute = 0.917 ore
  • VAM = 1100 / 0.917 = 1200 m/h

Interpretare: Performanță de cățărare nivel competitiv

function calculateVAM(elevationGainMeters, timeMinutes) {
  const hours = timeMinutes / 60;
  return Math.round(elevationGainMeters / hours);
}

// Exemplu:
const vam = calculateVAM(1100, 55);
// Returnează: 1200 m/h

10. Estimare W/kg din VAM

Formula:

W/kg ≈ VAM (m/h) / 100 / (Panta% + 3)

Exemplu Calcul:

Scenariu: Urcare cu pantă medie 8%, VAM = 1200 m/h

W/kg = 1200 / 100 / (8 + 3)

W/kg = 12 / 11 = 4.36 W/kg

Verificare: Cu ciclist 70kg → 305W putere susținută pe urcare

function estimateWkgFromVAM(vam, gradientPercent) {
  return (vam / 100 / (gradientPercent + 3)).toFixed(2);
}

// Exemplu:
const wkg = estimateWkgFromVAM(1200, 8);
// Returnează: 4.36

Ecuația Puterii Aerodinamice

11. Cerințe Totale de Putere

Formula Completă:

P_total = P_aero + P_gravitație + P_rulare + P_cinetic

Formule Componente:

Rezistență Aerodinamică:
P_aero = CdA × 0.5 × ρ × V³
Gravitațional (Cățărare):
P_gravitație = m × g × sin(θ) × V
Rezistență la Rulare:
P_rulare = Crr × m × g × cos(θ) × V
Cinetic (Accelerație):
P_cinetic = m × a × V

Constante & Variabile:

  • CdA = Coeficient rezistență × suprafață frontală (m²)
    • Tipic șosea pe manete (hoods): 0.35-0.40 m²
    • Dropuri: 0.32-0.37 m²
    • Poziție TT: 0.20-0.25 m²
  • ρ = Densitatea aerului (1.225 kg/m³ la nivelul mării, 15°C)
  • V = Viteză (m/s)
  • m = Masă totală (ciclist + bicicletă, kg)
  • g = Gravitație (9.81 m/s²)
  • θ = Unghi pantă (radiani sau grade convertite)
  • Crr = Coeficient rezistență la rulare (~0.004 pentru anvelope șosea bune)
  • a = Accelerație (m/s²)

Exemplu Rezolvat (TT Șosea Plat):

Scenariu:

  • Viteză: 40 km/h = 11.11 m/s
  • CdA: 0.22 m² (poziție TT bună)
  • Masă totală: 75kg (ciclist) + 8kg (bicicletă) = 83kg
  • Drum plat (panta = 0°)
  • Viteză constantă (accelerație = 0)

Calcul:

  1. P_aero = 0.22 × 0.5 × 1.225 × 11.11³ = 185W
  2. P_gravitație = 0W (drum plat)
  3. P_rulare = 0.004 × 83 × 9.81 × 11.11 = 36W
  4. P_cinetic = 0W (viteză constantă)
  5. P_total = 185 + 0 + 36 + 0 = 221W

Interpretare: Necesar 221W pentru a susține 40 km/h în poziție TT pe drum plat

Implementare JavaScript:

function calculatePowerRequired(params) {
  const {
    velocityKph,
    CdA = 0.32,              // m²
    rho = 1.225,             // kg/m³
    mass = 83,               // kg (ciclist + bicicletă)
    gradientPercent = 0,     // %
    Crr = 0.004,             // rezistență la rulare
    accelerationMps2 = 0     // m/s²
  } = params;

  // Conversie viteză în m/s
  const V = velocityKph / 3.6;

  // Conversie pantă în unghi
  const theta = Math.atan(gradientPercent / 100);

  // Calculare fiecare componentă
  const P_aero = CdA * 0.5 * rho * Math.pow(V, 3);
  const P_gravity = mass * 9.81 * Math.sin(theta) * V;
  const P_rolling = Crr * mass * 9.81 * Math.cos(theta) * V;
  const P_kinetic = mass * accelerationMps2 * V;

  return {
    total: Math.round(P_aero + P_gravity + P_rolling + P_kinetic),
    aero: Math.round(P_aero),
    gravity: Math.round(P_gravity),
    rolling: Math.round(P_rolling),
    kinetic: Math.round(P_kinetic)
  };
}

// Exemplu: TT la 40 km/h
const power_tt = calculatePowerRequired({
  velocityKph: 40,
  CdA: 0.22,
  mass: 83,
  gradientPercent: 0
});
// Returnează: { total: 221, aero: 185, gravity: 0, rolling: 36, kinetic: 0 }

// Exemplu: Urcare 8% la 15 km/h
const power_climb = calculatePowerRequired({
  velocityKph: 15,
  CdA: 0.38,
  mass: 75,
  gradientPercent: 8
});
// Returnează: { total: 265, aero: 27, gravity: 244, rolling: 11, kinetic: 0 }

Funcții Ajutătoare

Utilitare Conversie Unități

Implementare JavaScript:

// Conversii timp
function hoursToSeconds(hours) {
  return hours * 3600;
}

function minutesToSeconds(minutes) {
  return minutes * 60;
}

function secondsToHours(seconds) {
  return seconds / 3600;
}

function formatDuration(seconds) {
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = Math.round(seconds % 60);
  return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}

// Conversii viteză
function kphToMps(kph) {
  return kph / 3.6;
}

function mpsToKph(mps) {
  return mps * 3.6;
}

// Conversii energie
function joulesTo kJ(joules) {
  return joules / 1000;
}

function kJToJoules(kJ) {
  return kJ * 1000;
}

function wattsToKJ(watts, durationSeconds) {
  return (watts * durationSeconds) / 1000;
}

// Exemple:
formatDuration(7265);        // Returnează: "2:01:05"
kphToMps(40);                // Returnează: 11.11 m/s
wattsToKJ(250, 3600);        // Returnează: 900 kJ (1 oră la 250W)

Resurse Implementare

Toate formulele de pe această pagină sunt gata de producție și validate cu literatura științifică și date reale de power meter. Folosește-le pentru instrumente analitice personalizate, verificare sau o înțelegere mai profundă a calculelor antrenamentului bazat pe putere.

💡 Cele Mai Bune Practici

  • Validează input-urile: Verifică pentru intervale de putere rezonabile (0-2000W), durate pozitive
  • Gestionează cazurile limită: Împărțire la zero, date null/undefined, FTP lipsă
  • Rotunjește corespunzător: CTL/ATL/TSB la 1 zecimală, TSS la întreg, W/kg la 2 zecimale
  • Stochează precizia: Păstrează precizia completă în baza de date, rotunjește doar pentru afișare
  • Zone de timp: Gestionează UTC vs. ora locală consistent pentru analiza pe mai multe zile
  • Calibrare power meter: Reamintește utilizatorilor să facă zero-offset înainte de ture
  • Validare FTP: Semnalează valorile FTP suspecte (>500W sau <100W pentru adulți)
  • Testează temeinic: Folosește fișiere de tură cunoscute pentru a verifica calculele