Drowsy driving is one of the most dangerous and underreported causes of fatal road accidents. Commercial driver monitoring systems — like those in Mercedes, BMW, or Tesla — cost thousands of euros and are locked into specific vehicles. Fleet-grade standalone systems require cloud connectivity and recurring fees.
DrowSAFE is the open-source answer: a fully self-contained drowsiness detection system that anyone can build and install in any vehicle.
How It WorksDrowSAFE runs a continuous detection pipeline at ~30 fps:
The entire pipeline runs on the Pi 5's quad-core Cortex-A76 CPU. No GPU, no AI accelerator, no internet connection required.
The Science: EAR, MAR, and PERCLOSMediaPipe Face Mesh detects 468 facial landmarks per frame. DrowSAFE uses these to compute three drowsiness signals:
Eye Aspect Ratio (EAR) — introduced by Soukupová & Čech (2016) — measures the ratio of vertical to horizontal eye span from 6 landmarks per eye. When eyes are open, EAR is typically 0.28–0.35. During a blink it drops sharply; sustained low EAR indicates drowsiness.
EAR = (|p1–p5| + |p2–p4|) / (2 × |p0–p3|)
Mouth Aspect Ratio (MAR) applies the same geometry to the mouth. A MAR above threshold held for 15+ consecutive frames registers as a confirmed yawn.
PERCLOS (Percentage of Eye Closure) is the US NHTSA gold-standard drowsiness metric — the fraction of frames where eyes are more than 80% closed over a rolling 60-second window. PERCLOS > 15% reliably indicates impaired alertness.
Head Pose is estimated in real time via OpenCV's solvePnP using 6 canonical 3D facial landmarks. A forward pitch above threshold — the characteristic microsleep nod — contributes to the fatigue score.
These four signals are combined into a single composite fatigue score (0–100):
PERCLOS : 40%
Instantaneous EAR : 25%
Head pose (nod) : 20%
Yawn frequency (MAR) : 15%
Alert State MachineThe fatigue score drives a three-level state machine with hysteresis — the score must drop well below a threshold before the alert level downgrades. This prevents flickering between states on noisy frames
# Hysteresis prevents flickering — score must drop below these to downgrade
WARNING_HYSTERESIS = 30 # Drop below 30 to return to ALERT
CRITICAL_HYSTERESIS = 55 # Drop below 55 to return to WARNINGHere's what each alert level looks like on the display:
The following screenshots are taken directly from the running system on the Raspberry Pi 5, with the Camera Module 3 NoIR active and MediaPipe landmarks overlaid.
WARNING state — score 43, EAR 0.188, MAR 1.677, head pitch -27.3°:
CRITICAL state — score 81, EAR 0.029, eyes fully closed:
Both screenshots confirm the system running at a stable 30.0 fps on the Pi 5 with the full MediaPipe pipeline active. The EAR value of 0.029 in the CRITICAL screenshot shows near-complete eye closure correctly triggering the alarm.
The DashboardThe 7" Raspberry Pi Touch Display runs a fullscreen Pygame dashboard readable at a glance.
Left panel (60%): Live camera feed with MediaPipe landmark overlay (toggled by SHOW_LANDMARKS in config).
Right panel (40%): Fatigue score (large, colour-coded), score progress bar, live EAR / MAR / head pitch values, alert level badge.
Bottom banner: Full-width strip showing alert message — changes colour and text with alert level.
Why Edge AI?Most commercial driver monitoring systems either require an expensive vehicle or send video to a cloud server. DrowSAFE is different:
- No internet required — works anywhere, including tunnels and rural areas
- Zero latency — no network round trips, detection is instantaneous
- Complete privacy — the driver's face never leaves the device
- One-time cost — no subscription, no API fees
MediaPipe's Face Mesh model is optimised for ARM and runs at a stable 30 fps on the Pi 5 without any GPU or AI accelerator.
Hardware AssemblyStep 1 — Flash the OS
Flash Raspberry Pi OS (Legacy, 64-bit) — Debian 12 Bookworm — using Raspberry Pi Imager.
⚠️ Important: As of April 2026, the default Pi OS image installs Debian 13 Trixie with Python 3.13. MediaPipe is not yet available for Python 3.13. Select "Raspberry Pi OS (Legacy, 64-bit)" explicitly under "Raspberry Pi OS (other)" in the Imager.
⚠️ Important: As of April 2026, the default Pi OS image installs Debian 13 Trixie with Python 3.13. MediaPipe is not yet available for Python 3.13. Select "Raspberry Pi OS (Legacy, 64-bit)" explicitly under "Raspberry Pi OS (other)" in the Imager.
Enable SSH and set your credentials in the Imager customisation screen before flashing.
Step 2 — Connect the Display
Connect the 7" Touch Display to the Pi 5 via the DSI ribbon cable. The display is powered directly from the Pi's USB-C port.
Step 3 — Connect the Camera
The Pi 5 uses a mini CSI connector but the Camera Module 3 ships with a standard 15-pin FPC cable. Use a Standard-to-Mini adapter cable (300mm recommended).
Connect to the Pi 5's CAM/DISP 0 port.
Step 4 — Wire the Buzzer
Connect an active piezo buzzer to GPIO 18:
Software SetupStep 1 — Clone and install
git clone https://github.com/YOUR_USERNAME/drowsafe.git
cd drowsafe
bash scripts/setup_drowsafe.shExpected verification output:
✓ OpenCV 4.11.0
✓ MediaPipe 0.10.18
✓ ONNX Runtime 1.24.4
✓ Pygame 2.6.1
✓ NumPy 1.26.4
All packages OK!Step 2 — Test without camera (simulation mode)
python src/main.py --simulateRuns the full dashboard with a synthetic 90-second fatigue cycle — cycles automatically through all three alert levels without any camera or MediaPipe.
Step 3 — Launch live
bash scripts/run.shActivates the venv, sets DISPLAY=:0 for SSH sessions, and starts the pipeline.
Every face is different. Use the built-in calibration tool to measure your baseline EAR and MAR values:
bash scripts/calibrate.shOutput
EAR-L EAR-R EAR-AVG MAR PITCH
0.312 0.298 0.305 0.312 1.2° ← mouth closed, eyes open
0.089 0.076 0.083 0.712 18.4° ← eyes closed, yawningSet EAR_THRESHOLD to ~65% of your open-eye EAR. Set MAR_THRESHOLD between your resting and yawn MAR values. All thresholds are in config/config.py.
All parameters in a single config/config.py:
CAMERA_FLIP = True # Flip 180° if camera mounted upside down
# Detection thresholds — calibrate to your face
EAR_THRESHOLD = 0.22 # Eye considered closed below this
MAR_THRESHOLD = 0.75 # Yawn detected above this
HEAD_PITCH_THRESHOLD = 20 # Forward nod angle in degrees
# PERCLOS — NHTSA standard
PERCLOS_WINDOW_SEC = 60
PERCLOS_THRESHOLD = 0.15 # >15% closed = drowsy
# Alert levels
WARNING_SCORE = 40
CRITICAL_SCORE = 70
WARNING_HYSTERESIS = 30
CRITICAL_HYSTERESIS = 55
# GPIO
BUZZER_PIN = 18










Comments