ระบบตรวจจับ ติดตาม จำแนก และคัดแยกเมล็ดกาแฟแบบเรียลไทม์ด้วยกล้อง 2 ตัว โดยใช้ YOLO + ByteTrack + Vision Transformer (ViT) ร่วมกับ Arduino สำหรับควบคุมชุดดีดเมล็ดกาแฟที่ระบบยืนยันเป็น Unknown
Repository: https://github.com/Non41045/Coffee-Bean-Classification-System
---
โปรเจกต์นี้เป็นระบบ AI สำหรับสายพานคัดแยกเมล็ดกาแฟ ระบบรับภาพจากกล้องหลักและกล้องด้านข้าง นำภาพมาต่อกันแล้วใช้ YOLO ตรวจจับเมล็ดกาแฟ พร้อมใช้ ByteTrack กำหนด Tracking ID ให้แต่ละวัตถุ
เมล็ดที่ยังไม่ได้รับการยืนยันจะถูกตัดจากภาพและส่งเข้าโมเดล Vision Transformer แบบ Batch เพื่อจำแนกเป็นหนึ่งใน 9 คลาสที่โมเดลได้รับการฝึกไว้ ระบบมี Unknown/OOD Filter ซึ่งพิจารณาค่า Confidence, Margin ระหว่างอันดับ 1 และอันดับ 2, Normalized Entropy และความต่อเนื่องของผลทำนายหลายเฟรม
เมื่อระบบยืนยันคลาสแล้ว จะบันทึกผลลง CSV นับสถิติ และบันทึกภาพ Crop สำหรับวัตถุจากกล้องหลัก หากคลาสอยู่ใน EJECTCLASSES และวัตถุเคลื่อนเข้าสู่โซนดีด ระบบจะส่งคำสั่ง C ผ่าน Serial ไปยัง Arduino
---
-
รับภาพแบบเรียลไทม์จากกล้อง USB 2 ตัว
-
ตั้งค่าความละเอียด FPS Autofocus และ Manual Focus แยกแต่ละกล้อง
-
ตรวจจับเมล็ดกาแฟด้วย YOLO
-
ติดตามวัตถุด้วย ByteTrack
-
สร้าง Tracking ID แบบอ่านง่าย
M1,M2, ... สำหรับกล้องหลักS1,S2, ... สำหรับกล้องด้านข้าง
-
จำแนกภาพแบบ Batch ด้วย Vision Transformer
-
รองรับ ViT 3 Architecture
torchvision vit\_b\_16timm vit\_small\_patch16\_224timm vit\_tiny\_patch16\_224
-
รองรับ Checkpoint หลายรูปแบบ
- State dictionary โดยตรง
model\_state\_dictstate\_dictmodel- Checkpoint ที่มี Prefix
module.จาก DataParallel
-
กรอง Unknown/OOD ด้วย Confidence, Margin และ Normalized Entropy
-
ยืนยัน Known Class จากผลทำนายคลาสเดียวกันหลายเฟรมติดต่อกัน
-
ยืนยันเป็น
Unknownเมื่อยังไม่สามารถยืนยัน Known Class ภายในจำนวนเฟรมที่กำหนด -
ควบคุมชุดดีดผ่าน Arduino Serial
-
จำกัดการดีดด้วย Eject Zone, Lead Time และ Command Cooldown
-
บันทึกผลที่ยืนยันแล้วลง CSV
-
บันทึกเวลา YOLO และ ViT ลง Performance CSV
-
บันทึกภาพ Frame และ Crop แบบ Asynchronous
-
สร้างรายงานสรุปเมื่อปิดระบบ
-
ใช้ CUDA อัตโนมัติเมื่อพบ GPU
---
A
AA
AAA
B
wash
Dry
Honey
Peaberry
C wash
Unknown ไม่ใช่คลาสที่ใช้ฝึกโมเดล ViT แต่เป็นผลลัพธ์จากกฎ Unknown/OOD Filter ดังนั้นห้ามเพิ่ม Unknown เข้า VIT\_CLASSNAMES ถ้ายังไม่ได้ฝึกโมเดลใหม่ให้มีเอาต์พุตคลาสดังกล่าว
---
กล้องหลัก 90 องศา กล้องด้านข้าง 45 องศา
│ │
└──────── ต่อภาพแนวนอน ───┘
│
▼
YOLO ตรวจจับเมล็ด
│
▼
ByteTrack กำหนด Tracking ID
│
▼
ตรวจสอบ Camera Detection Zone
│
▼
Crop เมล็ดกาแฟ
│
▼
ViT Batch Classification
│
▼
Confidence + Margin + Entropy Filter
│
┌─────────┴─────────┐
▼ ▼
Known Candidate Unknown
│ │
▼ ▼
ยืนยันต่อเนื่อง 5 เฟรม รอสูงสุด 15 เฟรม
└─────────┬─────────┘
▼
ยืนยันคลาสสุดท้าย
│
┌────────────┼────────────┐
▼ ▼ ▼
บันทึก CSV บันทึก Crop นับสถิติ
│
▼
ถ้าเป็นคลาสที่ต้องดีด
│
▼
ตรวจ Zone และ Delay
│
▼
ส่งคำสั่ง C ไป Arduino
---
- ค่าเริ่มต้นคือ Camera Index
1 - ใช้สำหรับยืนยันผล นับสถิติ และควบคุมการดีด
- มี Eject Zone ตามแกน X ตั้งแต่ 30% ถึง 70% ของความกว้างกล้อง
- เฉพาะวัตถุที่เริ่ม Track จากกล้องหลักเท่านั้นที่ถูกเพิ่มใน
Total - ภาพ Crop แยกคลาสและภาพ Ejected มาจากวัตถุของกล้องหลัก
- ค่าเริ่มต้นคือ Camera Index
2 - เปิดหรือปิดได้ด้วย
USEDUALCAMERA - มี Detection Zone ตามแกน Y ตั้งแต่ 25% ถึง 85% ของความสูงภาพ
- วัตถุนอก Detection Zone จะไม่ถูกส่งเข้า ViT
- ผลที่ยืนยันได้จะถูกบันทึกใน
confirmed\_beans.csvแต่ไม่ถูกเพิ่มในสถิติรวมของกล้องหลัก
YOLO และ ByteTrack ทำงานบนภาพที่นำสองกล้องมาต่อกันในแนวนอน ไม่ได้ประมวลผลแยกกันคนละรอบ
---
ระบบจะรับผลอันดับ 1 และอันดับ 2 จาก Softmax แล้วคำนวณ 3 ค่า
- Top-1 Confidence คือความมั่นใจของคลาสอันดับแรก
- Confidence Margin คือ Top-1 Confidence ลบ Top-2 Confidence
- Normalized Entropy คือระดับความลังเลของการกระจายความน่าจะเป็น โดยหาร Entropy ด้วย
log(จำนวนคลาส)
ผลของเฟรมจะถูกระบุเป็น Unknown หากเป็นจริงอย่างน้อยหนึ่งเงื่อนไข
confidence < VIT\_MIN\_CONFIDENCE
confidence\_margin < VIT\_MIN\_MARGIN
normalized\_entropy > VIT\_MAX\_NORMALIZED\_ENTROPYค่าตั้งต้นคือ
VIT\_MIN\_CONFIDENCE = 0.80
VIT\_MIN\_MARGIN = 0.20
VIT\_MAX\_NORMALIZED\_ENTROPY = 0.65Known Class จะได้รับการยืนยันเมื่อโมเดลทำนายคลาสเดียวกันผ่าน Unknown Filter ติดต่อกันครบ
FRAMESTOCONFIRM = 5หากผลเปลี่ยนเป็น Known Class อื่น ระบบจะเริ่มนับใหม่จาก 1 และหากเฟรมปัจจุบันเป็น Unknown ระบบจะล้าง Candidate และจำนวนเฟรมต่อเนื่อง
หากวัตถุยังไม่สามารถสะสม Known Candidate ได้ครบภายใน
MAXFRAMESBEFOREUNKNOWN = 15ระบบจะยืนยันวัตถุนั้นเป็น Unknown โดย Confidence ที่บันทึกเป็นค่าเฉลี่ยของ Top-1 Confidence ทุกเฟรมที่ประมวลผล ไม่ใช่ Probability ของคลาส Unknown
---
ระบบเชื่อมต่อ Arduino ผ่าน Serial
SERIALPORT = "COM3"
BAUDRATE = 9600คลาสที่ต้องการดีดกำหนดใน
EJECTCLASSES = \["Unknown"]วัตถุจะได้รับคำสั่งดีดเมื่อครบทุกเงื่อนไขต่อไปนี้
- Track เริ่มต้นจากกล้องหลัก
- ได้รับการยืนยันเป็นคลาสใน
EJECTCLASSES - ยังไม่เคยส่งคำสั่งดีดสำหรับ Track นี้
- จุดกึ่งกลางวัตถุอยู่ใน Main Eject Zone
- ผ่านเวลาจากการพบครั้งแรกอย่างน้อย
LEADTIMEMS - ผ่านช่วงพักจากคำสั่งก่อนหน้าอย่างน้อย
COMMANDCOOLDOWNMS - Arduino เชื่อมต่ออยู่และสามารถรับข้อมูลได้
ค่าตั้งต้น
MAINZONESTART = 0.30
MAINZONEEND = 0.70
LEADTIMEMS = 500
COMMANDCOOLDOWNMS = 150เมื่อผ่านเงื่อนไข โปรแกรมส่ง Byte ต่อไปนี้
ser.write(b"C")Arduino ต้องมีโปรแกรมที่รอรับอักขระ C และสั่งงานอุปกรณ์ดีด หาก Arduino ไม่เชื่อมต่อ ระบบ AI ยังทำงานต่อได้ แต่จะไม่สามารถสั่งดีดจริง
---
ระบบใช้ time.perf\_counter() จับเวลา AI และใช้ torch.cuda.synchronize() ก่อนและหลังจับเวลาเมื่อทำงานบน CUDA
รวมเวลาของ
modelyolo.track(...)รวมเฉพาะเวลาที่เรียก Forward Pass
outputs = modelvit(batchinputtensor)จึงไม่รวมเวลาของ
- การ Crop ภาพ
- การแปลง OpenCV เป็น PIL
- Resize, ToTensor และ Normalize
- การ Stack Batch
- Softmax
- Top-k
- Entropy
- การบันทึก CSV หรือรูปภาพ
Estimated AI FPS คำนวณจาก
1000 / (YOLO Time + ViT Time)
จึงเป็นค่าประมาณความเร็วเฉพาะส่วน AI ไม่ใช่ FPS จริงแบบ End-to-End ของระบบทั้งหมด
---
- Python
- OpenCV
- Ultralytics YOLO
- ByteTrack
- PyTorch
- Torchvision
- timm
- Pillow
- NumPy
- PySerial
- Threading และ Queue
- Arduino
---
Coffee-Bean-Classification-System/
│
├── weights/
│ ├── best.pt
│ └── best\_vit\_small\_coffee.pth
│
├── logs/ # สร้างอัตโนมัติเมื่อรัน
├── main.py # สคริปต์ระบบหลัก
├── requirements.txt
└── README.md
ชื่อสคริปต์หลักอาจแตกต่างจาก main.py ให้ใช้ชื่อไฟล์จริงของโปรเจกต์แทนในคำสั่งรัน
---
- คอมพิวเตอร์ที่ใช้ Windows สำหรับ
cv2.CAP\_DSHOWและพอร์ตแบบCOM3 - กล้อง USB 1 หรือ 2 ตัว
- Arduino และวงจรขับชุดดีด
- GPU ที่รองรับ CUDA เป็นตัวเลือก แต่แนะนำสำหรับการทำงานแบบเรียลไทม์
- Python 3.9 หรือใหม่กว่า
- ไดรเวอร์กล้อง
- CUDA และ PyTorch รุ่นที่รองรับ GPU หากต้องการใช้ CUDA
- Arduino Sketch ที่รองรับคำสั่ง
C
---
git clone https://github.com/Non41045/Coffee-Bean-Classification-System.git
cd Coffee-Bean-Classification-Systempython -m venv venv
venv\\Scripts\\activateหากมี requirements.txt
python -m pip install --upgrade pip
pip install -r requirements.txtหากยังไม่มีไฟล์ดังกล่าว สามารถติดตั้งแพ็กเกจหลักได้ดังนี้
pip install pyserial opencv-python numpy ultralytics torch torchvision pillow timmควรติดตั้ง PyTorch ให้ตรงกับ CUDA Version ของเครื่อง หากใช้ GPU
วางไฟล์โมเดลตามตำแหน่งต่อไปนี้
weights/best.pt
weights/best\_vit\_small\_coffee.pth
ไฟล์ YOLO ใช้ตรวจจับเมล็ดกาแฟ ส่วนไฟล์ ViT ต้องมี Output จำนวน 9 คลาสและลำดับคลาสตรงกับ VIT\_CLASSNAMES
- Upload Arduino Sketch ที่รอรับอักขระ
C - เชื่อมต่อ Arduino ผ่าน USB
- เปิด Device Manager เพื่อตรวจสอบ COM Port
- แก้ค่า
SERIALPORTให้ตรงกับเครื่อง
SERIALPORT = "COM3"
BAUDRATE = 9600ค่าตั้งต้น
CAMERAMAININDEX = 1
CAMERASIDEINDEX = 2หากกล้องเปิดไม่ได้ ให้ทดลอง 0, 1, 2 ตามลำดับ และตรวจสอบว่าไม่มีโปรแกรมอื่นกำลังใช้งานกล้อง
python main.pyเมื่อระบบพร้อมจะแสดงข้อความ
🚀 SYSTEM READY! Press 'q' to exit.
กด q ในหน้าต่าง OpenCV เพื่อปิดโปรแกรมอย่างถูกต้อง
---
USEDUALCAMERA = True
CAMERAMAININDEX = 1
CAMERASIDEINDEX = 2
CAMERAWIDTH = 800
CAMERAHEIGHT = 600
CAMERA\_FPS = 30หากใช้กล้องเดียว
USEDUALCAMERA = FalseMAIN\_AUTOFOCUS = False
MAIN\_FOCUS = 120
SIDE\_AUTOFOCUS = False
SIDE\_FOCUS = 120ความสามารถในการตั้ง Manual Focus ขึ้นอยู่กับ Driver และรุ่นกล้อง โปรแกรมจะแสดงค่าที่ขอและค่าจริงที่กล้องรายงาน
YOLOMODELPATH = "weights/best.pt"
VITMODELPATH = "weights/best\_vit\_small\_coffee.pth"
CONFIDENCETHRESHOLD = 0.4Architecture ของ ViT ถูกเลือกจากชื่อไฟล์
- ชื่อมี
baseใช้torchvision vit\_b\_16 - ชื่อมี
tinyใช้timm vit\_tiny\_patch16\_224 - กรณีอื่นใช้
timm vit\_small\_patch16\_224
ดังนั้นชื่อไฟล์น้ำหนักต้องสอดคล้องกับ Architecture ที่ใช้ฝึก มิฉะนั้น load\_state\_dict จะเกิดข้อผิดพลาด
SAVEFRAMES = True
FRAMESAVEINTERVAL = 10
SAVECROPS = True
SAVE\_PERFORMANCE\_CSV = True
PERFORMANCE\_SAVE\_INTERVAL = 1
PERFORMANCE\_FLUSH\_INTERVAL = 30การบันทึก Frame ทุก 10 เฟรมและ Performance ทุกเฟรมอาจใช้พื้นที่ดิสก์มากเมื่อเปิดระบบเป็นเวลานาน
---
เมื่อเริ่มโปรแกรม ระบบสร้าง Run Folder จากวันและเวลา
logs/YYYY-MM-DD\_HH-MM-SS/
│
├── frames/
├── crops/
│ ├── ejected/
│ ├── Unknown/
│ ├── A/
│ ├── AA/
│ ├── AAA/
│ ├── B/
│ ├── wash/
│ ├── Dry/
│ ├── Honey/
│ ├── Peaberry/
│ └── C wash/
│
├── confirmed\_beans.csv
├── performance\_metrics.csv
└── summary\_report.txt
เก็บหนึ่งแถวต่อหนึ่ง Track ที่ได้รับการยืนยัน ทั้งจากกล้องหลักและกล้องด้านข้าง
เวลา
ชนิดที่ยืนยัน
ค่า Confidence เฉลี่ย
Margin ล่าสุด
Entropy ล่าสุด
กล้องที่ยืนยัน
Tracking ID
Timestamp
Frame Number
YOLO Time (ms)
YOLO Detected Objects
ViT Time (ms)
ViT Batch Size
ViT Time per Object (ms)
Total AI Time (ms)
Estimated AI FPS
Device
สร้างตอนปิดโปรแกรมและเก็บ
- เวลาสิ้นสุด
- ค่า Unknown Filter
- ค่า Performance Logging
- สถิติสุดท้ายของแต่ละคลาส
- Total และ Ejected
โปรแกรมใช้ Queue และ Background Thread ในการเขียนภาพลงดิสก์ เพื่อลดเวลาที่ Main Loop ต้องหยุดรอ cv2.imwrite() และจะรอให้งานบันทึกที่ค้างอยู่เสร็จก่อนปิดโปรแกรม
---
- เขียว: ยืนยัน Known Class แล้ว
- แดง: คลาสที่อยู่ใน
EJECTCLASSES - ส้ม: ผลล่าสุดหรือผลยืนยันเป็น Unknown
- ฟ้า: กำลังตรวจสอบและยังไม่ยืนยัน
- เทา: ยังไม่ได้กำหนด Custom Tracking ID
- เหลือง: เส้นขอบเขต Eject Zone ของกล้องหลัก
- แดง: เส้น Detection Zone ของกล้องด้านข้าง
- ขาว: เส้นแบ่งภาพจากกล้องหลักและกล้องด้านข้าง
ด้านบนของหน้าต่างแสดง Total Confirmed, Unknown, Ejected, เวลา YOLO, เวลา ViT และจำนวนของคลาสที่ตรวจพบ
---
Totalและจำนวนรายคลาสเพิ่มเฉพาะวัตถุจากกล้องหลัก- CSV บันทึกผลยืนยันจากทั้งสองกล้อง
- ViT ประมวลผลเฉพาะ Track ที่ยังไม่มี
confirmedclass - เมื่อตัดสินคลาสแล้ว Track เดิมจะไม่ถูกจำแนกซ้ำ
- Track ที่หายจากภาพเกิน 3 วินาทีจะถูกลบ
- หาก Track เดิมกลับมาหลังถูกลบ ByteTrack อาจกำหนด ID ใหม่และอาจถูกนับใหม่
Ejectedเพิ่มเฉพาะเมื่อส่งคำสั่งCสำเร็จ- หาก Arduino ไม่เชื่อมต่อ ระบบกำหนด
sentcommand=Trueหลังถึงจุดดีด เพื่อไม่พยายามส่งซ้ำกับ Track เดิม CLASS\_GROUPSมีผลเฉพาะชื่อและสถิติที่แสดงบนหน้าจอ ไม่ได้เปลี่ยนคลาสที่บันทึกใน CSV
---
❌ Cannot open camera
- ตรวจสอบ Camera Index
- ปิดโปรแกรมอื่นที่ใช้งานกล้อง
- ถอดและเสียบกล้องใหม่
- ลดความละเอียดหรือ FPS
- ตรวจสอบ Driver กล้อง
กล้องบางรุ่นหรือ Driver บางตัวไม่รองรับ CAP\_PROP\_AUTOFOCUS และ CAP\_PROP\_FOCUS ให้ตรวจสอบค่าที่โปรแกรมพิมพ์ออกมา หรือเปิด Autofocus แทน
MAIN\_AUTOFOCUS = True
SIDE\_AUTOFOCUS = True⚠️ Arduino NOT connected
- ตรวจสอบ COM Port และ Baud Rate
- ปิด Serial Monitor ของ Arduino IDE
- ตรวจสอบสาย USB และ Driver
- ตรวจสอบว่าไม่มีโปรแกรมอื่นจับพอร์ตอยู่
- ตรวจสอบ Path ของไฟล์
.pth - ตรวจสอบชื่อไฟล์ว่าเลือก Architecture ถูกต้อง
- ตรวจสอบจำนวนคลาสเป็น 9
- ตรวจสอบลำดับ
VIT\_CLASSNAMESให้ตรงกับตอนฝึก - ตรวจสอบว่า Checkpoint ไม่เสียหาย
- ลดความละเอียดกล้อง
- ลดจำนวนวัตถุในภาพ
- ใช้
vit\_tiny\_patch16\_224 - ใช้งาน CPU ชั่วคราว
- ปิดโปรแกรมอื่นที่ใช้ GPU
ทดลองปรับทีละค่าโดยตรวจสอบกับ Validation Dataset
VIT\_MIN\_CONFIDENCE = 0.75
VIT\_MIN\_MARGIN = 0.15
VIT\_MAX\_NORMALIZED\_ENTROPY = 0.70การลดเกณฑ์อาจลด Unknown แต่เพิ่มความเสี่ยงในการยืนยันคลาสผิด
ลด
FRAMESTOCONFIRM = 3
MAXFRAMESBEFOREUNKNOWN = 10แต่การลดจำนวนเฟรมอาจทำให้ผลยืนยันไม่นิ่ง
ปรับ
LEADTIMEMS = 500
MAINZONESTART = 0.30
MAINZONEEND = 0.70ค่าที่เหมาะสมขึ้นอยู่กับความเร็วสายพาน ตำแหน่งกล้อง ตำแหน่งหัวดีด และความหน่วงของอุปกรณ์
SAVEFRAMES = False
SAVECROPS = False
SAVE\_PERFORMANCE\_CSV = Falseหรือเพิ่ม FRAMESAVEINTERVAL และ PERFORMANCE\_SAVE\_INTERVAL
---
- ก่อนเปิดระบบจริง ควรทดสอบ Arduino Ejector โดยไม่มีวัตถุและรักษาระยะห่างจากชิ้นส่วนที่เคลื่อนไหว
- ตรวจสอบพิกัด Eject Zone ให้ตรงกับตำแหน่งหัวดีดจริง
- อย่าเพิ่ม
Unknownเข้าVIT\_CLASSNAMESเว้นแต่จะฝึกโมเดลใหม่ - อย่าเปลี่ยนลำดับคลาสหลังฝึกโมเดล เพราะ Index ของเอาต์พุตจะไม่ตรงกับชื่อคลาส
- หากเปลี่ยนชื่อไฟล์ ViT ต้องรักษาคำว่า
base,smallหรือtinyให้สอดคล้องกับ Architecture - ควรสำรองพื้นที่ดิสก์สำหรับโฟลเดอร์
logs - ควรปิดโปรแกรมด้วยปุ่ม
qเพื่อให้ CSV, Queue, กล้อง และ Serial ถูกปิดอย่างสมบูรณ์
---
นางสาว ฑิตฐิตา ประจำเมือง
นาย ณัฐชนน สังข์ทอง
นาย ธีรภัทร์ กันทา
---
- Ultralytics YOLO
- ByteTrack
- PyTorch
- Torchvision
- timm
- OpenCV
- Pillow
- NumPy
- PySerial