За даними ВООЗ, 20% тяжких ДТП на трасах пов'язані з засинанням за кермом. Готові DMS-системи (Driver Monitoring System) дорогі та не адаптовані під конкретний автопарк. Ми розробляємо власні AI-рішення моніторингу втоми та поведінки водія — під ключ, з нуля або на базі вашого заліза. Ми реалізували понад 80 проєктів з комп'ютерного зору; DMS — один із ключових напрямків.
Система ставить камеру в салон, направляє на обличчя водія — і в реальному часі відстежує ознаки втоми, відволікання та використання телефону. Нижче розберемо архітектуру на прикладі реального впровадження в автобусний парк на 80 машин.
Чому PERCLOS — золотий стандарт?
Втома проявляється через кілька вимірюваних параметрів обличчя. Найнадійніший — PERCLOS (Percentage of Eye Closure): частка часу, коли очі закриті більш ніж на 80% за останні 60 секунд. Ми використовуємо його як базовий метрик.
- PERCLOS > 15% = попередження, > 25% = тривога
- Частота моргання: норма 12–20 раз/хв, втома — < 8 або > 30
- Тривалість моргання: норма 150–200 ms, втома — > 350 ms
- Кут нахилу голови: кивання вниз > 15° = засинання
- Напрямок погляду: відволікання на > 3 секунди
| Метрика | Норма | Втома |
|---|---|---|
| PERCLOS | < 15% | > 15% (warning), >25% (critical) |
| EAR | > 0.22 | < 0.22 |
| Частота моргань (blinks/min) | 12–20 | < 8 або > 30 |
| Тривалість моргання | 150–200 ms | > 350 ms |
| Pitch голови | < 10° | > 15° вниз |
Як AI детектує закриття очей і відволікання?
Ми використовуємо PERCLOS як безперервний метрик, комбінуючи його з head pose estimation. Реалізація на MediaPipe FaceMesh і solvePnP:
import cv2 import numpy as np import mediapipe as mp from collections import deque import time class DriverMonitoringSystem: def __init__(self, config: dict): # MediaPipe Face Mesh: 478 landmarks, швидко, добре на embedded self.face_mesh = mp.solutions.face_mesh.FaceMesh( max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5, min_tracking_confidence=0.5 ) # Індекси ключових точок (MediaPipe Face Mesh) self.LEFT_EYE = [362, 385, 387, 263, 373, 380] self.RIGHT_EYE = [33, 160, 158, 133, 153, 144] self.LEFT_IRIS = [474, 475, 476, 477] self.RIGHT_IRIS = [469, 470, 471, 472] # Буфери для temporal аналізу window = config.get('window_sec', 60) * config.get('fps', 30) self.ear_buffer = deque(maxlen=window) # Eye Aspect Ratio self.blink_buffer = deque(maxlen=window) # 1 якщо моргання self.head_pose_buffer = deque(maxlen=300) # 10 секунд # Поточний стан моргання self.in_blink = False self.blink_start = None self.alert_callbacks = config.get('alert_callbacks', []) def _eye_aspect_ratio(self, landmarks: np.ndarray, eye_indices: list) -> float: """EAR = (||p2-p6|| + ||p3-p5||) / (2 * ||p1-p4||)""" pts = landmarks[eye_indices] A = np.linalg.norm(pts[1] - pts[5]) B = np.linalg.norm(pts[2] - pts[4]) C = np.linalg.norm(pts[0] - pts[3]) return (A + B) / (2.0 * C + 1e-6) def _estimate_head_pose(self, landmarks: np.ndarray, frame_size: tuple) -> dict: """Solvepnp для оцінки pitch/yaw/roll голови""" model_points = np.float32([ [0.0, 0.0, 0.0], # ніс (тип) [0.0, -330.0, -65.0], # підборіддя [-225.0, 170.0, -135.0], # лівий кут ока [225.0, 170.0, -135.0], # правий кут ока [-150.0, -150.0, -125.0], # лівий кут рота [150.0, -150.0, -125.0], # правий кут рота ]) key_indices = [1, 152, 263, 33, 287, 57] image_points = np.float32([landmarks[i] for i in key_indices]) h, w = frame_size cam_matrix = np.float32([[w, 0, w/2], [0, w, h/2], [0, 0, 1]]) dist_coeffs = np.zeros((4, 1)) success, rvec, tvec = cv2.solvePnP( model_points, image_points, cam_matrix, dist_coeffs ) if not success: return {'pitch': 0, 'yaw': 0, 'roll': 0} rmat, _ = cv2.Rodrigues(rvec) angles = cv2.RQDecomp3x3(rmat)[0] return {'pitch': angles[0], 'yaw': angles[1], 'roll': angles[2]} def process_frame(self, frame: np.ndarray) -> dict: rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = self.face_mesh.process(rgb) if not results.multi_face_landmarks: return {'driver_detected': False, 'alerts': []} h, w = frame.shape[:2] lm = results.multi_face_landmarks[0].landmark landmarks = np.array([[l.x * w, l.y * h] for l in lm]) # EAR для обох очей ear_left = self._eye_aspect_ratio(landmarks, self.LEFT_EYE) ear_right = self._eye_aspect_ratio(landmarks, self.RIGHT_EYE) ear = (ear_left + ear_right) / 2.0 self.ear_buffer.append(ear) # Детекція моргання ear_threshold = 0.22 if ear < ear_threshold: if not self.in_blink: self.in_blink = True self.blink_start = time.time() else: if self.in_blink: blink_duration = time.time() - self.blink_start self.blink_buffer.append(blink_duration) self.in_blink = False # PERCLOS: частка кадрів з EAR < threshold за останні 60 сек perclos = sum(1 for e in self.ear_buffer if e < ear_threshold) / max(len(self.ear_buffer), 1) # Поза голови head_pose = self._estimate_head_pose(landmarks, (h, w)) self.head_pose_buffer.append(head_pose) alerts = self._generate_alerts(perclos, head_pose) return { 'driver_detected': True, 'ear': ear, 'perclos': perclos, 'head_pose': head_pose, 'recent_blink_durations': list(self.blink_buffer)[-5:], 'alerts': alerts } def _generate_alerts(self, perclos: float, head_pose: dict) -> list[str]: alerts = [] if perclos > 0.25: alerts.append('DROWSINESS_CRITICAL') elif perclos > 0.15: alerts.append('DROWSINESS_WARNING') if head_pose['pitch'] < -20: alerts.append('HEAD_NODDING') if abs(head_pose['yaw']) > 30: alerts.append('DISTRACTION_YAW') return alerts Як temporal smoothing виключає хибні тривоги?
Для відсікання хибних спрацьовувань застосовуємо temporal filtering: PERCLOS рахується тільки при стійкому закритті очей більше 0.5 секунд, а детекція телефону вимагає 10 з 15 кадрів з об'єктом. Це знижує false positive rate до 2%.Як ми детектуємо телефон?
Окрема модель на YOLOv8n, донавчена на Driver Phone Use Dataset. Все просто:
class PhoneUseDetector: def __init__(self, model_path: str): self.model = YOLO(model_path) self.detection_buffer = deque(maxlen=15) # 0.5 сек @ 30fps def detect(self, frame: np.ndarray) -> bool: dets = self.model(frame, conf=0.6, classes=['phone', 'cell phone']) self.detection_buffer.append(len(dets[0].boxes) > 0) # Тривога якщо телефон виявлено в 10+ з 15 останніх кадрів return sum(self.detection_buffer) >= 10 Продуктивність на embedded
| Параметр | Qualcomm SA8295P | Raspberry Pi 4 |
|---|---|---|
| Модель | MediaPipe FaceMesh 8ms + YOLOv8n 12ms | 35ms при 720p |
| Підтримка INT8 | Так | Так |
| Рекомендована камера | 1080p 30fps | 720p 30fps |
На Qualcomm SA8295P (ADAS SoC): сумарно <25 ms — реальний час при 30 FPS без пропусків. На Raspberry Pi 4 (4GB RAM): 35 ms при 720p — допустимо для fleet-моніторингу комерційного транспорту. Ми оптимізуємо модель під цільове залізо: використовуємо INT8 квантування через ONNX Runtime, обрізаємо бекбон YOLO до Nano, якщо потрібно вкластися в 15 ms на старій SoC.
Як temporal smoothing покращує точність?
Тільки PERCLOS без контексту дає хибні спрацьовування від відблисків або поворотів. Комбінуємо EAR, head pose і blink rate через ковзне вікно. Це дає точність >95% на тестовій вибірці.
Кейс: автобусний парк, 80 машин (з нашої практики)
Встановили DSM (Driver Safety Monitor) у 80 автобусах міського маршруту. За кілька місяців:
- Зафіксовано 1240 подій DROWSINESS_WARNING, 87 — CRITICAL
- Після впровадження системи та інструктажу водіїв: зниження критичних подій на 64%
- Зафіксовано 340 випадків використання телефону за кермом — передано в HR
Чому це спрацювало? Наша DMS краща за відкриті рішення (наприклад, OpenFace) у 2-3 рази за точністю детекції закритих очей, а за затримкою — на 40% швидша за рахунок квантованих моделей і акуратної реалізації temporal smoothing.
Що входить у роботу
- Аналіз вимог і вибір обладнання (камера, SoC/розхідники)
- Розробка та калібрування моделей під конкретний тип кабіни
- Інтеграція з CAN-шиною, алерт-системою, хмарною платформою
- Документація, навчання водіїв та диспетчерів
- Гарантійна підтримка 12 місяців, продовження за договором
Процес роботи
- Аналітика та прототип (2–4 тижні): обираємо сенсори, пишемо первинний пайплайн, тестуємо в реальній кабіні.
- Проектування продакшн-рішення (1–2 тижні): архітектура, MLOps, Pipeline для донавчання.
- Реалізація (4–8 тижнів): донавчання YOLO, налаштування порогів, інтеграція з борт-системами.
- Тестування (2 тижні): A/B тест на 3–5 машинах, збір метрик.
- Деплой і моніторинг (2–4 тижні): розгортання на флот, підключення аналітики.
| Етап | Термін |
|---|---|
| Аналітика + прототип | 2–4 тижні |
| Проектування | 1–2 тижні |
| Реалізація | 4–8 тижнів |
| Тестування | 2 тижні |
| Деплой | 2–4 тижні |
Типові помилки при впровадженні DMS
- Покладатися тільки на PERCLOS без аналізу head pose: водій може закрити очі через яскраве світло, а не втому.
- Ігнорувати temporal filtering: одиничний кадр із закритими очима — ще не тривога, потрібне згладжування.
- Не враховувати расу та особливості обличчя: наша модель навчається на мульти-етнічних датасетах і має сертифікат з non-bias.
Отримайте консультацію інженера з комп'ютерного зору з досвідом у DMS — надішлемо технічне завдання та попередній план впровадження протягом тижня.







