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:

TSS = (durasi_detik × NP × IF) / (FTP × 3600) × 100
di mana IF = NP / FTP

Contoh Perhitungan:

Skenario: Berkendara 2 jam, NP = 235W, FTP = 250W

  1. Hitung IF: IF = 235 / 250 = 0.94
  2. Durasi dalam detik: 2 jam × 3600 = 7200 detik
  3. TSS = (7200 × 235 × 0.94) / (250 × 3600) × 100
  4. 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):

1. Hitung rata-rata bergulir 30 detik daya untuk seluruh perjalanan
2. Naikkan setiap nilai 30-detik ke pangkat 4
3. Ambil rata-rata dari semua nilai ^4 ini
4. Ambil akar pangkat 4 dari rata-rata itu
NP = ⁴√(rata-rata dari [30s_avg^4])

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:

IF = NP / FTP

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:

VI = NP / Daya Rata-rata

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:

Waktu = W' / (Daya - CP)
Diatur Ulang: Daya × Waktu = CP × Waktu + W'

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:

Pengeluaran (ketika P > CP):
W'exp(t) = ∫(P(t) - CP) dt
Pemulihan (ketika P < CP):
W'rec(t) = W' × (1 - e^(-t/τ))
di mana τ = 546 × e^(-0.01 × ΔCP) + 316
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):

CTL_hari_ini = CTL_kemarin + (TSS_hari_ini - CTL_kemarin) / 42
ATL_hari_ini = ATL_kemarin + (TSS_hari_ini - ATL_kemarin) / 7
TSB_hari_ini = CTL_kemarin - ATL_kemarin

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:

W/kg = Daya (watt) / Massa Tubuh (kg)

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:

VAM (m/jam) = Kenaikan Elevasi (m) / Waktu (jam)

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:

W/kg ≈ VAM (m/jam) / 100 / (Gradien% + 3)

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:

P_total = P_aero + P_gravitasi + P_rolling + P_kinetik

Rumus Komponen:

Hambatan Aerodinamis:
P_aero = CdA × 0.5 × ρ × V³
Gravitasi (Menanjak):
P_gravitasi = m × g × sin(θ) × V
Hambatan Gulir:
P_rolling = Crr × m × g × cos(θ) × V
Kinetik (Akselerasi):
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:

  1. P_aero = 0.22 × 0.5 × 1.225 × 11.11³ = 185W
  2. P_gravitasi = 0W (jalan datar)
  3. P_rolling = 0.004 × 83 × 9.81 × 11.11 = 36W
  4. P_kinetik = 0W (kecepatan konstan)
  5. 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