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:
Exemplu Rezolvat:
Scenariu: Tură de 2 ore, NP = 235W, FTP = 250W
- Calculează IF: IF = 235 / 250 = 0.94
- Durata în secunde: 2 ore × 3600 = 7200 secunde
- TSS = (7200 × 235 × 0.94) / (250 × 3600) × 100
- 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):
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:
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:
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:
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:
W'exp(t) = ∫(P(t) - CP) dt
W'rec(t) = W' × (1 - e^(-t/τ))
ș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):
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:
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:
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:
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ă:
Formule Componente:
P_aero = CdA × 0.5 × ρ × V³
P_gravitație = m × g × sin(θ) × V
P_rulare = Crr × m × g × cos(θ) × V
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:
- P_aero = 0.22 × 0.5 × 1.225 × 11.11³ = 185W
- P_gravitație = 0W (drum plat)
- P_rulare = 0.004 × 83 × 9.81 × 11.11 = 36W
- P_cinetic = 0W (viteză constantă)
- 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