Formules de Puissance en Cyclisme
Fondement Mathématique des Métriques Bike Analytics
Guide d'Implémentation
Cette page fournit des formules prêtes à copier-coller et des méthodes de calcul étape par étape pour toutes les métriques Bike Analytics. Utilisez-les pour des implémentations personnalisées, la vérification ou une compréhension approfondie de l'entraînement basé sur la puissance.
⚠️ Notes d'Implémentation
- Toutes les valeurs de puissance en watts (W), temps en secondes sauf indication contraire
- FTP et CP sont des seuils individuels—aucune valeur universelle
- Toujours valider les entrées pour des plages raisonnables (0-2000W typique)
- Gérer les cas limites (division par zéro, puissance négative)
- Les données de puissance nécessitent des intervalles d'enregistrement de 1 seconde pour la précision
Métriques de Performance Essentielles
1. Training Stress Score (TSS)
Formule :
Exemple Détaillé :
Scénario : Sortie de 2 heures, NP = 235W, FTP = 250W
- Calculer IF : IF = 235 / 250 = 0.94
- Durée en secondes : 2 heures × 3600 = 7200 secondes
- TSS = (7200 × 235 × 0.94) / (250 × 3600) × 100
- TSS = 1,590,720 / 900,000 × 100 = 176.7 TSS
Interprétation : Sortie d'entraînement difficile (>150 TSS), prévoir 2-3 jours de récupération
Implémentation JavaScript :
function calculateTSS(durationSeconds, normalizedPower, ftp) {
const intensityFactor = normalizedPower / ftp;
const tss = (durationSeconds * normalizedPower * intensityFactor) / (ftp * 3600) * 100;
return Math.round(tss);
}
// Example usage:
const tss = calculateTSS(7200, 235, 250);
// Returns: 177
2. Normalized Power (NP)
Algorithme (moyenne mobile de 30 secondes) :
Pourquoi la Puissance 4 ?
La relation quartique (puissance 4) reflète le coût physiologique non linéaire des efforts variables. Une sortie avec des accélérations et des récupérations coûte plus d'énergie qu'une puissance constante à la même moyenne.
Exemple :
- Sortie régulière : 200W pendant 1 heure → NP = 200W, Moyenne = 200W
- Sortie variable : Alternance 300W/100W → Moyenne = 200W, NP = 225W
Même puissance moyenne, mais la sortie variable a un NP 12% plus élevé en raison du coût physiologique des accélérations
Implémentation JavaScript :
function calculateNormalizedPower(powerData) {
// powerData is array of 1-second power values
// Step 1: Calculate 30-second rolling averages
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: Raise to 4th power
const powered = rollingAvgs.map(p => Math.pow(p, 4));
// Step 3: Average of 4th powers
const avgPowered = powered.reduce((sum, p) => sum + p, 0) / powered.length;
// Step 4: Take 4th root
const np = Math.pow(avgPowered, 0.25);
return Math.round(np);
}
// Example usage:
const powerData = [/* 1-second power array */];
const np = calculateNormalizedPower(powerData);
// Returns: NP in watts
3. Intensity Factor (IF)
Formule :
Plages d'Interprétation :
| Plage IF | Niveau d'Effort | Exemple d'Entraînement |
|---|---|---|
| < 0.75 | Récupération / Facile | Sortie de récupération active, Zone 1-2 |
| 0.75 - 0.85 | Endurance | Longue sortie régulière, base aérobie |
| 0.85 - 0.95 | Tempo | Entraînement sweet spot, intervalles tempo |
| 0.95 - 1.05 | Seuil | Intervalles FTP, effort contre-la-montre |
| 1.05 - 1.15 | VO₂max | Intervalles de 5 minutes, course de critérium |
| > 1.15 | Anaérobie | Sprints courts, attaques, accélérations VTT |
Exemple de Calcul :
Scénario : NP = 235W, FTP = 250W
IF = 235 / 250 = 0.94
Interprétation : Effort tempo élevé / sous-seuil, soutenable pendant 2-3 heures
function calculateIF(normalizedPower, ftp) {
return (normalizedPower / ftp).toFixed(2);
}
// Example:
const if_value = calculateIF(235, 250);
// Returns: 0.94
4. Variability Index (VI)
Formule :
Interprétation par Discipline :
| Discipline | VI Typique | Signification |
|---|---|---|
| Route CLM / Effort Régulier | 1.00 - 1.05 | Puissance très constante, allure optimale |
| Course sur Route | 1.05 - 1.10 | Quelques accélérations, généralement régulier |
| Critérium | 1.10 - 1.20 | Accélérations et attaques fréquentes |
| VTT XC | 1.15 - 1.30+ | Très variable, accélérations constantes |
Exemple de Calcul :
Course sur Route : NP = 240W, Puissance Moy = 230W
VI = 240 / 230 = 1.04 (allure régulière)
Course VTT : NP = 285W, Puissance Moy = 235W
VI = 285 / 235 = 1.21 (très variable, efforts par à-coups)
function calculateVI(normalizedPower, averagePower) {
return (normalizedPower / averagePower).toFixed(2);
}
// Example:
const vi_road = calculateVI(240, 230); // Returns: 1.04
const vi_mtb = calculateVI(285, 235); // Returns: 1.21
Critical Power & W' (Capacité Anaérobie)
5. Critical Power (CP) - Modèle Linéaire
Formule :
Calcul à Partir de Plusieurs Efforts :
Nécessite 2-4 efforts maximaux à différentes durées (par ex., 3, 5, 12, 20 minutes)
Exemple de Données :
| Durée | Puissance (W) | Travail 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 |
En utilisant la régression linéaire (Travail = CP × Temps + W') :
- CP = 270W (pente de la ligne de régression)
- W' = 18.5 kJ (ordonnée à l'origine)
Implémentation 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
// Linear regression: 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, // watts
Wprime: Math.round(Wprime * 10) / 10 // kJ
};
}
// Example usage:
const efforts = [
{duration: 180, power: 400},
{duration: 300, power: 365},
{duration: 720, power: 310},
{duration: 1200, power: 285}
];
const result = calculateCP_Linear(efforts);
// Returns: { CP: 270.0, Wprime: 18.5 }
6. W' Balance (W'bal) - Modèle d'Équation Différentielle
Formules :
W'exp(t) = ∫(P(t) - CP) dt
W'rec(t) = W' × (1 - e^(-t/τ))
and ΔCP = (CP - P(t))
Exemple Concret :
Caractéristiques du cycliste : CP = 270W, W' = 18.5 kJ
Scénario 1 - Attaque Difficile :
- Le coureur accélère à 400W pendant 30 secondes
- Dépense de W' : (400 - 270) × 30 = 3,900 J = 3.9 kJ
- W'bal restant : 18.5 - 3.9 = 14.6 kJ
Scénario 2 - Récupération :
- Après l'attaque, redescend à 200W (70W sous CP) pendant 2 minutes
- ΔCP = 270 - 200 = 70W
- τ = 546 × e^(-0.01 × 70) + 316 = 588 secondes
- Récupération en 120s : 18.5 × (1 - e^(-120/588)) = 3.5 kJ récupérés
- Nouveau W'bal : 14.6 + 3.5 = 18.1 kJ
Implémentation JavaScript :
function calculateWbalance(powerData, CP, Wprime) {
// powerData = array of {time: seconds, power: watts}
let wbal = Wprime * 1000; // Convert to 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) {
// Expenditure above CP
const expenditure = (power - CP) * dt;
wbal -= expenditure;
} else {
// Recovery below 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;
}
// Ensure W'bal doesn't exceed max or go negative
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;
}
// Example usage:
const powerData = [
{time: 0, power: 200},
{time: 1, power: 210},
// ... rest of ride data
];
const wbalHistory = calculateWbalance(powerData, 270, 18.5);
// Returns array of W'bal values over time
Performance Management Chart (PMC)
7. Calculs CTL, ATL, TSB
Formules (Moyennes Mobiles Exponentiellement Pondérées) :
Définitions des Métriques :
- CTL (Chronic Training Load) : Moyenne exponentiellement pondérée sur 42 jours - représente la forme
- ATL (Acute Training Load) : Moyenne exponentiellement pondérée sur 7 jours - représente la fatigue
- TSB (Training Stress Balance) : Forme = Condition - Fatigue
Exemple Détaillé (Bloc d'Entraînement de 7 Jours) :
| Jour | TSS | CTL | ATL | TSB | Statut |
|---|---|---|---|---|---|
| Lun | 100 | 75.0 | 80.0 | -5.0 | Entraînement |
| Mar | 50 | 74.4 | 75.7 | -1.3 | Récupération |
| Mer | 120 | 75.5 | 82.0 | -6.5 | Entraînement Difficile |
| Jeu | 0 | 73.7 | 70.3 | +3.4 | Jour de Repos |
| Ven | 80 | 73.8 | 71.7 | +2.1 | Modéré |
| Sam | 150 | 75.6 | 82.9 | -7.3 | Longue Sortie |
| Dim | 40 | 74.8 | 76.8 | -2.0 | Récupération |
Interprétation TSB :
| Plage TSB | Statut | Action |
|---|---|---|
| < -30 | Risque Élevé | Alerte surentraînement - réduire la charge |
| -30 à -10 | Entraînement Intensif | Construction de forme, surveiller la récupération |
| -10 à +5 | Optimal | Zone d'entraînement normale |
| +5 à +15 | Prêt à Courir | Forme de pointe - courir ce week-end |
| > +25 | Désentraînement | Perte de forme - augmenter la charge |
Implémentation JavaScript :
function calculatePMC(workouts) {
// workouts = [{date: "YYYY-MM-DD", tss: number}, ...]
let ctl = 0, atl = 0;
const results = [];
workouts.forEach(workout => {
// Update CTL (42-day time constant)
ctl = ctl + (workout.tss - ctl) / 42;
// Update ATL (7-day time constant)
atl = atl + (workout.tss - atl) / 7;
// Calculate TSB (yesterday's CTL - today's ATL for traditional calculation)
// For simplicity here using current values
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 "High Risk";
if (tsb < -10) return "Training Hard";
if (tsb < 5) return "Optimal";
if (tsb < 15) return "Race Ready";
return "Detraining";
}
// Example usage:
const workouts = [
{date: "2025-01-01", tss: 100},
{date: "2025-01-02", tss: 50},
{date: "2025-01-03", tss: 120},
// ... more workouts
];
const pmc = calculatePMC(workouts);
// Returns array with CTL, ATL, TSB for each day
Rapport Poids-Puissance & Métriques d'Ascension
8. Rapport Poids-Puissance
Formule :
Repères FTP W/kg :
| Niveau | Homme W/kg | Femme W/kg | Catégorie |
|---|---|---|---|
| Récréatif | 2.5 - 3.5 | 2.0 - 3.0 | Cycliste loisir |
| Compétitif | 3.5 - 4.5 | 3.0 - 4.0 | Cat 3-4, coureur par groupes d'âge |
| Avancé | 4.5 - 5.5 | 4.0 - 5.0 | Cat 1-2, amateur fort |
| Élite Amateur | 5.5 - 6.0 | 5.0 - 5.5 | Niveau national |
| Professionnel | 6.0 - 7.0+ | 5.5 - 6.5+ | World Tour, Grand Tour GC |
Exemple de Calcul :
Scénario : Cycliste avec FTP = 275W, masse corporelle = 70kg
W/kg = 275 / 70 = 3.93 W/kg
Interprétation : Niveau compétitif, capable en courses vallonnées
function calculateWattsPerKg(power, bodyMassKg) {
return (power / bodyMassKg).toFixed(2);
}
// Example:
const wpkg = calculateWattsPerKg(275, 70);
// Returns: 3.93
9. VAM (Velocità Ascensionale Media)
Formule :
Repères VAM :
| VAM (m/h) | Niveau | Exemple |
|---|---|---|
| 600 - 900 | Récréatif | Cycliste club sur montée locale |
| 900 - 1200 | Compétitif | Bon amateur sur Alpe d'Huez |
| 1200 - 1500 | Élite Amateur | Grimpeur niveau national |
| 1500 - 1800 | Professionnel | Équipier World Tour |
| > 1800 | Vainqueur Grand Tour | Pogačar, Vingegaard sur montées clés |
Exemple de Calcul :
Scénario : Montée de l'Alpe d'Huez
- Dénivelé positif : 1100 mètres
- Temps : 55 minutes = 0.917 heures
- VAM = 1100 / 0.917 = 1200 m/h
Interprétation : Performance de grimpeur niveau compétitif
function calculateVAM(elevationGainMeters, timeMinutes) {
const hours = timeMinutes / 60;
return Math.round(elevationGainMeters / hours);
}
// Example:
const vam = calculateVAM(1100, 55);
// Returns: 1200 m/h
10. Estimation W/kg à Partir de VAM
Formule :
Exemple de Calcul :
Scénario : Montée avec pente moyenne de 8%, VAM = 1200 m/h
W/kg = 1200 / 100 / (8 + 3)
W/kg = 12 / 11 = 4.36 W/kg
Vérification croisée : Avec un cycliste de 70kg → 305W de puissance soutenue en montée
function estimateWkgFromVAM(vam, gradientPercent) {
return (vam / 100 / (gradientPercent + 3)).toFixed(2);
}
// Example:
const wkg = estimateWkgFromVAM(1200, 8);
// Returns: 4.36
Équation de Puissance Aérodynamique
11. Besoins en Puissance Totale
Formule Complète :
Formules des Composantes :
P_aero = CdA × 0.5 × ρ × V³
P_gravity = m × g × sin(θ) × V
P_rolling = Crr × m × g × cos(θ) × V
P_kinetic = m × a × V
Constantes & Variables :
- CdA = Coefficient de traînée × surface frontale (m²)
- Vélo route typique sur cocottes : 0.35-0.40 m²
- Dans le cintre : 0.32-0.37 m²
- Position contre-la-montre : 0.20-0.25 m²
- ρ = Densité de l'air (1.225 kg/m³ au niveau de la mer, 15°C)
- V = Vitesse (m/s)
- m = Masse totale (cycliste + vélo, kg)
- g = Gravité (9.81 m/s²)
- θ = Angle de pente (radians ou degrés convertis)
- Crr = Coefficient de résistance au roulement (~0.004 pour bons pneus route)
- a = Accélération (m/s²)
Exemple Détaillé (CLM sur Route Plate) :
Scénario :
- Vitesse : 40 km/h = 11.11 m/s
- CdA : 0.22 m² (bonne position CLM)
- Masse totale : 75kg (cycliste) + 8kg (vélo) = 83kg
- Route plate (pente = 0°)
- Vitesse constante (accélération = 0)
Calcul :
- P_aero = 0.22 × 0.5 × 1.225 × 11.11³ = 185W
- P_gravity = 0W (route plate)
- P_rolling = 0.004 × 83 × 9.81 × 11.11 = 36W
- P_kinetic = 0W (vitesse constante)
- P_total = 185 + 0 + 36 + 0 = 221W
Interprétation : Besoin de 221W pour maintenir 40 km/h en position CLM sur route plate
Implémentation JavaScript :
function calculatePowerRequired(params) {
const {
velocityKph,
CdA = 0.32, // m²
rho = 1.225, // kg/m³
mass = 83, // kg (rider + bike)
gradientPercent = 0, // %
Crr = 0.004, // rolling resistance
accelerationMps2 = 0 // m/s²
} = params;
// Convert velocity to m/s
const V = velocityKph / 3.6;
// Convert gradient to angle
const theta = Math.atan(gradientPercent / 100);
// Calculate each 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)
};
}
// Example: TT at 40 km/h
const power_tt = calculatePowerRequired({
velocityKph: 40,
CdA: 0.22,
mass: 83,
gradientPercent: 0
});
// Returns: { total: 221, aero: 185, gravity: 0, rolling: 36, kinetic: 0 }
// Example: 8% climb at 15 km/h
const power_climb = calculatePowerRequired({
velocityKph: 15,
CdA: 0.38,
mass: 75,
gradientPercent: 8
});
// Returns: { total: 265, aero: 27, gravity: 244, rolling: 11, kinetic: 0 }
Fonctions Auxiliaires
Utilitaires de Conversion d'Unités
Implémentation JavaScript :
// Time conversions
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')}`;
}
// Speed conversions
function kphToMps(kph) {
return kph / 3.6;
}
function mpsToKph(mps) {
return mps * 3.6;
}
// Energy conversions
function joulesTo kJ(joules) {
return joules / 1000;
}
function kJToJoules(kJ) {
return kJ * 1000;
}
function wattsToKJ(watts, durationSeconds) {
return (watts * durationSeconds) / 1000;
}
// Examples:
formatDuration(7265); // Returns: "2:01:05"
kphToMps(40); // Returns: 11.11 m/s
wattsToKJ(250, 3600); // Returns: 900 kJ (1 hour at 250W)
Ressources d'Implémentation
Toutes les formules de cette page sont prêtes pour la production et validées par rapport à la littérature scientifique et aux données réelles de capteurs de puissance. Utilisez-les pour des outils d'analyse personnalisés, la vérification ou une compréhension approfondie des calculs d'entraînement basés sur la puissance.
💡 Meilleures Pratiques
- Valider les entrées : Vérifier les plages de puissance raisonnables (0-2000W), durées positives
- Gérer les cas limites : Division par zéro, données null/undefined, FTP manquant
- Arrondir de manière appropriée : CTL/ATL/TSB à 1 décimale, TSS en entier, W/kg à 2 décimales
- Stocker la précision : Conserver la précision complète en base de données, arrondir uniquement pour l'affichage
- Fuseaux horaires : Gérer UTC vs. heure locale de manière cohérente pour l'analyse multi-jours
- Calibration du capteur de puissance : Rappeler aux utilisateurs de faire le zéro avant les sorties
- Validation FTP : Signaler les valeurs FTP suspectes (>500W ou <100W pour adultes)
- Tester minutieusement : Utiliser des fichiers de sortie validés pour vérifier les calculs