-------------- =====2023===== testé en salle e02 sous windows S:\commun\BUT3\arduino-1.8.19-windows.zip dézipper vers D:\arduino-1.8.19-windows la version 2 fonctionne mais le plotter série zoom beaucoup trop Les drivers pour CH340 ne sont pas installés sous windows-> il faut des vrais arduino accès aux disques étudiants (ne marche pas pour ait ali) P:\etu-new\s5aii\s5aii1\besson_jules ====tentative windows==== https://bvdp.inetdoc.net/wiki/doku.php?id=tpoml1 cd P:\.... python -m venv .venv .\.venv\Scripts\activate pip install -U pip pip3 install matplotlib tk serial pyserial https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/serialhistoplot2022.py python serialhistoplot2022.py -t1 il faut fermer les applis qui utilise le port série arduino https://stackoverflow.com/questions/44056846/how-to-read-and-write-from-a-com-port-using-pyserial https://pyserial.readthedocs.io/en/latest/pyserial.html pip show serial Name: serial Version: 0.0.97 Summary: A framework for serializing/deserializing JSON/YAML/XML into python class instances and vice versa Home-page: https://bitbucket.com/davebelais/serial.git Author: David Belais Author-email: david@belais.me License: MIT Location: c:\users\bertrand.vandeportae\appdata\roaming\python\python39\site-packages Requires: future, iso8601, pyyaml Required-by: pip list pyserial 3.5 serial 0.0.97 pip uninstall serial Successfully uninstalled serial-0.0.97 python -m pip install pyserial Defaulting to user installation because normal site-packages is not writeable Requirement already satisfied: pyserial in c:\users\bertrand.vandeportae\appdata\roaming\python\python39\site-packages (3.5) vscode open folder install recommended extension=> intellisense https://pyserial.readthedocs.io/en/latest/appendix.html https://stackoverflow.com/questions/44056846/how-to-read-and-write-from-a-com-port-using-pyserial ====2022==== -------------------------------------------------- script dans home de bvandepo pour installer le nécéssaire au TP: #!/bin/bash sudo dpkg --configure -a sudo apt update #attention ne fonctionne pas sans que l'on tape explicitement O #paquet python-serial pour miniterm.py et serial sudo apt install -y python3-pip python3-tk python-serial idle3 sudo pip3 install tk serial pyserial sudo pip3 install matplotlib==3.0.3 python3 -c "import matplotlib; print(\"version de matplotlib installée: \" +str(matplotlib.__version__))" #IP objets connectés de 10.6.15.191 à 10.6.15.204 # nouvelle version de matplotlib installée par défaut: '3.4.3' or il faut la version précédente qui fonctionne '3.0.3' pour installer des versions spécifiques de paquets: https://www.marsja.se/pip-install-specific-version-of-python-package/ Pc de la salle objets connectés en version stretch LTS jusqu'à juin 2022: https://www.debian.org/releases/stretch/index.fr.html Certaines machines ont commencé la migration automatique vers buster: https://www.debian.org/releases/buster/index.fr.html apt/sources.list doit être comme: https://debian-facile.org/doc:systeme:apt:sources.list:buster mais sur la machine E10 qui a commencé à migrer il y a un hybride: deb http://ftp.fr.debian.org/debian/ buster main contrib non-free deb http://ftp.fr.debian.org/debian/ stretch main contrib non-free #deb-src http://ftp.fr.debian.org/debian/ stretch main contrib non-free deb http://security.debian.org/debian-security stretch/updates main contrib non-free #deb-src http://security.debian.org/debian-security stretch/updates main contrib non-free deb http://www.deb-multimedia.org stretch main non-free TODO: Il faudra que j'adapte le code python à une version plus récente de matplotlib, la fonction hist notamment pose problème: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.hist.html?highlight=matplotlib%20hist Virtual Env: problème de montage CIFS qui par defaut ne maintient pas les liens symboliques: sudo gedit /etc/security/pam_mount.conf.xml il faut ajouter ,mfsymlinks à la fin de chacune de ces lignes: l'environnement virtuel fonctionne avec opencv mais c'est très lent d'importer la librairie, probablement à cause du montage réseau.... ====Carte caméra 12 Mpixels pour raspberry pi==== https://astro-pi.org/about/the-computers https://www.raspberrypi.org/products/raspberry-pi-high-quality-camera/ ====Matériel pour le TP==== https://fr.aliexpress.com/item/1005001425154668.html?src=google&src=google&albch=shopping&acnt=248-630-5778&isdl=y&slnk=&plac=&mtctp=&albbt=Google_7_shopping&aff_platform=google&aff_short_key=UneMJZVf&albagn=888888&isSmbAutoCall=false&needSmbHouyi=false&albcp=10191220517&albag=107473525088&trgt=743612850874&crea=fr1005001425154668&netw=u&device=c&albpg=743612850874&albpd=fr1005001425154668&gclid=Cj0KCQjwqKuKBhCxARIsACf4XuGKaOSxW7oPYgbPddRi-9ssMPWqOq66-x7CX8awL7DE0LuJgohO3ewaAnqCEALw_wcB&gclsrc=aw.ds https://fr.aliexpress.com/item/32711652964.html?src=google&src=google&memo1=freelisting&albch=shopping&acnt=248-630-5778&isdl=y&slnk=&plac=&mtctp=&albbt=Google_7_shopping&aff_platform=google&aff_short_key=UneMJZVf&albagn=888888&isSmbAutoCall=false&needSmbHouyi=false&albcp=10191220526&albag=107473525328&trgt=539263010115&crea=fr32711652964&netw=u&device=c&albpg=539263010115&albpd=fr32711652964&gclid=Cj0KCQjwqKuKBhCxARIsACf4XuGhsA8wR0tlhdUAL65vkYbEISxB3BpmurHk1BgeJ3EEpouhsYGzWWwaAgOZEALw_wcB&gclsrc=aw.ds =====TP Capteur 2: Jauge de contrainte pour la mesure de force===== === Objectifs === * Utilisation d'un convertisseur Analogique vers Numérique différentiel et d'un pont de Wheatstone * Caractérisation et Étalonnage du capteur * Filtrage des données du capteur * Utilisation du capteur pour reconnaître des objets === Ressources documentaires === https://fr.wikipedia.org/wiki/Jauge_de_d%C3%A9formation https://en.wikipedia.org/wiki/Strain_gauge https://en.wikipedia.org/wiki/Wheatstone_bridge https://fr.wikipedia.org/wiki/Fonction_de_r%C3%A9partition https://fr.wikipedia.org/wiki/Variable_al%C3%A9atoire_%C3%A0_densit%C3%A9 https://fr.wikipedia.org/wiki/Loi_normale http://www.bibmath.net/dico/index.php?action=affiche&quoi=./r/reglin.html http://www.bibmath.net/dico/index.php?action=affiche&quoi=./r/reglin.html https://euler.ac-versailles.fr/baseeuler/lexique/notion.jsp?id=29 https://euler.ac-versailles.fr/baseeuler/lexique/notion.jsp?id=5 https://euler.ac-versailles.fr/baseeuler/lexique/notion.jsp?id=26 http://serge.mehl.free.fr/anx/meth_carr.html https://fr.wikipedia.org/wiki/%C3%89cart_type#Intervalle_de_fluctuation === Présentation du matériel=== {{https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/balance_et_arduino.jpg}} {{https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/balance.jpg}} datasheet du composant convertisseur HX711: https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/hx711_english_p1-5.pdf Schéma de connexion du composant convertisseur HX711: {{https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/vue_interne_hx711.png}} explication des connexions: * **A1** de l'Arduino (utilisée en tant qu'entrée numérique) connéctée à **DOUT** du HX711 via le fil jaune * **A0** de l'Arduino (utilisée en tant que sortiee numérique) connéctée à **SCK** du HX711 via le fil orange * **GND** de l'Arduino connecté à la masse du HX711 via le fil marron * **5V** de l'Arduino connecté à l'alimentation du HX711 via le fil rouge {{https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/connectionhx711.png}} Les balances sont numérotées, il est important que vous repreniez la même balance à chaque séance! =====Exercice 1: Récupération des données brutes issues du convertisseur===== Une version allégée de la librairie HX711 permettant à l'Arduino d'avoir accès aux échantillons convertis par le convertisseur différentiel HX711 est fournie dans le squelette d'application suivant: #include #define SLAVE_ADDR_8574_A 0x3e #define SLAVE_ADDR_8574_B 0x3f #include "Arduino.h" ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Code issu de la librairie HX711 pour Arduino de https://github.com/bogde/HX711.git class HX711 { private: byte PD_SCK; // Power Down and Serial Clock Input Pin byte DOUT; // Serial Data Output Pin byte GAIN; // amplification factor public: // define clock and data pin, channel, and gain factor // channel selection is made by passing the appropriate gain: 128 or 64 for channel A, 32 for channel B // gain: 128 or 64 for channel A; channel B works with 32 gain factor only HX711(byte dout, byte pd_sck, byte gain = 128){ begin(dout, pd_sck, gain); } HX711() { }; virtual ~HX711() {} // Allows to set the pins and gain later than in the constructor ////////////////////////////// void begin(byte dout, byte pd_sck, byte gain = 128){ PD_SCK = pd_sck; DOUT = dout; pinMode(PD_SCK, OUTPUT); pinMode(DOUT, INPUT); set_gain(gain); } ////////////////////////////// // check if HX711 is ready // from the datasheet: When output data is not ready for retrieval, digital output pin DOUT is high. Serial clock // input PD_SCK should be low. When DOUT goes to low, it indicates data is ready for retrieval. bool is_ready() { return digitalRead(DOUT) == LOW; } // set the gain factor; takes effect only after a call to read() // channel A can be set for a 128 or 64 gain; channel B has a fixed 32 gain // depending on the parameter, the channel is also set to either A or B void set_gain(byte gain = 128){ switch (gain) { case 128: GAIN = 1; break; // channel A, gain factor 128 case 64: GAIN = 3; break; // channel A, gain factor 64 case 32: GAIN = 2; break; // channel B, gain factor 32 } digitalWrite(PD_SCK, LOW); read(); } ////////////////////////////// // waits for the chip to be ready and returns a reading long read() { // wait for the chip to become ready while (!is_ready()) { // Will do nothing on Arduino but prevent resets of ESP8266 (Watchdog Issue) yield(); } long value = 0; uint8_t data[4] = { 0,0,0,0 }; // pulse the clock pin 24 times to read the data // https://www.arduino.cc/reference/en/language/functions/advanced-io/shiftin/ data[2] = shiftIn(DOUT, PD_SCK, MSBFIRST); data[1] = shiftIn(DOUT, PD_SCK, MSBFIRST); data[0] = shiftIn(DOUT, PD_SCK, MSBFIRST); // set the channel and the gain factor for the next reading using the clock pin for (unsigned int i = 0; i < GAIN; i++) { digitalWrite(PD_SCK, HIGH); digitalWrite(PD_SCK, LOW); } // Replicate the most significant bit to pad out a 32-bit signed integer if ( (data[2] & 0x80) !=0) { data[3] = 0xFF; } else { data[3] = 0x00; } // Construct a 32-bit signed integer value = ( static_cast(data[3]) << 24 | static_cast(data[2]) << 16 | static_cast(data[1]) << 8 | static_cast(data[0]) ); return value; } }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// HX711 scale(A1, A0); // DOUT, SCK #include #include ////////////////////////////////////////// bool detecteFrontMontantPortB(unsigned char numerobit) { static char ancien[8]={-1,-1,-1,-1,-1,-1,-1,-1}; bool ret=false; unsigned char pb=LirePortB(); if (ancien[numerobit]==-1){ for (int i=0;i<8;i++) ancien[i]=(pb>>i)&1; }else{ char actuel=(pb>>numerobit)&1; if ( (actuel==1) && (ancien[numerobit]==0)) ret=true; else ret=false; ancien[numerobit]=actuel; } return ret; } ////////////////////////////////////////// unsigned char LirePortB() { Wire.requestFrom((byte)SLAVE_ADDR_8574_B, (byte)1);// demande la lecture d'1 octet depuis l'adresse du pérpiphérique if (Wire.available()==1) //si l'octet est disponible return Wire.read(); // lire l'octet else return 0; } ////////////////////////////////////////// void EcrirePortA(unsigned char valeur) //cette fonction pilote les 8 leds avec la valeur 8bits fournie dans le paramètre valeur { Wire.beginTransmission((byte)SLAVE_ADDR_8574_A);//démarre la transmission avec l'adresse du pérpiphérique Wire.write(~(byte)valeur); //envoie la donnée complémentée car les LEDs s'allument à l'état 0 Wire.endTransmission(); } ////////////////////////////////////////// void setup() { Serial.begin(9600); Serial.println("reset"); //scale.set_scale(250.f); //// this value is obtained by calibrating the scale with known weights //scale.tare(); Wire.begin(); // joindre le bus i2c en tant que maître delay(100); Wire.beginTransmission((byte)SLAVE_ADDR_8574_B); Wire.write((byte)0xff); Wire.endTransmission();//configure le composant B en entrée pinMode(3,OUTPUT); } ////////////////////////////////////////// void loop() { } {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Lire et essayer de comprendre les différentes parties de ce squelette d'application. Demander à l'enseignant en cas de difficulté, APRES avoir tout lu. Compléter la fonction **loop()** pour déclencher la lecture d'un échantillon et écrire en ASCII sa valeur sur le port série, suivi d'un retour à la ligne. Programmer l'Arduino et vérifier dans la console série que l'affichage des valeurs fluctue lorsque vous appuyez (ou tirez) légèrement sur la balance. Solution: long val=scale.read(); Serial.println(val); {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Ajouter dans la fonction **loop()** le nécessaire (en vous aidant des fonctions fournies dans le squelette **bool detecteFrontMontantPortB(unsigned char numerobit)** et **unsigned char LirePortB()**) pour que l'échantillon ne soit acquis que: * si le bit 7 du port d'entrée présente un front montant (ceci permet d'obtenir un unique échantillon) * ou si le bit 6 du port d'entrée est à l'état haut (ceci permet d'obtenir des échantillons en continu) De plus, en cas d'une détection d'un front montant sur le bit 0 du port d'entrée, votre programme devra envoyer la chaîne **"reset"** suivi d'un retour à la ligne sur le port série. Les numéros de bits du port d'entrée à utiliser sont ceux inscrits sur le circuit imprimé, et non pas sur les interrupteurs. Il en est de même pour les niveaux logiques de ces entrées. Vérifier sur la console que les fonctionnalités demandées sont bien ajoutées . Solution: if (detecteFrontMontantPortB(0) ) Serial.println("reset"); if (detecteFrontMontantPortB(7) || (((LirePortB()>>6)&1)!=0) ) { long val=scale.read(); Serial.println(val); } {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Lancer l'outils de visualisation de courbes d'arduino (via Outils->Traceur série), et visualiser le signal issus du capteur en fonction du temps (positionner l'interrupteur 6 en position 1 pour faire l'acquisition en continu). Vous devriez voir un tracé tel que celui ci si vous exercez des pressions variables sur le capteur: {{https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/chronogramme.png}} {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Piloter le barregraphe pour allumer une seule LED indiquant une valeur de 0 à 7 qui est fonction affine par rapport au données brutes du capteur dont vous devrez mémoriser les valeurs minimales et maximales. Ainsi pour la valeur minimum mesurée par le capteur, vous devrez allumer la led 0 et pour la valeur maximum mesurée par le capteur, vous devrez allumer la led 7. Solution: static float valmin=20000000; static float valmax=-20000000; if (val>valmax) valmax=val; if (val {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Faire valider vos réponses à l'enseignant. =====Exercice 2: Caractérisation et Étalonnage du capteur===== Pour pouvoir exploiter les données brutes issues du convertisseur afin d'obtenir une masse (pour rappel le capteur utilisée est une jauge de contrainte, qui mesure une force, telle un poids et non pas une masse...), il est nécessaire de procéder à l'étalonnage. L'étalonnage consiste en l'estimation des paramètres d'un modèle simplifié du capteur et il peut être réalisé en plusieurs étapes: - Tout d'abord nous allons procéder à la collecte de données en relevant les valeurs fournies par le capteur pour différents 'étalons', qui sont des masses de référence connues. Dans le cadre du TP, les étalons seront divers objets dont la masse aura été mesurée à l'aide d'une balance de référence. - Ensuite, nous tracerons ces données pour les analyser et décider d'un modèle de fonction reliant les données brutes aux grandeurs physiques à mesurer. Nous utiliserons donc ici un modèle dit "paramétrique". - Puis, nous estimerons les paramètres de cette fonction, tout d'abord en utilisant un nombre minimal de données. Dans un second temps, cette fonction sera estimée en utilisant plus de données afin d'améliorer la précision. - Finalement nous modifierons le programme de l'exercice précédent pour qu'il fournisse une mesure en gramme au lieu de la valeur brute, puis nous analyserons les données fournies afin de caractériser le capteur en terme de précision/répétabilité/hystérésis etc. Si la carte convertisseur de votre balance dispose d'un cavalier, assurez vous qu'il est en position fermée pour cette partie de l'exercice. Pour procéder à la collecte et à l'analyse des données, une application fonctionnant sur le PC est fournie. Cette application décode les chaines de caractères émises par l'Arduino (sur le port /dev/ttyUSB0 ou /dev/ttyACM0), chaque ligne correspondant à une valeur numérique (avec éventuellement une partie fractionnaire) codée en ASCII, et terminée par un retour à la ligne. Vous devriez remarquer que c'est le format qui vous a été demandé à l'exercice précédent, votre application Arduino doit donc être capable de communiquer directement avec cette nouvelle application. ----------------------------------------------------------- Version avec environnement virtuel: sudo apt-get install -y python3-venv echo commence cd ~/ mkdir -p plot cd plot python3 -m venv env source env/bin/activate pip install -U pip pip3 install matplotlib tk serial pyserial wget --no-check-certificate https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/serialhistoplot2022.py -O ./serialhistoplot.py python3 ~/plot/serialhistoplot.py -t1 echo fini Pour ouvrir idle3 dans l'environnement virtuel: cd ~/vision_lprorob source env/bin/activate python3 -m idlelib.idle ----------------------------------------------------------- Pour récupérer l'application, copier coller dans un terminal: echo commence cd ~/ mkdir -p plot cd plot rm serialhistoplot.py* wget --no-check-certificate https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/serialhistoplot2022.py -O ./serialhistoplot.py chmod a+x serialhistoplot.py echo fini Pour lancer l'application afin qu'elle affiche les données reçues depuis l'arduino, vous devrez plus tard copier coller dans un terminal alors que les fenêtres du moniteur et du traceur série Arduino sont fermées.: ~/plot/serialhistoplot.py Dans un premier temps, nous allons tester cette application en utilisant des données issues non pas de l'arduino mais de l'application elle même, générées à l'aide de différents générateurs aléatoires. Pour cela, copier coller dans un terminal la ligne suivante: ~/plot/serialhistoplot.py -t1 Vous devriez voir l'affichage se stabiliser vers: {{https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/histogramme.png}} et dans la console obtenir les informations statistiques: ... nb mesures: 1342 cadence: 90.69 moyenne: 101.134 écart type: 30.678 nb mesures: 1343 cadence: 90.69 moyenne: 101.127 écart type: 30.667 nb mesures: 1344 cadence: 90.70 moyenne: 101.135 écart type: 30.657 nb mesures: 1345 cadence: 90.70 moyenne: 101.115 écart type: 30.655 nb mesures: 1346 cadence: 90.70 moyenne: 101.135 écart type: 30.652 ... Les bâtons bleus correspondent à un histogramme ( https://fr.wikipedia.org/wiki/Histogramme ). La notion d'histogramme sera largement réutilisée plus tard dans l'année lorsque nous traiterons des images: https://perso.esiee.fr/~perretb/I5FM/TAI/histogramme/index.html L'histogramme affiché par l'application fournie contient au maximum 10 intervalles de valeurs (axe horizontal) pour lesquels la fréquence relative d’occurrence est représentée (sur l'axe vertical). Ceci permet d'avoir une représentation graphique de la distribution des valeurs et de les interpréter ( https://fr.wikipedia.org/wiki/Histogramme#Interpr%C3%A9tation ). L'application fournie calcule la moyenne et l'écart type ( https://fr.wikipedia.org/wiki/%C3%89cart_type#Intervalle_de_fluctuation ) et affiche fonction appelée "densité de probabilité de la loi normale" ( https://fr.wikipedia.org/wiki/Loi_normale ) correspondant à ces moyennes et écarts types. Cet affichage permet de comparer l'histogramme issu des données avec une distribution normale (aussi appelée gaussienne) et de vérifier si les données suivent ou non une distribution normale. Pour l'exemple précédent, l'histogramme et la fonction de densité de probabilité sont assez proches, indiquant que les échantillons suivent une distribution de loi normale. Pour fermer l'application, cliquer sur la croix fermant la fenêtre ou taper CTRL+C dans la console ayant servi à la lancer. {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Tester maintenant cette application sur un autre générateur aléatoire. Pour cela, copier coller dans un terminal: ~/plot/serialhistoplot.py -t2 Faites de même avec: ~/plot/serialhistoplot.py -t5 Interpréter l'affichage et conclure sur les distributions des valeurs aléatoires générées. Interrogez vous sur le sens donné à l'écart type sur la page https://fr.wikipedia.org/wiki/%C3%89cart_type#Intervalle_de_fluctuation pour une distribution ne suivant pas la loi normale. ====Acquisition d'échantillons issus du capteur==== Alors que la carte Arduino a été chargée avec le programme de l'exercice précédent, lancer l'application sur le PC par copier coller dans un terminal: ~/plot/serialhistoplot.py {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Jouez sur les interrupteurs 0,6 et 7 du port d'entrée de la carte et observer l'évolution de l'affichage sur le PC. Analyser la distribution des valeurs lorsque la masse posée sur la balance ne change pas. Suit elle une loi normale? Faire de même en changeant la masse alors que l'application intègre les échantillons et analyser la distribution. {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Procéder maintenant à l'acquisition des mesures pour les masses de référence (fournies par l'enseignant). Pour cela, au lieu d'utiliser une seule mesure brute, nous allons utiliser l'application sur le PC pour calculer la moyenne d'un grand nombre de mesures, afin de minimiser l'influence du bruit de mesure. Pour chacune des masses de référence procéder suivant la séquence suivante: - positionner la masse au centre du plateau de la balance et attendre quelques secondes que la balance se stabilise mécaniquement (qu'il n'y ait plus d'oscillations mécaniques dues à l'élasticité). Veillez à partir d'ici à ne pas toucher la balance, la masse et même à éviter les vibrations de la table - mettre l'entrée 6 à l'état 1 pour acquérir des échantillons en continu et générer un front montant sur l'entrée 0 du port d'entrée sur la carte Arduino pour déclencher une réinitialisation des mesures. - attendre la collecte de suffisamment d'échantillons pour que l'écart type se stabilise (quelques centaines ou milliers) - copier coller la dernière ligne affichée dans la console dans un fichier texte en notant la masse correspondante {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Faire valider vos réponses à l'enseignant. ====Caractérisation et étalonnage du capteur==== Nous allons maintenant analyser les mesures réalisées à l'exercice précédent et déterminer la fonction (et ses paramètres) qui relie les données brutes à la grandeur physique mesurée. {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} - Après avoir reporté au moins quatre mesures d'étalons, reporter sur une feuille de papier quadrillée les mesures précédentes et proposer une fonction $mesurande=f(donnée)$ . - Déterminer approximativement les paramètres de cette fonction à partir du dessin. - Utiliser le programme fourni suivant pour obtenir une estimation précise (au sens des moindres carrés) des paramètres de cette fonction. Pour récupérer l'application, copier coller dans un terminal: echo commence cd ~/ mkdir -p plot cd plot rm ajustedroite2.py* wget --no-check-certificate https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/ajustedroite22021.py -O ./ajustedroite2.py chmod a+x ajustedroite2.py echo fini Pour lancer l'application afin qu'elle affiche les données, vous devrez saisir dans un terminal en utilisant les valeurs que vous avez collectées une commande de la forme: ~/plot/ajustedroite2.py masse1 mesurebrute1 ecarttype1 masse2 mesurebrute2 ecarttype2 ... par exemple: ~/plot/ajustedroite2.py 100 1000 25 500 7000 40 Solution pour balance 6, avec des écarts types à 0: ~/plot/ajustedroite2.py 185 203550 0 484 238040 0 975 294910 0 1361 339270 0 Le programme visualise alors dans une fenêtre les données fournies ainsi que les fonctions correspondantes: {{https://bvdp.inetdoc.net/files/iut/tp_lpro_capteur/ajustedroite.png}} Les valeurs des paramètres calculées par le programme s'affichent dans le terminal sous la forme: 2 correspondances saisies liste_masse <=> liste_mesure_brute +- ecart_type 100.00000 +1000.00000 +25.00000 500.00000 +7000.00000 +40.00000 ajuste aux sens des Moindres Carrés: évaluation de la fonction de cout: 0.0 ; résidus: [0.0, 0.0] ajuste sur 2 points seulement: évaluation de la fonction de cout: 0.0 ; résidus: [0.0, 0.0] Données brutes en sortie du capteur=f(Grandeur physique mesurée) coefficients de la droite ajustée y=a.x+b a=15.00000 b=-500.00000 Grandeur physique mesurée=f(Données brutes en sortie du capteur) coefficients de la droite ajustée y=a.x+b a=0.06667 b=33.33333 Solution pour balance 7 avec seulement 2 points (vide puis téléphone huawei de 202gr): ~/plot/ajustedroite2.py 0 -7519750 100 202 -7987760 100 liste_masse <=> liste_mesure_brute +- ecart_type 0.00000 -7519750.00000 +100.00000 202.00000 -7987760.00000 +100.00000 ajuste aux sens des Moindres Carrés: évaluation de la fonction de cout: 0.0 ; résidus: [0.0, 0.0] ajuste sur 2 points seulement: évaluation de la fonction de cout: 0.0 ; résidus: [0.0, 0.0] Données brutes en sortie du capteur=f(Grandeur physique mesurée) coefficients de la droite ajustée y=a.x+b a=-2316.88119 b=-7519750.00000 Grandeur physique mesurée=f(Données brutes en sortie du capteur) coefficients de la droite ajustée y=a.x+b a=-0.00043 b=-3245.63471 modification dans le programme arduino: changer le type de val en double double a=-0.00043; double b=-3245.63471; val= (a*val)+b; {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} - Relevez les valeurs fournies par le programme et les comparer à celle que vous aviez obtenues précédemment - Modifier le programme Arduino afin qu'il fournisse sur le port série non plus les données brutes mais les masses en gramme. - Utiliser l'application sur PC pour afficher les statistiques sur les mesures en gramme fournies par l'Arduino. Analyser: * Les valeurs moyennes affichées pour des masses connues utilisées ou non pour l'étalonnage. * Les écarts types en gramme pour un objet posé de manière statique sur la balance. * L'évolution de la cadence et de l'écart type des mesures selon que le cavalier de la carte convertisseur est ouvert ou fermé. * La présence ou non d'un hystérésis mesurable. Pour cela effectuer la mesure de masse d'un objet de masse a, la balance étant initialement vide. Puis ajouter un objet de masse b, le retirer et répéter la mesure. {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Faire valider vos réponses à l'enseignant. =====Exercice 3: Filtrage des données capteur===== Dans cet exercice, vous allez devoir intégrer sur l'Arduino le calcul de moyenne des échantillons afin de minimiser l'influence du bruit de mesure. Le calcul de moyenne sera réalisé sur les $n$ échantillons les plus récents, qui seront disponibles dans un tableau. Le code permettant la gestion de ce tableau en tant que **buffer circulaire** est fourni ci dessous et vous devrez le copier coller au début de votre programme arduino: #include ///////////////////////////////////////////////////////////////// typedef double echantillon_t; ///////////////////////////////////////////////////////////////// class Filtre{ public: Filtre(int nbMemoireVkInit=0, echantillon_t *memoireVkInit=NULL){ nbMemoireVk = nbMemoireVkInit; if (memoireVkInit!=NULL){ memoireVk = memoireVkInit; }else{ memoireVk= new echantillon_t[ nbMemoireVk]; // alloue un tableau de nbMemoireVk cases pour les échantillons } int i; indice_ecr=0; for (i=0;i=nbMemoireVk) indice_ecr=0; double moyenne=0; //A COMPLETER ICI PAR L'ETUDIANT return moyenne; } ////////////////////////////// protected: //attributs accessibles dans les classes dérivées int nbMemoireVk; //nombre de cases du buffer circulaire pour stocker les valeurs de vk echantillon_t * memoireVk;//buffer circulaire pour stocker les valeurs de vk int indice_ecr; //indice d'écriture dans le buffer rotatif memoireVk }; ///////////////////////////////////////////////////////////////// Filtre statistiqueMesures(10); //pour configurer le calcul sur 10 échantillons {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} - Compléter la fonction **echantillon_t TraiteUnEchantillon(echantillon_t ek)** pour qu'elle calcule la moyenne des échantillons stockés dans le tableau **memoireVk** contenant **nbMemoireVk** échantillons. Cette valeur doit ensuite être retournée par la fonction. - Afin d'utiliser ce code, vous ajouterez après l'acquisition de l'échantillon **val** par **scale.read()** et la conversion en gramme la ligne suivante dans votre programme, ce qui aura pour effet d'écraser la valeur lue sur la capteur par la valeur arrondie de la moyenne des 10 dernières mesures: val=statistiqueMesures.TraiteUnEchantillon(val); Solution : je fait le calcul de moyenne sur les données en gramme sinon les valeurs moyennées issues du capteur au début passent par des valeurs bidons et empêchent le barregraphe de fonctionner /* Serial.print("ici! "); Serial.print("nbMemoireVk:"); Serial.print(nbMemoireVk); */ //calcul de moyenne double somme=0; for (int i=0;i {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} - Tester le bon fonctionnement en utilisant les étalons de masse et en vérifiant que l'affichage de la masse dans la console arduino corresponde bien. - Interpréter les changements qui ont eu lieu en termes de temps de réponse du capteur et de bruit de mesure. {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Faire valider vos réponses à l'enseignant. =====Exercice 4: Reconnaissance d'objets en fonction de leurs masses==== Nous souhaitons maintenant utiliser le capteur (la balance) pour reconnaître des objets. Ceci peut permettre par exemple à identifier des pièces défectueuses sur une chaîne d'assemblage automatisée. Pour cela, nous allons devoir définir une plage de masses correspondant à chaque objet, car une égalité stricte de masse n'est pas adaptée (à cause du bruit de mesure, des perturbations extérieures et des variations de masse des objets quasi identiques). Nous proposons pour traiter ce problème une ébauche de solution qu'il vous faudra compléter. Une **structure de donnée** est utilisée pour stocker les différentes informations correspondant à **un ** objet: sa masse maximale et minimale ainsi que son nom. Un tableau contenant plusieurs éléments de la structure précédente est ensuite utilisé pour stocker les informations de **tous** les objets. En langage C, l'initialisation de ces éléments doit être faite dans une fonction **void initListeObjets()** dont un exemple est fourni ci dessous: ////////////////////////////////////////// //Structure pour un objet struct Objet { float masse_max; //masse max pour reconnaitre l'objet float masse_min; //masse min pour reconnaitre l'objet char nom[20]; //chaine de caractères pour le nom de l'objet, 20 caractères maximum }; ////////////////////////////////////////// //Tableau pour stocker les différentes informations sur les objets #define NBOBJETS 3 Objet listeObjets[NBOBJETS]; ////////////////////////////////////////// //fonction pour remplir le tableau de structure avec les informations sur les différents objets, à //appeler dans la fonction setup(). Cette fonction doit être adaptée selon les objets que vous voulez //pouvoir reconnaitre void initListeObjets(){ strcpy(listeObjets[0].nom,"Trousse"); //la fonction strcpy permet de copier tous les caractères de la chaine listeObjets[0].masse_max=301.35; listeObjets[0].masse_min=272.65; strcpy(listeObjets[1].nom,"Souris"); listeObjets[1].masse_max=73.5; listeObjets[1].masse_min=66.5; strcpy(listeObjets[2].nom,"Calculatrice"); listeObjets[2].masse_max=222.6; listeObjets[2].masse_min=201.4; } ////////////////////////////////////////// {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Copier coller le nouveau code dans votre programme précédent, adapter **NBOBJETS** au nombre d'objets que vous souhaitez reconnaître ainsi que la fonction **void initListeObjets()**. Compléter les fonctions **void setup()** et **void loop()** pour effectuer la reconnaissance d'objets. Piloter le barregraphe pour allumer la led correspondant au numéro de l'objet détecté et les éteindre toutes dans le cas où la masse mesurée ne correspond à aucun objet connu. Afficher sur la console Serial le nom de l'objet détecté. Expliquer pourquoi il peut arriver qu'un objet soit temporairement détecté à tort et proposer une solution pour résoudre ce problème (sans forcément l'implémenter). Un cas d'usage du système que vous devez réaliser est le suivant: La sortie pilotée par le barregraphe pourrait par exemple piloter un aiguillage des pièces vers différents tapis, permettant ainsi le tri. {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Faire valider vos réponses à l'enseignant. Solution à adapter en fonction des objets et balances: void setup() { ///... initListeObjets(); } ////////////////////////////////////////// void loop() { if (detecteFrontMontantPortB(Numerobit_0)) Serial.println("reset"); if ( ((LirePortB() & 0x80)!=0) || (detecteFrontMontantPortB(6)) ) { val=scale.read(); val=statistiqueMesures.TraiteUnEchantillon(val); masse = val; masse = masse+126143; masse = masse/-117; Serial.println(masse); } EcrirePortA(0); //éteindre tout par défaut for (int cpt=0;cpt=listeObjets[cpt].masse_min) ) { // Serial.print("Objet: "); Serial.println(listeObjets[cpt].nom); EcrirePortA(0x01< Solution à la question: L'affichage transitoire erroné est du au calcul de moyenne de masse qui fait varier lentement les valeurs en sortie du filtre et donc peut faire passer la masse fournie par des valeurs intermédiaires correspondant à différents objets. On pourrait faire en sorte de détecter si la valeur fournie par le filtre s'est stabilisée et ne l'exploiter qu'à ce moment. Pour cela on peut calculer l'écart type dans la fonction de filtrage et ne renvoyer la masse que lorsque l'écart type est assez réduit (par exemple en relatif par rapport à la masse) ---------------------------------------------------------------- Solution jusqu'à étalonnage: #include #define SLAVE_ADDR_8574_A 0x3e #define SLAVE_ADDR_8574_B 0x3f #include ///////////////////////////////////////////////////////////////// typedef double echantillon_t; ///////////////////////////////////////////////////////////////// class Filtre{ public: Filtre(int nbMemoireVkInit=0, echantillon_t *memoireVkInit=NULL){ nbMemoireVk = nbMemoireVkInit; if (memoireVkInit!=NULL){ memoireVk = memoireVkInit; }else{ memoireVk= new echantillon_t[ nbMemoireVk]; // alloue un tableau de nbMemoireVk cases pour les échantillons } int i; indice_ecr=0; for (i=0;i=nbMemoireVk) indice_ecr=0; //calcul de moyenne double somme=0; for (int i=0;i(data[3]) << 24 | static_cast(data[2]) << 16 | static_cast(data[1]) << 8 | static_cast(data[0]) ); return value; } }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// HX711 scale(A1, A0); // DOUT, SCK #include #include ////////////////////////////////////////// bool detecteFrontMontantPortB(unsigned char numerobit) { static char ancien[8]={-1,-1,-1,-1,-1,-1,-1,-1}; bool ret=false; unsigned char pb=LirePortB(); if (ancien[numerobit]==-1){ for (int i=0;i<8;i++) ancien[i]=(pb>>i)&1; }else{ char actuel=(pb>>numerobit)&1; if ( (actuel==1) && (ancien[numerobit]==0)) ret=true; else ret=false; ancien[numerobit]=actuel; } return ret; } ////////////////////////////////////////// unsigned char LirePortB() { Wire.requestFrom((byte)SLAVE_ADDR_8574_B, (byte)1);// demande la lecture d'1 octet depuis l'adresse du pérpiphérique if (Wire.available()==1) //si l'octet est disponible return Wire.read(); // lire l'octet else return 0; } ////////////////////////////////////////// void EcrirePortA(unsigned char valeur) //cette fonction pilote les 8 leds avec la valeur 8bits fournie dans le paramètre valeur { Wire.beginTransmission((byte)SLAVE_ADDR_8574_A);//démarre la transmission avec l'adresse du pérpiphérique Wire.write(~(byte)valeur); //envoie la donnée complémentée car les LEDs s'allument à l'état 0 Wire.endTransmission(); } ////////////////////////////////////////// void setup() { Serial.begin(9600); Serial.println("reset"); //scale.set_scale(250.f); //// this value is obtained by calibrating the scale with known weights //scale.tare(); Wire.begin(); // joindre le bus i2c en tant que maître delay(100); Wire.beginTransmission((byte)SLAVE_ADDR_8574_B); Wire.write((byte)0xff); Wire.endTransmission();//configure le composant B en entrée pinMode(3,OUTPUT); } ////////////////////////////////////////// void loop() { static float valmin=20000000; static float valmax=-20000000; if (detecteFrontMontantPortB(0) ) { Serial.println("reset"); valmin=20000000; valmax=-20000000; } if (detecteFrontMontantPortB(7) || (((LirePortB()>>6)&1)!=0) ) { long val=scale.read(); if (val>valmax) valmax=val; if (val