GC Python génération 0 (seuil 700 objets) peut déclencher des pauses
d'arrêt-du-monde (~1-2s) périodiquement quand l'encodage JPEG crée beaucoup
d'objets temporaires NumPy. Désactiver le GC automatique pendant l'exécution
élimine ces jitters, en particulier les freezes observés toutes les ~11s.
Cleanup propre à l'arrêt via gc.collect() manuel.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmaVcCrWp2QG7hkEDk15Cv
- Réduction largeur sommet : pyr_w 70 → 49 px (-30%)
- Base plate : min_bottom_ratio=0.35 évite la pointe fine
- Remplissage inversé : i >= (N - fill_count) pour progression bas→haut
- Signal faible = segment bas s'allume seul ; signal fort = remplissage vers haut
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmaVcCrWp2QG7hkEDk15Cv
- Thread rssi_loop() : lecture API JSON locale (127.0.0.1:8103), parse flux video rx pour extraire RSSI moyen des antennes
- Fonction draw_rssi_pyramid() : pyramide inversée (base large en haut, pointe en bas), 10 segments, remplissage proportionnel au RSSI
- Code couleur : vert (RSSI > -60 dBm), cyan (-60 à -70 dBm), rouge (< -70 dBm)
- Positionnement : haut à droite du frame
- Reconnexion automatique en cas de redémarrage wfb-server-gs
- Intégration dans capture_loop après draw_overlay()
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmaVcCrWp2QG7hkEDk15Cv
Le socket AF_INET seul ne recevait pas les stats Pi Zero envoyées en IPv6
via Mycelium. Passage en AF_INET6 + IPV6_V6ONLY=0 pour accepter les deux
familles sur un seul socket (Freebox IPv4 en dev, Mycelium IPv6 en prod).
L'ancienne sonde abandonnait définitivement après 15s et restait bloquée
en mode synthétique même quand le flux réel arrivait juste après (latence
Mycelium variable, attente du premier keyframe H.264). Remplacé par un
thread de fond (gstreamer_watcher) qui retente en continu et bascule la
source active dès qu'une frame valide est lue, y compris après une perte
de flux en cours de route. Démarrage MJPEG immédiat, plus d'attente bloquante.
Le décodeur H.264 a besoin du temps d'attendre le premier keyframe/SPS-PPS,
surtout via Mycelium où la latence est plus élevée qu'en local. 4s faisait
parfois basculer en mode synthétique alors que le flux réel arrivait juste après.
Le flux RTP arrive maintenant via Mycelium en IPv6, mais udpsrc bindé
sur "::" seul ne reçoit pas les paquets IPv4 (pas de dual-stack sur ce
Jetson, IPV6_V6ONLY actif). Fusion de deux udpsrc (0.0.0.0 + ::) via
funnel pour accepter Freebox (dev, IPv4) et Mycelium (prod, IPv6).
La tâche drain_pymavlink() séparée ne tournait jamais car input()
bloque l'event loop. Drain et lecture du HEARTBEAT directement dans
monitor_telemetry() et en fallback dans l'action status pour garantir
la lecture du custom_mode ArduCopter.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
run_in_executor + blocking=False retournait None dans un thread
séparé sans rien peupler dans mav.messages. Drain direct dans
la boucle asyncio avec while recv_match() pour vider tout le
buffer à chaque tick de 50ms.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
recv_match(blocking=False) ponctuel ratait les heartbeats car aucun
appel continu ne drainait le buffer pymavlink. Ajout d'une tâche
drain_pymavlink() qui appelle recv_match() toutes les 50ms pour
remplir mav.messages, et lecture du mode via mav.messages.get()
qui retourne le dernier heartbeat reçu.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MAVSDK flight_mode() retourne UNKNOWN pour les modes ArduCopter non
mappés dans son enum PX4. Lecture directe du champ custom_mode depuis
les HEARTBEAT pymavlink avec dictionnaire de mapping ArduCopter complet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
En udpout, mavp2p (udps) ignore le client tant qu'aucun paquet
n'a été émis depuis lui. L'envoi d'un heartbeat GCS dès la
connexion force mavp2p à mémoriser l'adresse de retour et à
relayer le flux H743 vers pymavlink.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mavp2p tourne en serveur (udps) sur 14551 et 14552.
Les anciens bind serveur (udp://:14551, udpin:0.0.0.0:14552)
causaient "Address already in use". Les deux connexions
passent maintenant en mode client vers 127.0.0.1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
temp → temp_c, cpu → cpu_pct pour correspondre exactement
au format {ts, width, height, fps, encoder, bitrate_kbps,
temp_c, cpu_pct, throttled, uptime_s} émis par le Pi Zero.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reçoit JSON Pi Zero (width, height, fps, encoder, temp, cpu, throttled)
toutes les secondes via UDP 5602. Affiche un overlay cv2 semi-transparent
(fond noir 55%) en haut à gauche de chaque frame MJPEG :
ligne 1 : "1280x720 | 15fps | hw | 56.2C"
ligne 2 : "CPU 42% | ok / THROTTLED"
Variable globale _pi_stats protégée par _stats_lock (threading.Lock).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Inférence yolov8n.pt sur chaque frame GStreamer
- Fallback frame noir numpy si Pi Zero absent (pas de download)
- Timeout 4s sur sonde GStreamer via thread dédié
- MJPEG annoté exposé sur port 5601
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Règle iptables ACCEPT udp dpt:5600 ajoutée et sauvegardée via netfilter-persistent.
sudoers mis à jour : nicoboy peut gérer iptables sans mot de passe.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sans ça, un ancien processus uvicorn reste en écoute sur 8765
entre deux lancements et répond à la place du nouveau.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 streams gRPC infinis en asyncio.gather() saturaient l'event
loop et empêchaient uvicorn d'accepter des connexions HTTP.
- monitor_telemetry : boucle while + async for...break + sleep(1)
au lieu de 3 streams continus en gather
- monitor_armed : await sleep(0) pour céder après chaque update
- ensure_future → create_task (API recommandée Python 3.7+)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sans description, Gemini mappait "position gps" sur position
au lieu de position_gps. Chaque action a maintenant un libellé
explicite, position_gps précise horodatage + fix + satellites.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- gps_fix et gps_sats ajoutés à telemetry_cache
- monitor_telemetry surveille aussi gps_info() en continu
- position_gps lit depuis le cache (comme battery/mode)
et ne risque plus de bloquer sur async for ... break
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- float(cmd.get("altitude") or 15) : gère altitude=null retourné
par Gemini pour les commandes sans altitude (land, rtl, status…)
- /command attrape les exceptions et retourne JSON 500 avec le
message d'erreur, évite le "Erreur réseau" opaque dans la page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ajout instruction explicite : message toujours en français
- Nouveau champ delta dans le schéma JSON : "monte de 5m" → delta=5,
"descend de 3m" → delta=-3, null pour altitude absolue
- takeoff et altitude calculent target_alt = current_alt + delta
quand delta est présent, sinon utilisent altitude absolue
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- altitude : change la hauteur en vol via goto_location,
ou décollage depuis le sol si alt < 2m
- position_gps : position complète avec horodatage HH:MM:SS,
fix GPS et nombre de satellites via telemetry.gps_info()
- SYSTEM_PROMPT et message d'accueil mis à jour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- init_pymavlink() passe en run_in_executor : ne bloque plus
l'event loop asyncio pendant le démarrage de uvicorn
- Ajout monitor_telemetry() : batterie et mode mis à jour en
continu en cache (même logique que monitor_armed)
- /status et action status lisent telemetry_cache directement,
plus aucun async for bloquant dans les route handlers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- wireclaw_core : expose drone comme global, extrait init()
depuis main() pour permettre l'import par web_server.py ;
corrige aussi le bug drone_armed manquant global dans init
- web_server.py : FastAPI avec process(text) générique
(interpret_command → execute_command), routes GET / POST /command
GET /status, page HTML sobre avec statut temps réel (2s)
et historique des commandes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ajout de eeprom.bin, logs/, terrain/, mav.tlog.raw générés
automatiquement par ArduPilot/MAVProxy à chaque session.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Quand Gemini mappe "monte à Xm" sur takeoff et que le drone est
déjà en vol, on recalcule l'altitude absolue MSL et on utilise
goto_location pour atteindre la nouvelle altitude à position fixe.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
drone_armed reste True après une session précédente même si le drone
est au sol. Le guard utilise désormais current_pos["alt"] < 2.0
comme goto et orbit, avec un refresh_position préalable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>