Система детекции дыма и огня по видео на основе AI
Традиционные дымовые датчики реагируют на задымление в радиусе 5–7 метров. На открытых территориях — складах без потолочных сенсоров, лесных массивах, промышленных площадках — они бесполезны. Видеоаналитика обнаруживает дым и пламя на расстоянии до 300+ метров, часто раньше, чем концентрация достигает порога срабатывания ионизационного датчика.
Почему задача нетривиальна
Дым — аморфный объект без чётких границ. Он меняет форму каждые 2–3 кадра. «Дымоподобные» артефакты на видео: паровые выхлопы, туман, пыль от строительства, блики от фар в тумане. Огонь: блики от стекла, светоотражающие поверхности, закаты.
Классические CNN-классификаторы на одиночных кадрах дают FAR 15–30% в реальных условиях. Нужна temporal-aware архитектура.
Architecture детектора
import torch
import torch.nn as nn
from ultralytics import YOLO
import numpy as np
from collections import deque
class SmokeFireDetector:
def __init__(self, model_path: str, temporal_window: int = 8):
self.detector = YOLO(model_path) # дообученный на smoke/fire
self.temporal_window = temporal_window
# Буфер кадров для temporal анализа
self.frame_buffer: deque = deque(maxlen=temporal_window)
self.detection_history: dict = {} # track_id -> history
# Минимальное кол-во кадров с детекцией для тревоги
self.confirm_frames = 5 # из 8 кадров окна
def _optical_flow_check(self, prev_frame, curr_frame,
bbox: list) -> float:
"""Дым движется хаотично — проверяем нерегулярность потока"""
x1, y1, x2, y2 = bbox
prev_roi = cv2.cvtColor(prev_frame[y1:y2, x1:x2], cv2.COLOR_BGR2GRAY)
curr_roi = cv2.cvtColor(curr_frame[y1:y2, x1:x2], cv2.COLOR_BGR2GRAY)
flow = cv2.calcOpticalFlowFarneback(
prev_roi, curr_roi, None,
pyr_scale=0.5, levels=3, winsize=15,
iterations=3, poly_n=5, poly_sigma=1.1, flags=0
)
magnitude = np.sqrt(flow[..., 0]**2 + flow[..., 1]**2)
# Дым: неравномерный поток, высокая std
return float(magnitude.std())
def detect(self, frame: np.ndarray) -> list[dict]:
self.frame_buffer.append(frame.copy())
results = self.detector(frame, conf=0.35, iou=0.4)
confirmed_events = []
for box in results[0].boxes:
cls_id = int(box.cls)
cls_name = self.detector.model.names[cls_id]
if cls_name not in ['smoke', 'fire']:
continue
bbox = list(map(int, box.xyxy[0]))
conf = float(box.conf)
# Temporal подтверждение
det_key = f"{cls_name}_{bbox[0]//50}_{bbox[1]//50}" # grid cell
if det_key not in self.detection_history:
self.detection_history[det_key] = deque(maxlen=self.temporal_window)
self.detection_history[det_key].append(conf)
confirmed_count = sum(1 for c in self.detection_history[det_key]
if c > 0.3)
if confirmed_count >= self.confirm_frames:
# Дополнительная проверка optical flow для дыма
flow_score = 0.0
if cls_name == 'smoke' and len(self.frame_buffer) >= 2:
flow_score = self._optical_flow_check(
self.frame_buffer[-2], frame, bbox
)
confirmed_events.append({
'class': cls_name,
'confidence': conf,
'temporal_score': confirmed_count / self.temporal_window,
'flow_irregularity': flow_score,
'bbox': bbox,
'alert': True
})
return confirmed_events
Metrics и пороги
| Показатель | Целевое значение | Типичный baseline (без temporal) |
|---|---|---|
| Recall (реальные возгорания) | > 95% | 87–91% |
| FAR на открытых площадках | < 1 в смену | 8–15 в смену |
| Время до тревоги | < 10 сек | < 5 сек (выше FAR) |
| Дистанция обнаружения (дым) | 50–300м | — |
Датасеты и дообучение
Публичные датасеты: FireNet, MIVIA Fire, VisiFire, D-Fire (11k+ изображений, дым + огонь). Но в продакшене всегда нужно дообучение на локальных данных: камеры конкретного объекта, типичные ложные срабатывания.
Типичная процедура: собрать 500–800 изображений с объекта (200 smoke/fire + 300–600 hard negatives — пар, туман, закат), дообучить YOLOv8m с learning_rate=0.001, 50 эпох, аугментация HSV + геометрия. Даёт снижение FAR в 3–5 раз на конкретном объекте.
Кейс: нефтехимический завод
Объект — открытая площадка 4 га, 18 PTZ-камер с ИК. Задача: ранее обнаружение возгорания резервуаров.
- Базовая модель: YOLOv8l, дообученная на 1200 изображениях объекта
- Temporal window: 10 кадров @ 10fps = 1 секунда
- Confirm threshold: 6 из 10 кадров
Результаты тестирования (15 инсценированных возгораний):
- Recall: 100% (все 15 обнаружены)
- Среднее время обнаружения: 4.2 секунды от начала горения
- FAR за 2 недели работы: 1 ложная тревога (закат + парение котла)
Инфраструктура: сервер с RTX 3090, 18 потоков @ 10fps, latency 180ms. Integration с пожарной панелью Notifier через Modbus TCP.
Integration с пожарными системами
- Протоколы: Modbus TCP/RTU, BACnet, OPC-UA — для прямой интеграции с пожарными панелями
- VMS: запись видеодоказательства за 60 сек до и после события
- Геолокация события: привязка bbox к карте объекта через калибровку камеры
| Масштаб | Срок |
|---|---|
| 1–6 камер, закрытое помещение | 3–5 недель |
| 10–30 камер, открытая площадка | 6–10 недель |
| 30+ камер, enterprise | 12–18 недель |







