Membangun Robot Line Follower 5-Channel dengan Kendali PID & Rute Kustom

Pelajari cara merakit, memprogram, dan men-tuning robot pengikut garis cerdas yang mampu melewati persimpangan rumit serta mendeteksi garis Start dan Finish secara otomatis.

1. Konsep & Arsitektur

Robot Line Follower (Pengikut Garis) ini menggunakan Arduino Nano sebagai otak pemroses. Berbeda dengan robot dasar yang hanya berbelok patah-patah, robot ini menggunakan algoritma PID (Proportional, Integral, Derivative).

P (Proportional)

Bereaksi terhadap error saat ini. Semakin jauh robot dari garis tengah, semakin kuat ia akan berbelok kembali.

I (Integral)

Mengumpulkan histori error. Berguna untuk mengatasi lintasan miring/tidak rata (Sering diset 0 untuk line follower).

D (Derivative)

Memprediksi error masa depan. Berfungsi sebagai "rem" untuk mencegah robot bergetar (osilasi) berlebihan saat kembali ke tengah.

2. Alat, Bahan & Wiring

Daftar Komponen

  • 1x Arduino Nano & Kabel USB
  • 1x Modul Driver Motor L298N
  • 1x Modul Sensor IR 5-Channel (TCRT5000/BFD-1000)
  • 2x Motor DC + Gearbox + Roda
  • 1x Roda Bebas (Caster Wheel) & Sasis Robot
  • 1x Baterai LiPo 2S (7.4V) atau 2x 18650
  • Kabel Jumper secukupnya & Saklar On/Off

Tabel Koneksi (Wiring)

Komponen APinKomponen BPin
Baterai (+)MerahL298N12V
Baterai (-)HitamL298N & ArduinoGND (Common)
L298N5V OutArduino5V In
Sensor 5-ChS1, S2, S3, S4, S5ArduinoA0, A1, A2, A3, A4
L298N (Kendali)ENA, IN1..IN4, ENBArduinoD5, D6, D7, D8, D9, D10

*Pastikan jumper 5V enable pada L298N terpasang jika menggunakan L298N untuk memberi daya ke Arduino.*

3. Simulator Logika Sensor

Pahami bagaimana letak garis di bawah robot memengaruhi nilai error. Klik kotak sensor di bawah ini untuk mensimulasikan garis hitam (warna gelap) dan latar putih (warna terang).

Hasil Pembacaan CPU

Error: 0
Lurus Sempurna

4. Script Arduino Master

Script ini mengintegrasikan PID, manajemen rute persimpangan, dan deteksi Start/Finish otomatis. Copy kode ini dan paste di Arduino IDE.

LineFollower_Pro.ino
// ======================================================
// LINE FOLLOWER 5 CH - PID, MULTI-BRANCH, START & FINISH
// ======================================================

// --- Pin Driver Motor L298N ---
#define ENA 5
#define IN1 6
#define IN2 7
#define IN3 8
#define IN4 9
#define ENB 10

// --- Pin Sensor IR 5-Channel ---
#define S1 A0 
#define S2 A1 
#define S3 A2 
#define S4 A3 
#define S5 A4 

// --- Pengaturan Rute & Logika Jalur ---
// 1 = Belok Kiri, 2 = Belok Kanan, 0 = Lurus
int rute[] = {1, 2}; 
int indexCabang = 0;         // Penunjuk rute saat ini
int jumlahCabangDiTengah = 2; // Total persimpangan di array rute
int totalHitam = 0;          // Penghitung garis hitam tebal

// --- Variabel Kecepatan & PID ---
int baseSpeed = 100;
int maxSpeed = 200;
float Kp = 15.0, Ki = 0.0, Kd = 10.0;
float error = 0, previous_error = 0, PID_value = 0;

bool robotBerhenti = false;

void setup() {
  pinMode(ENA, OUTPUT); pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT); pinMode(ENB, OUTPUT);
  pinMode(S1, INPUT); pinMode(S2, INPUT); pinMode(S3, INPUT);
  pinMode(S4, INPUT); pinMode(S5, INPUT);
}

void loop() {
  if (robotBerhenti) {
    stopRobot();
    return;
  }
  bacaSensor();
  hitungPID();
  kontrolMotor();
}

void bacaSensor() {
  int s1 = digitalRead(S1); int s2 = digitalRead(S2);
  int s3 = digitalRead(S3); int s4 = digitalRead(S4);
  int s5 = digitalRead(S5);

  // KONDISI KHUSUS: SEMUA HITAM (Start, Cabang, atau Finish)
  if (s1 == 1 && s2 == 1 && s3 == 1 && s4 == 1 && s5 == 1) {
    totalHitam++; 

    if (totalHitam == 1) { // A. GARIS START
      error = 0; 
      majuDikit(); 
    }
    else if (totalHitam > 1 && indexCabang < jumlahCabangDiTengah) { // B. CABANG
      int arah = rute[indexCabang];
      if (arah == 1) error = -5;      // Kiri
      else if (arah == 2) error = 5;  // Kanan
      else error = 0;                 // Lurus
      indexCabang++;
      majuDikit(); 
    }
    else { // C. FINISH
      robotBerhenti = true;
    }
  } 
  // LOGIKA PID NORMAL
  else if (s1 == 1 && s2 == 0 && s3 == 0 && s4 == 0 && s5 == 0) error = -4;
  else if (s1 == 1 && s2 == 1 && s3 == 0 && s4 == 0 && s5 == 0) error = -3;
  else if (s1 == 0 && s2 == 1 && s3 == 0 && s4 == 0 && s5 == 0) error = -2;
  else if (s1 == 0 && s2 == 1 && s3 == 1 && s4 == 0 && s5 == 0) error = -1;
  else if (s1 == 0 && s2 == 0 && s3 == 1 && s4 == 0 && s5 == 0) error = 0;
  else if (s1 == 0 && s2 == 0 && s3 == 1 && s4 == 1 && s5 == 0) error = 1;
  else if (s1 == 0 && s2 == 0 && s3 == 0 && s4 == 1 && s5 == 0) error = 2;
  else if (s1 == 0 && s2 == 0 && s3 == 0 && s4 == 1 && s5 == 1) error = 3;
  else if (s1 == 0 && s2 == 0 && s3 == 0 && s4 == 0 && s5 == 1) error = 4;
  else if (s1 == 0 && s2 == 0 && s3 == 0 && s4 == 0 && s5 == 0) {
    if (previous_error < 0) error = -5; else error = 5;
  }
}

void hitungPID() {
  float P = error;
  float I = I + error;
  float D = error - previous_error;
  PID_value = (Kp * P) + (Ki * I) + (Kd * D);
  previous_error = error;
}

void kontrolMotor() {
  int leftSpeed = baseSpeed + PID_value;
  int rightSpeed = baseSpeed - PID_value;
  leftSpeed = constrain(leftSpeed, -200, maxSpeed);
  rightSpeed = constrain(rightSpeed, -200, maxSpeed);
  moveMotor(leftSpeed, rightSpeed);
}

void moveMotor(int left, int right) {
  if (left >= 0) { digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); } 
  else { digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); }
  
  if (right >= 0) { digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); } 
  else { digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); }
  
  analogWrite(ENA, abs(left)); analogWrite(ENB, abs(right));
}

void majuDikit() {
  moveMotor(baseSpeed, baseSpeed);
  delay(200); // Waktu lompatan garis tebal
}

void stopRobot() {
  digitalWrite(IN1, LOW); digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW); digitalWrite(IN4, LOW);
  analogWrite(ENA, 0); analogWrite(ENB, 0);
}

5. Panduan Tuning PID

Mengisi kode saja tidak cukup. Kemampuan motor, berat baterai, dan sasis setiap robot berbeda. Anda wajib melakukan tuning (kalibrasi) nilai Kp, Ki, Kd.

1

Setel Awal (Ground Zero)

Ubah nilai di kode menjadi Kp = 5.0; Ki = 0.0; Kd = 0.0;. Pastikan robot berjalan di atas lintasan. Jika ia tidak merespon tikungan dan keluar jalur, naikkan nilai Kp (misal 10, 15, 20).

2

Atasi Gemetar (Osilasi)

Jika nilai Kp sudah pas untuk berbelok, robot biasanya akan "menggeleng" keras ke kiri dan kanan. Untuk menghaluskannya (mengerem efek Kp), naikkan nilai Kd sedikit demi sedikit (misal 5, 10, 15).

3

Kalibrasi Delay Persimpangan

Perhatikan variabel delay(200); di dalam fungsi majuDikit(). Jika robot berhenti sebelum mencapai Finish (karena salah mengira garis persimpangan sebagai garis finish), Naikkan delay. Jika robot sering kelewatan persimpangan tanpa belok, Turunkan delay.

Penjelasan Logika Flow

  • Garis Start: Saat robot baru dinyalakan dan berada di atas garis hitam penuh (semua sensor 1), variabel totalHitam menjadi 1. Robot menjalankan fungsi majuDikit(). Ini "memaksa" robot keluar dari garis start selama 0,2 detik sehingga ia masuk ke mode PID biasa.
  • Cabang di Tengah: Jika robot ketemu garis hitam penuh lagi, dan kita masih punya rute di array rute[], robot akan mengambil instruksi (belok kiri/kanan) lalu majuDikit() lagi agar tidak membaca garis yang sama berulang-ulang.
  • Garis Finish: Jika totalHitam sudah melebihi jumlah cabang yang didaftarkan, robot akan menganggap garis hitam penuh berikutnya adalah Finish dan memicu robotBerhenti = true.

Cara Menentukan Rute

Jika lintasan Anda adalah: START → Belok Kiri → Belok Kanan → FINISH.

Maka settingannya adalah:

int rute[] = {1, 2};
int jumlahCabangDiTengah = 2;

Jika lintasan Anda hanya: START → FINISH (tanpa cabang).

Maka settingannya adalah:

int rute[] = {};
int jumlahCabangDiTengah = 0;

Tips Tuning Tambahan

Jika robot berhenti sebelum benar-benar sampai finish (karena membaca garis cabang sebagai finish), naikkan nilai delay(200) pada fungsi majuDikit(). Sebaliknya, jika robot melewatkan cabang, kecilkan nilainya.

Halaman 1 dari 5
✅ Kode berhasil disalin!
v