Sensorfusion für Fitness: Präziser Wiederholungszähler
Unzuverlässige Einzelsensordaten adé. Lernen Sie, Beschleunigungssensor- und Gyrosignale mit einem Komplementärfilter zu fusionieren, um einen robusten Wiederholungszähler zu entwickeln.
Auf dieser Seite
Fitness-Apps sind überall, aber viele kämpfen mit einer fundamentalen Aufgabe: Wiederholungen für Übungen präzise zu zählen, die komplexer sind als Gehen oder Laufen. Wenn Sie jemals eine App verwendet haben, die Wiederholungen überspringt oder Phantom-Bewegungen zählt, haben Sie die Einschränkungen der Verwendung eines einzelnen Sensors erlebt. Der Beschleunigungsmesser in Ihrem Telefon oder Wearable ist verrauscht, und das Gyroskop, obwohl hervorragend für Rotation geeignet, driftet mit der Zeit. Die Lösung? Sensorfusion.
In diesem tiefgehenden Tutorial werden wir einen robusten Wiederholungszähler erstellen, indem wir Beschleunigungsmesser- und Gyroskopdaten fusionieren. Wir verzichten auf abstrakte Theorie und schreiben praktischen Python-Code, um rohe Sensorsignale zu verarbeiten, einen Filter anzuwenden, um eine saubere Orientierungsschätzung zu erhalten, und dann einen intelligenten Zählalgorithmus aufzubauen.
Dies ist für Entwickler relevant, da die Nachfrage nach präzisem, echtzeit-Feedback in Gesundheits- und Fitnesstechnologie boomt. Die Beherrschung dieser Techniken ermöglicht es Ihnen, intelligentere, zuverlässigere und wertvollere Anwendungen für Wearables, Mobilgeräte und IoT-Geräte zu erstellen.
Voraussetzungen:
- Grundlegendes Verständnis von Python.
- Vertrautheit mit Konzepten wie Vektoren und Winkeln.
- Python 3 und die NumPy-Bibliothek installiert.
Das Problem verstehen
Um eine großartige Lösung zu entwickeln, müssen wir zunächst die Schwächen unserer Rohstoffe schätzen: den Beschleunigungsmesser und das Gyroskop.
Eine Trägheitsmesseinheit (IMU), die in jedem modernen Smartphone enthalten ist, kombiniert typischerweise diese beiden Sensoren.
-
Beschleunigungsmesser: Misst die Eigenbeschleunigung (einschließlich der konstanten Anziehungskraft der Schwerkraft). Wir können die Richtung der Schwerkraft nutzen, um die Ausrichtung des Geräts (Neigung) zu bestimmen.
- Das Gute: Er ist langfristig stabil. Die Schwerkraft verschwindet nirgendwo.
- Das Schlechte: Er ist sehr empfindlich gegenüber externen Kräften. Ein plötzliches Schütteln, Vibration oder Bewegungsrauschen korruptiert die Orientierungsdaten.
-
Gyroskop: Misst die Winkelgeschwindigkeit (wie schnell sich das Gerät um eine Achse dreht). Wir können diese Geschwindigkeit über die Zeit integrieren, um Änderungen in der Orientierung zu verfolgen.
- Das Gute: Es ist hervorragend bei der Erkennung schneller Rotationsänderungen und ist immun gegen das externe Schütteln, das Beschleunigungsmesser plagt.
- Das Schlechte: Es driftet. Kleine, unvermeidliche Fehler in seinen Messungen akkumulieren sich über die Zeit und führen dazu, dass der berechnete Winkel zunehmend ungenau wird.
Wenn wir uns nur auf einen verlassen, wird unser Wiederholungszähler versagen. Ein rein auf dem Beschleunigungsmesser basierender Zähler wird bei jeder ruckartigen Bewegung Fehlalarme auslösen, während ein rein auf dem Gyroskop basierender Zähler letztendlich so stark driften wird, dass er die tatsächliche Orientierung verliert.
Hier kommt die Sensorfusion ins Spiel. Wir können die langfristige Stabilität des Beschleunigungsmessers mit der Kurzzeitgenauigkeit des Gyroskops kombinieren, um das Beste aus beiden Welten zu erhalten.
Voraussetzungen
Bevor wir mit dem Programmieren beginnen, richten wir unsere Umgebung ein. Sie benötigen Python 3 und NumPy.
# Stellen Sie sicher, dass python3 und pip installiert sind
pip install numpy
# Erwartete Ausgabe:
# Successfully installed numpy-x.x.x
Wir werden mit einem Beispieldatensatz arbeiten, um dieses Tutorial auf den Algorithmus zu konzentrieren. Sie können sich vorstellen, dass diese Daten von einem Mobilgerät unter Verwendung einer Bibliothek wie react-native-sensors von React Native oder direkt von einem IoT-Gerät gestreamt werden.
Schritt 1: Analyse unserer rohen Sensordaten
Zunächst definieren wir das Problem mit Code. Wir simulieren den verrauschten Beschleunigungsmesser und das driftende Gyroskop, um zu sehen, womit wir es zu tun haben. Bei einer Bizepscurl ist die Hauptaktion die Änderung des Winkels des Unterarms. Der Einfachheit halber konzentrieren wir uns auf eine einzelne Achse.
Was wir tun
Wir berechnen den Neigungswinkel des Geräts separat aus den Beschleunigungsmesser- und Gyroskopdaten, um ihre individuellen Schwächen zu visualisieren.
Implementierung
Nehmen wir an, wir haben eine Funktion get_sensor_data(), die (ax, ay, az, gx, gy, gz) vom IMU des Telefons zurückgibt. Für eine Bizepscurl, bei der Sie das Telefon halten, könnte der interessierende Winkel die Rotation um die X-Achse sein.
# src/data_analysis.py
import numpy as np
# dt: Zeitdelta zwischen Sensorablesungen
dt = 0.01
def get_accel_angle(ax, ay, az):
"""Berechnet den Neigungswinkel aus Beschleunigungsmesser-Daten."""
# Hinweis: Dies ist eine vereinfachte Berechnung für eine Achse.
# Eine vollständige Implementierung würde atan2(ay, az) für Pitch usw. verwenden.
return np.arctan2(ay, np.sqrt(ax**2 + az**2)) * 180 / np.pi
# Gyroskopdaten müssen integriert werden
gyro_angle_x = 0.0
def get_gyro_angle(gx, dt):
"""Berechnet den Winkel durch Integration der Gyroskopdaten."""
global gyro_angle_x
gyro_angle_x += gx * dt
return gyro_angle_x
# --- Lassen Sie uns einige Messwerte simulieren ---
# Simulieren einer Bizepscurl: Winkel geht von -90 auf 0 und zurück
true_angle = -90
gyro_drift = 0.1 # Eine kleine, konstante Drift
accel_noise_stddev = 2.5 # Rauschen in den Beschleunigungsmesserablesungen
# Speichern unserer Ergebnisse
simulated_gyro_angles = []
simulated_accel_angles = []
for _ in range(300): # Simuliere 3 Sekunden Daten
# Simuliere eine sanfte Curl-Bewegung
true_angle += 0.6
if true_angle > 0: true_angle = 0 # Begrenzt am oberen Ende
# Erzeuge verrauschte Beschleunigungsmesser-Daten
accel_reading_y = np.sin(np.deg2rad(true_angle)) + np.random.normal(0, accel_noise_stddev)
accel_reading_z = np.cos(np.deg2rad(true_angle)) + np.random.normal(0, accel_noise_stddev)
# Erzeuge driftende Gyroskopdaten
gyro_reading_x = 0.6 / dt # Ideale Winkelgeschwindigkeit
gyro_reading_x += gyro_drift # Drift hinzufügen
# Berechne Winkel
accel_angle = get_accel_angle(0, accel_reading_y, accel_reading_z)
gyro_angle = get_gyro_angle(gyro_reading_x, dt)
simulated_accel_angles.append(accel_angle)
simulated_gyro_angles.append(gyro_angle)
print(f"Finaler Gyro-Winkel (mit Drift): {simulated_gyro_angles[-1]:.2f}")
print(f"Finaler Accel-Winkel (sollte verrauscht sein, aber um 0): {simulated_accel_angles[-1]:.2f}")
Wie es funktioniert
get_accel_angle: Verwendet Trigonometrie (arctan2), um die Neigung basierend auf den Komponenten des Schwerkraftvektors zu berechnen.ayundazändern sich, wenn das Gerät geneigt wird.get_gyro_angle: Simuliert die Integration. Es nimmt die Winkelgeschwindigkeit (gx), multipliziert sie mit dem Zeitschritt (dt), um die Änderung des Winkels zu erhalten, und addiert sie zum vorherigen Winkel.- Simulation: Wir erstellen einen "wahren" Winkel und erzeugen dann Sensorablesungen daraus, indem wir realistisches Rauschen und Drift hinzufügen. Dies zeigt das Problem deutlich: Der finale Gyrowinkel ist erheblich vom wahren Wert (0) abgewichen, während die Beschleunigungsmesserablesung verrauscht ist.
Schritt 2: Fusion der Signale mit einem Komplementärfilter
Ein Kalman-Filter ist die optimale Lösung für Sensorfusion, aber er ist komplex. Ein Komplementärfilter ist eine fantastische, recheneffiziente Alternative, die ausgezeichnete Ergebnisse liefert. Er ist perfekt für mobile und eingebettete Systeme.
Die Kernidee ist einfach:
FusedAngle = α * (Gyro-basierter Winkel) + (1 - α) * (Beschleunigungsmesser-basierter Winkel)
Wir vertrauen dem Gyroskop für hochfrequente Änderungen, ergänzen es aber mit den stabilen Tieffrequenzdaten des Beschleunigungsmessers. Der α-Wert bestimmt das Gleichgewicht. Ein häufiger Wert ist 0.98.
Was wir tun
Wir erstellen eine Funktion, die die rohen Sensordaten und den vorherigen fusionierten Winkel entgegennimmt und den neuen, sauberen Winkel zurückgibt.
Implementierung
// src/fusion_filter.py
import numpy as np
class ComplementaryFilter:
def __init__(self, alpha=0.98):
self.alpha = alpha
self.angle = 0.0
def process(self, ax, ay, az, gx, dt):
"""
Verarbeitet neue Sensordaten, um den fusionierten Winkel zu aktualisieren.
"""
# Berechne Winkel aus dem Beschleunigungsmesser
accel_angle = np.arctan2(ay, np.sqrt(ax**2 + az**2)) * 180 / np.pi
# Integriere Gyroskopdaten, um Änderung des Winkels zu erhalten
gyro_angle_change = gx * dt
# Wende den Komplementärfilter an
# Vertraue dem Gyroskop für Kurzzeitänderungen, korrigiere mit dem Beschleunigungsmesser
self.angle = self.alpha * (self.angle + gyro_angle_change) + (1 - self.alpha) * accel_angle
return self.angle
# --- Lassen Sie uns unsere Simulation erneut mit dem Filter ausführen ---
filter = ComplementaryFilter(alpha=0.98)
fused_angles = []
# Simulationvariablen zurücksetzen
gyro_angle_x = 0.0
for _ in range(300):
# (Wiederverwendung der gleichen Simulationslogik aus Schritt 1)
true_angle += 0.6
if true_angle > 0: true_angle = 0
accel_reading_y = np.sin(np.deg2rad(true_angle)) + np.random.normal(0, accel_noise_stddev)
accel_reading_z = np.cos(np.deg2rad(true_angle)) + np.random.normal(0, accel_noise_stddev)
gyro_reading_x = (0.6 / dt) + gyro_drift
fused_angle = filter.process(0, accel_reading_y, accel_reading_z, gyro_reading_x, dt)
fused_angles.append(fused_angle)
print(f"Finaler fusionierter Winkel: {fused_angles[-1]:.2f}")
Wie es funktioniert
Die Klasse ComplementaryFilter verwaltet den aktuellen Zustand (self.angle). Bei jedem process-Aufruf:
- Sie berechnet die aktuelle Neigung aus dem Beschleunigungsmesser.
- Sie berechnet die Änderung des Winkels aus dem Gyroskop.
- Sie berechnet den neuen fusionierten Winkel, indem sie 98% des projizierten neuen Winkels aus dem Gyroskop nimmt und 2% des vom Beschleunigungsmesser gemessenen Winkels hinzufügt. Dies zieht den driftenden Gyrowinkel sanft zurück in Richtung des stabilen (aber verrauschten) Beschleunigungsmesserwinkels und korrigiert die Drift über die Zeit.
Die Ausgabe wird bemerkenswert nahe am wahren Wert von 0 liegen und damit die Leistungsfähigkeit des Filters demonstrieren. ✨
Schritt 3: Wiederholungen zählen mit einem Zustandsautomaten
Mit einem sauberen Signal können wir endlich Wiederholungen zählen. Ein naiver Ansatz wäre, einfach Spitzen in den Winkeldaten zu zählen. Dies ist jedoch anfällig. Was ist, wenn der Benutzer mitten in einer Wiederholung pausiert?
Eine robustere Methode ist ein Zustandsautomat. Eine Übung wie eine Bizepscurl hat unterschiedliche Phasen: eine "Hoch"-Phase und eine "Runter"-Phase. Wir können den Zustand des Benutzers verfolgen, während er sich durch diese Phasen bewegt.
Was wir tun
Wir definieren Zustände (IDLE, GOING_UP, GOING_DOWN) und Winkelschwellenwerte, die Übergänge zwischen ihnen auslösen. Wir erhöhen unseren Zähler nur, wenn ein vollständiger Zyklus abgeschlossen ist.
Implementierung
# src/rep_counter.py
class RepCounter:
def __init__(self, entry_threshold, exit_threshold):
self.count = 0
self.state = "IDLE" # Kann IDLE, GOING_UP, GOING_DOWN sein
self.entry_threshold = entry_threshold # Winkel, um eine Wiederholung zu starten
self.exit_threshold = exit_threshold # Winkel, um eine Wiederholung abzuschließen
def process(self, angle):
"""
Verarbeitet einen neuen Winkel, um die Wiederholungsanzahl zu aktualisieren.
Gibt die aktuelle Wiederholungsanzahl zurück.
"""
if self.state == "IDLE":
if angle > self.entry_threshold:
self.state = "GOING_UP"
elif self.state == "GOING_UP":
if angle < self.exit_threshold:
self.state = "GOING_DOWN"
elif self.state == "GOING_DOWN":
if angle >= self.exit_threshold:
self.count += 1
print(f"Wiederholung {self.count} erkannt!")
self.state = "IDLE"
return self.count
# --- Lassen Sie uns alles zusammenbringen ---
# Für eine Bizepscurl beginnt eine Wiederholung, wenn der Arm 45 Grad passiert
# und endet, wenn er oben ist (z.B. 140 Grad).
# Definieren wir Schwellenwerte für unseren fusionierten Winkelraum.
# Angenommen, der fusionierte Winkel ist ~0 am Anfang und ~90 am Höhepunkt.
rep_counter = RepCounter(entry_threshold=30.0, exit_threshold=75.0)
# Simuliere zwei vollständige Wiederholungen
full_motion_fused_angles = fused_angles + list(reversed(fused_angles)) # Eine Wiederholung
full_motion_fused_angles += full_motion_fused_angles # Zwei Wiederholungen
for angle in full_motion_fused_angles:
rep_counter.process(angle)
print(f"\
Finale Wiederholungsanzahl: {rep_counter.count}")
Wie es funktioniert
- Initialisierung: Der Zähler startet im Zustand
IDLEmit 0 Wiederholungen. Wir definieren zwei Winkelschwellenwerte. IDLEzuGOING_UP: Wenn der Winkel erstmals denentry_thresholdüberschreitet, wissen wir, dass eine Wiederholung begonnen hat. Der Zustand wechselt zuGOING_UP.GOING_UPzuGOING_DOWN: Wenn der Benutzer den Höhepunkt der Bewegung erreicht und wieder herunterkommt, nimmt der Winkel ab und überschreitet denexit_thresholdauf dem Weg nach unten. Wir ändern den Zustand zuGOING_DOWN.GOING_DOWNzuIDLE: Wenn der Benutzer in die Ausgangsposition zurückkehrt und der Winkel wieder über denexit_thresholdsteigt, wissen wir, dass eine vollständige Wiederholung erfolgreich abgeschlossen wurde. Wir erhöhen den Zähler und setzen den Zustand aufIDLEzurück, bereit für die nächste.
Diese Logik ist viel widerstandsfähiger gegen Pausen und kleine, ruckartige Bewegungen als einfaches Spitzenzählen.
Anpassung an andere Übungen
Die Schönheit dieses Frameworks liegt in seiner Anpassungsfähigkeit.
-
Kettlebell Swings: Hier könnte sich das Telefon in einer Tasche befinden. Die Hauptbewegung ist ein kraftvoller Hüftschwung, der zu einem zyklischen Muster in der Vorwärtsbeschleunigung (
az) und der Winkelgeschwindigkeit führt. Sie könnten dieselbe Fusions- und Zustandsautomaten-Logik auf den fusionierten Nickwinkel des Rumpfes oder Oberschenkels anwenden, um Spitze und Tiefpunkt des Schwungs zu erkennen. -
Kniebeugen: Ähnlich wie bei Kettlebell Swings würde die vertikale Bewegung des Telefons und der Nickwinkel ein sauberes, zyklisches Signal zum Zählen der Wiederholungen liefern. Die Schwellenwerte müssten lediglich angepasst werden.
Leistungsaspekte
- Komplementärfilter: Dies ist extrem leichtgewichtig. Es sind nur ein paar Multiplikationen und Additionen pro Sensorablesung erforderlich, was ihn perfekt für Echtzeit-Verarbeitung auf einem Mobilgerät oder sogar einem Mikrocontroller macht.
- Kalman-Filter: Eine vollständige Kalman-Filter-Implementierung ist rechenaufwändiger aufgrund von Matrixoperationen. Während genauer, könnte der Leistungskompromiss für einfaches Wiederholungszählen nicht lohnenswert sein. Verwenden Sie ihn, wenn Sie hochpräzise Orientierung für Anwendungen wie VR/AR oder Robotik benötigen.
Alternative Ansätze
- Kalman-Filter: Wie erwähnt, ist dies der Goldstandard. Er verwendet ein Vorhersagemodell des Systemzustands und ist außergewöhnlich gut im Umgang mit Rauschen. Bibliotheken wie
pykalmanin Python können helfen, aber Sie benötigen ein solides Verständnis der linearen Algebra, um ihn effektiv zu optimieren. - Maschinelles Lernen / Deep Learning: Sie können ein neuronales Netz auf beschrifteten Sensordaten trainieren, um Bewegungen zu klassifizieren und Wiederholungen zu zählen. Dieser Ansatz kann sehr leistungsfähig sein und nuancenreiche Übungen ohne manuelle Schwellenwertoptimierung erkennen. Allerdings erfordert er einen großen, hochwertigen Datensatz und mehr Rechenleistung für die Inferenz.
Fazit
Wir haben erfolgreich von verrauschten, unzuverlässigen Sensorablesungen zu einem intelligenten, zustandsbasierten Wiederholungszähler gewechselt. Indem wir die Schwächen einzelner Sensoren verstanden und sie mit einem einfachen und effektiven Komplementärfilter abgemildert haben, haben wir ein sauberes Signal erstellt. Wir haben dann dieses Signal verwendet, um einen Zustandsautomaten anzutreiben, der Wiederholungen robust zählt und kleine Störungen und Pausen ignoriert, die einfachere Algorithmen täuschen.
Diese Grundlage ist ein Ausgangspunkt für die Entwicklung anspruchsvoller Fitness-Anwendungen. Sie können jetzt Workouts zuverlässig verfolgen, Echtzeit-Feedback zur Form geben und ein ansprechenderes und genaueres Benutzererlebnis schaffen.
Nächste Schritte für Sie:
- Probieren Sie dies mit echten Sensordaten von Ihrem Telefon.
- Passen Sie die Schwellenwerte und Achsen für eine andere Übung wie Kniebeugen oder Liegestütze an.
- Erkunden Sie eine Kalman-Filter-Implementierung für einen Genauigkeitsvergleich.
Ressourcen
Kurze Referenz
| Aspekt | Wichtige Informationen |
|---|---|
| Thema | Entwicklung |
| Relevanz | Patientenaufklärung und Bewusstsein |
| Aktion | Besprechen Sie dies mit Ihrem Arzt |
| Häufigkeit | Nach Empfehlung Ihres Betreuungsteams |
| Evidenz | Basierend auf aktuellen klinischen Richtlinien |
Häufig gestellte Fragen
F: Wie weiß ich, ob dies für mich relevant ist? A: Besprechen Sie Ihre persönliche und familiäre Krankengeschichte mit Ihrem Arzt, um Ihre individuellen Risikofaktoren zu bewerten und geeignete nächste Schritte zu bestimmen.
F: Was sollte ich meinen Arzt fragen? A: Gute Fragen sind: "Was bedeuten meine Ergebnisse?", "Was sind meine Optionen?", "Welche Lebensstiländerungen würden helfen?" und "Wann sollte ich nachuntersuchen?"
F: Gibt es Lebensstiländerungen, die helfen können? A: Die meisten Gesundheitszustände profitieren von grundlegenden Maßnahmen: ausgewogene Ernährung, regelmäßige körperliche Aktivität (150+ Minuten/Woche), hochwertiger Schlaf (7-9 Stunden), Stressbewältigung und Vermeidung von Tabak.
Haftungsausschluss
Dieser Artikel dient nur zu Bildungszwecken und stellt keine medizinische Beratung dar. Konsultieren Sie immer einen qualifizierten Gesundheitsdienstleister, bevor Sie Entscheidungen über Ihre Gesundheit, Behandlung oder Pflege treffen. Individuelle Umstände variieren, und das, was für eine Person angemessen ist, ist möglicherweise nicht für eine andere geeignet.
Wenn Sie einen medizinischen Notfall haben, rufen Sie sofort 911 (oder Ihre örtliche Notrufnummer) an.