Problème de précision en trigonométrie avec un Arduino Uno

a marqué ce sujet comme résolu.

Bonjour !

Pour le PPE de Terminale, mon groupe doit s'occuper d'un phare qui se réglerait automatiquement en fonction du chargement de la voiture pour ne pas éblouir les autres passagers.

Seulement l'Arduino Uno pose quelques problèmes de précision : en effet les fonctions trigonométriques nécessite un angle en radians en float, or l'angle calculé varie si peu qu'après conversion il vaut systématiquement 0.03 radians. J'ai pensé à mettre des doubles, mais sur un Uno ça change rien.

Je voudrais donc avoir un truc pour améliorer la précision des calculs.

Voici le code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<Servo.h>

Servo servo;
float hauteurAr, hauteurAv, incliVoiture, positionPhare, startTime, endTime, correctPhare;
const int trigH1=10, echoH1=11, trigH2=12, echoH2=13;  //définition des ports utilisés

void setup(){
  pinMode(trigH1, OUTPUT);
  pinMode(trigH2, OUTPUT);
  digitalWrite(trigH1, LOW);
  digitalWrite(trigH2, LOW);
  pinMode(echoH1, INPUT);
  pinMode(echoH2, INPUT);

  servo.attach(7);

  hauteurAv = distanceH(1);     //Récupération distance H1 en cm
  hauteurAr = distanceH(2);     //......................H2......

  Serial.begin(9600);
}

void loop(){
  hauteurAv = distanceH(1);     //Récupération distance H1 en cm
  hauteurAr = distanceH(2);     //......................H2......

  incliVoiture = atan(2580/(hauteurAr-hauteurAv));                                      //calcul donnant en sortie des degrés
  correctPhare = atan((40842*(1-sin(incliVoiture)))/(hauteurAv-acos(incliVoiture)));    //calcul nécessitant des radians
  servo.write(correctPhare);
  delay(1000);

}

int distanceH(int x){                          //récupération de la distance (en cm) détecté par le capteur choisi
  int distance;
  if (x == 1){                                 //Pour H1
    digitalWrite(trigH1, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigH1, LOW);
    distance = (pulseIn(echoH1, HIGH))/58;
  } else if (x == 2){                          //Pour H2
    digitalWrite(trigH2, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigH2, LOW);
    distance = (pulseIn(echoH2, HIGH))/58;
  }
  return distance;
}

Merci.

+0 -0

Seulement l'Arduino Uno pose quelques problèmes de précision : en effet les fonctions trigonométriques nécessite un angle en radians en float, or l'angle calculé varie si peu qu'après conversion il vaut systématiquement 0.03 radians. J'ai pensé à mettre des doubles, mais sur un Uno ça change rien.

Je voudrais donc avoir un truc pour améliorer la précision des calculs.

il existe une règle trogonométrique qui dit que $cos(2*x) = cos^2(x) - sin^2(x)$. Si tu utilises non pas l'angle mais 2*l'angle ça va multiplier la différence par 2 aussi et donc elle sera plus grande.

Pour le reste je ne comprends pas pourquoi atan donnerait un résultat en degrès, de base atan est en radian.

La ligne 28 elle sent mauvais… Le calcul m'a l'air assez gros et meriterais bien d'etre decompose. Perso je ferais la division en premier puis apres la multiplication (histoire d'eviter un eventuel overflow).

Eskimon

C'est en effet une cause très possible, en multipliant par 40842, tu multiplies par $2^15$ que tu divises par un nombre potentiellement de l'ordre de $2^(-1)$. Tu viens donc de casser la moitié de la dynamique des float si tu fais la division en second.

Voici quelque chose qui peut être sympa à décomposer :

1
2
3
4
5
6
7
incliVoiture = atan(2580/(hauteurAr-hauteurAv));                                      
//calcul donnant en sortie des degrés
float y_proj = (1-sin(incliVoiture)); // ODG = 2^-1
float x_proj = (hauteurAv-acos(incliVoiture)); ODG = ODG(hauteurAv)
float tan_proj = y_proj/x_proj; //ODG = 2^(-1-log(hauteurAv))
tan_proj *= 40842; // ODG = 2^(-1-log(hauteurAv) + 15)
correctPhare = atan(tan_proj); //calcul nécessitant des radians

Le atan donne des degrés parce que quand tu fais de la trigo avec des millimètres, tu obtiens des angles en degrés… je ferais les tests au plus vite, c'est à dire pas avant jeudi y'a des chances…

Merci de vos réponses.

PS : artragis, c'est quoi ODG :/ ?

+0 -0

PS : artragis, c'est quoi ODG :/ ?

Ordre de grandeur.

Le atan donne des degrés parce que quand tu fais de la trigo avec des millimètres, tu obtiens des angles en degrés…

Hein? moi y'en a pas comprendre. Donnons que je fais un triangle rectangle de côté AB = 3mm, AC = 4mm, BC = 5mm.

$$tan(\widehat{ABC}) = \frac{AC}{AB} \iff \widehat{ABC} = atan(\frac{4}{3}) = 0,927295218 rad$$

Pourtant j'ai travaillé en millimètre. Le "rad" est une unité muette, tant que tu es équilibré en haut et en bas c'est bon. De base atan donne un résultat en gradian, pas en degré.

+0 -0

Déjà je te conseille fortement de tout diviser par 1000 pour jouer en mètre mais surtout réduire les ordres de grandeur et augmenter la précision (il y a autant de nombre flottant entre 0 et 1 qu'entre 1 et le maximum). Ensuite, tu as plusieurs problèmes qui sont loin de venir des radians ou pas.

  • arctan demande sémantiquement un nombre réel et te renvoie un angle compris entre -pi/2 et p/2 rad soit environ 1.57.
  • arccos te demande le résultat d'un cosinus compris entre -1 et 1 et te renvoie un angle entre 0 et pi.

Toi tu fais arccos d'un angle, ça va pas. Ce que tu veux faire c'est cos, pas acos.

Si je divise par 1000, le manque de précision des float rends inutile ce programme (l'angle varie de 2 degrés grand maximum je crois…), donc si le phare bouge pas… j'ai pas envie de foirer tout le PPE (au moins un de mes camarades me casserait la tête…).

Ensuite, la formule viens d'un prof de maths, mais je me suis peut-être gouré en recopiant, donc je vous dirais ça jeudi.

+0 -0

Si je divise par 1000, le manque de précision des float rends inutile ce programme

si tu divises par 1000, les flottants seront 10 000 fois plus précis. Peut être que diviser par 1000 est trop, mais je pense que tu gagnerais à au moins diviser par 10 ou 100 pour regagner de la précision du côté des floats.

Et franchement, tu peux largement corriger ta formule en mettant cos, vérifier que ça fonctionne puis redemander à ton prof.

Tu trouveras néanmoins déjà un indice dans la formule utilisée: au numérateur tu utilises sin alors que tu utilises acos au dénominateur.

Hum j'ai vérifié sur le truc que m'a filé le prof, c'est bien acos, mais sur mon fichier, j'avais trouvé cos…

Je ne peux pas tester le programme arduino avant jeudi, donc je ferais tout en même temps, en attendant, voici le lien du dossier du PPE, PPE.ggb c'est mon travail avec les vraies mesures, mais pas terminé, et le phare(1).ggb c'est le fichier du prof, avec les formules finales (normalement) mais des valeurs bidons. Et dans le dossier PPE, le PPE.ino c'est le programme Arduino.

+0 -0

C'est marrant pour mon PPE on avait exactement le même genre de problème: la triangulation avec arduino c'est quand même plus galère que ça en à l'air. Du coup nous on a changé de solution technique.

Est-ce qu'ainsi ça marche:

A hauteur roue avant B hauteur roue arriere F hauteur phare F' projeté F sol E target du phare (on conné donc EF')

i inclinaison voiture

au début pareil $$i = tan ((b - a)/AB)$$

hauteur phare $$FF' = a - FA/sin(i)$$

angle voulu $$e = tan(EF'/FF')$$

angle reel $$r = pi/2 - e$$

difference $$\Delta = e - r$$

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte