Rumus Daya Bersepeda
Fondasi Matematika Metrik Analitik Sepeda
Panduan Implementasi
Halaman ini menyediakan rumus salin-tempel dan metode perhitungan langkah demi langkah untuk semua metrik Bike Analytics. Gunakan ini untuk implementasi kustom, verifikasi, atau pemahaman yang lebih dalam tentang latihan berbasis daya.
⚠️ Catatan Implementasi
- Semua nilai daya dalam watt (W), waktu dalam detik kecuali ditentukan lain
- FTP dan CP adalah ambang batas spesifik individu—tidak ada nilai universal
- Selalu validasi input untuk rentang yang wajar (biasanya 0-2000W)
- Tangani kasus tepi (pembagian dengan nol, daya negatif)
- Data daya memerlukan interval perekaman 1 detik untuk akurasi
Metrik Performa Inti
1. Skor Stres Latihan (TSS)
Rumus:
Contoh Perhitungan:
Skenario: Berkendara 2 jam, NP = 235W, FTP = 250W
- Hitung IF: IF = 235 / 250 = 0.94
- Durasi dalam detik: 2 jam × 3600 = 7200 detik
- TSS = (7200 × 235 × 0.94) / (250 × 3600) × 100
- TSS = 1,590,720 / 900,000 × 100 = 176.7 TSS
Interpretasi: Latihan keras (>150 TSS), perkirakan pemulihan 2-3 hari
Implementasi JavaScript:
function calculateTSS(durationSeconds, normalizedPower, ftp) {
const intensityFactor = normalizedPower / ftp;
const tss = (durationSeconds * normalizedPower * intensityFactor) / (ftp * 3600) * 100;
return Math.round(tss);
}
// Contoh penggunaan:
const tss = calculateTSS(7200, 235, 250);
// Mengembalikan: 177
2. Daya Ternormalisasi (NP)
Algoritma (Rata-rata bergulir 30 detik):
Mengapa Pangkat 4?
Hubungan kuartik (pangkat 4) mencerminkan biaya fisiologis non-linear dari upaya variabel. Perjalanan dengan lonjakan dan pemulihan menghabiskan lebih banyak energi daripada daya stabil pada rata-rata yang sama.
Contoh:
- Perjalanan stabil: 200W selama 1 jam → NP = 200W, Rata-rata = 200W
- Perjalanan variabel: Bergantian 300W/100W → Rata-rata = 200W, NP = 225W
Rata-rata daya sama, tetapi perjalanan variabel memiliki NP 12% lebih tinggi karena biaya fisiologis lonjakan
Implementasi JavaScript:
function calculateNormalizedPower(powerData) {
// powerData adalah array nilai daya 1-detik
// Langkah 1: Hitung rata-rata bergulir 30 detik
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);
}
// Langkah 2: Naikkan ke pangkat 4
const powered = rollingAvgs.map(p => Math.pow(p, 4));
// Langkah 3: Rata-rata pangkat 4
const avgPowered = powered.reduce((sum, p) => sum + p, 0) / powered.length;
// Langkah 4: Ambil akar pangkat 4
const np = Math.pow(avgPowered, 0.25);
return Math.round(np);
}
// Contoh penggunaan:
const powerData = [/* array daya 1-detik */];
const np = calculateNormalizedPower(powerData);
// Mengembalikan: NP dalam watt
3. Faktor Intensitas (IF)
Rumus:
Rentang Interpretasi:
| Rentang IF | Tingkat Usaha | Contoh Latihan |
|---|---|---|
| < 0.75 | Pemulihan / Mudah | Perjalanan pemulihan aktif, Zona 1-2 |
| 0.75 - 0.85 | Ketahanan | Perjalanan stabil panjang, basis aerobik |
| 0.85 - 0.95 | Tempo | Latihan sweet spot, interval tempo |
| 0.95 - 1.05 | Ambang Batas | Interval FTP, upaya time trial |
| 1.05 - 1.15 | VO₂max | Interval 5 menit, balapan criterium |
| > 1.15 | Anaerobik | Sprint pendek, serangan, ledakan MTB |
Contoh Perhitungan:
Skenario: NP = 235W, FTP = 250W
IF = 235 / 250 = 0.94
Interpretasi: Tempo tinggi / upaya sub-ambang batas, berkelanjutan selama 2-3 jam
function calculateIF(normalizedPower, ftp) {
return (normalizedPower / ftp).toFixed(2);
}
// Contoh:
const if_value = calculateIF(235, 250);
// Mengembalikan: 0.94
4. Indeks Variabilitas (VI)
Rumus:
Interpretasi berdasarkan Disiplin:
| Disiplin | VI Tipikal | Arti |
|---|---|---|
| Road TT / Upaya Stabil | 1.00 - 1.05 | Daya sangat konsisten, pacing optimal |
| Balap Jalan Raya | 1.05 - 1.10 | Beberapa lonjakan, umumnya stabil |
| Criterium | 1.10 - 1.20 | Akselerasi dan serangan yang sering |
| Mountain Bike XC | 1.15 - 1.30+ | Sangat bervariasi, lonjakan konstan |
Contoh Perhitungan:
Balap Jalan Raya: NP = 240W, Avg Power = 230W
VI = 240 / 230 = 1.04 (pacing stabil)
Balap MTB: NP = 285W, Avg Power = 235W
VI = 285 / 235 = 1.21 (sangat bervariasi, upaya meledak)
function calculateVI(normalizedPower, averagePower) {
return (normalizedPower / averagePower).toFixed(2);
}
// Contoh:
const vi_road = calculateVI(240, 230); // Mengembalikan: 1.04
const vi_mtb = calculateVI(285, 235); // Mengembalikan: 1.21
Critical Power & W' (Kapasitas Anaerobik)
5. Critical Power (CP) - Model Linear
Rumus:
Perhitungan dari Beberapa Upaya:
Memerlukan 2-4 upaya maksimal pada durasi yang berbeda (misalnya, 3, 5, 12, 20 menit)
Contoh Data:
| Durasi | Daya (W) | Total Kerja (kJ) |
|---|---|---|
| 3 menit (180s) | 400W | 72 kJ |
| 5 menit (300s) | 365W | 109.5 kJ |
| 12 menit (720s) | 310W | 223.2 kJ |
| 20 menit (1200s) | 285W | 342 kJ |
Menggunakan regresi linear (Kerja = CP × Waktu + W'):
- CP = 270W (kemiringan garis regresi)
- W' = 18.5 kJ (intersep-y)
Implementasi 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
// Regresi linear: 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
};
}
// Contoh penggunaan:
const efforts = [
{duration: 180, power: 400},
{duration: 300, power: 365},
{duration: 720, power: 310},
{duration: 1200, power: 285}
];
const result = calculateCP_Linear(efforts);
// Mengembalikan: { CP: 270.0, Wprime: 18.5 }
6. Saldo W' (W'bal) - Model Persamaan Diferensial
Rumus:
W'exp(t) = ∫(P(t) - CP) dt
W'rec(t) = W' × (1 - e^(-t/τ))
dan ΔCP = (CP - P(t))
Contoh Dunia Nyata:
Spesifikasi pesepeda: CP = 270W, W' = 18.5 kJ
Skenario 1 - Serangan Keras:
- Pengendara melonjak ke 400W selama 30 detik
- Pengeluaran W': (400 - 270) × 30 = 3,900 J = 3.9 kJ
- Saldo W'bal tersisa: 18.5 - 3.9 = 14.6 kJ
Skenario 2 - Pemulihan:
- Setelah serangan, turun ke 200W (70W di bawah CP) selama 2 menit
- ΔCP = 270 - 200 = 70W
- τ = 546 × e^(-0.01 × 70) + 316 = 588 detik
- Pemulihan dalam 120s: 18.5 × (1 - e^(-120/588)) = 3.5 kJ dipulihkan
- Saldo W'bal baru: 14.6 + 3.5 = 18.1 kJ
Implementasi JavaScript:
function calculateWbalance(powerData, CP, Wprime) {
// powerData = array of {time: seconds, power: watts}
let wbal = Wprime * 1000; // Konversi ke 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) {
// Pengeluaran di atas CP
const expenditure = (power - CP) * dt;
wbal -= expenditure;
} else {
// Pemulihan di bawah 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;
}
// Pastikan W'bal tidak melebihi maks atau menjadi negatif
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;
}
// Contoh penggunaan:
const powerData = [
{time: 0, power: 200},
{time: 1, power: 210},
// ... sisa data perjalanan
];
const wbalHistory = calculateWbalance(powerData, 270, 18.5);
// Mengembalikan array nilai W'bal dari waktu ke waktu
Bagan Manajemen Performa (PMC)
7. Perhitungan CTL, ATL, TSB
Rumus (Rata-rata Bergerak Tertimbang Eksponensial):
Definisi Metrik:
- CTL (Beban Latihan Kronis): Rata-rata tertimbang eksponensial 42 hari - mewakili kebugaran
- ATL (Beban Latihan Akut): Rata-rata tertimbang eksponensial 7 hari - mewakili kelelahan
- TSB (Saldo Stres Latihan): Form = Kebugaran - Kelelahan
Contoh Perhitungan (Blok Latihan 7 Hari):
| Hari | TSS | CTL | ATL | TSB | Status |
|---|---|---|---|---|---|
| Sen | 100 | 75.0 | 80.0 | -5.0 | Latihan |
| Sel | 50 | 74.4 | 75.7 | -1.3 | Pemulihan |
| Rab | 120 | 75.5 | 82.0 | -6.5 | Latihan Keras |
| Kam | 0 | 73.7 | 70.3 | +3.4 | Hari Istirahat |
| Jum | 80 | 73.8 | 71.7 | +2.1 | Sedang |
| Sab | 150 | 75.6 | 82.9 | -7.3 | Perjalanan Panjang |
| Min | 40 | 74.8 | 76.8 | -2.0 | Pemulihan |
Interpretasi TSB:
| Rentang TSB | Status | Tindakan |
|---|---|---|
| < -30 | Risiko Tinggi | Peringatan overtraining - kurangi beban |
| -30 hingga -10 | Latihan Keras | Membangun kebugaran, pantau pemulihan |
| -10 hingga +5 | Optimal | Zona latihan normal |
| +5 hingga +15 | Siap Balapan | Form puncak - balapan akhir pekan ini |
| > +25 | Detraining | Kehilangan kebugaran - tingkatkan beban |
Implementasi JavaScript:
function calculatePMC(workouts) {
// workouts = [{date: "YYYY-MM-DD", tss: number}, ...]
let ctl = 0, atl = 0;
const results = [];
workouts.forEach(workout => {
// Update CTL (konstanta waktu 42 hari)
ctl = ctl + (workout.tss - ctl) / 42;
// Update ATL (konstanta waktu 7 hari)
atl = atl + (workout.tss - atl) / 7;
// Hitung TSB (CTL kemarin - ATL hari ini untuk perhitungan tradisional)
// Untuk kesederhanaan di sini menggunakan nilai saat ini
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 "Risiko Tinggi";
if (tsb < -10) return "Latihan Keras";
if (tsb < 5) return "Optimal";
if (tsb < 15) return "Siap Balapan";
return "Detraining";
}
// Contoh penggunaan:
const workouts = [
{date: "2025-01-01", tss: 100},
{date: "2025-01-02", tss: 50},
{date: "2025-01-03", tss: 120},
// ... lebih banyak latihan
];
const pmc = calculatePMC(workouts);
// Mengembalikan array dengan CTL, ATL, TSB untuk setiap hari
Rasio Daya-terhadap-Berat & Metrik Menanjak
8. Rasio Daya-terhadap-Berat
Rumus:
Tolok Ukur FTP W/kg:
| Tingkat | W/kg Pria | W/kg Wanita | Kategori |
|---|---|---|---|
| Rekreasi | 2.5 - 3.5 | 2.0 - 3.0 | Pengendara kebugaran |
| Kompetitif | 3.5 - 4.5 | 3.0 - 4.0 | Cat 3-4, pembalap kelompok umur |
| Lanjutan | 4.5 - 5.5 | 4.0 - 5.0 | Cat 1-2, amatir kuat |
| Amatir Elit | 5.5 - 6.0 | 5.0 - 5.5 | Tingkat nasional |
| Profesional | 6.0 - 7.0+ | 5.5 - 6.5+ | World Tour, Grand Tour GC |
Contoh Perhitungan:
Skenario: Pesepeda dengan FTP = 275W, massa tubuh = 70kg
W/kg = 275 / 70 = 3.93 W/kg
Interpretasi: Tingkat kompetitif, mampu dalam balapan berbukit
function calculateWattsPerKg(power, bodyMassKg) {
return (power / bodyMassKg).toFixed(2);
}
// Contoh:
const wpkg = calculateWattsPerKg(275, 70);
// Mengembalikan: 3.93
9. VAM (Velocità Ascensionale Media)
Rumus:
Tolok Ukur VAM:
| VAM (m/jam) | Tingkat | Contoh |
|---|---|---|
| 600 - 900 | Rekreasi | Pengendara klub di tanjakan lokal |
| 900 - 1200 | Kompetitif | Amatir bagus di Alpe d'Huez |
| 1200 - 1500 | Amatir Elit | Pendaki tingkat nasional |
| 1500 - 1800 | Profesional | Domestique World Tour |
| > 1800 | Pemenang Grand Tour | Pogačar, Vingegaard di tanjakan utama |
Contoh Perhitungan:
Skenario: Tanjakan Alpe d'Huez
- Kenaikan elevasi: 1100 meter
- Waktu: 55 menit = 0.917 jam
- VAM = 1100 / 0.917 = 1200 m/jam
Interpretasi: Performa menanjak tingkat kompetitif
function calculateVAM(elevationGainMeters, timeMinutes) {
const hours = timeMinutes / 60;
return Math.round(elevationGainMeters / hours);
}
// Contoh:
const vam = calculateVAM(1100, 55);
// Mengembalikan: 1200 m/jam
10. Estimasi VAM ke W/kg
Rumus:
Contoh Perhitungan:
Skenario: Tanjakan dengan rata-rata gradien 8%, VAM = 1200 m/jam
W/kg = 1200 / 100 / (8 + 3)
W/kg = 12 / 11 = 4.36 W/kg
Pemeriksaan silang: Dengan pengendara 70kg → daya berkelanjutan 305W di tanjakan
function estimateWkgFromVAM(vam, gradientPercent) {
return (vam / 100 / (gradientPercent + 3)).toFixed(2);
}
// Contoh:
const wkg = estimateWkgFromVAM(1200, 8);
// Mengembalikan: 4.36
Persamaan Daya Aerodinamis
11. Kebutuhan Daya Total
Rumus Lengkap:
Rumus Komponen:
P_aero = CdA × 0.5 × ρ × V³
P_gravitasi = m × g × sin(θ) × V
P_rolling = Crr × m × g × cos(θ) × V
P_kinetik = m × a × V
Konstanta & Variabel:
- CdA = Koefisien hambat × area frontal (m²)
- Hoods sepeda jalan raya tipikal: 0.35-0.40 m²
- Drops: 0.32-0.37 m²
- Posisi TT: 0.20-0.25 m²
- ρ = Kepadatan udara (1.225 kg/m³ di permukaan laut, 15°C)
- V = Kecepatan (m/s)
- m = Massa total (pengendara + sepeda, kg)
- g = Gravitasi (9.81 m/s²)
- θ = Sudut gradien (radian atau derajat dikonversi)
- Crr = Koefisien hambatan gulir (~0.004 untuk ban jalan raya yang baik)
- a = Akselerasi (m/s²)
Contoh Perhitungan (TT Jalan Datar):
Skenario:
- Kecepatan: 40 km/jam = 11.11 m/s
- CdA: 0.22 m² (posisi TT yang baik)
- Total massa: 75kg (pengendara) + 8kg (sepeda) = 83kg
- Jalan datar (gradien = 0°)
- Kecepatan konstan (akselerasi = 0)
Perhitungan:
- P_aero = 0.22 × 0.5 × 1.225 × 11.11³ = 185W
- P_gravitasi = 0W (jalan datar)
- P_rolling = 0.004 × 83 × 9.81 × 11.11 = 36W
- P_kinetik = 0W (kecepatan konstan)
- P_total = 185 + 0 + 36 + 0 = 221W
Interpretasi: Membutuhkan 221W untuk mempertahankan 40 km/jam dalam posisi TT di jalan datar
Implementasi JavaScript:
function calculatePowerRequired(params) {
const {
velocityKph,
CdA = 0.32, // m²
rho = 1.225, // kg/m³
mass = 83, // kg (pengendara + sepeda)
gradientPercent = 0, // %
Crr = 0.004, // hambatan gulir
accelerationMps2 = 0 // m/s²
} = params;
// Konversi kecepatan ke m/s
const V = velocityKph / 3.6;
// Konversi gradien ke sudut
const theta = Math.atan(gradientPercent / 100);
// Hitung setiap komponen
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)
};
}
// Contoh: TT pada 40 km/jam
const power_tt = calculatePowerRequired({
velocityKph: 40,
CdA: 0.22,
mass: 83,
gradientPercent: 0
});
// Mengembalikan: { total: 221, aero: 185, gravity: 0, rolling: 36, kinetic: 0 }
// Contoh: Tanjakan 8% pada 15 km/jam
const power_climb = calculatePowerRequired({
velocityKph: 15,
CdA: 0.38,
mass: 75,
gradientPercent: 8
});
// Mengembalikan: { total: 265, aero: 27, gravity: 244, rolling: 11, kinetic: 0 }
Fungsi Pembantu
Utilitas Konversi Unit
Implementasi JavaScript:
// Konversi waktu
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')}`;
}
// Konversi kecepatan
function kphToMps(kph) {
return kph / 3.6;
}
function mpsToKph(mps) {
return mps * 3.6;
}
// Konversi energi
function joulesTo kJ(joules) {
return joules / 1000;
}
function kJToJoules(kJ) {
return kJ * 1000;
}
function wattsToKJ(watts, durationSeconds) {
return (watts * durationSeconds) / 1000;
}
// Contoh:
formatDuration(7265); // Mengembalikan: "2:01:05"
kphToMps(40); // Mengembalikan: 11.11 m/s
wattsToKJ(250, 3600); // Mengembalikan: 900 kJ (1 jam pada 250W)
Sumber Daya Implementasi
Semua rumus di halaman ini siap produksi dan divalidasi terhadap literatur ilmiah dan data power meter dunia nyata. Gunakan untuk alat analitik kustom, verifikasi, atau pemahaman yang lebih dalam tentang perhitungan latihan berbasis daya.
💡 Praktik Terbaik
- Validasi input: Periksa rentang daya yang wajar (0-2000W), durasi positif
- Tangani kasus tepi: Pembagian dengan nol, data null/undefined, FTP hilang
- Bulatkan dengan tepat: CTL/ATL/TSB ke 1 desimal, TSS ke bilangan bulat, W/kg ke 2 desimal
- Simpan presisi: Simpan presisi penuh di basis data, bulatkan hanya untuk tampilan
- Zona waktu: Tangani UTC vs. waktu lokal secara konsisten untuk analisis multi-hari
- Kalibrasi power meter: Ingatkan pengguna untuk melakukan zero-offset sebelum berkendara
- Validasi FTP: Tandai nilai FTP yang mencurigakan (>500W atau <100W untuk orang dewasa)
- Uji secara menyeluruh: Gunakan file perjalanan yang diketahui baik untuk memverifikasi perhitungan