Geräuschbenachrichtigungen für gehörlose Menschen

Aus HRW FabLab MediaWiki
Wechseln zu: Navigation, Suche
Geräuschbenachrichtigungen für gehörlose Menschen
Geräuschbenachrichtigungen für gehörlose Menschen


Entwickler

Mathias Bauer, Moritz Meyer, Tat-Nam Nguyen

Projektdateien

GitHub

Verwendete Programmiersprache

Python

Verwendete externe Module

Librosa, Twilio, Pyaudio, NumPy, PyYAML

Eingesetzte Software

Sublime Text, Visual Studio Code, Python 3.7.2, Raspbian Stretch Lite

Eingesetzte Hardware

Raspberry Pi 3 Model B+, Handelsübliche Micro SD Speicherkarte (min. 8 GB Speicher), Handelsübliches USB-Mikrofon

Datenblätter

Raspberry Pi Model B+

Gehörlosigkeit oder Einschränkungen des Hörens stellen Betroffene vor elementare Probleme. Wichtige auditive Signale, wie etwa ein Alarmsignal, werden nicht oder nur ganz schwach vom Gehör erfasst. Dies kann vor allem in Notsituationen lebensentscheidende Folgen haben. Abseits von Notsituationen sind auch alltägliche Gegebenheiten weitreichend betroffen: Ein Klopfen an der Tür, ein laufender Wasserhahn oder ein sich anbahnendes Gewitter sind nur wenige Beispiele hierfür. Um dies zu kompensieren, entscheidet das entwickelte System, ob es den Nutzer oder die Nutzerin benachrichtigen soll. Hierfür werden kontinuierlich alle Umgebungsgeräusche durch ein Mikrofon aufgezeichnet und analysiert. Das Programm begutachtet die auditiven Signale und entscheidet, ob der Nutzer bzw. die Nutzerin per SMS benachrichtigt wird.

In diesem Wiki-Eintrag erfahren Sie, was zur Entwicklung des Projekts motivierte, wie sich das Projekt im Laufe der Zeit entwickelte, welches Feedback erfasst wurde und wie Sie das Projekt selbst umsetzen und nachbauen können. Der Programmcode ist auf GitHub öffentlich verfügbar und unter der GNU General Public License v3.0 kostenfrei verwendbar.

Motivation[Bearbeiten]

Um gehörlosen Menschen in Notsituationen Sicherheit zu bieten, offerieren Hersteller Lichtwarnsysteme, Vibrationsmatten und überdurchschnittlich laute Brandmelder. Diese Systeme können auf persönliche Bedürfnisse zugeschnitten werden, jedoch sind bisherige Lösungen signifikant teuer und eindeutig zu wenig agil. Dieser Markt wird für jeden kostengünstig, effizient und effektiv durch diesen Prototypen supplementiert.

Ideenentwicklung[Bearbeiten]

Da die eingeschränkte Fähigkeit des Hörens angegangen werden soll, müssen Geräusche erkannt werden. Hierfür wurden anfangs Ansätze des maschinellen Lernens verfolgt, welche zeitnah aufgrund von zu kleinen Datensätzen und zu geringer Robustheit gestrichen wurden. In Folge dessen wurde eine statische Herangehensweise, also eine klassische Geräuscherkennung ohne neuronales Netz, konzipiert. Schließlich wurde das Alarmgeräusch eines Rauchmelders ausgewählt, da diese Geräuscherkennung robust umzusetzen ist und für die Nutzerinnen und Nutzer Mehrwert bietet. Maßgebend bei der Ideenfindung und Evaluierung des Prototypen war unter anderem Herr Werner Brosch, Einrichtungsleiter des Internats für hörgeschädigte Schülerinnen und Schüler des Diakoniewerks Essen.

Benachrichtigung im Fall einer Alarmerkennung[Bearbeiten]

Wenn das Signal des Rauchmelders erkannt wird, können Nutzerinnen und Nutzer auf unterschiedlichste Weise benachrichtigt werden.

Ursprünglich war eine Progressive Web App (PWA) geplant, jedoch müsste diese immer im Hintergrund laufen und mit dem Internet verbunden sein. Zudem muss gewährleistet sein, dass mögliches Fehlverhalten auf Seiten der Nutzerinnen und Nutzer minimiert wird, weshalb essentielle Applikationen auf Nutzerebene eher ungeeignet sind. Das System muss möglichst autark fungieren.

Am unproblematischsten sind Benachrichtigungen per E-Mail in Bezug auf die technische Umsetzung, jedoch eignen sich diese aufgrund ihres briefähnlichen Charakters nicht für Warnungen dieser Art. Situativ könnte diese Benachrichtigungsmethode konzeptuell für andere Einsatzgebiete sinnvoll sein, nicht zuletzt aufgrund der geringen bis nicht vorhandenen Kosten von automatisierten E-Mails.

SMS im Gegenzug eignen sich hierfür perfekt - einzig ein Mobilfunknetz muss verfügbar sein. Bei Eingang einer Nachricht vibriert und leuchtet das Smartphone. Es können schnell viele Nachrichten zu günstigen Konditionen versendet werden. Mögliche Fehlerquellen auf Nutzerebene sind quasi nicht vorhanden und mobiles Internet ist keine Voraussetzung.

Angesichts der evaluierten Vor- und Nachteile wurde die Variante der SMS-Benachrichtigung ausgewählt.

Entwicklung[Bearbeiten]

schematischer Ablauf

Geräusche werden auf einem Raspberry Pi durch ein handelsübliches USB-Mikrofon aufgenommen. Ein Python-Skript analysiert und klassifiziert entsprechend in Echtzeit. Sofern das Brandmelder-Geräusch erkannt wird, sendet das Skript eine SMS an die hinterlegten Telefonnummern.

Theoretische Umsetzung[Bearbeiten]

Um die Umgebungsgeräusche analysieren bzw. klassifizieren zu können, muss zuerst das Geräusch des Brandmelders, welches erkannt werden soll, untersucht werden. So wird das Geräusch aufgezeichnet und essentielle Merkmale werden extrahiert. Auf Basis dieser Merkmale kann man nun andere Geräusch-Teile mit diesem vergleichen. Passen beispielsweise zwischen dem Brandmelder-Geräusch und dem zu überprüfenden Geräusch Merkmale wie gewisse Spitzenwerte oder sich zeitlich wiederholende Scheitelwerte überein, so handelt es sich vermutlich um das gesuchte Geräusch. Zu beachten ist, dass man jeden zu untersuchenden Geräusch-Teil aufgrund der permanenten Geräuschaufnahme mit jedem Merkmal des Brandmelders vergleichen muss. Dadurch gehen bei der Analyse keine essentiellen Merkmale aufgrund der zeitlichen Verschiebung verloren. Auch ist zu beachten, dass es immer gewisse Ungenauigkeiten bei Geräuschaufnahmen gibt, welche es entsprechend durch Schwellwerte zu berücksichtigen gilt.
Sind nun also Merkmale des zu erkennenden Geräusches extrahiert worden, kann nun die durchgängige Aufnahme in kleine Teile zerlegt werden und immer wieder mit dem Brandmelder-Geräusch verglichen werden. Bei genügend übereinstimmenden Merkmalen findet dann die Benachrichtigung der Nutzerinnen bzw. Nutzer statt.

Umsetzung mit Python[Bearbeiten]

Der komplette Quellcode ist im verlinkten Github-Repository zu finden.

Zu Beginn muss eine neue Datei angelegt werden. In utils.py werden zunächst nötige externe Module eingebunden, welche später noch benötigt werden. PyAudio bietet u.a. effiziente Audioaufnahmen an, wave lässt WAV-Dateien lesen- und schreiben, Librosa ist ein sehr geeignetes Modul zur Musik- bzw. Audioanalyse, Numpy enthält wichtige mathematische Funktionen und PyYAML stellt den Parser für Konfigurationsdateien im YAML Format dar.

import pyaudio
import wave
import librosa
import numpy as np
import yaml

Später wird für die SMS-Benachrichtigungen Twilio als Dienst genutzt. Um Account-SSID, Authentifizierungs-Token, Telefonnummer und Nachricht an Twilio unkompliziert einstellen zu können, wird das Format YAML genutzt. Entsprechend wird in utils.py eine Funktion angelegt, um YAML-Dateien zu laden:

def load_yaml(path):
  with open(path, 'r') as ymlfile:
    cfg = yaml.load(ymlfile)
  return cfg 


Nun kann auch die YAML-Datei config.yml angelegt werden. XX muss entsprechend ersetzt werden:

twilio: 
  account_sid: XX
  auth_token: XX
  phone_number: XX 
message: Ihr Rauchmelder schlägt Alarm, bitte verlassen Sie sofort das Gebäude.


Zurück in utils.py wird nun die Aufnahme-Funktion angelegt. Hierbei handelt es sich um ein leicht angepasstes PyAudio Beispiel:

def record(path, seconds=2):
  CHUNK = 1024
  FORMAT = pyaudio.paInt16
  CHANNELS = 2
  RATE = 22050
  RECORD_SECONDS = seconds
  WAVE_OUTPUT_FILENAME = path 
p = pyaudio.PyAudio()
stream = p.open( format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK )
frames = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)): data = stream.read(CHUNK) frames.append(data)
stream.stop_stream() stream.close() p.terminate()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb') wf.setnchannels(CHANNELS) wf.setsampwidth(p.get_sample_size(FORMAT)) wf.setframerate(RATE) wf.writeframes(b.join(frames)) wf.close()


Diese Funktion gibt die Summe eines Frequenzbandes zurück. Für einen späteren Iterations-Schritt wird diese Funktion benötigt.

def sum_freq(values, freq):
  return sum(np.array(values)[freq[0]:freq[1]])


Um die Scheitelwerte/Spitzen der Audiosignale über einem bestimmten Schwellwert zu erhalten, wird die nachfolgende Funktion angelegt. Iteriert wird über die gesamten Y-Werte des Signals. Die if-Abfragen stellen Anfang und Ende der Scheitelwerte fest - Rückgabewerte sind die gesuchten X- und Y-Koordinaten der Scheitelwerte/Spitzen.

def get_peaks_max(x, y, threshold):
  peaks_x = []
  peaks_y = []
  start = None
  end = None 
for b in range(len(y)): if y[b] > threshold and start is None: start = b if y[b] < threshold and start is not None: end = b if start is not None and end is not None: delta = end - start peaks_x.append(x[end - int(delta / 2)]) peaks_y.append(max(y[start:end])) start = None end = None
return peaks_x, peaks_y


Als nächstes wird die Anzahl der Scheitelwerte/Spitzen des Audiosignals unter Berücksichtigung von Schwellwerten mit Hilfe der schnellen Fourier-Transformation (FFT) berechnet. Prinzipiell berechnet dieser Algorithmus die diskrete Fourier-Transformation. Da auditive Signale nicht stationär sind, ändern sich die Merkmale über Zeit, deshalb ist eine Zerlegung des Signals in Frequenzanteile nötig. Erst nach dieser Zerlegung kann sinnvoll analysiert werden.

def get_peaks_count(x, sr, threshold=20, frequency_band=[330, 340]):
  hop_length = int(sr / 10)
  X = librosa.stft(x, hop_length=hop_length)
  X = abs(X) 
seconds_total = len(x) / sr seconds_window_size = seconds_total / (len(x) / hop_length)
x = [] y = []
for i in range(X.shape[1]): x.append(i*seconds_window_size) sums = [] for j in range(X.shape[0]): sums.append(X[j][i]) y.append(sum_freq(sums, frequency_band))
p_x, p_y = get_peaks_max(x, y, threshold)
return len(p_y)

Durchaus hilfreich ist das Darstellen des Signals nach Anwendung der FFT:

FFT des Audiosignals eines Brandmelders ohne Umgebungsgeräuche

Des Weiteren ist es hilfreich, sich die markanten Merkmale, wie bspw. die Ausschläge des Brandmelders darstellen zu lassen:

Ausschläge des Brandmelderalarms


Soweit sind alle Funktionen erfasst, die in eine separate Datei ausgelagert sind.
Eine weitere Datei index.py wird angelegt, in welcher erneut notwendige Module importiert werden.

import os
from time import time
import librosa
import utils
from colorama import Fore, Back, Style
from twilio.rest import Client


Nun wird die YAML-Konfiguration geladen und ein Twilio Client angelegt. Zudem müssen noch weitere Variablen initialisiert werden, wie die Empfänger-Telefonnummern und boolesche-Flags für Zeitstempel.

config = utils.load_yaml('config.yml') 
client = Client( config['twilio']['account_sid'], config['twilio']['auth_token'] )
count_log = []
message_sent = False
receivers = ['+491731586539']


Abschließend wird das Programm zusammengeführt und permanent mittels einer while-Schleife ohne Abbruchbedingung ausgeführt.

while True:
  print()
  print(Fore.RED + '●', Style.RESET_ALL, 'Recording… ')
  print()


In diesem Block werden nun die Funktionen aus utils.py ausgeführt. Zuerst wird das Audiosignal mit dem Mikrofon aufgenommen. Da die Daten nicht permanent gespeichert werden, wird dann die temporär aufgenommene WAV-Datei geladen. Auf dieser WAV-Datei wird dann die FFT angewendet. Sollte es passende Merkmale geben, wird der Zähler des Logs erhöht. Dieser darf immer nur maximal fünf Einträge haben. Anschließend wird der Log noch ausgegeben. Es wird also im Intervall von 2 Sekunden eine Aufnahme mit der Länge 2 Sekunden aufgezeichnet. Für die letzten 5 Aufnahmen werden die Ausschläge gezählt (insgesamt also 10 Sekunden). Treten in diesen 10 Sekunden mindestens 5 Ausschläge auf, so wird die Nachricht verschickt.

utils.record('./dist/record.wav') 
x, sr = librosa.load('./dist/record.wav')
count = utils.get_peaks_count(x, sr)
count_log.insert(0, count)
if(len(count_log) > 5): del count_log[-1]
print('---'*10) print('Peeps of count_log records:', count_log) print('---'*10)


Nun muss nur noch überprüft werden, ob in der oben genannten Liste der Logs fünf Brandmelder-Signale gefunden wurden. Zudem wird noch überprüft, ob in den letzten 30 Minuten bereits eine Nachricht abgeschickt wurde. Wenn die if-Anweisung erfüllt ist, werden die Nachrichten versendet. Zudem wird ein weiterer Log und Zeitstempel generiert.

 if sum(count_log) >= 5 and message_sent < time() - 60*30: 
for phone_number in receivers: message = client.messages.create( from_=config['twilio']['phone_number'], to=phone_number, body=config['message'] )
print(Fore.GREEN + 'Send message:') print('{} to {}: {}'.format(config['twilio']['phone_number'], phone_number, config['message']))
message_sent = time()

Tutorial/Einbettung auf einem Raspberry Pi[Bearbeiten]

1. Hardware Voraussetzungen[Bearbeiten]

1.1: Raspberry Pi 3 Model B+ (ältere Version möglich)
1.2: Micro SD-Karte (min. 8 GB Speicherkapazität)
1.3: USB-Mikrofon

2. Installation des Raspbian-OS[Bearbeiten]

Installieren Sie ein Raspbian-OS auf dem Raspberry Pi. Es genügt eine Lite-Version, welche Sie hier herunterladen können. Falls Sie hierbei Hilfe benötigen, klicken Sie hier. Verbinden Sie Maus, Tastatur und Monitor oder verbinden Sie sich per SSH mit dem Raspberry Pi.


3. Abhängigkeiten installieren & Source downloaden[Bearbeiten]

Updaten Sie das System und installieren Sie Git:

sudo su
apt-get install git
apt-get update && apt-get -y upgrade 
rpi-update

Laden Sie den Quellcode von Github per clone-Befehl herunter:

git clone  https://github.com/mrzmyr/hrw-sound-classification.git

4. Konfiguration[Bearbeiten]

Sie benötigen einen Twilio Account und eine dort hinterlegte Telefonnummer. Die account_sid und auth_token können im Dashboard / Console gefunden werden (Twilio - Console dashboard). Bitte tragen Sie alle Daten in die config.yml ein.

twilio: 
  account_sid: XX
  auth_token: XX
  phone_number: XX 

Hinweis: Unter Umständen müssen Sie in utils.py den Channel für das USB-Mikrofon anpassen.

5. Python-Abhängigkeiten installieren[Bearbeiten]

Installieren Sie nun die nötigen Python-Module für den Einsatz des Quellcodes und ein paar weitere Funktionalitäten:

pip install twilio # Twilio-Dienst (SMS-Dienst) 
pip3 install numpy -upgrade
pip3 install numba==0.32.0 
pip install librosa==0.4.2 
pip3 install llvmlite==0.15.0
apt-get install python3-pandas
apt-get install python3-sklearn
apt-get install llvm-3.8 
ln -s /usr/bin/llvm-config-3.8 /usr/bin/llvm-config
aptitude install python3-yaml 
apt-get install libportaudio0 libportaudio2 libportaudiocpp0 portaudio19-dev

6. Bonus: Skript automatisch starten[Bearbeiten]

Starten Sie das Skript automatisch beim Booten des Raspberry Pi. Dies kann durch einen cron-Job erledigt werden. Einfacher wäre ein Init-Script, eine Hinterlegung in rc.local oder .bash_profile. Orientieren Sie sich dabei an diesem Tutorial.

Resümee[Bearbeiten]

Die anfängliche Herangehensweise an die Problemstellung mittels maschinellem Lernen wurde aufgrund von zu kleinen Datensätzen und der mangelnden Robustheit eines lernenden neuronalen Netzes (in der Lernphase) nicht implementiert. Aufgrund dessen wurde die dynamische Herangehensweise durch die hier beschriebene ersetzt. Der anfänglich gewählte Ansatz des maschinellen Lernens wäre einer der nächsten Schritte nach diesem Prototyp. Mit dem maschinellen Lernen von neuen Geräuschen wäre diese Lösung noch agiler. Es könnte der einfache aber mächtige Ansatz des "Ok Google"-Trainings des Google Assistant gewählt werden. Nutzerinnen und Nutzer hätten beispielsweise eine PWA, in welcher ein Button zum erneuten Training des Modells (also der bspw. letzten fünf Sekunden) betätigt werden könnte. Dadurch würden falsche Prognosen verringert und gänzlich neue Geräusche antrainiert.

Mit dem hauptsächlichen Feedbackgeber, Herr Brosch, Einrichtungsleiter des Internats für hörgeschädigte Schülerinnen und Schüler, evaluierten wir zusammen Verbesserungen für unseren Prototyp. So schlug er vor, dass eine anfänglich von uns durchgeführte Speicherung der Umgebungsgeräusche überdacht werden sollte. Entsprechend wurde die permanente Speicherung der Geräuschdaten entfernt und eine sofortige Weiterverarbeitung der nur zwischengespeicherten zweisekündigen Aufnahmen implementiert. Sobald diese zweisekündigen Aufnahmen vom Programm verarbeitet wurden, werden sie entfernt, um die nächsten zweisekündigen Aufnahmen zu verarbeiten.

Zudem wäre auch bei der jetzigen Lösung eine Prozessautomatisierung von Nutzen. So könnte es eine kleine Webseite geben, in welche man Telefonnummern eintragen lassen kann, ähnlich einer Login-Seite eines Wi-Fi-Hotspots. Entsprechend könnten Nutzerinnnen und Nutzer einfach eine Webseite in ihrem Webbrowser auf dem Smartphone öffnen und entsprechend ihre Telefonnummern in ein Formular eintragen, um in Notsituationen benachrichtigt zu werden. Darüber hinaus wäre eine Austragung aus der Liste ein weiterer sinnvoller Schritt.

Anhänge[Bearbeiten]

Die folgenden Bilder sollen durch ihre Visualisierung die Problematiken bei Störgeräuschen und veränderten Bedingungen der Aufnahmemodalitäten darstellen.

originales Brandmeldergeräusch
einfache Aufnahme des Brandmeldergeräusches
Aufnahme des Brandmeldergeräusches mit Störgeräuschen
Umgebungsgeräusche