Programmes pour exploiter les différents montages

On présente ici trois solutions pour exploiter le module Geiger avec la carte Arduino :

  1. Comptages des désintégrations pendant une fenêtre de temps définie et affichage dans le moniteur série.

  2. Comptage des désintégrations pendant une fenêtre de temps définie et lecture des données par un programme Python pour construction de l’histogramme des comptages.

  3. Utilisation d’une bibliothèque d’instructions et mesure de dose radioactive.

Comptage et lecture des données sur le moniteur série

Principe

Dans ce programme, on paramètre la durée de la fenêtre de comptage et on lit directement sur le moniteur série le nombre de coups comptés par le module Geiger.

Le programme proposé permet aussi d’afficher les mesures sur un écran LCD si la durée de la fenêtre de comptage est suffisamment longue.

1
#include <Wire.h>
2
#include <LiquidCrystal_I2C.h>
3
LiquidCrystal_I2C lcd(0x27, 20, 4);  // Adapter l'adresse et nb colonnes lignes à l'écran
4
int Coups, Coups_avant;
5
int dt;  // Durée de la fenêtre de mesure en s
6
int pinEntree = 3;
7
long Tinit;
8
9
void Detection() {  // Routine executée lors de l'interruption (ajoute 1 coup)
10
  Coups++;
11
}
12
13
void setup() {
14
  Serial.begin(9600);  // Initialisation du moniteur série
15
  lcd.init();          //Initialisation de l'écran LCD
16
  lcd.backlight();     //Allumage du rétroéclairage
17
  lcd.setCursor(0, 0);
18
  lcd.print("Comptage commence");
19
  dt = 10;  // Modifier pour ajouster la durée de la fenêtre de comptage (en s)
20
  Coups = 0;
21
  Coups_avant = 0;
22
  pinMode(pinEntree, INPUT);
23
  attachInterrupt(digitalPinToInterrupt(pinEntree), Detection, FALLING);  // Déclenchement de l'interruption, Port 3 , front descendant
24
  Tinit = millis();
25
}
26
27
void loop() {
28
  delay(dt * 1000);
29
  int Res = Coups - Coups_avant;
30
  Coups_avant = Coups;
31
  // Affichage d'infos sur l'écran LCD si écran connecté (pas obligatoire)
32
  lcd.clear();
33
  lcd.setCursor(0, 0);
34
  lcd.print("Fenetre (s) : ");
35
  lcd.print(dt);
36
  lcd.setCursor(0, 2);
37
  lcd.print("Coups : ");
38
  lcd.print(Res);
39
  //--------------------------
40
  // Affichage d'infos sur le moniteur série (ne pas afficher le moniteur série pour récupération)
41
  Serial.print("nombre impulsion : ");
42
  Serial.print(Res);  // Affichage du dernier comptage
43
  Serial.print("    ");
44
  Serial.println((millis() - Tinit) / 1000);
45
}

MéthodeUtilisation

Le seul paramétrage à effectuer ici est le réglage de la durée de la fenêtre de mesure en seconde à la ligne 19, en renseignant la valeur de la variable dt.

RemarqueUtilisation des interruptions

Ce programme utilise les interruptions pour compter les désintégrations.

Explication de la ligne 23 : attachInterrupt(digitalPinToInterrupt(pinEntree), Detection, FALLING);

  • Le module Geiger génère une brève impulsion descendante 5V -> 0V lors de la détection d’une particule ou d’un rayonnement.

  • La détection de cette impulsion descendante (FALLING), appelle la fonction Detection qui incrémente le nombre de coups.

Récupération des données avec un programme Python pour tracer les histogrammes

Le programme proposé ici a pour objectif le tracé de l’histogramme des comptages effectués. Il récupères les données envoyées par la carte Arduino sur le port série et trace au fur et à mesure l’histogramme des comptages en superposant la gaussienne de même moyenne et de même écart-type que la distribution des mesures.

Comptage de désintégrationsInformations[1]

MéthodeUtilisation

Il faut téléverser le programme de la partie précédente dans la carte Arduino après avoir renseigné la ligne 19. Une fois le téléversement effectué, Arduino IDE peut être fermé. Il faut en tout état de cause que le moniteur série soit fermé.

On exécute ensuite le programme ci-dessous qui se charge de la récupération des données après quelques questions :

  • Titre du graphique.

  • Durée de la fenêtre de mesure.

  • Durée totale de l’acquisition.

Programme

1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Wed Feb 21 10:29:08 2024
5
6
@author: Jonas Forlot Modifications Compteur Geiger 2024 Cédric Vanden Driessche puis C. Bellessort
7
Dernière modif : détection du port Arduino sur nom du périphérique plutôt que sur le nom du port
8
Tracé du diagramme en "temps réel"
9
Ajout des données statistiques sur le graph
10
Tracé de la gaussienne de même moyenne et de même écart-type
11
12
VERSION CORRIGÉE : Compatible tout environnement Python (Spyder, Edupython, etc.)
13
Meilleure gestion de la fenêtre graphique utilisation de fig, ax = plt.subplot()
14
Rafraichissement de l'affichage sans générer de nouvelle figure
15
"""
16
17
# Importation des modules
18
import serial
19
import serial.tools.list_ports   # pour la communication avec le port série
20
import time  # gestion du temps
21
import matplotlib.pyplot as plt  # pour le tracé de graphe
22
from math import e, pi
23
from statistics import mean, stdev, pstdev
24
25
# Activation du mode interactif de matplotlib
26
plt.ion()
27
28
# initialisation des listes
29
liste_temps_mesure = []  # liste pour stocker le temps"brut"
30
liste_temps = []  # liste pour stocker les valeurs de temps en partant de t=0
31
liste_impulsion = []  # liste pour stocker les impulsions
32
liste_duree = []
33
34
Duree_acquisition = int(input(
35
    "Entrer la durée totale de l'acquisition en secondes : "))
36
t_fenetre = int(
37
    input("Entrer la durée de la fenêtre de mesure en secondes : "))
38
cps_total = 0  # Nombre total de détections
39
Ntotal = int(Duree_acquisition / t_fenetre)  # Nombre d'échantillons
40
N = 0
41
42
43
def recup_port_Arduino():
44
    """Détecte et ouvre la connexion série avec l'Arduino Uno"""
45
    ports = list(serial.tools.list_ports.comports())
46
    for p in ports:
47
        print(p.description)
48
        if "Arduino Uno" in p.description:  # On cherche dans la description le nom de la carte "Arduino Uno"
49
            mData = serial.Serial(p.device, 9600)
50
            print(f"Arduino connecté sur {mData.name}")
51
            print(f"Port ouvert : {mData.is_open}")
52
            return mData
53
54
    # Si aucun Arduino Uno n'est trouvé
55
    print("ATTENTION : Aucun Arduino Uno détecté !")
56
    print("Ports disponibles :")
57
    for p in ports:
58
        print(f"  - {p.device} : {p.description}")
59
    raise Exception("Arduino Uno non trouvé")
60
61
62
def gauss(m, s, c):
63
    """Calcule la valeur de la fonction gaussienne"""
64
    return 1 / (s * (2 * pi)**0.5) * e**(-0.5 * ((c - m) / s)**2)
65
66
67
# Configuration initiale
68
Titre = input("Nom de l'échantillon testé (titre du graphique) : ")
69
Titre = Titre + " - Coups par fenêtre de " + str(t_fenetre) + " s"
70
t0 = time.time()
71
Data = recup_port_Arduino()
72
73
# Création de la figure et des axes UNE SEULE FOIS
74
fig, ax = plt.subplots(figsize=(10, 6))
75
76
# Boucle d'acquisition
77
while N < Ntotal:
78
    ligne1 = Data.readline()
79
    liste_données = ligne1.strip().split()
80
81
    if len(liste_données) != 0:
82
        liste_impulsion.append(int(liste_données[3].decode()))
83
        liste_temps.append(int(time.time()-t0))
84
        cps_total = cps_total + liste_impulsion[-1]
85
        N = N + 1
86
87
        m = min(liste_impulsion)
88
        M = max(liste_impulsion)
89
        print(
90
            f"t={liste_temps[-1]}  Total={cps_total}  Dernier={liste_impulsion[-1]}")
91
92
        # Effacer seulement le contenu des axes (pas toute la figure)
93
        ax.clear()
94
95
        # Tracer l'histogramme
96
        ax.hist(liste_impulsion, bins=[i + 0.5 for i in range(m-1, M+1)],
97
                density=False, rwidth=0.8, color="blue", edgecolor='black', alpha=0.7)
98
99
        # Calcul et affichage des statistiques si on a au moins 2 mesures
100
        if len(liste_impulsion) > 1:
101
            cps_moy = mean(liste_impulsion)
102
            cps_ecartype = stdev(liste_impulsion)
103
            cps_pecartype = pstdev(liste_impulsion)
104
105
            # Tracer la courbe gaussienne si l'écart-type n'est pas nul
106
            if cps_ecartype != 0:
107
                x = [m + i * (M - m) / 500 for i in range(501)]
108
                y = [N * gauss(cps_moy, cps_pecartype, val) for val in x]
109
                ax.plot(x, y, color="red", linewidth=2, label="Loi normale")
110
                ax.legend()
111
112
            # Afficher les statistiques en haut à droite
113
            stats_text = (
114
                f"$cps_{{total}}$ = {cps_total:.0f}\n"
115
                f"$N$ = {N:.0f}\n"
116
                f"$\\overline{{cps}}$ = {cps_moy:.1f}\n"
117
                f"$\\sigma_{{n-1}}$ = {cps_ecartype:.3e}\n"
118
                f"$\\sigma_{{n}}$ = {cps_pecartype:.3e}"
119
            )
120
            ax.text(0.98, 0.90, stats_text, transform=ax.transAxes,
121
                    fontsize=11, verticalalignment='top', horizontalalignment='right',
122
                    bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
123
124
        # Mise en forme du graphique
125
        ax.set_title(Titre, fontsize=12, fontweight='bold')
126
        ax.set_xlabel('Coups', fontsize=11)
127
        ax.set_ylabel('Effectif', fontsize=11)
128
        ax.grid(True, alpha=0.3)
129
130
        # Rafraîchir l'affichage
131
        fig.canvas.draw()
132
        fig.canvas.flush_events()
133
        plt.pause(0.001)  # Très courte pause pour permettre la mise à jour
134
135
# Fermeture propre
136
Data.close()  # pour arrêter la lecture des données série
137
print("\nAcquisition terminée !")
138
print(f"Total de coups enregistrés : {cps_total}")
139
140
# Désactiver le mode interactif et afficher le graphique final
141
plt.ioff()
142
plt.show()
143
144
# Option : Sauvegarde des données dans un fichier txt
145
sauvegarder = input("\nVoulez-vous sauvegarder les données ? (o/n) : ")
146
if sauvegarder.lower() == 'o':
147
    nom_fichier = input("Nom du fichier (sans extension) : ")
148
    lines = ['N\timpulsion\ttemps\n']  # première ligne du fichier txt
149
    for i in range(len(liste_impulsion)):
150
        line = f"{i+1}\t{liste_impulsion[i]}\t{liste_temps[i]}\n"
151
        lines.append(line)
152
153
    with open(f'{nom_fichier}.txt', 'w', encoding='utf-8') as fichier:
154
        fichier.writelines(lines)
155
    print(f"Données sauvegardées dans {nom_fichier}.txt")
156

Bibliothèque et programme permettant de mesurer une dose

La documentation en ligne du module Geiger permet de télécharger une bibliothèque d'instructions permettant d'afficher :

  • le nombre de coups par minute (CPM), en moyenne glissante ;

  • le débit de dose en µS/h, en moyenne glissante.

Le programme par défaut affiche ces données dans le moniteur série. On peut ajouter un écran LCD pour une lecture plus simple et un compteur autonome.

Vous pouvez télécharger ci-dessous :