수면 해킹: Whisper와 FFT를 활용한 로컬 수면 무호흡 및 코골이 모니터 만들기 🌙💤

발행: (2026년 4월 3일 AM 10:20 GMT+9)
4 분 소요
원문: Dev.to

Source: Dev.to

소개

8시간을 잤음에도 불구하고 트럭에 치인 듯한 기분으로 깨어본 적 있나요? 당신은 수백만 명의 수면 무호흡증 또는 만성 코골이 환자 중 한 명일지도 모릅니다. 이 튜토리얼에서는 FFT 스펙트럼 분석과 Faster‑Whisper를 결합한 프라이버시‑우선, 로컬 수면 무호흡 감지 시스템을 구축합니다. 모든 처리는 장치 내에서 이루어지므로 오디오가 기기를 떠나지 않습니다.

파이프라인 개요

graph TD
    A[Nightly Audio Input] --> B[Librosa Pre‑processing]
    B --> C{FFT Energy Check}
    C -- Below Threshold --> A
    C -- Event Detected --> D[Faster‑Whisper Classification]
    D --> E[Breathing Pattern Tagging]
    E --> F[(SQLite Storage)]
    F --> G[Health Report Generation]

사전 요구 사항

  • Python 3.9+
  • Faster‑Whisper – 로컬 전사/분류
  • Librosa – 오디오 분석
  • SQLite – 로컬 저장소

설치

pip install faster-whisper librosa numpy sounddevice

Librosa와 FFT를 이용한 오디오 분석

import numpy as np
import librosa

def is_meaningful_audio(audio_data, sr=16000):
    """Return True if the audio segment is louder than a background threshold."""
    # Short‑Time Fourier Transform (STFT)
    stft = np.abs(librosa.stft(audio_data))

    # Convert amplitude to decibels
    db = librosa.amplitude_to_db(stft, ref=np.max)
    mean_db = np.mean(db)

    # -30 dB is a reasonable starting point for “significant” noise
    return mean_db > -30

def extract_features(audio_path):
    """Extract a simple spectral feature (centroid) from an audio file."""
    y, sr = librosa.load(audio_path, sr=16000)
    centroid = librosa.feature.spectral_centroid(y=y, sr=sr)
    return np.mean(centroid)

FFT 에너지 검사는 시스템이 실제 소리 이벤트(코골이, 헐떡임 등)가 포함된 구간만 처리하도록 보장합니다. 코골이는 보통 저주파 영역(≈ 60–500 Hz)을 차지하고, 무호흡 관련 소리는 뚜렷한 스펙트럼 특성을 가집니다.

Faster‑Whisper를 이용한 호흡 분류

from faster_whisper import WhisperModel

# Choose a small model for speed on CPUs
model_size = "base"
model = WhisperModel(model_size, device="cpu", compute_type="int8")

def classify_breathing(audio_segment_path):
    """Run Faster‑Whisper on a short audio segment and return detected tokens."""
    segments, info = model.transcribe(audio_segment_path, beam_size=5)

    findings = [segment.text.lower() for segment in segments]
    return " ".join(findings)

음성을 전사하는 대신 Whisper의 출력을 [snoring], [gasping], [heavy breathing]와 같은 비음성 소리를 분류하는 데 사용합니다.

SQLite로 이벤트 기록하기

import sqlite3
from datetime import datetime

def log_event(event_type, confidence):
    """Store a detected event with a timestamp in a local SQLite database."""
    conn = sqlite3.connect('sleep_health.db')
    c = conn.cursor()
    c.execute('''
        CREATE TABLE IF NOT EXISTS events (
            timestamp TEXT,
            type TEXT,
            confidence REAL
        )
    ''')
    c.execute(
        "INSERT INTO events VALUES (?, ?, ?)",
        (datetime.now().isoformat(), event_type, confidence)
    )
    conn.commit()
    conn.close()

이벤트를 로컬에 저장하면 “등받이에서 잘 때 코골이가 더 심한가?”와 같은 주간 패턴을 분석할 수 있습니다.

다음 단계

  • Matplotlib 대시보드를 추가해 코골이 “핫존”을 시각화합니다.
  • 심박수 모니터를 통합해 맥박 급증과 무호흡 이벤트를 연관시킵니다.
  • Raspberry Pi에 스크립트를 배포해 전용 침대 옆 모니터를 구축합니다.

이러한 확장은 프로토타입을 모든 데이터를 장치 내에 보관하면서도 강력한 개인 건강 대시보드로 전환할 수 있게 해줍니다.

0 조회
Back to Blog

관련 글

더 보기 »

AI 에이전트와 인간을 위한 Jira

우선, 걱정하지 마세요. 여기서 저는 Moltbook을 새로 만든 것이 아닙니다. 제가 아는 모든 스타트업은 도구가 너무 많습니다—어디엔가 보드 하나, Notion 문서 하나, Slack...