Files
wireclaw/CLAUDE.md

129 lines
7.7 KiB
Markdown

# WireClaw — Drone d'Inspection Thermique Autonome
## Contexte du projet
WireClaw est un système de pilotage de drone en langage naturel.
L'opérateur donne des instructions en français (texte ou voix),
un LLM les interprète et génère des commandes MAVLink vers ArduPilot.
## Architecture
- **Jetson Orin Nano 8Go** : station sol, héberge WireClaw + SITL
- **ESP32 DevKitV4** : relais WiFi↔UART (firmware PlatformIO sur Windows)
- **Pi Zero 2W** : IP 192.168.1.132 — flux vidéo H.264 UDP → Jetson (câblé, arrêté pour l'instant)
- **Matek H743** : à venir — contrôleur de vol ArduPilot réel
## Renommage du dossier (20 juillet 2026)
Ce dossier s'appelait `~/wireclaw`, renommé en `~/wireclaw-jetson` pour clarté : le repo `wireclaw-pizero` (config du drone) est maintenant aussi cloné localement sur le Jetson (voir ci-dessous), donc les deux noms de dossier doivent être distincts.
**Le dépôt distant Gitea reste `wireclaw.git`** (inchangé) — seul le nom du dossier local sur le Jetson a changé. Ne pas confondre les deux : `git remote -v` continuera d'afficher `wireclaw.git`, c'est normal.
Services systemd mis à jour en conséquence (`WorkingDirectory`/`ExecStart` pointent vers `/home/nicoboy/wireclaw-jetson`) :
- `wireclaw.service` (tests SITL ArduPilot, carte MAV+GPS)
- `receive-cam.service` (réception vidéo/YOLO/MJPEG)
## Organisation des deux repos sur le Jetson
Depuis le 20 juillet 2026, les deux dépôts du projet sont clonés localement sur le Jetson, pour permettre au script `switch_band.sh` (voir ci-dessous) de lire les deux profils de config radio sans dépendre du réseau vers un PC distant :
- `~/wireclaw-jetson/` — ce repo, code Jetson (réception vidéo, config radio ground station)
- `~/wireclaw-pizero/` — code Pi Zero (émission caméra, config radio drone). **Ce repo reste normalement édité depuis le PC habituel** — le clone Jetson sert uniquement de source pour le déploiement des profils radio, pas de zone d'édition principale. Toujours faire `git pull` avant toute édition, où que ce soit, pour éviter une divergence entre les deux clones.
## Profils radio versionnés (5GHz / 2.4GHz)
Deux profils complets et testés existent dans `config/` de chaque repo :
- `wifibroadcast_gs_5ghz.cfg` / `wifibroadcast_drone_5ghz.cfg` — canal 149, bande opérationnelle par défaut du projet
- `wifibroadcast_gs_24ghz.cfg` / `wifibroadcast_drone_24ghz.cfg` — canal 6, **usage diagnostic/comparatif uniquement** : confirmé sujet à interférence domestique sévère (freeze disparaît en mode auto Freebox, revient sur canal chevauchant — cf 20260720_wireclaw_avancement_v17.md §6)
Bascule via `scripts/switch_band.sh 5ghz` ou `scripts/switch_band.sh 2.4ghz` (voir le script pour le détail — copie directe des profils, pas de patch, backup unique `.bak_last` écrasé à chaque bascule).
## Stack logicielle
- Python 3.10, asyncio
- MAVSDK Python (udp://:14551)
- pymavlink (udpin:0.0.0.0:14552)
- Gemini 2.0 Flash API (clé dans .env, jamais en dur)
- MAVProxy en daemon (ports TCP:5760, UDP:14550/14551/14552)
- ArduPilot SITL (binaire ARM64 ~/ardupilot/build/sitl/bin/arducopter)
- PyTorch 2.8.0 + CUDA 12.6 (wheel Jetson : pypi.jetson-ai-lab.io/jp6/cu126, GPU Orin)
- ultralytics 8.4.62 (YOLOv8), numpy 1.26.4 (≥2 incompatible avec torch 2.8), cv2 4.5.4 système
## Fichiers principaux
- `wireclaw_core.py` : cerveau principal, boucle de commande
- `config.py` : configuration (lit .env via python-dotenv)
- `.env` : secrets — NE JAMAIS MODIFIER NI COMMITTER
- `start.sh` : lance ArduCopter + MAVProxy + WireClaw automatiquement
- `sitl_params.parm` : paramètres SITL (batterie désactivée)
- `receive_cam.py` : réception H.264 UDP port 5600 → inférence YOLOv8n → MJPEG annoté port 5601
## Ports réseau
- 5760 TCP : ArduCopter ↔ MAVProxy
- 14550 UDP : MAVProxy output standard + ESP32 source
- 14551 UDP : MAVSDK (WireClaw) + heartbeats ESP32
- 14552 UDP : pymavlink (WireClaw)
- 5600 UDP : flux H.264 RTP entrant depuis Pi Zero (caméra OV5647) — ouvert via `ufw allow 5600/udp` (2026-07-18, précaution). Fonctionne aujourd'hui via un relais loopback (`wfb-ng`/`wfb_rx` réinjecte le flux décodé sur `127.0.0.1:5600`), qui contourne de fait le filtrage sur l'interface externe : dépendance fragile, à surveiller si le pipeline de réception change (ex. flux direct depuis `gs-wfb` sans relais loopback)
- 5601 TCP : MJPEG annoté YOLOv8 exposé par receive_cam.py — ouvert via `ufw allow` (règle `5601/tcp`)
- 5602 UDP : sideband stats Pi Zero (résolution, fps, temp, CPU) vers receive_cam.py, via tunnel WFB-NG (`10.5.0.2``10.5.0.1` sur l'interface `gs-wfb`) — ouvert via `ufw allow 5602/udp` (2026-07-18). **Ce port était resté fermé par défaut (policy DROP d'ufw) alors que le code sideband existait depuis un moment** : l'overlay stats semblait "figé" ou absent car les paquets réels du Pi Zero étaient droppés par le pare-feu avant d'atteindre le socket applicatif, malgré un socket/threading fonctionnels par ailleurs (`tcpdump` voit toujours le trafic sur l'interface, quel que soit son sort côté netfilter — ne pas se fier à `tcpdump` seul pour valider qu'un flux atteint l'appli)
## Réseau local
- Jetson Ethernet : 192.168.1.84
- Jetson WiFi : 192.168.1.143 (utilisé pour ESP32)
- ESP32 WiFi : 192.168.1.122
## Règles importantes
- Ne jamais modifier `.env` ni `credentials.h` (secrets WiFi/API)
- Toujours tester via `~/wireclaw-jetson/start.sh` avant de commiter
- Les commandes MAVLink passent toutes par `execute_command()` dans wireclaw_core.py
- L'état armement est synchronisé en temps réel via `monitor_armed()` (tâche de fond)
- `MAV_CMD_DO_ORBIT` (commande 34) non supportée en SITL → fallback waypoints circulaires 16 points
- La batterie SITL est désactivée (BATT_MONITOR=0) pour éviter les failsafes en simulation
## Commandes de session
```bash
# Démarrer tout
~/wireclaw-jetson/start.sh
# Vérifier SITL actif
ps aux | grep -E "arducopter|mavproxy" | grep -v grep
# Vérifier WiFi Jetson actif
nmcli dev status | grep wifi
# Tuer Hermes si actif (occupe port 14550)
pkill -f hermes
# Lancer la réception caméra Pi Zero (quand Pi Zero actif)
python3 ~/wireclaw-jetson/receive_cam.py
# Vérifier MJPEG stream caméra
curl -s --max-time 2 http://localhost:5601/ | head -1
# Pipeline GStreamer brute (debug)
gst-launch-1.0 udpsrc port=5600 ! application/x-rtp,payload=96 ! rtph264depay ! avdec_h264 ! videoconvert ! fakesink
# Git commit + push
git add . && git commit -m "message" && git push origin master
```
## Commandes WireClaw validées
- `takeoff Xm` → décollage altitude X mètres
- `land` / `atterris` → atterrissage
- `rtl` / `rentre` → retour à la base
- `hover` → maintien de position
- `goto` → navigation vers coordonnées (calcul depuis description naturelle)
- `status` → position, altitude, batterie, mode, armement
- `orbit` → orbite native (fallback waypoints en SITL)
## Prochaines étapes
1. ~~Réception Pi Zero 2W + caméra OV5647~~ ✅ Pi Zero IP 192.168.1.132, reçu et testé
2. Câblage UART ESP32 ↔ Pi Zero (GPIO17→GPIO15, GPIO16←GPIO14, GND)
3. ~~Préparation réception flux H.264 UDP Jetson~~ ✅ receive_cam.py opérationnel, MJPEG 5601 accessible
4. ~~YOLOv8 intégré dans receive_cam.py~~ ✅ yolov8n.pt, CUDA Orin, mode synthétique actif en attendant Pi Zero
5. **Prochaine étape : Pi Zero émet GStreamer UDP H.264 vers 192.168.1.84:5600**
6. Intégrer MJPEG 5601 dans l'interface web (flux vidéo + commandes)
7. Matek H743 + ArduPilot réel (remplace SITL)
8. SkyDroid C12 (caméra thermique + gimbal 3 axes, 117g)
9. Whisper local (transcription vocale)
## Gitea
- WireClaw Python : git.syoul.fr/nicoboy/wireclaw
- Firmware ESP32 : git.syoul.fr/nicoboy/wireclaw-esp32