Dominique a soulevé il y a quelques temps la question de la gestion d’un passage à niveau avec plusieurs voies, dont une en Y. Nous avons réfléchi depuis à généraliser le problème à des trajets multiples, parallèles, convergents ou divergents arrivants dans la zone du passage à niveau. Le problème consiste à limiter le nombre de capteurs, à déterminer le moment ou la zone est libre (ou inversement occupée) et en fonction de l’état de la zone d’activer des éléments (ouverture/fermeture de barrières, allumer ou éteindre des feux de route, etc.). Nous vous proposons donc dans cet article notre solution qui admet tout type de trajet traversant cette zone. Après une présentation du but du jeu et l’algorithme envisagé, nous faisons une rapide présentation d’un capteur optique (le TCRT5000L) et du capteur ponctuel basé sur les relais ILS, puis du capteur à effet Hall. Nous terminons par la présentation du programme ainsi qu’une méthode pour le configurer.
De plus, nous avons écrit l’essentiel des algorithmes dans une bibliothèque que vous pouvez inclure dans votre IDE Arduino. Cette bibliothèque est d’abord un exemple de "comment faire une bibliothèque" (voir aussi Monter une bibliothèque !), avant d’être une solution générale pour tous les passages à niveau !
Etude d’un passage à niveau multivoies
.
Par : ,
DIFFICULTÉ :★★☆
Le but du jeu
L’objectif est de contrôler un passage sensible, comme un passage à niveau, pouvant être parcouru par plusieurs trajets dans les deux directions. Chaque trajet emprunte une zone à l’intérieur du passage, chaque voie arrivant sur le passage possède un capteur. Ici nous avons par exemple une voie avec le capteur C1 et le capteur C4 qui emprunte la zone1 et une voie avec le capteur C2 et C5 qui emprunte la zone2, de même pour C3 et C6 avec la zone3. Le passage n’est considéré comme libre que si bien évidemment l’ensemble des zones le sont. Cela permettra d’ouvrir les barrières en toute sécurité. Dans les autres cas, la barrière devra être fermée.
Mais également avec des trajets convergents (ou divergents).
Ici les choses se compliquent un peu car nous pouvons de C1 aller vers C4 ou C5 en empruntant la zone1. Il est à noter qu’il faut un cantonnement, on voit par exemple que l’on peut aller de C4 à C5 en empruntant la zone1. Théoriquement ce cas ne devrait pas arriver car une telle situation signifie qu’un train arrive sur le passage depuis C4 et un autre depuis C5, il va y avoir du grabuge au niveau du passage !!!!
On remarque surtout qu’il faut utiliser des capteurs ponctuels qui encadrent la zone protégée, de façon a connaitre l’entrée et la sortie des convois et en déduire les sens de circulation surtout dans les cas de trajets convergents et divergents. Un simple capteur de consommation ne suffit pas pour différencier les trajets. Par contre, un rebroussement de chemin pour passer de C4 à C5 en s’arrêtant sur la zone 1 serait possible avec ce type de capteur, mais notre programme n’en tient pas compte car le changement de sens n’est pas permis à l’intérieur du passage, mais possible seulement à l’extérieur, sans que cela réduise la richesse des manoeuvres possibles.
La situation peut se résumer de la manière suivante :
Un passage ne peut être libre que si toutes ses zones le sont.
Une zone ne peut être occupée que par un seul trajet (un couple de capteurs appartenant à cette zone). C’est ce que l’on appelle une mutuelle exclusion.
Chaque capteur est associé à une zone
Une zone peut être associée à plusieurs capteurs. Elle possède plusieurs états :
- LIBRE : personne ne l’utilise
- ENTREE : passage de LIBRE à OCCUPEE avec un capteur entrant
- SORTIE : passage d’OCCUPEE à LIBRE avec un capteur sortant
L’algorithme est le suivant :
On boucle sur tous les capteurs
Si un capteur est activé
Si sa zone est LIBRE, c’est une entrée
On passe sa zone en ENTREE
On la note OCCUPEE
On note le numéro du capteur entrant
Si sa zone est en ENTREE et le capteur est différent de celui entrant, c’est une sortie
On passe sa zone en SORTIE
On note le numéro du capteur sortant
Si sa zone est en SORTIE
On reste en zone SORTIE
On note le numéro du capteur sortant
Si le capteur est inactif (pendant le temps que le convoi libère la zone)
Si sa zone est dans l’état SORTIE et si le capteur est le même que celui sortant
Sa zone est LIBRE
On efface les capteurs entrant et sortant de la zone
On termine en vérifiant l’état de l’ensemble des zones du passage
Si elles sont toutes LIBRES on gère la libération du passage
Sinon on reste en mode occupé
Les Capteurs
Les capteurs optiques
L’avantage des capteurs optiques est d’être actif tant qu’il y a une réflexion, ce qui facilite la détection de trains de tailles différentes car le capteur ne redeviendra inactif qu’à la fin du convoi. L’inconvénient est qu’il ne faut pas les mettre en présence d’une lumière trop forte.
Utilisation du capteur TCRT5000L :
Ce capteur peut s’activer sur une distance de 2,5 cm ce qui est amplement suffisant pour nos applications. La longueur d’onde est de 950nm et le courant de sortie est de 1mA.
Il se connecte de la manière suivante :
Et il s’intègre parfaitement entre les traverses des rails en HO :
Les interrupteurs à lame souple (ILS)
Ces relais, aussi appelés "REED", sont très discrets et peuvent se coller n’importe où entre les traverses, par contre il ne détecte que le début du train (aimant sous la locomotive). Il faut donc calculer un certain temps avant de désactiver son action (la durée de passage de la plus longue rame).
L’utilisation avec des relais Reed est plutôt réservée à l’échelle HO :
Les capteurs à effet Hall
Pour l’échelle N on préférera les capteurs à effet Hall, dont la taille est à peu près celle d’un transistor, plus plat, qui peut facilement se coller sur une traverse de voie à l’échelle N donc des plus discrets ! Ils nécessitent aussi la présence d’un aimant sous une locomotive.
Autres capteurs ?
Des capteurs de consommation seraient possibles comme ceux utilisés dans la détection de présence des convois dans chaque canton ou dans des zones particulières. Ils nécessitent des coupures de rails pour isoler les zones de détection. Ce ne sont pas ceux qui sont utilisés dans ce projet, mais chacun pourra faire l’adaptation.
Attention : il faut bien déterminer l’état HIGH (5V) ou LOW (GND) du capteur lors d’un passage du train pour déclarer cet état dans la fonction initSensor
que nous verrons plus loin.
Le programme
Le programme commence par l’inclusion de la bibliothèque zoneMultiPath.h
#include <zoneMultiPath.h>
Cette bibliothèque définit une classe "zoneMultiPath
" qui peut être instanciée soit avec les méthodes d’action par défaut du programme, soit avec vos propres méthodes d’action que vous pourrez écrire selon vos besoins.
Ce programme exemple montre comment utiliser les méthodes de cette classe.
Pour que le programme se compile correctement, il faut évidemment intégrer la bibliothèque dans votre IDE Arduino : après téléchargement de la bibliothèque (à la fin de cet article), utilisez le menu Croquis/Inclure une Bibliothèque/ajouter une Bibliothèque.
Vous pouvez vous reporter à l’article Installer une bibliothèque pour plus de détails.
Ensuite il faut créer une instance de la classe zoneMultiPath :
zoneMultiPath passage; // sans argument, ici, donc à utiliser avec les méthodes d'action par défaut du programme
Le programme reprend l’algorithme présenté en début d’article. Il est construit de manière à être facilement configurable. En effet toute la première partie du programme concentre les parties configurables. La première partie consiste en choix d’options (à enlever si ce n’est pas nécessaire) et la seconde en la configuration proprement dite.
Méthode de configuration du programme
Option CONSOLE
initConsole
: méthode pour affichage du déroulement sur la console (le moniteur série de l’IDE), paramètre le débit (par défaut 9600 bauds). On peut définir la vitesse de transmission.
Exemple :
passage.initConsole(115200);
Option CONTROLE
initControle
: méthode qui permet d’avoir deux leds (verte = libre / rouge = occupé) et un bouton pour le reset, qui peuvent être utile pour un TCO. Les deux leds garantissent de visualiser l’état du passage, sans ambiguïté. Le bouton de reset n’est pas un reset de l’Arduino, mais simplement une remise des paramètres et des états du passage par défaut.
Exemple :
#define LED_LIBRE 12
#define LED_OCCUPE 13
#define BOUTON_RESET A0
passage.initControl(LED_LIBRE,LED_OCCUPE,BOUTON_RESET);
Option COMMANDE par défaut
initCommand
: méthode qui permet de définir une sortie pour des leds, une sortie en cas de passage libre et une sortie en cas de passage occupé. Utile pour commander des accessoires externes (feux de route, barrières).
Dans le cas d’un passage libre le programme désactive la sortie LED (état LOW), active la sortie libre (état HIGH), désactive la sortie occupée (état LOW).
Dans le cas d’un passage occupé le programme active la sortie LED (état HIGH), désactive la sortie libre (état LOW), active la sortie occupée (état HIGH).
Exemple :
#define COMMANDE_LED 11
#define COMMANDE_ZONE_LIBRE 9
#define COMMANDE_ZONE_OCCUPEE 10
passage.initCommand(COMMANDE_LED,COMMANDE_ZONE_LIBRE,COMMANDE_ZONE_OCCUPEE);
Option COMMANDE personnel
On peut écrire ses propres méthodes pour traiter les phases de libération et d’occupation. Cela remplace la méthode initCommand
. Il faut dans ce cas instancier la classe zoneMultiPath
avec le nom de deux fonctions, la première pour traiter le cas passage libre et la seconde dans le cas d’un passage occupé. Il faut bien sûr après écrire ces fonctions dans son programme.
Exemple :
zoneMultiPath passage (actionFree,actionBusy)
Les déclarations des zones
Il faut donner le nombre de zones parcourant le passage puis énumérer les index de chaque zone.
#define NOMBRE_ZONES 2
#define INDEX_ZONE1 0
#define INDEX_ZONE2 1
Les déclarations des capteurs
De même ici il faut donner le nombre de capteurs puis les énumérer.
#define NOMBRE_CAPTEURS 6
#define VOIE1 "voie1"
#define CAPTEUR_VOIE_1 2
#define VOIE2 "voie2"
#define CAPTEUR_VOIE_2 3
#define VOIE3 "voie3"
#define CAPTEUR_VOIE_3 4
#define VOIE4 "voie4"
#define CAPTEUR_VOIE_4 6
#define VOIE5 "voie5"
#define CAPTEUR_VOIE_5 7
#define VOIE6 "voie6"
#define CAPTEUR_VOIE_6 8
La déclaration du temps d’attente
Ce paramètre sert à faire une pause entre la fin d’une occupation et la libération du passage. Pour des capteurs optiques le temps peut être assez court puisque le capteur se libère en fin de convoi, à condition que les espaces entre les wagon ne déclenchent pas une fausse fin de convoi. Pour des capteurs ponctuels, comme des ILS et capteurs à effet Hall, le temps doit être assez grand pour permettre à la fin de convoi de la plus grande rame de sortir du passage. Par défaut la valeur est de 1s (1000 ms, donc à augmenter en général).
#define ATTENTE 1000
La méthode setup
C’est ici que l’on initialise toute la configuration.
void setup() {
passage.initConsole(); // optionnel
passage.initControl(LED_LIBRE,LED_OCCUPE,BOUTON_RESET); // optionnel
passage.initCommand(COMMANDE_LED,COMMANDE_ZONE_LIBRE,COMMANDE_ZONE_OCCUPEE);// optionnel
passage.initRailroadCrossing(NOMBRE_CAPTEURS,NOMBRE_ZONES,ATTENTE);// obligatoire, ATTENTE par defaut = 1000ms
passage.initSensor(CAPTEUR_VOIE_1,INDEX_ZONE1,VOIE1); // pour chaque voie: son capteur, sa zone et son nom (facultatif)
passage.initSensor(CAPTEUR_VOIE_2,LOW,INDEX_ZONE1,VOIE2);
passage.initSensor(CAPTEUR_VOIE_3,LOW,INDEX_ZONE2,VOIE3);
passage.initSensor(CAPTEUR_VOIE_4,LOW,INDEX_ZONE2,VOIE4);
passage.initSensor(CAPTEUR_VOIE_5,LOW,INDEX_ZONE2,VOIE5);
passage.initSensor(CAPTEUR_VOIE_6,LOW,INDEX_ZONE2,VOIE6);
}
Remarque : ici nous utilisons les capteurs dont l’état actif est LOW (0V), contrairement aux exemples de capteurs donnés en début d’article, pour lesquel il faudra indiquer HIGH (voir plus loin à propos de la bibliothèque).
La boucle
void loop() {passage.Loop();}
Les méthodes optionnelles
Dans ce cas, il faut utiliser l’instanciation zoneMultiPath passage (actionFree,actionBusy)
.
Le premier argument permet de définir vos propres actions en cas de passage libre.
void actionFree() {
// Mettre ici les actions à faire dans le cas d'un passage libre
Serial.println(" on utilise la fonction actionFreeZone du programme");
}
Le second argument permet de définir vos propres actions en cas de passage occupé.
void actionBusy() {
// Mettre ici les actions à faire dans le cas d'un passage occupé
Serial.println(" on utilise la fonction actionBusyZone du programme");
}
Le programme ino et la bibliothèque
Le programme complet et la bibliothèque sont téléchargeables en bas de l’article. D’ailleurs, quand la bibliothèque est importée dans l’IDE, il est facile de trouver ce programme dans le menu Exemples/zoneMultiPath.
Un exemple
Voici la topologie de l’exemple, qui correspond à celui du programme téléchargeable. Il ne prend que 15% de mémoire de stockage et 400 octets de mémoire dynamique.
et le banc de test sur plaque d’essai devant l’Arduino Mini, supportant les 6 capteurs TCRT5000 et les 2 diodes. Le bouton reset est emprunté à une carte de 8 boutons poussoirs (provenant d’un autre projet) dont un seul est utilisé.
avec une carte prototype pour Nano que l’on trouve sur les boutiques en ligne avec une recherche de type "Prototype Adapter Expansion Board Shield Sensor Interface For Arduino NANO Card".
Tester et adapter le programme et la bibliothèque est simple :
- Relier un fil branché sur OV (Gnd) d’un coté avec les pins configurées pour les capteurs (ci-dessous).
- Lire le compte-rendu sur la console de l’IDE.
Et voici les ingrédients de cet exemple et quelques scenarii de passage de trains :
Configuration de la zone : nombre de capteurs : 6
capteur 1 voie1 zone1 : broches = 2 , etat : LIBRE
capteur 2 voie2 zone1 : broches = 3 , etat : LIBRE
capteur 3 voie3 zone2 : broches = 4 , etat : LIBRE
capteur 4 voie4 zone2 : broches = 6 , etat : LIBRE
capteur 5 voie5 zone2 : broches = 7 , etat : LIBRE
capteur 6 voie6 zone2 : broches = 8 , etat : LIBRE
Etat initial : On prendra comme hypothèse que le passage est libre (tous les capteurs sont libres). Cela dépend évidemment du placement du passage dans votre réseau. Pour moi c’est le cas le plus général. Sinon il vous faudra adapter un peu ce programme selon votre besoin. Cet état initial est restauré en appuyant sur le bouton de Reset prévu pour cela.
1) entrée depuis la voie 6
- le capteur C6 est activé
- -> le passage est occupé (dans la zone 2)
2) Sortie vers la voie3
- le capteur C3 est occupé, puis libre
- -> le passage est libre
3) entrée depuis la voie 1
- le capteur C1 est activé
- -> le passage est occupé (dans la zone 1)
4) entrée depuis la voie 5
- le capteur C5 est activé
- Sortie vers la voie 3
- la zone 2 est libre mais pas la zone 1
- Sortie vers la voie 2
- le capteur C2 libère la zone 1, toutes les zones sont libres
- -> le passage est libre
5) entrée depuis la voie 4
- le passage est occupé (la zone 2 est occupée)
- entrée depuis la voie 2 (la zone 1 est occupée)
- sortie vers la voie 6 (la zone 2 est libérée, pas la zone 1)
- sortie vers la voie 1 (la zone 1 est libérée)
- ->le passage est libre
A propos de la bibliothèque zoneMultiPath
Le fichier zoneMultiPath.h décrit l’objet zoneMultiPath
, en particulier la structure Tsensor
:
struct Tsensor {
char* name;
int pin;
bool activeLowHigh = HIGH;
int zoneNumber;
int predState;
};
Remarquez que le booléen activeLowHigh sert à choisir l’état de détection : HIGH (+5V) ou LOW (0V) : En général il est plus pratique et plus sûr d’utiliser des détecteurs dont l’état actif est LOW : appliqué sur une entrée de l’Arduino initialisé en mode INPUT_PULLUP évite les détections parasites (les entrées de l’Arduino en mode INPUT peuvent se comporter comme des antennes et ramasser tous les parasites environnants).
La fonction void initSensor (int Pin, bool Active, int Zone,char* Name = "");
comporte un paramètre Active
qui permet de préciser si les capteurs utilisés sont actifs LOW ou HIGH.
La fonction loop()
contient l’algorithme du passage à niveau : vous pouvez le modifier dans le fichier zoneMultiPath.cpp :
void zoneMultiPath::Loop() {
if (control) { testResetButton();}
boolean busy = false;
byte sensorStatus;
for (int i =0; i<sensorsNB;i++) {
sensorStatus = digitalRead(sensor[i].pin);
if (sensorStatus != sensor[i].predState ){ // si le capteur a changer d'etat
sensor[i].predState = sensorStatus; // on conserve le nouvel etat
if (sensorStatus == sensor[i].activeLowHigh) { // si le capteur est actif
if ((busyZone[sensor[i].zoneNumber].busy) & // on passe de ENTREE -> SORTIE
(busyZone[sensor[i].zoneNumber].state == ENTRY)
& (busyZone[sensor[i].zoneNumber].entry_sensor_number != i)) { // capteur de sortie != capteur entree
if (console){
Serial.print(" exit to path "); Serial.println(sensor[i].name);
}
busyZone[sensor[i].zoneNumber].state = EXIT; // on passe en zone SORTIE
busyZone[sensor[i].zoneNumber].exit_sensor_number = i; // on garde le numero de capteur de sortie
}
else {if (busyZone[sensor[i].zoneNumber].state == FREE) { // on passe de LIBRE a ENTREE
if (console) {
Serial.print(" enter from path "); Serial.println(sensor[i].name);
}
busyZone[sensor[i].zoneNumber].state = ENTRY; // on passe en zone ENTREE
busyZone[sensor[i].zoneNumber].busy = true; // la zone est occupee
busyZone[sensor[i].zoneNumber].entry_sensor_number = i; // on garde le numero du capteur d'entree
}
else {if (busyZone[sensor[i].zoneNumber].state == EXIT){ // on reste en zone SORTIE mais on change de capteur de sortie
if(console) {
Serial.print(" exit to path "); Serial.println(sensor[i].name);
}
busyZone[sensor[i].zoneNumber].exit_sensor_number = i; // on garde le numero de capteur de sortie
}
}
}
}
else { if ((busyZone[sensor[i].zoneNumber].state == EXIT)&
(busyZone[sensor[i].zoneNumber].exit_sensor_number == i)) {
busyZone[sensor[i].zoneNumber].state = FREE;
busyZone[sensor[i].zoneNumber].busy = false;
busyZone[sensor[i].zoneNumber].entry_sensor_number = -1;
busyZone[sensor[i].zoneNumber].exit_sensor_number = -1;}
}
}
}
if (checkBusyZone() == 0 ) {freeZoneManagement();}else {busyZoneManagement();}
}
Conclusion
Nous avons abordé d’un point de vue algorithmique le problème de partage d’une ressource par plusieurs demandeurs et résolu le problème de gestion d’un passage critique sur un réseau pouvant être parcouru par un ensemble quelconque de trajets en minimisant le nombre de capteurs.
Le programme complet se trouve dans la bibliothèque ci-dessous. Pour l’ouvrir, installez la bibliothèque (après téléchargement) puis ouvrez le menu Exemples/zoneMultiPath/zone_multi_trajet
Voici la bibliothèque :