За даними ВООЗ, 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 — надішлемо технічне завдання та попередній план впровадження протягом тижня.







