Sistem Robot Pendeteksi Warna dengan Sensor Ultrasonik Berbasis Raspberry Pi dan OpenCV

Sistem Robot Pendeteksi Warna dengan Sensor Ultrasonik Berbasis Raspberry Pi dan OpenCV

Kelompok RE-3D/6 — Dhesta Athaya S., Khoirudin, Rachmadhani Aji K., Theofilus Ogawa G.

Abstrak

Praktikum ini menghasilkan robot beroda dua motor yang bergerak berdasarkan warna yang ditangkap kamera PiCam dan diproses dengan OpenCV pada ruang warna HSV. Tiga warna dikenali: hijau, kuning, dan merah, masing-masing dipetakan ke perintah maju penuh, maju pelan, dan berhenti. Perintah warna terakhir disimpan dalam variabel global sehingga robot tetap menjalankan perintah tersebut walau objek berwarna sudah keluar dari bidang pandang kamera. Sensor ultrasonik HC-SR04 berjalan pada thread terpisah untuk memantau jarak ke penghalang secara terus-menerus; pada jarak di bawah 5 cm robot dihentikan paksa dan buzzer dimatikan, sedangkan pada rentang 5-15 cm robot diperlambat menjadi 30% dan buzzer berbunyi berselang 0,3 detik.

Kata kunci: OpenCV, HSV, Raspberry Pi, sensor ultrasonik, robot pendeteksi warna.

I. Pendahuluan

A. Latar Belakang

Robot pengikut warna umum digunakan sebagai media praktikum untuk memperkenalkan pengolahan citra digital pada sistem tertanam. Pada praktikum kelompok RE-3D/6, robot dirancang untuk membaca warna melalui kamera PiCam yang terpasang pada Raspberry Pi, lalu mengubah warna tersebut menjadi perintah gerak. Tiga warna dipilih karena kemiripannya dengan lampu lalu lintas: hijau untuk maju, kuning untuk melambat, dan merah untuk berhenti.

Masalah yang sering muncul pada robot semacam ini adalah hilangnya objek warna dari bidang pandang kamera saat robot bergerak, sehingga robot berhenti mendadak atau kehilangan arah. Selain itu, robot yang hanya mengandalkan kamera berisiko menabrak dinding atau benda di depannya karena kamera tidak mengukur jarak secara langsung. Kedua masalah ini menjadi dasar penambahan mekanisme penyimpanan perintah terakhir dan sensor ultrasonik pada rancangan robot.

B. Rumusan Masalah

1) Bagaimana merancang program yang dapat mendeteksi warna hijau, kuning, dan merah secara langsung dari kamera dan mengubahnya menjadi perintah gerak robot? 

2) Bagaimana agar robot tetap menjalankan perintah warna yang sedang aktif meski objek warna sudah tidak terlihat oleh kamera? 

3) Bagaimana mencegah robot menabrak penghalang di depannya tanpa mengganggu proses pembacaan warna yang berjalan bersamaan?

C. Tujuan

Praktikum ini bertujuan membangun robot yang dapat mengubah warna hijau, kuning, dan merah menjadi perintah maju penuh, maju pelan, dan berhenti; mempertahankan perintah warna terakhir selama belum ada warna baru yang terdeteksi; serta menambahkan sensor ultrasonik sebagai pengaman independen yang bekerja pada thread terpisah agar pembacaan jarak tidak terhambat oleh proses pengolahan citra.

II. Metodologi

Robot dikembangkan melalui empat tahap. Tahap pertama adalah pemilihan komponen dan pengecekan kompatibilitas antara Raspberry Pi, kamera PiCam, motor DC, sensor HC-SR04, dan buzzer. Tahap kedua adalah perancangan diagram blok, diagram alir, rangkaian skematik, dan diagram pengawatan sebagai acuan sebelum pemrograman, sebagaimana disajikan pada bagian Kajian Pustaka. Tahap ketiga adalah penulisan program dengan Python, mencakup fungsi deteksi warna, fungsi kendali robot, dan thread pembacaan sensor. Tahap keempat adalah pengujian langsung: kartu warna digerakkan di depan kamera untuk menguji logika perintah warna, dan tangan didekatkan secara bertahap ke sensor untuk menguji ketiga level jarak (aman, hati-hati, bahaya).

III. Kajian Pustaka

A. Komponen

1.laptop
Gambar 1.laptop
Laptop dipakai sebagai perangkat untuk menulis dan meng-upload program ke Raspberry Pi, biasanya lewat SSH atau VNC (remote desktop) karena Raspberry Pi tidak selalu dipasangi monitor sendiri saat robot bergerak bebas. Laptop juga dipakai untuk memantau output program secara langsung, misalnya jendela tampilan OpenCV yang menunjukkan hasil deteksi warna, sebelum program dijalankan mandiri di Raspberry Pi.

2.Raspberry Pi 4
Gambar 2.Rasberry pi 4
Raspberry Pi 4 adalah komputer papan tunggal (single-board computer) yang menjadi otak dari robot ini. Pi 4 dipakai karena punya prosesor quad-core Cortex-A72 dan RAM yang cukup (1-8 GB tergantung varian) untuk menjalankan OpenCV secara langsung tanpa terlalu lambat, sekaligus punya pin GPIO untuk mengendalikan motor, sensor, dan buzzer, serta port CSI khusus untuk kamera PiCam.

3.Sensor Ultrasonik HC-SR04

Gambar 3.hcsr04
HC-SR04 mengukur jarak dengan cara memancarkan gelombang ultrasonik lewat pin Trigger, lalu menghitung waktu tempuh pantulan gelombang yang diterima pin Echo. Waktu tempuh itu dikalikan kecepatan suara dan dibagi dua (karena gelombang menempuh jarak pergi-pulang) untuk mendapatkan jarak ke objek. Sensor ini punya jangkauan efektif sekitar 2-400 cm, cukup untuk mendeteksi dinding atau penghalang di depan robot pada jarak dekat.

4.Buzzer

Gambar 4.Bzuzzer
Buzzer adalah komponen yang mengubah sinyal listrik menjadi bunyi, dipakai di sini sebagai peringatan audio saat robot mendekati penghalang. Pada rangkaian ini buzzer dikendalikan langsung lewat satu pin GPIO (nyala/mati), sehingga pola bunyinya (berbunyi berselang atau diam total) diatur lewat program, bukan lewat rangkaian analog tambahan.

5.Motor DC 12V
Gambar 5. Motor DC 12V
Motor DC 12V adalah penggerak roda robot. Motor ini butuh tegangan dan arus lebih besar dari yang bisa disuplai langsung oleh pin GPIO Raspberry Pi (yang hanya sekitar 3.3V dan arus kecil), sehingga tidak bisa dihubungkan langsung ke Pi — di sinilah driver motor berperan sebagai perantara.

6.Driver motor
Gambar 6.Driver Motor
Driver motor adalah modul elektronik (biasanya berbasis rangkaian H-bridge) yang menjembatani sinyal logika dari Raspberry Pi dengan daya besar yang dibutuhkan motor DC. Raspberry Pi hanya mengirim sinyal kendali arah dan kecepatan (lewat PWM) ke driver, sedangkan driver yang mengatur aliran arus 12V ke motor. Modul ini juga melindungi Raspberry Pi dari arus balik motor yang bisa merusak pin GPIO.

7.Camera PI
Gambar 7. Camera pi
Kamera PiCam adalah modul kamera resmi untuk Raspberry Pi yang terhubung lewat port CSI (Camera Serial Interface), bukan lewat USB. Koneksi CSI ini memungkinkan transfer data gambar lebih cepat dan penggunaan CPU lebih ringan dibanding kamera USB biasa, cocok untuk aplikasi real-time seperti deteksi warna yang butuh pemrosesan frame terus-menerus.

Raspberry Pi berperan sebagai unit pemroses utama yang menjalankan program Python, mengolah citra dari kamera, dan mengirim sinyal ke motor melalui pin GPIO. Kamera PiCam terhubung ke slot CSI dan diakses lewat pustaka picamera2 untuk mengambil citra secara langsung. Dua motor DC digerakkan lewat kelas Robot pada pustaka gpiozero, dengan pin GPIO 6 dan 5 untuk motor kiri, serta 12 dan 13 untuk motor kanan. Sensor ultrasonik HC-SR04 mengukur jarak lewat pantulan gelombang suara, dengan pin echo di GPIO 23 dan trigger di GPIO 18. Buzzer terhubung ke GPIO 4 sebagai indikator suara. Pada sisi perangkat lunak, OpenCV (cv2) digunakan untuk mengubah setiap frame ke ruang warna HSV dan mencari kontur warna, sedangkan modul threading menjalankan pembacaan sensor secara bersamaan dengan pengolahan citra tanpa saling menunggu.

B. Diagram Blok

Diagram blok berikut menggambarkan hubungan antara perangkat masukan (kamera dan sensor ultrasonik), unit pemroses (Raspberry Pi dengan dua thread), dan perangkat keluaran (motor kiri, motor kanan, buzzer).


Gambar 1. Diagram blok sistem robot pendeteksi warna.

C. Diagram Alir

Diagram alir berikut menjelaskan urutan proses dalam satu putaran program, mulai dari pengambilan frame kamera, pengecekan tiga level jarak sensor secara berurutan (bahaya, hati-hati, lalu perintah warna), hingga kondisi keluar program.


Gambar 2. Diagram alir logika program utama.

D. Rangkaian Skematik

Rangkaian skematik berikut menunjukkan hubungan tiap komponen ke Raspberry Pi secara garis besar: kamera ke port CSI, motor DC ke modul driver motor, sensor ultrasonik dan buzzer langsung ke pin GPIO, serta baterai sebagai sumber daya bersama.


Gambar 3. Rangkaian skematik komponen robot.

E. Diagram Pengawatan

Diagram pengawatan berikut memetakan pin fisik pada header GPIO 40-pin Raspberry Pi ke masing-masing komponen. Pin bertanda merah adalah pin yang benar-benar dipakai pada robot ini, sedangkan pin lain mengikuti fungsi bawaan Raspberry Pi dan tidak digunakan pada rangkaian.


Gambar 4. Diagram pengawatan pin GPIO Raspberry Pi.

IV. Hasil dan Pembahasan

Pada pengujian dengan tiga kartu warna (hijau, kuning, merah) yang digerakkan bergantian di depan kamera, robot berpindah perintah sesuai warna yang ditunjukkan dan tetap menjalankan perintah tersebut walau kartu dijauhkan dari bidang pandang kamera. Perilaku ini sesuai rancangan pada diagram alir, karena perintahTerakhir tidak direset ke None kecuali digantikan oleh warna baru.

Pengujian sensor ultrasonik dilakukan dengan mendekatkan tangan ke sensor secara bertahap. Pada jarak di atas 15 cm buzzer diam dan robot mengikuti perintah warna aktif. Begitu jarak masuk rentang 5-15 cm, robot langsung melambat ke 30% terlepas dari warna yang sedang aktif, dan buzzer berbunyi berselang. Pada jarak di bawah 5 cm, robot berhenti total dan buzzer ikut mati; perilaku mati saat bahaya ini disengaja agar buzzer tidak berbunyi terus-menerus saat robot sudah diam menempel pada penghalang.

Satu keterbatasan yang ditemukan selama pengujian adalah rentang HSV untuk kuning yang cukup berdekatan dengan hijau pada pencahayaan redup, sehingga kadang kartu kuning sempat terbaca sebagai hijau selama satu-dua frame sebelum stabil kembali ke kuning. Masalah ini belum diuji pada kondisi pencahayaan di bawah 100 lux.

V. Program

"""============================================================================
 Pemrogram      : Kelompok RE-3D/6
  1. 06-Dhesta Athaya S         NIM:4.34.23.3.06
  2. 12-Khoirudin               NIM:4.34.23.3.12
  3. 18-Rachmadhani Aji K       NIM:4.34.23.3.18
  4. 24-Theofilus Ogawa G       NIM:4.34.23.3.24
Tgl.Praktikum   : Rabu, 24 Juni 2026
===============================================================================
Proyek
ProyekOpenCV_RobotWarna.py
  Program untuk mendeteksi warna (Merah/Kuning/Hijau) secara langsung (live)
  menggunakan OpenCV(cv2) dan picamera2, lalu mengontrol gerak robot
  berdasarkan warna yang terdeteksi (Merah, Kuning, Hijau).
  Sensor ultrasonik HC-SR04 digunakan sebagai pengaman agar robot tidak
  menabrak objek/tembok di depannya. Buzzer memberikan peringatan jarak.
  Robot mengingat perintah warna terakhir dan terus menjalankannya
  meski warna hilang dari kamera, sampai warna lain terdeteksi.
  Saat jarak bahaya (<=5 cm): robot berhenti PAKSA dan buzzer ikut diam.
-------------------------------------------------------------------------------
Materi:
- Kamera PiCam
- Kelas Picamera2 pada pustaka picamera2
- Pustaka OpenCV (cv2) untuk mendeteksi warna pada ruang warna HSV
- Pustaka gpiozero untuk mengontrol Robot, DistanceSensor, dan Buzzer
-------------------------------------------------------------------------------
Komponen:
- 1x PiCam
- 2x Motor DC
- 1x Sensor Ultrasonik HC-SR04
- 1x Buzzer
-------------------------------------------------------------------------------
Tabel Perintah Warna:
  Hijau  -> Robot MAJU penuh (100%)  -- terus sampai warna lain terdeteksi
  Kuning -> Robot MAJU pelan (40%)   -- terus sampai warna lain terdeteksi
  Merah  -> Robot BERHENTI           -- terus sampai warna lain terdeteksi
  <= 5 cm -> Robot BERHENTI PAKSA + Buzzer DIAM
  <=15 cm -> Robot DIPERLAMBAT (30%) + Buzzer sedang
=========================================================================== """

#=== Pustaka ===
import time                                         # pustaka untuk fungsi jeda waktu (sleep)
import threading                                    # pustaka untuk menjalankan beberapa proses secara bersamaan (thread)
import numpy as np                                  # pustaka numpy untuk membuat array nilai HSV (batas warna)
from gpiozero import Robot, DistanceSensor, Buzzer  # mengimpor kelas Robot, sensor jarak, dan buzzer dari gpiozero
from picamera2 import Picamera2                     # mengimpor kelas Picamera2 untuk mengakses kamera Raspberry Pi
import cv2                                          # mengimpor pustaka OpenCV untuk pengolahan gambar dan deteksi warna

#=== Deklarasi Variabel ===
robot  = Robot(left=(6, 5), right=(12, 13))         # membuat objek robot dengan pin GPIO motor kiri (6,5) dan kanan (12,13)
sensor = DistanceSensor(echo=23, trigger=18)        # membuat objek sensor ultrasonik dengan pin Echo=23 dan Trigger=18
bel    = Buzzer(4)                                  # membuat objek buzzer yang terhubung ke pin GPIO 4
kamera = Picamera2()                                # membuat objek kamera PiCam yang terhubung ke slot CSI

LUAS_MINIMUM = 800                                  # batas luas kontur minimum (piksel) agar warna dianggap terdeteksi (bukan noise)
JARAK_BAHAYA = 5                                    # batas jarak (cm) kondisi bahaya: robot berhenti PAKSA & buzzer diam
JARAK_HATI   = 15                                   # batas jarak (cm) kondisi hati-hati: robot diperlambat & buzzer berbunyi sedang

rentangWarna = {                                    # kamus berisi rentang nilai HSV untuk setiap warna yang ingin dideteksi
    "Hijau" : [(np.array([40,  70,  70]),           # batas bawah HSV warna Hijau
                np.array([85,  255, 255]))],         # batas atas  HSV warna Hijau
    "Kuning": [(np.array([20, 100, 100]),           # batas bawah HSV warna Kuning
                np.array([35,  255, 255]))],         # batas atas  HSV warna Kuning
    "Merah" : [(np.array([0,  120,  70]),           # batas bawah HSV warna Merah (rentang bawah, H=0-10)
                np.array([10,  255, 255])),          # batas atas  HSV warna Merah (rentang bawah)
               (np.array([170, 120, 70]),            # batas bawah HSV warna Merah (rentang atas, H=170-180)
                np.array([180, 255, 255]))],          # batas atas  HSV warna Merah (rentang atas) -- merah butuh 2 rentang karena melingkar di HSV
}

warnaKotak = {                                      # kamus berisi warna garis kotak (BGR) untuk setiap label warna
    "Hijau" : (0, 255,   0),                        # warna garis kotak untuk Hijau dalam format BGR
    "Kuning": (0, 255, 255),                        # warna garis kotak untuk Kuning dalam format BGR
    "Merah" : (0,   0, 255),                        # warna garis kotak untuk Merah dalam format BGR
}

#=== Variabel Bersama Antar-Thread ===
jarak_cm_global  = 999.0                            # variabel global penyimpan jarak terkini dalam cm (nilai awal 999 = aman)
lock_jarak       = threading.Lock()                 # kunci (lock) agar variabel jarak tidak diakses dua thread sekaligus (race condition)
perintahTerakhir = None                             # variabel global untuk menyimpan warna/perintah terakhir yang terdeteksi; None = belum ada perintah sama sekali
statusBahaya     = False                            # variabel global penanda kondisi bahaya aktif; True = jarak terlalu dekat, robot & buzzer dipaksa diam

#=== Fungsi Thread Sensor Ultrasonik & Buzzer ===
def thread_sensor():                                # definisi fungsi yang akan dijalankan oleh thread sensor
    global jarak_cm_global, statusBahaya           # mendeklarasikan bahwa fungsi ini akan mengubah kedua variabel global ini
    while True:                                     # perulangan tanpa henti agar sensor terus membaca jarak
        jarak = sensor.distance * 100               # membaca jarak dari sensor (satuan meter) lalu mengalikan 100 agar menjadi cm

        with lock_jarak:                            # mengunci akses variabel global agar thread lain tidak membaca setengah-jalan
            jarak_cm_global = jarak                 # memperbarui nilai jarak global dengan hasil bacaan terbaru
            statusBahaya    = jarak <= JARAK_BAHAYA # perbarui penanda bahaya: True jika jarak <= 5 cm, False jika tidak

        if jarak <= JARAK_BAHAYA:                   # jika jarak kurang dari atau sama dengan 5 cm (kondisi bahaya)
            bel.off()                               # matikan buzzer -- robot sudah berhenti paksa, tidak perlu bunyi peringatan
            time.sleep(0.1)                         # jeda kecil agar CPU tidak bekerja terlalu keras saat menunggu robot menjauh
        elif jarak <= JARAK_HATI:                   # jika jarak antara 5-15 cm (kondisi hati-hati)
            bel.on()                                # nyalakan buzzer sebagai peringatan jarak dekat
            time.sleep(0.3)                         # tahan buzzer selama 0.3 detik (bunyi sedang)
            bel.off()                               # matikan buzzer
            time.sleep(0.3)                         # jeda 0.3 detik sebelum siklus bunyi berikutnya (ritme sedang)
        else:                                       # jika jarak lebih dari 15 cm (kondisi aman)
            bel.off()                               # pastikan buzzer mati (tidak berbunyi saat kondisi aman)
            time.sleep(0.1)                         # jeda kecil agar CPU tidak bekerja terlalu keras (throttle loop)

#=== Fungsi Deteksi Warna Terbesar ===
def deteksiWarnaTerbesar(bingkaiHSV):               # fungsi menerima bingkai gambar dalam format HSV
    labelTerpilih  = None                           # variabel untuk menyimpan nama warna yang terdeteksi (awal: belum ada)
    luasTerbesar   = 0                              # variabel untuk menyimpan luas kontur terbesar yang ditemukan (awal: 0)
    kotakTerpilih  = None                           # variabel untuk menyimpan koordinat kotak pembatas warna terbesar (awal: belum ada)

    for label, daftarRentang in rentangWarna.items():   # iterasi setiap warna (Hijau, Kuning, Merah) beserta rentang HSV-nya
        maskGabungan = None                             # mask awal untuk warna ini dikosongkan dulu
        for (bawah, atas) in daftarRentang:            # iterasi setiap pasang batas bawah & atas HSV untuk warna ini
            maskBagian = cv2.inRange(bingkaiHSV, bawah, atas)  # buat mask: piksel putih jika warna cocok, hitam jika tidak
            if maskGabungan is None:                    # jika ini adalah rentang pertama untuk warna ini
                maskGabungan = maskBagian               # langsung gunakan mask bagian ini sebagai mask gabungan
            else:                                       # jika sudah ada mask sebelumnya (warna Merah punya 2 rentang)
                maskGabungan = cv2.bitwise_or(maskGabungan, maskBagian) # gabungkan kedua mask dengan operasi OR (piksel putih jika salah satu cocok)

        maskGabungan = cv2.erode(maskGabungan,  None, iterations=2)    # erosi: mengecilkan area putih untuk menghilangkan noise kecil
        maskGabungan = cv2.dilate(maskGabungan, None, iterations=2)    # dilasi: memperluas kembali area putih agar bentuk warna tetap utuh

        kontur, _ = cv2.findContours(                   # mencari semua kontur (tepi bentuk) pada mask
                        maskGabungan,                   # mask yang digunakan sebagai sumber
                        cv2.RETR_EXTERNAL,              # hanya ambil kontur paling luar
                        cv2.CHAIN_APPROX_SIMPLE)        # simpan hanya titik sudut kontur (hemat memori)
        if not kontur:                                  # jika tidak ada kontur ditemukan untuk warna ini
            continue                                    # lewati ke warna berikutnya

        konturTerbesar = max(kontur, key=cv2.contourArea)  # pilih kontur dengan luas terbesar dari semua kontur yang ditemukan
        luas = cv2.contourArea(konturTerbesar)              # hitung luas (piksel) dari kontur terbesar tersebut

        if luas > LUAS_MINIMUM and luas > luasTerbesar:    # jika luas melebihi batas minimum DAN lebih besar dari warna sebelumnya
            luasTerbesar  = luas                            # perbarui nilai luas terbesar
            labelTerpilih = label                           # simpan nama warna ini sebagai warna yang terdeteksi
            kotakTerpilih = cv2.boundingRect(konturTerbesar)# hitung kotak pembatas (x,y,lebar,tinggi) dari kontur terbesar

    return labelTerpilih, kotakTerpilih             # kembalikan nama warna dan koordinat kotak yang terdeteksi

#=== Fungsi Kendali Robot ===
def kendalikanRobot(labelWarna):                    # fungsi untuk menggerakkan robot berdasarkan warna dan jarak
    global perintahTerakhir                         # mendeklarasikan bahwa fungsi ini akan mengubah variabel global perintahTerakhir

    with lock_jarak:                                # kunci akses variabel global agar aman dibaca dari thread ini
        jarak  = jarak_cm_global                    # ambil nilai jarak terkini dari variabel global
        bahaya = statusBahaya                       # ambil status bahaya terkini dari variabel global

    # -- Prioritas 1 -- BAHAYA: jarak <= 5 cm -> robot berhenti PAKSA, buzzer diam --
    if bahaya:                                      # jika status bahaya aktif (jarak terlalu dekat ke objek/tembok)
        print(f"[BAHAYA]  Jarak {jarak:.1f} cm -- Robot BERHENTI PAKSA, Buzzer DIAM") # cetak status ke terminal
        robot.stop()                                # hentikan PAKSA semua motor robot; perintah warna diabaikan sepenuhnya
        bel.off()                                   # matikan buzzer karena robot sudah berhenti (tidak perlu bunyi peringatan)
        return                                      # keluar dari fungsi, tidak proses warna sama sekali

    # -- Prioritas 2 -- HATI-HATI: jarak 5-15 cm -> robot diperlambat paksa --
    if jarak <= JARAK_HATI:                         # jika jarak cukup dekat tapi belum bahaya (5-15 cm)
        print(f"[HATI]    Jarak {jarak:.1f} cm -- Robot DIPERLAMBAT (override warna)") # cetak status ke terminal
        robot.forward(speed=0.3)                    # jalankan robot maju paksa kecepatan 30%, mengabaikan perintah warna
        return                                      # keluar dari fungsi, tidak proses warna

    # -- Perbarui perintah hanya jika ada warna BARU yang terdeteksi --
    if labelWarna is not None:                      # jika kamera mendeteksi warna pada frame ini
        if labelWarna != perintahTerakhir:          # jika warna yang terdeteksi BERBEDA dari perintah yang sedang aktif
            perintahTerakhir = labelWarna           # simpan warna baru ini sebagai perintah aktif yang akan terus dijalankan
            print(f"[GANTI]   Warna baru: {labelWarna} -- perintah diperbarui") # cetak info pergantian perintah ke terminal

    # -- Jalankan perintah terakhir (meski warna sudah hilang dari kamera) --
    if perintahTerakhir == "Hijau":                 # jika perintah aktif adalah Hijau
        print(f"[HIJAU]   Jarak {jarak:.1f} cm -- Robot MAJU penuh (100%)")         # cetak status ke terminal
        robot.forward()                             # maju kecepatan penuh 100%; tetap berjalan meski Hijau hilang dari kamera
    elif perintahTerakhir == "Kuning":              # jika perintah aktif adalah Kuning
        print(f"[KUNING]  Jarak {jarak:.1f} cm -- Robot MAJU pelan (40%)")          # cetak status ke terminal
        robot.forward(speed=0.4)                    # maju kecepatan 40%; tetap berjalan meski Kuning hilang dari kamera
    elif perintahTerakhir == "Merah":               # jika perintah aktif adalah Merah
        print(f"[MERAH]   Jarak {jarak:.1f} cm -- Robot BERHENTI")                  # cetak status ke terminal
        robot.stop()                                # hentikan robot; tetap berhenti meski Merah hilang dari kamera
    else:                                           # jika belum ada perintah sama sekali sejak program dimulai (perintahTerakhir = None)
        print(f"[--]      Jarak {jarak:.1f} cm -- Menunggu perintah warna pertama...") # cetak status ke terminal
        robot.stop()                                # robot diam menunggu karena belum pernah menerima perintah warna apapun

#=== Program Utama ===
print("\t=== Demo Kamera + OpenCV + Ultrasonik + Robot ===") # tampilkan judul program di terminal
print("Program berjalan otomatis. Tekan 'q' pada jendela gambar untuk keluar.\n") # tampilkan petunjuk keluar
print("Logika perintah:")                           # tampilkan keterangan logika ke terminal
print("  Hijau  -> Maju 100%   (terus sampai warna lain terdeteksi)")  # keterangan Hijau
print("  Kuning -> Maju  40%   (terus sampai warna lain terdeteksi)")  # keterangan Kuning
print("  Merah  -> Berhenti    (terus sampai warna lain terdeteksi)")  # keterangan Merah: berhenti, bukan mundur
print("  <= 5 cm -> Robot BERHENTI PAKSA + Buzzer DIAM")                # keterangan kondisi bahaya
print("  <=15 cm -> Robot DIPERLAMBAT (30%) + Buzzer sedang\n")         # keterangan kondisi hati-hati

t_sensor = threading.Thread(target=thread_sensor, daemon=True)  # buat thread baru yang menjalankan fungsi thread_sensor; daemon=True agar ikut berhenti saat program utama selesai
t_sensor.start()                                    # mulai jalankan thread sensor secara bersamaan dengan program utama

kamera.configure(kamera.create_video_configuration())   # konfigurasikan kamera ke mode video (resolusi & format optimal untuk capture berkelanjutan)
kamera.start()                                      # aktifkan kamera agar siap mengambil gambar
time.sleep(1)                                       # jeda 1 detik untuk memberi waktu kamera menyesuaikan pencahayaan

try:                                                # blok try untuk menangkap error atau penghentian paksa
    while True:                                     # perulangan utama: terus proses setiap frame kamera
        bingkai    = kamera.capture_array()                         # ambil satu frame gambar dari kamera dalam format array numpy (RGB)
        bingkaiLtr = cv2.cvtColor(bingkai,    cv2.COLOR_RGB2BGR)    # ubah format warna dari RGB (format kamera) ke BGR (format OpenCV)
        bingkaiHSV = cv2.cvtColor(bingkaiLtr, cv2.COLOR_BGR2HSV)   # ubah format warna dari BGR ke HSV untuk mempermudah deteksi warna

        labelWarna, kotak = deteksiWarnaTerbesar(bingkaiHSV)        # panggil fungsi deteksi warna; kembalikan nama warna dan koordinat kotak

        if labelWarna is not None:                  # jika ada warna yang berhasil terdeteksi pada frame ini
            (x, y, w, h) = kotak                   # urai koordinat kotak menjadi: x=kiri, y=atas, w=lebar, h=tinggi
            warna = warnaKotak[labelWarna]          # ambil warna garis kotak sesuai warna yang terdeteksi dari kamus warnaKotak
            cv2.rectangle(bingkaiLtr, (x, y), (x+w, y+h), warna, 2)       # gambar kotak persegi di sekeliling area warna yang terdeteksi
            cv2.putText(bingkaiLtr, labelWarna, (x, y - 10),               # tulis nama warna di atas kotak
                        cv2.FONT_HERSHEY_SIMPLEX, 0.7, warna, 2)           # dengan font Hershey, ukuran 0.7, tebal 2 piksel

        # Tampilkan label perintah aktif di layar meski warna sudah tidak terdeteksi
        if perintahTerakhir is not None:            # jika sudah ada perintah aktif yang tersimpan
            teks_perintah  = f"Perintah: {perintahTerakhir}"   # buat teks yang menampilkan perintah aktif saat ini
            warna_perintah = warnaKotak[perintahTerakhir]       # ambil warna teks sesuai perintah aktif dari kamus warnaKotak
            cv2.putText(bingkaiLtr, teks_perintah, (10, 60),   # tampilkan teks perintah aktif di layar posisi y=60
                        cv2.FONT_HERSHEY_SIMPLEX, 0.8, warna_perintah, 2)  # dengan font Hershey, ukuran 0.8, tebal 2 piksel

        with lock_jarak:                            # kunci akses variabel global agar aman dibaca
            jarak_tampil  = jarak_cm_global         # ambil nilai jarak terkini untuk ditampilkan di layar
            bahaya_tampil = statusBahaya            # ambil status bahaya terkini untuk menentukan warna teks jarak
        teks_jarak  = f"Jarak: {jarak_tampil:.1f} cm"  # format teks jarak dengan 1 angka desimal
        warna_jarak = (0, 0, 255) if bahaya_tampil \
                 else (0, 255, 255) if jarak_tampil <= JARAK_HATI \
                 else (0, 255, 0)                   # pilih warna teks: merah=bahaya, kuning=hati-hati, hijau=aman
        cv2.putText(bingkaiLtr, teks_jarak, (10, 30),                      # tampilkan teks jarak di pojok kiri atas layar (posisi x=10, y=30)
                    cv2.FONT_HERSHEY_SIMPLEX, 0.8, warna_jarak, 2)         # dengan font Hershey, ukuran 0.8, tebal 2 piksel

        # Tampilkan peringatan BAHAYA di layar saat robot berhenti paksa karena sensor
        if bahaya_tampil:                           # jika kondisi bahaya aktif (jarak <= 5 cm)
            cv2.putText(bingkaiLtr, "!! BAHAYA - ROBOT BERHENTI !!", (10, 90), # tulis teks peringatan di layar posisi y=90
                        cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)    # dengan warna merah, ukuran 0.7, tebal 2 piksel

        kendalikanRobot(labelWarna)                 # panggil fungsi kendali robot dengan warna yang terdeteksi frame ini (bisa None jika tidak ada)

        cv2.imshow("Deteksi Warna & Robot - Kelompok RE-3D/06", bingkaiLtr) # tampilkan bingkai gambar yang sudah diberi anotasi ke jendela OpenCV
        if cv2.waitKey(1) & 0xFF == ord('q'):       # tunggu input keyboard selama 1 ms; jika tombol 'q' ditekan
            break                                   # keluar dari perulangan utama dan hentikan program

except KeyboardInterrupt:                           # tangkap jika pengguna menekan Ctrl+C untuk menghentikan program paksa
    print("\nDipaksa Berhenti Oleh Pengguna (CTRL+C)")  # tampilkan pesan pemberitahuan penghentian paksa

finally:                                            # blok ini selalu dijalankan, baik program selesai normal maupun dihentikan paksa
    cv2.destroyAllWindows()                         # tutup semua jendela tampilan OpenCV
    robot.stop()                                    # pastikan semua motor robot berhenti (keamanan)
    bel.off()                                       # pastikan buzzer dimatikan (keamanan)
    kamera.stop()                                   # hentikan proses pengambilan gambar kamera
    kamera.close()                                  # lepaskan resource kamera agar tidak terkunci
    print("\n\tKamera Dimatikan ... Demo Berakhir") # tampilkan pesan bahwa program telah berakhir

VI. Kesimpulan

Robot yang dibangun mampu menggabungkan deteksi warna berbasis OpenCV dengan pengaman jarak berbasis sensor ultrasonik dalam satu sistem kendali yang berjalan pada dua thread terpisah. Penyimpanan perintah warna terakhir membuat robot tetap bergerak konsisten meski objek warna sempat hilang dari kamera, sementara sensor ultrasonik memastikan robot tidak menabrak penghalang terlepas dari perintah warna yang sedang berjalan. Pengujian lanjutan pada kondisi pencahayaan rendah dan pada permukaan lantai yang tidak rata masih perlu dilakukan untuk mengetahui batas kemampuan sistem ini secara lebih menyeluruh.

VII. Lampiran
link youtube : https://youtu.be/xluZFh1lgaU
linkppthttps://www.canva.com/design/DAHMyEma0ic/ILE8OQzk7bCvasuC7FdAJQ/edit 


Komentar

Postingan populer dari blog ini

SISTEM KONVEYOR OTOMATIS DENGAN SENSOR INFRARED DAN KONTROL MANUAL

Pompa Air Otomatis Berbasis ATMega8535

SISTEM PEMANTAUAN SUHU DAN KELEMBABAN PADA SUATU RUANGAN MENGGUNAKAN SENSOR DHT22 BERBASIS MIKROKONTROLLER ARDUINO UNO ATMEGA328P