Programmes pour exploiter les différents montages
On présente ici trois solutions pour exploiter le module Geiger avec la carte Arduino :
Comptages des désintégrations pendant une fenêtre de temps définie et affichage dans le moniteur série.
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.
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.
LiquidCrystal_I2C lcd(0x27, 20, 4); // Adapter l'adresse et nb colonnes lignes à l'écran
int Coups, Coups_avant;
int dt; // Durée de la fenêtre de mesure en s
int pinEntree = 3;
long Tinit;
void Detection() { // Routine executée lors de l'interruption (ajoute 1 coup)
Coups++;
}
void setup() {
Serial.begin(9600); // Initialisation du moniteur série
lcd.init(); //Initialisation de l'écran LCD
lcd.backlight(); //Allumage du rétroéclairage
lcd.setCursor(0, 0);
lcd.print("Comptage commence");
dt = 10; // Modifier pour ajouster la durée de la fenêtre de comptage (en s)
Coups = 0;
Coups_avant = 0;
pinMode(pinEntree, INPUT);
attachInterrupt(digitalPinToInterrupt(pinEntree), Detection, FALLING); // Déclenchement de l'interruption, Port 3 , front descendant
Tinit = millis();
}
void loop() {
delay(dt * 1000);
int Res = Coups - Coups_avant;
Coups_avant = Coups;
// Affichage d'infos sur l'écran LCD si écran connecté (pas obligatoire)lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Fenetre (s) : ");
lcd.print(dt);
lcd.setCursor(0, 2);
lcd.print("Coups : ");
lcd.print(Res);
//-------------------------- // Affichage d'infos sur le moniteur série (ne pas afficher le moniteur série pour récupération)Serial.print("nombre impulsion : ");
Serial.print(Res); // Affichage du dernier comptage
Serial.print(" ");
Serial.println((millis() - Tinit) / 1000);
}
Méthode : Utilisation
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.
Remarque : Utilisation 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
Detectionqui 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.
Méthode : Utilisation
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
#!/usr/bin/env python3# -*- coding: utf-8 -*-"""Created on Wed Feb 21 10:29:08 2024@author: Jonas Forlot Modifications Compteur Geiger 2024 Cédric Vanden Driessche puis C. BellessortDernière modif : détection du port Arduino sur nom du périphérique plutôt que sur le nom du portTracé du diagramme en "temps réel"Ajout des données statistiques sur le graphTracé de la gaussienne de même moyenne et de même écart-typeVERSION CORRIGÉE : Compatible tout environnement Python (Spyder, Edupython, etc.)Meilleure gestion de la fenêtre graphique utilisation de fig, ax = plt.subplot()Rafraichissement de l'affichage sans générer de nouvelle figure"""# Importation des modulesimport serial
import serial.tools.list_ports # pour la communication avec le port série
import time # gestion du temps
import matplotlib.pyplot as plt # pour le tracé de graphe
from math import e, pi
from statistics import mean, stdev, pstdev
# Activation du mode interactif de matplotlibplt.ion()
# initialisation des listesliste_temps_mesure = [] # liste pour stocker le temps"brut"
liste_temps = [] # liste pour stocker les valeurs de temps en partant de t=0
liste_impulsion = [] # liste pour stocker les impulsions
liste_duree = []
Duree_acquisition = int(input(
"Entrer la durée totale de l'acquisition en secondes : "))t_fenetre = int(
input("Entrer la durée de la fenêtre de mesure en secondes : "))
cps_total = 0 # Nombre total de détections
Ntotal = int(Duree_acquisition / t_fenetre) # Nombre d'échantillons
N = 0
def recup_port_Arduino():
"""Détecte et ouvre la connexion série avec l'Arduino Uno"""ports = list(serial.tools.list_ports.comports())
for p in ports:
print(p.description)
if "Arduino Uno" in p.description: # On cherche dans la description le nom de la carte "Arduino Uno"
mData = serial.Serial(p.device, 9600)
print(f"Arduino connecté sur {mData.name}")
print(f"Port ouvert : {mData.is_open}")
return mData
# Si aucun Arduino Uno n'est trouvéprint("ATTENTION : Aucun Arduino Uno détecté !")
print("Ports disponibles :")
for p in ports:
print(f" - {p.device} : {p.description}")
raise Exception("Arduino Uno non trouvé")
def gauss(m, s, c):
"""Calcule la valeur de la fonction gaussienne"""return 1 / (s * (2 * pi)**0.5) * e**(-0.5 * ((c - m) / s)**2)
# Configuration initialeTitre = input("Nom de l'échantillon testé (titre du graphique) : ")
Titre = Titre + " - Coups par fenêtre de " + str(t_fenetre) + " s"
t0 = time.time()
Data = recup_port_Arduino()
# Création de la figure et des axes UNE SEULE FOISfig, ax = plt.subplots(figsize=(10, 6))
# Boucle d'acquisitionwhile N < Ntotal:
ligne1 = Data.readline()
liste_données = ligne1.strip().split()
if len(liste_données) != 0:
liste_impulsion.append(int(liste_données[3].decode()))
liste_temps.append(int(time.time()-t0))
cps_total = cps_total + liste_impulsion[-1]
N = N + 1
m = min(liste_impulsion)
M = max(liste_impulsion)
print(f"t={liste_temps[-1]} Total={cps_total} Dernier={liste_impulsion[-1]}")
# Effacer seulement le contenu des axes (pas toute la figure)ax.clear()
# Tracer l'histogrammeax.hist(liste_impulsion, bins=[i + 0.5 for i in range(m-1, M+1)],
density=False, rwidth=0.8, color="blue", edgecolor='black', alpha=0.7)
# Calcul et affichage des statistiques si on a au moins 2 mesuresif len(liste_impulsion) > 1:
cps_moy = mean(liste_impulsion)
cps_ecartype = stdev(liste_impulsion)
cps_pecartype = pstdev(liste_impulsion)
# Tracer la courbe gaussienne si l'écart-type n'est pas nulif cps_ecartype != 0:
x = [m + i * (M - m) / 500 for i in range(501)]
y = [N * gauss(cps_moy, cps_pecartype, val) for val in x]
ax.plot(x, y, color="red", linewidth=2, label="Loi normale")
ax.legend()
# Afficher les statistiques en haut à droitestats_text = (
f"$cps_{{total}}$ = {cps_total:.0f}\n"
f"$N$ = {N:.0f}\n"
f"$\\overline{{cps}}$ = {cps_moy:.1f}\n"
f"$\\sigma_{{n-1}}$ = {cps_ecartype:.3e}\n"
f"$\\sigma_{{n}}$ = {cps_pecartype:.3e}"
)
ax.text(0.98, 0.90, stats_text, transform=ax.transAxes,
fontsize=11, verticalalignment='top', horizontalalignment='right',
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
# Mise en forme du graphiqueax.set_title(Titre, fontsize=12, fontweight='bold')
ax.set_xlabel('Coups', fontsize=11)
ax.set_ylabel('Effectif', fontsize=11)
ax.grid(True, alpha=0.3)
# Rafraîchir l'affichagefig.canvas.draw()
fig.canvas.flush_events()
plt.pause(0.001) # Très courte pause pour permettre la mise à jour
# Fermeture propreData.close() # pour arrêter la lecture des données série
print("\nAcquisition terminée !")
print(f"Total de coups enregistrés : {cps_total}")
# Désactiver le mode interactif et afficher le graphique finalplt.ioff()
plt.show()
# Option : Sauvegarde des données dans un fichier txtsauvegarder = input("\nVoulez-vous sauvegarder les données ? (o/n) : ")
if sauvegarder.lower() == 'o':
nom_fichier = input("Nom du fichier (sans extension) : ")
lines = ['N\timpulsion\ttemps\n'] # première ligne du fichier txt
for i in range(len(liste_impulsion)):
line = f"{i+1}\t{liste_impulsion[i]}\t{liste_temps[i]}\n"
lines.append(line)
with open(f'{nom_fichier}.txt', 'w', encoding='utf-8') as fichier:
fichier.writelines(lines)
print(f"Données sauvegardées dans {nom_fichier}.txt")
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 :
la bibliothèque d'instructions DF-Robot pour le module Geiger ;
le programme original permettant de tester le bon fonctionnement via le moniteur série ;
le programme affichant les données sur un écran LCD. (À adapter à votre modèle d’écran)

