- Remplace le continuum exponentiel par un modèle réaliste CsI(Tl) dans l'entraînement (bosse asymétrique ~110 keV + queue Compton) - Ajoute l'injection de background mesuré (70% mesuré / 30% synthétique) via --measured_background et MEASURED_BACKGROUND_PATH - Ajoute l'endpoint /api/background/continuum et le toggle "Continuum CsI" sur le dashboard background - Exclut le canal 1023 (overflow bin) de l'affichage web (NUM_CHANNELS=1023) - Corrige le lissage Gaussien du background (normalisation locale aux bords) - Met à jour README.md, CLAUDE.md, TUTORIEL.md, TOTO.md, vega_ml/README.md Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
279 lines
14 KiB
Markdown
279 lines
14 KiB
Markdown
# Radiacode 103 — Identification automatique d'isotopes
|
|
|
|
Pipeline Docker complet pour la capture, l'analyse et l'identification automatique d'isotopes radioactifs avec un spectrometre gamma Radiacode 103.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Radiacode 103 (USB)
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Conteneur detect (Python 3.11 + PyTorch CPU) │
|
|
│ │
|
|
│ capture_background.py ──> background_24h.npy (24h) │
|
|
│ radiacode_monitor.py ──> rapport JSON quotidien │
|
|
│ │ │
|
|
│ ├── Echantillonnage chaque 60s │
|
|
│ ├── Soustraction du background │
|
|
│ ├── Inference VegaModel (34.5M params, 82 iso) │
|
|
│ └── Rapport quotidien a 00h00 │
|
|
│ │
|
|
│ Modele: vega_best.pt (entraite sur RTX 5060 Ti) │
|
|
└─────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Conteneur web (FastAPI + Chart.js) │
|
|
│ │
|
|
│ Dashboard :8080 — Spectre en temps reel, │
|
|
│ background, CPS, historique, isotopes detectes │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Demarrage rapide
|
|
|
|
```bash
|
|
# 1. Builder les images
|
|
docker compose build
|
|
|
|
# 2. Lancer l'entrainement (GPU, ~45 min)
|
|
docker compose run --rm train
|
|
|
|
# 3. Capturer le bruit de fond (24h, sans source radioactive)
|
|
docker compose run --rm -d --name radiacode-bg detect python capture_background.py
|
|
|
|
# 4. Lancer la detection continue + dashboard
|
|
docker compose up detect web
|
|
```
|
|
|
|
## Configuration
|
|
|
|
Variables d'environnement (dans `docker-compose.yml`) :
|
|
|
|
### Entrainement (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 s) |
|
|
| `MAX_DURATION` | `86400` | Duree max des spectres (24h en s) |
|
|
| `SEED` | `42` | Graine aleatoire |
|
|
| `MEASURED_BACKGROUND_PATH` | `/data/background_24h.npy` | Background mesure pour entrainement hybride |
|
|
|
|
### Detection (service `detect`)
|
|
|
|
| Variable | Defaut | Description |
|
|
|----------|--------|-------------|
|
|
| `MODEL_PATH` | `/models/vega_best.pt` | Chemin du modele PyTorch |
|
|
| `ISOTOPE_INDEX_PATH` | `/models/vega_isotope_index.txt` | Index des 82 isotopes |
|
|
| `BACKGROUND_PATH` | `/data/background_24h.npy` | Fichier background de reference |
|
|
| `THRESHOLD` | `0.5` | Seuil de probabilite pour la detection |
|
|
| `SAMPLE_INTERVAL` | `60` | Intervalle d'echantillonnage (s) |
|
|
| `REPORT_HOUR` | `0` | Heure du rapport quotidien |
|
|
| `MIN_LIVE_TIME` | `3600` | Live time minimum pour un rapport (s) |
|
|
| `VEGA_DEVICE` | `cpu` | Device PyTorch (`cpu` ou `cuda`) |
|
|
|
|
### Dashboard (service `web`)
|
|
|
|
| Variable | Defaut | Description |
|
|
|----------|--------|-------------|
|
|
| `ENERGY_CALIBRATION_OFFSET` | `0.33` | Calibration energetique (offset keV) |
|
|
| `ENERGY_CALIBRATION_SLOPE` | `2.97` | Calibration energetique (pente keV/canal) |
|
|
|
|
## Bruit Poissonnien et modele
|
|
|
|
### Physique du bruit
|
|
|
|
La detection gamma est un processus de comptage intrinsequement stochastique. Chaque canal du spectre suit une loi de Poisson : si un canal accumule en moyenne N comptages, l'ecart-type est sqrt(N). Le rapport signal/bruit s'ameliore donc en sqrt(N) :
|
|
|
|
- **6.5 CPS de background** sur 1024 canaux -> ~0.006 CPS/canal
|
|
- Apres 1h : ~22 comptages/canal, ecart-type ~4.7, SNR ~5
|
|
- Apres 24h : ~560 comptages/canal, ecart-type ~24, SNR ~23
|
|
- Apres 1 semaine : ~3900 comptages/canal, ecart-type ~62, SNR ~63
|
|
|
|
C'est pourquoi la capture de background dure **24h** : en dessous, les pics du background (K-40 a 1460 keV, Bi-214 a 609 keV, Pb-214 a 352 keV) sont noyes dans le bruit Poissonnien.
|
|
|
|
### Modele VegaModel
|
|
|
|
Le VegaModel est un CNN-FCNN multi-tache inspire de l'architecture Vega d'Open-RadiaCode-Android :
|
|
|
|
- **Entree** : spectre 1D de 1023 canaux (20-3000 keV), normalise par max
|
|
- **Sortie** : deux tetes
|
|
- **Classification** : 82 neurones avec sigmoid (presence/absence de chaque isotope)
|
|
- **Regression** : 82 neurones (activite estimee en Bq, normalisee a max_activity_bq=1000)
|
|
- **Architecture** :
|
|
- 3 blocs CNN (64, 128, 256 canaux) avec BatchNorm + LeakyReLU + MaxPool
|
|
- 2 couches FC (512, 256) avec Dropout(0.3)
|
|
- **34 493 156 parametres** au total
|
|
- **Fonction de perte** : VegaLoss = classification_weight * BCE + regression_weight * Huber (ponderee pour ne penaliser l'activite que sur les isotopes presents)
|
|
- **Entrainement** : 50 000 spectres synthetiques, 100 epochs, AMP (mixed precision), early stopping (patience=10)
|
|
|
|
### Background d'entrainement
|
|
|
|
Le background synthetique utilise un modele realiste calibre sur les mesures du Radiacode 103 :
|
|
|
|
- **Continuum CsI(Tl)** : Bosse asymetrique a ~110 keV (sigma_gauche=55 keV, sigma_droite=50 keV) + queue Compton exponentielle (0.45*exp(-E/240) + 0.04*exp(-E/700)) + plancher de bruit. Ce modele remplace l'ancien continuum exponentiel qui ne reproduisait pas la forme reelle du spectre CsI(Tl).
|
|
- **Pics environnementaux** : 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 activites aleatoires realistes
|
|
- **Entrainement hybride** : Si le fichier `background_24h.npy` est disponible, 70% du background est issu de la mesure reelle (mélange 70% mesuré / 30% synthétique) et 30% du modele synthetique, avec variation stochastique des pics par-dessus. Ceci ameliore la robustesse du modele face aux variations locales du background.
|
|
|
|
### Spectres synthetiques
|
|
|
|
Les donnees d'entrainement simulent la physique complete :
|
|
1. **Pics photoelectriques** : Gaussiennes avec FWHM dependant de l'energie (8.4% a 662 keV pour le CsI(Tl))
|
|
2. **Continuum Compton** : distribution de Klein-Nishina simplifiee sous chaque pic
|
|
3. **Bruit Poissonnien** : echantillonnage Poisson(N) pour chaque canal, simulant les fluctuations de comptage reelles
|
|
4. **Background environnemental** : Continuum CsI(Tl) realiste + pics de K-40, radon, thorium avec activites aleatoires
|
|
5. **Efficacite du detecteur** : modele phenomenologique qui decroit avec l'energie
|
|
6. **Durees de 12-24h** : suffisamment longues pour que le rapport signal/bruit soit comparable aux mesures reelles
|
|
|
|
### Soustraction du background a l'inference
|
|
|
|
Le modele est entraine avec du background synthetique inclus, mais a l'inference on soustrait le vrai background mesure :
|
|
|
|
```python
|
|
rate = cumulated_counts / cumulated_live_time # spectre brut en CPS
|
|
bg_rate = bg_counts / bg_live_time # background de reference en CPS
|
|
net_rate = np.clip(rate - bg_rate, 0, None) # spectre net (background soustrait)
|
|
normalized = net_rate / net_rate.max() # normalisation pour le modele
|
|
```
|
|
|
|
Cette approche hybride est optimale :
|
|
- Le modele apprend a ignorer les pics du background pendant l'entrainement
|
|
- La soustraction reelle elimine les variations locales du background
|
|
- Resultat : meilleure sensibilite et moins de faux positifs
|
|
|
|
Le conteneur `train` execute deux phases :
|
|
|
|
1. **Generation des spectres synthetiques** : 50 000 spectres 1D (20k mono-isotope, 15k bi-isotope, 10k multi-isotope, 5k background seul) avec des durees de 12-24h
|
|
2. **Entrainement VegaModel** : CNN-FCNN multi-tache, 82 isotopes, 34.5M parametres
|
|
|
|
Resultats de l'entrainement sur RTX 5060 Ti :
|
|
- **Val accuracy** : 99.89%
|
|
- **AUC macro** : 0.995
|
|
- **Best val loss** : 0.0051
|
|
- **Duree** : ~45 min (100 epochs, 25s/epoch)
|
|
|
|
## Detection
|
|
|
|
Le moniteur `radiacode_monitor.py` :
|
|
- Echantillonne le spectre toutes les 60 secondes
|
|
- Cumule les comptages et le live time
|
|
- Soustrait le background de reference (si disponible)
|
|
- Execute l'inference VegaModel sur le spectre net
|
|
- Genere un rapport JSON quotidien a `REPORT_HOUR`
|
|
|
|
Le rapport contient :
|
|
- Live time et comptages totaux
|
|
- CPS moyen
|
|
- Isotopes detectes avec probabilite et activite estimee (Bq)
|
|
|
|
## Dashboard web
|
|
|
|
Le conteneur `web` expose un dashboard sur le port 8080 avec :
|
|
|
|
- **Onglet Spectre** : Spectre cumule en temps reel (lineaire ou log), soustraction du background, lignes d'energie des isotopes detectes, overlay du background de reference
|
|
- **Onglet Background** : Spectre du bruit de fond (live et 24h), modele theorique CsI(Tl), pics detectes, statistiques (duree, CPS, comptages)
|
|
- **Onglet CPS** : Evolution du comptage par seconde dans le temps, de 1h a 30 jours
|
|
- **Onglet Historique** : Liste des rapports quotidiens avec isotopes detectes
|
|
|
|
### Points techniques du dashboard
|
|
|
|
- Le canal 1024 (bin de debordement) est exclu de l'affichage — seuls les 1023 premiers canaux sont utilises (20-3036 keV)
|
|
- Le lissage du spectre (Gaussienne sigma=8 canaux) utilise une normalisation locale aux bords pour eviter les artefacts
|
|
|
|
## Capture du bruit de fond
|
|
|
|
Avant la detection, capturer le background pendant 24h sans source radioactive a proximite :
|
|
|
|
```bash
|
|
docker compose run --rm -d --name radiacode-bg \
|
|
-e TARGET_DURATION=86400 -e SAMPLE_INTERVAL=60 \
|
|
detect python capture_background.py
|
|
```
|
|
|
|
Suivre la progression :
|
|
```bash
|
|
docker logs -f radiacode-bg
|
|
cat data/background_snapshot.json
|
|
```
|
|
|
|
## Structure du projet
|
|
|
|
```
|
|
radiacode_103/
|
|
├── docker-compose.yml # Orchestration des conteneurs
|
|
├── CLAUDE.md # Guide pour Claude Code
|
|
├── TUTORIEL.md # Tutoriel detaille
|
|
├── TOTO.md # Suivi des taches
|
|
├── README.md
|
|
├── train/
|
|
│ ├── Dockerfile # PyTorch 2.7.0 + CUDA 12.8 (Blackwell)
|
|
│ ├── entrypoint.sh # Generation + entrainement
|
|
│ ├── requirements.txt
|
|
│ └── vega_ml/ # Code VegaModel
|
|
│ ├── synthetic_spectra/ # Generateur de spectres synthetiques
|
|
│ │ ├── config.py # Configurations detecteur (Radiacode 101-110)
|
|
│ │ ├── generator.py # Generateur principal (SpectrumConfig)
|
|
│ │ ├── physics/
|
|
│ │ │ └── spectrum_physics.py # Physique + background realiste CsI(Tl)
|
|
│ │ └── ground_truth/ # Base de donnees 82 isotopes
|
|
│ ├── training/vega/ # Modele, dataset, trainer
|
|
│ └── inference/ # Inference
|
|
├── detect/
|
|
│ ├── Dockerfile # Python 3.11-slim + radiacode + torch CPU
|
|
│ ├── requirements.txt
|
|
│ ├── radiacode_monitor.py # Moniteur principal
|
|
│ └── capture_background.py # Capture du bruit de fond 24h
|
|
├── web/
|
|
│ ├── Dockerfile # Python 3.11-slim + FastAPI
|
|
│ ├── requirements.txt
|
|
│ ├── app/
|
|
│ │ ├── main.py # FastAPI app + routes
|
|
│ │ ├── config.py # Config (canaux, calibration energetique)
|
|
│ │ ├── routers/
|
|
│ │ │ ├── status.py # /api/status
|
|
│ │ │ ├── spectrum.py # /api/spectrum/current, /api/spectrum/difference
|
|
│ │ │ ├── background.py # /api/background/*, background theorique
|
|
│ │ │ ├── cps.py # /api/cps/timeline
|
|
│ │ │ └── history.py # /api/history, /api/history/{date}
|
|
│ │ └── theoretical_bg.py # Modele theorique CsI(Tl) pour le dashboard
|
|
│ └── static/
|
|
│ ├── index.html # Dashboard SPA
|
|
│ ├── css/style.css
|
|
│ └── js/ # app.js, spectrum.js, background.js, cps.js, history.js
|
|
├── data/
|
|
│ ├── synthetic/spectra/ # 50 000 spectres synthetiques (~4.2 Go)
|
|
│ └── background_snapshot.json
|
|
├── models/
|
|
│ ├── vega_best.pt # Meilleur modele (395 Mo)
|
|
│ ├── vega_final.pt # Modele final
|
|
│ ├── vega_history.json # Metriques d'entrainement
|
|
│ └── vega_isotope_index.txt # 82 isotopes
|
|
└── logs/ # Rapports quotidiens JSON
|
|
```
|
|
|
|
## Materiel
|
|
|
|
| Composant | Modele |
|
|
|-----------|--------|
|
|
| Spectrometre | Radiacode 103 (CsI(Tl), 1024 canaux, FWHM 8.4% @662 keV) |
|
|
| GPU entrainement | NVIDIA RTX 5060 Ti 16 Go (Blackwell, sm_120) |
|
|
| GPU secondaire | NVIDIA RTX 4060 Ti 16 Go (Ada Lovelace) |
|
|
| Production (futur) | Raspberry Pi 4 8 Go |
|
|
|
|
## 82 isotopes detectables
|
|
|
|
Ac-227, Ac-228, Ag-110m, Am-241, Au-198, Ba-133, Ba-137m, Be-7, Bi-210, Bi-211, Bi-212, Bi-214, C-14, Cd-109, Ce-139, Ce-144, Co-57, Co-58, Co-60, Cr-51, Cs-134, Cs-137, Eu-152, Eu-154, F-18, Fe-59, Ga-67, Hf-181, Hg-203, I-123, I-129, I-131, In-111, Ir-192, K-40, Lu-177, Mn-54, Na-22, Pa-231, Pa-234m, Pb-210, Pb-211, Pb-212, Pb-214, Po-210, Po-212, Po-214, Po-216, Po-218, Pr-144, Ra-223, Ra-224, Ra-226, Ra-228, Rh-106, Rn-220, Rn-222, Ru-106, Sb-125, Sc-46, Se-75, Sm-153, Sn-113, Sr-85, Sr-90, Ta-182, Tc-99m, Th-228, Th-230, Th-231, Th-232, Th-234, Tl-201, Tl-207, Tl-208, U-234, U-235, U-238, Xe-133, Y-88, Y-90, Zn-65
|
|
|
|
## Passage en production (Raspberry Pi 4)
|
|
|
|
1. Copier `models/vega_best.pt` et `models/vega_isotope_index.txt` sur le Pi 4
|
|
2. Capturer le background sur le Pi 4 (emplacement final)
|
|
3. Le meme `detect/Dockerfile` fonctionne sur ARM64
|
|
4. Adapter `docker-compose.yml` : retirer `deploy.resources.reservations.devices`
|
|
5. `VEGA_DEVICE=cpu` (inference CPU sur Pi 4, ~1s par spectre) |