LOCODUINO

Un gestionnaire en C++ pour votre réseau

Un gestionnaire en C++ pour votre réseau (4)

Trains et commandes de trains

.
Par : Pierre59

DIFFICULTÉ :

Le gestionnaire présenté dans les articles précédents est pratiquement complet, on va lui ajouter ici quelques améliorations : le suivi des trains, le suivi des commandes, le cab-signal, … et discuter de l’arrêt des trains aux signaux.
Le suivi des trains et le cab-signal seront ajoutés à l’exemple complet du Locodrome.
Jusqu’à présent le gestionnaire de réseau est complètement indépendant du matériel, progressivement dans cet article les contraintes et diversités du matériel vont apparaître. Ce qui va impliquer que la partie programme va s’amenuiser petit à petit, les classes vont se faire de plus en plus incomplètes avec des méthodes suggérées, puis faire la place à des descriptions, voire des idées.

Parlons de trains et de commandes de trains. Dans la réalité il y a d’une part des "trains" et d’autre part des "commandes" de trains

  • un "train" c’est normalement une locomotive avec ou sans une rame de voitures ou de wagons, ou une rame automotrice (on appellera "train" tout ce qui roule sur un réseau)
  • une "commande" de train c’est normalement un homme : le "conducteur". Le conducteur fait rouler le train en observant les signaux et les consignes de vitesse. Une commande peut être aussi automatique (comme sur certains métros). Un train sans commande est normalement à l’arrêt (quoique ?).

Les deux choses sont à priori disjointes, il peut y avoir des trains sans commande (sans conducteur) et des commandes sans train (des conducteurs sans train).

Sur nos réseaux on a aussi des trains, et aussi des conducteurs. Les conducteurs peuvent être des hommes (nous) conduisant les trains avec des "manettes" (ou "souris", transfo, smartphone, tablette, …) ou un automatisme électronique ou informatique. Ces manettes ou automatismes sont les commandes de trains que l’on appellera "commandes".

Trains et commandes de trains

Pour prendre en compte les trains et les commandes dans notre gestionnaire de réseau il va falloir des objets " train" et des objets "commande".

Il est pratique de regrouper toutes les caractéristiques des locomotives ou des automotrices dans un objet supplémentaire "engin-moteur" (mais ce n’est pas une obligation), ces caractéristiques ne changeant quasiment pas cela peut faciliter la gestion mémoire.

Trains

Commençons par les trains, par ce qui caractérise un train, essentiellement son sens (pair, impair voire indéterminé) et sa "vitesse". La "vitesse" est contrôlée surtout par les manettes, mais l’information fournie par les manettes n’a souvent pas grand chose a voir avec une "vitesse" (exprimée en kilomètres par heure) et les consignes envoyées aux alimentations analogiques ou numérique n’ont pas non plus grand chose à voir avec des "vitesses", par exemple en numérique les "vitesses" vont de 0 à 127 au maximum. On va appeler "cran" (un peu par analogie avec les machines électriques) les pseudo vitesses reçues des manettes ou envoyées aux alimentations.

La vitesse réelle d’un train dépend des caractéristiques de l’engin moteur, du cran, de la charge, du pourcentage d’une rampe, du réglage de CVs en numérique, … Si l’engin moteur est étalonné on peut à partir du cran obtenir la vitesse réelle (s’il y a un dispositif de compensation de charge).

Le sens et le cran sont les informations de base, mais on peut ajouter plein d’autres choses optionnelles : l’engin-moteur, le nom du train ("flèche d’or", "train bleu", … pour affichage sur le TCO par exemple), la zone ou les zones où est le train, un mode (ligne, manoeuvre, …), …

Concernant l’engin-moteur on peut avoir optionnellement : le nom (230D13, BB16007,… pour affichage sur le TCO par exemple), un type (vapeur, diesel, autorail, analogique, numérique, …), une adresse DCC, une icône (si on a de la mémoire !), une table de correspondance cran/vitesse, …

Voici les ébauches des classes engin-moteur et train :

enum {PAIR,IMPAIR /*,INDETERMINE*/}; // les sens des trains  

class EnginMoteur {
  char* nom;   // OPTION
  byte type;   // OPTION
  int adresse; // OPTION
  … // icone   // OPTION

  EnginMoteur(…) {…} // constructeur
};

class Zones { … } // petite classe pour gerer les zones où se trouve le train

class Train {
  byte sens  /*=INDETERMINE*/; // le sens du train
  byte cran = 0;               // 0 a 127 le cran du train
  EnginMoteur* em = NULL;      // l'engin moteur
  char* nom;                   // le nom du train   OPTION
  Zone* zone = NULL;           // la zone ou est la tete de train
  //Zones* zones = NULL;       // les zones ou est le train // OPTION
  byte mode;                   // informations sur le train   OPTION
  Commande* commande = NULL;   // la commande associee
  // byte max = 127;           // le cran maximum autorise   OPTION
	
  Train(…) {…} // constructeur

  byte getCran() { 
    return cran; 
  }
  void setCran(byte c) { 
    cran = c; 
  }
  byte getSens() { 
    return sens; 
  }
  void setSens(byte s) { 
    sens = s; 
  }
  void invSens() { // inversion du sens
    sens = !sens; 
  } 
  setCranEtSens(byte c,byte s) { 
    cran = c; 
    sens = s; 
  }

  // … getAdresse() { … }    // adresse DCC
  // byte getVitesse() { … } // vitesse reelle ( Km/h )

  boolean sensable() { // test si changement de sens possible
    return cran == 0     // la vitesse est nulle
    //&& zone.type … si le type de la zone le permet
    //&& le train est entierement sur une seule zone
    ;
  }
};

Le sens du train peut être un booléen si on n’utilise pas le sens indéterminé, la méthode sensable() permet de contrôler les changements de sens du train.

Commandes de trains

Les objets "commande" de train servent d’interface entre les manettes et le gestionnaire. On aura un objet commande par manette ou par commande automatique. Une commande échange des informations avec sa manette et des objets du gestionnaire.

L’objet commande reçoit de la manette des demandes :

  • d’incrémentation/décrémentation de cran
  • de changement de sens
  • de fonctions en numérique
  • d’arrêt d’urgence

Ces demandes sont contrôlées avant d’être transmises au gestionnaire (pas de vitesses négatives, pas de dépassement de vitesse, pas de changement de sens intempestifs, …). Les demandes sont alors envoyées à l’alimentation associée en analogique ou à la centrale DCC en numérique, ainsi qu’au train pour l’information du gestionnaire et éventuellement à la manette elle même pour la mise à jour de ses affichages.

L’objet commande envoie donc des informations ou des demandes :

  • à l’alimentation associée (analogique)
  • à la centrale DCC (numérique)
  • au train associé s’il existe (sens, cran)
  • à la manette associée (sens, cran, vitesse, icône, feux du cabsignal, nom du train ,icone du train, …) pour affichage sur la manette

Voici une ébauche de la classe commande :

class Commande {
  byte no;  // numero de commande et d'alimentation en analogique
  …         // lien avec la manette pour actions sur la manette (sens, cran, icone, cabsignal, …)
  …         // lien avec l'alimentation 
  …         // lien avec la centrale DCC
  Train* train = NULL; // le train commande

  Commande(…) { … } // constructeur

  void incCran(byte d) { 
    byte c;                // demande d'incrementation (en + ou -) du cran du train 
    if (train == NULL) {
      return; // precaution
    }
    c = train.cran;        // le cran ancien
    if (d > 0) { 
      c += d; 
      if (c > 127) {       // if (c>max) c=max; cran maximum autorise
        c = 127; 
      }                  
    } else  { 
      c -= d; 
      if (c < 0) {
        c = 0;
      }
    }
    train.setCran(c);    // le nouveau cran
    // envoi du cran (+ sens) a l'alimentation ou a la centrale (+ manette)
  }

  void setSens(byte s) { // demande de changement de sens du train 
    if (train == NULL) {
      return; // precaution
    }
    if (!train.sensable()) {
      return; // controles du changement de sens
    }
    train.setSens(s); 
    // envoi du sens (+ cran) a l'alimentation ou a la centrale (+ manette)
  }
};

Ici aussi le sens du train peut être booléen, mais il est pratique d’avoir un sens indéterminé, donc trois états (indéterminé, pair ou impair), notamment pour les manettes avec interrupteur à trois positions (dont deux instables) ou avec deux boutons.

On pourrait avoir une méthode setCran(), pour gérer des manettes à potentiomètre, mais pour le gestionnaire de réseau il est beaucoup plus pratique d’avoir une incrémentation/décrémentation du cran, donc des manettes à encodeur en quadrature ou smartphones ou tablettes. Par exemple lors de l’arrêt automatique devant un signal fermé le cran va devenir nul et il faudra forcer d’une manière ou d’une autre la remise à zéro du potentiomètre avant de pouvoir repartir. On a les mêmes problèmes avec les limitations de vitesses automatiques.

Allocation mémoire

Regardons un peu les problèmes d’allocation mémoire. On aura un objet "engin-moteur" par engin-moteur pouvant être mis sur le réseau. On aura un objet "train" par train sur le réseau (commandé ou pas). On aura un objet "commande" par manette, plus un par commande automatique.

Les objets "engin-moteur" peuvent être très gourmands en mémoire, on a souvent beaucoup de locomotives ou d’autorails et si on veut des icônes cela explose, ce n’est plus possible avec un Arduino seul, il faut passer par des fichiers soit sur une carte SD soit sur un ordinateur. Une solution pour les icônes est de les mettre dans les manettes, dans une carte SD par exemple.

Les objets "train" sont moins nombreux on peut les borner en fonction de la taille du réseau (nombre de voies disponibles). Les objets "commande" dépendent du nombre de manettes sur le réseau, on peut aussi les borner.

Ce qui faut éviter c’est de créer des objets (avec des new) et les abandonner par la suite pour un créer d’autres, cela transforme la mémoire en gruyère, avec des trous partout, ce qui est très difficile à gérer pour l’Arduino. Par contre tant qu’il y a de la mémoire disponible on peut ajouter des objets en fonction des besoins.

Pour suivre et contrôler la disponibilité de la mémoire, il faut se servir de la fonction freeRam qui a été ajoutée, avec une détection du processeur (ARM pour l’Arduino Due et AVR pour les Uno, Nano, Mega, Mini...) :

#if defined(__arm__) 

#include <malloc.h>               // pour la mesure de mémoire sur Due
extern "C" char *sbrk(int i);

int freeRam() {
	struct mallinfo mi=mallinfo();
	char *heapend=sbrk(0);
	register char * stack_ptr asm("sp");
	return (int) (stack_ptr - heapend + mi.fordblks);
}

#elif defined(__AVR__) 

int freeRam () { 
  int v; // calcul de la memoire libre
  extern int __heap_start, *__brkval;    
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

#else

#error Processeur non supporté.

#endif

On peut aussi se passer des objets "engin-moteur" et fusionner les objets "train" et les objets "commande". Sur le Locodrome, pour économiser la mémoire, il n’y a pas d’objet "engin-moteur", seulement deux objets "train" et pas d’objet "commande". Les objets "train" et "commande" sont fusionnés, on a toujours deux trains et deux commandes.

Suivi des trains

Muni des objets trains on peut assez facilement faire un suivi des trains. On a un suivi des trains quand le gestionnaire sait a tout moment où sont les trains, plus précisément dans quelle(s) zone(s) ils sont.

Le suivi des train sert :

  • à l’affichage des trains sur tco
  • de base pour le cab-control

L’idée du fonctionnement est le suivant, chaque zone peut mémoriser un train. Quand un train occupe une zone on copie le train de la zone "précédente" dite zone de provenance. Quand un train libère une zone on efface le train.

Le plus délicat est de trouver la zone de provenance, sauf pour les zones en cul de sac ou parcourues toujours dans le même sens, il y a deux provenances possibles et il faut choisir la bonne.

Depuis le premier article il y a dans la classe zone une méthode provenance() (non documentée) prévue à cet effet :

Zone* provenance() { 
  Zone* p; 
  Zone* i; 
  // if (type==SENS_IMPAIR_SEUL) return suivantePaire();   // optimisation (si type)
  // if (type==SENS_PAIR_SEUL)   return suivanteImpaire(); // optimisation (si type)
  p=suivantePaire();   
  if (p != NULL && p->libre()) {
    p = NULL; // candidate si non nulle et occupee
  }
  i = suivanteImpaire(); 
  if (i != NULL && i->libre()) {
    i = NULL; // candidate si non nulle et occupee
  }
  if (p != NULL) {
    if (i == NULL) {
      return p; 
    } else {
      erreur(201); // deux candidates (ambiguite)
    }
  } else {
    if (i != NULL) {
      return i; 
    } else {
      erreur(202); // aucune candidate (erreur)
    }
  }
  return NULL;
};
 

Si on a des types de zone, une optimisation peut être faite en testant si la zone ne peut être parcourue que dans un sens (pair ou impair). Dans le cas général on regarde les zones adjacentes et si une seule est occupée on a trouvé la provenance sinon il y a ambiguïté et c’est une erreur.

Comme il a été précisé dans l’article précédent (avec l’exemple du Locodrome) il faut apporter un grand soin à l’écriture des méthodes suivantePaire() et suivanteImpaire(), une zone a une suivante seulement si les aiguilles sont bien positionnées pour qu’un train virtuel puisse passer de la zone à la suivante ou vice versa sans dérailler, sinon elle n’a pas de suivante (résultat NULL). Cet aspect est essentiel pour le bon fonctionnement de la méthode provenance().

Malgré cela dans quelques cas l’ambiguïté reste, c’est malheureusement (ou heureusement) le cas du Locodrome à cause du BAL de voie unique. Par exemple, quand un train impair passe de la zone z4 à la zone z3, lors de l’occupation de z3 on recherche la provenance, z4 est occupée (il y a toujours deux zones occupées lors d’un changement de zone) et z2 peut être occupée par un autre train (de sens impair aussi à cause du BAL), l’ambiguïté reste, il faut la lever.

Tous les moyens sont bons pour lever l’ambiguïté : sens de trains, sens d’itinéraires, sens d’autorisation , … . Pour le Locodrome on a choisi le sens de l’autorisation. Pour mettre en oeuvre cette levée d’ambiguïté on va retoucher très légèrement la méthode provenance() :

Zone* provenance() { 
  Zone* p; 
  Zone* i; 
  // if (type==SENS_IMPAIR_SEUL) return suivantePaire();   // optimisation (si type)
  // if (type==SENS_PAIR_SEUL)   return suivanteImpaire(); // optimisation (si type)
  p = suivantePaire();   
  if (p != NULL && p->libre()) {
    p = NULL;  // candidate si non nulle et occupee
  }
  i = suivanteImpaire(); 
  if (i != NULL && i->libre()) {
    i = NULL;  // candidate si non nulle et occupee
  }
  if (p != NULL) {
    if (i == NULL) {
      return p; 
    } else {
      return(ambiguite()); // deux candidates (ambiguite)
    }
  } else  {
    if (i != NULL) {
      return i; 
    } else {
      erreur(202);         // aucune candidate (erreur)
    }
  }
  return NULL;
}

virtual Zone* ambiguite() { 
  erreur(201); 
  return NULL; 
}

En cas d’ambiguïté une méthode annexe ambiguite() est appelée, elle signale une erreur et retourne NULL, le comportement de la méthode provenance() ne change donc pas, mais la méthode ambiguite() est virtuelle, elle peut donc être redéfinie pour lever cette ambiguité, par exemple dans le cas de la zone z3 du Locodrome :

class Z3 : Zone { 
  …
  virtual Zone* ambiguite() { 
    if (!aut->getSens())  {
      return z2; 
    } else {
      return z4;
    }
  }
};

On a aussi le même problème avec z4.

Pour mettre en oeuvre le suivi des trains il faut ajouter une variable train dans la classe zone et quelques instructions dans les méthodes occuper() et liberer() de zone :

class Zone { 
  …
  Train* train = NULL; // le train dans la zone
  …
  void occuper() { 
    Zone* z;
    …
    z = provenance(); // provenance du train
    train = z->train; // train de la zone de provenance
    train->zone = this; // la zone pour le train    
  }

  void liberer() { 
    …
    train = NULL;    
  }
};

Il faut remarquer le lien croisé entre le train et la zone.

Un train peut occuper plusieurs zones en même temps, il peut être intéressant de mémoriser dans la classe train la liste de ces zones sous forme d’un tableau, ou mieux dans une petite classe annexe "Zones" qui fait toute la gestion de la liste. Sur un réseau donné on peut borner le nombre de zones occupées par un train. Avec une telle liste de zones on peut réaliser des contrôles :

  • test de rupture d’attelage, si dépassement de la borne (un simple compteur peut suffire dans ce cas)
  • en cas de libération de zone, vérification que c’est bien la dernière de la liste
  • en cas de changement de sens, vérification que le train est dans une seule zone (un simple compteur peut suffire dans ce cas)

Au démarrage du gestionnaire, celui ci ne connait aucun train, il faut donc un mécanisme pour lui dire où sont les trains. Cela peut se faire par un dialogue avec le gestionnaire par le biais d’un moyen quelconque (petit écran, boutons, …), ou aussi s’envisager avec des méthodes automatiques de recherche des trains (voir le forum ici notamment). Il faut fournir au gestionnaire des couples zone/train, à lui de vérifier que la zone est bien occupée.

Sur un grand réseau, avec beaucoup de trains, c’est assez fastidieux, il est alors intéressant d’avoir une mémorisation. A l’arrêt du gestionnaire on mémorise tous les couples zone/train, au démarrage on les reprend (en faisant des vérifications d’occupation des zones). La mémorisation peut se faire dans l’EEPROM de l’Arduino, dans un fichier d’une carte SD ou d’un ordinateur, … .

Sur le Locodrome le suivi des trains est matérialisé avec le nom des trains dans des cercles.

Suivi des commandes

Le suivi des commandes est très semblable au suivi des trains, il faut ajouter une variable commande dans la classe zone et quelques instructions dans les méthodes occuper() et liberer() de zone :

class Zone { 
  …
  Commande* comm = NULL; // la commande de la zone
  …
  void occuper() { 
    Zone* z;
    …
    z = provenance(); // provenance de la commande
    comm = z->comm; // commande de la zone de provenance
    comm->zone = this; // la zone pour la commande    
  }

  void liberer() { 
    …
    comm = NULL;    
  }
};

Comme pour le suivi des trains il faudra un dialogue pour associer une zone à une commande au démarrage. Le suivi des trains et le suivi des commandes font un peu double emploi, on peut choisir l’un ou l’autre en fonction des besoins et de ce que l’on veut mettre en oeuvre par la suite. Dans certains cas il peut être intéressant d’avoir les deux, par exemple si certaines zones sont découpées en sous-zones (sous-zones d’arrêt ou sous-zones pour avoir plusieurs engins-moteurs dans une zone (en analogique), ...

Poursuite en canton

La poursuite en canton, appelée aussi conduite sélective, permet de commander un train donné partout sur le réseau avec la même manette (quelle qu’elle soit), cela est quasiment indispensable en analogique.

En analogique on a plusieurs alimentations contrôlées par des manettes (ou une commande automatique), il faut donc du matériel pour soit commuter les alimentations aux zones, soit faire des passages de cran (PWM) de zone en zone, … . La poursuite en canton automatise ces opérations. La commutation peut se faire avec des matrices de relais ou tout autre système équivalent. En numérique n’a pas besoin d’un tel dispositif, mais c’est la seule "économie", tout le reste est nécessaire.

En analogique, la poursuite en canton, doit aller très vite pour alimenter la zone lors de l’occupation, sinon le train peut avoir un à-coup (manque d’alimentation un petit moment), on peut avoir aussi un système prédictif qui alimente les cantons en avance (mais c’est beaucoup plus difficile à gérer).

Le mécanisme de poursuite en cantons s’appuie logiquement sur le suivi des commandes (mais il peut aussi souvent s’appuyer sur le suivi des trains), il suffit de rajouter (au suivi des commandes ou des trains) des instructions pour agir sur les alimentations (commutation ou transfère de PWM) :

class Zone { 
  …
  Train* commande = NULL;     // le train dans la zone
  …
  void occuper() { 
    Zone* z;
    …
    z = provenance();         // provenance de la commande
    commande = z->commande; // commande de la zone de provenance
    commande->zone = this;  // la zone pour la commande
    // actions sur le materiel
  }

  void liberer() { 
    …
    commande = NULL;
    // actions sur le materiel
  }
};

On a juste ajouté au suivi des commandes des actions sur le matériel, on pourrait faire de même avec le suivi des trains.

Il faut remarquer que c’est la première fois que l’on fait une distinction entre l’analogique et le numérique, mais les différences vont s’accentuer par la suite.

Cabsignal

Le cab-signal permet d’afficher sur la manette le signal que voit le train associé à cette manette. La mise en oeuvre du cab-signal est basée sur le suivi des trains (mais on peut le faire aussi avec le suivi des commandes). Il y a plusieurs cas ou il faut mettre à jour le cab-signal :

  • quand le train change de zone
  • quand le train change de sens
  • quand le signal devant le train change de feux
  • quand un train est associé à (ou dissocié d’) une commande

C’est à la classe zone de prendre en compte les changements de zone d’un train, cela se fait dans la méthode occuper() :

class Zone { 
  …
  void occuper() { 
    …
    if (train != NULL) {
      signalVu(train);
    } 
    ...
  }
};

void signalVu(Train* t) { 
  Signal* signal;       // le signal vu par le train  
  if (t->sens == SENS_PAIR) {
    signal = t->zone->signalPair;
  } else  {                  
    signal = t->zone->signalImpair;
  }
  if (signal != NULL) { // test si signal   
    // envoi des feux du signal a la manette associee au train (si elle existe)
  } else { // pas de signal (eteint)
    // envoi des feux du signal a la manette associee au train (si elle existe)
  }
}

La fonction signalVu() n’est pas dans une classe, ce n’est donc pas une méthode, ceci pour s’adapter plus facilement à différents cas de figure, elle utilise le sens du train pour trouver un éventuel signal.

C’est à la classe train de prendre en compte les changements de sens d’un train :

class Train {
  …
  void setSens(byte s) { // positionner le sens
    sens = s; 
    signalVu(this); 
  } 
  void invSens() { // inverser le sens
    sens = !sens; 
    signalVu(this); 
  } // inverser le sens
};

Toutes les méthodes intervenant sur le sens du train appellent la fonction signalVu()

Quand un signal change de feux c’est à la classe signal de transmettre les nouveaux feux au cab-signal, cela se fait dans la méthode manoeuvrer() :

class Signal {
  …
  void manoeuvrer() { // appele quand un signal change   
    …
    signalChange(this);
  }
};

void signalChange(Signal* s) { // le signal vu par le train change
  Zone* zone; 
  Train* train; 
  zone = s->zone; 
  train = zone->train; 
  if (train == NULL) {
    return; // pas de train
  }
  if (train->sens == SENS_PAIR) { 
    if (s != zone->signalPair) {
    return; // pas bon sens ou pas signal
    }   
  } else { 
    if (s != zone->signalImpair) {
    return; // pas bon sens ou pas signal
    } 
  }
  // envoi des feux du signal a la manette associee au train (si elle existe) 
}

Ici aussi signalChange() est une fonction pour plus de souplesse, cette fonction utilise les informations de la zone où est implanté le signal pour chercher un train éventuel sur cette zone puis en fonction du sens de ce train transmettre au cab-signal les nouveaux feux du signal (s’il est dans le bon sens).

Quand un train est associé à (ou dissocié d’) une commande il faut aussi mettre à jour le cab-signal.

Sur le Locodrome, comme il n’y a quasiment pas de manettes, le cab-signal est reporté sur le TCO.

Arrêt des trains

Pour assurer la sécurité des circulations des trains il faut que les trains respectent les signaux, avec un cab-signal c’est possible en conduite manuelle mais il est difficile de conduire plusieurs trains simultanément, en conduite automatique ce n’est pas possible. Il faut donc qu’il y ait, au moins, un arrêt automatique des trains devant les signaux fermés, mieux un respect des vitesses. Avec le KVB (contrôle de Vitesse par Balise, le K est utilisé à la place du C) la SNCF a de tels dispositifs. On peut s’étonner que sur le Locodrome les trains s’arrêtent automatiquement sans un tel dispositif, mais ce sont des trains virtuels programmés pour respecter les signaux. Avec des trains réels, même miniatures, il faut un dispositif idoine.

Commençons pas le respect des signaux d’arrêt (carrés, carrés violets, sémaphores, … ). Sur nos réseaux beaucoup de solutions existent, la plus répandue est la zone d’arrêt. En analogique il suffit de couper le courant dans la zone d’arrêt, le gestionnaire peut commander cette coupure (couplée avec la manœuvre du signal ou l’établissement de l’itinéraire). En numérique on peut utiliser sur la zone d’arrêt un générateur de freinage, le système ABC de Lenz ou autre, là aussi le gestionnaire peut commuter les dispositifs.

Le système avec zone d’arrêt présente pas mal d’inconvénients :

  • il faut couper les rails (au moins un) en plus des inévitables coupures de zone
  • la zone d’arrêt doit être suffisamment longue
  • l’arrêt au pied du signal n’est pas facile à réaliser
  • la détection de présence des trains (par consommation de courant) est plus compliquée (deux ou trois sous-zones)
  • la double traction est plus délicate
  • il faut prévoir les circulations en contresens
  • il est difficile d’avoir des circulations en pousse (rames de banlieues, corail réversibles, autorails et automotrices multicaisses, … )
  • arrêt brutal en analogique (sauf si dispositif de ralentissement et d’accélération)

L’avantage est la simplicité du système, surtout en analogique.

Une autre possibilité pour arrêter les trains devant les signaux fermés est le "tir au but", dans ce cas pas besoin de matériel, c’est le gestionnaire qui prend tout en charge (ou presque). Pour pouvoir faire cela il y a plusieurs pré-conditions :

  • en analogique avoir des alimentations à régulation de charge, en numérique la plupart des décodeurs font la régulation de charge
  • mesurer précisément la longueur des zones (éventuellement en fonction de la position d’aiguilles)
  • de préférence étalonner les engins-moteurs pour connaitre la vitesse réelle en fonction du cran (et inversement)

Le principe est simple, quand un train franchit un signal à l’avertissement (le signal suivant est donc fermé) le gestionnaire connait la vitesse réelle du train (à partir du cran) et la distance avec le signal d’arrêt, il peut alors calculer une courbe de décélération progressive pour arrêter le train juste devant le signal et envoyer périodiquement à l’alimentation ou au décodeur des consignes de vitesse pour respecter cette courbe de décélération. Cela peut nécessiter l’envoi de beaucoup de consignes et il faut que matériel supporte la charge.

Les inconvénients sont les pré-conditions, mais les avantages sont nombreux :

  • pas de matériel supplémentaire nécessaire
  • pas de zone d’arrêt
  • arrêt au pied du signal quelque soit le train
  • arrêt progressif et redémarrage progressif
  • pas de problèmes avec la double traction, ni les circulations en contresens
  • possibilité de circulations en pousse (une belle rame de VB2N poussée par une 16500 !)

Une détection ponctuelle (cellule photo électrique ou infrarouge, … ) un peu avant le signal peut améliorer le tir au but. Cela peut aussi s’envisager avec des alimentations analogiques sans régulation de charge avec éventuellement une autre détection ponctuelle à une distance plus importante du signal. Bien évidemment tout cela peut être pris en charge par le gestionnaire.

D’autres systèmes de tir au but sont possibles notamment avec le dispositif de "distance de freinage constante" inclus dans certains décodeurs DCC.

Avec les mécanismes de tir au but le gestionnaire peut aussi contrôler la vitesse réelle des trains et borner cette vitesse. En conduite manuelle on peut reporter une alarme sur la manette associée au train s’il dépasse la vitesse permise, en conduite automatique c’est directement pris en compte. On peut aussi imaginer de respecter tous les panneaux de limitation de vitesse rencontrés.

Quel plaisir de voir une belle rame qui se met à ralentir au passage d’un avertissement pour s’arrêter pile-poil devant le carré, puis repartir progressivement quand celui-ci passe au vert. Ou une rame qui se met à ralentir progressivement en franchissant un signal de ralentissement pour aborder le signal de rappel de ralentissement à 30 Km/h (ou 60). Ou une rame qui reprend de la vitesse avant d’atteindre le signal si celui ci s’ouvre avant.

Bilan

Cet article (et les précédents) montre bien que le gestionnaire décrit permet de faire tout, ou presque, ce que l’on veut avec le matériel que l’on veut. Beaucoup de logiciels commerciaux ou libres font la même chose (plutôt en numérique qu’en analogique), généralement avec des contraintes sur le matériel, l’intérêt du gestionnaire décrit ici est que l’on fait tout soi-même (matériel et logiciel), comme on veut, quand on veut, pas à pas, en maîtrisant (à peu près) tout.

Comme dans les articles précédent on trouvera dans le fichier ci-dessous le programme de gestion du Locodrome bien écrit en C++, ainsi que le TCO virtuel écrit en Processing :

Gestionnaire et Locodrome (version 4)

Dans les différents fichiers, du programme de gestion du Locodrome, tout ce qui est spécifique à l’interface avec le TCO virtuel est marqué "TCO" et tout ce qui est spécifique au cab-signal est marqué "CAB". Cette version devient pour l’instant la version de référence, elle peut être utilisée pour d’autres réseaux en réécrivant le fichier "LocodromeBAL".

Dans le gestionnaire le suivi des trains et le cab-signal ont étés ajoutés, une réorganisation des fichiers a aussi été faite, ainsi que quelques modifications et corrections. Le TCO a été l’objet de grosses modifications, outre l’ajout du suivi des trains et du cab-signal, des simplifications on été faites dans le déplacement des trains (simplification du calcul des points de passage et lissage du déplacement, orientation des trains, nouveaux boutons de commande des trains).

Pour essayer le programme de gestion du Locodrome avec le TCO virtuel et faire circuler des trains tout aussi virtuels consulter le fichier "Notice". La discussion est ouverte sur le Forum : Modélisation logicielle d’un réseau - le système de Pierre59.

28 Messages

  • locodrome 2 avril 2017 01:13, par VENET

    Bjr
    Je voudrais diriger un petit circuit N pour mon petit fils avec un arduino Uno (et arduino 1.8.1)
    J’ai telechargé Locodrome pour m’initier, le TCO s’affiche bien sur le PC mais les programmes locodromeBal.ino et LododromeBAL4.ino renvoient une erreur de compilation qui concerne a chaque fois la 1ere instruction # include :
    # include "TCO.h" pour la V3
    # include "Constantes.h" pour la V4.
    Pourriez vous me dire ce qui ne va pas. Merci

    Répondre

  • Un gestionnaire en C++ pour votre réseau (4) 2 avril 2017 10:04, par Dominique

    Le programme LocodromeBAL4.ino doit se trouver dans un dossier du même nom, qui contient aussi les autres fichiers Constantes.h, etc... comme sur l’image ci-dessus.

    Avez-vous bien respecté ceci ?
    Sinon, précisez l’erreur dans le bas de l’éditeur de l’IDE et donnez nous plus de détails.

    Je viens de recompiler le programme, sans erreur pour un Uno.
    Cordialement
    Dominique

    Répondre

    • Un gestionnaire en C++ pour votre réseau (4) 2 avril 2017 15:37, par venet

      Merci pour ces reponses hyper rapides,
      J’ai fait ce que vous preconisiez et ... c’est OK
      Maintenant, nouveau probleme surement simpliste, mon Arduino etant sur le com3, je dois aller indiquer cela dans la variable NO_PORT, mais je ne la trouve pas dans le programme telechargé sur l’Uno.
      Suis je a cote de la plaque ?
      Debutant oblige !!
      Merci

      Répondre

  • Un gestionnaire en C++ pour votre réseau (4) 2 avril 2017 10:25, par Pierre59

    Bonjour

    Moi aussi je viens de recompiler LododromeBAL4 avec Arduino 1.8.1 (sur Mac OSX 10.9.5) sans problèmes.

    Cordialement

    Pierre

    Répondre

  • Un gestionnaire en C++ pour votre réseau (4) 2 avril 2017 17:49, par Pierre59

    Bonjour

    La variable NO_PORT se trouve dans le programme Processing (fichier TCOlocodrome4C.pde). Voir aussi les instructions dans le fichier "notice" (vers la fin), pour appairer le n° de port de Processing avec celui de l’Arduino..

    Cordialement

    Pierre

    Répondre

    • Un gestionnaire en C++ pour votre réseau (4) 2 avril 2017 18:09, par venet

      Encore merci...
      Si j’ai bien compris j’installe le programme processing (que je ne connais pas du tout), j’ouvre le fichier TCOLocodrome4C.pde, je modifie NO_PORT et j’enregistre... Puis je relance..
      Je cherche surtout a faire ’bouger’ des trains dans un premier temps, les programmes Locodrome V3 et V4 peuvent ils etre utilises sans TCO sur un petit circuit reel (donc sans affichage) ou sinon puis je me baser sur le projet de Guillaume de ’petit locodrome’ qui a le meme schema ?
      C’est pour realiser avec un enfant de 8 ans, donc progressivement, et puis il faut que je me mette a niveau..
      Cordialement
      Claude

      Répondre

      • Un gestionnaire en C++ pour votre réseau (4) 2 avril 2017 18:37, par Pierre59

        Bonjour

        Il faut tout d’abord installer une version 3 de Processing, puis lancer le TCO en Processing pour avoir la liste de ports USB/série et leurs numéros et ajuster le NO_PORT. Tout cela est décrit dans le fichier "notice".

        Le gestionnaire peut être utilisé sans TCO, mais dans ce cas il faut l’interfacer avec un réseau réel, ce qui demande de l’électronique, notamment des détecteurs de présence des trains sur les zones.

        Le gestionnaire est conçu pour s’adapter à n’importe quel réseau, mais cela demande réécrire toute la description du réseau. Sur Locoduino on est dans une logique de DIY (faites le vous même).

        Le Locodrome de Guillaume est à l’origine de l’écriture du gestionnaire car il est très simple. Depuis la publication d’autres utilisations du gestionnaire sont en cours sur d’autres réseaux, voir sur le Forum Locoduino :

        http://forum.locoduino.org/index.ph...

        Cordialement

        Pierre

        Répondre

  • Un gestionnaire en C++ pour votre réseau (4) 2 avril 2017 22:59, par Dominique

    Etant donné que le Locodrome est tout fait, coté Arduino pour le Gestionnaire et cote Processing pour le TCO, cela peut constituer un excellent projet pour un enfant de 8 ans, mais il vous faudra quelques autres ingrédients et aider votre gamin en faisant vous-même toute l’électronique :

    • des rails évidemment avec 2 aiguillages à moteur
    • un train ou 2, de préférence en DCC (digital)
    • une centrale DCC (de préférence à construire vous même, à partir d’un montage décrit ici http://www.locoduino.org/spip.php?a.... J’en ai fait déjà plusieurs, c’est amusant et pas cher !
    • un module de commande des aiguillages (Arduino avec des relais)
    • quelques détecteurs de consommation (on peut vous aider à choisir)
    • quelques signaux (en option ou plus tard)
    • un Arduino avec le Gestionnaire (qui peut probablement être intégré avec l’Arduino centrale DCC, si vous prenez un Mega)
    • relier tout cela ensemble (selon un joli schéma à faire).

    C’est un projet auquel je pense comme démonstrateur dans un club, quand j’aurai fini mon réseau.

    Qu’en pensez-vous ?

    Répondre

  • Un gestionnaire en C++ pour votre réseau (4) 27 novembre 2017 22:48, par Dario Bello

    Bonsoir,

    J’ai lu une bonne partie du projet et franchement c’est assez génial tout ce que vous avez fait. Quand je dis, "Vous avez fait", je veux dire, toutes les personnes qui ont contribué au développement de ce projet et de tous les autres sur ce site/forum.

    J’ai installé Processing 3.3.6, la dernière version de java et téléchargé le fichier zip Locodrome V4, puis extrait le tout.
    J’exécute le fichier TCOlocodrome4C.pde, Processing s’ouvre avec tous les fichiers .pde chargés.
    A l’exécution de TCOlocodrome4C j’ai cette erreur :
    “java.lang.ArrayIndexOutOfBoundsException : 0
    at TCOlocodrome4C.setup(TCOlocodrome4C.java:64)”

    et le TCO ne s’affiche pas.
    J’ai une question : peut-on utiliser Locodrome sans brancher un Arduino ou doit-on obligatoirement le brancher ?

    Est-il possible de voir juste le dessin du TCO sans connexion avec l’Arduino, sans simulation de train ?
    Si non, je dois avoir manqué ou pas « imprimé » quelque chose dans la lecture du projet !

    Merci pour vos réponses et bonne soirée.
    Bien à vous
    Dario

    Répondre

  • Un gestionnaire en C++ pour votre réseau (4) 28 novembre 2017 09:22, par Pierre59

    Bonjour

    A priori le programme TCOlocodrome est prévu pour fonctionner avec un gestionnaire sur Arduino.

    Mais on peut aussi, moyennant quelques précautions, le faire fonctionner seul.
    Je l’ai essayé seul avec Processing 3.3.6 (sur Mac), juste un petit problème avec le numéro de port (variable NO_PORT), indice hors limite à la ligne 45 (du setup), après changement (NO_PORT=0) le TCO apparait normalement.

    Pour en revenir à votre message d’erreur, la ligne 64 dans le fichier TCOlocodrome4C ne contient qu’une accolade ( ’}’ ) ???
    Pouvez vous préciser votre système, ce qu’il y a dans votre ligne 64 et ce qui est affiché dans la console.

    Vous pourriez aussi nous dire ce que vous voulez faire avec le TCO.

    Cordialement

    Pierre

    Répondre

    • Un gestionnaire en C++ pour votre réseau (4) 28 novembre 2017 16:26, par Dario Bello

      Bonjour Pierre et merci pour votre réponse.

      Mon système, PC Windows 7 Professionnel 64 bit.

      En fait l’erreur se situe à la ligne 43, println("Le port utilise : "+Serial.list()[NO_PORT]) ;
      En effet, comme les erreurs ne sont pas "catchées", il donne l’erreur à la fin de la méthode setup() ligne 64 qui est l’accolade.

      Vous avez mis quelle valeur dans la constante [NO_PORT] ?
      Moi j’ai 0 et j’ai essayé avec un numéro de port mais cela ne fonctionne pas.

      J’ai peut-être mal posé ma question. Je voulais essayer (tester) TCOlocodrome, avant de créer mon Locodrome, afin de mieux comprendre le fonctionnement. Je me disais en mode simulation peut-être que je peux lancer TCOlocodrome et voir ce que cela donne avec le debugger en activant les boutons du TCO, est-ce possible sans le brancher à un Arduino ?

      Ensuite si je désire créer mon TCO, je dois lancer « TCO_V5_3.pde » et dessiner mon TCO est-ce correct ?

      Merci encore et bonne fin de journée.
      Je me suis inscrit sur le forum Locoduino, ainsi j’expliquerai mon projet et je créerai un fil.

      Dario

      Répondre

      • Un gestionnaire en C++ pour votre réseau (4) 29 novembre 2017 11:46, par Pierre59

        Bonjour

        Le TCOlocodrome ne fonctionne pas sans Arduino, on peut juste avoir l’affichage en supprimant dans le programme ce qui concerne le port série (utilisations de la variable "port").

        Si vous voulez savoir comment cela fonctionne, il suffit de demander, j’ai un article, en cours de finition, sur ce sujet, je peux vous l’envoyer si vous voulez. Il décrit comment faire son propre TCO (comme celui du Locodrome).

        Cordialement

        Pierre

        Répondre

        • Un gestionnaire en C++ pour votre réseau (4) 29 novembre 2017 13:55, par Dario Bello

          Bonjour Pierre et merci pour vos réponses.

          Très volontiers, je serais intéressé à recevoir votre article sur "comment faire son TCO".

          Comme je le disais, je suis inscrit sur Locoduino sous dbe8f. C’est aussi plus facile si vous désirez me l’envoyer par là.

          Toute bonne fin de journée.
          Bien à vous.
          Dario

          Répondre

      • Un gestionnaire en C++ pour votre réseau (4) 28 novembre 2020 13:12, par Laurent18

        Bonjour Dario,

        Pb NO_PORT :
        Lors de l’exécution du setup modifié ci-dessous, le logiciel TCOlocodrome4C vous propose la liste complète des COM parmi lesquelles se trouve celle de l’Arduino (pour moi COM3 en première position dans la liste). NO_PORT est l’index dans la liste (0 est le premier élément). A vous de mettre dans NO_PORT l’index de la COM Arduino pour que ça fonctionne bien (NO_PORT = 0 pour moi).

        void setup()
        print("Les ports serie : ") ;
        printArray((Object[])Serial.list()) ;
        print("Le port utilise : ") ;
        println(Serial.list()[NO_PORT]) ;
        port=new Serial(this,Serial.list()[NO_PORT],9600) ;
        port.buffer(3) ;
        portWrite(’X’) ; // (re)initialisation du programme de gestion
        size(1100,500,P2D) ; noStroke() ;

        En utilisant une liste, il n’y a plus d’erreur d’index.

        Par contre je cherche toujours pourquoi, de temps en temps, TCOlocodrome4C déclare le bus de la carte Arduino en cours d’utilisation (il suffit de redémarrer TCOlocodrome4C et la carte Arduino pour que ça fonctionne à nouveau). Je pense que c’est l’instruction « new » qui cherche à déclarer une nouvelle classe « serial » alors que le port fonctionne dans une ancienne (?) A étudier.

        Pb : carte Arduino
        Les modifications à effectuer sont trop importantes pour fonctionner sans Arduino ...

        Cordialement
        Laurent18

        Répondre

        • Un gestionnaire en C++ pour votre réseau (4) 3 décembre 2020 22:13, par Laurent18

          Rebonjour,

          Je viens de trouver pourquoi la carte Arduino ne démarre pas correctement. Il s’agit effectivement de l’instruction "new" qui fait des siennes lorsque l’Aduino cause sur le bus. Voici la solution à mettre au début du setup du TCOlocodrome4C :
          (alt91) = crochet ouvert
          (alt93) = crochet fermé
          void setup() (alt91)
          print("Les ports serie : ") ;
          printArray(Serial.list()) ;
          String portName=Serial.list()[NO_PORT] ;
          print("Le port utilise : ") ; println(portName) ;
          port=null ;
          while (port==null)(alt91)
          try (alt91)
          port=new Serial(this,portName,9600) ;
          (alt93)
          catch (Exception e) (alt91)
          e.printStackTrace() ;
          (alt93)
          (alt93)
          port.clear() ;
          port.buffer(3) ;
          portWrite(’X’) ; // (re)initialisation du programme de gestion
          size(1100,500,P2D) ; noStroke() ; etc...

          A vous de jouer avec le locodrome.
          Cordialement
          Laurent18

          Répondre

  • Un gestionnaire en C++ pour votre réseau (4) 24 mars 2020 23:16, par Gilles

    bonjour,
    accessoires et trains commandés avec une seule carte Arduino.
    Merci pour la réponse
    Gilles

    Répondre

  • Un gestionnaire en C++ pour votre réseau (4) 25 mars 2020 10:32, par Gilles

    Bonjour,
    1. Est-ce qu’il faut plusieurs cartes Arduino pour commander à la fois les accessoires (décodeurs signaux, aiguilles, ...) et les trains (décodeurs dans les locos pour vitesse, direction, ...).
    2. Comment mélanger tout ça dans l’IDE ?
    Merci.
    Gilles

    Répondre

    • Bonjour

      Le gestionnaire émet des messages sur différents canaux virtuels pour commander les accessoires (signaux, aiguilles, ...) et les trains (vitesse, direction, ...)

      Ces canaux virtuels doivent êtres implémentés dans le programme du gestionnaire par du code Arduino pour envoyer les messages sur des bus physiques : CAN, DCC, I2C, réseau (ethernet ou wifi), ...

      Une centrale DCC est nécessaire sur l’un de ces bus réels pour générer les trames DCC et les injecter sur le réseau avec une puissance électrique suffisante. Ce peut être une centrale faite maison en suivant les conseils du site Locoduino.

      Le gestionnaire attend aussi des messages venant de ces bus pour fonctionner : messages de rétrosignalisation (occupation des zones ou cantons), messages venant des manettes (vitesse et sens des locomotives), ...

      Pierre

      Répondre

  • Un gestionnaire en C++ pour votre réseau (4) 25 mars 2020 11:23, par Gilles

    Encore moi,

    Désolé si je pose des questions idiotes.

    Dans l’IDE, on met tous ces programmes à la queue-leu-leu ?
    C’est ça que je ne comprends pas.
    Je veux commander deux locomotives et 4 aiguillages, par exemple.

    Merci de m’éclairer.
    Gilles

    Répondre

    • Bonjour

      Je suppose que vous voulez commander vos locos en DCC.
      Dans ce cas il vous faut une centrale DCC reliée au gestionnaire par un bus quelconque (CAN si vous utilisez une centrale Locoduino ou autre). Il faudra rajouter au gestionnaire un peu de logiciel traduisant les messages du gestionnaire vers le protocole du bus utilisé (ce logiciel se met n’importe où dans le gestionnaire)
      Pour les aiguilles c’est un peu le même topo, il faudra aussi quelque chose entre le gestionnaire et les aiguilles, par exemple un décodeur d’accessoire DCC supportant votre modèle d’aiguille, ou un ou plusieurs satellites Locoduno qui sont sur le bus CAN, ou autre. Il faudra aussi rajouter au gestionnaire un peu de logiciel pour la prise en charge du bus (DCC,CAN,...)
      Il faudra peut être aussi une ou deux manettes si la centrale n’en est pas équipée.
      Pierre

      Répondre

  • Un gestionnaire en C++ pour votre réseau (4) 21 octobre 2020 19:05, par Jean-Michel Dubois

    Bonjour,

    D’abord merci pour ce très intéressant dossier.

    Pour que les algorithmes fonctionnent, les aiguilles doivent elles être placées dans des zones distinctes des zones de pleine voie ou les aiguilles prises en pointe peuvent-elles la partie terminale d’une zone comme on me le recommande ailleurs ?

    Répondre

    • Un gestionnaire en C++ pour votre réseau (4) 22 octobre 2020 10:18, par Pierre59

      Bonjour

      A priori les aiguilles peuvent êtres n’importe où. Mais elle doivent êtres protégées par un signal carré (qui peut être éventuellement loin de l’aiguille).

      Ce signal carré est aubiné (fermé) par l’occupation de la zone qui la suit immédiatement. Cette fermeture pourrait se faire par une détection ponctuelle (voire une zone un peut plus loin), mais ce n’est pas prévu pour l’instant.

      Pour résumer les zones de pleine voie avec une aiguille en pointe à la fin sont à éviter, c’est ailleurs normalement le cas à la SNCF. Sur nos réseaux on a tendance à le faire pour gagner des zones , donc les détecteurs associés.

      Ne pas oublier dans le découpage des zones qu’une zone ne peut laisser le passage à un seul train à la fois, quelque soient les positions des aiguilles.

      Le gestionnaire se rapproche le plus possible de ce qui existe à la SNCF, notamment dans le découpage des zones.

      Pierre

      Répondre

      • Un gestionnaire en C++ pour votre réseau (4) 22 octobre 2020 10:30, par Jean-Michel Dubois

        Merci beaucoup pour cet avis étayé. Si la seule raison de l’inclusion des aiguilles prises en pointe dans la zone amont est l’économie d’un détecteur, je vais les garder dans une zone distincte, tel que je l’ai conçu au départ.

        Répondre

Réagissez à « Un gestionnaire en C++ pour votre réseau (4) »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Projets »

Les derniers articles

Les articles les plus lus