Différences entre versions de « Projets:Wi-canne »

De wikiup
Sauter à la navigation Sauter à la recherche
 
(37 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 fabrikarium arianegroup aux Mureaux du 16 au 18 octobre 2019.   
+
[[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.   
Ligne 10 : Ligne 14 :
  
 
Ce projet fait suite au projet de [[Projets:GantSonar|gant sonar]]
 
Ce projet fait suite au projet de [[Projets:GantSonar|gant sonar]]
 +
 +
<br clear="all" />
  
 
= Analyse de l'existant =
 
= Analyse de l'existant =
Ligne 22 : Ligne 28 :
  
 
* Porteur de projet : François
 
* Porteur de projet : François
* Contribut/eur/rice/s : Aymeric (ArianeGroup), Bruno (ArianeGroup), Laurraine (ArianeGroup), Océane (ArianeGroup), Olivier (ArianeGroup), Robin (ArianeGroup), Yves (My Human Kit)
+
* 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 43 : 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
  
Ligne 53 : Ligne 60 :
  
 
[[File:conception_boitier_croquis.jpg|700px|none|alt=croquis préparatif]]
 
[[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 =
Ligne 69 : 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 ==
+
= 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-canne_v1_circuit_ultrason.png|800px|none|alt=schéma du circuit du premier prototype]]
 
[[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.
 
/!\ 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 n'est pas utilisé dans ce prototype. L'utilisa/teur/trice étant en mouvement, les données sont difficiles à interpréter.
+
/!\ 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 125 : 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 130 : 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 258 : 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]
  
= Circuits de test =
+
== 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 382 : Ligne 990 :
 
</nowiki>
 
</nowiki>
  
= Lexique =
+
= 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 397 : Ligne 1 005 :
  
 
'''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
 
'''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


Photos prototypes v0 et v1.jpg

Description du projet

Wi-canne.png

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.

montage croquis préparatifs
croquis préparatif

Le prototype est fabriqué à partir du croquis suivant :

croquis du prototype

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 à carte électronique unique

Version sans fil à 2 cartes

version sans fil

Premier prototype

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

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

schéma du circuit du premier prototype
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.

photo du boîtier avec électronique intégrée

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 VL53L0X

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 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 :

brochage de la carte VL53L1X-SATEL

documentation

ressources

Capteur accéléromètre ADXL335 (Adafruit)

capteur ADXL335

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

Capteur Benewake TFmini

caption capteur 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

Capteur Benewake TFmini Plus

caption capteur 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

Capteur de distance à ultrason HC-SR05 (Velleman VMA306)

capteur de distance à ultrason HC-SR05

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.

zone de détection du capteur ultrasonique

documentation

Capteur de distance à ultrason SRF05

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.

zone de détection du capteur ultrasonique

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.

circuit de test capteurs

// 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

Documentation PDF de la V2.3

Mise à jour du projet 24/12/2019

Documentation PDF de la V2.33


Fichiers sources

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é.

WiCanne v3-44.pdf


Fichiers sources v3.44

Télécharger les fichiers source (Code arduino, librairie Nano Edge (IA), acquisition de données, pdf de la doc