Différences entre versions de « Projets:Wi-canne »
(49 versions intermédiaires par 2 utilisateurs non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
+ | |||
+ | [[File:photos_prototypes_v0_et_v1.jpg|400px|right]] | ||
= Description du projet = | = Description du projet = | ||
− | Projet initié pendant le | + | [[File:wi-canne.png|100px|left|thumb]] |
+ | |||
+ | Projet initié pendant le Fabrikarium ArianeGroup aux Mureaux du 16 au 18 octobre 2019. | ||
Ce projet a pour objectif de créer une "canne blanche" augmentée à destination des déficient·e·s visuels. | Ce projet a pour objectif de créer une "canne blanche" augmentée à destination des déficient·e·s visuels. | ||
− | + | La wi-canne renseigne sur les reliefs du sol et l'environnement proche pour faciliter les déplacements. Un signal sonore ou haptique (moteur vibrant) indique les obstacles perçus par les capteurs embarqués. | |
+ | |||
+ | Ce projet fait suite au projet de [[Projets:GantSonar|gant sonar]] | ||
+ | |||
+ | <br clear="all" /> | ||
= Analyse de l'existant = | = Analyse de l'existant = | ||
Ligne 13 : | Ligne 21 : | ||
Systèmes existants : | Systèmes existants : | ||
* Tom Pouce | * Tom Pouce | ||
− | * | + | * Minitact |
+ | |||
+ | Une description précise de ces systèmes est donnée dans la thèse de doctorat de Joselin Villanueva "[https://tel.archives-ouvertes.fr/file/index/docid/598605/filename/VA2_VILLANUEVA_JOSELIN_16052011.pdf Contribution à la télémétrie optique active pour l'aide aux déplacements des non-voyants (pdf)]" (2011) | ||
= Equipe (Porteur de projet et contributeurs) = | = Equipe (Porteur de projet et contributeurs) = | ||
* Porteur de projet : François | * Porteur de projet : François | ||
− | * Contribut/eur/rice/s : Aymeric ( | + | * Contribut/eur/rice/s : Aymeric (ArianeGroup), Bruno (ArianeGroup), Lorraine (ArianeGroup), Océane (ArianeGroup), Olivier (ArianeGroup), Robin (ArianeGroup), Yves (My Human Kit) |
* Fabmanageuse référente : Lucie | * Fabmanageuse référente : Lucie | ||
* Responsable de documentation : Pierre | * Responsable de documentation : Pierre | ||
Ligne 39 : | Ligne 49 : | ||
= Matériel et outils nécessaires = | = Matériel et outils nécessaires = | ||
− | * imprimante 3D | + | * imprimante 3D (filament PLA + filament flex) |
+ | * découpeuse laser | ||
* circuits électroniques et capteurs | * circuits électroniques et capteurs | ||
+ | |||
+ | = Conception du boîtier = | ||
+ | |||
+ | C'est un processus itératif! Plusieurs hypothèses sont explorées. | ||
+ | |||
+ | [[File:conception_boitier_croquis_preparatifs.png|700px|none|alt=montage croquis préparatifs]] | ||
+ | |||
+ | [[File:conception_boitier_croquis.jpg|700px|none|alt=croquis préparatif]] | ||
+ | |||
+ | Le prototype est fabriqué à partir du croquis suivant : | ||
+ | |||
+ | [[File:croquis_prototype.jpg|700px|none|alt=croquis du prototype]] | ||
= Electronique = | = Electronique = | ||
− | + | Pour le premier prototype, 2 architectures sont envisagées : | |
* carte unique intégrée dans une poignée | * carte unique intégrée dans une poignée | ||
Ligne 57 : | Ligne 80 : | ||
[[File:electronique_scenario_sans_fil.png|600px|none|alt=version sans fil]] | [[File:electronique_scenario_sans_fil.png|600px|none|alt=version sans fil]] | ||
− | == | + | = Premier prototype = |
+ | |||
+ | [[File:photos_prototypes_v0_et_v1.jpg|700px|none|alt=photographies du prototype versions 0 et 1]] | ||
+ | |||
+ | Deux versions du prototype sont réalisées, elles utilisent le même circuit électronique. A droite, la version définitive comporte des pièces imprimées et d'autres découpées au laser. | ||
+ | |||
+ | == Comment ça fonctionne ? == | ||
+ | |||
+ | La wi-canne se porte à la main et détecte les reliefs du sol et les obstacles en hauteur, ces deux situations lui font émettre des avertissements sonores et haptiques différenciés. | ||
+ | |||
+ | A l'allumage, un cycle d'initialisation débute pour calibrer les capteurs et fixer la hauteur par rapport au sol, il doit impérativement se dérouler sur un sol plat. Après 10/15 secondes, la fin du cycle s'achève par une petite musique. | ||
+ | |||
+ | C'est parti! Pour la détection d'obstacles au sol : tant que le sol est plat, aucun son n'est émis, quand un obstacle est détecté à la limite de détection (2m), il est signalé par une vibration haptique du moteur vibrant. Lorsqu'on se retrouve à la verticale de l'obstacle, des bips sonores sont émis. | ||
+ | |||
+ | Dans le cas d'un obstacle en hauteur, un bip sonore (différent de celui d'un obstacle au sol) se déclenche à environ 1m de distance. | ||
+ | |||
+ | == Fichiers de fabrication == | ||
+ | |||
+ | <gallery mode="packed-hover" heights=300px> | ||
+ | boite_wicanne_3.png|boitier | ||
+ | manche_002.png|manche | ||
+ | </gallery> | ||
+ | |||
+ | Le prototype est composé de deux parties à assembler, une première en contreplaqué qui contient l'électronique, et une seconde en plastique imprimé qui forme le manche. | ||
+ | |||
+ | boitier au format svg, pour découpe laser : [[Media:boite_wicanne_3.svg|boite_wicanne_3.svg]] | ||
+ | |||
+ | manche au format stl, pour impression 3D : [[Media:manche_002.stl|manche_002.stl]] | ||
+ | |||
+ | == Circuit électronique == | ||
Basé sur l'architecture à carte unique | Basé sur l'architecture à carte unique | ||
− | [[File:wi- | + | [[File:wi-canne_v1_circuit_ultrason.png|800px|none|alt=schéma du circuit du premier prototype]] |
+ | |||
+ | {| class="wikitable" | ||
+ | !align="center"| Ex. | ||
+ | !align="center"| Composant | ||
+ | !align="center"| Type | ||
+ | |- | ||
+ | |align="center"| 1 | ||
+ | |align="center"| arduino nano | ||
+ | |align="center"| carte à microcontrôleur | ||
+ | |- | ||
+ | |align="center"| 1 | ||
+ | |align="center"| module buzzer | ||
+ | |align="center"| buzzer piezo | ||
+ | |||
+ | |- | ||
+ | |align="center"| 1 | ||
+ | |align="center"| module moteur | ||
+ | |align="center"| double pont en H L9110S | ||
+ | |- | ||
+ | |align="center"| 2 | ||
+ | |align="center"| moteur vibrant | ||
+ | |align="center"| | ||
+ | |||
+ | |- | ||
+ | |align="center"| 1 | ||
+ | |align="center"| écran LCD 2 lignes I2C | ||
+ | |align="center"| | ||
+ | |||
+ | |- | ||
+ | |align="center"| 1 | ||
+ | |align="center"| ADXL335 | ||
+ | |align="center"| accéléromètre 3 axes analogique | ||
+ | |- | ||
+ | |align="center"| 3 | ||
+ | |align="center"| module CJVL53L0XV2 | ||
+ | |align="center"| capteur ToF STmicroelectronics VL53L0X | ||
+ | |- | ||
+ | |align="center"| 1 | ||
+ | |align="center"| module SRF05 | ||
+ | |align="center"| capteur de distance à ultrason | ||
+ | |} | ||
+ | |||
+ | /!\ le capteur ultrason est cablé pour fonctionner avec une seule broche, utilisée alternativement pour envoyer l'impulsion ultrasonique et pour la recevoir. Cette alternance est programmée dans le code. | ||
+ | |||
+ | /!\ L'accéléromètre représenté sur le circuit dans un bloc rouge n'est pas utilisé dans ce prototype. L'utilisa/teur/trice étant en mouvement, les données sont difficiles à interpréter. | ||
+ | |||
+ | [[File:prototype_photo.jpg|700px|none|alt=photo du boîtier avec électronique intégrée]] | ||
+ | |||
+ | === Code du prototype === | ||
+ | |||
+ | <nowiki> | ||
+ | |||
+ | // Wi-Canne FABRIKARIUM ARIANE | ||
+ | // 15/10/2019 AU 18/10/2019 | ||
+ | /** | ||
+ | * VL53L0X version 1.0.2 https://github.com/pololu/vl53l0x-arduino | ||
+ | * LiquidCrystal_I2C version 1.1.2 https://github.com/marcoschwartz/LiquidCrystal_I2C | ||
+ | * | ||
+ | */ | ||
+ | #include <Wire.h> | ||
+ | #include <math.h> | ||
+ | #include <VL53L0X.h> | ||
+ | #include <LiquidCrystal_I2C.h> | ||
+ | |||
+ | // Notes de musique pour musique après initialisation | ||
+ | #define NOTE_B0 31 | ||
+ | #define NOTE_C1 33 | ||
+ | #define NOTE_CS1 35 | ||
+ | #define NOTE_D1 37 | ||
+ | #define NOTE_DS1 39 | ||
+ | #define NOTE_E1 41 | ||
+ | #define NOTE_F1 44 | ||
+ | #define NOTE_FS1 46 | ||
+ | #define NOTE_G1 49 | ||
+ | #define NOTE_GS1 52 | ||
+ | #define NOTE_A1 55 | ||
+ | #define NOTE_AS1 58 | ||
+ | #define NOTE_B1 62 | ||
+ | #define NOTE_C2 65 | ||
+ | #define NOTE_CS2 69 | ||
+ | #define NOTE_D2 73 | ||
+ | #define NOTE_DS2 78 | ||
+ | #define NOTE_E2 82 | ||
+ | #define NOTE_F2 87 | ||
+ | #define NOTE_FS2 93 | ||
+ | #define NOTE_G2 98 | ||
+ | #define NOTE_GS2 104 | ||
+ | #define NOTE_A2 110 | ||
+ | #define NOTE_AS2 117 | ||
+ | #define NOTE_B2 123 | ||
+ | #define NOTE_C3 131 | ||
+ | #define NOTE_CS3 139 | ||
+ | #define NOTE_D3 147 | ||
+ | #define NOTE_DS3 156 | ||
+ | #define NOTE_E3 165 | ||
+ | #define NOTE_F3 175 | ||
+ | #define NOTE_FS3 185 | ||
+ | #define NOTE_G3 196 | ||
+ | #define NOTE_GS3 208 | ||
+ | #define NOTE_A3 220 | ||
+ | #define NOTE_AS3 233 | ||
+ | #define NOTE_B3 247 | ||
+ | #define NOTE_C4 262 | ||
+ | #define NOTE_CS4 277 | ||
+ | #define NOTE_D4 294 | ||
+ | #define NOTE_DS4 311 | ||
+ | #define NOTE_E4 330 | ||
+ | #define NOTE_F4 349 | ||
+ | #define NOTE_FS4 370 | ||
+ | #define NOTE_G4 392 | ||
+ | #define NOTE_GS4 415 | ||
+ | #define NOTE_A4 440 | ||
+ | #define NOTE_AS4 466 | ||
+ | #define NOTE_B4 494 | ||
+ | #define NOTE_C5 523 | ||
+ | #define NOTE_CS5 554 | ||
+ | #define NOTE_D5 587 | ||
+ | #define NOTE_DS5 622 | ||
+ | #define NOTE_E5 659 | ||
+ | #define NOTE_F5 698 | ||
+ | #define NOTE_FS5 740 | ||
+ | #define NOTE_G5 784 | ||
+ | #define NOTE_GS5 831 | ||
+ | #define NOTE_A5 880 | ||
+ | #define NOTE_AS5 932 | ||
+ | #define NOTE_B5 988 | ||
+ | #define NOTE_C6 1047 | ||
+ | #define NOTE_CS6 1109 | ||
+ | #define NOTE_D6 1175 | ||
+ | #define NOTE_DS6 1245 | ||
+ | #define NOTE_E6 1319 | ||
+ | #define NOTE_F6 1397 | ||
+ | #define NOTE_FS6 1480 | ||
+ | #define NOTE_G6 1568 | ||
+ | #define NOTE_GS6 1661 | ||
+ | #define NOTE_A6 1760 | ||
+ | #define NOTE_AS6 1865 | ||
+ | #define NOTE_B6 1976 | ||
+ | #define NOTE_C7 2093 | ||
+ | #define NOTE_CS7 2217 | ||
+ | #define NOTE_D7 2349 | ||
+ | #define NOTE_DS7 2489 | ||
+ | #define NOTE_E7 2637 | ||
+ | #define NOTE_F7 2794 | ||
+ | #define NOTE_FS7 2960 | ||
+ | #define NOTE_G7 3136 | ||
+ | #define NOTE_GS7 3322 | ||
+ | #define NOTE_A7 3520 | ||
+ | #define NOTE_AS7 3729 | ||
+ | #define NOTE_B7 3951 | ||
+ | #define NOTE_C8 4186 | ||
+ | #define NOTE_CS8 4435 | ||
+ | #define NOTE_D8 4699 | ||
+ | #define NOTE_DS8 4978 | ||
+ | |||
+ | #define TRACES 1 // 1 pour activer trace sur port série vers ordinateur, 0 pour la désactiver | ||
+ | #define NB_CAPTEURS 3 // Nombre de capteurs IR | ||
+ | #define NB_VIBREURS 2 // Nombre de vibreurs | ||
+ | #define LONG_RANGE // Capteurs IR en mode long range | ||
+ | #define NB_HIST 10 // Nombre de donnees sauvegardees dans l historique | ||
+ | |||
+ | // Notes de musique pour BIP | ||
+ | #define NOTE_PROTECTION_TETE 1500 | ||
+ | #define NOTE_PROTECTION_TETE2 2500 | ||
+ | |||
+ | // Le sonar prends des mesures toutes les 10 boucles | ||
+ | #define SONAR_DIVIDER 10 | ||
+ | |||
+ | // Compteurs de boucle | ||
+ | int i = 0; | ||
+ | int k = 0; | ||
+ | int j = 0; | ||
+ | |||
+ | // Informations stockees pour alarmes | ||
+ | bool alarm_activate = false; | ||
+ | int alarm_note1 = 0; | ||
+ | int alarm_note2 = 0; | ||
+ | |||
+ | // PINs | ||
+ | const int in_buzz = 8; // Pin buzzer | ||
+ | const int pingPin = 7; // Pin Ultrasonic sensor (SRF05) | ||
+ | const int in_vib_sens1[NB_VIBREURS] = {9,10}; // PIN vibreurs sens 1 | ||
+ | //const int in_vib_sens2[NB_VIBREURS] = {5,6}; // PIN vibreur sens 2 | ||
+ | |||
+ | int note = 800; | ||
+ | int durations[2] = {50.0,50.0}; | ||
+ | const int xshut[3] = {2,3,4}; | ||
+ | const int addr[3] = {0x25,0x30,0x35}; | ||
+ | const double delay_init = 50.0; | ||
+ | const double timeout_capteur = 50.0; | ||
+ | const double delai_mesure = 10.0; | ||
+ | int palvib[4] = {255, 140, 80, 50}; // forces de vibrations correspondant aux 4 paliers distances de chaque plage | ||
+ | |||
+ | double data[NB_CAPTEURS]; // Table de stockage des données fournies par le lidar | ||
+ | double hist_data[NB_CAPTEURS][NB_HIST]; | ||
+ | double hist_temps[NB_CAPTEURS][NB_HIST]; | ||
+ | double delta0; | ||
+ | int idata = 0; | ||
+ | |||
+ | long sonarCm=0; | ||
+ | int sonarDivider=0; | ||
+ | double hauteur0; | ||
+ | |||
+ | int dist; // valeur de la distance mesurée | ||
+ | int force; // force du signal reçu | ||
+ | int ctrol; // somme de controle des données reçues (checksum) | ||
+ | int corr = 5; // valeur de correction de la mesure lue en cm | ||
+ | int x_adc_value; | ||
+ | int y_adc_value; | ||
+ | int z_adc_value; | ||
+ | |||
+ | double x_g_value; | ||
+ | double y_g_value; | ||
+ | double z_g_value; | ||
+ | double roll, pitch, yaw; | ||
+ | |||
+ | // notes in the melody: | ||
+ | int melody[] = { | ||
+ | NOTE_E7,NOTE_E7,0,NOTE_E7,0,NOTE_C7,NOTE_E7,0,NOTE_G7,0,0,0,NOTE_G6,0,0,0 | ||
+ | }; | ||
+ | |||
+ | // note durations: 4 = quarter note, 8 = eighth note, etc.: | ||
+ | int noteDurations[] = { | ||
+ | 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, | ||
+ | }; | ||
+ | |||
+ | LiquidCrystal_I2C lcd(0x3f,20,4); // déclaration afficheur LCD série I2C | ||
+ | VL53L0X sensor[NB_CAPTEURS]; | ||
+ | |||
+ | |||
+ | void musique_init() { | ||
+ | // iterate over the notes of the melody: | ||
+ | for (int thisNote = 0; thisNote < 16; thisNote++) { | ||
+ | |||
+ | // to calculate the note duration, take one second | ||
+ | // divided by the note type. | ||
+ | //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. | ||
+ | int noteDuration = 1000 / noteDurations[thisNote]; | ||
+ | tone(8, melody[thisNote], noteDuration); | ||
+ | |||
+ | // to distinguish the notes, set a minimum time between them. | ||
+ | // the note's duration + 30% seems to work well: | ||
+ | int pauseBetweenNotes = noteDuration * 1.30; | ||
+ | delay(pauseBetweenNotes); | ||
+ | // stop the tone playing: | ||
+ | noTone(8); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void setup() { | ||
+ | |||
+ | #if TRACES // si mise au point activée | ||
+ | Serial.begin(115200); // vitesse du port de communication avec l'ordinateur | ||
+ | #endif | ||
+ | |||
+ | // LCD INIT | ||
+ | lcd.init(); // initialize the lcd | ||
+ | lcd.backlight(); | ||
+ | lcd.setCursor(0,0); | ||
+ | lcd.print("Hello, world!"); | ||
+ | |||
+ | // SENSOR INIT | ||
+ | Serial.println("Start wire init."); | ||
+ | Wire.begin(); | ||
+ | |||
+ | // Ouverture capteurs et extinction | ||
+ | for(k=0;k<NB_CAPTEURS;k++) { | ||
+ | pinMode(xshut[k], OUTPUT); | ||
+ | digitalWrite(xshut[k], LOW); | ||
+ | } | ||
+ | // Ouverture vibreurs et extinction | ||
+ | for(k=0;k<NB_VIBREURS;k++) { | ||
+ | pinMode(in_vib_sens1[k], OUTPUT); | ||
+ | //pinMode(in_vib_sens2[k], OUTPUT); | ||
+ | digitalWrite(in_vib_sens1[k], LOW); | ||
+ | //digitalWrite(in_vib_sens2[k], LOW); | ||
+ | analogWrite(in_vib_sens1[k], 255); | ||
+ | //analogWrite(in_vib_sens2[k], 255); | ||
+ | } | ||
+ | |||
+ | // Ouverture des capteurs et nommage | ||
+ | for (k=0;k<NB_CAPTEURS;k++) { | ||
+ | |||
+ | // Ouverture capteur | ||
+ | delay(delay_init); | ||
+ | digitalWrite(xshut[k], HIGH); | ||
+ | delay(delay_init); | ||
+ | |||
+ | // nommage | ||
+ | sensor[k].setAddress(addr[k]); | ||
+ | |||
+ | // Affichage | ||
+ | lcd.setCursor(0,1); | ||
+ | lcd.print(k); | ||
+ | Serial.println("starting sensor "); | ||
+ | Serial.println(k); | ||
+ | delay(delay_init); | ||
+ | |||
+ | // Demarrage capteur | ||
+ | sensor[k].init(); | ||
+ | sensor[k].setTimeout(timeout_capteur); | ||
+ | #if defined LONG_RANGE | ||
+ | sensor[k].setSignalRateLimit(0.1); | ||
+ | sensor[k].setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18); | ||
+ | sensor[k].setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14); | ||
+ | #endif | ||
+ | sensor[k].startContinuous(); | ||
+ | delay(delay_init); | ||
+ | } | ||
+ | |||
+ | // 10 premieres mesures | ||
+ | for (j=0;j<NB_HIST*10;j++) { | ||
+ | recuperer_data(); | ||
+ | } | ||
+ | |||
+ | // ecart d1 d2 sur sol lisse | ||
+ | delta0 = abs(recuperer_moyenne(2) - recuperer_moyenne(1)); | ||
+ | hauteur0 = abs(recuperer_moyenne(0)); | ||
+ | |||
+ | Serial.println(delta0); | ||
+ | Serial.println(hauteur0); | ||
+ | musique_init() ; | ||
+ | } | ||
+ | |||
+ | void alarm(bool activate,int note1,int note2,int priority) { | ||
+ | if (activate) { | ||
+ | if ((!alarm_activate) || priority > 0) { | ||
+ | alarm_activate = activate; | ||
+ | alarm_note1 = note1; | ||
+ | alarm_note2 = note2; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void jouer_alarm() { | ||
+ | if (alarm_activate) { | ||
+ | tone(in_buzz,alarm_note1,durations[0]); | ||
+ | if (alarm_note1 != alarm_note2){ | ||
+ | delay(100.0); | ||
+ | tone(in_buzz,alarm_note2,durations[1]); | ||
+ | } | ||
+ | } | ||
+ | else{ | ||
+ | noTone(in_buzz); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void vibration(int k, boolean vibrate,double niveau) { | ||
+ | |||
+ | if(vibrate) | ||
+ | { | ||
+ | analogWrite(in_vib_sens1[k], int(255-(niveau*255))); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | analogWrite(in_vib_sens1[k], 255); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void decalage_historique() { | ||
+ | |||
+ | for (int k=0; k<NB_CAPTEURS; k++) { | ||
+ | for (int j=1;j<NB_HIST; j++) { | ||
+ | hist_data[k][j-1] = hist_data[k][j]; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void recuperer_data() { | ||
+ | |||
+ | decalage_historique(); | ||
+ | |||
+ | // Distances senseurs | ||
+ | for (k=0;k<NB_CAPTEURS;k++) { | ||
+ | data[k] = sensor[k].readRangeContinuousMillimeters(); | ||
+ | if ((data[k] < 2000.0) && (data[k] > 100.0)) { | ||
+ | hist_data[k][idata] = data[k]; | ||
+ | hist_temps[k][idata] = millis(); | ||
+ | } | ||
+ | else{ | ||
+ | data[k] = 2000.0; | ||
+ | } | ||
+ | |||
+ | // Affichage distances | ||
+ | lcd.setCursor(0,k); | ||
+ | lcd.print(" "); | ||
+ | lcd.setCursor(0,k); | ||
+ | lcd.print(data[k]); | ||
+ | } | ||
+ | delay(delai_mesure); | ||
+ | idata = idata + 1; | ||
+ | if (idata >= NB_HIST) { | ||
+ | idata = NB_HIST-1; | ||
+ | } | ||
+ | |||
+ | sonarDivider++; | ||
+ | if(sonarDivider > SONAR_DIVIDER) | ||
+ | { | ||
+ | sonarDivider=0; | ||
+ | // The PING))) is triggered by a HIGH pulse of 2 or more microseconds. | ||
+ | // Give a short LOW pulse beforehand to ensure a clean HIGH pulse: | ||
+ | pinMode(pingPin, OUTPUT); | ||
+ | digitalWrite(pingPin, LOW); | ||
+ | delayMicroseconds(2); | ||
+ | digitalWrite(pingPin, HIGH); | ||
+ | delayMicroseconds(5); | ||
+ | digitalWrite(pingPin, LOW); | ||
+ | pinMode(pingPin, INPUT); | ||
+ | sonarCm = microsecondsToCentimeters(pulseIn(pingPin, HIGH)); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | double recuperer_moyenne(int k) { | ||
+ | |||
+ | double moy; | ||
+ | int nmes = 0; | ||
+ | |||
+ | // Moyenne groupe 1 | ||
+ | moy = 0.0; | ||
+ | for (j=0;j<NB_HIST;j++) { | ||
+ | if (hist_data[k][j] < 2000.0) { | ||
+ | moy = moy + hist_data[k][j]; | ||
+ | nmes = nmes + 1; | ||
+ | } | ||
+ | } | ||
+ | moy = moy / double(nmes); | ||
+ | return moy; | ||
+ | } | ||
+ | |||
+ | double delta_mesure(int k) { | ||
+ | |||
+ | double moy1,moy2,der; | ||
+ | |||
+ | // Moyenne groupe 1 | ||
+ | moy1 = 0.0; | ||
+ | for (j=0;j<NB_HIST/2;j++) { | ||
+ | moy1 = moy1 + hist_data[k][j]; | ||
+ | } | ||
+ | moy1 = moy1 / double(NB_HIST/2); | ||
+ | |||
+ | // Moyenne groupe 2 | ||
+ | moy2 = 0.0; | ||
+ | for (j=NB_HIST/2;j<NB_HIST;j++) { | ||
+ | moy2 = moy2 + hist_data[k][j]; | ||
+ | } | ||
+ | moy2 = moy2 / double(NB_HIST/2); | ||
+ | der = (moy2 - moy1); | ||
+ | |||
+ | return der; | ||
+ | } | ||
+ | |||
+ | void loop() { | ||
+ | |||
+ | alarm_activate = false; | ||
+ | |||
+ | // Recuperation donnees senseurs | ||
+ | recuperer_data(); | ||
+ | double saut = abs(data[1]-data[2]); | ||
+ | double dsaut = abs(saut - delta0); | ||
+ | |||
+ | // Detection | ||
+ | if ( (dsaut > 150.0) | ||
+ | && (abs(data[1])<2000.0) | ||
+ | && (abs(data[2])<2000.0)) { | ||
+ | vibration(1,true,1.0);//max(dsaut/200.0, 1.0)); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | vibration(1,false,0.0); | ||
+ | } | ||
+ | |||
+ | // Test saturation | ||
+ | if ( (abs(data[1])<2000.0) || (abs(data[2])<2000.0)) { | ||
+ | //alarm(true,NOTE_PROTECTION_TETE,NOTE_PROTECTION_TETE2,1); | ||
+ | } | ||
+ | Serial.print(data[0]); | ||
+ | Serial.print(" "); | ||
+ | Serial.print(data[1]); | ||
+ | Serial.print(" "); | ||
+ | Serial.println(data[2]); | ||
+ | |||
+ | if ( (abs(data[1])<2000.0) | ||
+ | && (abs(data[2])<2000.0)){ | ||
+ | // Affichage distances | ||
+ | lcd.setCursor(0,3); | ||
+ | lcd.print(" "); | ||
+ | lcd.setCursor(0,3); | ||
+ | lcd.print(delta0); | ||
+ | } | ||
+ | |||
+ | // trottoir | ||
+ | if (abs(delta_mesure(0)) > 100.0) { | ||
+ | alarm(true,note,note,0); | ||
+ | } | ||
+ | |||
+ | // Protection tete | ||
+ | Serial.print(sonarCm); | ||
+ | Serial.print(" "); | ||
+ | Serial.println(abs(1800-hauteur0)); | ||
+ | if (sonarCm > 10 && (sonarCm*10.0) < abs(1800.0-hauteur0)) { | ||
+ | //alarm(true,NOTE_PROTECTION_TETE,NOTE_PROTECTION_TETE,0); | ||
+ | alarm(true,NOTE_PROTECTION_TETE,NOTE_PROTECTION_TETE2,1); | ||
+ | } | ||
+ | |||
+ | jouer_alarm(); | ||
+ | } | ||
+ | |||
+ | //from arduino ping sample. | ||
+ | long microsecondsToCentimeters(long microseconds) { | ||
+ | // The speed of sound is 340 m/s or 29 microseconds per centimeter. | ||
+ | // The ping travels out and back, so to find the distance of the | ||
+ | // object we take half of the distance travelled. | ||
+ | return microseconds / 29 / 2; | ||
+ | } | ||
+ | |||
+ | //****************************************************/ | ||
+ | </nowiki> | ||
= Capteurs testés = | = Capteurs testés = | ||
Ligne 110 : | Ligne 679 : | ||
|align="center"| 40 Hz max | |align="center"| 40 Hz max | ||
|align="right"| 10 à 40 mA | |align="right"| 10 à 40 mA | ||
+ | |- | ||
+ | | SRF05 | ||
+ | |align="center"| ultrason 40 kHz | ||
+ | |align="center"| 30° | ||
+ | |align="center"| 4 à 400 cm | ||
+ | |align="center"| 20 Hz max | ||
+ | |align="right"| ? | ||
|} | |} | ||
Ligne 115 : | Ligne 691 : | ||
Sur la comparaison des caractéristiques de différents types de capteurs de distance, voir aussi https://www.sparkfun.com/distance_sensor_comparison_guide | Sur la comparaison des caractéristiques de différents types de capteurs de distance, voir aussi https://www.sparkfun.com/distance_sensor_comparison_guide | ||
+ | |||
+ | Caractéristiques de différents transducteurs à ultrasons : https://www.robot-electronics.co.uk/htm/sonar_faq.htm | ||
== Carte CJVL53L0XV2 == | == Carte CJVL53L0XV2 == | ||
Ligne 171 : | Ligne 749 : | ||
[[File:benewake_tfmini.jpg|300px|alt=caption capteur TFmini]] | [[File:benewake_tfmini.jpg|300px|alt=caption capteur TFmini]] | ||
− | Il s'agit d'un capteur ToF vendu sous l'appellation LiDAR, toutefois il | + | Il s'agit d'un capteur ToF vendu sous l'appellation LiDAR, toutefois il utilise plutôt une diode LED infrarouge et une optique adéquate. Ce capteur permet de mesurer une distance comprise entre 30 centimètres et 12 mètres, à une fréquence de 100Hz, soit 100 mesures par seconde. Il fonctionne à une tension de 5v et communique par une interface série (UART). |
=== zone de détection === | === zone de détection === | ||
Ligne 243 : | Ligne 821 : | ||
* distributeur : [https://www.velleman.eu/products/view/?country=be&lang=fr&id=435526 Velleman] | * distributeur : [https://www.velleman.eu/products/view/?country=be&lang=fr&id=435526 Velleman] | ||
− | = | + | == Capteur de distance à ultrason SRF05 == |
+ | |||
+ | [[File:SRF05.jpg|300px|none|alt=capteur de distance à ultrason SRF05]] | ||
+ | |||
+ | Ce capteur permet de mesurer la distance avec un obstacle en mesurant le temps de rebond d'une impulsion ultrasonique à 40 kHz. Il est basé sur le transducteur ultrasonique T400S16 | ||
+ | |||
+ | === zone de détection === | ||
+ | |||
+ | D'après la datasheet du fabricant du transducteur. | ||
+ | |||
+ | [[File:SRF05_detection_range.png|300px|none|alt=zone de détection du capteur ultrasonique]] | ||
+ | |||
+ | === documentation === | ||
+ | |||
+ | * [[Media:SRF05_technical_documentation.pdf|datasheet]] | ||
+ | * [[Media:transducteur_T400S16.pdf|datasheet du capteur T400S16]] | ||
+ | |||
+ | = Journal du Fabrikarium ArianeGroup = | ||
+ | |||
+ | '''jour 1''' | ||
+ | |||
+ | Dans un premier temps, faire connaissance! Définir les besoins et les replacer dans un contexte existant. | ||
+ | Puis l'équipe se concentre en deux pôles : test de différents types de capteurs pour le groupe électronique et pour le groupe design : recherches pour l'ergonomie et la conception de l'objet. | ||
+ | |||
+ | En fin de journée, le groupe ergonomie a conçu une poignée intégrant batteries et circuit de charge ("poignée rechargeable"). Sur le plan électronique deux architectures électroniques sont posées comme des pistes possibles et un ensemble conséquent de capteurs a pu être testé. | ||
+ | |||
+ | '''jour 2''' | ||
+ | |||
+ | "Copier c'est gagné" dixit Olivier, l'équipe décide d'intégrer une powerbank dans la poignée pour résoudre la partie alimentation : ce type de batterie intègre un régulateur de charge, alimente en 5V un port USB, et surtout est très simple à se procurer, banco! | ||
+ | |||
+ | Le groupe électronique poursuit les recherches sur les capteurs par des tests sur les capteurs VCSEL | ||
+ | L'après-midi est consacrée à la conception et l'assemblage du circuit du premier prototype | ||
+ | |||
+ | Le groupe design définit l'ergonomie de la poignée et poursuit la recherche sur les matériaux et le design. Le choix des matériaux du prototype est contraint par le temps limité... | ||
+ | En fin de journée, les impressions 3D sont lancées pour sortir pendant le nuit | ||
+ | |||
+ | '''jour 3''' | ||
+ | |||
+ | "il faut se hâter lentement" dixit Yves, aux prises avec le traitement des données de capteur | ||
+ | |||
+ | Le groupe design découvre le résultat des 3 impressions lancées pendant la nuit : 2 sont parfaitement imprimées, la 3e, un grip de poignée en filament flex n'a pas correctement été imprimé. Il reste peu de temps et la fabrication du boîtier qui accueillera le système électronique est lancée! | ||
+ | |||
+ | Au plan électronique, un premier prototype du circuit est achevé et l'équipe s'attelle à peaufiner le code du programme arduino. | ||
+ | |||
+ | |||
+ | = Annexe : circuits de test = | ||
== distance et mouvement == | == distance et mouvement == | ||
Ligne 367 : | Ligne 990 : | ||
</nowiki> | </nowiki> | ||
− | = | + | = Annexe : lexique = |
'''FoV (Field of View)''' : champ de vue, angle de sensibilité d'un capteur optique. cf. https://fr.wikipedia.org/wiki/Champ_de_vue | '''FoV (Field of View)''' : champ de vue, angle de sensibilité d'un capteur optique. cf. https://fr.wikipedia.org/wiki/Champ_de_vue | ||
Ligne 381 : | Ligne 1 004 : | ||
'''UART (Universal asynchronous receiver-transmitter)''' : interface hardware de communication asynchrone, dont le type de données et la vitesse de transmission peut-être définie. cf. https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter | '''UART (Universal asynchronous receiver-transmitter)''' : interface hardware de communication asynchrone, dont le type de données et la vitesse de transmission peut-être définie. cf. https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter | ||
− | '''VCSEL (Vertical-Cavity Surface-Emitting Laser)''' : à prononcer [https://itinerarium.github.io/phoneme-synthesis/?w=/v'ɪxl/ | + | '''VCSEL (Vertical-Cavity Surface-Emitting Laser)''' : à prononcer [https://itinerarium.github.io/phoneme-synthesis/?w=/v'ɪxl/ vixel]! diode laser à cavité verticale émettant par la surface. cf. https://fr.wikipedia.org/wiki/Diode_laser_%C3%A0_cavit%C3%A9_verticale_%C3%A9mettant_par_la_surface |
+ | |||
+ | ==Journal de bord == | ||
+ | |||
+ | ==Mise à jour du projet 09/12/2019 == | ||
+ | [[Media:Wicanne v2.3.pdf|Documentation PDF de la V2.3]] | ||
+ | |||
+ | ==Mise à jour du projet 24/12/2019 == | ||
+ | [[Media:Wicanne v2.33.pdf|Documentation PDF de la V2.33]] | ||
+ | |||
+ | |||
+ | ===Fichiers sources=== | ||
+ | [[Media:WiCanne_v2-3_Sources.zip|Lien vers les fichiers source ]] | ||
+ | |||
+ | ==Mise à jour du projet 08/03/2021 : Documentation PDF de la V3.44 == | ||
+ | Cliquer sur le texte pour voir le pdf dans son intégralité. | ||
+ | |||
+ | [[File:WiCanne_v3-44.pdf]] | ||
+ | |||
+ | |||
+ | ===Fichiers sources v3.44=== | ||
+ | [[Media:Wi-canne_Envoi_V3-44.zip|Télécharger les fichiers source (Code arduino, librairie Nano Edge (IA), acquisition de données, pdf de la doc ]] | ||
+ | |||
+ | |||
+ | [[Category:Projets]] | ||
+ | [[Category:En cours]] | ||
+ | [[Category:Malvoyance]] |
Version actuelle datée du 19 avril 2022 à 09:54
Description du projet
Projet initié pendant le Fabrikarium ArianeGroup aux Mureaux du 16 au 18 octobre 2019.
Ce projet a pour objectif de créer une "canne blanche" augmentée à destination des déficient·e·s visuels.
La wi-canne renseigne sur les reliefs du sol et l'environnement proche pour faciliter les déplacements. Un signal sonore ou haptique (moteur vibrant) indique les obstacles perçus par les capteurs embarqués.
Ce projet fait suite au projet de gant sonar
Analyse de l'existant
Systèmes existants :
- Tom Pouce
- Minitact
Une description précise de ces systèmes est donnée dans la thèse de doctorat de Joselin Villanueva "Contribution à la télémétrie optique active pour l'aide aux déplacements des non-voyants (pdf)" (2011)
Equipe (Porteur de projet et contributeurs)
- Porteur de projet : François
- Contribut/eur/rice/s : Aymeric (ArianeGroup), Bruno (ArianeGroup), Lorraine (ArianeGroup), Océane (ArianeGroup), Olivier (ArianeGroup), Robin (ArianeGroup), Yves (My Human Kit)
- Fabmanageuse référente : Lucie
- Responsable de documentation : Pierre
Cahier des charges
- anticiper le relief au sol dans un intervalle de + ou - 10 cm
- retour d'informations par vibreur ou message sonore
- coût de fabrication inférieur à 300 euros
- autonomie de 6 heures
- objet équilibré
- poids maximum : 300g
- recharge par port USB intégrée dans le manche
- ambidextre : symétrique pour utilisation de la main gauche ou de la main droite
- boîtier résistant à la pluie et au soleil
- indications en relief sur le boîtier
- objet autonome, fonctionne sans canne
- indication de l'état de charge de la batterie par un appui bouton
Matériel et outils nécessaires
- imprimante 3D (filament PLA + filament flex)
- découpeuse laser
- circuits électroniques et capteurs
Conception du boîtier
C'est un processus itératif! Plusieurs hypothèses sont explorées.
Le prototype est fabriqué à partir du croquis suivant :
Electronique
Pour le premier prototype, 2 architectures sont envisagées :
- carte unique intégrée dans une poignée
- système sans-fil à 2 cartes, l'une chargée de la détection (capteurs et traitement des données) qui communique sans-fil avec une seconde dédiée aux signaux d'avertissement sonores et vibrants.
Version à carte unique
Version sans fil à 2 cartes
Premier prototype
Deux versions du prototype sont réalisées, elles utilisent le même circuit électronique. A droite, la version définitive comporte des pièces imprimées et d'autres découpées au laser.
Comment ça fonctionne ?
La wi-canne se porte à la main et détecte les reliefs du sol et les obstacles en hauteur, ces deux situations lui font émettre des avertissements sonores et haptiques différenciés.
A l'allumage, un cycle d'initialisation débute pour calibrer les capteurs et fixer la hauteur par rapport au sol, il doit impérativement se dérouler sur un sol plat. Après 10/15 secondes, la fin du cycle s'achève par une petite musique.
C'est parti! Pour la détection d'obstacles au sol : tant que le sol est plat, aucun son n'est émis, quand un obstacle est détecté à la limite de détection (2m), il est signalé par une vibration haptique du moteur vibrant. Lorsqu'on se retrouve à la verticale de l'obstacle, des bips sonores sont émis.
Dans le cas d'un obstacle en hauteur, un bip sonore (différent de celui d'un obstacle au sol) se déclenche à environ 1m de distance.
Fichiers de fabrication
Le prototype est composé de deux parties à assembler, une première en contreplaqué qui contient l'électronique, et une seconde en plastique imprimé qui forme le manche.
boitier au format svg, pour découpe laser : boite_wicanne_3.svg
manche au format stl, pour impression 3D : manche_002.stl
Circuit électronique
Basé sur l'architecture à carte unique
Ex. | Composant | Type |
---|---|---|
1 | arduino nano | carte à microcontrôleur |
1 | module buzzer | buzzer piezo |
1 | module moteur | double pont en H L9110S |
2 | moteur vibrant | |
1 | écran LCD 2 lignes I2C | |
1 | ADXL335 | accéléromètre 3 axes analogique |
3 | module CJVL53L0XV2 | capteur ToF STmicroelectronics VL53L0X |
1 | module SRF05 | capteur de distance à ultrason |
/!\ le capteur ultrason est cablé pour fonctionner avec une seule broche, utilisée alternativement pour envoyer l'impulsion ultrasonique et pour la recevoir. Cette alternance est programmée dans le code.
/!\ L'accéléromètre représenté sur le circuit dans un bloc rouge n'est pas utilisé dans ce prototype. L'utilisa/teur/trice étant en mouvement, les données sont difficiles à interpréter.
Code du prototype
// Wi-Canne FABRIKARIUM ARIANE // 15/10/2019 AU 18/10/2019 /** * VL53L0X version 1.0.2 https://github.com/pololu/vl53l0x-arduino * LiquidCrystal_I2C version 1.1.2 https://github.com/marcoschwartz/LiquidCrystal_I2C * */ #include <Wire.h> #include <math.h> #include <VL53L0X.h> #include <LiquidCrystal_I2C.h> // Notes de musique pour musique après initialisation #define NOTE_B0 31 #define NOTE_C1 33 #define NOTE_CS1 35 #define NOTE_D1 37 #define NOTE_DS1 39 #define NOTE_E1 41 #define NOTE_F1 44 #define NOTE_FS1 46 #define NOTE_G1 49 #define NOTE_GS1 52 #define NOTE_A1 55 #define NOTE_AS1 58 #define NOTE_B1 62 #define NOTE_C2 65 #define NOTE_CS2 69 #define NOTE_D2 73 #define NOTE_DS2 78 #define NOTE_E2 82 #define NOTE_F2 87 #define NOTE_FS2 93 #define NOTE_G2 98 #define NOTE_GS2 104 #define NOTE_A2 110 #define NOTE_AS2 117 #define NOTE_B2 123 #define NOTE_C3 131 #define NOTE_CS3 139 #define NOTE_D3 147 #define NOTE_DS3 156 #define NOTE_E3 165 #define NOTE_F3 175 #define NOTE_FS3 185 #define NOTE_G3 196 #define NOTE_GS3 208 #define NOTE_A3 220 #define NOTE_AS3 233 #define NOTE_B3 247 #define NOTE_C4 262 #define NOTE_CS4 277 #define NOTE_D4 294 #define NOTE_DS4 311 #define NOTE_E4 330 #define NOTE_F4 349 #define NOTE_FS4 370 #define NOTE_G4 392 #define NOTE_GS4 415 #define NOTE_A4 440 #define NOTE_AS4 466 #define NOTE_B4 494 #define NOTE_C5 523 #define NOTE_CS5 554 #define NOTE_D5 587 #define NOTE_DS5 622 #define NOTE_E5 659 #define NOTE_F5 698 #define NOTE_FS5 740 #define NOTE_G5 784 #define NOTE_GS5 831 #define NOTE_A5 880 #define NOTE_AS5 932 #define NOTE_B5 988 #define NOTE_C6 1047 #define NOTE_CS6 1109 #define NOTE_D6 1175 #define NOTE_DS6 1245 #define NOTE_E6 1319 #define NOTE_F6 1397 #define NOTE_FS6 1480 #define NOTE_G6 1568 #define NOTE_GS6 1661 #define NOTE_A6 1760 #define NOTE_AS6 1865 #define NOTE_B6 1976 #define NOTE_C7 2093 #define NOTE_CS7 2217 #define NOTE_D7 2349 #define NOTE_DS7 2489 #define NOTE_E7 2637 #define NOTE_F7 2794 #define NOTE_FS7 2960 #define NOTE_G7 3136 #define NOTE_GS7 3322 #define NOTE_A7 3520 #define NOTE_AS7 3729 #define NOTE_B7 3951 #define NOTE_C8 4186 #define NOTE_CS8 4435 #define NOTE_D8 4699 #define NOTE_DS8 4978 #define TRACES 1 // 1 pour activer trace sur port série vers ordinateur, 0 pour la désactiver #define NB_CAPTEURS 3 // Nombre de capteurs IR #define NB_VIBREURS 2 // Nombre de vibreurs #define LONG_RANGE // Capteurs IR en mode long range #define NB_HIST 10 // Nombre de donnees sauvegardees dans l historique // Notes de musique pour BIP #define NOTE_PROTECTION_TETE 1500 #define NOTE_PROTECTION_TETE2 2500 // Le sonar prends des mesures toutes les 10 boucles #define SONAR_DIVIDER 10 // Compteurs de boucle int i = 0; int k = 0; int j = 0; // Informations stockees pour alarmes bool alarm_activate = false; int alarm_note1 = 0; int alarm_note2 = 0; // PINs const int in_buzz = 8; // Pin buzzer const int pingPin = 7; // Pin Ultrasonic sensor (SRF05) const int in_vib_sens1[NB_VIBREURS] = {9,10}; // PIN vibreurs sens 1 //const int in_vib_sens2[NB_VIBREURS] = {5,6}; // PIN vibreur sens 2 int note = 800; int durations[2] = {50.0,50.0}; const int xshut[3] = {2,3,4}; const int addr[3] = {0x25,0x30,0x35}; const double delay_init = 50.0; const double timeout_capteur = 50.0; const double delai_mesure = 10.0; int palvib[4] = {255, 140, 80, 50}; // forces de vibrations correspondant aux 4 paliers distances de chaque plage double data[NB_CAPTEURS]; // Table de stockage des données fournies par le lidar double hist_data[NB_CAPTEURS][NB_HIST]; double hist_temps[NB_CAPTEURS][NB_HIST]; double delta0; int idata = 0; long sonarCm=0; int sonarDivider=0; double hauteur0; int dist; // valeur de la distance mesurée int force; // force du signal reçu int ctrol; // somme de controle des données reçues (checksum) int corr = 5; // valeur de correction de la mesure lue en cm int x_adc_value; int y_adc_value; int z_adc_value; double x_g_value; double y_g_value; double z_g_value; double roll, pitch, yaw; // notes in the melody: int melody[] = { NOTE_E7,NOTE_E7,0,NOTE_E7,0,NOTE_C7,NOTE_E7,0,NOTE_G7,0,0,0,NOTE_G6,0,0,0 }; // note durations: 4 = quarter note, 8 = eighth note, etc.: int noteDurations[] = { 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, }; LiquidCrystal_I2C lcd(0x3f,20,4); // déclaration afficheur LCD série I2C VL53L0X sensor[NB_CAPTEURS]; void musique_init() { // iterate over the notes of the melody: for (int thisNote = 0; thisNote < 16; thisNote++) { // to calculate the note duration, take one second // divided by the note type. //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. int noteDuration = 1000 / noteDurations[thisNote]; tone(8, melody[thisNote], noteDuration); // to distinguish the notes, set a minimum time between them. // the note's duration + 30% seems to work well: int pauseBetweenNotes = noteDuration * 1.30; delay(pauseBetweenNotes); // stop the tone playing: noTone(8); } } void setup() { #if TRACES // si mise au point activée Serial.begin(115200); // vitesse du port de communication avec l'ordinateur #endif // LCD INIT lcd.init(); // initialize the lcd lcd.backlight(); lcd.setCursor(0,0); lcd.print("Hello, world!"); // SENSOR INIT Serial.println("Start wire init."); Wire.begin(); // Ouverture capteurs et extinction for(k=0;k<NB_CAPTEURS;k++) { pinMode(xshut[k], OUTPUT); digitalWrite(xshut[k], LOW); } // Ouverture vibreurs et extinction for(k=0;k<NB_VIBREURS;k++) { pinMode(in_vib_sens1[k], OUTPUT); //pinMode(in_vib_sens2[k], OUTPUT); digitalWrite(in_vib_sens1[k], LOW); //digitalWrite(in_vib_sens2[k], LOW); analogWrite(in_vib_sens1[k], 255); //analogWrite(in_vib_sens2[k], 255); } // Ouverture des capteurs et nommage for (k=0;k<NB_CAPTEURS;k++) { // Ouverture capteur delay(delay_init); digitalWrite(xshut[k], HIGH); delay(delay_init); // nommage sensor[k].setAddress(addr[k]); // Affichage lcd.setCursor(0,1); lcd.print(k); Serial.println("starting sensor "); Serial.println(k); delay(delay_init); // Demarrage capteur sensor[k].init(); sensor[k].setTimeout(timeout_capteur); #if defined LONG_RANGE sensor[k].setSignalRateLimit(0.1); sensor[k].setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18); sensor[k].setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14); #endif sensor[k].startContinuous(); delay(delay_init); } // 10 premieres mesures for (j=0;j<NB_HIST*10;j++) { recuperer_data(); } // ecart d1 d2 sur sol lisse delta0 = abs(recuperer_moyenne(2) - recuperer_moyenne(1)); hauteur0 = abs(recuperer_moyenne(0)); Serial.println(delta0); Serial.println(hauteur0); musique_init() ; } void alarm(bool activate,int note1,int note2,int priority) { if (activate) { if ((!alarm_activate) || priority > 0) { alarm_activate = activate; alarm_note1 = note1; alarm_note2 = note2; } } } void jouer_alarm() { if (alarm_activate) { tone(in_buzz,alarm_note1,durations[0]); if (alarm_note1 != alarm_note2){ delay(100.0); tone(in_buzz,alarm_note2,durations[1]); } } else{ noTone(in_buzz); } } void vibration(int k, boolean vibrate,double niveau) { if(vibrate) { analogWrite(in_vib_sens1[k], int(255-(niveau*255))); } else { analogWrite(in_vib_sens1[k], 255); } } void decalage_historique() { for (int k=0; k<NB_CAPTEURS; k++) { for (int j=1;j<NB_HIST; j++) { hist_data[k][j-1] = hist_data[k][j]; } } } void recuperer_data() { decalage_historique(); // Distances senseurs for (k=0;k<NB_CAPTEURS;k++) { data[k] = sensor[k].readRangeContinuousMillimeters(); if ((data[k] < 2000.0) && (data[k] > 100.0)) { hist_data[k][idata] = data[k]; hist_temps[k][idata] = millis(); } else{ data[k] = 2000.0; } // Affichage distances lcd.setCursor(0,k); lcd.print(" "); lcd.setCursor(0,k); lcd.print(data[k]); } delay(delai_mesure); idata = idata + 1; if (idata >= NB_HIST) { idata = NB_HIST-1; } sonarDivider++; if(sonarDivider > SONAR_DIVIDER) { sonarDivider=0; // The PING))) is triggered by a HIGH pulse of 2 or more microseconds. // Give a short LOW pulse beforehand to ensure a clean HIGH pulse: pinMode(pingPin, OUTPUT); digitalWrite(pingPin, LOW); delayMicroseconds(2); digitalWrite(pingPin, HIGH); delayMicroseconds(5); digitalWrite(pingPin, LOW); pinMode(pingPin, INPUT); sonarCm = microsecondsToCentimeters(pulseIn(pingPin, HIGH)); } } double recuperer_moyenne(int k) { double moy; int nmes = 0; // Moyenne groupe 1 moy = 0.0; for (j=0;j<NB_HIST;j++) { if (hist_data[k][j] < 2000.0) { moy = moy + hist_data[k][j]; nmes = nmes + 1; } } moy = moy / double(nmes); return moy; } double delta_mesure(int k) { double moy1,moy2,der; // Moyenne groupe 1 moy1 = 0.0; for (j=0;j<NB_HIST/2;j++) { moy1 = moy1 + hist_data[k][j]; } moy1 = moy1 / double(NB_HIST/2); // Moyenne groupe 2 moy2 = 0.0; for (j=NB_HIST/2;j<NB_HIST;j++) { moy2 = moy2 + hist_data[k][j]; } moy2 = moy2 / double(NB_HIST/2); der = (moy2 - moy1); return der; } void loop() { alarm_activate = false; // Recuperation donnees senseurs recuperer_data(); double saut = abs(data[1]-data[2]); double dsaut = abs(saut - delta0); // Detection if ( (dsaut > 150.0) && (abs(data[1])<2000.0) && (abs(data[2])<2000.0)) { vibration(1,true,1.0);//max(dsaut/200.0, 1.0)); } else { vibration(1,false,0.0); } // Test saturation if ( (abs(data[1])<2000.0) || (abs(data[2])<2000.0)) { //alarm(true,NOTE_PROTECTION_TETE,NOTE_PROTECTION_TETE2,1); } Serial.print(data[0]); Serial.print(" "); Serial.print(data[1]); Serial.print(" "); Serial.println(data[2]); if ( (abs(data[1])<2000.0) && (abs(data[2])<2000.0)){ // Affichage distances lcd.setCursor(0,3); lcd.print(" "); lcd.setCursor(0,3); lcd.print(delta0); } // trottoir if (abs(delta_mesure(0)) > 100.0) { alarm(true,note,note,0); } // Protection tete Serial.print(sonarCm); Serial.print(" "); Serial.println(abs(1800-hauteur0)); if (sonarCm > 10 && (sonarCm*10.0) < abs(1800.0-hauteur0)) { //alarm(true,NOTE_PROTECTION_TETE,NOTE_PROTECTION_TETE,0); alarm(true,NOTE_PROTECTION_TETE,NOTE_PROTECTION_TETE2,1); } jouer_alarm(); } //from arduino ping sample. long microsecondsToCentimeters(long microseconds) { // The speed of sound is 340 m/s or 29 microseconds per centimeter. // The ping travels out and back, so to find the distance of the // object we take half of the distance travelled. return microseconds / 29 / 2; } //****************************************************/
Capteurs testés
Les capteurs de distance sont basés sur plusieurs principes techniques : Lidar, Ultrason, LED ou VCSEL (diodes laser), ... Voir lexique en bas de cette page
Capteur | Type de détection | Champ de vue | intervalle de distance | fréquence | consommation |
---|---|---|---|---|---|
VL53L0X | VCSEL (940 nm) | 25° | 5 à 120 cm | ? | 20 mA |
VL53L1X | VCSEL (940nm) | 27° | 4 à 400 cm | 50 Hz | 20 mA ? |
TFMini | led infrarouge (850nm) | 2,3° | 30 à 1200 cm | 100 Hz | 120 mA max |
TFMini plus | led infrarouge (850 nm) | 3,6° | 10 à 1200 cm | 1000 Hz max. | ? |
HC-SR05 | ultrason | 15° | 2 à 450 cm | 40 Hz max | 10 à 40 mA |
SRF05 | ultrason 40 kHz | 30° | 4 à 400 cm | 20 Hz max | ? |
(Données assemblées à partir de sources diverses, à prendre avec des pincettes!)
Sur la comparaison des caractéristiques de différents types de capteurs de distance, voir aussi https://www.sparkfun.com/distance_sensor_comparison_guide
Caractéristiques de différents transducteurs à ultrasons : https://www.robot-electronics.co.uk/htm/sonar_faq.htm
Carte CJVL53L0XV2
Carte de développement pour le capteur ToF miniature VL53L0X de STMicroelectronics. Ce capteur fonctionne dans un intervalle de 5 à 120 cm en utilisant un laser infrarouge dans un champ de vue (FoV) de 25°. La communication se fait par le protocole I2C.
documentation
ressources
Carte VL53L1X-SATEL
Carte de développement pour le capteur miniature ToF VL53L1X. Ce capteur permet de réaliser des mesures de distance entre 4 et 400 cm en utilisant un laser infrarouge à une fréquence de 50Hz, dans un champ de vue (FOV) de 27°. Ce capteur se base sur la technologie FlightSense, brevetée par ST Microelectronics.
Le brochage n'est pas clairement indiqué sur la carte, le voici :
documentation
ressources
- https://www.st.com/en/evaluation-tools/vl53l1x-satel.html
- https://www.st.com/en/imaging-and-photonics-solutions/vl53l1x.html
Capteur accéléromètre ADXL335 (Adafruit)
Accéléromètre trois-axes basé sur la puce ADXL335 d'Analog Devices. Ce capteur renvoie 3 mesures analogiques de la gravité sur les axes X, Y, Z. Le module utilisé ici est concçu et fabriqué par Adafruit, il permet de récupérer les mesures à une fréquence de 50 Hz.
Ce capteur doit être calibré si on veut obtenir des mesures précises.
documentation
ressources
- https://learn.adafruit.com/adafruit-analog-accelerometer-breakouts?view=all
- https://www.analog.com/en/products/adxl335.html#product-documentation
Capteur Benewake TFmini
Il s'agit d'un capteur ToF vendu sous l'appellation LiDAR, toutefois il utilise plutôt une diode LED infrarouge et une optique adéquate. Ce capteur permet de mesurer une distance comprise entre 30 centimètres et 12 mètres, à une fréquence de 100Hz, soit 100 mesures par seconde. Il fonctionne à une tension de 5v et communique par une interface série (UART).
zone de détection
- 1 : zone aveugle du capteur, entre 0 et 30cm, la distance mesurée n'est pas fiable
- 2 : zone de détection en conditions extrêmes (lumière exterieure très forte > 100 kLux et réflexion sur une surface très faiblement réfléchissante)
- 3 : zone de détection en lumière extérieure (autour de 70 kLux)
- 4 : zone de détection en intérieur ou par faible lumière extérieure
- 5 : distance latérale dans laquelle la mesure peut être considérée comme fiable
documentation
ressources externes
- fabricant : Benewake
- https://learn.sparkfun.com/tutorials/tfmini---micro-lidar-module-hookup-guide
- https://wiki.dfrobot.com/TF_Mini_LiDAR_ToF__Laser_Range_Sensor_SKU__SEN0259
Capteur Benewake TFmini Plus
Capteur conçu sur le même principe que le TFmini. Il s'en différencie sur plusieurs points :
- boîtier IP65, résistant à l'eau et à la poussière
- suffisamment résistant aux vibrations pour être installé sur un drone
- trajet de lumière optimisé et algorithme différent pour minimiser l'influence du milieu lumineux, cf. zone de détection ci-dessous
- fréquence des mesures ajustable jusqu'à 1000 Hz
Ce capteur est conçu à partir du circuit intégré OPT3101 de Texas Instruments.
zone de détection
- 1 : zone aveugle du capteur entre 0 et 10 cm
- 2 : zone de détection d'un objet très faiblement réfléchissant (reflectivité de 10%)
- 3 : zone de détection de d'un objet réfléchissant (réflectivité de 90%)
documentation
Ressources
- fabricant : Benewake
- capteur TI OPT3101 : http://www.ti.com/tool/BW-3P-TFMINI-PLUS + http://www.ti.com/product/OPT3101
Capteur de distance à ultrason HC-SR05 (Velleman VMA306)
Ce capteur permet de mesurer la distance avec un obstacle en mesurant le temps de rebond d'une impulsion ultrasonique. Il est capable de détecter un obstacle à une distance maximum de 4,5m.
zone de détection
Ce schéma correspond au module HC-SR04 qui utilise le même type de transducteur ultrasonique.
documentation
- datasheet
- schéma électrique
- distributeur : Velleman
Capteur de distance à ultrason SRF05
Ce capteur permet de mesurer la distance avec un obstacle en mesurant le temps de rebond d'une impulsion ultrasonique à 40 kHz. Il est basé sur le transducteur ultrasonique T400S16
zone de détection
D'après la datasheet du fabricant du transducteur.
documentation
Journal du Fabrikarium ArianeGroup
jour 1
Dans un premier temps, faire connaissance! Définir les besoins et les replacer dans un contexte existant. Puis l'équipe se concentre en deux pôles : test de différents types de capteurs pour le groupe électronique et pour le groupe design : recherches pour l'ergonomie et la conception de l'objet.
En fin de journée, le groupe ergonomie a conçu une poignée intégrant batteries et circuit de charge ("poignée rechargeable"). Sur le plan électronique deux architectures électroniques sont posées comme des pistes possibles et un ensemble conséquent de capteurs a pu être testé.
jour 2
"Copier c'est gagné" dixit Olivier, l'équipe décide d'intégrer une powerbank dans la poignée pour résoudre la partie alimentation : ce type de batterie intègre un régulateur de charge, alimente en 5V un port USB, et surtout est très simple à se procurer, banco!
Le groupe électronique poursuit les recherches sur les capteurs par des tests sur les capteurs VCSEL L'après-midi est consacrée à la conception et l'assemblage du circuit du premier prototype
Le groupe design définit l'ergonomie de la poignée et poursuit la recherche sur les matériaux et le design. Le choix des matériaux du prototype est contraint par le temps limité... En fin de journée, les impressions 3D sont lancées pour sortir pendant le nuit
jour 3
"il faut se hâter lentement" dixit Yves, aux prises avec le traitement des données de capteur
Le groupe design découvre le résultat des 3 impressions lancées pendant la nuit : 2 sont parfaitement imprimées, la 3e, un grip de poignée en filament flex n'a pas correctement été imprimé. Il reste peu de temps et la fabrication du boîtier qui accueillera le système électronique est lancée!
Au plan électronique, un premier prototype du circuit est achevé et l'équipe s'attelle à peaufiner le code du programme arduino.
Annexe : circuits de test
distance et mouvement
Un circuit test est réalisé pour tester différents composants, il intègre un accéléromètre analogique, un capteur de distance TFmini et un afficheur LCD pour le retour d'informations.
// Télémètre Lidar TFmini avec CARTE ARDUINO MEGA 2560 // YLC 16/10/2019 /*--------------------------------------------------- Pour montage sur une carte avec 1 seul port série (UNO, NANO) definir un port série logiciel pour le lidar : (NB : ralentit les mesures car cela prend des temps de cycle du contrôleur) #include<SoftwareSerial.h> // définit un port série logiciel SoftwareSerial Serial1(2,3); // nomme le port logiciel "Serial1" (pin2 = RX, pin3 = TX) Sur carte Arduino MEGA utiliser directement les ports série Serial et Serial1 brochages MEGA : 18 TX du lidar 19 RX du lidar 20 SDA du LCD 21 SCL du LCD Format des données reçues du lidar (9 octets) : (le code d'en-tête est répété dans les 2 premiers octets) les valeurs distance et force sont fournies sur 2 octets en little endian (poids faible en premier) octet0=0x59 octet1=0x59 octet2=Dist_faible octet3=Dist_fort octet4=force_faible octet5=force_fort octet6=mode octet7=0x00 octet8=somme de contrôle -----------------------------------------------------*/ #include <Wire.h> #include <math.h> #include <LiquidCrystal_I2C.h> int i = 0; // index sur la table de stockage const int entete = 0x59; // Identifiant du flux de données const int x_out = A1; /* connect x_out of module to A1 of UNO board */ const int y_out = A2; /* connect y_out of module to A2 of UNO board */ const int z_out = A3; /* connect z_out of module to A3 of UNO board */ int data[9]; // Table de stockage des données fournies par le lidar int dist; // valeur de la distance mesurée int force; // force du signal reçu int ctrol; // somme de controle des données reçues (checksum) int corr = 5; // valeur de correction de la mesure lue en cm int x_adc_value; int y_adc_value; int z_adc_value; double x_g_value; double y_g_value; double z_g_value; double roll, pitch, yaw; LiquidCrystal_I2C lcd(0x3f,20,4); // déclaration afficheur LCD série I2C #define TRACES 1 // 1 pour activer trace sur port série vers ordinateur, 0 pour la désactiver void setup() { #if TRACES // si mise au point activée Serial.begin(115200); // vitesse du port de communication avec l'ordinateur #endif Serial1.begin(115200); // vitesse du port de communication avec le LiDAR lcd.init(); // initialize the lcd // Print a message to the LCD. lcd.backlight(); lcd.setCursor(0,0); lcd.print("Hello, world!"); } void loop() { // Accelerometre x_adc_value = analogRead(x_out); /* Digital value of voltage on x_out pin */ y_adc_value = analogRead(y_out); /* Digital value of voltage on y_out pin */ z_adc_value = analogRead(z_out); /* Digital value of voltage on z_out pin */ x_g_value = ( ( ( (double)(x_adc_value * 5)/1024) - 1.65 ) / 0.330 ); /* Acceleration in x-direction in g units */ y_g_value = ( ( ( (double)(y_adc_value * 5)/1024) - 1.65 ) / 0.330 ); /* Acceleration in y-direction in g units */ z_g_value = ( ( ( (double)(z_adc_value * 5)/1024) - 1.80 ) / 0.330 ); /* Acceleration in z-direction in g units */ roll = ( ( (atan2(y_g_value,z_g_value) * 180) / 3.14 ) + 180 ); pitch = ( ( (atan2(z_g_value,x_g_value) * 180) / 3.14 ) + 180 ); // Affichage LCD valeurc accelerometre lcd.setCursor(0,3); lcd.print(roll); lcd.print(" "); lcd.print(pitch); if (Serial1.available()) { lcd.setCursor(0,0); lcd.print("OK "); // test si données fournies par le lidar // Lecture entete 1 data[0] = Serial1.read(); if (data[0] == entete) { data[1] = Serial1.read(); if (data[1] == entete) { i=1; for(i = 2; i < 9; i++) { // on lit les 7 octets suivants data[i] = Serial1.read(); // on stocke les données lues dans un tableau } ctrol = data[0]+data[1]+data[2]+data[3]+data[4]+data[5]+data[6]+data[7]; if(data[8] == (ctrol&0xff)) { dist = data[2]+data[3]*256; force = data[4]+data[5]*256; if (dist < 1e-5) { dist = 0.0; } lcd.setCursor(7, 0); // affichage mesures sur l'écran LCD lcd.print(dist); lcd.print(" cm "); lcd.setCursor(7, 1); lcd.print(force); lcd.print(" "); } } } else { } } else { lcd.setCursor(0,0); lcd.print("ERREUR"); // test si données fournies par le lidar } }
Annexe : lexique
FoV (Field of View) : champ de vue, angle de sensibilité d'un capteur optique. cf. https://fr.wikipedia.org/wiki/Champ_de_vue
laser (light amplification by stimulated emission of radiation) : « amplification de la lumière par émission stimulée de radiation » rayonnement lumineux spatialement et temporellement cohérent basé sur l'effet laser. cf. https://fr.wikipedia.org/wiki/Laser
LiDAR (Light Detection and Ranging) : procédé d'estimation de la distance à l'aide d'un faisceau lumineux, en général produit par un laser. cf. https://fr.wikipedia.org/wiki/Lidar
lux unité de mesure de l'éclairement lumineux. cf. https://fr.wikipedia.org/wiki/Lux_(unit%C3%A9)
ToF (Time of Flight) : Mesure du temps nécessaire pour parcourir une distance. cf. https://en.wikipedia.org/wiki/Time_of_flight
UART (Universal asynchronous receiver-transmitter) : interface hardware de communication asynchrone, dont le type de données et la vitesse de transmission peut-être définie. cf. https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter
VCSEL (Vertical-Cavity Surface-Emitting Laser) : à prononcer vixel! diode laser à cavité verticale émettant par la surface. cf. https://fr.wikipedia.org/wiki/Diode_laser_%C3%A0_cavit%C3%A9_verticale_%C3%A9mettant_par_la_surface
Journal de bord
Mise à jour du projet 09/12/2019
Mise à jour du projet 24/12/2019
Fichiers sources
Mise à jour du projet 08/03/2021 : Documentation PDF de la V3.44
Cliquer sur le texte pour voir le pdf dans son intégralité.