Articles les plus consultés

jeudi 18 octobre 2018

Cap a gauche ou à droite?

Nous avons enfin nos coordonnées et notre cap!
Il reste encore quelques petits calculs a faire:
Si notre mobile suit un capGPS à 30° et qu'il doit suivre un capDir à 230°, doit t'il tourner a droite ou a gauche pour se retrouver le plus rapidement possible dans l'axe de son nouveau cap?

C'est bien l'un des simple problème qui m'a pourtant donné le plus de fil a retordre!

J'ai fini par le résoudre en décidant de prendre CapDir comme référence et de calculer CapDir + 180 comme opposé + la correction nécessaire si CapDir + 180 dépasse les 360°... et en conditionnant que si "capGPS" empiète sur le secteur de 180° a droite de CapDir, le mobile doit virer a gauche...Et vice-versa...

Voici encore un dessin pour comprendre le principe et le bloc de programme Processing a mettre en œuvre pour les tests :

int capGPS = 81;
int capDir = 80;
int OpposeCapDir =0; // la valeur en degré de l'opposé de la diagonale de CapDir
int ComplementOpposeCapDir = 0; // si le calcul de la diagonale dépasse 360°
void setup() {
  size(200, 200);
  noLoop();
  println ( "capGPS = " +capGPS);
  println ("capDir ="+capDir);
}

void draw (){
  CarteDir();
}


void CarteDir(){
  if ( capGPS != capDir ){ // si égal, ne fait rien
  OpposeCapDir = capDir +180;
  //------------------------------------------------------
  if (OpposeCapDir <= 360){
    if ((capGPS > capDir) && (capGPS < OpposeCapDir)){
      println ("virage a gauche");
  } else {
      println ("virage a droite");
  }
  }
  
  if (OpposeCapDir > 360) {// débordement des 360°
  ComplementOpposeCapDir = OpposeCapDir - 360;
  if ((capGPS > capDir) || (capGPS < ComplementOpposeCapDir)){
    println ("virage a gauche");
  } else {
      println ("virage a droite");
    }
  }
  }
}
    
    



Note: Virage à droite = Allumage moteur gauche.
          Virage à gauche =  Allumage moteur droit.
Dans l'exemple ci-dessus, si "CapGPS" se trouve dans la partie en jaune, le mobile doit tourner sur la gauche pour se caler sur "CapDir". En effet, "CapGPS" est plus grand que "CapDir", mais plus petit que"OpposeCapDir" .
(Le mobile devrait tourner sur la droite si  "CapGPS"était plus grand que "OpposeCapDir", mais plus petit que "CapDir").
 Le calcul n'est pas compliqué...
 Dans l'exemple ci-dessus, les choses se compliquent, car CapDir + 180 donne 430°, ce qui n'existe pas:
"ComplementOpposeCapDir" représente donc la partie qui "repart de zéro" et vient finaliser le demi-cercle quand "CapDir + 180°" dépasse les 360°...
Il faut finalement comparer si "CapGPS"est dans le secteur de "CapDir" à 360 ou de 0 à "ComplementOpposeCapDir"...
Si "CapGPS" se trouve dans ces secteurs, notre mobile doit virer a gauche pour se rapprocher de la direction "CapDir".
C'est assez difficile a interpréter, mais facile a comprendre si l'on se fait (comme moi) une maquette de compas en papier et carton...

Ouf!




mardi 16 octobre 2018

Propulsion et Pilotage...


A présent, notre module sait quelle direction il doit prendre pour se déplacer et a quelle altitude il doit voler...
Il faut maintenant lui en donner les moyens.

J'ai opté pour deux moteurs électriques fixes de propulsion principaux + deux moteurs d'appoint orientables qui serviront a élever le mobile ou le faire descendre, mais aussi a lui faire changer de direction en accélérant un des moteurs. De cette manière, on se passe de systèmes de gouvernes, ce qui allège l'ensemble...

Il nous faut donc:
- 1 relais commandé pour les moteurs propulsion principaux.
- 1 relais commandé pour le moteur de direction droit.
- 1 relais commandé pour le moteur de direction gauche.
- 1 servos commandé pour l'inclinaison verticale des moteurs de direction.

 Outre l'occupation de 2 pins ( RX, TX ) pour l'acquisition des données du module GPS,
Ce système nécessite 4 pins de l’Arduino pour commander le module,
ce qui revient a 6 pins pour commander l'ensemble du mobile.
(On pourra, a l'occasion, réserver 1 ou 2 pins pour d'autres fonctions (appareil photo, flash lumineux...)

Moteurs:
Il s'agit de moteurs de "jouets", bien connus des bricoleurs appelé aussi le "130".
Ils n'ont pas un rendement terrible, mais ils ne sont pas cher et on trouve facilement des hélices en plastique tri-pales qui s'adaptent rapidement sur leur axe...
Poids : 17 grammes.
Leur alimentation se fait sur 3 Volts et ils consomment 0,38 Ampères... (1,23 Waths)
Calcul de l'autonomie:
1,23 Whats /3 Volts = 0,41 Ampères.

2 piles baton AAA rechargeables de 2000 mAh en série=
3 Volts et 4000 mAh

4000 mAh / 0,41 mAh = 9, 75 heures de fonctionnement au maximum...

Poids:
-1 moteur  =                                17g
-2 piles AA 2100 mAh =             52g
                                                 -------
                                                   69g  X 4  = 276 g



Programme Arduino pour calcul de Cap

Suite au programme de test convainquant sous Processing, le voici à la sauce Arduino:
Comme indiqué, il faut entrer les valeurs dans les 8 premières variables...


int LatDepart = 473771;
char DirectionLatDepart = 'N';
int LongDepart = 37082;
char DirectionLongDepart = 'E';

int LatArrive = 479027;
char DirectionLatArrive = 'N';
int LongArrive = 19057;
char DirectionLongArrive = 'E';

char Tendance1 = 'A'; // pour Nord ou Sud 
char Tendance2 = 'A'; // pour Est ou West
float DimensionRectangleLatitudeDegres = 0.3;
float DimensionRectangleLongitudeDegres = 0.3;
float DimensionRectangleLatitudeMetres = 0.3;
float DimensionRectangleLongitudeMetres = 0.3;
float DimensionRectanglePourcent = 025.03;
int DegresBase = 0;
int Cap =0;
void setup() {
  Serial.begin(9600);
  Serial.println ("Connection OK!..");
  Serial.print ("Calculateur de Cap");
  Serial.println ("Entrer valeurs en INT dans les 8 premières variables");
}

void loop() {
DetectionDirectionLatitude (); 
 DetectionDirectionLongitude ();
 DefinitionRectanglePourcent ();
 DefinitionRectangleDegres ();
 DefinitionCap ();
}
void DetectionDirectionLatitude (){
if ((LatDepart > LatArrive) && (DirectionLatDepart == 'N') && (DirectionLatArrive == 'N')){
  Tendance1 = 'S';
  DimensionRectangleLatitudeDegres = LatDepart - LatArrive;
}
if ((LatDepart > LatArrive) && (DirectionLatDepart == 'S') && (DirectionLatArrive == 'S')){
  Tendance1 = 'N';
   DimensionRectangleLatitudeDegres = LatDepart - LatArrive;
}
if ((LatDepart < LatArrive) && (DirectionLatDepart == 'N') && (DirectionLatArrive == 'N')){
  Tendance1 = 'N';
   DimensionRectangleLatitudeDegres = LatArrive - LatDepart;
}
if ((LatDepart < LatArrive) && (DirectionLatDepart == 'S') && (DirectionLatArrive == 'S')){
  Tendance1 = 'S';
  DimensionRectangleLatitudeDegres = LatArrive - LatDepart;
}
if  (DirectionLatDepart != DirectionLatArrive){
  Tendance1 = DirectionLatArrive;
   DimensionRectangleLatitudeDegres = LatDepart + LatArrive;
}
 DimensionRectangleLatitudeMetres = DimensionRectangleLatitudeDegres * 111730;
} 
void DetectionDirectionLongitude (){
if ((LongDepart > LongArrive) && (DirectionLongDepart == 'E') && (DirectionLongArrive == 'E')){
  Tendance2 = 'W';
  DimensionRectangleLongitudeDegres = LongDepart - LongArrive;
}
if ((LongDepart > LongArrive) && (DirectionLongDepart == 'W') && (DirectionLongArrive == 'W')){
  Tendance2 = 'E';
  DimensionRectangleLongitudeDegres = LongDepart - LongArrive;
}
if ((LongDepart < LongArrive) && (DirectionLongDepart == 'E') && (DirectionLongArrive == 'E')){
  Tendance2 = 'E';
  DimensionRectangleLongitudeDegres = LongArrive - LongDepart;
}
if ((LongDepart < LongArrive) && (DirectionLongDepart == 'W') && (DirectionLongArrive == 'W')){
  Tendance2 = 'W';
   DimensionRectangleLongitudeDegres = LongArrive - LongDepart;
}
if  (DirectionLongDepart != DirectionLongArrive){
  Tendance2 = DirectionLongArrive;
  DimensionRectangleLongitudeDegres = LongDepart + LongArrive;
}
DimensionRectangleLongitudeMetres = DimensionRectangleLongitudeDegres * 75840;
  Serial.print ("Tendance  =    ");
  Serial.print (Tendance1);
  Serial.print (",");
  Serial.println (Tendance2);
  
   Serial.print ("  Dimension Rectangle Latitude en Degres =   ");
   Serial.println (DimensionRectangleLatitudeDegres);
   Serial.print ("                               en metres =   ");
   Serial.println (DimensionRectangleLatitudeMetres);
   Serial.print ("  Dimension Rectangle Longitude en Degres =  ");
   Serial.println (DimensionRectangleLongitudeDegres);
   Serial.print ("                               en metres =   ");
   Serial.println (DimensionRectangleLongitudeMetres);
} 
//-----------------------------------------------------------------------------------------------------
void DefinitionRectanglePourcent (){
  if (DimensionRectangleLatitudeMetres > DimensionRectangleLongitudeMetres ){
    DimensionRectanglePourcent = (100 * DimensionRectangleLongitudeMetres) / DimensionRectangleLatitudeMetres;
}
 if (DimensionRectangleLatitudeMetres < DimensionRectangleLongitudeMetres ){
    DimensionRectanglePourcent = (100 * DimensionRectangleLatitudeMetres) / DimensionRectangleLongitudeMetres;
 }
   Serial.print("                               en pourcent =   ");
   Serial.print (DimensionRectanglePourcent);
   Serial.println ("  %");
}
void DefinitionRectangleDegres (){
  if (DimensionRectanglePourcent <= 5.241){
    DegresBase = 0;
  }
  if (DimensionRectanglePourcent > 5.241){
    DegresBase = 3;
  }
   if (DimensionRectanglePourcent > 9){
    DegresBase = 6;
  }
   if (DimensionRectanglePourcent > 12){
    DegresBase = 9;
  }
   if (DimensionRectanglePourcent > 20){
    DegresBase = 12;
  }
   if (DimensionRectanglePourcent > 24){
    DegresBase = 15;
  }
   if (DimensionRectanglePourcent > 30){
    DegresBase = 18;
  }
   if (DimensionRectanglePourcent > 35){
    DegresBase = 21;
  }
   if (DimensionRectanglePourcent > 42){
    DegresBase = 24;
  }
   if (DimensionRectanglePourcent > 48){
    DegresBase = 27;
  }
   if (DimensionRectanglePourcent > 55){
    DegresBase = 30;
  }
   if (DimensionRectanglePourcent > 62){
    DegresBase = 33;
  }
   if (DimensionRectanglePourcent >70){
    DegresBase = 36;
  }
   if (DimensionRectanglePourcent > 78){
    DegresBase = 39;
  }
   if (DimensionRectanglePourcent > 88){
    DegresBase = 42;
  }
   if (DimensionRectanglePourcent > 93){
    DegresBase = 45;
  }
 Serial.print ("Degres de base  =    ");
 Serial.print (DegresBase);
 Serial.println ("   degres");   
}
void DefinitionCap () { 
  if  ((Tendance1== 'N') && (Tendance2 == 'E') && (DimensionRectangleLongitudeDegres < DimensionRectangleLatitudeDegres)){
  Cap = DegresBase;
}
if  ((Tendance1== 'N') && (Tendance2 == 'E') && (DimensionRectangleLongitudeDegres > DimensionRectangleLatitudeDegres)){
  Cap = 90 - DegresBase;
}
 if  ((Tendance1== 'S') && (Tendance2 == 'E') && (DimensionRectangleLongitudeDegres < DimensionRectangleLatitudeDegres)){
  Cap = 180 - DegresBase;
}
if  ((Tendance1== 'S') && (Tendance2 == 'E') && (DimensionRectangleLongitudeDegres > DimensionRectangleLatitudeDegres)){
  Cap = 90 + DegresBase;
}
 if  ((Tendance1== 'S') && (Tendance2 == 'W') && (DimensionRectangleLongitudeDegres < DimensionRectangleLatitudeDegres)){
  Cap = 180 + DegresBase;
}
if  ((Tendance1== 'S') && (Tendance2 == 'W') && (DimensionRectangleLongitudeDegres > DimensionRectangleLatitudeDegres)){
  Cap = 270 - DegresBase;
}
 if  ((Tendance1== 'N') && (Tendance2 == 'W') && (DimensionRectangleLongitudeDegres < DimensionRectangleLatitudeDegres)){
  Cap = 360 - DegresBase;
}
if  ((Tendance1== 'N') && (Tendance2 == 'W') && (DimensionRectangleLongitudeDegres > DimensionRectangleLatitudeDegres)){
  Cap = 270 + DegresBase;
}
 Serial.print ("CAP a suivre =  ");
 Serial.println (Cap);     
}


Faites vous-même vos essais...
Dans le programme final, les paramètres de départ sont remplacés par les paramètres de route envoyés par le GPS

dimanche 7 octobre 2018

Test convert

Note:
Dans ce programme de test de conversion d'une chaine en {int}, "ChaineLongitude" doit toujours avoir la même longueur,
(ce qui est bien le cas de la Longitude renvoyée par le module GPS).
 
 
char ChaineLongitude[11] = "123.123456";
char CaractereChaineLongitude = 'A';
int TestCaractereLongitude = 0;
int ChiffreLongitude = 0;
int Compteur1 = 0;
int Compteur2 = 0;


void setup() {
Serial.begin(9600); 

}

void loop() {
  DecodageCharenInt();
  AffichageValeur();
}

void DecodageCharenInt(){
 Compteur1 = 10; //pour multiplication (dixaines, cent...)
Compteur2 = 10; //pour capture caractere dans chaine 
 ChiffreLongitude = ChaineLongitude[Compteur2]; // capture premier caractere sur la droite (unités)

while (Compteur1 < 100000000){
 TestCaractereLongitude = int (ChaineLongitude[Compteur2]);
 if  (( TestCaractereLongitude >= 48) && ( TestCaractereLongitude <= 57)){
   ChiffreLongitude = ChaineLongitude[Compteur2]*Compteur1;
   Compteur1 = Compteur1 *10;
 }
 Compteur2--;
 ChiffreLongitude = ChaineLongitude[Compteur2];   
} 
}
void AffichageValeur(){
  Serial.println (ChaineLongitude);
  Serial.println (ChiffreLongitude);
  Serial.println ("------------");
}




mardi 2 octobre 2018

Mise en oeuvre du GY 26


La vitesse de transmission série du module GY 26 est de 9600bps.

Il s'agit bien d'un véritable compas (comparable a une bousole) et non pas un gyro-compas 3 axes (qu'il est d'ailleur difficile a configurer et est plutôt réservé pour la stabilisation des drones-hélico...)
Ce module là est bien facile a utiliser et est relativement bien précis, suivant mes tests.
Quand on envoie la "commande" (0x31) sur le port série, le module GY 26 répond par une trame de 8 octets au format ASCII dont voici le détail:

Octet0:0x0D              "Enter" en ASCII
Octet1:0x0A              "new line" en ASCII
Octet2:0x30~0x33     Chiffre des centaine de l'angle (0 à 3)
Octet3:0x30~0x39     Chiffre des dizaine de l'angle (0 à 9)
Octet4:0x30~0x39     Chiffre des unités de l'angle (0 à 9)
Octet5:0x2E               "point décimal" en ASCII
Octet6: 0x30~0x39     Chiffre de la décimale de l'angle (0 à 9)
Octet7: 0x00~0xFF     (somme de vérification)

Octet7= partie basse des octets (Octet0+ Octet1+ Octet2+……Octet6)

Exemple: <0x0D-0x0A-0x33-0x35-0x39-0x2E-0x36-0x1C> = 359.6°


Dans notre projet, seuls les octets 2,3 & 4 nous intéressent.
Nous allons créer un tableau "char" pour réceptionner nos 8 octets, car ils sont au format ASCII, puis nous capturerons les octets 2,3 & 4 pour les convertir au format "int".
 La valeur rendue va de 0° a 359°.


Voici un programme Arduino (Mega) de test:
_____________________________________________________________

/*
Gy-26 - Compass.
Base créee par Igor Araujo
Ce programme récupére les données d'une boussole électronique
et l'affiche sur le port serie vers l'ordinateur
a tester!!!
 Serial 2: 17 (RX) and 16 (TX)
*/

 // RX is digital pin 17 (connect to TX of other device)
 // TX is digital pin 16 (connect to RX of other device)

char ChaineBoussole[8];
int ResultatDegresBoussole = 0;
int compteur = 0;
byte Drapeau = 0;

void setup() {
  // Mise en route du port série et serie2
  Serial2.begin(9600);
  Serial.begin(9600);
  // test connection serie:
  Serial.println("Goodnight moon!");
}


void loop() {
  Lecture();
}

void Lecture() {
  // vidange tampon serie
  compteur = 0;
  while (Serial2.available()) {
      ChaineBoussole[compteur] = Serial2.read();
  }
  Drapeau = 0; // drapeau va bloquer le programme le temps que les datas soient transmis par le module
  // demande de réception datas
  Serial2.write(0x31);
  while (Drapeau == 0) {
    if (Serial2.available()) {
      ChaineBoussole[compteur] = Serial2.read();
      compteur = (++ compteur);
      if (compteur == 5) {
        ResultatDegresBoussole = (ChaineBoussole[2] - 48) * 100 + (ChaineBoussole[3] - 48) * 10 + (ChaineBoussole[4] - 48);
        Drapeau = 1;
      }
    }
  }
  Serial.println(ResultatDegresBoussole);
  delay(500);
}

jeudi 27 septembre 2018

Programme Processing pour calcul de cap

Pour ne pas "fatiguer" mon arduino en chargeant et rechargeant des programme d'essai, j'ai mis au point ce programme tournant sur Processing (qui fait exactement la même chose, dans un langage assez proche,mais sur ordinateur):
Il faut entrer les Latitudes & Longitudes, ainsi que les directions dans les premiéres lignes du programme pour tester...
Comparez vos résultats avec les résultats de sites et vous serez surpris de la précision.
Par ex:
 https://www.sunearthtools.com/fr/tools/distance.php



float LatDepart = 47.3771;
char DirectionLatDepart = 'N';
float LongDepart = 0.7082;
char DirectionLongDepart = 'E';

float LatArrive = 47.9027;
char DirectionLatArrive = 'N';
Float LongArrive = 1.9057;
char DirectionLongArrive = 'E';

char Tendance1 = 'A'; // pour Nord ou Sud 
char Tendance2 = 'A'; // pour Est ou West
Float DimensionRectangleLatitudeDegres = 0.3;
Float DimensionRectangleLongitudeDegres = 0.3;
Float DimensionRectangleLatitudeMetres = 0.3;
Float DimensionRectangleLongitudeMetres = 0.3;
Float DimensionRectanglePourcent = 025.03;
int DegresBase = 0;
int Cap =0;
void draw() {
 DetectionDirectionLatitude (); 
 DetectionDirectionLongitude ();
 DefinitionRectanglePourcent ();
 DefinitionRectangleDegres ();
 DefinitionCap ();
}
void DetectionDirectionLatitude (){
if ((LatDepart > LatArrive) && (DirectionLatDepart == 'N') && (DirectionLatArrive == 'N')){
  Tendance1 = 'S';
  DimensionRectangleLatitudeDegres = LatDepart - LatArrive;
}
if ((LatDepart > LatArrive) && (DirectionLatDepart == 'S') && (DirectionLatArrive == 'S')){
  Tendance1 = 'N';
   DimensionRectangleLatitudeDegres = LatDepart - LatArrive;
}
if ((LatDepart < LatArrive) && (DirectionLatDepart == 'N') && (DirectionLatArrive == 'N')){
  Tendance1 = 'N';
   DimensionRectangleLatitudeDegres = LatArrive - LatDepart;
}
if ((LatDepart < LatArrive) && (DirectionLatDepart == 'S') && (DirectionLatArrive == 'S')){
  Tendance1 = 'S';
  DimensionRectangleLatitudeDegres = LatArrive - LatDepart;
}
if  (DirectionLatDepart != DirectionLatArrive){
  Tendance1 = DirectionLatArrive;
   DimensionRectangleLatitudeDegres = LatDepart + LatArrive;
}
 DimensionRectangleLatitudeMetres = DimensionRectangleLatitudeDegres * 111730;
} 
void DetectionDirectionLongitude (){
if ((LongDepart > LongArrive) && (DirectionLongDepart == 'E') && (DirectionLongArrive == 'E')){
  Tendance2 = 'W';
  DimensionRectangleLongitudeDegres = LongDepart - LongArrive;
}
if ((LongDepart > LongArrive) && (DirectionLongDepart == 'W') && (DirectionLongArrive == 'W')){
  Tendance2 = 'E';
  DimensionRectangleLongitudeDegres = LongDepart - LongArrive;
}
if ((LongDepart < LongArrive) && (DirectionLongDepart == 'E') && (DirectionLongArrive == 'E')){
  Tendance2 = 'E';
  DimensionRectangleLongitudeDegres = LongArrive - LongDepart;
}
if ((LongDepart < LongArrive) && (DirectionLongDepart == 'W') && (DirectionLongArrive == 'W')){
  Tendance2 = 'W';
   DimensionRectangleLongitudeDegres = LongArrive - LongDepart;
}
if  (DirectionLongDepart != DirectionLongArrive){
  Tendance2 = DirectionLongArrive;
  DimensionRectangleLongitudeDegres = LongDepart + LongArrive;
}
DimensionRectangleLongitudeMetres = DimensionRectangleLongitudeDegres * 75840;
  println ("Tendance  =    " + Tendance1 + "," + Tendance2);
  println ("  Dimension Rectangle Latitude en Degres =   "+ DimensionRectangleLatitudeDegres);
  println ("                               en metres =   "+ DimensionRectangleLatitudeMetres);
  println ("  Dimension Rectangle Longitude en Degres =  "+ DimensionRectangleLongitudeDegres);
  println ("                               en metres =   "+ DimensionRectangleLongitudeMetres);
} 
//-----------------------------------------------------------------------------------------------------
void DefinitionRectanglePourcent (){
  if (DimensionRectangleLatitudeMetres > DimensionRectangleLongitudeMetres ){
    DimensionRectanglePourcent = (100 * DimensionRectangleLongitudeMetres) / DimensionRectangleLatitudeMetres;
}
 if (DimensionRectangleLatitudeMetres < DimensionRectangleLongitudeMetres ){
    DimensionRectanglePourcent = (100 * DimensionRectangleLatitudeMetres) / DimensionRectangleLongitudeMetres;
 }
  println ("                               en pourcent =   "+ DimensionRectanglePourcent +"  %");

}
void DefinitionRectangleDegres (){
  if (DimensionRectanglePourcent <= 5.241){
    DegresBase = 0;
  }
  if (DimensionRectanglePourcent > 5.241){
    DegresBase = 3;
  }
   if (DimensionRectanglePourcent > 9){
    DegresBase = 6;
  }
   if (DimensionRectanglePourcent > 12){
    DegresBase = 9;
  }
   if (DimensionRectanglePourcent > 20){
    DegresBase = 12;
  }
   if (DimensionRectanglePourcent > 24){
    DegresBase = 15;
  }
   if (DimensionRectanglePourcent > 30){
    DegresBase = 18;
  }
   if (DimensionRectanglePourcent > 35){
    DegresBase = 21;
  }
   if (DimensionRectanglePourcent > 42){
    DegresBase = 24;
  }
   if (DimensionRectanglePourcent > 48){
    DegresBase = 27;
  }
   if (DimensionRectanglePourcent > 55){
    DegresBase = 30;
  }
   if (DimensionRectanglePourcent > 62){
    DegresBase = 33;
  }
   if (DimensionRectanglePourcent >70){
    DegresBase = 36;
  }
   if (DimensionRectanglePourcent > 78){
    DegresBase = 39;
  }
   if (DimensionRectanglePourcent > 88){
    DegresBase = 42;
  }
   if (DimensionRectanglePourcent > 93){
    DegresBase = 45;
  }
println ("Degres de base  =    " + DegresBase + "   degres");   
}
void DefinitionCap () { 
  if  ((Tendance1== 'N') && (Tendance2 == 'E') && (DimensionRectangleLongitudeDegres < DimensionRectangleLatitudeDegres)){
  Cap = DegresBase;
}
if  ((Tendance1== 'N') && (Tendance2 == 'E') && (DimensionRectangleLongitudeDegres > DimensionRectangleLatitudeDegres)){
  Cap = 90 - DegresBase;
}
 if  ((Tendance1== 'S') && (Tendance2 == 'E') && (DimensionRectangleLongitudeDegres < DimensionRectangleLatitudeDegres)){
  Cap = 180 - DegresBase;
}
if  ((Tendance1== 'S') && (Tendance2 == 'E') && (DimensionRectangleLongitudeDegres > DimensionRectangleLatitudeDegres)){
  Cap = 90 + DegresBase;
}
 if  ((Tendance1== 'S') && (Tendance2 == 'W') && (DimensionRectangleLongitudeDegres < DimensionRectangleLatitudeDegres)){
  Cap = 180 + DegresBase;
}
if  ((Tendance1== 'S') && (Tendance2 == 'W') && (DimensionRectangleLongitudeDegres > DimensionRectangleLatitudeDegres)){
  Cap = 270 - DegresBase;
}
 if  ((Tendance1== 'N') && (Tendance2 == 'W') && (DimensionRectangleLongitudeDegres < DimensionRectangleLatitudeDegres)){
  Cap = 360 - DegresBase;
}
if  ((Tendance1== 'N') && (Tendance2 == 'W') && (DimensionRectangleLongitudeDegres > DimensionRectangleLatitudeDegres)){
  Cap = 270 + DegresBase;
}
println ("CAP ==  " + Cap);
  
    
}

lundi 17 septembre 2018

Trouver le bon cap

Si, comme moi, vous êtes nul en maths, mais que vous rêvez d'envoyer un mobile à un endroit précis de la Planète, ce petit tuto est fait pour vous.
Je vais vous expliquer comment déterminer très facilement un cap selon les données de Latitude et longitude de votre module GPS.
Le principe est celui des calculs simples sur les triangles-rectangles et sur les "pentes", principalement utilisés par les charpentiers-couvreurs et autres maçons.
2 raisons a ce choix:
- Je suis nul en maths mais je veux absolument maitriser et comprendre comment mon mobile calcule son cap.
-De base, et a moins d'intégrer de gourmandes librairies, l'Arduino ne dispose pas des fonctions mathématiques "arctan" ou autres, permettant de convertir des pourcents en degrés... Et la plupart des tutos que j'ai trouvé sont imbuvables.

Ce système utilise énormément d’opérations de comparaison, mais peu d'opérations de calcul, ce qui est un confort pour notre microcontrôleur.

Principe de calcul de la Pente:
En utilisant cette formule de couvreur, pour calculer le pourcentage de la pente d'un toit, il suffit de poser l'opération suivante:
pente (%) = 100 x hauteur / largeur
 ou:
pente (%) = 100 x Plus petit côté du rectangle / plus grand côté du rectangle
Note: Dans ce principe de calcul de pente en pourcentage, le résultat de l’opération fait que la pente ne peut jamais représenter plus de 100% .
Ce "100%" représente une valeur de 45°. Donc, la pente ne peux jamais dépasser la valeur de 45°.

Nous allons voir comment on détermine le triangle-rectangle pour calculer la pente et comment s'affranchir facilement de la limitation des "45°".

Tout d'abord, il nous faut les coordonnées de départ et les coordonnées de destination.

Premier point important:
Prenez bien note que les "coordonnées de départ" deviendront les coordonnées de route, dès que votre mobile aura quitté le plancher des vaches.
Le mobile recalculera son cap a intervalles réguliers pour vérifier sa position actuelle donnée par son GPS embarqué.
Les coordonnées de destination sont normalement rentrées une fois pour toute avant le départ du mobile.
On peut cependant prévoir plusieurs "points de passage" sur la route, pour prendre des photos, etc...

Nous allons commencer par un exemple.
Imaginons un trajet de Tours à Orléans.
Sur Géoportail IGN ou Map, nous trouvons pour ces deux ville les coordonnées suivantes:

Départ : Tours=     Lat: 47.3771,N    Long:  0.7082,E
Arrivée : Orléans= Lat: 47.9027,N      Long: 1.9057,E
Sans nous aider de cartes, nous allons de suite déduire de ces coordonnées plusieurs éléments simples qui seront importants pour la suite des calculs:
- La latitude d’Orléans est plus élevée que celle de Tours, notre mobile devra se diriger plutôt vers le Nord...
- La Longitude d’Orléans est plus élevée que celle de Tours,notre mobile devra se diriger plutôt vers l'Est.
Donc, le cap aura donc une tendance de cap de base "Nord-Est".

- Les deux Latitudes ont le même sens : Nord, Nord.
- Les deux Longitudes ont le même sens : Est, Est.
(  Si nous envoyions notre mobile à Cochabamba, en Bolivie,
Coordonnées : 17° 23′ 00″ sud, 66° 10′ 00″ ouest,
La destination du mobile se situerait alors dans l'hémisphère Sud ainsi que dans les coordonnées West du méridien de Greenwich...)
Nous verrons plus loin que cette notion de "Sens" influe sur le calcul des dimensions du triangle-rectangle.

Nous allons maintenant définir le cap vrai avec beaucoup plus de précision.
 Si vous regardez une mappemonde, vous constaterez que les "tranches" des longitudes se resserrent de plus en plus aux pôles tandis que les "disques" des latitudes, eux, restent a égales distances entre eux...
la distance en Km d'1 degré de longitude dépend a quelle latitude il est calculé:
A l'équateur, par exemple, ou la latitude est à 0°, 1 degré de longitude représente 200, 335 Km...
A Dunkerque, nous somme déjà à 51° Nord de latitude et 1 degré de longitude ne représente plus que 69,740 Km.
A  Perpignan, nous somme  à 42° Nord de latitude et 1 degré de longitude représente 80,850 Km

Nous décidons donc que, pour la France, 1 degré de longitude correspond à 75,84 Km (75840 m).
En ce qui concerne la latitude, elle ne change pratiquement pas et nous prendrons donc une moyenne de 111,73 Km (111730 m) par degré.

Consultez le site:
http://www.francetopo.fr/
Il posséde une règle trés pratique pour mesurer des distances...


Pour matérialiser les dimensions de notre rectangle, il faut:
 -Soustraire la plus grande valeur des Latitudes de la plus petite:
47.9027 - 47.3771 = 0,5256
-Multiplier la valeur trouvée par le nombre de mètres que représente 1° de latitude:
0,5256 (°)   x   111730 m= 58725 m
-Soustraire la plus grande valeur des Longitudes de la plus petite:
1.9057 - 0.7082 = 1,1975
-Multiplier la valeur trouvée par le nombre de Kilomètres que représente 1° de longitude:
1,1975(°)   x   75840 m = 90818 m
Note: Comme nous le verrons plus loin, ce calcul n'est valable que si le "sens" des coordonnées sont identiques...

Notre rectangle fait donc  58725 m  sur 90818 m

Plus haut, nous avons déjà constaté que notre cap avait une tendance de base "Nord-Est".
La valeur de la longitude (en degrés) étant plus grande que la valeur de la latitude (notre rectangle est plutôt "horizontal", ou plat...), on en déduit qu'on est dans une tendance de cap "Nord, Est, Est".
(Si la valeur de la longitude (en degrés) était plus petite que la valeur de la latitude (notre rectangle serait alors plutôt "vertical", ou haut...), on en déduirait qu'on est dans une tendance de cap "Nord, Nord, Est".
Nous verrons leur importance un peu plus bas...

Attention de ne pas confondre les degrés avec les Kilomètres!
- On définit les dimensions du triangle-rectangle avec des unités métriques ( mètres ou kilomètres)
-On définit les "tendances de cap" avec des degrés.

En reprenant donc les dimensions de notre rectangle qui fait 58725 m sur 90818 m, nous posons l'opération suivante:100 * 58725 / 90818   = 64,66 %

Rappel : Dans ce principe de calcul de pente en pourcentage, le résultat de l’opération fait que la pente ne peut jamais représenter plus de 100% et donc ne jamais présenter un nombre de degrés supérieur a 45°. 

On trouve dans la table de conversion.
  64,66% = ~ 33°.
Comme on est dans une tendance de cap "Nord, Est, Est", et comme on le verra plus loin, il nous faut retrancher cette valeur a 90°, ce qui donne:
90° - 33 = Cap = ~ 57°
________________________________________________________________________________

Pourquoi les "tendances de cap" sont t'elles importantes?

- Par ce qu'avec le principe de calcul de pente, nous avons vu qu'on ne pouvais pas mesurer des angles supérieurs a 45°.
Cela n'est pas un problème, car, comme nous l'avons vu plus haut, il suffit de vérifier la hauteur et la largeur (en degrés et non en Km) du rectangle pour en déduire la tendance de cap, puis, selon cette tendance, d'ajouter ou retrancher les degrés (multiples de 45) si nécessaire, afin de reconstituer le cap vrai.
Des dessins valent mieux qu'un tas d’explications:
Le cercle orange représente un rapporteur complet (360°) dont le "0" est toujours orienté vers le Nord.
 La Latitude > Longitude  && tendance N,N,E  (le rectangle est vertical )=
 Cap = Pente en degrés.
(comme une direction Paris-Laon, par exemple)
 La tendance "Nord,Nord,Est" est la seule tendance où nous n'avons pas a ajouter ou retrancher de degrés complémentaires a la fin du calcul!
 ______________________________________________________________________

 La Latitude < Longitude  && tendance N,E,E (le rectangle est horizontal)=
Cap = 90 - Pente en degré.
 (comme une direction Paris-Bruxelles, par exemple)
______________________________________________________________________

 La Latitude > Longitude && tendance S,S,E (le rectangle est vertical )=
 Cap = 180 - Pente en degré.
 (comme une direction Paris-Lyon, par exemple)
 ______________________________________________________________________
La Latitude < Longitude && tendance S,E,E (le rectangle est horizontal)=
Cap = 90 + Pente en degré.
 (comme une direction Paris-Monaco, par exemple)
 ______________________________________________________________________
La Latitude > Longitude && tendance S,S,W (le rectangle est vertical )=
Cap = 180 + Pente en degré.
(comme une direction Paris-Bayonne, par exemple)
 ______________________________________________________________________
La Latitude < Longitude  && tendance S,W,W (le rectangle est horizontal)=
Cap = 270 - Pente en degré.
(comme une direction Paris-La Rochelle, par exemple)
 ______________________________________________________________________
La Latitude > Longitude && tendance N,N,W (le rectangle est vertical )=
Cap = 360 - Pente en degré.
(comme une direction Paris-Dieppe, par exemple)
 ______________________________________________________________________

La Latitude < Longitude && tendance N,W,W (le rectangle est horizontal)=
Cap = 270 +Pente en degré.
(comme une direction Paris-Caen, par exemple)
 ______________________________________________________________________

Donc, pour résumer:
 tendance N,N,E  : Cap = Pente en degrés.
 tendance N,E,E  : Cap = 90 - Pente en degré.
 tendance S,S,E  : Cap = 180 - Pente en degré.
 tendance S,E,E  : Cap = 90 + Pente en degré.
 tendance S,S,W  : Cap = 180 + Pente en degré.
 tendance S,W,W : Cap = 270 - Pente en degré.
 tendance N,N,W:  Cap = 360 - Pente en degré.
 tendance N,W,W:  Cap = 270 +Pente en degré.


Essayez de trouver une petite carte de France ou du Monde avec les Latitudes & les Longitudes et entrainez-vous à dessiner les différents triangles-rectangles avec les 8 tendances possible, afin de vous familiariser avec le principe.
Fixez votre carte sur un mur et avec des punaises et de la ficelle, matérialisez les triangles-rectangles puis tirez les diagonales avec du fil de couleur différents...

Ci-dessous le tableau de conversion a entrer dans la mémoire de l'Arduino.
Il a été calculé avec des pas de 3°, de 0° a 45°,( ce qui représente 17 valeurs) ce qui est suffisant pour calculer un cap et ne pas surcharger la mémoire du module Arduino.

degrés     pourcentage
0             0
3             5.241
6             10.51
9             15.838
12           21.256
15          26.795   
18          32.492
21          38.386
24          44.523
27          50.953
30          57.735
33          64.941
36         72.654
39         80.978
42         90.04
45          100
On peut constater que plus les triangles-rectangles sont plats (en hauteur ou en largeur), plus on s'approche des coordonnées de base : Nord,Sud,Est,West.


Liens utiles pour vérifier les calculs de conversion de pourcents en degrés:
http://www.skitour.fr/divers/conversion-pourcentage-degre.php

Vous pourriez penser qu'une précision de 3° n'est pas "top", mais en fait, ce n'est pas du tout un problème. Le mobile recalcule son cap à intervalles régulier et plus il s'approche de sa destination, plus le cap est précis.
Si vous en avez le courage, tentez l’expérience en essayant de vous rendre a pied dans un endroit prés de chez vous, (environ 1 Km) muni juste d'un rapporteur et en vous donnant une marge d'erreur approximative de trois degrés. Vous recalculez votre position tous les dix mètres environ. Vous constaterez que vous arriverez facilement et parfaitement a destination.

Pourquoi le "Sens" a t'il une importance?

Comme nous l'avons vu plus haut, il est important de savoir si notre destination se trouve dans un autre hémisphère que celui de départ.
Notre GPS M6 MV2 nous indique "N","S","E" W", et non pas des valeurs négatives, comme d'autres modèles le font.
Cela est important pour le calcul des dimensions de notre triangle-rectangle.
En effet, dans ce cas, par exemple, une des dimension de notre rectangle peut faire -5° (soit 5° Nord) sur 5°(soit 5° Sud).
Vous l’aurez compris; dans ce cas, il faut additionner et non pas soustraire les degrés pour reconstituer les dimensions de notre rectangle, tant que notre GPS nous indique un sens différent de route et de destination...

Ha les maths et les nombres relatifs! (voyez plus de détails sur internet...)
Sinon, découpez un petit rectangle de quelques degrés que vous baladerez sur votre planisphère pour appréhender et comprendre le principe...
Sinon, cela ne change rien au reste du calcul.

Lien utile pour vérifier les calculs de cap:

 https://www.sunearthtools.com/fr/tools/distance.php

J'ai testé cette théorie avec l'IDE Processing, qui s'apparente a l'IDE Arduino, afin de ne pas surcharger le microcontrôleur avec des tests répétitifs...
Dans une autre partie, nous allons mettre tous les calculs de façon assimilable par l'Arduino.