Formule Potenza Ciclismo

Fondamenti Matematici delle Metriche Bike Analytics

Guida all'Implementazione

Questa pagina fornisce formule copia-incolla e metodi di calcolo passo-passo per tutte le metriche Bike Analytics. Usale per implementazioni personalizzate, verifica o una comprensione più profonda dell'allenamento basato sulla potenza.

⚠️ Note di Implementazione

  • Tutti i valori di potenza in watt (W), tempo in secondi se non specificato
  • FTP e CP sono soglie individuali—nessun valore universale
  • Valida sempre gli input per range ragionevoli (0-2000W tipico)
  • Gestisci i casi limite (divisione per zero, potenza negativa)
  • I dati di potenza richiedono intervalli di registrazione di 1 secondo per accuratezza

Metriche Prestazionali Core

1. Training Stress Score (TSS)

Formula:

TSS = (durata_secondi × NP × IF) / (FTP × 3600) × 100
dove IF = NP / FTP

Esempio Svolto:

Scenario: Uscita 2 ore, NP = 235W, FTP = 250W

  1. Calcola IF: IF = 235 / 250 = 0.94
  2. Durata in secondi: 2 ore × 3600 = 7200 secondi
  3. TSS = (7200 × 235 × 0.94) / (250 × 3600) × 100
  4. TSS = 1,590,720 / 900,000 × 100 = 176.7 TSS

Interpretazione: Allenamento duro (>150 TSS), aspettati 2-3 giorni di recupero

Implementazione JavaScript:

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

// Esempio uso:
const tss = calculateTSS(7200, 235, 250);
// Ritorna: 177

2. Normalized Power (NP)

Algoritmo (media mobile 30 secondi):

1. Calcola media mobile 30 secondi potenza per intera uscita
2. Eleva ogni valore 30-sec alla 4a potenza
3. Fai la media di tutti questi valori ^4
4. Prendi la radice 4a di quella media
NP = ⁴√(media di [30s_avg^4])

Perché la 4a Potenza?

La relazione quartica (4a potenza) riflette il costo fisiologico non lineare degli sforzi variabili. Un'uscita con scatti e recuperi costa più energia di una potenza costante alla stessa media.

Esempio:

  • Uscita costante: 200W per 1 ora → NP = 200W, Media = 200W
  • Uscita variabile: Alternando 300W/100W → Media = 200W, NP = 225W

Stessa potenza media, ma l'uscita variabile ha NP 12% più alta dovuto al costo fisiologico degli scatti

Implementazione JavaScript:

function calculateNormalizedPower(powerData) {
  // powerData è array di valori potenza 1-secondo

  // Step 1: Calcola medie mobili 30-secondi
  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);
  }

  // Step 2: Eleva alla 4a potenza
  const powered = rollingAvgs.map(p => Math.pow(p, 4));

  // Step 3: Media delle 4e potenze
  const avgPowered = powered.reduce((sum, p) => sum + p, 0) / powered.length;

  // Step 4: Prendi radice 4a
  const np = Math.pow(avgPowered, 0.25);

  return Math.round(np);
}

// Esempio uso:
const powerData = [/* array potenza 1-secondo */];
const np = calculateNormalizedPower(powerData);
// Ritorna: NP in watt

3. Intensity Factor (IF)

Formula:

IF = NP / FTP

Range Interpretazione:

Range IF Livello Sforzo Esempio Allenamento
< 0.75 Recupero / Facile Uscita recupero attivo, Zona 1-2
0.75 - 0.85 Endurance Uscita lunga costante, base aerobica
0.85 - 0.95 Tempo Allenamento sweet spot, intervalli tempo
0.95 - 1.05 Soglia Intervalli FTP, sforzo cronometro
1.05 - 1.15 VO₂max Intervalli 5-minuti, gara criterium
> 1.15 Anaerobico Sprint brevi, attacchi, scatti MTB

Esempio Calcolo:

Scenario: NP = 235W, FTP = 250W

IF = 235 / 250 = 0.94

Interpretazione: Alto tempo / sforzo sub-soglia, sostenibile per 2-3 ore

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

// Esempio:
const if_value = calculateIF(235, 250);
// Ritorna: 0.94

4. Variability Index (VI)

Formula:

VI = NP / Potenza Media

Interpretazione per Disciplina:

Disciplina VI Tipico Significato
Strada TT / Sforzo Costante 1.00 - 1.05 Potenza molto costante, pacing ottimale
Gara Strada 1.05 - 1.10 Qualche scatto, generalmente costante
Criterium 1.10 - 1.20 Accelerazioni e attacchi frequenti
Mountain Bike XC 1.15 - 1.30+ Altamente variabile, scatti costanti

Esempio Calcolo:

Gara Strada: NP = 240W, Potenza Avg = 230W

VI = 240 / 230 = 1.04 (pacing costante)

Gara MTB: NP = 285W, Potenza Avg = 235W

VI = 285 / 235 = 1.21 (molto variabile, sforzi a scatti)

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

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

Critical Power & W' (Capacità Anaerobica)

5. Critical Power (CP) - Modello Lineare

Formula:

Tempo = W' / (Potenza - CP)
Riarrangiata: Potenza × Tempo = CP × Tempo + W'

Calcolo da Sforzi Multipli:

Richiede 2-4 sforzi massimali a diverse durate (es. 3, 5, 12, 20 minuti)

Dati Esempio:

Durata Potenza (W) Lavoro Totale (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

Usando regressione lineare (Lavoro = CP × Tempo + W'):

  • CP = 270W (pendenza linea regressione)
  • W' = 18.5 kJ (intercetta y)

Implementazione 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

  // Regressione lineare: 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,      // watt
    Wprime: Math.round(Wprime * 10) / 10  // kJ
  };
}

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

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

6. W' Balance (W'bal) - Modello Equazione Differenziale

Formule:

Spesa (quando P > CP):
W'exp(t) = ∫(P(t) - CP) dt
Recupero (quando P < CP):
W'rec(t) = W' × (1 - e^(-t/τ))
dove τ = 546 × e^(-0.01 × ΔCP) + 316
e ΔCP = (CP - P(t))

Esempio Mondo Reale:

Specifiche Ciclista: CP = 270W, W' = 18.5 kJ

Scenario 1 - Attacco Duro:

  • Ciclista scatta a 400W per 30 secondi
  • Spesa W': (400 - 270) × 30 = 3,900 J = 3.9 kJ
  • W'bal rimanente: 18.5 - 3.9 = 14.6 kJ

Scenario 2 - Recupero:

  • Dopo attacco, scende a 200W (70W sotto CP) per 2 minuti
  • ΔCP = 270 - 200 = 70W
  • τ = 546 × e^(-0.01 × 70) + 316 = 588 secondi
  • Recupero in 120s: 18.5 × (1 - e^(-120/588)) = 3.5 kJ recuperati
  • Nuovo W'bal: 14.6 + 3.5 = 18.1 kJ

Implementazione JavaScript:

function calculateWbalance(powerData, CP, Wprime) {
  // powerData = array di {time: seconds, power: watts}
  let wbal = Wprime * 1000; // Converti in joule
  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) {
      // Spesa sopra CP
      const expenditure = (power - CP) * dt;
      wbal -= expenditure;
    } else {
      // Recupero sotto 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;
    }

    // Assicura che W'bal non superi max o vada negativo
    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;
}

// Esempio uso:
const powerData = [
  {time: 0, power: 200},
  {time: 1, power: 210},
  // ... resto dati uscita
];

const wbalHistory = calculateWbalance(powerData, 270, 18.5);
// Ritorna array valori W'bal nel tempo

Performance Management Chart (PMC)

7. Calcoli CTL, ATL, TSB

Formule (Medie Mobili Ponderate Esponenzialmente):

CTL_oggi = CTL_ieri + (TSS_oggi - CTL_ieri) / 42
ATL_oggi = ATL_ieri + (TSS_oggi - ATL_ieri) / 7
TSB_oggi = CTL_ieri - ATL_ieri

Definizioni Metriche:

  • CTL (Chronic Training Load): Media ponderata esponenziale 42-giorni - rappresenta fitness
  • ATL (Acute Training Load): Media ponderata esponenziale 7-giorni - rappresenta fatica
  • TSB (Training Stress Balance): Forma = Fitness - Fatica

Esempio Svolto (Blocco Allenamento 7-Giorni):

Giorno TSS CTL ATL TSB Stato
Lun 100 75.0 80.0 -5.0 Allenamento
Mar 50 74.4 75.7 -1.3 Recupero
Mer 120 75.5 82.0 -6.5 Allenamento Duro
Gio 0 73.7 70.3 +3.4 Giorno Riposo
Ven 80 73.8 71.7 +2.1 Moderato
Sab 150 75.6 82.9 -7.3 Uscita Lunga
Dom 40 74.8 76.8 -2.0 Recupero

Interpretazione TSB:

Range TSB Stato Azione
< -30 Alto Rischio Avviso sovrallenamento - ridurre carico
-30 a -10 Allenamento Duro Costruendo fitness, monitorare recupero
-10 a +5 Ottimale Zona allenamento normale
+5 a +15 Pronto Gara Picco forma - gareggia questo weekend
> +25 Detraining Perdita fitness - aumentare carico

Implementazione JavaScript:

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

  workouts.forEach(workout => {
    // Aggiorna CTL (costante tempo 42-giorni)
    ctl = ctl + (workout.tss - ctl) / 42;

    // Aggiorna ATL (costante tempo 7-giorni)
    atl = atl + (workout.tss - atl) / 7;

    // Calcola TSB (CTL ieri - ATL oggi per calcolo tradizionale)
    // Per semplicità qui usando valori correnti
    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 "Alto Rischio";
  if (tsb < -10) return "Allenamento Duro";
  if (tsb < 5) return "Ottimale";
  if (tsb < 15) return "Pronto Gara";
  return "Detraining";
}

// Esempio uso:
const workouts = [
  {date: "2025-01-01", tss: 100},
  {date: "2025-01-02", tss: 50},
  {date: "2025-01-03", tss: 120},
  // ... altri allenamenti
];

const pmc = calculatePMC(workouts);
// Ritorna array con CTL, ATL, TSB per ogni giorno

Metriche Potenza-Peso & Salita

8. Rapporto Potenza-Peso

Formula:

W/kg = Potenza (watt) / Massa Corporea (kg)

Benchmark FTP W/kg:

Livello Uomo W/kg Donna W/kg Categoria
Ricreativo 2.5 - 3.5 2.0 - 3.0 Ciclista fitness
Competitivo 3.5 - 4.5 3.0 - 4.0 Cat 3-4, amatore age group
Avanzato 4.5 - 5.5 4.0 - 5.0 Cat 1-2, amatore forte
Amatore Elite 5.5 - 6.0 5.0 - 5.5 Livello nazionale
Professionista 6.0 - 7.0+ 5.5 - 6.5+ World Tour, Grand Tour GC

Esempio Calcolo:

Scenario: Ciclista con FTP = 275W, massa corporea = 70kg

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

Interpretazione: Livello competitivo, capace in gare collinari

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

// Esempio:
const wpkg = calculateWattsPerKg(275, 70);
// Ritorna: 3.93

9. VAM (Velocità Ascensionale Media)

Formula:

VAM (m/h) = Guadagno Elevazione (m) / Tempo (ore)

Benchmark VAM:

VAM (m/h) Livello Esempio
600 - 900 Ricreativo Ciclista club su salita locale
900 - 1200 Competitivo Buon amatore su Alpe d'Huez
1200 - 1500 Amatore Elite Scalatore livello nazionale
1500 - 1800 Professionista Gregario World Tour
> 1800 Vincitore Grand Tour Pogačar, Vingegaard su salite chiave

Esempio Calcolo:

Scenario: Salita Alpe d'Huez

  • Guadagno elevazione: 1100 metri
  • Tempo: 55 minuti = 0.917 ore
  • VAM = 1100 / 0.917 = 1200 m/h

Interpretazione: Prestazione scalata livello competitivo

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

// Esempio:
const vam = calculateVAM(1100, 55);
// Ritorna: 1200 m/h

10. Stima W/kg da VAM

Formula:

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

Esempio Calcolo:

Scenario: Salita con 8% pendenza media, VAM = 1200 m/h

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

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

Verifica: Con ciclista 70kg → 305W potenza sostenuta su salita

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

// Esempio:
const wkg = estimateWkgFromVAM(1200, 8);
// Ritorna: 4.36

Equazione Potenza Aerodinamica

11. Requisiti Potenza Totale

Formula Completa:

P_totale = P_aria + P_rotolamento + P_gravità + P_cinetica + P_perdite