# Tutoriel : Radiacode 103 — Du debut a la detection d'isotopes Ce tutoriel explique chaque etape du pipeline, pourquoi les durees sont ce qu'elles sont, et comment interpreter les resultats. --- ## 1. Pre-requis materiels | Element | Detail | |---------|--------| | Radiacode 103 | Spectrometre gamma CsI(Tl), 1024 canaux, USB | | Machine GPU | NVIDIA avec drivers + nvidia-container-toolkit | | Docker | Version 20.10+ avec Docker Compose V2 (`docker compose`) | | Cable USB | Pour connecter le Radiacode a la machine | Verifier que le Radiacode est bien detecte : ```bash lsusb | grep -i radiacode # Doit afficher : ID 0483:f123 RadiaCode RadiaCode-103 ``` Verifier le GPU : ```bash nvidia-smi # Doit afficher votre carte NVIDIA ``` --- ## 2. Builder les images Docker (~5 min) ```bash docker compose build ``` Deux images sont construites : - `radiacode_103-train` : PyTorch 2.7.0 + CUDA 12.8 (pour l'entrainement sur GPU) - `radiacode_103-detect` : Python 3.11 + radiacode + PyTorch CPU (pour la detection) **Pourquoi PyTorch 2.7.0 ?** Les cartes RTX 50-series (Blackwell, compute capability sm_120) ne sont pas supportees par PyTorch < 2.7. Si vous avez une carte plus ancienne (RTX 30/40-series), PyTorch 2.4.0 suffit. --- ## 3. Entrainer le modele (~45 min sur RTX 5060 Ti) ```bash docker compose run --rm train ``` Cette commande lance deux phases automatiquement : ### Phase 1 : Generation des spectres synthetiques (~10 min) Le generateur cree 50 000 spectres 1D simulant la physique reelle : | Type | Nombre | Description | |------|--------|-------------| | Mono-isotope | 20 000 | Un seul isotope + background | | Bi-isotope | 15 000 | Deux isotopes + background | | Multi-isotope | 10 000 | 3-5 isotopes + background | | Background seul | 5 000 | Background environnemental uniquement | **Pourquoi des durees de 12-24h ?** Les spectres synthetiques simulent des acquisitions de 12 a 24 heures (43200-86400 secondes). C'est volontaire : 1. **Bruit Poissonnien** : La statistique de comptage suit une loi de Poisson. Pour un canal avec N comptages, l'ecart-type est sqrt(N). Le rapport signal/bruit s'ameliore en sqrt(N), donc en sqrt(duree). - A 6.5 CPS/canal moyen et 1024 canaux, apres 1h on a ~23 comptages/canal (SNR ~5) - Apres 24h on a ~560 comptages/canal (SNR ~23) - Apres 1 semaine ~3900 comptages/canal (SNR ~63) 2. **Conditions reelles** : Le detecteur sera utilise en continu pendant des jours. Le modele doit voir des spectres avec le meme niveau de bruit que les mesures reelles. 3. **Sensibilite** : Les isotopes a faible activite (1-5 Bq) ne sont detectables qu'apres plusieurs heures d'accumulation. Le modele doit apprendre cette relation. **Ce que le generateur simule physiquement :** - **Pics photoelectriques** : Gaussiennes dont la largeur (FWHM) depend de l'energie. Pour le CsI(Tl) du Radiacode 103, FWHM = 8.4% a 662 keV, avec la relation FWHM(E) = FWHM_662 * sqrt(E/662). - **Continuum Compton** : Distribution de Klein-Nishina simplifiee sous chaque pic, simulant la diffusion des photons gamma. - **Bruit Poissonnien** : Chaque canal est echantillonne depuis une loi Poisson(lambda), ce qui reproduit exactement les fluctuations statistiques reelles. - **Background environnemental** : Continuum CsI(Tl) realiste (bosse asymetrique a ~110 keV + queue Compton exponentielle) + pics de K-40 (1460 keV), Pb-214 (295, 352 keV), Bi-214 (609, 1120, 1764 keV), Ac-228 (911 keV), Pb-212 (239 keV), Tl-208 (583, 2614 keV), avec des activites aleatoires realistes. - **Efficacite du detecteur** : Modele phenomenologique tenant compte de l'absorption basse energie et de la penetration haute energie du scintillateur CsI(Tl) de 1 cm3. **Entrainement hybride (optionnel mais recommande) :** Si vous avez capture un fichier `background_24h.npy`, le generateur peut l'utiliser pour rendre les spectres synthetiques plus realistes. Le background mesure est melange a 70% avec le modele synthetique (30%), et les pics isotopiques sont ajoutes avec variation aleatoire par-dessus. ```bash # Avec background mesure (recommande si disponible) docker compose run --rm train # Le fichier /data/background_24h.npy est automatiquement utilise # grace a MEASURED_BACKGROUND_PATH dans docker-compose.yml ``` ### Phase 2 : Entrainement du VegaModel (~35 min sur RTX 5060 Ti) Le modele VegaModel est un CNN-FCNN multi-tache : ``` Entree : spectre 1D (1023 canaux, 20-3000 keV) | |-- Bloc CNN 1 : Conv1d(1, 64, 7) -> BN -> LeakyReLU -> MaxPool |-- Bloc CNN 2 : Conv1d(64, 128, 5) -> BN -> LeakyReLU -> MaxPool |-- Bloc CNN 3 : Conv1d(128, 256, 3) -> BN -> LeakyReLU -> MaxPool | |-- Tete classification : FC(256->512->256->82) -> Sigmoid | 82 isotopes, probabilite de presence [0, 1] | +-- Tete regression : FC(256->512->256->82) Activite estimee en Bq pour chaque isotope ``` - **34 493 156 parametres** au total - **Fonction de perte** : BCE (classification) + Huber ponderee (regression sur isotopes presents uniquement) - **Mixed precision (AMP)** : Acceleration sur GPU via float16 - **Early stopping** : Patience de 10 epochs sans amelioration **Resultats typiques :** | Metrique | Valeur | |----------|--------| | Val accuracy | 99.89% | | AUC macro | 0.995 | | Precision | 99.8% | | Recall | 95.6% | | Exact match | 93.5% | | Best val loss | 0.0051 | **Fichiers generes dans `models/` :** | Fichier | Taille | Description | |---------|--------|-------------| | `vega_best.pt` | 395 Mo | Meilleur modele (checkpoint avec val loss minimale) | | `vega_final.pt` | 395 Mo | Modele final (epoch 100) | | `vega_isotope_index.txt` | 546 o | Liste des 82 isotopes detectables | | `vega_history.json` | 75 Ko | Metriques d'entrainement par epoch | > **Note** : Seul `vega_best.pt` et `vega_isotope_index.txt` sont necessaires pour la detection. Les checkpoints par epoch (`vega_epoch_*.pt`) peuvent etre supprimes pour gagner ~39 Go. --- ## 4. Capturer le bruit de fond (24h) Cette etape est **critique** et ne doit pas etre ignoree. Placez le detecteur dans un endroit sans source radioactive connue. ```bash docker compose run --rm -d --name radiacode-bg \ -e TARGET_DURATION=86400 -e SAMPLE_INTERVAL=60 \ detect python capture_background.py ``` ### Pourquoi 24h ? Le background environnemental n'est pas constant. Il varie selon : 1. **Le radon** : Concentration dans l'air qui varie avec la ventilation, la meteo, l'heure du jour (cycle diurne) 2. **Le thorium** : Present dans les materiaux de construction (beton, brique), relativement stable mais varie lentement 3. **Le potassium-40** : Partout dans la nature (aliments, corps humain), assez stable 4. **Les rayons cosmiques** : Varient avec l'altitude et l'activite solaire Un echantillonnage de 24h capture un cycle complet de ces variations, donnant un background de reference representative. **Pourquoi pas moins ?** | Duree | CPS/canal | SNR/canal | Pics visibles | |-------|-----------|-----------|---------------| | 1h | ~23 | ~5 | Aucun pic clair | | 4h | ~94 | ~10 | K-40 commence a emerger | | 12h | ~280 | ~17 | K-40, Bi-214 visibles | | **24h** | **~560** | **~24** | **Tous les pics de background nets** | | 48h | ~1120 | ~33 | Pics tres nets, variations capturees | A 24h, les principaux pics du background sont clairement identifiables : - **K-40 a 1460 keV** : Toujours present (~5-15 Bq dans l'environnement) - **Bi-214 a 609 keV** : Fille du radon - **Pb-214 a 295 et 352 keV** : Fille du radon - **Tl-208 a 2614 keV** : Fille du thorium ### Suivre la progression ```bash # Voir les logs docker logs -f radiacode-bg # Lire le snapshot JSON cat data/background_snapshot.json | python3 -m json.tool | head -20 ``` Le snapshot montre en temps reel les pics detectes, le CPS moyen et la duree ecoulee. ### Le script gere les deconnexions Si le Radiacode est debranche pendant la capture, le script se reconnecte automatiquement et reprend l'accumulation. Les donnees deja collectees ne sont pas perdues. ### A la fin (24h plus tard) Le fichier `data/background_24h.npy` est genere avec : ```python { "counts": np.array, # 1024 canaux, comptages cumules "duration": float, # live time en secondes "timestamp": float # timestamp Unix } ``` --- ## 5. Lancer la detection continue + dashboard ```bash docker compose up detect web ``` Le dashboard est accessible sur `http://localhost:8080` avec quatre onglets : - **Spectre** : Spectre cumule en temps reel, soustraction du background, lignes d'energie des isotopes detectes - **Background** : Spectre du bruit de fond (live et 24h), modele theorique CsI(Tl), pics detectes - **CPS** : Evolution du comptage par seconde dans le temps (1h a 30 jours) - **Historique** : Liste des rapports quotidiens avec isotopes detectes ### Comment ca marche Toutes les 60 secondes, le moniteur : ``` 1. Echantillonnage Radiacode.spectrum() -> 1024 canaux + duree cumulated_counts += counts cumulated_live_time += duration.total_seconds() Radiacode.spectrum_reset() 2. A 00h00 chaque jour : Rapport Si live_time > 1h : rate = cumulated_counts / cumulated_live_time bg_rate = bg_counts / bg_live_time net_rate = clip(rate - bg_rate, 0, None) normalized = net_rate / net_rate.max() logits, activities = model(normalized) probs = sigmoid(logits) Pour chaque isotope avec prob > 0.5 : rapport[name, prob%, activite_bq] Sauvegarder dans logs/report_YYYY-MM-DD.json Reset cumulateurs 3. Si detecteur debranche : Attendre 60s, retenter la connexion Les donnees cumulees sont conservees ``` ### Pourquoi soustraire le background ? Le modele est entraite avec du background synthetique realiste (continuum CsI(Tl) + pics environnementaux), mais le background reel varie selon l'emplacement. La soustraction reelle ameliore la detection : - **Sans soustraction** : Le modele voit K-40 a 1460 keV et peut le signaler comme isotope detecte, meme si c'est juste du background - **Avec soustraction** : Le pic de K-40 du background est elimine, seul un signal supplementaire est analyse - **Resultat** : Moins de faux positifs, meilleure sensibilite pour les isotopes faibles --- ## 6. Interpreter les rapports Chaque jour a `REPORT_HOUR` (00h00 par defaut), un rapport JSON est genere dans `logs/` : ```json { "date": "2026-05-20T00:00:00", "live_time_hours": 18.5, "total_counts": 434500, "cps_mean": 6.52, "background_subtracted": true, "isotopes_detected": [ { "isotope": "Cs-137", "probability": 0.97, "activity_bq": 12.3 }, { "isotope": "K-40", "probability": 0.85, "activity_bq": 8.1 } ] } ``` ### Champs du rapport | Champ | Description | |-------|-------------| | `live_time_hours` | Temps effectif d'acquisition (sans les trous de deconnexion) | | `total_counts` | Nombre total de coups cumules | | `cps_mean` | Comptage par seconde moyen | | `background_subtracted` | `true` si le background a ete soustrait | | `isotopes_detected` | Liste des isotopes avec probabilite > seuil (0.5 par defaut) | ### Interpretation des resultats - **Probabilite > 95%** : Detection certaine, l'isotope est present - **Probabilite 70-95%** : Detection probable, a confirmer avec plus de donnees - **Probabilite 50-70%** : Detection incertaine, probablement du bruit ou une confusion spectrale - **Probabilite < 50%** : Non detecte (en dessous du seuil) **Isotopes de background frequents** (normaux, pas une alerte) : - K-40 : Toujours present dans l'environnement (~5-15 Bq) - Bi-214 / Pb-214 : Filles du radon, varient selon la ventilation - Ac-228 / Pb-212 / Tl-208 : Chaine du thorium, presents dans les materiaux de construction **Isotopes d'interet** (a surveiller) : - Cs-137 : Marqueur de contamination (accident nucleaire, sources industrielles) - Co-60 : Source medicale/industrielle - I-131 : Marqueur medical (scintigraphie thyroidienne) - Am-241 : Source de calibration, detecteurs de fumee - Ir-192 : Source industrielle (radiographie gamma) --- ## 7. Tester avec une source connue Si vous avez une source radioactive connue (par ex. Cs-137) : 1. Placez la source pres du detecteur 2. Lancez le moniteur 3. Attendez au moins 1 heure d'acquisition (MIN_LIVE_TIME=3600) 4. Forcez un rapport manuel : ```bash docker compose exec detect python -c " from radiacode_monitor import RadiacodeMonitor m = RadiacodeMonitor() m.generate_report() " ``` Pour un Cs-137 (662 keV, ~10 uSv/h a 10 cm), le resultat attendu est : - Cs-137 detecte avec probabilite > 90% - Activite estimee approximative (la calibration exacte necessite un etalonnage) --- ## 8. Nettoyer les checkpoints (~39 Go a recuperer) Apres l'entrainement, seuls `vega_best.pt` et `vega_isotope_index.txt` sont necessaires : ```bash # Supprimer les checkpoints intermediaires rm models/vega_epoch_*.pt models/vega_final.pt # Verifier ls -lh models/ # vega_best.pt vega_history.json vega_isotope_index.txt ``` --- ## 9. Transfer vers Raspberry Pi 4 (production) Quand le pipeline fonctionne sur la machine GPU : 1. Copier les fichiers du modele : ```bash scp models/vega_best.pt models/vega_isotope_index.txt pi@raspberrypi:~/radiacode_103/models/ ``` 2. Capturer le background sur le Pi (emplacement final) : ```bash docker compose run --rm -d --name radiacode-bg \ -e TARGET_DURATION=86400 -e SAMPLE_INTERVAL=60 \ detect python capture_background.py ``` 3. Adapter `docker-compose.yml` sur le Pi : - Retirer `deploy.resources.reservations.devices` (pas de GPU) - Mettre `VEGA_DEVICE=cpu` - Le detect/Dockerfile fonctionne sur ARM64 (PyTorch a des wheels ARM) 4. Lancer : ```bash docker compose up detect ``` L'inference CPU sur Pi 4 prend environ 0.5-1 seconde par spectre, ce qui est suffisant pour un rapport quotidien. --- ## 10. Variables d'environnement de reference ### Entrainement (`docker-compose.yml` - service `train`) | Variable | Defaut | Description | |----------|--------|-------------| | `NUM_SAMPLES` | `50000` | Nombre de spectres synthetiques | | `EPOCHS` | `100` | Epochs max d'entrainement | | `BATCH_SIZE` | `64` | Taille de batch | | `LEARNING_RATE` | `0.001` | Taux d'apprentissage initial | | `DETECTOR` | `radiacode_103` | Config du detecteur | | `MIN_DURATION` | `43200` | Duree min des spectres (12h en secondes) | | `MAX_DURATION` | `86400` | Duree max des spectres (24h en secondes) | | `NVIDIA_VISIBLE_DEVICES` | `1` | GPU a utiliser (0 ou 1) | | `CUDA_VISIBLE_DEVICES` | `1` | GPU visible par CUDA | | `MEASURED_BACKGROUND_PATH` | `/data/background_24h.npy` | Background mesure pour entrainement hybride | ### Detection (`docker-compose.yml` - service `detect`) | Variable | Defaut | Description | |----------|--------|-------------| | `MODEL_PATH` | `/models/vega_best.pt` | Chemin du modele | | `ISOTOPE_INDEX_PATH` | `/models/vega_isotope_index.txt` | Index des isotopes | | `BACKGROUND_PATH` | `/data/background_24h.npy` | Fichier background | | `VEGA_DEVICE` | `cpu` | Device PyTorch | | `THRESHOLD` | `0.5` | Seuil de probabilite | | `SAMPLE_INTERVAL` | `60` | Intervalle d'echantillonnage (s) | | `REPORT_HOUR` | `0` | Heure du rapport quotidien | | `MIN_LIVE_TIME` | `3600` | Live time min pour rapport (s) | ### Dashboard (`docker-compose.yml` - service `web`) | Variable | Defaut | Description | |----------|--------|-------------| | `ENERGY_CALIBRATION_OFFSET` | `0.33` | Calibration energetique offset (keV) | | `ENERGY_CALIBRATION_SLOPE` | `2.97` | Calibration energetique pente (keV/canal) | ### Capture de background | Variable | Defaut | Description | |----------|--------|-------------| | `TARGET_DURATION` | `86400` | Duree de capture (24h en secondes) | | `SAMPLE_INTERVAL` | `60` | Intervalle entre echantillons (s) | | `BACKGROUND_PATH` | `/data/background_24h.npy` | Fichier de sortie |