Python: calibration tool — correction logique fenêtre glissante

Remplace la moyenne glissante par un max glissant sur 3 samples (60ms),
ce qui reflète fidèlement la logique réelle du XIAO : la valeur brute
est comparée au seuil et le trigger reste actif 60ms (pas de moyenne).
Légende mise à jour en conséquence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
j.foucher 2026-02-19 09:40:21 +01:00
parent 6048ed9066
commit 3bb8d6c58a

View File

@ -44,8 +44,10 @@ debug_mode = 3 # 0=OFF, 1=RAW, 2=TRIGGERS, 3=FULL (actif par défaut)
import numpy as np
X = np.arange(WINDOW_SIZE) # axe X fixe, ne change jamais
# Fenêtre de moyenne glissante en samples (60ms window / 50ms debug rate = ~1.2 → 3 samples min)
AVG_WINDOW = 3 # correspond à la accelWindow/gyroWindow/audioWindow envoyée au XIAO (60ms @ 20Hz)
# Fenêtre en samples correspondant à la Window du XIAO (60ms / 50ms debug rate = ~1.2 → 3 samples)
# Le XIAO ne fait PAS de moyenne : il compare la valeur brute au seuil et maintient le trigger 60ms.
# On affiche donc le MAX glissant sur 3 samples = ce que le XIAO "retient" comme pic actif.
AVG_WINDOW = 3
# ─── BLE ────────────────────────────────────────────────
def debug_callback(sender, data):
@ -231,9 +233,9 @@ axes[3].set_yticks([]) # pas de graduations Y sur la timeline
line_a, = axes[0].plot(X, list(accel_buf), color=CA, lw=1.0, alpha=0.5, label='Brut')
line_g, = axes[1].plot(X, list(gyro_buf), color=CG, lw=1.0, alpha=0.5, label='Brut')
line_m, = axes[2].plot(X, list(audio_buf), color=CM, lw=1.0, alpha=0.5, label='Brut')
line_a2, = axes[0].plot(X, list(accel_buf), color=CA2, lw=2.0, label=f'Moyenne ({AVG_WINDOW} pts / 60ms)')
line_g2, = axes[1].plot(X, list(gyro_buf), color=CG2, lw=2.0, label=f'Moyenne ({AVG_WINDOW} pts / 60ms)')
line_m2, = axes[2].plot(X, list(audio_buf), color=CM2, lw=2.0, label=f'Moyenne ({AVG_WINDOW} pts / 60ms)')
line_a2, = axes[0].plot(X, list(accel_buf), color=CA2, lw=2.0, label=f'Pic maintenu ({AVG_WINDOW} pts / 60ms) ← logique XIAO')
line_g2, = axes[1].plot(X, list(gyro_buf), color=CG2, lw=2.0, label=f'Pic maintenu ({AVG_WINDOW} pts / 60ms) ← logique XIAO')
line_m2, = axes[2].plot(X, list(audio_buf), color=CM2, lw=2.0, label=f'Pic maintenu ({AVG_WINDOW} pts / 60ms) ← logique XIAO')
line_s, = axes[3].plot(X, list(shot_buf), color=CS, lw=0, marker='|',
markersize=20, markeredgewidth=2.5) # barres verticales
@ -276,10 +278,12 @@ def update_spans(spans, trig_list):
for i, span in enumerate(spans):
span.set_alpha(0.25 if i < len(tl) and tl[i] else 0)
def rolling_avg(arr, window):
"""Moyenne glissante simple — même logique que le XIAO"""
kernel = np.ones(window) / window
return np.convolve(arr, kernel, mode='same')
def rolling_max(arr, window):
"""Max glissant — simule le trigger qui reste actif pendant 'window' samples (logique XIAO)"""
from numpy.lib.stride_tricks import sliding_window_view
pad = window - 1
padded = np.pad(arr, (pad, 0), mode='edge')
return np.max(sliding_window_view(padded, window), axis=1)
def update(frame):
# Snapshot des buffers (rapide)
@ -288,10 +292,10 @@ def update(frame):
m = np.array(audio_buf, dtype=float)
s = np.array(shot_buf)
# Moyennes glissantes (même fenêtre que le XIAO)
a_avg = rolling_avg(a, AVG_WINDOW)
g_avg = rolling_avg(g, AVG_WINDOW)
m_avg = rolling_avg(m, AVG_WINDOW)
# Pic maintenu sur la fenêtre (même logique que le trigger XIAO)
a_avg = rolling_max(a, AVG_WINDOW)
g_avg = rolling_max(g, AVG_WINDOW)
m_avg = rolling_max(m, AVG_WINDOW)
# Mise à jour des données des lignes
line_a.set_ydata(a)