Files
radiacode/TUTORIEL.md
Jacquin Antoine 27ef0727e8 Ajout du tutoriel detaille
Explication etape par etape du pipeline complet :
- Durees et raisons (24h background, 12-24h spectres synthetiques)
- Bruit Poissonnien et rapport signal/bruit
- Physique des spectres synthetiques (Compton, FWHM, efficacite)
- Architecture VegaModel et resultats d'entrainement
- Interpretation des rapports et isotopes de reference
- Guide de transfer vers Raspberry Pi 4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-19 12:32:56 +02:00

16 KiB

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 :

lsusb | grep -i radiacode
# Doit afficher : ID 0483:f123 RadiaCode RadiaCode-103

Verifier le GPU :

nvidia-smi
# Doit afficher votre carte NVIDIA

2. Builder les images Docker (~5 min)

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)

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 exponentiel + 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.

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 → ReLU → MaxPool
  ├── Bloc CNN 2 : Conv1d(64, 128, 5) → BN → ReLU → MaxPool
  ├── Bloc CNN 3 : Conv1d(128, 256, 3) → BN → ReLU → 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) + MSE 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.

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

# 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 :

{
    "counts": np.array,    # 1024 canaux, comptages cumules
    "duration": float,      # live time en secondes
    "timestamp": float      # timestamp Unix
}

5. Lancer la detection continue

docker compose up detect

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, 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

Debranchement et rebranchement

Le moniteur gere les deconnexions USB proprement :

  • Si le Radiacode est debranche, try_connect() echoue et retourne None
  • Le moniteur attend 60 secondes et retente
  • Les compteurs cumules ne sont pas reinitialises
  • Quand le detecteur est rebranche, l'accumulation reprend

Cela permet de prendre le detecteur avec soi pendant la journee sans perdre les donnees de la nuit.


6. Interpreter les rapports

Chaque jour a REPORT_HOUR (00h00 par defaut), un rapport JSON est genere dans logs/ :

{
  "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 :
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 :

# 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 :

    scp models/vega_best.pt models/vega_isotope_index.txt pi@raspberrypi:~/radiacode_103/models/
    
  2. Capturer le background sur le Pi (emplacement final) :

    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 :

    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

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)

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