IMA3/IMA4 2018/2020 P17 : Différence entre versions
(→Semaine 6) |
(→Semaine 3) |
||
(193 révisions intermédiaires par 3 utilisateurs non affichées) | |||
Ligne 518 : | Ligne 518 : | ||
*Equipe1: Webcam -> Ming Chen | *Equipe1: Webcam -> Ming Chen | ||
*Equipe2: Lidar + Configuration de la raspberry -> Jing Hua | *Equipe2: Lidar + Configuration de la raspberry -> Jing Hua | ||
− | *Equipe3: | + | *Equipe3: Actionneurs -> Emilie Raouto |
Donc on sépare la tâche totale en 3 partie: | Donc on sépare la tâche totale en 3 partie: | ||
*Une partie de webcam pour détecter les feux qui doit connaître le feu rouge, et si le feu maintenant est rouge il doit envoyer un message pour activer le vibreur et aussi le son. | *Une partie de webcam pour détecter les feux qui doit connaître le feu rouge, et si le feu maintenant est rouge il doit envoyer un message pour activer le vibreur et aussi le son. | ||
− | *Une partie de lidar pour scanner l'environnement de l'utilisateur, et si une voiture (ou une chose rapide, un vélo par exemple) qui approche avec une vitesse dangereuse, il doit | + | *Une partie de lidar pour scanner l'environnement de l'utilisateur, et si une voiture (ou une chose rapide, un vélo par exemple) qui approche avec une vitesse dangereuse, il doit envoyer un message pour activer le vibreur et le son pareil qu'un webcam. Dans notre cas ici on considère avec 20km/h est déjà dangereuse dans la ville pour l'enfant souleyman. |
− | *Une partie | + | *Une partie pour la gestion des actionneurs qui contient 4 vibreurs et un haut-parleur. On utilise 4 vibreurs pour indiquer les niveaux de l'alerte qui dépend de la vitesse de la voiture et la distance entre la voiture et l'utilisateur. |
Donc pendant cette séance chaqu'un étudie sa tâche et aussi la configuration de la raspberry. Tous les travaux de préparation doivent être fait avant des matériaux arrivent. | Donc pendant cette séance chaqu'un étudie sa tâche et aussi la configuration de la raspberry. Tous les travaux de préparation doivent être fait avant des matériaux arrivent. | ||
Ligne 565 : | Ligne 565 : | ||
Pour la partie lidar, la tâche est de scanner l'environnement, c'est-à-dire 360 degrés au tour de l'utilisateur (dans notre prototype est 180 degrés car on veut tester le fonctionnement pour un côté d'adord), puis envoyer ces valeur dans un tableau (c'est juste pour suivre le format du code donné par l'entreprise sur github) à la raspberry pour qu'elle peut calculer la vitesse de chaque élément mobile (normalement on considère un élément avec une vitesse 0 ou avec une petite vitesse) selon le tableau. Et si cette vitesse arrive à la limite qu'on propose au début, la raspberry va contrôler ses sorties pour avtiver le vibreurs.(Comme au début l'équipe n'ai pas fait la fusion avec l'équipe 3 qui fait la partie son de la rasberry, le seul actionneur pour instant est de vibreur) | Pour la partie lidar, la tâche est de scanner l'environnement, c'est-à-dire 360 degrés au tour de l'utilisateur (dans notre prototype est 180 degrés car on veut tester le fonctionnement pour un côté d'adord), puis envoyer ces valeur dans un tableau (c'est juste pour suivre le format du code donné par l'entreprise sur github) à la raspberry pour qu'elle peut calculer la vitesse de chaque élément mobile (normalement on considère un élément avec une vitesse 0 ou avec une petite vitesse) selon le tableau. Et si cette vitesse arrive à la limite qu'on propose au début, la raspberry va contrôler ses sorties pour avtiver le vibreurs.(Comme au début l'équipe n'ai pas fait la fusion avec l'équipe 3 qui fait la partie son de la rasberry, le seul actionneur pour instant est de vibreur) | ||
+ | |||
+ | '''Actionneur: Audio''' | ||
+ | |||
+ | Pour cette partie, nous voulons tester l'audio de la raspberry et cherchons aussi la commande adéquate à utiliser pour l'audio. | ||
+ | OMXPlayer est un lecteur multimédia en ligne de commande Installé sur Raspbian. Il peut lire de nombreux formats de fichiers audio. La ligne de commande la plus simple est omxplayer <nom du fichier média>. Le fichier multimédia peut être un fichier audio ou vidéo. | ||
+ | ssh raspberry pi | ||
+ | omxplayer son.mp3 | ||
+ | |||
+ | Ainsi, pour la gestion des actionneurs il nous faut premièrement répertorié les différentes situations qui auront pour conséquence l'activation de l'audio ou des vibreurs. Puis, nous allons faire des enregistrements associés à chaque situations avec une voix féminine pour apaiser le petit garçon. Enfin, nous mettrons tout ceci en code relié au code du capteur et webcam parce que c'est grace à ses différentes valeurs: vitesse de voitures, distance , détection de feux de signalisation (rouge, vert ) que nous pourront demander activation. | ||
+ | |||
+ | [[Fichier:prog_P17.png|700px]] | ||
===Semaine 3=== | ===Semaine 3=== | ||
Ligne 585 : | Ligne 596 : | ||
ifconfig | ifconfig | ||
Avec cette commande on peut aussi avoir l'addresse de la raspberry pour faire une connextion SSH ou VNC. | Avec cette commande on peut aussi avoir l'addresse de la raspberry pour faire une connextion SSH ou VNC. | ||
− | Jusqu'à maintenant on peut utiliser la commande suivante pour faire une connextion SSH dans n'importe quel terminal sur un PC | + | Jusqu'à maintenant on peut utiliser la commande suivante pour faire une connextion SSH dans n'importe quel terminal sur un PC. |
ssh pi@172.20.10.3 | ssh pi@172.20.10.3 | ||
− | Ici l'addresse IP est l'addresse qu'on a trouvé avec la méthode précédente. Et on peut voir | + | Ici l'addresse IP est l'addresse qu'on a trouvé avec la méthode précédente. Et on peut voir sa réponse. |
− | |||
− | |||
Et pour la connextion VNC est un peu près la même méthode sachant l'addresse IP de la raspberry, on utilise l'outil "connextion en distance" sur le système Windows: | Et pour la connextion VNC est un peu près la même méthode sachant l'addresse IP de la raspberry, on utilise l'outil "connextion en distance" sur le système Windows: | ||
Ligne 595 : | Ligne 604 : | ||
[[Fichier:p17_connextion_VNC.png|300px]] | [[Fichier:p17_connextion_VNC.png|300px]] | ||
− | On juste entre l'addresse IP dans la fenêtre de clique connecter ensuite on peut voir la fenêtre pour entrer le nom et le mot de passe de la raspberry | + | On juste entre l'addresse IP dans la fenêtre de clique connecter ensuite on peut voir la fenêtre pour entrer le nom et le mot de passe de la raspberry. |
− | |||
− | |||
Pareil que la raspberry qu'on a vu en cours Réseau en semestre 7, le nom on met pi et le mot de passe est raspberry. | Pareil que la raspberry qu'on a vu en cours Réseau en semestre 7, le nom on met pi et le mot de passe est raspberry. | ||
− | Ensuite on peut voir le desktop qu'on avait prévu en semestre 7 | + | Ensuite on peut voir le desktop qu'on avait prévu en semestre 7. |
− | |||
− | |||
Jusqu'à maintenant on a tout finir la préparation de la réalisation de projet, et maintenant on commence à travailler sur le webcam et le lidar. | Jusqu'à maintenant on a tout finir la préparation de la réalisation de projet, et maintenant on commence à travailler sur le webcam et le lidar. | ||
Ligne 681 : | Ligne 686 : | ||
Le résultat montré dans la figure ci-dessous montre qu'OpenCV a été installé avec succès dans l'environnement Python3. | Le résultat montré dans la figure ci-dessous montre qu'OpenCV a été installé avec succès dans l'environnement Python3. | ||
− | [[Fichier: | + | '''Actionneurs: Différentes situations''' |
+ | |||
+ | Ici, nous voulons répertorier les différentes situations que pourrait rencontrer l'enfant à une sortie d'école par exemple. Le but étant de lui faire savoir qu'il a la possibilité de traverser en tenant compte des voitures à proximité (distance et vitesse) , de la couleur des feux de signalisation. Aussi nous devons lui dire s'il y'a un danger à venir, lui donner les indications adéquates concernant le danger. | ||
+ | |||
+ | [[Fichier:situations.png|400px]] | ||
+ | |||
+ | Nous avons 4 types d'alertes selon les situations : | ||
+ | |||
+ | Alerte_arrêt indique que le feu est rouge et donc qu'il ne peut pas traverser: Attente | ||
+ | Alerte_proche indique que la voiture est assez proche de l'enfant et même si le feu est au vert, il ne peut pas traverser | ||
+ | Alerte_danger indique que la voiture arrive avec une grande vitesse et donc il ne peut pas traverser | ||
+ | Alerte0 indique voiture assez éloignée et feu vert alors il peut traverser | ||
===Semaine 4=== | ===Semaine 4=== | ||
Ligne 777 : | Ligne 793 : | ||
Ce sont le base avant étudier certaines applications qu'il donne pendant la prochaine séance. | Ce sont le base avant étudier certaines applications qu'il donne pendant la prochaine séance. | ||
+ | |||
+ | '''Indications sonores''' | ||
+ | |||
+ | Selon l'alerte et la situation nous voulons donner les différentes indications sonores qui conviennent | ||
+ | |||
+ | [[Fichier:indications_P17.png|500px]] | ||
+ | |||
+ | Ainsi, nous faisons des enregistrements de ces phrases en utilisant une voix féminine. Les différentes indications explicitent clairement ce que Souleymane doit faire à l'instant. Soit il peut traverser, soit il doit patienter. lorsqu'il y a l'alerte danger, il y a aussi l'activation des vibreurs par intermittence selon la vitesse. | ||
+ | Nous devons aussi veiller à ce que les enregistrements ne brusque pas mais plutôt ramène l'attention de Souleymane à la situation en douceur. | ||
+ | Ces enregistrements seront convertis en fichier.wav . | ||
===Semaine 5=== | ===Semaine 5=== | ||
Ligne 840 : | Ligne 866 : | ||
hue(couleur): décrit la couleur, utile lors de la division d'objets en fonction de la couleur | hue(couleur): décrit la couleur, utile lors de la division d'objets en fonction de la couleur | ||
− | saturation: du blanc au noir | + | |
+ | saturation: du blanc au noir. | ||
+ | |||
Value: décrit la luminosité ou la densité de la couleur | Value: décrit la luminosité ou la densité de la couleur | ||
Ligne 913 : | Ligne 941 : | ||
Pour un premier teste, on veut commencer par détecter la vitesse d'un point(ou plutôt une orientation) par l'étapes suivantes: | Pour un premier teste, on veut commencer par détecter la vitesse d'un point(ou plutôt une orientation) par l'étapes suivantes: | ||
+ | |||
1.On fixe un point/une orientation supposant que c'est cette orientation où la voiture vient. On propose l'orientation dans l'axe de RPLIDAR est 0 degré pendant notre test. Et ici il faut montrer cet axe de RPLIDAR pour on peut imaginer le fonctionnement: | 1.On fixe un point/une orientation supposant que c'est cette orientation où la voiture vient. On propose l'orientation dans l'axe de RPLIDAR est 0 degré pendant notre test. Et ici il faut montrer cet axe de RPLIDAR pour on peut imaginer le fonctionnement: | ||
Ligne 932 : | Ligne 961 : | ||
===Semaine 6=== | ===Semaine 6=== | ||
− | + | '''La conception spécifique de la section webcam''' | |
− | + | Après avoir compris les principales fonctions de openCV, je pense que pour réaliser la fonction d'identification des feux de circulation, vous devez commencer par la manière la plus simple d'identifier les feux rouges. Pour y parvenir, je pense que ma fonction peut être divisée en les parties suivantes: | |
− | [[Fichier: | + | 1. Détectez la position de la lumière rouge de la webcam en temps réel.(Déjà réalisé) |
+ | |||
+ | 2. Reconnaître le rouge avec l'espace HSV. | ||
+ | |||
+ | 3. Binarisez la partie rouge d'origine en blanc et le reste en noir. | ||
+ | |||
+ | 4. Tracez un cercle sur la zone rouge. | ||
+ | |||
+ | 5. Démarrez le shaker. | ||
+ | |||
+ | 6. Améliorez et complétez. | ||
+ | |||
+ | '''Reconnaître le rouge avec l'espace HSV''' | ||
+ | |||
+ | *Modèle HSV | ||
+ | |||
+ | Il est difficile de trouver la gamme exacte de rouge en contrôlant les canaux RVB, j'ai donc choisi l'espace HSV. HSV (Hue, Saturation, Value) est un espace colorimétrique créé à partir des caractéristiques intuitives des couleurs. | ||
+ | Les paramètres de couleur dans le modèle HSV sont: teinte (H: teinte), saturation (S: saturation) et luminosité (V: valeur). Un espace colorimétrique créé par A. R. Smith en 1978, également connu sous le nom de modèle hexagonal. | ||
+ | |||
+ | Teinte (H: teinte): Mesurée en termes d'angles, allant de 0 ° à 360 °, en comptant dans le sens antihoraire du rouge, 0 du rouge, 120 ° du vert et 240 ° du bleu. Leurs couleurs complémentaires sont: 60 ° pour le jaune, 180 ° pour le cyan et 300 ° pour le magenta; | ||
+ | |||
+ | Saturation (S: saturation): La valeur varie de 0,0 à 1,0. Plus la valeur est élevée, plus la couleur est saturée. | ||
+ | Luminosité (V: valeur): La valeur va de 0 (noir) à 255 (blanc). | ||
+ | |||
+ | *Du modèle RVB au modèle HSV | ||
+ | |||
+ | Soit (r, g, b) les coordonnées rouge, verte et bleue d'une couleur, et leurs valeurs sont des nombres réels compris entre 0 et 1. Soit max l'équivalent du plus grand de r, g et b. Soit min la plus petite de ces valeurs. Pour trouver la valeur (h, s, v) dans l'espace HSV, où h ∈ [0, 360) est l'angle de teinte de l'angle, et s, v ∈ [0,1] est la saturation et la luminosité, calculées comme suit: | ||
+ | |||
+ | max=max(R,G,B) | ||
+ | min=min(R,G,B) | ||
+ | if R = max, H = (G-B)/(max-min) | ||
+ | if G = max, H = 2 + (B-R)/(max-min) | ||
+ | if B = max, H = 4 + (R-G)/(max-min) | ||
+ | H = H * 60 | ||
+ | if H < 0, H = H + 360 | ||
+ | V=max(R,G,B) | ||
+ | S=(max-min)/max | ||
+ | |||
+ | Il existe une fonction dans OpenCV qui peut directement convertir des modèles RVB en modèles HSV. Notez que dans OpenCV, H ∈ [0, 180), S ∈ [0, 255] et V ∈ [0, 255]. Nous savons que la composante H peut représenter fondamentalement la couleur d'un objet, mais les valeurs de S et V doivent également être dans une certaine plage, car S représente le degré de mélange de la couleur représentée par H et le blanc, ce qui signifie que le plus petit S, Plus la couleur est blanche, plus elle est claire; V représente le degré de mélange de la couleur représentée par H et le noir, c'est-à-dire que plus le V est petit, plus la couleur est noire. | ||
+ | |||
+ | [[Fichier:hsv1.png|600px]] | ||
+ | |||
+ | Il s'agit d'une gamme floue, avec des parties de violet classées comme rouges. | ||
+ | |||
+ | *Réalisé | ||
+ | |||
+ | Mat imgHSV; | ||
+ | vector<Mat> hsvSplit; | ||
+ | cvtColor(imgOriginal, imgHSV, COLOR_BGR2HSV);//Convert the captured frame from RGB to HSV | ||
+ | split(imgHSV, hsvSplit); | ||
+ | equalizeHist(hsvSplit[2],hsvSplit[2]); | ||
+ | merge(hsvSplit,imgHSV); | ||
+ | |||
+ | imgHSV est une image obtenue en convertissant l'espace RVB d'imgOriginal en espace HSV, puis en séparant les trois canaux de HSV. La fonction cvEqualizeHist est utilisée pour égaliser l'histogramme, qui peut transformer une image plus claire en une image plus profonde (c'est-à-dire améliorer la luminosité et le contraste de l'image). cvMerge: Combinez plusieurs images monocanal en une image multicanal, donc j'obtiens une image dans l'espace HSV. Et en ajustant la valeur de HSV, toutes les zones non rouges peuvent être filtrées. | ||
+ | |||
+ | '''Binarisation''' | ||
+ | |||
+ | La binarisation consiste à convertir une image couleur en noir et blanc pur. cvtColor peut convertir l'image en image en niveaux de gris, mais pas en image binaire. CvThreshold est une fonction de binarisation. La partie rouge d'origine de l'image obtenue est blanche et le reste est noir. | ||
+ | |||
+ | '''Carte électronique: Montage''' | ||
+ | |||
+ | |||
+ | Concernant la partie actionneurs, nous utilisons 4 moteurs vibreurs et un haut parleur pour le son. Dans le but d'amplifier le son de sortie, nous avons opté pour un amplificateur constitué d'un LM386. En effet, le LM386 est un amplificateur audio à faible voltage qui est fréquemment utilisé dans les appareils musicaux à piles comme les radios, les guitares, les jouets, etc. La plage de gain est de 20 à 200 . Ici nous avons augmenté le gain à 200 en utilisant une résistance et un condensateur entre les PIN 1 et 8. | ||
+ | |||
+ | |||
+ | [[Fichier:schema_v2.png|500px]] | ||
+ | |||
+ | [[Fichier:screen_p17.png|400px]] | ||
+ | |||
+ | '''Réalisation d'un essai simple pour Lidar''' | ||
+ | |||
+ | Pendant cette semaine on a réalisé un essai simple pour tester le cas ou il y une voiture aller vers l'utilisateur directement dans un certain l'angle. | ||
+ | |||
+ | L'algoritheme est simple, on utilise une fonction delay_lidar pour prendre le temps qu'on veut entre 2 mesures, et ces 2 mesures on a 2 distance, avec la formule | ||
+ | |||
+ | [[Fichier:formule_simple_p17.png|200px]] | ||
+ | |||
+ | on peut calculer sa vitesse. Et comme on ne connecte qu'un vibreur, donc on n'a pas établi un système de l'alerte de différents niveau, mais avec un vibreur et le terminal pour afficher le résultat est déjà suffisant. | ||
+ | |||
+ | Le code correspond est aussi simple à réaliser avec les exemples donnés en GitHub. On ajoute seulement un compteur du temps et prend 2 mesures successives pour trouver la vitesse d'un objet en approche. | ||
===Semaine 7=== | ===Semaine 7=== | ||
+ | '''Tracez un cercle sur la zone rouge''' | ||
+ | |||
+ | Utilisez d'abord la transformation de cercle de Hough pour détecter le cercle: la fonction cvHougeCircles a plusieurs paramètres: la première est l'image d'entrée, la seconde est la mémoire, la troisième est la méthode et l'algorithme et la quatrième est la résolution: lorsqu'elle est définie sur 1, La résolution est la même, la résolution 2 est normale et la cinquième est la distance minimale entre deux cercles que l'algorithme peut clairement distinguer. Les deux suivants sont des seuils, suivis des rayons maximum et minimum du cercle qui peuvent être trouvés. cvPoint est un type de données dans OpenCV et représente un point en deux dimensions. centre est le centre du cercle. En fait, dessiner le centre du cercle et dessiner le cercle sont la même chose. La fonction cvCircle est utilisée, mais lorsque le rayon du cercle dessiné est 0, il devient le centre du cercle de dessin. | ||
+ | |||
+ | vector<Vec3f> circles; | ||
+ | HoughCircles( imgThresholded, circles, CV_HOUGH_GRADIENT,1.5, 10, 200, 100, 0, 0 ); | ||
+ | for( size_t i = 0; i < circles.size(); i++ ) | ||
+ | { | ||
+ | Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); | ||
+ | int radius = cvRound(circles[i][2]); | ||
+ | //Dessiner le centre du cercle | ||
+ | circle( imgThresholded, center, 3, Scalar(0,255,0), -1, 8, 0 ); | ||
+ | //Dessinez un contour de cercle | ||
+ | circle( imgThresholded, center, radius, Scalar(155,50,255), 3, 8, 0 ); | ||
+ | } | ||
+ | |||
+ | '''Démarrez le shaker''' | ||
+ | |||
+ | Après avoir dessiné le cercle rouge, nous pouvons démarrer le vibreur pour réaliser le rappel. Tout d'abord, j'ai ajouté les bibliothèques nécessaires au vibreur: | ||
+ | #include <wiringPi.h> | ||
+ | Ensuite, j'ai initialisé le vibreur: | ||
+ | if(-1==wiringPiSetup()) | ||
+ | { | ||
+ | cerr<<"setup error\n"; | ||
+ | exit(-1); | ||
+ | } | ||
+ | pinMode(7,OUTPUT); | ||
+ | |||
+ | Enfin, nous démarrons le vibrateur après avoir détecté la lumière rouge, le temps de vibration est de 3 secondes: | ||
+ | digitalWrite(7,HIGH); | ||
+ | delay(3000); | ||
+ | digitalWrite(7,LOW); | ||
+ | |||
+ | '''Etablir un système de l'alerte de différents niveaus''' | ||
+ | |||
+ | De même côté pour les vibreurs dans la partie Lidar, on veut utiliser 3 vibreurs avec 6 broches correspondantes sur la Raspberry pour établir un système de l'alerte de différents niveaus selon le niveau de danger. | ||
+ | |||
+ | On utilise le numéros de broche 0, 2 et 7 pour les vibreurs. On propose le plus dangereux de la situation, le plus fort(ici c'est le nombre de vibreurs activés) et le plus long temps d'activation d'un vibreur. | ||
+ | |||
+ | |||
+ | '''Code Actionneur''' | ||
+ | |||
+ | Nous voulons écrire le code des actionneurs: | ||
+ | *Pour rendre l'alerte du danger proportionnelle aux vibrations, nous activons l'un des vibreurs ou les 4 en même temps pour bien signifier le rapprochement du danger vers l'enfant. | ||
+ | *Pour le son, nous avons la fonction "omxplayer" qui appliqué directement en ligne de commande avec comme paramètre "leFichierSon.mp3" fait joué le son jusqu'à la fin. Pour le code alors nous faisons deux scripts shell, tel que l'un prend en compte l'audio de l'avertissement et le second l'audio du danger. | ||
+ | Dans un programme en C, nous utilisons la fonction "execl" qui prend en paramètre: le chemin d'accès prog, prog, les arguments et NULL. | ||
+ | |||
+ | int pid; | ||
+ | pid=fork(); | ||
+ | if(pid==0){ | ||
+ | execl("/home/IMA4/projet/programmes/code1", " ", "code1", NULL) | ||
+ | _exit(0) } | ||
+ | else{ | ||
+ | wait();} | ||
+ | |||
===Semaine 8=== | ===Semaine 8=== | ||
+ | '''Améliorez et complétez''' | ||
+ | |||
+ | En raison des différentes conditions d'éclairage à différents moments, il est préférable d'avoir une interface de contrôle pour ajuster le HSV en temps réel afin que l'effet d'identification du rouge soit le meilleur. OpenCV a une interface graphique dédiée pour écrire des fonctions, ce qui est également très pratique pour écrire. | ||
+ | |||
+ | |||
+ | '''La conception et la réalsiation d'une première méthode de approximative du Lidar''' | ||
+ | |||
Pendant cette séance on a ajouté le code pour notre deuxième cas: | Pendant cette séance on a ajouté le code pour notre deuxième cas: | ||
*La voiture n'approche pas directement vers l'utilisateur mais ils vont se rencontrer dans un certain temps. | *La voiture n'approche pas directement vers l'utilisateur mais ils vont se rencontrer dans un certain temps. | ||
− | |||
Et ici selon le consigne du prof, on utilise 2 notions qui sont points immobiles et points mobiles. | Et ici selon le consigne du prof, on utilise 2 notions qui sont points immobiles et points mobiles. | ||
Ligne 948 : | Ligne 1 117 : | ||
Si on calcule la vitesse de tous les points de 360 degrés, on trouve il y a des points mobiles et des points immobiles. Points mobiles sont des points qui ont une distance change de temps en temps, points immobiles sont des points qui ont une distance fixée ou change pas beaucoup. Et le calcul de la vitesse d'un point est utilisé pour détecter d'une voiture en approche directement vers l'utilisateur. | Si on calcule la vitesse de tous les points de 360 degrés, on trouve il y a des points mobiles et des points immobiles. Points mobiles sont des points qui ont une distance change de temps en temps, points immobiles sont des points qui ont une distance fixée ou change pas beaucoup. Et le calcul de la vitesse d'un point est utilisé pour détecter d'une voiture en approche directement vers l'utilisateur. | ||
− | + | Pour notre deuxième cas: | |
− | dans | + | Si l'utilisateur de la voiture va se rencontrer dans un certain temps, c'est-à-dire pour lidar, le point qui répresente la voiture, ses paramètre(angle et distance) est comme cela: |
+ | Si le lidar est sur le bras droite, selon notre graphe sur wiki, on propose que l'angle 0 est la drection vers laquelle l'utilisateur avance. Donc un moment son angle 60 a une distance D1 qui est la distance entre la voiture et lidar, ensuite dans un certain temps t1 on mesure son angle 45 et il a une distance D2 qui est plus petite que D1, et dans un certain temps t2 on mesure son angle 30 et il a une distance D3 qui est le plus petite. Si les mesures sont comme cela on peut dire approximativement il y un danger potentiel pour l'utilisateur et on alerte pour que l'utilisateur vérifie si la voiture freine bien. | ||
+ | D1 est stockée dans la variable distance_60, D2 est stockée dans la variable distance_45, et D3 est stockée dans la variable distance_30. | ||
− | + | Donc pour la partie code, je mesure les distance de ces 3 angle pour un premier test, et j'ai imposé 3 conditions pour sauvegarder cette distance: | |
+ | |||
+ | 1.La qualité de scanner cette distance n’est pas nulle, car si cette valeur est nulle cela veut dire on n'a pas réussi à prendre cette mesure et la distance est 0, donc si on prend cette mesure quand même on va recevoir une distance qui est 0. | ||
+ | |||
+ | 2.La distance est supérieur que 200mm, car si c'est inférieur que 200mm=0.2m, il n'y a pas trop de sens(trop proche) et j'ai aussi eu une erreur qui est un moment cette valeur est négative(pour éviter cette erreur). Mais après ajouter cela, la distance = 0 quand même qui n'est pas normal, car si cette distance est inférieur à 200mm, on l'abandonne, sûrement la valeur minimales des distance_60, distance_45, distance_30 sont 200mm. (200mm en effet est limité par la taille de ma chambre pour un test). | ||
+ | |||
+ | 3.Cette distance est inférieur que la distance qui est stockée dans la variable distance_60 (initialisée par 12000 en mm car la distance maximale est 12m) | ||
[[Fichier:condition_P17_save_mesure.png|800px]] | [[Fichier:condition_P17_save_mesure.png|800px]] | ||
+ | |||
+ | A la fin, on compare ces 3 valeurs, et si distance_60>distance_45>distance_30, on peut dire approximativement la voiture et l'utilisateur peut-être se rencontrer dans un certain temps. | ||
[[Fichier:erreur1_P17_save.png|500px]] | [[Fichier:erreur1_P17_save.png|500px]] | ||
Ligne 965 : | Ligne 1 144 : | ||
Après ajouter cette condition, on peut trouver la distance sauvegardé dans nos variables sont égaux à 0 qui n'est pas possible car si la valeur de mesure est inférieur que 200mm alors on l'abandonne cette mesure. | Après ajouter cette condition, on peut trouver la distance sauvegardé dans nos variables sont égaux à 0 qui n'est pas possible car si la valeur de mesure est inférieur que 200mm alors on l'abandonne cette mesure. | ||
+ | |||
+ | Mais cette méthode a un problème de précision. Donc nous allons essayer une autre méthode la semaine prochaine. | ||
===Semaine 9=== | ===Semaine 9=== | ||
+ | '''Compilation de la partie webcam''' | ||
+ | |||
+ | Parce que dans cette partie, j'utilise beaucoup de fichiers en openCV. Par conséquent, lors de la compilation, car le format de compilation n'est pas normalisé, de nombreuses tentatives échouent. Enfin, avec l'aide du professeur, j'ai appris la bonne méthode de compilation: | ||
+ | |||
+ | [[Fichier:complier.png|800px]] | ||
+ | |||
+ | Lors de la compilation, j'échoue toujours et l'ordinateur signale toujours les erreurs et les fichiers manquants. J'ai essayé plusieurs méthodes: | ||
+ | La première fois, j'avais l'habitude de compiler directement, et j'ai échoué. | ||
+ | |||
+ | [[Fichier:cuowu1.png|300px]] | ||
+ | |||
+ | La deuxième fois, parce que la dernière erreur était due à des fichiers manquants, j'ai placé tous les fichiers d'en-tête utilisés dans un dossier (c'est-à-dire dans le même dossier que le fichier d'exécution), bien que cette façon ait résolu certains problèmes , Mais il y a encore beaucoup de fichiers manquants signalés. | ||
+ | |||
+ | [[Fichier:cuowu2.png|600px]] | ||
+ | |||
+ | La troisième fois, j'ai essayé d'ajouter les fichiers requis au dossier étape par étape, mais en raison du trop grand nombre de fichiers et d'erreurs, je ne peux que renoncer à cette méthode. | ||
+ | |||
+ | La quatrième fois, j'ai essayé d'écrire un makefile, cette méthode peut connecter tous les fichiers d'en-tête, mais en raison du manque de connaissances, je ne peux pas écrire un makefile avec compétence, cette méthode a finalement déclaré l'échec. | ||
+ | |||
+ | La cinquième et dernière fois, j'ai demandé à M. Redon, il m'a appris un moyen de compiler, peut appeler tous les fichiers d'en-tête. Enfin, avec l'aide du professeur, j'ai réussi à mettre en œuvre la compilation. | ||
+ | |||
+ | '''La conception et la réalsiation d'une deuxière méthode de l'approximation du Lidar''' | ||
+ | |||
+ | Pendant cette semaine, selon le consigne des profs, on trouve que notre ancienne méthode ne marche pas, et donc on propose une deuxième méthode avec le principal suivant: | ||
+ | |||
+ | On trouve tout d'abord le point de gravité de la voiture(on fait un algoritheme pour chercher ces points à chaque temps et on propose la voiture va droit), et ensuite on créer un repère qui va présenter la voiture sur 2 coordoonnées polaires pour connaître à quelle distance la voiture va passer au plus près et à quel instant. A la fin on va traiter le temps t dans lequel la voiture passera au plus près et la distance dis entre la dernière mesure de la voiture et l'utilisateur. | ||
+ | |||
+ | Dans ce cas, si t est petit et dis est petit aussi c'est très grave et on va alerter fortement pour que l'utilisateur avertir vite. Si t est grand et dis est petit c'est moins grave parce que la voiture peut-être en train de freiner donc on va alerter moins fort. Si dis est supérieur que 5m c'est pas grave donc on n'alerte pas. | ||
+ | |||
+ | '''Le calcul sur papier pour trouver la droite de mouvement de la voiture''' | ||
+ | |||
+ | Tout d'abord on crée un repère des coordoonnées polaires comme au-dessous: | ||
+ | |||
+ | [[Fichier:repere.png|400px]] | ||
+ | |||
+ | On propose que point O présente la position de l'utilisateur, et P2 est le premier point de gravité de la voiture, P1 est le deuxième point de gravité de la voiture, et y est l'axe qui est aussi la direction de mouvement de l'utilisateur. Et dans chaque mesure on connaît la distance r et l'angle theta de chaque point(on propose r1,theta1,r2,theta2 sont connus). | ||
+ | |||
+ | Donc on peut représenter P1, P2: | ||
+ | |||
+ | [[Fichier:P17_P1P2.png|400px]] | ||
+ | |||
+ | Ici on remplace r1*sin(theta1) par x1, r1*cos(theta1) par y1, r2*sin(theta2) par x2, et r2*cos(theta2) par y2 pour simplifier le calcul. | ||
+ | |||
+ | Sachant les positions de P1 et P2, on peut calculer la droite du mouvement de la voiture(avec une condition theta1<theta2, sinon la voiture va plus loin): | ||
+ | |||
+ | [[Fichier:P17_calcul_droite.png|400px]] | ||
+ | |||
+ | Ensuite on peut trouver la position au plus près est: | ||
+ | |||
+ | [[Fichier:P17_Position_plus_pres.png|200px]] | ||
+ | |||
+ | Donc le temps t pour passer de la postion P1 à cette position au plus près est: | ||
+ | |||
+ | [[Fichier:P17_calcul_t.png|300px]] | ||
+ | |||
+ | Maintenant on a le temps t et la distance dis puis on va la comparaison avec les paramètres qu'on impose. Et cette comparaison sera faite dans la prochaine partie qui va présenter le code. | ||
+ | |||
+ | '''Algoritheme pour trouver le point de gravité de la voiture à chaque temps''' | ||
+ | |||
+ | Pour trouver ces points, on propose stocker les mesures entre 0 degré et 90 degrés car ce lidar traite seulement la voiture en droit et avant. Ensuite dans chaque deux mesure successifs on compare la distance de point avec le même angle, si ces distance changent beaucoup(comme de 12m à 5m) cela veut dire il y a un objet(la voiture dans notre cas) appraît, et ce point on l'appelle le point mobile. Par contre, le points qui ont une distance presque constante on l'appelle le points immobile. Si il y une voiture, apparament les points de certaines angles successifs vont tout changer donc ces points sont tous le point mobile et on prend la distance moyenne et l'angle moyenne comme le point de gravité de cette voiture. | ||
+ | |||
+ | '''Algoritheme pour calculer le temps entre 2 mesure successifs''' | ||
+ | |||
+ | Comme nous avons montré au début, pour calculer la droite, c'est mieux si on prend la deuxième mesure(recherche de point mobile) dans un second après prendre la première mesure. Si les 2 mesures sont trop proche, l'erreur est plus grande. Avant cette séance on utilise la fonction clock() qui n'est pas assez précis, donc cette séance on préfère utiliser la fonction gettimeofday(). A la place de montrer le code lourd, ici on met un exemple de cette fonction pour montre l'idée comment on calculer le temps entre 2 mesures: | ||
+ | struct timeval debut; | ||
+ | struct timeval fin; | ||
+ | unsigned long difference; | ||
+ | gettimeofday(&debut,NULL); | ||
+ | delay(1000); | ||
+ | gettimeofday(&fin,NULL); | ||
+ | difference = 1000000* (fin.tv_sec-debut.tv_sec)+ fin.tv_usec-debut.tv_usec; | ||
+ | printf(“la différence est %ld\n”,difference); | ||
+ | Comme dans cette fontion le résultat est en microseconde(μs), on divise 1000 000 pour le présenter en second. C'est un peu près la même que clock() mais plus précis. Et on l'appelle avec debut quand on prend la première mesure et fin quand on prend la deuxième mesure. | ||
+ | |||
+ | ===Semaine 10=== | ||
+ | '''Ajouter un vibreur au module webcam''' | ||
+ | |||
+ | Enfin, afin de pouvoir rappeler les enfants à temps, j'utilise un vibreur pour réaliser, lorsque la webcam détecte une lumière rouge, activer le vibreur pour le rappeler aux enfants. | ||
+ | Tout d'abord, j'ai ajouté le fichier d'en-tête: | ||
+ | #include<wiringPi.h> | ||
+ | Ensuite, nous avons soudé le vibrateur à la broche 7, puis configuré le Raspberry Pi pour activer le vibreur sur la broche 7 pour alerter l'enfant lorsqu'un feu rouge a été détecté: | ||
+ | if(-1==wiringPiSetup()) | ||
+ | { | ||
+ | cerr<<"setup error\n"; | ||
+ | exit(-1); | ||
+ | } | ||
+ | pinMode(7,OUTPUT); | ||
+ | |||
+ | Enfin, chaque fois que je détecte une lumière rouge et dessine un cercle, je démarre le vibrateur et le fais vibrer pendant 1000 ms: | ||
+ | digitalWrite(7,HIGH); | ||
+ | delay(1000); | ||
+ | digitalWrite(7,LOW); | ||
+ | |||
+ | Ensuite, je compile d'une nouvelle manière: | ||
+ | |||
+ | [[Fichier:complier2.png|800px]] | ||
+ | |||
+ | Enfin, le résultat de l'opération est très réussi, le résultat est le suivant: | ||
+ | |||
+ | [[Fichier:resultat2.png|400px]] | ||
+ | |||
+ | Lorsqu'une lumière rouge est détectée, le programme dessine un cercle et déclenche la vibration du vibrateur. Il y a un panneau sur l'écran principal qui contrôle la luminosité et les niveaux de gris, ce qui est pratique pour ajuster en fonction de la lumière différente pour obtenir le meilleur effet. | ||
+ | |||
+ | '''La introduction supplémentaire de la deuxière méthode de l'approximation du Lidar''' | ||
+ | |||
+ | Pendant ces 2 semaines qui est la vacance de Pâque, on a réussi à une implémentation de l'algorithme avec des équations mathématiques qu'on a calculé pendant la semaine dernière, mais il y a quelque part qui n'est pas précis. Donc avant présenter le détail du code, nous allons préciser quelque définition ici: | ||
+ | |||
+ | 1.L'ordre de mesure de 2 points de gravités successif | ||
+ | Pour faire une droite dans le repère qu'on a fait, il faut trouver 2 points pour fixer une droite, et comme on suppose que l'utilisateur marche vers la flèche positif de l'axe y, et selon le calcul au'on a fait, il y a aura un danger si et seulement si l'angle de point P1 est plus petit que l'angle de point P2, et même pour la distance. | ||
+ | |||
+ | Donc dans notre code, on va tout d'abord chercher un point de gravité dans la zone entre 0 degré et 90 degrés qui est notre point P2 avec un angle 'angle2'. Ensuite, on fait un petit delay pour la voiture avance une certaine distance, et puis on cherche le deuxième point de gravité dans la zone entre 0 degré et 'angle2' degrés. Si ce n'est pas le cas on utilise un variable s'appelle 'presence_voiture' qui représente la présence de la voiture, si on n'a pas détecté un point de gravité pendant ces 2 mesures, on pose cette variable à 0, et si cette varibale égale à 0, il n'y a pas de l'alerte. Parce que si dans un scan de l'environnement le lidar n'a pas pris la mesuer de quelquel angle, il va mettre 0 comme la valeur de distance de cet angle, donc on ajoute cette nouvelle variable 'presence_voiture' pour choisir les mesures utiles. Par exemple, si il y une distance égale à 0, on abandonne cette mesure et pose 'presence_voiture' égale à 0, donc il faut attendre la prochiane detection de point de gravité. | ||
+ | |||
+ | 2.La méthode pour trouver le point le plus proche | ||
+ | Ici pour trouver ce point, on propose une méthode de l'approximation qui n'est pas précis dans la semaine 9. | ||
+ | |||
+ | Le principle de cette méthode est: | ||
+ | |||
+ | On pose que la vitesse de l'utilisateur est négligeable par rapport à la voiture. Donc le point le plus proche est un point sur la droite. Cette droite on peut trouver par 2 points de gravités qu'on a. Et on a déjà fait un calcul général sur papier pour trouver une droite sachant les coordonnées des 2 point sur cette droite. | ||
+ | |||
+ | Donc maintenant on sait que le point o qui a les coordonnées (0,0) est la position de l'utilisateur et supposant qu'on a 2 points de gravités avec la méthode présentée au-dessus, on veut trouver le point le plus proche. Aussi il y a une limite pour ce point, c'est: ce point il faut se situe dans la zone où les coordonnées x et y sont tous positifs, car sinon la voiture ne va jamais se rencontrer avec l'utilisateur(même principe pour le produit sur le bras à gauche, chaque produit est responsable pour chaque côté) | ||
+ | |||
+ | [[Fichier:repere.png|400px]] | ||
+ | |||
+ | Donc, on peut trouver le point le plus proche à le point o est le point sur l'axe y, aussi ce point est sur la droite en même temps. | ||
+ | |||
+ | [[Fichier:P17_Position_plus_pres.png|200px]] | ||
+ | |||
+ | 3.La méthode pour calculer la vitesse de la voiture et le temps pour la voiture passera au point le plus proche | ||
+ | Ici on suppose qu'on a le point le plus proche de l'utilisateur qui est le point au-dessus. Pour calculer la vitesse, on mesure le temps entre le moment où on mesure le point de gravité P2 et le moment où on mesure le point de gravité P1 avec la fonction 'gettimeofday()', et la distance de ces 2 points est facile de calculer par la méthode qu'on propose pendant la semaine 9. Ensuite on calcule la distance entre le point de gravité P1 et le point le plus proche comme on a vu en semaine 9 et ici on reprend l'équation: | ||
+ | |||
+ | [[Fichier:P17_calcul_t.png|300px]] | ||
+ | |||
+ | Donc on voit que avec cette distance, et sachant la vitesste de la voiture, on peut calculer le temps pour la voiture passera au point le plus proche, et avec ce temps et le distance entre l'utilisateur et le point le plus proche, on peut établir un système de l'alerte selon le niveau d'urgence. | ||
+ | |||
+ | '''Le code principal''' | ||
+ | |||
+ | Dans cette partie nous allons présenter des codes important en C++ pour montrer comment réaliser ces algoritheme en détail. | ||
+ | |||
+ | if (IS_OK(op_result)) { | ||
+ | drv->ascendScanData(nodes, count); | ||
+ | } | ||
+ | |||
+ | Jusqu'à ici on a stocké les premières mesures dans le tableau 'nodes'. | ||
+ | |||
+ | op_result = drv->grabScanData(new_nodes, count); | ||
+ | if (IS_OK(op_result)) { | ||
+ | drv->ascendScanData(new_nodes, count); | ||
+ | |||
+ | Jusqu'à ici on a stocké les deuxièmes mesures dans le tableau 'new_nodes'. | ||
+ | |||
+ | for (int pos2 = 0; pos2 < 2000 ; pos2=pos2+1) | ||
+ | |||
+ | Ici on s'arrêt à 2000 parce qu'il y a 8192 de mesures dans un tableau et 2000 est environ 25% qui est un peu près 90 degrés. | ||
+ | |||
+ | if( ((nodes[pos2].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f<90) && ((new_nodes[pos2].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f<90) && | ||
+ | (nodes[pos2].distance_q2/4.0f!=0) && (new_nodes[pos2].distance_q2/4.0f!=0) && fabs(nodes[pos2].distance_q2/4.0f-new_nodes[pos2].distance_q2/4.0f)>1000 ) | ||
+ | |||
+ | ici il y a 5 conditions pour trouver le point de gravité, qui sont: | ||
+ | |||
+ | 1.L'angle du point P2 qui représente la permière mesure dans le tableau 'nodes' est inférieur que 90 degrés. | ||
+ | |||
+ | 2.L'angle du point P1 qui représente la deuxième mesure dans le tableau 'new_nodes' est inférieur que 90 degrés. | ||
+ | |||
+ | 3.La distance du P2 qui représente la permière mesure dans le tableau 'nodes' n'est pas égale à 0 car si cette valeur est nulle cela veut dire cette mesure est inutile, donc il faut l'abandonner. | ||
+ | |||
+ | 4.La distance du P1 qui représente la deuxième mesure dans le tableau 'new_nodes' n'est pas égale à 0 car si cette valeur est nulle cela veut dire cette mesure est inutile, donc il faut l'abandonner. | ||
+ | |||
+ | 5.La différence des distances de ces 2 points P1 et P2 est supérieur que 1 mètre pour assurer c'est un point mobile parce que si il y a un changement de plus 1 mètre, on peut dire apparemment il y un objet qui apparaît ou disparaît dans cet angle, et normalement la distance plus petite est la distance de cet objet, et autre distance qui est plus grande est la distance de l'environnement. | ||
+ | |||
+ | Avec ces 5 conditions, on est sûr que ce point est un point de gravité, et ensuite on peut faire des calculs pour la vitesse et le temps. | ||
+ | |||
+ | { | ||
+ | presence_voiture=1; | ||
+ | |||
+ | Ici si on détecte un point de gravité, on pose la valeur presence_voiture égale à 1 comme cela on peut continuer dans la suite. Sinon pour la prochaine for, il y a six conditions, parmi ces six conditions, il y une qui est la variable 'presence_voiture' n'est pas nulle. | ||
+ | |||
+ | num2=pos2; | ||
+ | gettimeofday(&debut,NULL); | ||
+ | |||
+ | Ici dès que on trouve le primier point de gravité qui est P2, on commence compter le temps. | ||
+ | |||
+ | dis2= (nodes[pos2].distance_q2/4.0f>new_nodes[pos2].distance_q2/4.0f)?new_nodes[pos2].distance_q2/4.0f:nodes[pos2].distance_q2/4.0f; | ||
+ | angle2=(nodes[pos2].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f; | ||
+ | |||
+ | Ici on stocke la valeur de distance et de l'angle dans ces 2 variables qui seront utilisées plus tard. | ||
+ | |||
+ | x2 = dis2 * sin(angle2*PI/180.0f); | ||
+ | y2 = dis2 * cos(angle2*PI/180.0f); | ||
+ | |||
+ | Ici avec des équations mathématiques on peut calculer les coordonnées de P2. Il faut faire attension que cet angle il faut transférer sous forme de degré pas en radian. Pour cela on a aussi fait un essai pour montrer cette transformation est correcte. On va présenter cet essai plus tard. | ||
+ | |||
+ | /*printf("theta: %03.2f Dist: %08.2f \n ", //pour montrer si on a trouve le point de gravite | ||
+ | (new_nodes[num1].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f, | ||
+ | new_nodes[num1].distance_q2/4.0f); */ | ||
+ | |||
+ | Ici c'est pour montrer qu'on a trouvé le point de gravité, on a fait un essai pour montrer si on a vraiment trouvé ce point sur affichage, et cet essai on va présenter plus tard aussi. | ||
+ | |||
+ | break; | ||
+ | |||
+ | ici on pose une interruption de boucle, car on a besoin de seulement un point de gravité pour ce moment et faut mieux attendre un peu puis on commence à chercher le deuxième point de gravité. | ||
+ | |||
+ | } | ||
+ | else | ||
+ | { | ||
+ | presence_voiture=0; | ||
+ | } | ||
+ | |||
+ | Ici on ajoute une condition, si on n'a pas détecté le point de gravité, on pose la valeur de 'presence_voiture' égale à 0, et donc on ne peut pas passer dans le prochain boucle,et puis donc on attend le prochaine détection. | ||
+ | |||
+ | } | ||
+ | delay_lidar(500); | ||
+ | |||
+ | Ici on pose un retard de 500ms puis on prend la deuxième mesure. | ||
+ | |||
+ | drv->ascendScanData(nodes, count); | ||
+ | drv->ascendScanData(new_nodes, count); | ||
+ | |||
+ | Ici avec le même principe on trouve le point de gravité P1. | ||
+ | |||
+ | for (int pos1 = 0; pos1 < 2000 ; pos1=pos1+1) { | ||
+ | |||
+ | Pareil que P2, on prend seulement 90 degrés pour chaque côté. | ||
+ | |||
+ | if( presence_voiture && ((nodes[pos1].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f<angle2) && ((nodes[pos1].angle_q6_checkbit >> | ||
+ | RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f<90) && ((new_nodes[pos1].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f<90) && (nodes[pos1].distance_q2/4.0f!=0) && | ||
+ | (new_nodes[pos1].distance_q2/4.0f!=0) && fabs(nodes[pos1].distance_q2/4.0f-new_nodes[pos1].distance_q2/4.0f)>1000 ) | ||
+ | |||
+ | On ajoute une condition supplémentaire qui est 'presence_voiture' doit être non nulle. Sinon on manque un point et on ne peut pas calculer la droite avec seulement un point. | ||
+ | |||
+ | { | ||
+ | num1=pos1; | ||
+ | gettimeofday(&fin,NULL); | ||
+ | |||
+ | Ici c'est la fin de compteur du temps. | ||
+ | |||
+ | dis1= (nodes[pos1].distance_q2/4.0f>new_nodes[pos1].distance_q2/4.0f)?new_nodes[pos1].distance_q2/4.0f:nodes[pos1].distance_q2/4.0f; | ||
+ | angle1=(nodes[pos1].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f; | ||
+ | |||
+ | Pareil que P2, on a 2 variables qui stocke les valeurs de la distance et l'angle de point P1. | ||
+ | |||
+ | x1 = dis1 * sin(angle1*PI/180.0f); | ||
+ | y1 = dis1 * cos(angle1*PI/180.0f); | ||
+ | |||
+ | Avec des équations mathématiques, c'est facile de calculer les coordonnées. | ||
+ | |||
+ | /*printf("theta: %03.2f Dist: %08.2f \n ", //pour montrer si on a trouve le point de gravite | ||
+ | new_nodes[num1].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f, | ||
+ | new_nodes[num1].distance_q2/4.0f); */ | ||
+ | |||
+ | Pareil cette partie en commentaire est pour montrer si on a trouvé le deuxième point de gravité. | ||
+ | |||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | Pareil on pose une interruption ici car on a besoin de seulement un point. | ||
+ | |||
+ | En effet le code jusqu'à ici marche bien et on voit la distance et l'angle de ces points de gravité sur terminal, mais dans la suite avec des équations racine et carré on a eu un petit problème que nous allons montrer dans la partie 'Problèmes rencontrés'. | ||
+ | |||
+ | //maintenant on a toutes les mesures qu'on a besoin pour calculer le droite | ||
+ | temps=(fin.tv_sec-debut.tv_sec)+fin.tv_usec-debut.tv_usec; //le temps en second | ||
+ | Vitesse=sqrt( (y2-y1)*(y2-y1) + (x2-x1)*(x2-x1) )/temps; | ||
+ | dis=sqrt( x1*x1 + pow( (x1*y2-x1*y1)/(x2-x1) ,2) ); | ||
+ | t=dis/Vitesse; | ||
+ | |||
+ | Dans cette partie on applique les équations mathématique qu'on a calculé pendant la semaine 9. Et puis on peut trouver le temps avec le résultat de la fonction gettimeofday(), avec ce temps et sachant la distance entre P1 et P2(on a leurs coordonnées), on peut calculer la vitesse de cet objet. Aussi avec notre introduction, ici on prend le point sur l'axe y le point plus proche, donc on peut calculer le temps dans lequel la voiture passe au plus proche que l'utilisateur. | ||
+ | |||
+ | if(Vitesse > double(5000)) | ||
+ | { | ||
+ | alerte=1; | ||
+ | } | ||
+ | if(Vitesse > 15000) | ||
+ | { | ||
+ | alerte_vite=1; | ||
+ | } | ||
+ | if(dis < 2000) | ||
+ | { | ||
+ | alerte_proche=1; | ||
+ | } | ||
+ | if(dis > 5000) | ||
+ | { | ||
+ | alerte=0; | ||
+ | } | ||
+ | |||
+ | Ici sont des contidions pour alerter. Comme nous avons déjà présenté notre système de l'alerte avec différents niveaus d'urgence. | ||
+ | |||
+ | Si la vitesse arrive à une limite, on active l'alerte normale qui dure pas long temps, et si la vitesse est vraiment grande, on active l'alerte de grande vitesse dure un certain temps, ensuite si la distance est plus que 5 mètres, dans ce cas n'est pas très grave, on n'alerte pas. Mais si cette distance est petit que 2 mètres, cela veut dire c'est grave, il faut alerter tout de suite et dure long temps. | ||
+ | |||
+ | '''Des essais individuels''' | ||
+ | |||
+ | Car dans le code final il y a beaucoup de fonctions que je n'ai jamais vu avant, donc ici je crée un nouveau fichier pour tester ces fonctions séparément. | ||
+ | |||
+ | [[Fichier:essai_code_final.png|600px]] | ||
+ | |||
+ | Comme on a vu dans la figure au-dessus, on teste le fonctionnement de la fonction gettimeofday et comment transformer le radian en degré dans la fonction sin() et cos(). | ||
+ | |||
+ | [[Fichier:resultat_point_gravite.png|600px]] | ||
+ | |||
+ | Et ici on peut voir la distance et l'angle d'un point de gravité si on utiliser la partie en commentaire dans le code pour afficher si on a trouver le point de gravité. | ||
+ | |||
+ | '''Problèmes rencontrés''' | ||
+ | |||
+ | [[Fichier:erreur1_P17.png|800px]] | ||
+ | |||
+ | Ici en effet après réussir trouver les points de gravité, on veut faire la calcul, mais c'est les codes suivants qui ont une erreur:(sans ces codes je peux complier et exécuter le fichier) | ||
+ | |||
+ | Vitesse=sqrt( (y2-y1)*(y2-y1) + (x2-x1)*(x2-x1) )/temps; | ||
+ | dis=sqrt( x1*x1 + pow( (x1*y2-x1*y1)/(x2-x1) ,2) ); | ||
+ | t=dis/Vitesse; | ||
+ | |||
+ | Et je suis sûr que j'ai implémenté le fichier de tête correspondants. Et j'ai cherché sur Internet il y quelqu'un dit que le fichier de tête de fonction mathématique est | ||
+ | |||
+ | #include <cmath> | ||
+ | |||
+ | en C++ et en C est: | ||
+ | |||
+ | #include <math.h> | ||
+ | |||
+ | Comme j'ai bien mis ces fichier de tête mais il y a quand même une erreur que je n'avais pas eu dans l'essai avec la même fonction. | ||
+ | |||
+ | ===Semaine 11=== | ||
+ | ''' La liaison entre le lidar et le webcam''' | ||
+ | |||
+ | Enfin, nous avons combiné la webcam et le radar. Afin de comprendre l'effet de l'approche du véhicule au feu rouge, nous avons utilisé une boîte noire pour simuler le véhicule, et l'image du feu rouge a simulé le feu rouge. Après de nombreux tests, il a été constaté que dans ce cas, la vibration du vibrateur peut rappeler à l'enfant d'améliorer sa concentration. | ||
+ | |||
+ | [[Fichier:liaision.jpg|200px]] | ||
+ | |||
+ | ===Semaine 12=== | ||
+ | '''Limitations''' | ||
+ | |||
+ | Même si on a réussi un essai avec lidar et webcam, il y encore des limites de notre prototype. | ||
+ | |||
+ | 1.Le temps de réponse pour le webcam est un peu long qui sera danger dans la vie. | ||
+ | |||
+ | 2.Nous n'avons pas considéré le cas où il y a plusieurs voitures. | ||
+ | |||
+ | 3.Nous n'avons pas réussi la fin de troisième méthode. | ||
+ | |||
+ | 4.Le prototype est lourd à porter pour un enfant, à cause du lidar, la raspberry et la batterie. | ||
+ | |||
===Documents Rendus=== | ===Documents Rendus=== | ||
+ | Le rapport:[[Fichier:rapport_final_S8_P17_3.pdf]] | ||
+ | |||
+ | Le diaporama:[[Fichier:Support_Soutenance_P17.pdf]] |
Version actuelle datée du 5 mai 2020 à 08:02
Sommaire
- 1 Présentation générale
- 2 Analyse du projet
- 3 Préparation du projet
- 4 Réalisation du Projet
Présentation générale
Ce projet est mené en collaboration avec Marion Binninger, ergothérapeute à APF (Association Paralysés de France) Environnement et encadré par Alexandre Boé , Xavier Redon , Thomas Vantroys. Selon l'organisation mondiale de la santé (OMS) environ 1.25 millions de personnes meurent chaque année des suites d'accidents de la route . Dans le cas particulier des enfants ce problème est à prendre avec beaucoup de sérieux étant donnée la circulation de plus en plus dense dans le monde . Notre projet a pour but de mettre un accent sur les difficultés attentionnelles des enfants,qui sont un frein à leurs déplacements et à l'autonomie et de trouver une solution .
Description
Ce projet a pour but d'aider un jeune ayant d'importantes difficultés attentionnelles et des réactions impulsives à se déplacer en ville . Étant donné plusieurs cas réels, due à l'inattention des enfants en traversants la rue ,on a pensé à étudier différentes solutions à base de capteurs (luminosité, son, voire caméra) pour remonter une alarme lors de l'approche d'une rue.
Objectifs
Le principal déficit d'attention à corriger est la traversée de rues et pour cela nous avons pour objectifs de :
- choisir des capteurs appropriés à la détection de l'approche des voitures .
- définir les conditions de réaction des capteurs (intensité des sons , distance....).
- analyser la réponse des capteurs et la comparer aux consignes que nous auront définies .
- Selon le résultat de la comparaison , une alerte doit être déclenchée si nécessaire pour attirer l'attention de la personne concernée .
Analyse du projet
Positionnement par rapport à l'existant
Analyse du premier concurrent
Notre premier concurrent serait l'accompagnant de l'enfant tel que les parents,la nounou.
Avantages :
- avoir une personne consciente comme accompagnant est certainement plus sécurisé et donne aux parents plus de garantie sur l'état de leur enfant .
- avoir une personne qui s'adapte aux différentes situations qui se présentent et trouve des solutions adéquates .
- éviter une grosse dépense pour notre produit .
Inconvénients :
- limite l’indépendance de l'enfant et donc son autonomie dans la rue .
- éviter que l'enfant soit éternellement assisté ce qui provoquerait à la longue des problèmes en confiance en soi .
- le tuteur doit être toujours disponible ce qui n'est pas toujours facile .
Analyse du second concurrent
Notre second concurrent serait Direct Line, une société d’assurance qui a collaboré avec le cabinet d’architecture Umbrellium pour créer un nouveau «Smart Crossing», baptisé Starling Crossing, conçu pour aider à maintenir la sécurité des piétons. En effet, encore sous une forme de prototype , ce nouveau <smart crossing> n'est pas encore appliqué dans notre vie quotidienne. Avantages:
- Il est apte à satisfaire tous les cas possibles sur le trafique.
- Il n'est pas restreint à un type de personnes.
- Il est disponible à tout moment, car il est appliqué sur la route.
- C'est l'Etat qui sponsorise l'application de ce produit.
Inconvénients:
- Le Coût de l'application du produit est excessivement cher.
- Il utilisent énormément de capteurs, de cameras ... , qu'une panne peut endommager le fonctionnement du système.
Scénario d'usage du produit ou du concept envisagé
Je m'appelle Nino, je suis un petit garçon plein de vie, j'aime m'amuser avec mes amis. Mes parents me laisse aller à l'école tout seul, comme un grand garçon, j'aime beaucoup car je rencontre mes amis en chemin. Mais.. depuis mon accident, je ne suis plus aussi indépendant, mes parents m'ont pris une nounou qui me dépose tous les matins à l'école car depuis mon accident j'ai des troubles attentionnels, je perds très vite ma concentration et donc mes parents ont vraiment peur de me laisser traversé la route tout seul. Cependant, depuis peu ils m'ont achetés un "..." et son utilisation est tellement facile, il me permet de reprendre ma concentration surtout lorsque je traverse la route, il m'alerte losqu'une voiture se rapproche de moi. Ainsi, mes parents n'ont plus peur de me laisser partir à l'école seul comme un grand, je suis si content, mon accident m'avait enlevé mon indépendance et aujourd'hui, je me sens comme les autres enfants.
Questions difficiles
- quelles types de capteurs doit-on utiliser pour réaliser ce projet ? (répondu en partie préparation de projet)
- comment choisir le type d'alimentation pour maintenir le fonctionnement du dispositif toute la journée sans devoir utiliser bes batteries lourdes ?
- Comment faire pour assembler les composants dans une carte électronique assez petite pour ne pas être encombrant?
Réponses aux Questions difficiles
- Le dispositif étant destiné à un usage quotidien et pour pallier au problème d'autonomie, nous pouvons utiliser une batterie rechargeable qui tiendra en autonomie pendant toute l'utilisation par l'enfant.
- Pour réduire l'encombrement du dispositif, nous voulons dessiner un schéma PCB sur le logiciel Altium.
Bibliographie et webographie
- https://www.evolving-science.com/environment-smart-cities/smart-crossing-designed-make-pedestrians-safer-00429
- http://umbrellium.co.uk/initiatives/starling-crossing/
- https://www.carnetdumaker.net/articles/mesurer-une-distance-avec-un-capteur-ultrason-hc-sr04-et-une-carte-arduino-genuino/#principe-de-fonctionnement-du-capteur
Préparation du projet
- <<Que choisir entre LIDAR ,RADAR et ultrasons ? Quel est le plus adéquat pour notre projet ?>>
- Que souhaite-on mesurer? la distance entre l'enfant et les voitures.
- Quelle condition environnementale? Un environnement urbain.
- Quelle distance entre l'émetteur et la cible? Environ plus de 50m.
- ULTRASON:
La mesure par ultrason est basé sur les ondes sonores, ils sont insensibles aux couleurs, à la brillance et à la transparence. Cependant, son utilisation est adéquat pour une surveillance de volume et pour les distances courtes moins de 8m.
- LIDAR:
La mesure par lidar est basé sur la détection et l'estimation de la distance par la lumière. il s'agit d'une technologie de mesure à distance fondée sur l'analyse des propriétés d'un faisceau de lumière renvoyé vers son émetteur. Cette technique est parfaite lorsqu'on souhaite une indication de position précise, elle est idéale pour les petits objets et à une distance allant jusqu'à 200m. L'utilisation de la technologie LiDAR 2D ou 3D est adéquate lorsqu’une indication de position précise ou une classification d’objet sera requise, ou bien lorsqu’une grande zone de numération sera impérative (Champs de vue horizontal jusqu’à 360°, Champs de vue vertical jusqu’à 15°). Par contre, il est vivement déconseillé d’utiliser cette méthode sur de l’eau, ou dans un environnement difficile (fortes chutes de neige, pluie, brouillard, poussière…).
- RADAR:
Le fonctionnement du RaDAR est proche de la technologie par ultrasons, il permet de calculer la distance, le mouvement et l’angle. Le radar va devoir mesurer à plusieurs reprises. Les données vont être comparées et vérifiées, et si les résultats sont identiques alors l’objet sera reconnu. C’est ce que l’on appelle le suivi (ou tracking). La technologie RaDAR sera préconisée pour la détection d’objet volumineux car il y aura une plus grande surface de réflexion (RCS) et sera donc plus facile à détecter. L’objet à détecter sera de préférence métallique (le métal étant un réflecteur parfait pour cette technique de mesure). Le RaDAR est capable de mesurer de grands volumes jusqu’à plusieurs dizaines (voir centaines) de mètres, mêmes dans des conditions météorologiques extrêmes (pluie, neige , etc).
- <<Quels capteurs sont adéquats pour notre projet?>>:
- HB100 microwave : détecte la distance et calcule la vitesse de tout les objets qui se trouvent dans un rayon de 16m avec un angle de 72° horizentalement.
- Capteur infrarouge C7288 ou Amg8833: detécte la présence des personnes et/ou d'animaux à une distance maximale de 7m.
- utilisation : le capteur amg8833 détecte la présence
- Conclusion:
D'après l'analyse de ces trois types de captations, il en découle que le radar est le plus adéquat à notre projet. En effet, nous aurons besoin d'une grande surface de réflexion, et notre cible principale sera les voitures.
Cahier des charges du groupe
Pour répondre aux besoins de l'enfant souffrant de troubles attentionnels ,on propose:
- Pour le calcul de la vitesse des voitures, on utilisera un capteur .
- Pour alerter l'enfant du danger (voitures, vélos...) on utilisera une vibration ou une alarme .
- Pour assurer l'énergie qui sera consommée par notre produit, on utilisera une batterie qui doit être légère.
- Pour implanter l'algorithme, on utilisera un arduino.
Cahier des charges des équipes
Equipe 1
Hua Jing et Mahmoudi Sanae :
- Ecriture de code .
- Branchement de dispositif: Arduino , capteur ultason , vibreur.
- Tester des programmes .
- Améliorer le fonctionnement du programme .
Equipe 2
Raouto Emilie
- Recherche des informations sur le dispositif et répondre aux questions suivantes :quel est le meilleur capteur à utiliser, quel types d'alimentation il faut utiliser ,
- Remplissage de WIKI .
- Préparations des questions à poser lors de la visite de l'enfant ;
- rejoindre l'équipe 1 pour améliorer le code .
Choix techniques : matériel et logiciel
Pour réaliser notre projet, on veut tester notre idée avec un capteur ultrason, un vibeur et un Arduino. On utilise les composants électroniques suivants:
Composant | Image |
---|---|
Capteur ultrasons HC-SR04 |
|
Vibreur |
|
Arduino MEGA 2560 |
Equipe 1 & Equipe 2
CAPTEUR HC-SR04
Le capteur HC-SR04 est un capteur à ultrason low cost. Ce capteur fonctionne avec une tension d'alimentation de 5 volts, dispose d'un angle de mesure de 15° environ et permet de faire des mesures de distance entre 2 centimètres et 4 mètres avec une précision de 3mm (en théorie, dans la pratique ce n'est pas tout à fait exact).
Pour l'information de ce capteur on a trouvé plusieur sites comme https://randomnerdtutorials.com/complete-guide-for-ultrasonic-sensor-hc-sr04/ https://howtomechatronics.com/tutorials/arduino/ultrasonic-sensor-hc-sr04/
Le MONTAGE
L'alimentation 5V de la carte Arduino va sur la broche VCC du capteur. La broche GND de la carte Arduino va sur la broche GND du capteur. La broche D2 de la carte Arduino va sur la broche TRIGGER du capteur. La broche D3 de la carte Arduino va sur la broche ECHO du capteur. La broche D9 de la carte Arduino est connectée à un vibreur .
Liste des tâches à effectuer
Equipe 1
- 1.Connaĩtre le datasheet de capteur ultrson qu'on a choisi.
- 2.Connecter des branches de capteur ultrason, vibreur et Arduino
- 3.Tester le vibreur
- 4.Tester le capteur avec le code exemple pour mesurer le distance
- 5.Ajouter le code de calculer la vitesse selon distance
- 6.Installer le condition de activer le vibreur
- 7.Améliorer le programme pour fonctionner bien
Equipe 2
- 1.Chercher des informations des différents capteurs sur Internet
- 2.Comparer les capacités entre des capteurs
- 3.Trouver le capteur qui satisfait meilleur notre projet
- 4.Chercher des informations pour répondre les quesions difficiles
- 5.Remplir le reste de Wiki
- 6.Préparer des qustions pour enfant
- 7.Améliorer le code dont Equipe 1 a fait
Calendrier prévisionnel
Le calendrier prévisionnel peut se concrétiser sous la forme d'un diagramme de GANTT.
Equipe 1
Equipe 2
Réalisation du Projet
Projet S6
Eventuellement créer des sous-pages par équipe avec le compte-rendu des réunions de groupe sur cette page principale.
Semaine 4
- Lecture de sujet
- Chercher plus d'information sur l'état des enfants qui souffrent des troubles attentionnels pour trouver la bonne solution qui va satisfaire les besoins de l'enfant
- Poser le maximum de questions pour bien comprendre le sujet .
- Débuter le scénario d'usage.
- Recherche de différentes solutions pour notre problème.
Semaine 5
Lors de cette séance, nous avons eu l'opportunité de rencontrer une intervenante extérieure pour mieux comprendre le sujet et améliorer le scénario d'usage. Etant donné que notre dispositif sera utilisé par un enfant handicapé , cette séance nous a surtout permis de se mettre dans la peau de l'utilisateur pour mieux assimiler ses besoins, comprendre les attentes éventuelles et ainsi réfléchir à une solution adéquate.
La séance a commencé par un petit pitch pour que l'intervenante ait une idée assez globale de notre projet. Après ce pitch nous avons eu quelques remarques instructives pour nous améliorer. Puis nous avons dû donner une perception du projet en utilisant de nouvelles méthodes efficaces et inhabituelles telles que des cartes et des legos.
A partir des informations collectées suite à cette réflexion, nous avons pu imaginer un usager type , c'est l'étape de la 'collecte'. Avec les cartes qui étaient à notre disposition nous avons créer un scénario d'usage en s'appuyant sur l'image d'un usager type (voir image).
Au terme de cette séance, nous avons améliorer notre scénario d'usage, nos idées sur le projet étaient un peu plus claires, et enfin nous avons pu lister plusieurs questions à poser à l'enfant lors d'une éventuelle rencontre.
Semaine 6
- Recherche de types de captation entre(LIDAR,RADAR et Ultrasons ) et caractéristiques de chacun d'eux et par conséquent nous avons choisi d'utiliser le RADAR car ses caractéristiques correspondaient à nos attentes .
- Élaborer le cahier de charge .
Semaine 7
Durant cette séance on a débuter l'écriture de notre code (langage pour Arduino) parmi les principales fonctions dans notre code :
- Fonction pour mesurer la distance entre le capteur ultrason et les objet qui l'entoure .
- Fonction pour le calcul de la vitesse en se basant sur deux distances mesurées successivement avec un delay de 200 ms .
- Fonction qui permettra d'activer la vibration dans le cas où on a dépassé la limite de la vitesse (la voiture se rapproche de l'enfant ) .
- Branchement entre arduino, capteur et keyboard .
Semaine 8
Continuer le code et le tester . Ajout de la vibreur dans notre Montage .
Semaine 9
- Connection de la vibration sur l'arduino (broche ~9).
- Structuration du programme en sous-programmes.
- Adaptation du programme pour prendre en compte la vibration ,si la vitesse est supérieure à 20cm/s.
Semaine 10
Améliorer le code et s'assurer que ça fonctionne bien. corriger tous ce qui manquait sur le Wiki.
const byte TRIGGER_PIN = 2; // Broche TRIGGER const byte ECHO_PIN = 3; // Broche ECHO const byte vibration = 10; //Beoche vibreur const unsigned long MEASURE_TIMEOUT = 25000UL; // 25ms = ~8m à 340m/s Constantes pour le timeout const float SOUND_SPEED = 340.0 / 1000; //Vitesse du son dans l'air en mm/us long distance0 = 0; int cpt =0; void setup() { /* Initialise le port série */ Serial.begin(115200); /* Initialise les broches */ pinMode(TRIGGER_PIN, OUTPUT); digitalWrite(TRIGGER_PIN, LOW); // La broche TRIGGER doit être à LOW au repos pinMode(ECHO_PIN, INPUT); pinMode(vibration, OUTPUT); digitalWrite(vibration,LOW); } float test_dis() //mesurer la distance et calculer la vitesse { /* 1. Lance une mesure de distance en envoyant une impulsion HIGH de 10µs sur la broche TRIGGER */ digitalWrite(TRIGGER_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIGGER_PIN, LOW); /* 2. Mesure le temps entre l'envoi de l'impulsion ultrasonique et son écho (si il existe) */ long measure = pulseIn(ECHO_PIN,HIGH, MEASURE_TIMEOUT); delay(1000); /* 3. Calcul la distance à partir du temps mesuré */ float distance_mm = measure / 2.0 * SOUND_SPEED; return distance_mm; } void loop() { if(cpt > 1 ) { digitalWrite(vibration,HIGH); //si il y a deux 0.5s on trouve la vitesse > 5.0 on active vibration cpt = 0; delay(1000); } if(cpt==0) { digitalWrite(vibration,LOW); } float dis0=test_dis(); //mesurer la distance ancient en mm Serial.print(F(" Distance0: ")); Serial.print(dis0/10); delay(100); //attendre 0.5s pour mesurer le 2ème distance float dis1=test_dis(); //mesurer la distance nouvelle en mm Serial.print("cm Distance1: "); Serial.print(dis1/10); Serial.print("cm "); float vitesse_cm_s=( (dis0 - dis1 ) / 10.0) * 10 ; if(vitesse_cm_s > 5.0) cpt=cpt+1; //chaque fois la vitesse est > 5.0 cm/s cpt s'incrmente 1 else cpt = 0; Serial.print(F(" Cpt: ")); Serial.print(cpt); Serial.print(F(" Vistesse: ")); Serial.print(vitesse_cm_s); Serial.println(F(" cm/s")); /* Délai d'attente pour éviter d'afficher trop de résultats à la seconde */ delay(100);// en ms }
Semaine 11
- Fabrication du boitier:
Nous avons pris rendez-vous au fabricarium, on a fait une formation d'une heure pour apprendre à utiliser la découpe laser. A l'issue de cette formation, nous avons pris des mesures adéquates pour fabriquer le boitier qui abritera les éléments de notre dispositif. De ce fait, nous avons fait en sorte que le boitier soit le plus petit possible pour permettre qu'il soit facilement transportable.
Semaine 12
- Rencontre avec Souleyman:
Nous nous sommes rendu à l'Institut d'Education Motrice de Lille, une école spécialisée Jules Ferry, afin de rencontrer Souleyman. Dans une première approche, nous nous sommes présentés afin de faire plus ample connaissance avec lui. Nous avons pu en savoir plus sur son handicap et aussi échanger avec sa tutrice. Suite à cet échange, on a pu éclaircir nos idées sur ses besoins et nous avons découvert que son bras gauche est moins actif, nous avons dû modifier la structure du dispositif. Ensuite, nous avons tester le prototype de notre produit et avoir les remarques de la tutrice. Et pour finir, grâce à celle-ci, nous avons eu d'autres recommandations dans le but de répondre parfaitement aux besoins de l'enfant parmi lesquelles:
- Une alerte sonore/vocale
- Un téléphone comme gps
- Informer l'enfant qu'il peut traverser(car il reste longtemps dans la vérification).
Cette rencontre a vraiment été bénéfique pour nous, dans l'optique où nous avons pu mieux comprendre les besoins de Souleyman et améliorer le dispositif pour le semestre suivant.
Documents Rendus
Projet S7
Semaine 1
La Fusion
Suite au différents changements dans le groupe, il y a eu une fusion entre le groupe p18 et le nôtre en vue de la proximité de nos sujets respectifs. Dans les deux projets, il y a plusieurs modules communs, donc pour un travail efficace nous travaillons ensemble en se répartissant les tâches.
Module | P17&P18 | P18 |
---|---|---|
Capteurs |
Distance |
Relief, profondeur |
Actionneurs |
Minis moteurs vibrants |
Moteurs DC pour la main |
Energie |
Batterie |
Autonomie d'une/deux heures Batterie dans un sac à dos |
PCB gérant les actionneurs et les parties sonores |
Gestion enregistrements sonores Moteurs vibrants |
Moteurs pour la main Liaison série vers les données des capteurs |
Répartition des Tâches
- Equipe 1: Actionneurs(vibreur avec puissance variable, son) -> Laurine et Clara
- Equipe 2: Capteurs(distance, profondeur) -> Emilie, Nour et Caroline
- Equipe 3: Cartes(FPGA ou microcontrôleur) -> Jing et Ming
Cependant, nous avons des tâches spécifique à notre groupe telles que: l'énergie à utiliser et la solution pour pouvoir capter les feux de circulation. A la fin du S7, notre objectif est d'avoir la liste du matériels afin de réaliser le dispositif final.
Composants choisis
- Un capteur de distance-> RPLiDAR A1M8-360 Degree LaserScanner Kit - 12M RangeRoHS
- UneRaspberry pi 3 ou 4
- Une batterie
- Un webcam: Elle sera combiné avec la raspberry pi pour un traitement d'images
Semaine 2
Après la fusion en semestre 7, on a séparé en 3 groupes pour faire une liste de composants possibles pour notre produit final et ensuite on choisira des composants qui suffisent meilleur nos besoins dans 2 ou 3 semaines pour on peut commencer faire de travaux suivants.
Pendant cette séance, avec le suggestion du prof, monsieur Alexandre Boé, nous avons trouvé une première liste des différent matériaux possibles pour notre poejet, pour cela on cherche des différent produit sur Internet et compare leur performance:
Equipe 1 :
- Vibreurs : La question est: Quel est le meilleur moyen pour faire varier l'intensité des vibrations dans le but de créer une communication entre le prototype et l'enfant (P17) surtout pour les urgences.
- Choix des moteurs pour la main, plus efficaces que ceux du prototype.
- Partie son : module MP3 pour sélectionner et lire enregistrements sur une carte microSD. Éventuel besoin d'un adaptateur jack/USB pour les écouteurs.
Equipe 2 :
- Recherche pour le choix entre deux capteurs le D415 et le D435, pour savoir s'ils seraient adéquats pour les différents besoins
Equipe 3 :
- Recherche des informations ou datasheet de FPGA, micro-processeur comme ATMEGA328 ou un produit qui déjà existe comme un Arduino ou une Raspberry.
- Comparer leurs performances selon notre besoins et nous avons faire une première préférence: une raspberry 3 est parfaitement suffisant de notre projet mais on n'est pas sûr. Et notre deuxième préférence est un micro-processeur comme ATMEGA328. On va demander les profs avec ces quesions pendant la prochaine séance.
Semaine 3
Avant cette séance on a déjà une primière liste de matériaux qui nous semblent possible pour notre projet, donc pendant cette séance nous avons tout d'abord enrichi notre liste pour nos matériaux peuvent suffire les besoins en différents environnement. Ensuite nous voulons démander des consigne du prof monsieur Alexandre Boé pour vérifier si nos matériaux sont possible, est-ce qu'on est dans la bonne direction, cependant malheureusement on n'a pas le temps à la fin de la séance.
Equipe 1 :
- Mettre en place différents stades de vibrations en fonction de la dangerosité d'un obstacle. Pour cela nous allons faire vibrer les moteurs par intermittence. Nous avons défini trois stades de danger. En premier, pour donner une indication, des vibrations séparées par un délai important. Dans le cas d'un obstacle ou d'un changement de relief plus important, le délai sera raccourci. Enfin, en cas de grand danger, les moteurs vibreraient en continu.
- Partie son :
Choix du module MP3 DFR0299 pour stocker et exploiter les enregistrements d'indications sonores.
Pour connecter les écouteurs, on placerait un port jack directement sur le PCB.
Équipe 2 :
- Besoin d'une Raspberry Pi 4 pour pouvoir traiter les images en utilisant une webcam (P17).
Équipe 3 :
- Vérifier si avec une Raspberry Pi 3 on peut contrôller un capteur de distance et un webcam pour scanner l'environnement de la Rue et envoyer les commandes pour avtiver un son et une vibration, surtout sa compléxité et son coût d'énergie, aussi son coût d'argent.
- Vérifier les même qustions pour des micro-processeur et FPGA.
Groupe :
- Discuter avec lesquels matériaux sont mieux pour notre cas, comme on sait qu'un enfant (le garçon Souleyman dans notre situation) qui a des limites, par emxemple, ce n'est pas réaliste de porter un produit lourd etc. Nous avons aussi discuté sur papier pour faire une liaison entre différentes parites.
Semaine 4
Donc pendant cette séance nous avons tout d'abord démandé la faisabilité de notre produit final par des composants dans notre deuxième liste au prof Alexandre Boé, avec des consignes il nous a montré, aussi il nous a montré un prototype d'un produit pour nous comprendre miexu des idées. Donc nous avons décidé de utiliser une raspberry pi 3 pour contrôller le webcam et aussi notre capteur de distance qui est un lidar parmi les choix possible.
Cepandant, nous avons mal compris ce que monsieur Alexandre Boé disait: il nous présentait des avantages et des inconvénients de la raspberry et du micro-processeur, mais on pensait que faut mieux faire une liaison de la raspberry et le micro-processeur et c'est pourquoi on va faire un PCB dans la suite. (C'est le commentaire ajouté après la soutenance)
Après nos recherches sur les capteurs D415 et D435 nous avons conclus qu'ils n'étaient pas adéquat pour nos besoins. Comme dans notre situation faut mieux le capteur de distance peut envoyer la distance de l'environnement, c'est-à-dire un ^capteur qui peut scanner l'environnement de 360 degrés .Donc nous avons définitivement pris comme capteur le Rplidar.
- Un capteur de distance-> RPLiDAR A1M8-360 Degree LaserScanner Kit - 12M RangeRoHS
- Ce capteur est beaucoup utilisé pour éviter des obstacles et pour la sécurité.Ce RPLIDAR A1M8 permet la mesure de distances de 0,15 à 12 m sur 360° grâce à un moteur autorisant une rotation complète. Cette rotation permet par exemple la cartographie de pièces, la modélisation d'un objet ou tout simplement l'évaluation d'une distance ce qui nous intéresse dans notre cas. Il peut communiquer via une liaison UART avec un microcontrôleur mais également avec un PC via une liaison micro-USB. La mesure de distance est basée sur une triangulation émise par le laser et autorise une acquisition rapide des mesures (8000 mesures par secondes). La fréquence d'échantillonnage est adaptée automatiquement en fonction de la vitesse de rotation du capteur Lidar par rapport à sa base.
- Caractéristiques importants de RPlidar A1M8
- Fréquence de mesure: 8000 mesures/sec
- Poids: 170 g
- Remarques
- Ce module peut être utilisé en intérieur et en extérieur mais est non résistant à l'eau.
- L'appareil ne peut pas effectuer de mesure si il est directement exposé aux rayons du soleil.
- L'appareil n'est pas trop lourd par rapport à sa précision de détection.
- L'appareil peut détecter tous les degrés de l'envrionnement.
- Raspberry pi : : Nous utilisons ce composant pour faire du traitement d'image dans le but de distinguer la couleur d'un feu de signalisation en utilisant une caméra webcam et aussi pour envoyer des commandes au lidar et recevoir des réponses.
- Pour ce faire, nous pouvons utiliser la librairie Opencv qui donne la possibilité de combiner une raspberry et une caméra. Elle a été dévéloppé par Intel spécialisé dans plusieurs domaines de traitement d'images: lire, écrire et afficher une image et aussi calculer le niveau d'histogramme de couleur.
- Lien our plus d'informations concernant l'installation de la librairie: https://www.pihomeserver.fr/en/2014/01/22/raspberry-pi-home-server-utiliser-opencv-avec-la-pi-camera/
Semaine 5
Comme au premier temps, on pense que il faut faire un PCB pour connecter la raspberry et un micro-processeur, qui est un peu près la même idée qu'autre groupe P18 donc pendant cette séance, après concevoir un plan de 3 vues pour le prototype sur un bras droite, et ensuite on commencer dessiner le schéma de la carte.
Tout d'abord, nous proposon notre prototype se met en 2 bras d'un enfant pour chaque lidar peut scanner 180 degrés de son coté. Ensuite on propose le webcam doit être en face du prototype donc il peut prendre des images ou des vidéo en face, c'est-à-dire la même direction d'avancement du enfant. Respectant ces consigne au-dessus, on propose un plan de 3 vues suivant:
En ce moment là nous avons aussi étudié le fonctionnement du lidar A1M8, un est le format de la sortie du lidar et un autre est des broches pour connecter la raspberry et le lidar.
- Le format contient 4 petites parties en mode normal qui sont:
- La distance en mm
- L'orientation en degré
- Le signe de commence un nouveau boucle en booléen
- La qualité de scannage en entiers entre 0 et 255
- Les broches pour connecter la raspberry et le lidar sont:
- Soit on utilise une un port USB comme un périphérique
- Soit on utilise une liaison série par RX/TX
Ensuite après discuter avec le groupe P18, on a choisi une liste de composant pour faire une carte PCB qui connecte la raspberry. Pour réaliser cette connexion, on propose mettre 40 broches au-dessous de la carte PCB qu'on va faire et au-dessur de la raspberry. Et les vibreurs et le port jack pour faire le son sont dans la carte aussi. Suivre des consignes, on propose une solution sur papier:
Semaine 6
Comme nous avons déjà proposé une solution de la carte PCB, donc pendant cette séance, nous avons faire le schéma d'abord sur le logiciel Altium et ensuite le PCB.
Cependant, nous avons eu des problèmes, c'est sur le logiciel Altium, il y a des composants qui ne sont pas là dans le bibliothèque, du coup c'est à nous de convevoir ces composants, considérant avec un autre logiciel Fritzing qui a un peu près la même fonction donc on a décidé de changer le logiciel. Ensuite, pendant cette séance nous avons conçu un schéma:
Et selon ce schéma, on fait un PCB en respectant la connexion dans le schéma précédent:
Cependant on est dans la mauvaise direction comme nous avons mal compris au début, en effet on n'a pas besoin de faire ce schéma ni le PCB, et le micro-processeur ATMEGA328 non plus car avec un raspberry c'est déjà suffisant. (c'est le commentaire ajouté après la soutenance)
Semaine 7
Comme nous avons déjà conçu le schéma et le PCB dans la séance précédente, pendant cette séance, on vérifie si avec ce PCB on peut réaliser notre but. Et comme nous avons vite fait le PCB donc on le corrige un peu pour il nous semble plus clair. Sauf cela, cette séance on a aussi un traval à faire, c'est l'environnement de la raspberry, c'est-à-dire comment on choisit le système installé dans la raspberry et comment on implémente nos algorithemes sur la raspberry, avec quel langage.
A la fin de la séance, on a décidé:
- Utiliser le système Raspbian avec un Desktop pour faciliter l'affichage et visualisation quand on a besoin. C'est différent que nous avons vu en cours TP Réseau.
- Utiliser la méthode de ssh qu'on a vu en cours de système pour faire la connexion surtout pour nous 3 peuvent travailler en même temps.
Documents Rendus
Projet S8
Semaine 1
Après la soutenance on a compris qu'on était dans la mauvaise direction donc on réfléchit dessus. Pendant cette primière séance, nous avons décidé de séparé les travaux en 3 parties, chaqu'un fait sa partie et à la fin on fait une fusion de 3 équipes:
- Equipe1: Webcam -> Ming Chen
- Equipe2: Lidar + Configuration de la raspberry -> Jing Hua
- Equipe3: Actionneurs -> Emilie Raouto
Donc on sépare la tâche totale en 3 partie:
- Une partie de webcam pour détecter les feux qui doit connaître le feu rouge, et si le feu maintenant est rouge il doit envoyer un message pour activer le vibreur et aussi le son.
- Une partie de lidar pour scanner l'environnement de l'utilisateur, et si une voiture (ou une chose rapide, un vélo par exemple) qui approche avec une vitesse dangereuse, il doit envoyer un message pour activer le vibreur et le son pareil qu'un webcam. Dans notre cas ici on considère avec 20km/h est déjà dangereuse dans la ville pour l'enfant souleyman.
- Une partie pour la gestion des actionneurs qui contient 4 vibreurs et un haut-parleur. On utilise 4 vibreurs pour indiquer les niveaux de l'alerte qui dépend de la vitesse de la voiture et la distance entre la voiture et l'utilisateur.
Donc pendant cette séance chaqu'un étudie sa tâche et aussi la configuration de la raspberry. Tous les travaux de préparation doivent être fait avant des matériaux arrivent.
Semaine 2
Configuration
Avant préciser dans chaque tâche, on a demandé un raspberry pi 3 pour commencer la configuration par des étapes suivants:
- Tout d'abord, j'ai implémenté le système Raspbian sur la raspberry avec la carte SD, le système Raspbian est un système basé du système Debian Linux qui peut être implémenté dans chaque version de raspberry. On peut trouver des différentes versions sur le site officiel de raspberry. Ici j'ai choisi la version avec un desktop pour faciliter des testes avec l'affichage.(à la fin pour le produit final on n'a plus besoin de l'affichage, c'est juste pour tester l'algoritheme)
- Ensuite, j'essaie la connextion entre le pc et la raspberry par la liaison série en utilisant minicom puis je peux autoriser la connexion ssh sachant que l'addresse de raspberry est connu dans le même réseau après une configuration de Internet que je vais présenter dans la suite. Et pour le desktop il faut autoriser VNC qui est dans la même fenêtre que ssh. La commande sur terminal pour vérifier si la raspberry est bien connecté sur une interface de PC:
ls /dev/ttyUSB* lsusb
Après vérifier la connextion sur la raspberry, on utilise le minicom pour communiquer. Ensuite, il nous demande d'entrer le mot de passe, par défaut est raspberry. Jusqu'à maintenant on peut communiquer la raspberry avec ce terminal qui est le même pour un système Debian Linux. Le premier étape pour configurer la raspberry on tape la commande suivante:
sudo raspi-config
On peut voir une fenêtre avec un liste de configuration qu'on peut modifier:
Ici on choisit 5:Interface Options et on a une fenêtre suivante:
Et dans cette fenêtre on peut autoriser 2.SSH et 3.VNC pour la connexion en distance avec un termianl et avec un desktop.
Webcam
Pour la partie webcam,le but de cette partie est d'identifier le feu rouge, de rappeler à l'enfant de se concentrer sous forme de vibration lorsque le feu rouge est allumée.Donc,je pense que ma tâche peut être divisée en trois parties: 1. Écrivez un programme pour identifier des couleurs et des formes spécifiques. 2. Complétez la procédure ci-dessus afin que lorsqu'une forme d'une couleur spécifiée est reconnue, le vibreur se déclenche pour alerter l'enfant. 3. Optimisation. (En testant les procédures terminées, en découvrant les limites des procédures pour optimiser notre installation)
À s7, nous avons finalement décidé de programmer sur la base de RaspberryPi. Après avoir consulté les informations, j'ai trouvé une excellente bibliothèque de vision pour identifier les images --- openCV.
openCV
Le nom complet d'OpenCV est Open Source Computer Vision Library, qui est une bibliothèque de vision par ordinateur multiplateforme qui peut être utilisée pour développer des programmes de traitement d'image, de vision par ordinateur et de reconnaissance de formes en temps réel.Du coup,je pense que ce logiciel peut m'aider beaucoup.
Lidar
Pour la partie lidar, la tâche est de scanner l'environnement, c'est-à-dire 360 degrés au tour de l'utilisateur (dans notre prototype est 180 degrés car on veut tester le fonctionnement pour un côté d'adord), puis envoyer ces valeur dans un tableau (c'est juste pour suivre le format du code donné par l'entreprise sur github) à la raspberry pour qu'elle peut calculer la vitesse de chaque élément mobile (normalement on considère un élément avec une vitesse 0 ou avec une petite vitesse) selon le tableau. Et si cette vitesse arrive à la limite qu'on propose au début, la raspberry va contrôler ses sorties pour avtiver le vibreurs.(Comme au début l'équipe n'ai pas fait la fusion avec l'équipe 3 qui fait la partie son de la rasberry, le seul actionneur pour instant est de vibreur)
Actionneur: Audio
Pour cette partie, nous voulons tester l'audio de la raspberry et cherchons aussi la commande adéquate à utiliser pour l'audio. OMXPlayer est un lecteur multimédia en ligne de commande Installé sur Raspbian. Il peut lire de nombreux formats de fichiers audio. La ligne de commande la plus simple est omxplayer <nom du fichier média>. Le fichier multimédia peut être un fichier audio ou vidéo.
ssh raspberry pi omxplayer son.mp3
Ainsi, pour la gestion des actionneurs il nous faut premièrement répertorié les différentes situations qui auront pour conséquence l'activation de l'audio ou des vibreurs. Puis, nous allons faire des enregistrements associés à chaque situations avec une voix féminine pour apaiser le petit garçon. Enfin, nous mettrons tout ceci en code relié au code du capteur et webcam parce que c'est grace à ses différentes valeurs: vitesse de voitures, distance , détection de feux de signalisation (rouge, vert ) que nous pourront demander activation.
Semaine 3
Configuration
A la base du travail qu'on a réalisé la semaine dernière, il nous reste seulement le dernier étape avant nous pouvons commencer la manipulation sur nos ordinateurs en distance. C'est la connextion de l'Internet. Pour cela, tout d'abord il faut créer un fichier de configuration de l'internet par vim:
vim /etc/wpa_supplicant/wpa_supplicant.conf
Ici wpa_supplicant.conf est une fichier de configuration de WiFi sur Linux qui a un contenu suivant:
Avec ssid le nom de WiFi, psk le mot de passe de WiFi et priority le prioirité de la connextion. Pour plus de détail, dans nos testes, on utilise le partage de connextion sur nos portable. Et le plus grande la valeur de priority, le plus prioritaire de cette connextion. Ensuite avant utiliser SSH ou VNC, il faut savoir l'addresse de raspberry dans le réseau de WiFi, on a 2 méthodes pour l'instant:
- On utilise un logiciel s'appelle Advanced IP Scanner sur le PC, il peut nous donner le nom, l'addresse IP, l'addresse MAC et le fabriquant de chaque périphérique qui sont connectés dans le même réseau local, avec un exemple au-dessous:
- Sinon on peut utilser:
ifconfig
Avec cette commande on peut aussi avoir l'addresse de la raspberry pour faire une connextion SSH ou VNC. Jusqu'à maintenant on peut utiliser la commande suivante pour faire une connextion SSH dans n'importe quel terminal sur un PC.
ssh pi@172.20.10.3
Ici l'addresse IP est l'addresse qu'on a trouvé avec la méthode précédente. Et on peut voir sa réponse.
Et pour la connextion VNC est un peu près la même méthode sachant l'addresse IP de la raspberry, on utilise l'outil "connextion en distance" sur le système Windows:
On juste entre l'addresse IP dans la fenêtre de clique connecter ensuite on peut voir la fenêtre pour entrer le nom et le mot de passe de la raspberry.
Pareil que la raspberry qu'on a vu en cours Réseau en semestre 7, le nom on met pi et le mot de passe est raspberry. Ensuite on peut voir le desktop qu'on avait prévu en semestre 7.
Jusqu'à maintenant on a tout finir la préparation de la réalisation de projet, et maintenant on commence à travailler sur le webcam et le lidar. En ce moment, on a aussi fait une étude sur xrdp qui a la même fonction que VNC. Avec les commandes suivantes on peut utiliser le desktop aussi:
sudo apt-get install xrdp sudo apt-get install vnc4server tightvncserver sudo apt-get install xubuntu-desktop echo "xfce4-session" >~/.xsession sudo service xrdp restart
Installation du openCV
Après avoir décidé d'utiliser la bibliothèque openCV, le premier travail a été de l'installer sur le Raspberry Pi.Voici les principales étapes de mon installation:
1.Nous avons d'abord configuré le système d'exploitation Raspbian pour le Raspberry Pi.
2.J'ai entré la commande suivante sur la ligne de commande. Cette commande signifie ouvrir le fichier des modules avec l'éditeur nano:
sudo nano /etc/modules
J'ai ajouté une ligne à la fin de ce fichier:
bcm2835-v4l2
3.J'entre la commande:
vcgencmd get_camera
4.Les résultats suivants ont été obtenus:
Cela prouve que la caméra est correctement connectée.
Jusqu'à présent, le travail de préparation était terminé et j'ai commencé à installer OpenCV sur Raspberry Pi sous Python2. 5.
sudo apt-get install libopencv-dev sudo apt-get install python-opencv
6.J'ai ensuite testé OpenCV sur Python2. Je tape python2 sur la ligne de commande et appuyez sur Entrée:
import cv2
Ensuite, je suis entré:
cv2 .__ version__
Les résultats suivants sont apparus, prouvant que l'installation a réussi:
Ensuite, j'ai installé OpenCV sur Raspberry Pi exécutant Python3.
1.Tout d'abord, j'ai ouvert l'interface de ligne de commande et entré la commande suivante pour installer la bibliothèque de calcul scientifique Python numpy:
sudo pip3 install numpy
2.J'ai ensuite installé les bibliothèques requises par OpenCV:
sudo apt-get install build-essential git cmake pkg-config -y sudo apt-get install libjpeg8-dev -y sudo apt-get install libtiff5-dev -y sudo apt-get install libjasper-dev -y sudo apt-get install libpng12-dev -y sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev -y sudo apt-get install libgtk2.0-dev -y sudo apt-get install libatlas-base-dev gfortran -y
3.J'ai ensuite commencé à télécharger OpenCV:
cd wget https://github.com/Itseez/opencv/archive/3.4.0.zip wget https://github.com/Itseez/opencv_contrib/archive/3.4.0.zip
Alors, j'ai déballé ces deux archives:
cd /home/pi/Downloads unzip opencv-3.4.0.zip unzip opencv_contrib-3.4.0.zip
4.Ensuite, j'ai défini les paramètres de compilation:
cd /home/pi/Downloads/opencv-3.4.0 mkdir build cd build
5.Ensuite, j'ai défini les paramètres CMAKE:
[[cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D INSTALL_C_EXAMPLES=ON -D INSTALL_PYTHON_EXAMPLES=ON -D OPENCV_EXTRA_MODULES_PATH=/home/pi/Downloads/opencv_contrib-3.4.0/modules -D BUILD_EXAMPLES=ON -D WITH_LIBV4L=ON PYTHON3_EXECUTABLE=/usr/bin/python3.5 PYTHON_INCLUDE_DIR=/usr/include/python3.5 PYTHON_LIBRARY=/usr/lib/arm-linux-gnueabihf/libpython3.5m.so PYTHON3_NUMPY_INCLUDE_DIRS=/home/pi/.local/lib/python3.5/site-packages/numpy/core/include ..]]
La figure suivante montre que cmake s'avère efficace:
6.La dernière et la plus importante étape: compiler(Cela a pris beaucoup de temps.)
cd /home/pi/Downloads/opencv-3.4.0/build make sudo make insall
7.Après l'avoir installé, tapez python3 dans la ligne de commande et appuyez sur Entrée:
import cv2 cv2.__version__
Le résultat montré dans la figure ci-dessous montre qu'OpenCV a été installé avec succès dans l'environnement Python3.
Actionneurs: Différentes situations
Ici, nous voulons répertorier les différentes situations que pourrait rencontrer l'enfant à une sortie d'école par exemple. Le but étant de lui faire savoir qu'il a la possibilité de traverser en tenant compte des voitures à proximité (distance et vitesse) , de la couleur des feux de signalisation. Aussi nous devons lui dire s'il y'a un danger à venir, lui donner les indications adéquates concernant le danger.
Nous avons 4 types d'alertes selon les situations :
Alerte_arrêt indique que le feu est rouge et donc qu'il ne peut pas traverser: Attente Alerte_proche indique que la voiture est assez proche de l'enfant et même si le feu est au vert, il ne peut pas traverser Alerte_danger indique que la voiture arrive avec une grande vitesse et donc il ne peut pas traverser Alerte0 indique voiture assez éloignée et feu vert alors il peut traverser
Semaine 4
Choix du langage d'assemblage
Du code réalisable basé sur Raspberry Pi, C ++ nous est plus familier. Une raisson est que nous avons beaucoup étudié sur le langage C qui se semble C++ en cours, et une raison est que nous avons aussi cours OSCA qui contient une partie de le langage C++ pendant ce semestre. Et de plus, nous utiliserons beaucoup le C ++ dans les études ultérieures. Après avoir consulté nos coéquipiers, nous avons décidé d'utiliser le langage C ++ pour la programmation.
Allume la webcam.
Afin de détecter la position de le feu rouge en temps réel, je dois d'abord allumer le webcam du Raspberry Pi.
VideoCapture cap(0); if ( !cap.isOpened() ) { cout << "Cannot open the cam" << endl; return -1; }
Mais le webcam ne marche pas, après avoir consulté les informations en ligne, j'ai trouvé une solution: J'ai modifié / etc / modules et ajouté une ligne: bcm2835-v412。 Ensuite, j'ai redémarré, ls -l / dev / video0 et j'ai trouvé mon webcam.
Capture en temps réel depuis le webcam
Mat imgOriginal; bool bSuccess = cap.read(imgOriginal); // read a new frame from video if (!bSuccess) //if not success, break loop { cout << "Cannot read a frame from video stream" << endl; }
Cela déclare une image du type 'Mat'--imgOriginal, puis il existe une variable booléenne nommée bSuccess, qui est utilisée pour déterminer si l'acquisition d'image est réussie.
Etudes de fonctionnement de lidar
Après une recherche sur Internet, on a trouvé le datasheet de notre lidar RPLIDAR A1M8:
Fichier:Datasheet RPLIDAR A1M8.pdf
et le Development Kit User Manual:
Fichier:Development kit RPLIDAR A1M8.pdf
aussi on a une source le plus importante qui est le support sur github:
Pendant cette séance dont j'ai étudié des fichiers de type .h qui sont inclus au début dans l'exemple sur le github et aussi fait une simulation sur papier pour silmuler un cas simple dans la rue que je vais présenter dans la suite.
Donc dans mes études, les 5 fichiers de tête sont important car ils nous donnent des fonctions de base, des définitions très utiles qui permettent de implémenter le code facilement. Ici on va présenter plusieurs définitions importantes dans ces fichiers qui sont:
- Dans le fichier "rplidar.h" on inclut les 4 autres fichiers comme cela dans notre code on a besoin de faire seulement une inclure de cette fichier.
- Dans le fichier "rptypes.h" on a défini des structures et des vairables qui ne conernent pas le lidar comme
typedef uint32_t _u32;
- Dans le fichier "rplidar_driver.h" on a défini la class RPlidarDriver qui est le plus important interface dans le SDK(Software Development Kit). En effet ici on peut l'implémenter directement et on peut l'appliquer comme il a montré dans les exemples qu'il donne.Pour montrer un peu la principal, on donne quelque exemple suivants:
1.La fontion qui ouvert un port spécial pour connecter une cible qui est un périphérique RPLIDAR en communication sériale.
virtual u_result connect(const char *, _u32, _u32 flag = 0) = 0;
2.La fonction qui déconnecte le périphérique RPLIDAR et ferme le port de la communication sériale.
virtual void disconnect() = 0;
3.La fontion qui vérifie le résultat de la connextion.
virtual bool isConnected() = 0;
4.La fonction qui permet de commencer scanner les data de l'environnement.
virtual u_result startScan(bool force, bool useTypicalScan, _u32 options = 0, RplidarScanMode* outUsedScanMode = NULL) = 0;
5.La fonction qui récupère l'état de la sante de RPLIDAR.
virtual u_result getHealth(rplidar_response_device_health_t & health, _u32 timeout = DEFAULT_TIMEOUT) = 0;
6.La fonction qui stocke des mesures de l'environnement dans le nœud en l'ordre montant de la valeur de l'angle.
virtual u_result ascendScanData(rplidar_response_measurement_node_hq_t * nodebuffer, size_t count) = 0;
- Dans le fichier "rplidar_protocol.h" on a défini quelque structures de base et certain variables.
- Dans le fichier "rplidar_cmd.h" on a défini des structures et des variables de demandes et réponses de la communication. Je vais lister celui qui sont importants:
1.La structure de nœud où on stocke les mesure des distance, l'orientation, qualité de scan.
typedef struct _rplidar_response_measurement_node_t { _u8 sync_quality; // syncbit:1;syncbit_inverse:1;quality:6; _u16 angle_q6_checkbit; // check_bit:1;angle_q6:15; _u16 distance_q2; } __attribute__((packed)) rplidar_response_measurement_node_t;
2.La structure d'état où on stocke des informations du lidar.
typedef struct _rplidar_response_device_info_t { _u8 model; _u16 firmware_version; _u8 hardware_version; _u8 serialnum[16]; } __attribute__((packed)) rplidar_response_device_info_t;
3.La structure d'état où on stocke niveau de la santé du lidar
typedef struct _rplidar_response_device_health_t { _u8 status; _u16 error_code; } __attribute__((packed)) rplidar_response_device_health_t;
Ce sont le base avant étudier certaines applications qu'il donne pendant la prochaine séance.
Indications sonores
Selon l'alerte et la situation nous voulons donner les différentes indications sonores qui conviennent
Ainsi, nous faisons des enregistrements de ces phrases en utilisant une voix féminine. Les différentes indications explicitent clairement ce que Souleymane doit faire à l'instant. Soit il peut traverser, soit il doit patienter. lorsqu'il y a l'alerte danger, il y a aussi l'activation des vibreurs par intermittence selon la vitesse. Nous devons aussi veiller à ce que les enregistrements ne brusque pas mais plutôt ramène l'attention de Souleymane à la situation en douceur. Ces enregistrements seront convertis en fichier.wav .
Semaine 5
openCV
Après avoir configuré la caméra la semaine dernière, j'ai commencé à réfléchir à la façon d'utiliser openCV pour reconnaître les feux de circulation. Tout d'abord, je dois comprendre les différents fichiers de bibliothèque dans openCV.
I.HighGUI
Le module HighGUI comprend les entrées et sorties multimédias, la capture vidéo, l'encodage et le décodage d'images et de vidéos, et les interfaces d'interface graphique. Les fonctions principales sont:
1. imread (): lire une seule image dans OpenCV. 2. imshow (): affiche une image dans la fenêtre spécifiée. 3. NamedWindow (): créez une fenêtre. Si vous affichez simplement des images, vous pouvez utiliser imread () et imshow (). Mais lorsque vous devez utiliser le nom de la fenêtre avant d'afficher la fenêtre, vous devez utiliser cette fonction. 4. imwrite (): fichier image de sortie. 5. createTrackbar (): créez un curseur qui peut ajuster la valeur et attachez le curseur à la fenêtre spécifiée, qui est souvent utilisée en conjonction avec une fonction de rappel.
II.imgproc
1.Filtrage linéaire
Il est généralement utilisé pour débruiter et fonctionner avec différents noyaux de convolution. Les paramètres spécifiques sont visibles https://docs.opencv.org/3.2.0/d4/d86/group__imgproc__filter.html
blur() GaussianBlur() medianBlur() bilateralFilter()
2.Traitement morphologique(Morphology Operations)
Autrement dit, c'est un noyau (matrice) qui est prédéfini pour traiter l'image d'entrée. Chaque pixel dépend des pixels environnants. Différents noyaux sont utilisés pour obtenir des sorties différentes, donc le noyau est la clé. Érosion et dilatation: Prenez les valeurs minimale et maximale dans la plage du noyau de convolution, respectivement. Étant donné que l'image est une image en noir et blanc, le blanc est la valeur du pixel du sujet et le noir est l'arrière-plan. -cv :: erode: opération de corrosion, prendre une petite valeur, le blanc devient noir et la figure rétrécit. -cv :: dilate: opération de dilatation, par opposition à la corrosion. Sur la base de ces deux opérations, il existe également des opérations d'ouverture, de fermeture, de gradient morphologique, de chapeau haut de forme et de chapeau noir. Pour plus de détails, consultez https://docs.opencv.org/trunk/d3/dbe/tutorial_opening_closing_hats.html
Après avoir connu le principe de la corrosion et du gonflement, vous pouvez également extraire des lignes spéciales dans l'image, telles que des lignes horizontales et verticales.
3.Pyramides d'images(Image Pyramids)
Zoom sur l'image. Il existe deux méthodes courantes:
Pyramide gaussienne Pyramide laplacienne
Gauss est utilisé ici. Noyau gaussien:
Lors de la réduction: l'image est convoluée avec un noyau gaussien, puis même les lignes et les colonnes sont supprimées et le motif est réduit au quart de l'original. Lors d'un zoom avant: le noyau gaussien est utilisé pour la convolution pour estimer les pixels manquants. OpenCV propose deux fonctions:
pyrUp () pyrDown ()
4.Opérations de seuillage(Thresholding Operations)
Espace colorimétrique HSV:
hue(couleur): décrit la couleur, utile lors de la division d'objets en fonction de la couleur
saturation: du blanc au noir.
Value: décrit la luminosité ou la densité de la couleur
Espace colorimétrique RVB:
Trois canaux de chaque couleur ne peuvent pas être utilisés pour segmenter des objets en fonction de la couleur. L'espace colorimétrique peut être converti avec la fonction cv :: cvtColor ()
// Convert from BGR to HSV colorspace cvtColor(frame, frame_HSV, COLOR_BGR2HSV);
Autres fonctions principales:
threshold (): segmente une image en fonction d'un certain seuil. inRange (src, low, up, dst): En bref, il s'agit d'extraire les pixels de l'image src avec des couleurs entre low et up to dst, qui est une version avancée de la fonction de seuil.
5.Détection de lignes droites
Une ligne droite est détectée sur la base des bords détectés.
Si (x0, y0) est une valeur fixe dans la formule ci-dessus, alors toute paire (θ, rθ) représente un groupe de droites passant par (x0, y0). Si un groupe de lignes à trois points a une ligne droite commune, les trois points sont colinéaires.
HoughLines (): obtenir (θ, rθ) de la ligne droite détectée HoughLinesP (): obtenez les deux extrémités d'une ligne droite
Détecter les cercles: commencez par détecter les bords pour déduire le centre du cercle, puis trouvez le rayon le plus approprié pour le centre du cercle fixe. Utilisez cette fonction HoughCircles ().
6.Détection des contours
Les dérivés sont utilisés ici. Le bord de l'image est la zone où la valeur de pixel change considérablement, c'est-à-dire que le taux de changement de valeur de pixel est grand, c'est-à-dire la valeur dérivée. OpenCV fournit la fonction Sobel ().
Sobel(src_gray, grad_x, ddepth, 1, 0, ksize, scale, delta, BORDER_DEFAULT); Sobel(src_gray, grad_y, ddepth, 0, 1, ksize, scale, delta, BORDER_DEFAULT);
src_gray: Dans notre exemple, l'image d'entrée. Ici c'est CV_8U grad_x / grad_y: l'image de sortie. ddepth: La profondeur de l'image de sortie. Nous l'avons défini sur CV_16S pour éviter le débordement. x_order: Ordre de la dérivée dans la direction x. y_order: Ordre de la dérivée dans la direction y. scale, delta et BORDER_DEFAULT: Nous utilisons des valeurs par défaut.
Ici, les bords grad_x et grad_y dans les directions x et y sont respectivement calculés puis ajoutés pour obtenir les bords de l'image.
Vous pouvez également utiliser la deuxième valeur dérivée pour être 0 pour déterminer le bord. OpenCV fournit la fonction Laplacian ().
Fonction cv :: Canny
7.Transformation affine
cv :: remap: transformer les pixels, comme retourner l'image horizontalement getAffineTransform: récupère la matrice de transformation du rayonnement cv :: warpAffine: Transformer selon la matrice de transformation, warp. cv :: getRotationMatrix2D: rotation
Etudes de l'application
Dans le github on peut trouver 3 applications de base, dans notre cas, nous pouvons prendre une application simple et le modifier pour suffre la demande dqns le cahier de charge. Les 3 fonctions sont:
- Ultra_simple qui permet de scanner l'environnement et envoyer les data tout le temps, avec la commande 'ultra_simple /dev/ttyUSB0' si le RPLIDAR est connecté sur ttyUSB0. On peut aussi entrer 'ultra_simple' directement car dans le fichier on vous dit si on n'a pas entré le numéro de l'interface, par défaut est ttyUSB0.
- Simple_grabber qui permet de scanner l'environnement et l'afficher dans terminal un diagramme de rectangle pour visualiser l'environnement avec la même commande précédente. Cette fonction ne peut que montrer comment on utilise certaines fonctions car on n'a pas besoin de faire une partie d'affichage.
- Frame_grabber qui permet de scanner l'environnement et l'afficher sur une fenêtre qui peut nous donner un photo de la vu en haut.
Donc d'après ces 3 fonctions qu'on a, nous pouvons commencer à concevoir notre algoritheme pour chercher des voitures ou des choses mobiles avec une grande vitesse.
Pour un premier teste, on veut commencer par détecter la vitesse d'un point(ou plutôt une orientation) par l'étapes suivantes:
1.On fixe un point/une orientation supposant que c'est cette orientation où la voiture vient. On propose l'orientation dans l'axe de RPLIDAR est 0 degré pendant notre test. Et ici il faut montrer cet axe de RPLIDAR pour on peut imaginer le fonctionnement:
2.Ensuite on va mesurer la distance d1 entre l'obstacle et le RPLIDAR qui est 12 mètres maximal pour un moment t1 de cette direction.
3.Dans certains temps, on utilise la fonction delay(x) avec un paramètre x qui représente le temps en ms.
4.Ensuite on va mesurer la deuxième distance d2 de cette même direction sachant le temps passé entre ces 2 mesures pour un moment t2.
5.Avec des valeurs on mesure pendant les dernières étapes, on peut calculer la vitesse de cette voiture par la formule suivante:
7.Comparer la vitesse qu'on obtient avec la vitesse de dangeureuse qui est normalement la vitesse de la voiture dans la rue. Si la vitesse qu'on obtient est plus grande que la vitesse de dangeureuse, on va activer des vibreurs(dans un premier test on a 1 vibreur, mais après ce test on va avoir plus de vibreur
Cependant, avec cette méthode on ne peut que trouver seulement un résultat approxmatif car ici le vrai temps entre 2 mesures n'est pas x ms. Même si nous n'avons pas forcément besoin une valeur exacte, mais est-ce qu'on a une méthode qui est plus optimal? Et pour la taile de l'échantillon on fixe à 1 pour l'instant.
Semaine 6
La conception spécifique de la section webcam
Après avoir compris les principales fonctions de openCV, je pense que pour réaliser la fonction d'identification des feux de circulation, vous devez commencer par la manière la plus simple d'identifier les feux rouges. Pour y parvenir, je pense que ma fonction peut être divisée en les parties suivantes:
1. Détectez la position de la lumière rouge de la webcam en temps réel.(Déjà réalisé)
2. Reconnaître le rouge avec l'espace HSV.
3. Binarisez la partie rouge d'origine en blanc et le reste en noir.
4. Tracez un cercle sur la zone rouge.
5. Démarrez le shaker.
6. Améliorez et complétez.
Reconnaître le rouge avec l'espace HSV
- Modèle HSV
Il est difficile de trouver la gamme exacte de rouge en contrôlant les canaux RVB, j'ai donc choisi l'espace HSV. HSV (Hue, Saturation, Value) est un espace colorimétrique créé à partir des caractéristiques intuitives des couleurs. Les paramètres de couleur dans le modèle HSV sont: teinte (H: teinte), saturation (S: saturation) et luminosité (V: valeur). Un espace colorimétrique créé par A. R. Smith en 1978, également connu sous le nom de modèle hexagonal.
Teinte (H: teinte): Mesurée en termes d'angles, allant de 0 ° à 360 °, en comptant dans le sens antihoraire du rouge, 0 du rouge, 120 ° du vert et 240 ° du bleu. Leurs couleurs complémentaires sont: 60 ° pour le jaune, 180 ° pour le cyan et 300 ° pour le magenta;
Saturation (S: saturation): La valeur varie de 0,0 à 1,0. Plus la valeur est élevée, plus la couleur est saturée. Luminosité (V: valeur): La valeur va de 0 (noir) à 255 (blanc).
- Du modèle RVB au modèle HSV
Soit (r, g, b) les coordonnées rouge, verte et bleue d'une couleur, et leurs valeurs sont des nombres réels compris entre 0 et 1. Soit max l'équivalent du plus grand de r, g et b. Soit min la plus petite de ces valeurs. Pour trouver la valeur (h, s, v) dans l'espace HSV, où h ∈ [0, 360) est l'angle de teinte de l'angle, et s, v ∈ [0,1] est la saturation et la luminosité, calculées comme suit:
max=max(R,G,B) min=min(R,G,B) if R = max, H = (G-B)/(max-min) if G = max, H = 2 + (B-R)/(max-min) if B = max, H = 4 + (R-G)/(max-min) H = H * 60 if H < 0, H = H + 360 V=max(R,G,B) S=(max-min)/max
Il existe une fonction dans OpenCV qui peut directement convertir des modèles RVB en modèles HSV. Notez que dans OpenCV, H ∈ [0, 180), S ∈ [0, 255] et V ∈ [0, 255]. Nous savons que la composante H peut représenter fondamentalement la couleur d'un objet, mais les valeurs de S et V doivent également être dans une certaine plage, car S représente le degré de mélange de la couleur représentée par H et le blanc, ce qui signifie que le plus petit S, Plus la couleur est blanche, plus elle est claire; V représente le degré de mélange de la couleur représentée par H et le noir, c'est-à-dire que plus le V est petit, plus la couleur est noire.
Il s'agit d'une gamme floue, avec des parties de violet classées comme rouges.
- Réalisé
Mat imgHSV; vector<Mat> hsvSplit; cvtColor(imgOriginal, imgHSV, COLOR_BGR2HSV);//Convert the captured frame from RGB to HSV split(imgHSV, hsvSplit); equalizeHist(hsvSplit[2],hsvSplit[2]); merge(hsvSplit,imgHSV);
imgHSV est une image obtenue en convertissant l'espace RVB d'imgOriginal en espace HSV, puis en séparant les trois canaux de HSV. La fonction cvEqualizeHist est utilisée pour égaliser l'histogramme, qui peut transformer une image plus claire en une image plus profonde (c'est-à-dire améliorer la luminosité et le contraste de l'image). cvMerge: Combinez plusieurs images monocanal en une image multicanal, donc j'obtiens une image dans l'espace HSV. Et en ajustant la valeur de HSV, toutes les zones non rouges peuvent être filtrées.
Binarisation
La binarisation consiste à convertir une image couleur en noir et blanc pur. cvtColor peut convertir l'image en image en niveaux de gris, mais pas en image binaire. CvThreshold est une fonction de binarisation. La partie rouge d'origine de l'image obtenue est blanche et le reste est noir.
Carte électronique: Montage
Concernant la partie actionneurs, nous utilisons 4 moteurs vibreurs et un haut parleur pour le son. Dans le but d'amplifier le son de sortie, nous avons opté pour un amplificateur constitué d'un LM386. En effet, le LM386 est un amplificateur audio à faible voltage qui est fréquemment utilisé dans les appareils musicaux à piles comme les radios, les guitares, les jouets, etc. La plage de gain est de 20 à 200 . Ici nous avons augmenté le gain à 200 en utilisant une résistance et un condensateur entre les PIN 1 et 8.
Réalisation d'un essai simple pour Lidar
Pendant cette semaine on a réalisé un essai simple pour tester le cas ou il y une voiture aller vers l'utilisateur directement dans un certain l'angle.
L'algoritheme est simple, on utilise une fonction delay_lidar pour prendre le temps qu'on veut entre 2 mesures, et ces 2 mesures on a 2 distance, avec la formule
on peut calculer sa vitesse. Et comme on ne connecte qu'un vibreur, donc on n'a pas établi un système de l'alerte de différents niveau, mais avec un vibreur et le terminal pour afficher le résultat est déjà suffisant.
Le code correspond est aussi simple à réaliser avec les exemples donnés en GitHub. On ajoute seulement un compteur du temps et prend 2 mesures successives pour trouver la vitesse d'un objet en approche.
Semaine 7
Tracez un cercle sur la zone rouge
Utilisez d'abord la transformation de cercle de Hough pour détecter le cercle: la fonction cvHougeCircles a plusieurs paramètres: la première est l'image d'entrée, la seconde est la mémoire, la troisième est la méthode et l'algorithme et la quatrième est la résolution: lorsqu'elle est définie sur 1, La résolution est la même, la résolution 2 est normale et la cinquième est la distance minimale entre deux cercles que l'algorithme peut clairement distinguer. Les deux suivants sont des seuils, suivis des rayons maximum et minimum du cercle qui peuvent être trouvés. cvPoint est un type de données dans OpenCV et représente un point en deux dimensions. centre est le centre du cercle. En fait, dessiner le centre du cercle et dessiner le cercle sont la même chose. La fonction cvCircle est utilisée, mais lorsque le rayon du cercle dessiné est 0, il devient le centre du cercle de dessin.
vector<Vec3f> circles; HoughCircles( imgThresholded, circles, CV_HOUGH_GRADIENT,1.5, 10, 200, 100, 0, 0 ); for( size_t i = 0; i < circles.size(); i++ ) { Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); int radius = cvRound(circles[i][2]); //Dessiner le centre du cercle circle( imgThresholded, center, 3, Scalar(0,255,0), -1, 8, 0 ); //Dessinez un contour de cercle circle( imgThresholded, center, radius, Scalar(155,50,255), 3, 8, 0 ); }
Démarrez le shaker
Après avoir dessiné le cercle rouge, nous pouvons démarrer le vibreur pour réaliser le rappel. Tout d'abord, j'ai ajouté les bibliothèques nécessaires au vibreur:
#include <wiringPi.h>
Ensuite, j'ai initialisé le vibreur:
if(-1==wiringPiSetup()) { cerr<<"setup error\n"; exit(-1); } pinMode(7,OUTPUT);
Enfin, nous démarrons le vibrateur après avoir détecté la lumière rouge, le temps de vibration est de 3 secondes:
digitalWrite(7,HIGH); delay(3000); digitalWrite(7,LOW);
Etablir un système de l'alerte de différents niveaus
De même côté pour les vibreurs dans la partie Lidar, on veut utiliser 3 vibreurs avec 6 broches correspondantes sur la Raspberry pour établir un système de l'alerte de différents niveaus selon le niveau de danger.
On utilise le numéros de broche 0, 2 et 7 pour les vibreurs. On propose le plus dangereux de la situation, le plus fort(ici c'est le nombre de vibreurs activés) et le plus long temps d'activation d'un vibreur.
Code Actionneur
Nous voulons écrire le code des actionneurs:
- Pour rendre l'alerte du danger proportionnelle aux vibrations, nous activons l'un des vibreurs ou les 4 en même temps pour bien signifier le rapprochement du danger vers l'enfant.
- Pour le son, nous avons la fonction "omxplayer" qui appliqué directement en ligne de commande avec comme paramètre "leFichierSon.mp3" fait joué le son jusqu'à la fin. Pour le code alors nous faisons deux scripts shell, tel que l'un prend en compte l'audio de l'avertissement et le second l'audio du danger.
Dans un programme en C, nous utilisons la fonction "execl" qui prend en paramètre: le chemin d'accès prog, prog, les arguments et NULL.
int pid; pid=fork(); if(pid==0){ execl("/home/IMA4/projet/programmes/code1", " ", "code1", NULL) _exit(0) } else{ wait();}
Semaine 8
Améliorez et complétez
En raison des différentes conditions d'éclairage à différents moments, il est préférable d'avoir une interface de contrôle pour ajuster le HSV en temps réel afin que l'effet d'identification du rouge soit le meilleur. OpenCV a une interface graphique dédiée pour écrire des fonctions, ce qui est également très pratique pour écrire.
La conception et la réalsiation d'une première méthode de approximative du Lidar
Pendant cette séance on a ajouté le code pour notre deuxième cas:
- La voiture n'approche pas directement vers l'utilisateur mais ils vont se rencontrer dans un certain temps.
Et ici selon le consigne du prof, on utilise 2 notions qui sont points immobiles et points mobiles.
Si on calcule la vitesse de tous les points de 360 degrés, on trouve il y a des points mobiles et des points immobiles. Points mobiles sont des points qui ont une distance change de temps en temps, points immobiles sont des points qui ont une distance fixée ou change pas beaucoup. Et le calcul de la vitesse d'un point est utilisé pour détecter d'une voiture en approche directement vers l'utilisateur.
Pour notre deuxième cas: Si l'utilisateur de la voiture va se rencontrer dans un certain temps, c'est-à-dire pour lidar, le point qui répresente la voiture, ses paramètre(angle et distance) est comme cela: Si le lidar est sur le bras droite, selon notre graphe sur wiki, on propose que l'angle 0 est la drection vers laquelle l'utilisateur avance. Donc un moment son angle 60 a une distance D1 qui est la distance entre la voiture et lidar, ensuite dans un certain temps t1 on mesure son angle 45 et il a une distance D2 qui est plus petite que D1, et dans un certain temps t2 on mesure son angle 30 et il a une distance D3 qui est le plus petite. Si les mesures sont comme cela on peut dire approximativement il y un danger potentiel pour l'utilisateur et on alerte pour que l'utilisateur vérifie si la voiture freine bien. D1 est stockée dans la variable distance_60, D2 est stockée dans la variable distance_45, et D3 est stockée dans la variable distance_30.
Donc pour la partie code, je mesure les distance de ces 3 angle pour un premier test, et j'ai imposé 3 conditions pour sauvegarder cette distance:
1.La qualité de scanner cette distance n’est pas nulle, car si cette valeur est nulle cela veut dire on n'a pas réussi à prendre cette mesure et la distance est 0, donc si on prend cette mesure quand même on va recevoir une distance qui est 0.
2.La distance est supérieur que 200mm, car si c'est inférieur que 200mm=0.2m, il n'y a pas trop de sens(trop proche) et j'ai aussi eu une erreur qui est un moment cette valeur est négative(pour éviter cette erreur). Mais après ajouter cela, la distance = 0 quand même qui n'est pas normal, car si cette distance est inférieur à 200mm, on l'abandonne, sûrement la valeur minimales des distance_60, distance_45, distance_30 sont 200mm. (200mm en effet est limité par la taille de ma chambre pour un test).
3.Cette distance est inférieur que la distance qui est stockée dans la variable distance_60 (initialisée par 12000 en mm car la distance maximale est 12m)
A la fin, on compare ces 3 valeurs, et si distance_60>distance_45>distance_30, on peut dire approximativement la voiture et l'utilisateur peut-être se rencontrer dans un certain temps.
Pour les 2 résultats au-dessus, on voit que la distance est négative car je ne met pas la condition
nodes[pos].distance_q2/4.0f>200
Après ajouter cette condition, on peut trouver la distance sauvegardé dans nos variables sont égaux à 0 qui n'est pas possible car si la valeur de mesure est inférieur que 200mm alors on l'abandonne cette mesure.
Mais cette méthode a un problème de précision. Donc nous allons essayer une autre méthode la semaine prochaine.
Semaine 9
Compilation de la partie webcam
Parce que dans cette partie, j'utilise beaucoup de fichiers en openCV. Par conséquent, lors de la compilation, car le format de compilation n'est pas normalisé, de nombreuses tentatives échouent. Enfin, avec l'aide du professeur, j'ai appris la bonne méthode de compilation:
Lors de la compilation, j'échoue toujours et l'ordinateur signale toujours les erreurs et les fichiers manquants. J'ai essayé plusieurs méthodes: La première fois, j'avais l'habitude de compiler directement, et j'ai échoué.
La deuxième fois, parce que la dernière erreur était due à des fichiers manquants, j'ai placé tous les fichiers d'en-tête utilisés dans un dossier (c'est-à-dire dans le même dossier que le fichier d'exécution), bien que cette façon ait résolu certains problèmes , Mais il y a encore beaucoup de fichiers manquants signalés.
La troisième fois, j'ai essayé d'ajouter les fichiers requis au dossier étape par étape, mais en raison du trop grand nombre de fichiers et d'erreurs, je ne peux que renoncer à cette méthode.
La quatrième fois, j'ai essayé d'écrire un makefile, cette méthode peut connecter tous les fichiers d'en-tête, mais en raison du manque de connaissances, je ne peux pas écrire un makefile avec compétence, cette méthode a finalement déclaré l'échec.
La cinquième et dernière fois, j'ai demandé à M. Redon, il m'a appris un moyen de compiler, peut appeler tous les fichiers d'en-tête. Enfin, avec l'aide du professeur, j'ai réussi à mettre en œuvre la compilation.
La conception et la réalsiation d'une deuxière méthode de l'approximation du Lidar
Pendant cette semaine, selon le consigne des profs, on trouve que notre ancienne méthode ne marche pas, et donc on propose une deuxième méthode avec le principal suivant:
On trouve tout d'abord le point de gravité de la voiture(on fait un algoritheme pour chercher ces points à chaque temps et on propose la voiture va droit), et ensuite on créer un repère qui va présenter la voiture sur 2 coordoonnées polaires pour connaître à quelle distance la voiture va passer au plus près et à quel instant. A la fin on va traiter le temps t dans lequel la voiture passera au plus près et la distance dis entre la dernière mesure de la voiture et l'utilisateur.
Dans ce cas, si t est petit et dis est petit aussi c'est très grave et on va alerter fortement pour que l'utilisateur avertir vite. Si t est grand et dis est petit c'est moins grave parce que la voiture peut-être en train de freiner donc on va alerter moins fort. Si dis est supérieur que 5m c'est pas grave donc on n'alerte pas.
Le calcul sur papier pour trouver la droite de mouvement de la voiture
Tout d'abord on crée un repère des coordoonnées polaires comme au-dessous:
On propose que point O présente la position de l'utilisateur, et P2 est le premier point de gravité de la voiture, P1 est le deuxième point de gravité de la voiture, et y est l'axe qui est aussi la direction de mouvement de l'utilisateur. Et dans chaque mesure on connaît la distance r et l'angle theta de chaque point(on propose r1,theta1,r2,theta2 sont connus).
Donc on peut représenter P1, P2:
Ici on remplace r1*sin(theta1) par x1, r1*cos(theta1) par y1, r2*sin(theta2) par x2, et r2*cos(theta2) par y2 pour simplifier le calcul.
Sachant les positions de P1 et P2, on peut calculer la droite du mouvement de la voiture(avec une condition theta1<theta2, sinon la voiture va plus loin):
Ensuite on peut trouver la position au plus près est:
Donc le temps t pour passer de la postion P1 à cette position au plus près est:
Maintenant on a le temps t et la distance dis puis on va la comparaison avec les paramètres qu'on impose. Et cette comparaison sera faite dans la prochaine partie qui va présenter le code.
Algoritheme pour trouver le point de gravité de la voiture à chaque temps
Pour trouver ces points, on propose stocker les mesures entre 0 degré et 90 degrés car ce lidar traite seulement la voiture en droit et avant. Ensuite dans chaque deux mesure successifs on compare la distance de point avec le même angle, si ces distance changent beaucoup(comme de 12m à 5m) cela veut dire il y a un objet(la voiture dans notre cas) appraît, et ce point on l'appelle le point mobile. Par contre, le points qui ont une distance presque constante on l'appelle le points immobile. Si il y une voiture, apparament les points de certaines angles successifs vont tout changer donc ces points sont tous le point mobile et on prend la distance moyenne et l'angle moyenne comme le point de gravité de cette voiture.
Algoritheme pour calculer le temps entre 2 mesure successifs
Comme nous avons montré au début, pour calculer la droite, c'est mieux si on prend la deuxième mesure(recherche de point mobile) dans un second après prendre la première mesure. Si les 2 mesures sont trop proche, l'erreur est plus grande. Avant cette séance on utilise la fonction clock() qui n'est pas assez précis, donc cette séance on préfère utiliser la fonction gettimeofday(). A la place de montrer le code lourd, ici on met un exemple de cette fonction pour montre l'idée comment on calculer le temps entre 2 mesures:
struct timeval debut; struct timeval fin; unsigned long difference; gettimeofday(&debut,NULL); delay(1000); gettimeofday(&fin,NULL); difference = 1000000* (fin.tv_sec-debut.tv_sec)+ fin.tv_usec-debut.tv_usec; printf(“la différence est %ld\n”,difference);
Comme dans cette fontion le résultat est en microseconde(μs), on divise 1000 000 pour le présenter en second. C'est un peu près la même que clock() mais plus précis. Et on l'appelle avec debut quand on prend la première mesure et fin quand on prend la deuxième mesure.
Semaine 10
Ajouter un vibreur au module webcam
Enfin, afin de pouvoir rappeler les enfants à temps, j'utilise un vibreur pour réaliser, lorsque la webcam détecte une lumière rouge, activer le vibreur pour le rappeler aux enfants. Tout d'abord, j'ai ajouté le fichier d'en-tête:
#include<wiringPi.h>
Ensuite, nous avons soudé le vibrateur à la broche 7, puis configuré le Raspberry Pi pour activer le vibreur sur la broche 7 pour alerter l'enfant lorsqu'un feu rouge a été détecté:
if(-1==wiringPiSetup()) { cerr<<"setup error\n"; exit(-1); } pinMode(7,OUTPUT);
Enfin, chaque fois que je détecte une lumière rouge et dessine un cercle, je démarre le vibrateur et le fais vibrer pendant 1000 ms:
digitalWrite(7,HIGH); delay(1000); digitalWrite(7,LOW);
Ensuite, je compile d'une nouvelle manière:
Enfin, le résultat de l'opération est très réussi, le résultat est le suivant:
Lorsqu'une lumière rouge est détectée, le programme dessine un cercle et déclenche la vibration du vibrateur. Il y a un panneau sur l'écran principal qui contrôle la luminosité et les niveaux de gris, ce qui est pratique pour ajuster en fonction de la lumière différente pour obtenir le meilleur effet.
La introduction supplémentaire de la deuxière méthode de l'approximation du Lidar
Pendant ces 2 semaines qui est la vacance de Pâque, on a réussi à une implémentation de l'algorithme avec des équations mathématiques qu'on a calculé pendant la semaine dernière, mais il y a quelque part qui n'est pas précis. Donc avant présenter le détail du code, nous allons préciser quelque définition ici:
1.L'ordre de mesure de 2 points de gravités successif Pour faire une droite dans le repère qu'on a fait, il faut trouver 2 points pour fixer une droite, et comme on suppose que l'utilisateur marche vers la flèche positif de l'axe y, et selon le calcul au'on a fait, il y a aura un danger si et seulement si l'angle de point P1 est plus petit que l'angle de point P2, et même pour la distance.
Donc dans notre code, on va tout d'abord chercher un point de gravité dans la zone entre 0 degré et 90 degrés qui est notre point P2 avec un angle 'angle2'. Ensuite, on fait un petit delay pour la voiture avance une certaine distance, et puis on cherche le deuxième point de gravité dans la zone entre 0 degré et 'angle2' degrés. Si ce n'est pas le cas on utilise un variable s'appelle 'presence_voiture' qui représente la présence de la voiture, si on n'a pas détecté un point de gravité pendant ces 2 mesures, on pose cette variable à 0, et si cette varibale égale à 0, il n'y a pas de l'alerte. Parce que si dans un scan de l'environnement le lidar n'a pas pris la mesuer de quelquel angle, il va mettre 0 comme la valeur de distance de cet angle, donc on ajoute cette nouvelle variable 'presence_voiture' pour choisir les mesures utiles. Par exemple, si il y une distance égale à 0, on abandonne cette mesure et pose 'presence_voiture' égale à 0, donc il faut attendre la prochiane detection de point de gravité.
2.La méthode pour trouver le point le plus proche Ici pour trouver ce point, on propose une méthode de l'approximation qui n'est pas précis dans la semaine 9.
Le principle de cette méthode est:
On pose que la vitesse de l'utilisateur est négligeable par rapport à la voiture. Donc le point le plus proche est un point sur la droite. Cette droite on peut trouver par 2 points de gravités qu'on a. Et on a déjà fait un calcul général sur papier pour trouver une droite sachant les coordonnées des 2 point sur cette droite.
Donc maintenant on sait que le point o qui a les coordonnées (0,0) est la position de l'utilisateur et supposant qu'on a 2 points de gravités avec la méthode présentée au-dessus, on veut trouver le point le plus proche. Aussi il y a une limite pour ce point, c'est: ce point il faut se situe dans la zone où les coordonnées x et y sont tous positifs, car sinon la voiture ne va jamais se rencontrer avec l'utilisateur(même principe pour le produit sur le bras à gauche, chaque produit est responsable pour chaque côté)
Donc, on peut trouver le point le plus proche à le point o est le point sur l'axe y, aussi ce point est sur la droite en même temps.
3.La méthode pour calculer la vitesse de la voiture et le temps pour la voiture passera au point le plus proche Ici on suppose qu'on a le point le plus proche de l'utilisateur qui est le point au-dessus. Pour calculer la vitesse, on mesure le temps entre le moment où on mesure le point de gravité P2 et le moment où on mesure le point de gravité P1 avec la fonction 'gettimeofday()', et la distance de ces 2 points est facile de calculer par la méthode qu'on propose pendant la semaine 9. Ensuite on calcule la distance entre le point de gravité P1 et le point le plus proche comme on a vu en semaine 9 et ici on reprend l'équation:
Donc on voit que avec cette distance, et sachant la vitesste de la voiture, on peut calculer le temps pour la voiture passera au point le plus proche, et avec ce temps et le distance entre l'utilisateur et le point le plus proche, on peut établir un système de l'alerte selon le niveau d'urgence.
Le code principal
Dans cette partie nous allons présenter des codes important en C++ pour montrer comment réaliser ces algoritheme en détail.
if (IS_OK(op_result)) { drv->ascendScanData(nodes, count); }
Jusqu'à ici on a stocké les premières mesures dans le tableau 'nodes'.
op_result = drv->grabScanData(new_nodes, count); if (IS_OK(op_result)) { drv->ascendScanData(new_nodes, count);
Jusqu'à ici on a stocké les deuxièmes mesures dans le tableau 'new_nodes'.
for (int pos2 = 0; pos2 < 2000 ; pos2=pos2+1)
Ici on s'arrêt à 2000 parce qu'il y a 8192 de mesures dans un tableau et 2000 est environ 25% qui est un peu près 90 degrés.
if( ((nodes[pos2].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f<90) && ((new_nodes[pos2].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f<90) && (nodes[pos2].distance_q2/4.0f!=0) && (new_nodes[pos2].distance_q2/4.0f!=0) && fabs(nodes[pos2].distance_q2/4.0f-new_nodes[pos2].distance_q2/4.0f)>1000 )
ici il y a 5 conditions pour trouver le point de gravité, qui sont:
1.L'angle du point P2 qui représente la permière mesure dans le tableau 'nodes' est inférieur que 90 degrés.
2.L'angle du point P1 qui représente la deuxième mesure dans le tableau 'new_nodes' est inférieur que 90 degrés.
3.La distance du P2 qui représente la permière mesure dans le tableau 'nodes' n'est pas égale à 0 car si cette valeur est nulle cela veut dire cette mesure est inutile, donc il faut l'abandonner.
4.La distance du P1 qui représente la deuxième mesure dans le tableau 'new_nodes' n'est pas égale à 0 car si cette valeur est nulle cela veut dire cette mesure est inutile, donc il faut l'abandonner.
5.La différence des distances de ces 2 points P1 et P2 est supérieur que 1 mètre pour assurer c'est un point mobile parce que si il y a un changement de plus 1 mètre, on peut dire apparemment il y un objet qui apparaît ou disparaît dans cet angle, et normalement la distance plus petite est la distance de cet objet, et autre distance qui est plus grande est la distance de l'environnement.
Avec ces 5 conditions, on est sûr que ce point est un point de gravité, et ensuite on peut faire des calculs pour la vitesse et le temps.
{ presence_voiture=1;
Ici si on détecte un point de gravité, on pose la valeur presence_voiture égale à 1 comme cela on peut continuer dans la suite. Sinon pour la prochaine for, il y a six conditions, parmi ces six conditions, il y une qui est la variable 'presence_voiture' n'est pas nulle.
num2=pos2; gettimeofday(&debut,NULL);
Ici dès que on trouve le primier point de gravité qui est P2, on commence compter le temps.
dis2= (nodes[pos2].distance_q2/4.0f>new_nodes[pos2].distance_q2/4.0f)?new_nodes[pos2].distance_q2/4.0f:nodes[pos2].distance_q2/4.0f; angle2=(nodes[pos2].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f;
Ici on stocke la valeur de distance et de l'angle dans ces 2 variables qui seront utilisées plus tard.
x2 = dis2 * sin(angle2*PI/180.0f); y2 = dis2 * cos(angle2*PI/180.0f);
Ici avec des équations mathématiques on peut calculer les coordonnées de P2. Il faut faire attension que cet angle il faut transférer sous forme de degré pas en radian. Pour cela on a aussi fait un essai pour montrer cette transformation est correcte. On va présenter cet essai plus tard.
/*printf("theta: %03.2f Dist: %08.2f \n ", //pour montrer si on a trouve le point de gravite (new_nodes[num1].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f, new_nodes[num1].distance_q2/4.0f); */
Ici c'est pour montrer qu'on a trouvé le point de gravité, on a fait un essai pour montrer si on a vraiment trouvé ce point sur affichage, et cet essai on va présenter plus tard aussi.
break;
ici on pose une interruption de boucle, car on a besoin de seulement un point de gravité pour ce moment et faut mieux attendre un peu puis on commence à chercher le deuxième point de gravité.
} else { presence_voiture=0; }
Ici on ajoute une condition, si on n'a pas détecté le point de gravité, on pose la valeur de 'presence_voiture' égale à 0, et donc on ne peut pas passer dans le prochain boucle,et puis donc on attend le prochaine détection.
} delay_lidar(500);
Ici on pose un retard de 500ms puis on prend la deuxième mesure.
drv->ascendScanData(nodes, count); drv->ascendScanData(new_nodes, count);
Ici avec le même principe on trouve le point de gravité P1.
for (int pos1 = 0; pos1 < 2000 ; pos1=pos1+1) {
Pareil que P2, on prend seulement 90 degrés pour chaque côté.
if( presence_voiture && ((nodes[pos1].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f<angle2) && ((nodes[pos1].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f<90) && ((new_nodes[pos1].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f<90) && (nodes[pos1].distance_q2/4.0f!=0) && (new_nodes[pos1].distance_q2/4.0f!=0) && fabs(nodes[pos1].distance_q2/4.0f-new_nodes[pos1].distance_q2/4.0f)>1000 )
On ajoute une condition supplémentaire qui est 'presence_voiture' doit être non nulle. Sinon on manque un point et on ne peut pas calculer la droite avec seulement un point.
{ num1=pos1; gettimeofday(&fin,NULL);
Ici c'est la fin de compteur du temps.
dis1= (nodes[pos1].distance_q2/4.0f>new_nodes[pos1].distance_q2/4.0f)?new_nodes[pos1].distance_q2/4.0f:nodes[pos1].distance_q2/4.0f; angle1=(nodes[pos1].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f;
Pareil que P2, on a 2 variables qui stocke les valeurs de la distance et l'angle de point P1.
x1 = dis1 * sin(angle1*PI/180.0f); y1 = dis1 * cos(angle1*PI/180.0f);
Avec des équations mathématiques, c'est facile de calculer les coordonnées.
/*printf("theta: %03.2f Dist: %08.2f \n ", //pour montrer si on a trouve le point de gravite new_nodes[num1].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f, new_nodes[num1].distance_q2/4.0f); */
Pareil cette partie en commentaire est pour montrer si on a trouvé le deuxième point de gravité.
break; } }
Pareil on pose une interruption ici car on a besoin de seulement un point.
En effet le code jusqu'à ici marche bien et on voit la distance et l'angle de ces points de gravité sur terminal, mais dans la suite avec des équations racine et carré on a eu un petit problème que nous allons montrer dans la partie 'Problèmes rencontrés'.
//maintenant on a toutes les mesures qu'on a besoin pour calculer le droite temps=(fin.tv_sec-debut.tv_sec)+fin.tv_usec-debut.tv_usec; //le temps en second Vitesse=sqrt( (y2-y1)*(y2-y1) + (x2-x1)*(x2-x1) )/temps; dis=sqrt( x1*x1 + pow( (x1*y2-x1*y1)/(x2-x1) ,2) ); t=dis/Vitesse;
Dans cette partie on applique les équations mathématique qu'on a calculé pendant la semaine 9. Et puis on peut trouver le temps avec le résultat de la fonction gettimeofday(), avec ce temps et sachant la distance entre P1 et P2(on a leurs coordonnées), on peut calculer la vitesse de cet objet. Aussi avec notre introduction, ici on prend le point sur l'axe y le point plus proche, donc on peut calculer le temps dans lequel la voiture passe au plus proche que l'utilisateur.
if(Vitesse > double(5000)) { alerte=1; } if(Vitesse > 15000) { alerte_vite=1; } if(dis < 2000) { alerte_proche=1; } if(dis > 5000) { alerte=0; }
Ici sont des contidions pour alerter. Comme nous avons déjà présenté notre système de l'alerte avec différents niveaus d'urgence.
Si la vitesse arrive à une limite, on active l'alerte normale qui dure pas long temps, et si la vitesse est vraiment grande, on active l'alerte de grande vitesse dure un certain temps, ensuite si la distance est plus que 5 mètres, dans ce cas n'est pas très grave, on n'alerte pas. Mais si cette distance est petit que 2 mètres, cela veut dire c'est grave, il faut alerter tout de suite et dure long temps.
Des essais individuels
Car dans le code final il y a beaucoup de fonctions que je n'ai jamais vu avant, donc ici je crée un nouveau fichier pour tester ces fonctions séparément.
Comme on a vu dans la figure au-dessus, on teste le fonctionnement de la fonction gettimeofday et comment transformer le radian en degré dans la fonction sin() et cos().
Et ici on peut voir la distance et l'angle d'un point de gravité si on utiliser la partie en commentaire dans le code pour afficher si on a trouver le point de gravité.
Problèmes rencontrés
Ici en effet après réussir trouver les points de gravité, on veut faire la calcul, mais c'est les codes suivants qui ont une erreur:(sans ces codes je peux complier et exécuter le fichier)
Vitesse=sqrt( (y2-y1)*(y2-y1) + (x2-x1)*(x2-x1) )/temps; dis=sqrt( x1*x1 + pow( (x1*y2-x1*y1)/(x2-x1) ,2) ); t=dis/Vitesse;
Et je suis sûr que j'ai implémenté le fichier de tête correspondants. Et j'ai cherché sur Internet il y quelqu'un dit que le fichier de tête de fonction mathématique est
#include <cmath>
en C++ et en C est:
#include <math.h>
Comme j'ai bien mis ces fichier de tête mais il y a quand même une erreur que je n'avais pas eu dans l'essai avec la même fonction.
Semaine 11
La liaison entre le lidar et le webcam
Enfin, nous avons combiné la webcam et le radar. Afin de comprendre l'effet de l'approche du véhicule au feu rouge, nous avons utilisé une boîte noire pour simuler le véhicule, et l'image du feu rouge a simulé le feu rouge. Après de nombreux tests, il a été constaté que dans ce cas, la vibration du vibrateur peut rappeler à l'enfant d'améliorer sa concentration.
Semaine 12
Limitations
Même si on a réussi un essai avec lidar et webcam, il y encore des limites de notre prototype.
1.Le temps de réponse pour le webcam est un peu long qui sera danger dans la vie.
2.Nous n'avons pas considéré le cas où il y a plusieurs voitures.
3.Nous n'avons pas réussi la fin de troisième méthode.
4.Le prototype est lourd à porter pour un enfant, à cause du lidar, la raspberry et la batterie.
Documents Rendus
Le rapport:Fichier:Rapport final S8 P17 3.pdf
Le diaporama:Fichier:Support Soutenance P17.pdf