cosmos0311's picture
Update README.md
367e6cc verified
---
license: agpl-3.0
base_model: ultralytics/yolov8n
tags:
- yolov8
- object-detection
- onnx
- onnxruntime
- raspberry-pi5
- edge-ai
- fire-detection
pipeline_tag: object-detection
---
# Edge Fire Detection AI (ํ™”์žฌ ๊ฐ์‹œ ์˜จ์  ์‹œ์Šคํ…œ)
๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด 5(Raspberry Pi 5) ์ž„๋ฒ ๋””๋“œ ์—ฃ์ง€ ๋””๋ฐ”์ด์Šค ํ™˜๊ฒฝ์—์„œ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ถˆ๊ฝƒ(Flame)๊ณผ ์—ฐ๊ธฐ(Smoke)๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค๋ฅผ ์ถ”์ ํ•˜๋Š” ๊ฒฝ๋Ÿ‰ ๊ฐ์ฒด ํƒ์ง€(Object Detection) ๋ชจ๋ธ์ž…๋‹ˆ๋‹ค. `YOLOv8n(Nano)` ์•„ํ‚คํ…์ฒ˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ „์ด ํ•™์Šต(Transfer Learning)์„ ์ง„ํ–‰ํ–ˆ์œผ๋ฉฐ, ํ•˜๋“œ์›จ์–ด ์—ฐ์‚ฐ ๋ถ€ํ•˜๋ฅผ ์ตœ์†Œํ™”ํ•˜๊ธฐ ์œ„ํ•ด `ONNX` ํฌ๋งท ๊ณ ์† ๋Ÿฐํƒ€์ž„ ์ตœ์ ํ™”๋ฅผ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
## 1. ํ•™์Šต ๋ฐ์ดํ„ฐ (Training Data)
* **๋ฐ์ดํ„ฐ ์ถœ์ฒ˜:** AI Hub ํ™”์žฌ ๊ฐ์ง€ ์˜์ƒ ๋ฐ์ดํ„ฐ์…‹ ๋ฐ ์ž์ฒด ์ˆ˜์ง‘ ํ™”์žฌ ์ด๋ฏธ์ง€ ์…‹
* **ํด๋ž˜์Šค ์ •์˜:** ์ด 2๊ฐœ ํด๋ž˜์Šค (`flame`: ๋ถˆ๊ฝƒ / `smoke`: ์—ฐ๊ธฐ)
* **๋ฐ์ดํ„ฐ ์ •์ œ ๋ฐ ์ฆ๊ฐ• (Data Augmentation):**
* ์•ผ๊ฐ„ ๋ฐ ์˜ค์ง€ ๋“ฑ ์ €์กฐ๋„ ์ƒํ™ฉ์—์„œ์˜ ์˜ค์ž‘๋™(False Positive)์„ ์ฐจ๋‹จํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ๊ธฐ ๋ณ€ํ˜•, ํ๋ฆผ(Blur), ๋ชจ์ž์ดํฌ(Mosaic) ์ฆ๊ฐ• ๊ธฐ๋ฒ•์„ ์ ์šฉํ•˜์—ฌ ๋ชจ๋ธ์˜ ๊ฐ•๊ฑด์„ฑ(Robustness) ํ™•๋ณด
* ์—ฃ์ง€ ๋””๋ฐ”์ด์Šค์˜ I/O ๋ฒ„์Šค ๋Œ€์—ญํญ ๋ถ€ํ•˜๋ฅผ ๊ฒฝ๊ฐํ•˜๊ธฐ ์œ„ํ•ด ์ž…๋ ฅ ํ•ด์ƒ๋„๋ฅผ 320x240 ๋ฐ 640x480 ํˆฌ ํŠธ๋ž™ ๊ทœ๊ฒฉ์œผ๋กœ ์Šค์ผ€์ผ๋ง ์ „์ฒ˜๋ฆฌ ์ˆ˜ํ–‰
## 2. ๋ชจ๋ธ ์„ฑ๋Šฅ (Evaluation Metrics)
| ํ‰๊ฐ€ ์ง€ํ‘œ | ํŒŒ์ดํ† ์น˜ (.pt) ์›๋ณธ | ONNX ๊ฒฝ๋Ÿ‰ ๊ฐ€์† ๊ทœ๊ฒฉ | ๋น„๊ณ  (๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด 5 CPU ๊ธฐ์ค€) |
| :--- | :---: | :---: | :--- |
| **mAP50 (์ „์ฒด ์ •ํ™•๋„)** | 84.5% | 83.9% | ํฌ๋งท ๋ณ€ํ™˜์— ๋”ฐ๋ฅธ ์ •ํ™•๋„ ์†์‹ค 0.6% ๋ฏธ๋งŒ ๋ฐฉ์–ด |
| **mAP50-95** | 0.521 | 0.518 | - |
| **์ถ”๋ก  ์†๋„ (Latency)** | ์•ฝ 350ms | **์•ฝ 45ms** | ONNX ๋ณ€ํ™˜ ํ›„ ์‹ค์‹œ๊ฐ„์„ฑ(20 FPS ์ด์ƒ) ์ˆ˜๋ ด |
## 3. ๋ชจ๋ธ ํ•™์Šต ๊ณผ์ • (Model Training Process)
* **ํ•™์Šต ์ธํ”„๋ผ:** ๊ณ ์„ฑ๋Šฅ ์™ธ์žฅ GPU(RTX 5070 Ti, 32GB RAM) ํ™˜๊ฒฝ ๊ธฐ๋ฐ˜ ๋กœ์ปฌ ๊ฐ€์† ๋นŒ๋“œ ๊ตฌ๋™
* **๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜:** 3.2M ๊ฐ€๋ฒผ์šด ํŒŒ๋ผ๋ฏธํ„ฐ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง„ `YOLOv8n`์„ ๋ฐฑ๋ณธ(Backbone)์œผ๋กœ ์ฑ„ํƒํ•˜์—ฌ ์ž„๋ฒ ๋””๋“œ CPU ๊ฐ€๋™ ๋Œ€์—ญํญ ์‚ฌ์ „ ํ™•๋ณด
* **ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •:**
| ํ•ญ๋ชฉ | ์„ค์ •๊ฐ’ | ๋น„๊ณ  |
| :--- | :---: | :--- |
| **Epochs** | 50 | - |
| **Batch Size** | 32 | ํ•˜๋“œ์›จ์–ด VRAM ์ตœ์ ํ™” ํฌ๊ธฐ |
| **Optimizer** | AdamW | - |
| **Learning Rate** | 1e-3 | ์ดˆ๊ธฐ ํ•™์Šต๋ฅ  ์ง€์ • |
| **Augmentation** | Mosaic (1.0), Blur (0.2) | ๊ฐ€ํ˜น ํ™˜๊ฒฝ ์˜ค์ž‘๋™ ์ฐจ๋‹จ์šฉ |
## 4. ๊ฐœ๋ฐœ ์‹œํ–‰์ฐฉ์˜ค ๋ฐ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… (Trials & Errors)
์ž„๋ฒ ๋””๋“œ ์—ฃ์ง€ AI ์‹œ์Šคํ…œ ๊ตฌํ˜„ ์ค‘ ๋ฐœ์ƒํ•œ ๋ฌผ๋ฆฌ/์†Œํ”„ํŠธ์›จ์–ด ๋ ˆ์ด์–ด์˜ ๋ณ‘๋ชฉ ํ˜„์ƒ๊ณผ ํ•ด๊ฒฐ ๋ฆฌํฌํŠธ์ž…๋‹ˆ๋‹ค.
### โ‘  PyTorch ์›์‹œ ๋ชจ๋ธ(.pt) ๊ตฌ๋™ ์‹œ ๋ ˆ์ดํ„ด์‹œ ์ €ํ•˜
* **๋ฌธ์ œ ์ƒํ™ฉ:** ํ•™์Šต ์™„๋ฃŒ๋œ `best.pt` ๊ฐ€์ค‘์น˜๋ฅผ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด 5 CPU ๋‹จ๋… ํ™˜๊ฒฝ์—์„œ ์ถ”๋ก ํ–ˆ์„ ๋•Œ ํ”„๋ ˆ์ž„ ์ง€์—ฐ์ด ์•ฝ 350ms(3 FPS ๋ฏธ๋งŒ)๊นŒ์ง€ ๋Š˜์–ด๋‚˜ ์‹ค์‹œ๊ฐ„ ํ™”์žฌ ํƒ์ง€๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ณ‘๋ชฉ ์ง๋ฉด.
* **ํ•ด๊ฒฐ ์กฐ์น˜:** ์ •์  ์—ฐ์‚ฐ ๊ทธ๋ž˜ํ”„ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง„ `ONNX` ํฌ๋งท์œผ๋กœ ๋ชจ๋ธ์„ ์ต์ŠคํฌํŠธ(`format=onnx`, `imgsz=320`)ํ•˜๊ณ , ํŒŒ์ด์ฌ ๋ฐฑ์—”๋“œ์— `onnxruntime` ๊ฐ€์† ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฒฐํ•ฉํ•˜์—ฌ ์ถ”๋ก  ๋ ˆ์ดํ„ด์‹œ๋ฅผ 45ms(22 FPS ์ด์ƒ) ์ˆ˜์ค€์œผ๋กœ ๋น„์•ฝ์ ์œผ๋กœ ๋‹จ์ถ•ํ•จ.
### โ‘ก Linux V4L2 ์žฅ์น˜ ์ฑ„๋„ ํƒ€์ž„์•„์›ƒ (`select() timeout`)
* **๋ฌธ์ œ ์ƒํ™ฉ:** ์ตœ์‹  ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด OS(Bookworm) ์ปค๋„์—์„œ OpenCV๋กœ ์›น์บ ์„ ํ˜ธ์ถœํ•  ๋•Œ ๋ฏธ๋””์–ด ์ปจํŠธ๋กค๋Ÿฌ ๊ฐ€์† ์Šคํƒ์ด `/dev/video0` ์ž์›์„ ๋…์  ์ ์œ (Lock)ํ•˜์—ฌ `select() timeout`๊ณผ ํ•จ๊ป˜ ํ•˜๋“œ์›จ์–ด ์ข€๋น„ ๋ฝ ํ˜„์ƒ ๋ฐœ์ƒ.
* **ํ•ด๊ฒฐ ์กฐ์น˜:** OpenCV ์นด๋ฉ”๋ผ ๊ฐœ์‹œ ์ฝ”๋“œ๋‹จ์— V4L2 ๋ฐฑ์—”๋“œ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •(`cv2.VideoCapture(0, cv2.CAP_V4L2)`)ํ•˜๊ณ , ์ฃผํ”ผํ„ฐ ์„œ๋ฒ„ ๊ตฌ๋™ ์‹œ ๋ฆฌ๋ˆ…์Šค ์—๋ฎฌ๋ ˆ์ด์…˜ ํ˜ธํ™˜ ๋ ˆ์ด์–ด ํ™˜๊ฒฝ ๋ณ€์ˆ˜์ธ `LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libv4l/v4l1compat.so`๋ฅผ ํ”„๋ฆฌ๋กœ๋“œ ๋ž˜ํ•‘ํ•˜์—ฌ ์ปค๋„ ์ ์œ  ์ถฉ๋Œ์„ ์šฐํšŒํ•จ.
### โ‘ข USB ๋ฒ„์Šค ๋Œ€์—ญํญ ์ดˆ๊ณผ ๋ฐ ํ”ฝ์…€ ์˜ค์—ผ (`error dequeuing buf`)
* **๋ฌธ์ œ ์ƒํ™ฉ:** ์••์ถ• ์ฝ”๋ฑ ๊ธฐ๋Šฅ์ด ์—†๋Š” ์ผ๋ฐ˜ ์›น์บ ์˜ ์›์‹œ ๋ฐ์ดํ„ฐ(Raw YUYV)๋ฅผ 640x480 ํ•ด์ƒ๋„๋กœ ์ˆ˜์‹  ์‹œ, ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด USB ๋Œ€์—ญํญ ํ•œ๊ณ„๋กœ ํ”„๋ ˆ์ž„ ๋ฒ„ํผ๊ฐ€ ๋ˆ„์ˆ˜๋˜์–ด ํ™”๋ฉด์ด ์ง€์งˆ๊ฑฐ๋ฆฌ๊ฑฐ๋‚˜ ๊ฒ€์€์ƒ‰(Black Screen)์œผ๋กœ ์ฃฝ๋Š” ํ˜„์ƒ ๋ฐœ์ƒ.
* **ํ•ด๊ฒฐ ์กฐ์น˜:** ์›น์บ ์„ ์ „๋ ฅ ๋ฐ ์ „์†ก ํšจ์œจ์ด ๋†’์€ **ํŒŒ๋ž€์ƒ‰ USB 3.0 ํฌํŠธ**๋กœ ๋ฌผ๋ฆฌ์  ์ด๋™ ์ •๋ ฌ์„ ์ˆ˜ํ–‰ํ•œ ํ›„, ์ฝ”๋“œ ๋ ˆ๋ฒจ์—์„œ ์ž…๋ ฅ ํ•ด์ƒ๋„๋ฅผ `320x240`์œผ๋กœ ๋Œ€ํญ ๋‚ฎ์ถ”๊ณ  ๊ณ ์† ์••์ถ• ์ŠคํŠธ๋ฆฌ๋ฐ ํฌ๋งท(`MJPG`) ์ฝ”๋ฑ ์ง€์ •์„ ๊ฐ•์ œํ™”ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๋ฒ„์Šค ํ†ต๋กœ๋ฅผ ๋ณต๊ตฌํ•จ.
### โ‘ฃ ์ฃผํ”ผํ„ฐ ๋„คํŠธ์›Œํฌ ์›น ์†Œ์ผ“ ๋ถ•๊ดด ํฌ๋ž˜์‹œ (`ZMQError`)
* **๋ฌธ์ œ ์ƒํ™ฉ:** ์ถ”๋ก  `while` ๋ฃจํ”„๊ฐ€ ์ดˆ๋‹น ์ˆ˜์‹ญ ์žฅ์˜ ์ด๋ฏธ์ง€๋ฅผ ์ง€์—ฐ ์—†์ด ์ฃผํ”ผํ„ฐ ์ธ๋ผ์ธ ์œ„์ ฏ(`ipywidgets`) ํ™”๋ฉด์œผ๋กœ ๋ฐ€์–ด ๋„ฃ์œผ๋ฉด์„œ ํ†ต์‹  ๋ฒ„ํผ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ๋กœ ์ธํ•ด ์ฃผํ”ผํ„ฐ ์ปค๋„์ด ์ •์ง€ํ•˜๊ณ  ์†Œ์ผ“์ด ํ„ฐ์ง€๋Š” ํ˜„์ƒ ๋ฐœ์ƒ.
* **ํ•ด๊ฒฐ ์กฐ์น˜:** ์ด๋ฏธ์ง€ ๋ณ€ํ™˜ ๋ฐ ์ „์†ก ๋ฃจํ‹ด ์งํ›„ ๋ฏธ์„ธ ์œ ํœด ์—ฐ์‚ฐ ํƒ€์ž„์ธ `time.sleep(0.04)` ์ง€์—ฐ ๋ฒ„ํผ ๋กœ์ง์„ ์•ˆ์ฐฉ์‹œ์ผœ ์ดˆ๋‹น ์ „์†ก๋ฅ ์„ 20~25ํ”„๋ ˆ์ž„ ์ˆ˜์ค€์œผ๋กœ ์•ˆ์ •ํ™”ํ•จ์œผ๋กœ์จ ์—ฐ์† ๊ตฌ๋™ ์•ˆ์ „์„ฑ์„ ํ™•๋ณดํ•จ.
## 5. ์‚ฌ์šฉ ๋ฐฉ๋ฒ• (Inference Code)
```python
import cv2
import numpy as np
from ultralytics import YOLO
from IPython.display import display
import ipywidgets as widgets
import time
def main():
model_path = "best.onnx"
model = YOLO(model_path, task='detect')
# V4L2 ๊ฐ€์† ๋ฐฑ์—”๋“œ ๋งคํ•‘
cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
time.sleep(1) # ํ•˜๋“œ์›จ์–ด ์•ˆ์ •ํ™” ๋Œ€๊ธฐ
if not cap.isOpened():
print("[์˜ค๋ฅ˜] ์นด๋ฉ”๋ผ ์žฅ์น˜๋ฅผ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
return
image_widget = widgets.Image(format='jpeg', width=320, height=240)
display(image_widget)
try:
while cap.isOpened():
success, frame = cap.read()
if not success or frame is None:
continue
# ONNX ๊ฐ€์† ์ถ”๋ก  ๊ตฌ๋™ (imgsz ์ผ์น˜)
results = model(frame, imgsz=320, stream=True)
for r in results:
annotated_frame = r.plot()
# JPEG ์••์ถ• ํ›„ ์ฃผํ”ผํ„ฐ ๋ Œ๋”๋ง ์˜์—ญ ์—…๋ฐ์ดํŠธ
ret, jpeg = cv2.imencode('.jpg', annotated_frame)
if ret:
image_widget.value = jpeg.tobytes()
# ZMQ ๊ณผ๋ถ€ํ•˜ ๋ฐฉ์ง€์šฉ micro-delay
time.sleep(0.04)
except KeyboardInterrupt:
print("\n์‚ฌ์šฉ์ž์— ์˜ํ•ด ์‹œ์Šคํ…œ ๊ฐ€๋™์ด ์ค‘๋‹จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
finally:
cap.release()
cv2.destroyAllWindows()
print("ํ•˜๋“œ์›จ์–ด ์ž์› ํšŒ์ˆ˜ ์™„๋ฃŒ.")
if __name__ == '__main__':
main()
```
## 6. ์ž…์ถœ๋ ฅ ํ˜•์‹ (Input / Output Format)
``` Plaintext
์ž…๋ ฅ(Input): ์‹ค์‹œ๊ฐ„ ์ž„๋ฒ ๋””๋“œ ์นด๋ฉ”๋ผ ๋น„๋””์˜ค ํ”„๋ ˆ์ž„ (320x240 RGB Image)
์ถœ๋ ฅ(Output):
- ๋ถˆ๊ฝƒ ๋ฐ ์—ฐ๊ธฐ ์˜์—ญ ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค(Bounding Box) ์‹œ๊ฐํ™” ํˆฌ์‚ฌ
- ํƒ์ง€ ํด๋ž˜์Šค ๋ ˆ์ด๋ธ” ๋งคํ•‘: [flame, smoke]
- ๊ฐ์ฒด๋ณ„ ์ถ”๋ก  ์‹ ๋ขฐ๋„ ์ˆ˜์น˜ ์ถœ๋ ฅ (Confidence Score: 0.0 ~ 1.0)
```
## 7. ๋กœ์ปฌ ๋™๊ธฐํ™” ๊ฐ€์ค‘์น˜ ๋‹ค์šด๋กœ๋“œ CLI
``` Bash
huggingface-cli download firedetection/fire_detection_yolov8n --local-dir ./model
```