Python: calibration tool — ligne orange pic dernier tir + titre simplifié
Ajoute une ligne horizontale orange sur chaque graphe indiquant le niveau atteint au moment du dernier tir détecté, avec % du seuil dans le titre. Permet de comprendre pourquoi un tir est déclenché même si le pic BLE (20Hz) ne semble pas dépasser le seuil (résolution temporelle limitée). Supprime le pic 1s (remplacé par le pic dernier tir, plus pertinent). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f23ddf9a82
commit
f33fd5b216
@ -34,6 +34,8 @@ thresholds = {"accel": 2.5, "gyro": 200.0, "audio": 3000} # PDM : 0-32767
|
|||||||
min_sensors = 3 # Nb de capteurs requis simultanément (1-3) — NUM6=+1 NUM4=-1
|
min_sensors = 3 # Nb de capteurs requis simultanément (1-3) — NUM6=+1 NUM4=-1
|
||||||
shot_count = 0
|
shot_count = 0
|
||||||
shot_pending = False # Flag : un tir reçu, à enregistrer dans shot_buf au prochain debug tick
|
shot_pending = False # Flag : un tir reçu, à enregistrer dans shot_buf au prochain debug tick
|
||||||
|
# Pic max au moment du dernier tir (mis à jour au moment où shot_pending devient True)
|
||||||
|
last_shot_peak = {"accel": 0.0, "gyro": 0.0, "audio": 0}
|
||||||
ble_status = "🔍 Connexion..."
|
ble_status = "🔍 Connexion..."
|
||||||
ble_running = True
|
ble_running = True
|
||||||
audio_max_global = 1000 # Tracks le max absolu jamais vu
|
audio_max_global = 1000 # Tracks le max absolu jamais vu
|
||||||
@ -66,10 +68,14 @@ def debug_callback(sender, data):
|
|||||||
shot_pending = False
|
shot_pending = False
|
||||||
|
|
||||||
def shot_callback(sender, data):
|
def shot_callback(sender, data):
|
||||||
global shot_count, shot_pending
|
global shot_count, shot_pending, last_shot_peak
|
||||||
if data[0] == 1:
|
if data[0] == 1:
|
||||||
shot_count += 1
|
shot_count += 1
|
||||||
shot_pending = True
|
shot_pending = True
|
||||||
|
# Capturer le pic courant au moment exact du tir (meilleure approximation possible en BLE)
|
||||||
|
last_shot_peak["accel"] = accel_buf[-1] if accel_buf else 0.0
|
||||||
|
last_shot_peak["gyro"] = gyro_buf[-1] if gyro_buf else 0.0
|
||||||
|
last_shot_peak["audio"] = audio_buf[-1] if audio_buf else 0
|
||||||
|
|
||||||
async def find_device():
|
async def find_device():
|
||||||
"""Scan par nom (toutes les 0.5s pendant 30s max).
|
"""Scan par nom (toutes les 0.5s pendant 30s max).
|
||||||
@ -235,11 +241,16 @@ thr_a = axes[0].axhline(thresholds["accel"], color=CT, ls='--', lw=1.5, label='S
|
|||||||
thr_g = axes[1].axhline(thresholds["gyro"], color=CT, ls='--', lw=1.5, label='Seuil (trigger XIAO)')
|
thr_g = axes[1].axhline(thresholds["gyro"], color=CT, ls='--', lw=1.5, label='Seuil (trigger XIAO)')
|
||||||
thr_m = axes[2].axhline(thresholds["audio"], color=CT, ls='--', lw=1.5, label='Seuil (trigger XIAO)')
|
thr_m = axes[2].axhline(thresholds["audio"], color=CT, ls='--', lw=1.5, label='Seuil (trigger XIAO)')
|
||||||
|
|
||||||
# Légendes — incluent le fond rouge = trigger actif
|
# Ligne horizontale orange = pic au moment du dernier tir (invisible au départ)
|
||||||
|
peak_a = axes[0].axhline(0, color=CS, ls='-', lw=1.5, alpha=0.0, label='Pic dernier tir')
|
||||||
|
peak_g = axes[1].axhline(0, color=CS, ls='-', lw=1.5, alpha=0.0, label='Pic dernier tir')
|
||||||
|
peak_m = axes[2].axhline(0, color=CS, ls='-', lw=1.5, alpha=0.0, label='Pic dernier tir')
|
||||||
|
|
||||||
|
# Légendes
|
||||||
for ax in axes[:3]:
|
for ax in axes[:3]:
|
||||||
ax.legend(loc='upper left', fontsize=7, facecolor=PANEL, edgecolor='#444466',
|
ax.legend(loc='upper left', fontsize=7, facecolor=PANEL, edgecolor='#444466',
|
||||||
labelcolor=TEXT, framealpha=0.8,
|
labelcolor=TEXT, framealpha=0.8,
|
||||||
handles=[ax.get_lines()[0], ax.get_lines()[1],
|
handles=[ax.get_lines()[0], ax.get_lines()[1], ax.get_lines()[2],
|
||||||
plt.Rectangle((0,0),1,1, fc=CT, alpha=0.25, label='Trigger actif (XIAO)')])
|
plt.Rectangle((0,0),1,1, fc=CT, alpha=0.25, label='Trigger actif (XIAO)')])
|
||||||
|
|
||||||
from matplotlib.patches import Rectangle
|
from matplotlib.patches import Rectangle
|
||||||
@ -279,17 +290,25 @@ def update(frame):
|
|||||||
m = np.array(audio_buf, dtype=float)
|
m = np.array(audio_buf, dtype=float)
|
||||||
s = np.array(shot_buf)
|
s = np.array(shot_buf)
|
||||||
|
|
||||||
# Pic max sur la dernière seconde (pour affichage dans le titre)
|
# Mise à jour des données des lignes brutes
|
||||||
a_peak = float(a[-PEAK_WINDOW:].max())
|
|
||||||
g_peak = float(g[-PEAK_WINDOW:].max())
|
|
||||||
m_peak = float(m[-PEAK_WINDOW:].max())
|
|
||||||
|
|
||||||
# Mise à jour des données des lignes
|
|
||||||
line_a.set_ydata(a)
|
line_a.set_ydata(a)
|
||||||
line_g.set_ydata(g)
|
line_g.set_ydata(g)
|
||||||
line_m.set_ydata(m)
|
line_m.set_ydata(m)
|
||||||
line_s.set_ydata(s)
|
line_s.set_ydata(s)
|
||||||
|
|
||||||
|
# Ligne orange = pic au moment du dernier tir (visible seulement si un tir a eu lieu)
|
||||||
|
if shot_count > 0:
|
||||||
|
peak_a.set_ydata([last_shot_peak["accel"]] * 2)
|
||||||
|
peak_g.set_ydata([last_shot_peak["gyro"]] * 2)
|
||||||
|
peak_m.set_ydata([last_shot_peak["audio"]] * 2)
|
||||||
|
peak_a.set_alpha(0.9)
|
||||||
|
peak_g.set_alpha(0.9)
|
||||||
|
peak_m.set_alpha(0.9)
|
||||||
|
else:
|
||||||
|
peak_a.set_alpha(0.0)
|
||||||
|
peak_g.set_alpha(0.0)
|
||||||
|
peak_m.set_alpha(0.0)
|
||||||
|
|
||||||
# Mise à jour des seuils
|
# Mise à jour des seuils
|
||||||
thr_a.set_ydata([thresholds["accel"], thresholds["accel"]])
|
thr_a.set_ydata([thresholds["accel"], thresholds["accel"]])
|
||||||
thr_g.set_ydata([thresholds["gyro"], thresholds["gyro"]])
|
thr_g.set_ydata([thresholds["gyro"], thresholds["gyro"]])
|
||||||
@ -310,22 +329,22 @@ def update(frame):
|
|||||||
for i, span in enumerate(shot_spans):
|
for i, span in enumerate(shot_spans):
|
||||||
span.set_alpha(0.85 if i < len(sl) and sl[i] > 0 else 0)
|
span.set_alpha(0.85 if i < len(sl) and sl[i] > 0 else 0)
|
||||||
|
|
||||||
# Titres avec valeurs courantes
|
# Titres avec valeurs courantes + pic dernier tir
|
||||||
a_ratio = a_peak / thresholds['accel'] * 100
|
pa = last_shot_peak["accel"] if shot_count > 0 else None
|
||||||
g_ratio = g_peak / thresholds['gyro'] * 100
|
pg = last_shot_peak["gyro"] if shot_count > 0 else None
|
||||||
m_ratio = m_peak / thresholds['audio'] * 100
|
pm = last_shot_peak["audio"] if shot_count > 0 else None
|
||||||
titles[0].set_text(
|
titles[0].set_text(
|
||||||
f"Accelerometre "
|
f"Accelerometre val: {a[-1]:.2f} G seuil: {thresholds['accel']:.1f} G"
|
||||||
f"val: {a[-1]:.2f} G pic(1s): {a_peak:.2f} G seuil: {thresholds['accel']:.1f} G "
|
+ (f" ── pic dernier tir: {pa:.2f} G ({pa/thresholds['accel']*100:.0f}%)" if pa else "")
|
||||||
f"({a_ratio:.0f}% du seuil) [NUM7=+0.1 NUM1=-0.1]")
|
+ f" [NUM7=+0.1 NUM1=-0.1]")
|
||||||
titles[1].set_text(
|
titles[1].set_text(
|
||||||
f"Gyroscope "
|
f"Gyroscope val: {g[-1]:.0f} dps seuil: {thresholds['gyro']:.0f} dps"
|
||||||
f"val: {g[-1]:.0f} dps pic(1s): {g_peak:.0f} dps seuil: {thresholds['gyro']:.0f} dps "
|
+ (f" ── pic dernier tir: {pg:.0f} dps ({pg/thresholds['gyro']*100:.0f}%)" if pg else "")
|
||||||
f"({g_ratio:.0f}% du seuil) [NUM8=+10 NUM2=-10]")
|
+ f" [NUM8=+10 NUM2=-10]")
|
||||||
titles[2].set_text(
|
titles[2].set_text(
|
||||||
f"Microphone PDM "
|
f"Microphone PDM val: {m[-1]:.0f} seuil: {thresholds['audio']}"
|
||||||
f"val: {m[-1]:.0f} pic(1s): {m_peak:.0f} seuil: {thresholds['audio']} "
|
+ (f" ── pic dernier tir: {pm:.0f} ({pm/thresholds['audio']*100:.0f}%)" if pm else "")
|
||||||
f"({m_ratio:.0f}% du seuil) [NUM9=+500 NUM3=-500]")
|
+ f" [NUM9=+500 NUM3=-500]")
|
||||||
titles[3].set_text(f"Tirs detectes : {shot_count} (fenetre glissante)")
|
titles[3].set_text(f"Tirs detectes : {shot_count} (fenetre glissante)")
|
||||||
|
|
||||||
status_txt.set_text(
|
status_txt.set_text(
|
||||||
@ -336,6 +355,7 @@ def update(frame):
|
|||||||
|
|
||||||
return (line_a, line_g, line_m, line_s,
|
return (line_a, line_g, line_m, line_s,
|
||||||
thr_a, thr_g, thr_m,
|
thr_a, thr_g, thr_m,
|
||||||
|
peak_a, peak_g, peak_m,
|
||||||
*titles, status_txt, debug_txt)
|
*titles, status_txt, debug_txt)
|
||||||
|
|
||||||
def on_key(event):
|
def on_key(event):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user