IMA5 2019/2020 P09 : Différence entre versions

De Wiki de Projets IMA
(Accéléromètre)
(Semaine 21 : 17 - 21 Février (Soutenance 2))
 
(26 révisions intermédiaires par 2 utilisateurs non affichées)
Ligne 154 : Ligne 154 :
 
|style="text-align:center;" bgcolor="#D3D3D3" |  
 
|style="text-align:center;" bgcolor="#D3D3D3" |  
 
|style="text-align:center;" |2
 
|style="text-align:center;" |2
 +
|-
 +
| Modélisation et Impression 3D
 +
| style="text-align:center;" bgcolor="#D3D3D3" |
 +
| style="text-align:center;" bgcolor="#D3D3D3" |
 +
| style="text-align:center;" bgcolor="#D3D3D3" |
 +
|style="text-align:center;" bgcolor="#D3D3D3" |
 +
|style="text-align:center;" bgcolor="#D3D3D3" |
 +
|style="text-align:center;" bgcolor="#D3D3D3" |
 +
|style="text-align:center;" bgcolor="#D3D3D3" |
 +
|style="text-align:center;" bgcolor="#D3D3D3" |
 +
|style="text-align:center;" bgcolor="#D3D3D3" |
 +
|style="text-align:center;" bgcolor="#D3D3D3" |
 +
|style="text-align:center;" bgcolor="#D3D3D3" |
 +
| style="text-align:center;" bgcolor="#B0F2B6" | 5
 +
|
 +
|
 +
|
 +
|style="text-align:center;" |5
 
|-
 
|-
 
| style="text-align:center;" bgcolor="#A1D4E5" | '''Formation - Documentation'''
 
| style="text-align:center;" bgcolor="#A1D4E5" | '''Formation - Documentation'''
Ligne 187 : Ligne 205 :
 
|
 
|
 
|
 
|
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 4
 
|
 
|
 
|
 
|
|
+
|style="text-align:center;" | 6
|style="text-align:center;" | 2
 
 
|-
 
|-
 
| Formation Partie Cartographie (LIDAR)
 
| Formation Partie Cartographie (LIDAR)
Ligne 276 : Ligne 294 :
 
|style="text-align:center;" bgcolor="#B0F2B6" | 2
 
|style="text-align:center;" bgcolor="#B0F2B6" | 2
 
|style="text-align:center;" bgcolor="#B0F2B6" | 7
 
|style="text-align:center;" bgcolor="#B0F2B6" | 7
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 3
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 2
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 2
 
|
 
|
|
+
|style="text-align:center;" | 19
|
 
|
 
|style="text-align:center;" | 15
 
 
|-
 
|-
 
| Code C++ : Lidar
 
| Code C++ : Lidar
Ligne 313 : Ligne 331 :
 
|
 
|
 
|
 
|
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 2
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 2
 
|
 
|
|
+
|style="text-align:center;" | 17
|
 
|style="text-align:center;" | 13
 
 
|-
 
|-
 
| Code Python : Open CV
 
| Code Python : Open CV
Ligne 348 : Ligne 366 :
 
|
 
|
 
|style="text-align:center;" bgcolor="#B0F2B6" | 5
 
|style="text-align:center;" bgcolor="#B0F2B6" | 5
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 4
 
|
 
|
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 3
 
|
 
|
|
+
|style="text-align:center;" |21
|
 
|style="text-align:center;" |14
 
 
|-
 
|-
 
| style="text-align:center;" bgcolor="#A1D4E5" | '''Rencontre Tuteurs'''
 
| style="text-align:center;" bgcolor="#A1D4E5" | '''Rencontre Tuteurs'''
Ligne 384 : Ligne 402 :
 
|style="text-align:center;" bgcolor="#B0F2B6" | 2
 
|style="text-align:center;" bgcolor="#B0F2B6" | 2
 
|style="text-align:center;" bgcolor="#B0F2B6" | 2
 
|style="text-align:center;" bgcolor="#B0F2B6" | 2
|
+
|style="text-align:center;" bgcolor="#B0F2B6" | 2
|
+
|style="text-align:center;" bgcolor="#B0F2B6" | 2
|
+
|style="text-align:center;" bgcolor="#B0F2B6" | 2
|
+
|style="text-align:center;" bgcolor="#B0F2B6" | 2
|style="text-align:center;" | 24
+
|style="text-align:center;" | 32
 
|-
 
|-
 
| style="text-align:center;" bgcolor="#A1D4E5" | '''Documentation'''
 
| style="text-align:center;" bgcolor="#A1D4E5" | '''Documentation'''
Ligne 423 : Ligne 441 :
 
|
 
|
 
|
 
|
|
+
|style="text-align:center;" bgcolor="#B0F2B6" | 3
 
|style="text-align:center;" |21
 
|style="text-align:center;" |21
 
|-
 
|-
Ligne 438 : Ligne 456 :
 
|
 
|
 
|
 
|
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 1
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 3
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 3
 
|
 
|
|
+
|style="text-align:center;" | 9
|
 
|
 
|style="text-align:center;" | 2
 
 
|-
 
|-
 
| Préparation Soutenance 1
 
| Préparation Soutenance 1
Ligne 476 : Ligne 494 :
 
|
 
|
 
|
 
|
 +
|style="text-align:center;" bgcolor="#B0F2B6" | 2
 
|
 
|
|
+
|style="text-align:center;" | 2
|style="text-align:center;" |  
 
 
|-
 
|-
 
| Rédaction du rapport (Interm et Final)
 
| Rédaction du rapport (Interm et Final)
Ligne 493 : Ligne 511 :
 
|
 
|
 
|
 
|
|
+
|style="text-align:center;" bgcolor="#B0F2B6" | 2
|
+
|style="text-align:center;" bgcolor="#B0F2B6" | 4
|
+
|style="text-align:center;" bgcolor="#B0F2B6" | 2
|style="text-align:center;" | 8
+
|style="text-align:center;" | 16
 
|-
 
|-
 
!scope=col style="text-align:center;" | Total  
 
!scope=col style="text-align:center;" | Total  
Ligne 510 : Ligne 528 :
 
!scope=col style="text-align:center;" |  12
 
!scope=col style="text-align:center;" |  12
 
!scope=col style="text-align:center;" |  26
 
!scope=col style="text-align:center;" |  26
!scope=col style="text-align:center;" |   
+
!scope=col style="text-align:center;" |  15
!scope=col style="text-align:center;" |   
+
!scope=col style="text-align:center;" |  15
!scope=col style="text-align:center;" |   
+
!scope=col style="text-align:center;" |  23
!scope=col style="text-align:center;" |   
+
!scope=col style="text-align:center;" |  7
 
|style="text-align:center;" bgcolor="#A1D4E5" |  
 
|style="text-align:center;" bgcolor="#A1D4E5" |  
 
|}
 
|}
Ligne 580 : Ligne 598 :
 
Pour calculer la sortie du neurone, on applique au potentiel une fonction qui peut être continue ou non.
 
Pour calculer la sortie du neurone, on applique au potentiel une fonction qui peut être continue ou non.
 
Il est donc possible de connecter la sortie d’un neurone à l’une des entrées d’un autre neurone, c’est de cette façon que l’on peut former un réseau de neurone. <br>
 
Il est donc possible de connecter la sortie d’un neurone à l’une des entrées d’un autre neurone, c’est de cette façon que l’on peut former un réseau de neurone. <br>
* Il existe ensuite plusieurs fonctions d’apprentissage, qui permettent de modifier les poids des connections entre les neurones. La modification des poids entraîne un changement dans la sortie du réseau de neurone. Il existe différents types d’apprentissage pour les réseaux de neurones :
+
* Il existe ensuite plusieurs fonctions d’apprentissage, qui permettent de modifier les poids des connexions entre les neurones. La modification des poids entraîne un changement dans la sortie du réseau de neurone. Il existe différents types d’apprentissage pour les réseaux de neurones :
 
::- '''Apprentissage supervisé''' : Plusieurs échantillons, représentant le contexte dans lequel se situe le neurone, sont présentés au réseau de neurones. Chaque échantillon est donné avec une sortie qui devrait être la même que la sortie calculée. Le réseau de neurone va alors se corriger selon la différence entre la sortie donnée et la sortie calculée par le réseau.
 
::- '''Apprentissage supervisé''' : Plusieurs échantillons, représentant le contexte dans lequel se situe le neurone, sont présentés au réseau de neurones. Chaque échantillon est donné avec une sortie qui devrait être la même que la sortie calculée. Le réseau de neurone va alors se corriger selon la différence entre la sortie donnée et la sortie calculée par le réseau.
 
::- '''Apprentissage par renforcement''' : Plusieurs échantillons sont aussi présentés au réseau de neurones. Lorsque la sortie du réseau de neurone n’est pas celle voulue, le réseau est notifié de son erreur, mais aussi de sa réussite en cas de succès. Contrairement à l’apprentissage supervisé, le réseau ne peut pas connaître la valeur de son erreur en comparant sa sortie calculée avec une sortie donnée.
 
::- '''Apprentissage par renforcement''' : Plusieurs échantillons sont aussi présentés au réseau de neurones. Lorsque la sortie du réseau de neurone n’est pas celle voulue, le réseau est notifié de son erreur, mais aussi de sa réussite en cas de succès. Contrairement à l’apprentissage supervisé, le réseau ne peut pas connaître la valeur de son erreur en comparant sa sortie calculée avec une sortie donnée.
Ligne 900 : Ligne 918 :
 
===Croquis du site final===
 
===Croquis du site final===
  
Voici un croquis du site que je souhaite réaliser:  
+
Voici un croquis du site que je souhaite réaliser :  
  
 
[[Fichier:P9_Site SLAMiT.png|center |500 px| Croquis du site ]]
 
[[Fichier:P9_Site SLAMiT.png|center |500 px| Croquis du site ]]
Depuis la création du projet, j'ai choisi le nom du robot: SLAMiT. SLAM (simultaneous localization and mapping) fait référence à la cartographie et la localisation simultanées.  
+
Depuis la création du projet, j'ai choisi le nom du robot: SLAMiT. SLAM (simultaneous localization and mapping) fait référence à la cartographie et la localisation simultanée.  
 
 
L'objectif serait d'avoir une interface simple dans un premier temps, qui permettrait de:
 
  
 +
L'objectif serait d'avoir une interface simple dans un premier temps, qui permettrait de :
 +
 
* Voir le retour caméra du robot
 
* Voir le retour caméra du robot
 
* Utiliser des flèches pour déplacer le robot à distance  
 
* Utiliser des flèches pour déplacer le robot à distance  
Ligne 1 018 : Ligne 1 036 :
 
=== Open CV ===
 
=== Open CV ===
  
Comme l'affichage des données est fonctionnel sur le site, j'ai cherché une façon d'exploiter mes résultats. J'ai choisi de creuser du coté du langage python parce qu'il possède une bibliothèque graphique libre pour le traitement d'images en temps réel. N'ayant pas de connaissance dans le domaine de Computer Vision, je me suis dans un premier temps fortement inspiré des codes déjà rédigés sur le site [https://www.pyimagesearch.com/ suivant] :  
+
Comme l'affichage des données est fonctionnel sur le site, j'ai cherché une façon d'exploiter mes résultats. J'ai choisi de creuser du côté du langage python parce qu'il possède une bibliothèque graphique libre pour le traitement d'images en temps réel. N'ayant pas de connaissance dans le domaine de Computer Vision, je me suis dans un premier temps fortement inspiré des codes déjà rédigés sur le site [https://www.pyimagesearch.com/ suivant] :  
  
 
==== Affichage de la cartographie  ====
 
==== Affichage de la cartographie  ====
Ligne 1 289 : Ligne 1 307 :
  
 
Ce problème est dû à des mouvements brusques du robot, lorsqu'il va trop vite :  le phénomène de shift. Cela  arrive souvent lorsque le robot  tourne de façon trop rapide.  
 
Ce problème est dû à des mouvements brusques du robot, lorsqu'il va trop vite :  le phénomène de shift. Cela  arrive souvent lorsque le robot  tourne de façon trop rapide.  
Mais c'est également dû au fait que le robot n'est pas "solide". Lorsqu'il avance avec un vitesse importante et qu'il freine subitement. C'est à cause des suspensions de mauvaise qualité, le lidar qui est placé sur le robot change de position et ne reste pas vraiment horizontal.  
+
Mais c'est également dû au fait que le robot n'est pas "solide". Lorsqu'il avance avec une vitesse importante et qu'il freine subitement, la plateforme sur laquelle est fixée le Lidar ne reste pas parallèle au sol. C'est à cause des suspensions de mauvaise qualité, le lidar qui est placé sur le robot change de position et ne reste pas vraiment horizontal.
  
 
=== Autre piste de recherche :  Exploitation des données du fichier .bag  ===
 
=== Autre piste de recherche :  Exploitation des données du fichier .bag  ===
Ligne 1 301 : Ligne 1 319 :
 
'''Screen Recorder'''  
 
'''Screen Recorder'''  
  
Dans un premier temps, j'ai travaillé en effectuant des enregistrement vidéos de mon écran avec l'application lorsqu'il n'était pas branché à mon écran. Ca fonctionne mais ce n'est pas pratique.  
+
Dans un premier temps, j'ai travaillé en effectuant des enregistrements vidéos de mon écran avec l'application lorsqu'il n'était pas branché à mon écran. Ca fonctionne mais ce n'est pas pratique.  
 
  sudo apt-get update
 
  sudo apt-get update
 
  sudo apt-get install simplescreenrecorder
 
  sudo apt-get install simplescreenrecorder
Ligne 1 389 : Ligne 1 407 :
 
J'aimerai pour la suite du projet, pouvoir contrôler le robot avec l'interface graphique de ROS. Pour le moment je contrôle les moteurs du robot avec en imposant une valeur entre 0 et 255 à chaque roue. (Pour rappel, le robot est équipé de 6 moteurs à courant continu).  
 
J'aimerai pour la suite du projet, pouvoir contrôler le robot avec l'interface graphique de ROS. Pour le moment je contrôle les moteurs du robot avec en imposant une valeur entre 0 et 255 à chaque roue. (Pour rappel, le robot est équipé de 6 moteurs à courant continu).  
  
Avec ROS, la seule façon de contrôler un robot c'est en lui imposant les valeurs de vitesse x, y et θ. Mais je n'ai pas d'encodeur sur mes moteurs. Pour le moment, il faut que je trouve une solution pour controler mon robot en vitesse.
+
Avec ROS, la seule façon de contrôler un robot c'est en lui imposant les valeurs de vitesse x, y et θ. Mais je n'ai pas d'encodeur sur mes moteurs. Pour le moment, il faut que je trouve une solution pour contrôler mon robot en vitesse.
'''A compléter'''
 
  
 
==Semaines 17 et 18  : 20 Janvier - 2 février ==
 
==Semaines 17 et 18  : 20 Janvier - 2 février ==
Ligne 1 403 : Ligne 1 420 :
 
=== Modélisations===
 
=== Modélisations===
 
Le problème qui se pose est que je n'ai pas suffisamment de place pour positionner l'encodeur sur l'axe du moteur.  
 
Le problème qui se pose est que je n'ai pas suffisamment de place pour positionner l'encodeur sur l'axe du moteur.  
* Dans un premier temps, j'ai modélisé une pièce 3D qui permettait de rallonger l'axe :  
+
====  > Modélisation 1 ====
[[Fichier:P9_encodeur_axe.png|center|200px]]
+
Dans un premier temps, j'ai modélisé une pièce 3D qui permettait de rallonger l'axe : (cf Modélisation 1)
 
Cette solution n'a pas fonctionnée parce que les valeurs sont tellement petites (0,3 cm de diamètre) que  ça n'a pas abouti.  L'imprimante 3D n'a pas une précision assez bonne.
 
Cette solution n'a pas fonctionnée parce que les valeurs sont tellement petites (0,3 cm de diamètre) que  ça n'a pas abouti.  L'imprimante 3D n'a pas une précision assez bonne.
  
* J'ai ensuite modélisé deux pièces, un support pour accrocher l'encodeur à la structure du robot et une roue à la bonne échelle pour qu'elle puisse tenir sur l'axe des coupleurs plutôt que sur l'axe du moteur.  
+
====  > Modélisation 2 ====
[[Fichier:P9_encodeur_support.png|thumb|Support de l'encodeur|left|200px]]
+
J'ai ensuite modélisé deux pièces, un support pour accrocher l'encodeur à la structure du robot et une roue à la bonne échelle pour qu'elle puisse tenir sur l'axe des coupleurs plutôt que sur l'axe du moteur.   (cf Modélisation 2)
[[Fichier:P9_encodeur_roue.png|thumb|Roue|center|200px]]
 
 
 
  
 
Sur internet, il n y a pas de documentations concernant [https://www.ebay.com/itm/TinySine-Wheel-Encoder-Kit-for-Robot-Car/254430315819?hash=item3b3d3a852b:g:bM4AAOSwbv9d2Bxi cette] version d'encodeur. Mais il y a trois pins :  
 
Sur internet, il n y a pas de documentations concernant [https://www.ebay.com/itm/TinySine-Wheel-Encoder-Kit-for-Robot-Car/254430315819?hash=item3b3d3a852b:g:bM4AAOSwbv9d2Bxi cette] version d'encodeur. Mais il y a trois pins :  
Ligne 1 416 : Ligne 1 431 :
 
* GRND
 
* GRND
 
* OUT : Ce qui indique qu'il un signal dès que la lumière de la LED est reçue par le capteur de lumière.
 
* OUT : Ce qui indique qu'il un signal dès que la lumière de la LED est reçue par le capteur de lumière.
 
  
 
Voici le rendu :  
 
Voici le rendu :  
 
[[Fichier:P9_encodeur_robot.png|thumb|Montage|center|500px]]  
 
[[Fichier:P9_encodeur_robot.png|thumb|Montage|center|500px]]  
 
J'ai imprimé les pièces en double afin de placer les encodeurs sur les deux roues à l'avant.
 
J'ai imprimé les pièces en double afin de placer les encodeurs sur les deux roues à l'avant.
 +
 +
====  > Modélisation 3 ====
 +
<span style="color:blue">'''[EDIT]''' </span>Après avoir testé plusieurs fois, les résultats n'étaient pas convaincants, ils manquent de précisions.  A force de déplacer les roues du robots. le centre du disque de l'encodeur s'élargie, ce qui implique qu'il ne tourne plus en même temps que la roue. Cela fausse donc les résultats. L'autre pièce est aussi problématique puisqu'elle s'accroche directement sur les suspensions du robot. Ce choix n'était pas judicieux puisque le robot est très peu droit.
 +
Il faut donc réaliser une nouvelle modélisation des pièces.  (cf Modélisation 3)
 +
 +
Voici le rendu :
 +
[[Fichier:P9_encodeur_robot2.png|thumb|Montage 2|center|300px]]
 +
 +
==== Récapitulatif  ====
 +
{| class="wikitable"
 +
| style="width: 20%; background-color:#F1AAA4;" | Modélisation 1
 +
| style="width: 30%; background-color:#FECE7F;" | Modélisation 2
 +
| style="width: 30%; background-color:#BBF1A9;" | Modélisation 3
 +
|-
 +
|
 +
{|
 +
|'''Rallonge axe moteur'''
 +
[[Fichier:P9_encodeur_axe.png|center|200px]]
 +
Le but était de modéliser une pièce qui permettrait d'allonger l'axe du moteur. Ce n'est pas réalisable parce que son diamètre est de 4 mm. Il est impossible d'atteindre une telle précision avec une imprimante 3D.
 +
|}
 +
|
 +
{|
 +
|'''Support encodeur'''
 +
[[Fichier:P9_encodeur_support.png|Support de l'encodeur|left|200px]]
 +
| Ce robot n'a pas été conçu pour avoir des encodeurs. Il n'y a pas de place pour les faire tenir. Sur les suspensions du robot, il y a des trous, je voulais en profiter pour accrocher le support de l'encodeur. 
 +
|-
 +
|'''Disque de l'encodeur'''
 +
[[Fichier:P9_encodeur_roue.png|Roue|center|200px]]
 +
| Roue à la bonne échelle pour qu'elle puisse tenir sur l'axe des coupleurs plutôt que sur l'axe du moteur.J'ai pris leurs dimensions, mais à force, le disque centrale s'élargie et n'est plus stable ce qui engendre des erreurs de distance.
 +
|}
 +
|
 +
{|
 +
|'''Support encodeur v2'''
 +
[[Fichier:P9_encodeur_support2.png|Support de l'encodeur|250px]]
 +
|Pour éviter d'avoir des erreurs de calculs impacté par la mauvaise qualité des suspensions, cette dernière version du support est directement positionnée sur le moteur qui lui reste stable.
 +
|-
 +
|'''Disque de l'encodeur v2'''
 +
[[Fichier:P9_encodeur_roue2.png|Roue|250px]]
 +
|Concernant le disque, j'ai également fais des modifications par rapport à la version 1. J'ai réduit le nombres de trous, parce que j'ai remarqué que lorsque le robot se déplaçait rapidement, il perdait des informations. (surement parce que les trous étaient trop rapprochés et petits) J'ai également rendu possible la fixation des vis directement sur la pièce 3D. Cela permettra de fixer la roue sur le coupleur !
 +
|}
 +
|}
  
 
===Code Arduino===
 
===Code Arduino===
Ligne 1 460 : Ligne 1 515 :
 
Ce code ne prend en compte qu'un encodeur sur deux. L'objectif est de l'adapter pour deux, de faire une moyenne du nombre d'interactions pour minimiser les erreurs. Pour le moment mon code me permet d'avancer d'un mètre et d'arrêter le robot. Après plusieurs essais, mon robot avance réellement d'une valeur comprise entre 80 cm et 120 cm. Cette solution fonctionne mais n'est pas très fiable.  
 
Ce code ne prend en compte qu'un encodeur sur deux. L'objectif est de l'adapter pour deux, de faire une moyenne du nombre d'interactions pour minimiser les erreurs. Pour le moment mon code me permet d'avancer d'un mètre et d'arrêter le robot. Après plusieurs essais, mon robot avance réellement d'une valeur comprise entre 80 cm et 120 cm. Cette solution fonctionne mais n'est pas très fiable.  
  
=== Accéléromètre ===
+
=== Compas ===
  
 
Voici la [https://fr.rs-online.com/web/p/accelerometres/9054665?cm_mmc=FR-PLA-DS3A-_-google-_-CSS_FR_FR_(FR:Whoop!)+Semi-conducteurs_Experiment-_-(FR:Whoop!)+Accéléromètres-_-PRODUCT_GROUP&matchtype=&pla-327734554961&gclid=Cj0KCQiAsbrxBRDpARIsAAnnz_Ms6vS58cvG-dOa8UuoLxLalpMe32ybCVYR5essG6MQOXPDDKyAcsQaAm5YEALw_wcB&gclsrc=aw.ds référence] de l'accéléromètre que je vais utiliser pour ce projet :  '''Adafruit Triple-axis Accelerometer+Magnetometer (Compass) Board - LSM303 [ADA1120]'''
 
Voici la [https://fr.rs-online.com/web/p/accelerometres/9054665?cm_mmc=FR-PLA-DS3A-_-google-_-CSS_FR_FR_(FR:Whoop!)+Semi-conducteurs_Experiment-_-(FR:Whoop!)+Accéléromètres-_-PRODUCT_GROUP&matchtype=&pla-327734554961&gclid=Cj0KCQiAsbrxBRDpARIsAAnnz_Ms6vS58cvG-dOa8UuoLxLalpMe32ybCVYR5essG6MQOXPDDKyAcsQaAm5YEALw_wcB&gclsrc=aw.ds référence] de l'accéléromètre que je vais utiliser pour ce projet :  '''Adafruit Triple-axis Accelerometer+Magnetometer (Compass) Board - LSM303 [ADA1120]'''
Ligne 1 466 : Ligne 1 521 :
 
[[Fichier:P9_accele.png|center|200px]]
 
[[Fichier:P9_accele.png|center|200px]]
  
=== Coral ===
+
J'ai utilisé la datasheet pour en savoir plus sur ce composant. Il permet d'avoir 3 variables de champ magnétique et 3 variables d'accélération. Il utilise le bus I2C pour envoyer les données en liaison série.
  
=== Encodeurs incrémentals ===
+
[[Fichier:P9_accele_info.png|center|300px]]
 +
 
 +
==== Cablâge ====
 +
 
 +
Comme j'utilise déjà les pins SDA et SDL pour la carte extensive qui contrôle les moteurs du robot, je me suis renseignée sur internet pour trouver une solution. Sur ce [https://f-leb.developpez.com/tutoriels/arduino/bus-i2c/ site], j'ai trouvé une information intéressante : il est possible d'avoir à nouveau deux pins SDA et SDL en utilisant les pins A5 et A4.
 +
 
 +
''"Ceux qui disposent de l'Arduino Uno ou d'une carte compatible utiliseront les connecteurs A4 pour SDA (les données) et A5 pour SCL (l'horloge)"''
 +
 
 +
Donc en résumé : 
 +
* VCC : 5V
 +
* GND : GND
 +
* SDA : A4
 +
* SDL : A5
 +
 
 +
 
 +
==== Code Arduino  ====
 +
 
 +
Une fois le câblage réalisé, j'ai récupéré une librairie du composant LSM303DHL sur le logiciel Arduino que j'ai inclus dans le code. J'ai exécuté un exemple qu'Arduino propose, voici le résultat du moniteur série :
 +
 
 +
 
 +
{| class="wikitable"
 +
|[[Fichier:P9_gyro_moniteur.png|center|600px]]
 +
|Je récupère bien les données x, y et z. Mais pour ROS ce n'est pas le format de données qu'il me faut. Il y a une relation mathématique qui me permet de convertir ces trois données en une donnée en degré:
 +
 
 +
'''angle= 180*atan2(magy,magx)/PI'''
 +
|-
 +
|[[Fichier:P9_gyro_moniteur2.png|center|600px]]
 +
|A présent, j'obtiens bien les données en degrés. Les valeurs sont comprises entre 0 et 360°.
 +
|}
  
 
==Semaine 19 : 3 - 9 Février ==
 
==Semaine 19 : 3 - 9 Février ==
 +
 +
=== Liaison ROS et Robot pour commander le robot  ===
 +
 +
'''> Comment est structuré ROS'''
 +
 +
ROS crée un réseau où tous les processus sont connectés. N'importe quel nœud du système peut accéder à ce réseau, interagir avec d'autres nœuds, voir les informations qu'ils envoient et transmettre des données au réseau:
 +
 +
Voici une liste des principaux éléments de ROS  et quelques commandes utiles :
 +
 +
* '''Nodes (Nœuds)''': les nœuds sont des processus où le calcul est effectué. Pour avoir un processus qui peut interagir avec d'autres nœuds, il faut créer un nœud avec ce processus pour le connecter au réseau ROS. Habituellement, un système aura de nombreux nœuds pour contrôler différentes fonctions. Il est préférable d'avoir de nombreux nœuds qui ne fournissent qu'une seule fonctionnalité, plutôt que d'avoir un grand nœud qui fait tout dans le système. Les nœuds sont écrits avec une bibliothèque client ROS, par exemple, roscpp ou rospy.
 +
 +
rosnode info NODE // Affiche des informations sur un noeud
 +
rosnode kill NODE // Arrête un noeud en cours d'exécution
 +
rosnode list // Liste les noeuds actifs
 +
rosnode ping NODE // Teste la connexion avec le noeud
 +
 +
* '''Master (Le maître)''' : le maître fournit l'enregistrement des noms et le service de recherche au reste des nœuds. Il établit également des connexions entre les nœuds.
 +
 +
* '''Messages''' : les nœuds communiquent entre eux par le biais de messages. Un message contient des données qui fournissent des informations à d'autres nœuds. ROS propose de nombreux types de messages, et vous pouvez également développer votre propre type de message à l'aide de types de messages standard.
 +
 +
rosmsg list // Affiche tous les massages
 +
rosmsg package // Affiche les messages d'un package
 +
 +
* '''Topic''' : Chaque message doit avoir un nom pour être acheminé par le réseau ROS. Lorsqu'un nœud envoie des données, le nœud publie un sujet. Les nœuds peuvent recevoir des sujets d'autres nœuds en s'abonnant simplement au sujet. Un nœud peut s'abonner à un sujet même s'il n'y a aucun autre nœud publiant sur ce sujet spécifique.  Il est important que les noms des sujets soient uniques pour éviter les problèmes et la confusion entre les sujets portant le même nom.
 +
 +
rostopic echo /topic // Affiche les messages
 +
rostopic find message_type // Trouver un Topic par son nom
 +
rostopic hz /topic // Affiche la fréquence d'affichage
 +
rostopic info /topic // Affiche des informations sur le topic, le type du message, le publishers et subscribers.
 +
rostopic list // Affiche les topics en cours
 +
rostopic pub /topic type args // Permet de publier une donnée dans un topic
 +
 +
* '''Bag''' : les bags sont un format pour enregistrer et lire les données des messages ROS. Les bags sont un mécanisme important pour stocker des données, telles que les données de capteurs, qui peuvent être difficiles à collecter mais qui sont nécessaires pour développer et tester des algorithmes.
 +
 +
rosbag // Permet d'enregistrer des données
 +
 +
 +
'''> Première version de code ROS'''
 +
 +
<span style="color:blue"> A compléter </span>
 +
 +
=== Déplacement non linéaire du robot ===
 +
 +
Lorsque je commande le robot pour qu'il avance d'un mètre vers l'avant, il a tendance à dévier vers la gauche. Ce qui n'est pas normal, puisque j'impose à chaque roue la même puissance. C'est possible que ça soit un problème du châssis, ou bien que certains coupleurs des moteurs ne soient pas bien visés.
 +
Je pense aussi que le problème pourrait venir de tous les éléments que j'ai rajouté sur le robot (batterie portable, Lidar, Raspberry Pi), leur poids doit avoir un impact sur l'inclinaison du robot ce qui pourrait imposer une force vers le bas d'un coté du robot.
 +
Pour essayer de voir si le problème vient de la dernière supposition, je vais modéliser un étage supplémentaire sur le robot. Cela permettra de mieux équilibrer les poids des équipements sur tout le robot.
 +
 +
'''Modélisations'''
 +
Voici le rendu :
 +
{|
 +
|[[Fichier:P9_etage2.png|thumb|Modélisation de l'étage 2|300px]]
 +
|[[Fichier:P9_copperpilar.png|thumb|Pilier reliant les étages |200px]]
 +
|        [[Fichier:P9_rendu_Etage2.png|300px]]
 +
|}
 +
 +
Ce n'était pas le problème, puisqu'en équilibrant le poids sur le robot, le robot dérive tout de même vers la gauche. Pour corriger ce problème, je vais donc mettre plus de puissance sur les roues de gauche pour essayer de corriger le parcours du robot. Pour avoir un retour du parcours des roues des deux côtés, je place un deuxième encodeur sur la roue du milieu à gauche.
 +
 +
<span style="color:brown"> '''REMARQUE''' :  Le disque de l'encodeur ne peut pas être imprimé avec du PLA blanc, il faut que ca soit une couleur sombre. </span>
 +
 +
{|
 +
|[[Fichier:P9_unSeulEncodeur.png|600px]]
 +
|[[Fichier:P9_deuxEncodeur.png|600px]]
 +
|}
 +
 +
On remarque bien sur la première photo que les roues de droite effectuent plus de tours que celles de gauches, environ une dizaine de plus. En modifiant le code et en augmentant la puissance sur les roues de gauches, on obtient les résultats sur la deuxième photo. <span style="color:green "> Le robot avance droit maintenant.  </span>
 +
 +
=== Problème Boussole ===
 +
La semaine dernière quand j'avais testé la boussole (pas sur le robot), je pensais avoir des résultats assez cohérents. Mais cette semaine en la plaçant sur le robot, je me suis rendue compte que les valeurs étaient pas utilisables. J'ai récupéré les valeurs que j'obtenais en fonction de l'orientation du robot (cf image à droite). J'ai ensuite resté la boussole e, dehors du robot, (cf à gauche), j'obtiens des valeurs plus cohérentes mais ce n'est pas suffisant pour bien contrôler le robot (un quart de cercle représente 180° et les trois autres 180°). Le problème vient des champs magnétiques présents sur le robot qui perturbent la boussole.
 +
 +
[[Fichier:P9_boussole.png|600px]]
 +
 +
=== Ajout de la caméra sur le site  ===
 +
 +
pi@raspberrypi: ~/ sudo apt-get install motion
 +
pi@raspberrypi: ~/mkdir /home/pi/motion
 +
pi@raspberrypi: ~/chmod 755 /home/pi/motion
 +
Il faut ensuite changer le fichier de configuration suivant :
 +
pi@raspberrypi: ~/ sudo vim /etc/motion/motion.conf
 +
      daemon on
 +
      framerate 15
 +
      webcam_motion on
 +
      webcam_localhost off
 +
 +
  pi@raspberrypi: ~/ sudo vim /etc/default/motion // modifier start_motion_daemon=yes
 +
 +
 +
Pour lancer le broadcast :
 +
  pi@raspberrypi: ~/ sudo service motion start
 +
 +
Il suffit d'aller sur le lien suivant : localhost:8080 pour accéder à l'image de la caméra.
 +
 +
=== Publication de la carte sur un site web ===
 +
<span style="color:blue"> A compléter </span>
 +
{|
 +
|[[Fichier:P9_cartographiev1.png|600px]]
 +
|[[Fichier:P9_cartographiev2.png|600px]]
 +
 +
|}
 +
 +
=== Script bash pour lancer toutes les commandes ===
 +
 +
Pour lancer le noyau ROS, le Lidar, Hector, le serveur ROS, il y a 5 commandes a exécuter depuis le terminal. Pour améliorer le lancement, j'ai écris un script bash qui permet de tout lancer à distance. Il regroupe simplement les commandes suivantes :
 +
 +
roscore
 +
roslaunch ydlidar lidar.launch
 +
roslaunch hector_slam slamit.launch
 +
rosrun bridge server
 +
rosrun rosserial
  
 
==Semaine 20 : 10 - 16 Février==
 
==Semaine 20 : 10 - 16 Février==
  
 
==Semaine 21 : 17 - 21 Février (Soutenance 2)==
 
==Semaine 21 : 17 - 21 Février (Soutenance 2)==
 +
 +
 +
<include iframe width="500px" height="500px" src="https://www.youtube.com/watch?v=HFoK1urJyv8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></include>
 +
Lien : https://www.youtube.com/watch?v=HFoK1urJyv8
  
 
=Documentation  =
 
=Documentation  =
 +
 +
Documentation du projet : [[Fichier:Documentation_Projet.pdf]]
 +
 +
Code du projet : https://github.com/lmejbar/Slamit
 +
 
=Documents Rendus=
 
=Documents Rendus=
  
Ligne 1 482 : Ligne 1 682 :
  
 
Soutenance intermédiaire : [[Fichier:P9_RapportSoutenance_MEJBAR.pdf]]
 
Soutenance intermédiaire : [[Fichier:P9_RapportSoutenance_MEJBAR.pdf]]
 +
 +
Rapport final : [[Fichier:P9_Rapport2_MEJBAR.pdf]]
 +
 +
Soutenance finale : [[Fichier:P9_Soutenance2_MEJBAR.pdf]]

Version actuelle datée du 21 février 2020 à 13:56

Sommaire


Présentation générale

Sujet : Véhicule autonome pour cartographie
Etudiant : MEJBAR Lina
Encadrants : Alexandre Boé / Xavier Redon / Thomas Vantroys / Xavier Chenot

Description

Le secteur de l'intelligence artificielle a été beaucoup marqué durant la dernière décennie par une évolution du nombre de recherches et de publications scientifiques. Son but est de concevoir des systèmes capables de reproduire le comportement humain et son raisonnement. S'il est vrai que de domaine parait très abstrait, il possède de nombreux avantages lorsqu'on sait comment l'utiliser. Le retail, l'industrie ou encore la sécurité sont des secteurs qui deviennent de plus en plus performants lorsque l'intelligence artificielle est développée. L'objectif est de permettre une automatisation plus intelligente des tâches. Le machine learning est un champ d'étude de l'intelligence artificielle qui se fonde sur des approches statistiques pour donner aux ordinateurs la capacité d' « apprendre » à partir de données.

Objectifs

La mission sera de proposer un système autonome (véhicule mobile) capable de se déplacer dans une zone et de la cartographier. Le système doit pouvoir ensuite trouver le chemin adéquat pour se déplacer d’un point à un autre tout en évitant des obstacles fixes et/ou mobiles. Ce projet est proposé par l'entreprise Bonduelle. Le but final, serait de placer le robot dans leur entrepôt, et qu'il puisse déplacer une plaquette de produit d'un point A à un point B, tout en évitant les obstacles et en cartographiant la pièce.


Préparation du projet

Cahier des charges

Dans un contexte international de développement de projet innovants, il faudra proposer un système autonome (véhicule mobile) capable de se déplacer dans une zone et de la cartographier. Le système doit pouvoir ensuite trouver le chemin adéquat pour se déplacer d’un point à un autre tout en évitant des obstacles fixes et/ou mobiles.

Choix techniques : matériel et logiciel

Matériel mis à disposition par Polytech :

  • VL6180X Distance Sensor
  • Carte Raspberry Pi 3GB


Matériels mis à disposition par Bonduelle :

  • Carte Raspberry Pi 4GB et carte SD
  • Kit pour déplacement caméra suivant deux axes

Questions

Cartographie :

→Quel taille de pièce vise-t-on ?

→ Combien de temps doit prendre la cartographie ?

→ Sous quel format doit être la cartographie ?


Intelligence Artificielle

→ Quelles formes doit reconnaitre le robot ?

Matériel

→ Proposition d'un capteur ultrason


Robot

→Trouver une solution pour le rendre plus robuste

Liste des tâches à effectuer

Tâche \ Heures S01 S02 S03 S04 S05

S06

S07 S08

S11

S12 S13 S14

Oral 1

S15

S16

S17

S18

S19 S20 S21

Oral 2

Total
Analyse
Analyse du projet 2 2 1 2 7
Recherches concernant le projet 2 2 2 2 8
Montage du robot 2 2
Modélisation et Impression 3D 5 5
Formation - Documentation
Formation Machine Learning 2 4 6
Formation Partie Cartographie (LIDAR) 4 2 3 9
Formation Computer Vision 10 10
Formation ROS 4 10 8 22
Code
Code Arduino 3 3 2 7 3 2 2 19
Code C++ : Lidar 2 4 6
Code du site Web 3 4 4 2 2 2 17
Code Python : Open CV 8 8
Code ROS 5 4 5 4 3 21
Rencontre Tuteurs
Rencontre Bonduelle 1 1 2 2 4 4 4 2 2 2 2 2 2 2 32
Documentation
Remplissage du wiki 2 1 2 3 3 2 3 1 4 3 21
Documentation pour Bonduelle 2 1 3 3 9
Préparation Soutenance 1 3 2 4 9
Préparation Soutenance 2 2 2
Rédaction du rapport (Interm et Final) 4 4 2 4 2 16
Total 11 6 12 12 20 13 43 15 8 12 26 15 15 23 7

Calendrier prévisionnel

P9 calendrier.png

Etat de l'art

  • L’aspirateur robot IRobot I7+ est un aspirateur autonome nouvelle génération. Il se vide automatiquement dans un sac fermé pouvant contenir l’équivalent de 30 bacs. Le robot doit bien entendu récupérer la poussière, éviter les obstacles fixes (meubles, murs ..) et mobiles (personnes, animaux ..). Sa particularité est qu'il peut cartographier la maison après deux-trois passages. Ce robot est commercialisé 1200€.
P9 IROBOT.jpg
  • Un deuxième robot avec les mêmes caractéristiques que mon cahier des charges a vu le jour en 2017. C'est le robot mobile Apollo qui est l’une des meilleures bases mobiles du marché. Il est doté de plusieurs capteurs, un LIDAR, une caméra de profondeur, un capteur ultrason, un capteur de dénivelation et un SLAMWARE. Il a été pensé pour pouvoir évoluer de manière autonome dans des lieux publics et être capable de faire face à de nombreuses situations :
- Contournement des obstacle,
- Automatisation de la planification des trajets,
- Rechargement autonome.


Vidéo du robot

Réalisation du Projet

Semaine 1 : 09-15 Septembre

Je me suis rendue mercredi au sein de l'entreprise Bonduelle à Villeneuve d'Ascq. Pour la réalisation de ce projet, Bonduelle m'a fourni un robot en Kit. J'ai commencé par le monter en suivant les instructions du mode d'emploi.

Photo 1
Robot en montage.
Photo 2
Robot monté.

Une fois le robot monté, j'ai fait des recherches concernant les différents domaines de mon sujet :

* Robot : De nos jours, on trouve de plus en plus de nombreux kits d'assemblage de robots programmables. C'est sur l'un d'entre eux, que je vais travailler dans le cadre de mon projet. Ce kit est commercialisé par la marque Yahboom. Ils vendent de nombreux modèles de robots : Char, Tank, des robots de 2 à des 6 roues. Celui sur lequel je vais travailler dans le cadre de mon projet est le robot Yahboom Professional 6WD UNO R3 smart robot kit compatible with Arduino Ce kit contient : Une caméra avant, 6 roues et 6 moteurs, une carte Arduino et une carte 6WD, une pile rechargeable.

*Carte 6WD :

Photo 3
Carte 6WD.

Yahboom 6WD carte d'extension intelligente pour Uno et Raspberry Pi. La carte d'extension 6WD est conçue avec trois prises d'entraînement de moteur et supporte jusqu'à trois modules d'entraînement de moteur. Chaque module peut conduire deux moteurs. Chaque moteur peut être contrôlé indépendamment, et 6 moteurs peuvent produire 6 vitesses différentes en même temps, réalisation contrôle indépendant. La carte d'extension 6WD prend en charge quatre contrôleurs populaires, y compris Raspberry Pi, Arduino, STM32, 51, etc.

Semaine 2 : 16 - 22 Septembre

Pendant la deuxième semaine de projet, j'ai majoritairement fait des recherches sur internet concernant mon projet. Le projet étant porté sur l’IA (Intelligence artificielle) et notamment les réseaux de neurones artificiels, la formation actuelle en IMA ne donne pas les connaissances nécessaires. Je vais donc passer une bonne partie du temps à me former pour apprendre son fonctionnement.

Recherche Machine Learning

  • Un neurone artificiel peut se décomposer en :
- Des entrées
- Une fonction d’activation
- Une sortie

On appelle potentiel du neurone la somme des entrées pondérées par leurs poids. Si l’on a Xi la i−ème entrée d’un neurone et Wi le poids qui lui est associé, alors le potentiel du neurone est : Fichier:P09 ML1.png Pour calculer la sortie du neurone, on applique au potentiel une fonction qui peut être continue ou non. Il est donc possible de connecter la sortie d’un neurone à l’une des entrées d’un autre neurone, c’est de cette façon que l’on peut former un réseau de neurone.

  • Il existe ensuite plusieurs fonctions d’apprentissage, qui permettent de modifier les poids des connexions entre les neurones. La modification des poids entraîne un changement dans la sortie du réseau de neurone. Il existe différents types d’apprentissage pour les réseaux de neurones :
- Apprentissage supervisé : Plusieurs échantillons, représentant le contexte dans lequel se situe le neurone, sont présentés au réseau de neurones. Chaque échantillon est donné avec une sortie qui devrait être la même que la sortie calculée. Le réseau de neurone va alors se corriger selon la différence entre la sortie donnée et la sortie calculée par le réseau.
- Apprentissage par renforcement : Plusieurs échantillons sont aussi présentés au réseau de neurones. Lorsque la sortie du réseau de neurone n’est pas celle voulue, le réseau est notifié de son erreur, mais aussi de sa réussite en cas de succès. Contrairement à l’apprentissage supervisé, le réseau ne peut pas connaître la valeur de son erreur en comparant sa sortie calculée avec une sortie donnée.
- Apprentissage non supervisé : Lors de cet apprentissage, on présente aussi plusieurs échantillons. Cette fois-ci, le réseau essaye de dégager lui-même des caractéristiques communes à certaines entrées, pour répondre en conséquence. Le réseau établit lui-même différentes classes d’entrées.

Lecture de thèses

Papier 1 : Robot Mapping for Rescue Robot, Chercheurs : Nagesh Adluru, Longin Jan Latecki, Rolf Lakaemper, Raj Madhavan - 2006

Construire une carte intérieure de façon autonome avec une auto localisation du robot dans la carte générée par ce dernier représente en ce moment un vrai point de recherche dans le domaine de la robotique mobile. Faire face à un environnement inconnu ou qui change est un problème qui se nomme SLAM (Simultaneous localization and mapping). C'est le sujet sur lequel porte cette recherche.

  • Définition :
- 'Odométrie ': Technique permettant d'estimer la position d'un véhicule en mouvement grâce à des capteurs embarqués permettant de mesurer le déplacement des roues du robot.
  • Solution proposée par l'article : Faire correspondre les structures en utilisant la similarité des formes. Dans un premier temps, ils utilisent un laser range scanner (LIDAR/ Télémètre laser) qui génère un fichier de points. Par la suite ils font abstraction de la positions des points et utilisent les méthodes des tangentes et des histogrammes angulaires qui permettent de détecter les similitudes entre les scans.


P9 these1.png


Thèse 2 : Application of Simplified Complex-Value Adaptive Neuro-Fuzzy Inference System for Extended Kalman Filter in Robot Localization, Auteur : Nickolas Tsz Hong Lam - 2018

  • Définition :
- Fuzzy Logic (FL) : Logique floue est une logique polyvalente où les valeurs de vérité des variables - au lieu d'être vrai ou faux - sont des réels entre 0 et 1. En ce sens, elle étend la logique booléenne classique avec des valeurs de vérités partielles. Elle consiste à tenir compte de divers facteurs numériques pour aboutir à une décision qui prend en compte plus de facteurs.
- Adaptive neuro-fuzzy inference system (ANFIS) : Le système d'inférence neuro-fuzzy adaptatif ANFIS combine les avantages du réseau neuronal artificiel (ANN) et de la logique floue (FL), constituant ainsi un meilleur système d'approximation des fonctions non linéaires. Il a été proposé que ANFIS coopère avec EKF afin de réduire le déséquilibre entre la covariance théorique et réelle des séquences d’innovation. En fait, la qualité de l’estimation EKF est déterminée par les informations a priori des matrices de covariance de processus (Qk) et de bruit de mesure (Rk).
- Simplified Complex-Value Neural Network (SCVNN) : La valeur complexe simplifiée Neuro-Floue égalise le nombre de couches de règles et de divisions dans la couche d'entrée. L'augmentation du nombre de paramètres d'entrée n'augmentera pas le nombre de règles. Dans l'intervalle, le format QNF simplifié permet à un réseau neuro-flou d'avoir une vitesse de calcul plus rapide avec moins de ressources occupées et un temps d'exécution moins important, indispensables à tout système informatique de grande taille en temps réel.
- Extended Kalman Filter (EKF) : C'est une modélisation basée sur les fonctionnalités utilisant un algorithme de maximum de vraisemblance et une approche statistique pour l'association de données et l'association de données probabilistes. L'approche typique pour simuler la localisation et la cartographie en temps réel est généralement effectuée à l'aide de filtres de Kalman et de la méthode de Monte Carlo séquentielle, dans laquelle le résultat n'est pas satisfaisant en termes de quantité volumineuse d'entrées, de temps de traitement long et de performances médiocres en termes de précision. C'est depuis longtemps un algorithme populaire utilisé dans SLAM, en particulier avec les systèmes non linéaires.
  • Solution proposée par l'article : Nouvelle approche pour la localisation du robot et la cartographie en vue d'améliorer le stockage des données d'entrées.


P9 these2.png

Semaine 3 : 23 - 29 Septembre

Premier pas du robot

Comment roule le robot ? Le robot possède 6 roues reliées à six moteurs à courant continu.

- Un moteur à courant continu fonctionne à l'aide du principe de la PWM (Pulse Width Modulation), c'est de cette façon qu'il est possible d'agir sur la vitesse et le sens de rotation du moteur. Il faut utiliser les pins de sorties digitales de l'Arduino, on les reconnaît car elles ont le symbole " ~ ". On les commande avec une fonction de l'Arduino qui va prendre une valeur entre 0 et 255 :
analogWrite(pin,valeur);

où  pin est le pin PWM choisi et mis en mode OUTPUT, et  valeur est la valeur comprise entre 0 et 255 qui va définir la part de temps pendant laquelle le pin sera à l’état HAUT durant chaque intervalle (255 correspondra à 100% du temps d’intervalle à l’état HAUT)

- Pour permettre le déplacement du robot, il faut lui fournir de l'énergie séparée pour l'Arduino et les moteurs. Ce principe est assez utilisé en électricité. Cela permet de séparer le circuit de commande et le circuit de puissance. Le circuit de commande envoie des informations binaires à une tension qui est celle de l'Arduino (+5 V) et un ampérage souvent faible (de l'ordre de 40 mA). L'alimentation de ce circuit est réalisée par une batterie (pile 9 V). Le circuit de puissance fournit l'énergie nécessaire pour faire fonctionner la charge.

Voici un gif qui montre les premiers déplacements du robot qui selon le code doit avancer reculer puis tourner:

P09-robot.gif

Code

void setup(){
 pinMode(pinMoteur,OUTPUT);
}
void loop(){
 digitalWrite(pinMoteur,HIGH); //le moteur se lance
 delay(1000);
 digitalWrite(pinMoteur,LOW); //le moteur s'arrête
 delay(1000);
}


Orientation du robot

Pour permettre le déplacement du robot, il faut actionner les moteurs. Ci-dessous, une photo du robot:

P9 orientationRobot.jpeg

A côté de chaque de roue, il y a deux numéros.

Prenons par exemple la roue avant droite, on peut lire " 15 - 14 ". Ces numéros représentent les pinMoteur, il y a deux valeurs : la première permet à la roue de tourner dans le sens anti-horaire (15) et la seconde dans le sens horaire (14).

Pour avancer, il faudra donc donner ces valeurs aux moteurs:

   //Arrière
   pwm.setPWM(8, 0, Speed); 
   pwm.setPWM(9, 0, 0);
   pwm.setPWM(11, 0, Speed);
   pwm.setPWM(10, 0, 0);
   //Milieu
   pwm.setPWM(0, 0, Speed);
   pwm.setPWM(1, 0, 0);
   pwm.setPWM(3, 0, Speed);
   pwm.setPWM(2, 0, 0);
   //Avant
   pwm.setPWM(12, 0, Speed);
   pwm.setPWM(13, 0, 0);
   pwm.setPWM(15, 0, Speed);
   pwm.setPWM(14, 0, 0);

Pour tourner à gauche, il faudra donc donner ces valeurs aux moteurs:

   //Arrière
   pwm.setPWM(8, 0, Speed);
   pwm.setPWM(9, 0, 0);
   pwm.setPWM(11, 0, 0);
   pwm.setPWM(10, 0, Speed);
   //Milieu
   pwm.setPWM(0, 0, Speed);
   pwm.setPWM(1, 0, 0);
   pwm.setPWM(3, 0, 0);
   pwm.setPWM(2, 0, Speed);
   /Avant
   pwm.setPWM(12, 0, Speed);
   pwm.setPWM(13, 0, 0);
   pwm.setPWM(15, 0, 0);
   pwm.setPWM(14, 0, Speed);


J'ai donc une dizaine de fonctions qui me permettent de déplacer le robot.

Rendez-vous Bonduelle

Nous avons décidé de remplacer l'Arduino par un ESP32, un microprocesseur qui possède deux coeurs, ce qui lui permet de gérer simultanément deux tâches à la fois. Nous avons défini les objectifs pour les prochaines semaines et pour la première soutenance. Dans un premier temps, il faudra que je me concentre sur la réception et le traitement des données du LIDAR.


Lidar

L'entreprise propose un logiciel avec le LIDAR X4 qui fonctionne uniquement sous Windows. Ce logiciel permet de visualiser les résultats du scan réalisé avec le LIDAR, avec une visualisation en 2D/3D des points. N'ayant pas de machine sous windows, il fallait trouver une autre façon de traiter les données.

Une autre méthode est de télécharger l'outil ROS (Robot Operating System) Kinetic. Cependant cet outil n'est pas opérationnel sous Debian, il est en cours d'expérimentation : le seul OS supporté est Ubuntu.

Un code est fourni avec le LIDAR et peut être téléchargé ici , voici les données récupérées :

Photo 5
Exécution du fichier ./ylidar_test

On peut voir qu'un tour complet du LIDAR permet d'obtenir 656 valeurs de distances. Il y a donc en moyenne 1.8 mesures de distance par degrés.

Semaine 4 : 30 Septembre - 6 Octobre

Code Lidar

Cette semaine, l'objectif est de récupérer les données du LIDAR. J'ai modifié le fichier fourni par Ylidar afin d'afficher les valeurs de distances et d'angles. Ce fichier est codé en C++. C'est un langage que je ne connais pas encore, mais il s'apparente beaucoup au C, de nombreux sites tels que Stack Overflow me permettront de répondre à d'éventuelles questions.

Voici une partie du code qui permet de faire un Scan 360°, de l'arrêter et d'afficher les valeurs dans le terminal.

   LaserScan scan;
   if(laser.doProcessSimple(scan, hardError )){
       for(int i =0; i < scan.ranges.size(); i++ ){
            float angle = (scan.config.min_angle + i*scan.config.ang_increment)*180/3.14;
            float dis = scan.ranges[I];
            ydlidar::console.message("\tAngle:  %.2f ° et  Distance %.2f m ", angle,distance);
       }
       ydlidar::console.message("Scan received[%llu]: %u rangess",scan.self_time_stamp, (unsigned int)scan.ranges.size());
    } else {
       ydlidar::console.warning("Failed to get Lidar Data");
   }


Récupération des données

Voici ce que je récupère:

Photo 6 Affichage des valeurs : distance et angle


J'ai ensuite modifié le code pour créer un fichier .txt et le remplir ligne par ligne avec les valeurs. Voici comment se présente une ligne: angle_value/distance_value.


Traitement des points

L'affichage du traitement des points s'effectuera sur une interface web. Le but de cette partie est de placer un point en fonction de son angle et de sa distance à partir de l'origine O placé au centre de la page web.

Voici la fonction qui s'en charge. En paramètre, les deux valeurs envoyées par le LIDAR. Pour placer le point, il faut déterminer son abscisse et son ordonnée. Pour les obtenir, on doit d'abord se placer au centre de la page web, définir l'échelle (équivalence entre les pixels de la page et la distance réelle en mètre) et utiliser les formules de trigonométrie : SOH CAH TOA.

Positionnement d'un point
P9 repère.png
function placerPoint(angle,distance){
 ctx.beginPath(); 
 var abscisse = centre_page_x + échelle * Math.cos(-angle*Math.PI/180) * distance;
 var ordonnée = centre_page_y + échelle * Math.sin(-angle*Math.PI/180) * distance;
 ctx.beginPath();
 ctx.arc(x, y, point_size, 0, 2 * Math.PI);
 ctx.fill();
}


Affichage de la cartographie

Je suis en train de me former pour coder un site en HTML et Javascript.

Une fois les données envoyées par le Lidar, il faut les afficher sur l'interface web., voici une première version de la cartographie :


Conditions de la capture des données :

  • Le Lidar était positionné en hauteur afin d'éviter d'avoir des distances faussées par les différents objets de la salle (Ordinateurs, tours..)
  • Position exacte Zabeth05 (représenté par le point O sur la cartographie)

Cette première version a été réalisée à Polytech dans la salle E306. On reconnaît bien la disposition de la salle mais si ce n'est pas le cas voici ci-dessous l'image légendée pour se mieux visualiser la pièce.


P9cartographieE306.png
P9cartographieE306lengendee.png


Analyse de cette première version :

  • Les angles morts ne sont pas traités (exemple : les armoires)
  • Les fenêtres sont mal représentées car il n'y a pas de réflexion du laser.

Raspberry Pi

En fin de semaine, j'ai travaillé à Bonduelle. J'ai choisi de télécharger l'image Ubuntu Mate 18.05 parce que ROS n'est disponible que sous Ubuntu. J'ai installé tous les packages nécessaires.

J'ai ensuite récupéré les fichiers de code et réussi à les exécuter sur la Raspberry Pi. J'ai cartographie le bureau où je travaille à Bonduelle et en l'analysant je peux confirmer les premières observations faites dans la salle E306 : le Lidar a du mal à cartographier les vitres. Voici le résultat non commenté et commenté pour avoir une idée précise de la pièce.

Voici le résultat :

P9CarthographieBonduelle.png P9CarthographieBonduelleAnalyse.png

Semaines 5 et 6 : 7-20 Octobre

L'objectif de ces deux prochaines semaines est :

  • de pouvoir lancer la cartographie à distance : sur un autre ordinateur connecté sur le même réseau
  • d'améliorer le fonctionnement précédent : changer le format du fichier où l'on enregistre les données TXT -> JSON
  • Premier croquis du site final


ROS

L'installation de ROS n'est finalement pas supportée sur UBUNTU Mate l'OS de la Raspberry pi. Ce n'est pas grave parce que l'objectif n'est pas de récupérer un outil qui fait déjà la cartographie, mais plutôt que je le réalise seule. Cependant voici ce que cet outil informatique est capable de faire :

Rendu de la cartographie en utilisant l'outils ROS


[Lien] de la photo. Cartographer est un système qui assure la localisation et la cartographie simultanées (SLAM) en 2D et 3D sur plusieurs plates-formes et configurations de capteurs. Le rendu est assez impressionnant.

Lancer l'acquisition à distance

Voici le code qui me permet de lancer l'acquisition à distance (depuis mon ordinateur connecté sur le même réseau que la Raspberry Pi) :

  • Fichier HTML
       <body>
            <form action="lidar.php" method="post">
                <input type="submit" name="lidar" value="Start"/>
            </form>
        </body>
  • Fichier PHP
<?php
define('LIDAR_ON','/home/lmejbar/Lidar/ydlidar-master/build/samples/ydlidar_test');
 if(array_key_exists('lidar',$_REQUEST)){
    $lidar=$_REQUEST['lidar'];
    if($lidar=="Lancer l'aquisition") exec(LIDAR_ON);
    header('Location: http://192.168.0.20/serial.html');
    exit();
 }
?>

Voici le fonctionnement :

P9 Foncitonnement acquisitionadistance.png


Changement de fichier

Une fois que j'ai compris comment enregistrer des données (en TXT), les interpréter et dessiner les points sur un site, il est préférable maintenant de passer au format de données JSON. En effet cela me permettra de récupérer les données facilement et les enregistrer dans une structure automatiquement avec les méthodes prévues en JS. C'est assez simple d'utiliser JSON en C++. Il faut d'abord réfléchir à la structure de ses données, j'aimerais avoir la suivante:

{
   "0" : {
     "angle" : "-180",
     "distance" : "2.08"
    },
    "1" : {
     "angle" : "-179",
     "distance" : "2.0"
    },
   ...
}

Voici le code pour avoir cette structure :

#include <jsoncpp/json/json.h>
file.open("lidar.json");
Json::StyledWriter styledWriter;
Json::Value lidarValues;
// boucle pour chaque valeur reçues
  lidarValues[to_string(i)]["angle"]=to_string(angle);
  lidarValues[to_string(i)]["distance"]=to_string(dis);

Pour compiler le fichier main.cpp, il faut ajouter la librairie 'ljsoncpp' dans la commande :

g++ -o profile profile.cpp -ljsoncpp

Cependant dans mon projet, j'ai un premier Makefile qui en lance un second qui en lance un Cmake, chaque fichier contient 400 lignes. Je n'avais pas vraiment la main pour ajouter cette librairie. J'ai fait pas mal de recherches et j'ai étudié tous les fichiers sans trouver comment ajouter cette librairie. J'ai alors repris le [git] du LIDAR et j'ai tout repris depuis le départ. Lorsqu'on clone le projet, il faut créer le dossier build, et lancer cette commande lorsqu'on se situe dans le dossier.

cmake ../sdk 

Le fichier CMakeLists.txt était le seul que je n'avais pas étudié. C'est donc dans ce fichier qu'il faut rajouter la ligne:

target_link_libraries(ydlidar_driver ljsoncpp)

Croquis du site final

Voici un croquis du site que je souhaite réaliser :

Croquis du site

Depuis la création du projet, j'ai choisi le nom du robot: SLAMiT. SLAM (simultaneous localization and mapping) fait référence à la cartographie et la localisation simultanée.

L'objectif serait d'avoir une interface simple dans un premier temps, qui permettrait de :

  • Voir le retour caméra du robot
  • Utiliser des flèches pour déplacer le robot à distance
  • La cartographie de la pièce ou se trouve le robot
  • Quelques boutons

Exemple d'utilisation :

L'utilisateur se rend sur la page du projet, il se connecte au robot avec le bouton n°1. Il voit alors le retour de la caméra du robot, et il peut aussi déplacer le robot. Lorsqu'il appuie sur lancer l'acquisition, il doit avoir la cartographie avec le point O qui représente la localisation du robot dans sa cartographie. Le but serait de superposer les cartographies pour avoir un rendu précis et régler le problème des angles morts.

Semaine 7 : 21 Octobre - 3 Novembre

Les objectifs de la 7ème semaine et la semaine de la Toussaint sont les suivants :

  • Réaliser une première version du site en suivant l'idée du croquis
  • Lancer l'acquisition de la cartographie à distance
  • Déplacer le robot à distance


Réalisation du site

Voici une image du site:

P9 Site.png

Dans un premier temps, je ne m'intéresse pas au design du site. Je préfère travailler le fond avant la forme.

  • Concernant la Raspberry Pi :
Eteindre :
Etant donné que je travaille maintenant à distance avec la RP, je dois l'arrêter proprement en exécutant la commande suivante 'sudo shutdown now '


  • Concernant le robot, il y a 5 boutons:
Se connecter et se déconnecter:
Le titre de la page permet d'avoir des informations sur cette connexion. S'il est vert, la connexion est établie, sinon il faut cliquer sur le bouton 'se connecter'. Si l'on souhaite se déconnecter au robot, il faut cliquer sur le bouton 'se déconnecter', le titre devient alors rouge.
Déplacement du robot :
Il y a trois boutons pour le moment 'Avancer', 'Reculer', 'Tourner'. Ils permettront de commander le robot à distance.


  • Concernant la cartographie :
Lancer l'acquisition :
Le fonctionnement de ce bouton est expliqué dans la partie suivante [4.5.2].

Cartographie

Le lancement de la cartographie la semaine dernière fonctionnait, mais il affichait le résultat de l'exécution du fichier PHP. Il faudrait maintenant que lorsque l'utilisateur appuie sur le bouton 'Lancer l'acquisition' :

  • En arrière-plan : exécution du fichier .php qui permet au LIDAR de se lancer et d'enregistrer les données dans un fichier JSON
  • Cette exécution en arrière-plan doit permettre à l'utilisateur de rester sur la page du site
  • Lorsque le fichier est prêt le site affiche automatiquement la nouvelle cartographie.

Déplacement du robot

Pour permettre la communication entre le robot (Arduino) et la Raspberry Pi, je vais utiliser des Websockets. C'est un protocole qui permet de développer un canal de communication sur un socket TCP. Il permet donc d’ouvrir une connexion permanente entre le navigateur et le serveur.

  • Code HTML
var localhost = '127.0.0.1:9000'; 
var ip_raspberry = '192.168.0.20:9000';
var websocket=new WebSocket('ws://'+ip_raspberry,'serial');
websocket.onopen=function(){ $('h1').css('color','green'); };
websocket.onerror=function(){ $('h1').css('color','red'); };

Les deux dernières lignes permettent d'informer l'utilisateur de la page

- Si le titre est vert => la connexion entre le navigateur et la Raspberry Pi est fonctionnelle
- Le titre sera rouge dans le cas contraire.
  • Code C

J'ai eu l'occasion de travailler sur les websockets pour mon projet d'IMA3. J'ai récupéré le fichier C

Cf Git

Semaine 8 - 11 : Mois Novembre

Deuxième site : Distance

J'ai réalisé un deuxième site sur le serveur de la Raspberry Pi. Le but de ce site est de récupérer les valeurs des distances dans le canvas.

Fichier:P9 siteN2 distance.png

Ce deuxième permet à l'utilisateur d'avoir plus d'interactions avec la cartographie réalisée. En effet, lorsque l'utilisateur déplace la souris sur la cartographie, il obtient directement la valeur de la distance entre le pointeur et la position du robot. Pour ce faire, je garde le premier canvas où la cartographie est affichée, et j'en rajoute un second ou j'affiche du texte :

<canvas id="myCanvas2" width="200" height="30" style="border:1px solid #d3d3d3;"></canvas>

J'obtiens la position du curseur avec la fonction suivante :

function getMousePos(canvas, evt) {
       var rect = canvas.getBoundingClientRect();
       return {
         x: evt.clientX - rect.left,
         y: evt.clientY - rect.top
       };
    }

Le message que j'affiche est obtenu de cette façon :

   var mousePos = getMousePos(c, evt);
   var xA = (mousePos.x - 400)/40;
   var yA = (mousePos.y - 400)/40;
   var distanceA = 'Distance : ' + Math.sqrt(Math.pow(xA,2)+Math.pow(yA,2));


Je réduis la position x et y de 400 pour avoir la valeur en fonction de l'origine (position du robot). Je divise par 40 parce que mon canvas mesure 800 px et permet de représenter 10m de chaque côté donc 800/20=40.

Open CV

Comme l'affichage des données est fonctionnel sur le site, j'ai cherché une façon d'exploiter mes résultats. J'ai choisi de creuser du côté du langage python parce qu'il possède une bibliothèque graphique libre pour le traitement d'images en temps réel. N'ayant pas de connaissance dans le domaine de Computer Vision, je me suis dans un premier temps fortement inspiré des codes déjà rédigés sur le site suivant :

Affichage de la cartographie

J'ai codé un premier code, qui permet de récupérer un fichier JSON et de placer les points en fonction de l'angle et de la distance. J'ai gardé le même principe que l'affichage des données en Javascript.

P9 printcanvas.png

Sur cette photo, j'ai mis en parallèle une première cartographie, puis une seconde après le déplacement du robot dans la pièce. Le but est de pouvoir par la suite comparer deux versions afin de les superposer.

Extrait du code

J'utilise ces bibliothèques :
import math // Pour avoir les fonctions Cos et Sin 
import matplotlib.pyplot as plt //Pour créer le graphique (=Cartographie)
import json // Pour récupérer le fichier JSON
from dataclasses import dataclass // Pour créer une classe Point 
Voila la classe que j'ai codé qui me permet de définir un point avec les deux valeurs récupérées dans le fichier JSON
@dataclass
class P_Point:
   distance: float
   angle: float
Fonction qui permet de récupérer les données du fichier et de les ajouter dans un tableau de P_Point
def get_data(file, tb):
   with open(file) as json_file:
       data = json.load(json_file)
       for value in data:
           if (data[value]['distance'] != "0.000000" ): 
               p= P_Point(float (data[value]['distance']),float (data[value]['angle']))
               tb.append(p)
Fonction qui permet d'afficher les points dans le graphique
def plot_point(angle, length):
    endy = length * math.sin(math.radians(angle))
    endx = length * math.cos(math.radians(angle))
    ax = plt.subplot(111)
    ax.plot([0, endx], [0, endy]')
    plt.plot(0,0, color ="red",  marker='.', linestyle='dashed', linewidth=5, markersize=10)
    

J'arrive aux mêmes résultats que l'affichage sur le site du projet, il faut maintenant exploiter la cartographie.


Tracé des murs

Pour tracer les murs, j'ai pensé à :

  • Reconnaître la forme de la pièce,
  • Relier les points récupérés par le LIDAR,

Mais le premier affichage de la cartographie ne donne rien. Python ne reconnaît pas les formes parce qu'il y a des endroits sans informations.

Comparaison des résultats
Résultats Commentaires
P9 relierPoints.png
Comme on peut voir sur l'image, les informations ne sont pas exploitables. J'ai donc changé la façon d'afficher les données en copiant l'affichage de ROS. Le principe serait de mettre un fond gris, et de tracer des lignes blanche entre la position du robot et l'obstacle détecté, voici le rendu du deuxième affichage ci-dessous.
P9 printcanvasGray.png
Comme on peut voir, les endroits sans informations sont parfois complétés, et j'arrive maintenant à reconnaitre les formes afin de tracer les contours de la pièce.
P9 printcanvasShape.png
Il reste à lisser les contours pour corriger les éventuelles erreur du LIDAR. J'ai trouvé une thèse intéressante à ce sujet sur le site suivant, voici quelques résultats de leurs recherches sur la photo ci-dessous.
P9 TheseContour.png
Je trouve le résultat assez impressionnant, je pourrai reprendre l'idée pour combler le manque d'information et lisser les contours des pièces.

Recherche de similitudes

Afin de pouvoir superposer deux cartographies, je dois trouver des keypoints communs aux deux photos. Pour y arriver je vais utiliser un outil disponible en Python qui est SURF, SIRF, et OBO. Pour comprendre comment fonctionne SURF j'ai regardé deux tutoriels sur YouTube  :

Le code de cette partie se trouve ici : Lien.


Comment fonctionne-t-il ?

...


Voici le résultat de mes tests/ recherches:

J'ai remarqué que plus il y a d'informations sur les deux photos, plus python détecte des keypoints. Le principe de SURF est de comparer les keypoints des deux photos et regarder s'il y a des similitudes. Donc si les photos représentent seulement un rectangle par exemple, SURF a du mal à traiter les photos.


Comparaison des résultats
Résultats Commentaires
P9 SURF1.png
Comme on peut voir sur cette image, Python n'a détecté que 2 Keypoints et ils sont faux !
P9 SURF2.png
J'ai essayé avec deux cartographies, on peut voir que le résultat s'améliore puisqu'il y a beaucoup plus d'informations. Cependant il reste quelques erreurs (1 seule: ligne violette).
P9 SURF3.png
J'ai créé une forme avec beaucoup d'informations pour voir le résultat, on remarque à nouveau beaucoup de keypoints détectés et une fois de plus il n'y en a qu'un seul qui est faux.
P9 SURF4.png
J'ai pivoté une des deux photos, le résultat est impressionnant! Malgré la rotation, il y a toujours autant de keypoints et ils sont presque tous justes !

ROS

Comme dit précédemment, son installation est impossible sur la RP3 qui n'a que 1GB de RAM. Bonduelle m'a fourni la RP4 (4GB), j'ai donc réessayé l'installation de ROS. Après de longues heures d'installations et de nombreux problèmes qu'il fallait régler. L'installation fonctionne et ROS démarre !! Comme je l'ai fait sur trois RP, les problèmes sont récurrents, et aucun tutoriel sur internet n'est complet. Je vais donc en rédiger un, il servira à Bonduelle et aux autres étudiants qui travailleront sur RP4 avec ROS.

Fichier:P9 ROS Instructions.pdf

Configuration de ROS :

- Version : Mélodic

- Packages :

Pour lancer la cartographie, il faut ouvrir trois onglets du terminal avec les commandes suivantes :

$ roscore C’est le noeud qui prépare le système ROS, il accepte les commandes. ROS est un système centralisé, un nœud maître (roscore) est toujours nécessaire pour les autres nœuds et doit être exécuté avant les autres nœuds. Roscore démarre ce noeud principal.
$ roslaunch ylidar lidar.launch Permet de démarrer l'acquisition des données du Lidar
$ roslaunch hector_slam_launch tutorial.launch Permet de démarrer Rviz et affiche les résultats du LIDAR en temps réel.

Voici le résultat chez moi :

Sans Hector : ROS + YLIDAR
P9 ROS HECTOR1.png
On peut voir que cette version s'apparente beaucoup à ce que j'obtiens sur le site du projet. ROS positionne seulement les points en fonction de l'angle et de la distance.. La seule différente non négligeable avec mon programme c'est le programme tourne en temps réel, donc les points s'actualisent si l'environnement a changé.
Avec Hector  : ROS + YLIDAR + HECTOR
P9 ROS HECTOR2.png
Dans cette version, ROS trace les murs en noir et trace le chemin parcouru en vert. Les deux lignes verte et rouge permettent de situer le robot dans la pièce. Cette version s'actualise en Temps réel aussi.

Semaines 12 & 13 : 2 - 15 Décembre

Point avec Bonduelle : Modification

J'ai présenté mon avancement à Xavier CHENOT et Erwan NIQUET. Ils sont satisfaits de mon avancement, mais ils préfèrent que je me concentre seulement sur ROS. Ils pensent que c'est exactement l'outil qu'ils envisagent pour ce projet. Donc il faut que je trouve un outil qui permet d'enregistrer les données de la cartographie en temps réel, afin de les afficher sur le site du projet.

A la place de la recherche sur OpenCv, Erwan propose d'ajouter en fonction de l'avancement de mon projet en mi-janvier/ février du machine Learning. Pour le permettre sur la RP4, ils ont acheté Coral c'est un USB Accelerator.

P9 Coral.jpg

ROS

Etant donné que je vais totalement me concentrer sur ROS pour la suite du projet, j'ai choisi de me former à l'aide du site Udemy. Voici le lien du cours que je suis : https://www.udemy.com/course/ros-essentials/

P9 ROS UDEMY.png


J'ai également regardé les trois vidéos suivantes : 1, 2 et 3. Ce sont des cours concernant ROS et plus précisément Hector SLAM, elles ont été réalisées par Paritosh Kelkar un diplômé de l'université de Pennsylvanie.


Ces cours m'ont vraiment aidés à mieux comprendre comment fonctionne ROS, ce qui m'a permis d'avancer dans mon projet.


Enregistrer des données de cartographie

rosbag record -a 

Cette commande permet d'enregistrer toutes les données en cours dans un format '.bag'. Il faut maintenant savoir comment les extraire pour les analyser.


ROS Serial

Grâce aux cours que j'ai suivi, j'ai appris qu'il était possible de dialoguer en communication série entre Arduino et ROS.

cd <ws>/src
git clone https://github.com/ros-drivers/rosserial.git
cd ..
catkin_make

<ws> est le répertoire catkin_ws.

Il faut ensuite se déplacer dans le dossier des libraires d'Arduino, pour ajouter la librairie de ROS:

cd <sketchbook>/libraries
rm -rf ros_lib
rosrun rosserial_arduino make_libraries.py .

Une fois la libraire ajoutée, il faut ouvrir Arduino, 'File', 'Example' et choisir dans un exemple depuis ros_lib.

Hello World

Dans un premier temps, j'ai choisi l'exemple 'Hello World' que j'ai téléversé. J'ai ouvert trois terminaux sur le bureau de la RP4 avec les commandes suivantes:

Terminal 1 Terminal 2
roscore rosrun roserial-python serial_node.py /dev/ttyUSB1

Permet d'établir la communication série avec le port ttyUSB1 => Arduino

Terminal 3
rostopic echo /chatter

Affiche les messages envoyés par l'arduino (≃ minicom)

Dans ce premier exemple, l'Arduino joue le rôle de Publisher et ROS le rôle de Subscriber. Le second exemple que j'ai suivi est 'Blink', son code permet de changer l'état d'un LED lorsque l'Arduino reçoit un message vide.

Blink Led
Terminal 1 Terminal 2
roscore rosrun roserial-python serial_node.py /dev/ttyUSB1

Permet d'établir la communication série avec le port ttyUSB1 => Arduino

Terminal 3
rostopic pug toggle_led std_msgs/Empty --once

Permet d'envoyer un message vide sur la liaison série .

Dans ce second exemple, l'Arduino joue le rôle de Subscriber et ROS le rôle de Publisher, parce que c'est un des noeuds de ROS qui envoie un message et c'est Arduino qui attend/écoute. Dans cet exemple, l'arudino attend un message vide. J'ai modifié le code pour qu'il attende la valeur 2. La commande du terminal 3 devient alors :

rostopic pug toggle_led std_msgs/Int32 2 --once
Déplacement du robot

Le robot est commandé à distance depuis le site du projet, pour y arriver j'utilise des websockets qui envoient des données en liaison série à l'arduino. Lorsqu'il reçoit '1' le robot avance, lorsqu'il reçoit '2', le robot recule et il tourne lorsqu'il reçoit '3'. J'ai donc continué la modification du code Blink pour faire apparaitre ces conditions. J'arrive à présent à déplacer mon Robot en utilisant ROS.

Semaine 14 : 16 - 22 Décembre (Soutenance 1)

Voici un aperçu de l'avancement de mon projet en vidéo:

Dans un premier temps, je lance ROS :

  • roscore
  • roslaunch ydlidar lidar.launch
  • roslaunch hector_slam tutorial.launch

Ensuite, je permets la communication entre mon site => la Raspberry Pi => et le Robot (Arduino) à l'aide des websockets.

On peut enfin voir, que lorsque le robot se déplace la cartographie se construit en même temps.

Semaines 15 - 16  : 6 - 19 janvier

Problème de shift

La vidéo de la rubrique précédente montre que la cartographie fonctionne très bien lorsque qu'il avance en ligne droite. J'ai fait d'autres tests lorsque le robot tourne, voici le résultat :

P9 navigation shift.gif

Ce problème est dû à des mouvements brusques du robot, lorsqu'il va trop vite : le phénomène de shift. Cela arrive souvent lorsque le robot tourne de façon trop rapide. Mais c'est également dû au fait que le robot n'est pas "solide". Lorsqu'il avance avec une vitesse importante et qu'il freine subitement, la plateforme sur laquelle est fixée le Lidar ne reste pas parallèle au sol. C'est à cause des suspensions de mauvaise qualité, le lidar qui est placé sur le robot change de position et ne reste pas vraiment horizontal.

Autre piste de recherche : Exploitation des données du fichier .bag

Lorsque je cherchais à enregistrer les données de la cartographie, j'ai réussi à enregistrer un fichier sous le format .bag. En faisant des recherches sur internet, j'ai trouvé un script qui permet de convertir un fichier . bag en un fichier .csv. Je garde en tête cette piste, si je n'arrive pas déporté Vos sur mon site Web.

Contrôle à distance de la Rapberrypi

Depuis le début de ce projet, j'utilise le desktop de la RP plutôt que son contrôle uniquement par ligne de commandes en shh. L'un des inconvénients majeurs était que je devais toujours avoir un câble HMDI qui reliait le robot à un écran, mais ce n'est pas pratique lorsqu'on souhaite que le robot se déplace dans une pièce.

Screen Recorder

Dans un premier temps, j'ai travaillé en effectuant des enregistrements vidéos de mon écran avec l'application lorsqu'il n'était pas branché à mon écran. Ca fonctionne mais ce n'est pas pratique.

sudo apt-get update
sudo apt-get install simplescreenrecorder

Export d'affichage par SSH

Ensuite, j'ai fait des recherches pour pouvoir me connecter en ssh à la RP mais avec l'affichage graphique.

ssh -X -Y pi@172.20.10.2

J'ai réussi à afficher mon bureau à distance sur mon ordinateur, mais lorsque je lançais ROS (RViz), j'avais un message d'erreur qui m'indiquait une incompatibilité du à un problème interne. Donc cette solution n'est pas envisageable.

Serveur VNC

Enfin, j'ai essayé une dernière solution : screen sharing depuis un Mac. Sur la RP :

 Menu > Raspberry Pi Configuration > Activer VNC 

Une fois que la raspberry a redémarré, une icône VNC est visible sur la barre du menu. Il faut la sélectionner : Sur RP - VNC :

 Menu > Options > Sécurité 
  Encryption = Prefer off
  Authentication = VNC Password

Enfin il faut choisir un mot de passe.

Sur le Mac  : Il faut ouvrir l'application Partage d'écran et entrer l'adresse IP de la RP et le mot de passe qui a été crée dans les paramètres de VNC.

‎⁨Macintosh HD⁩ ▸ ⁨Système⁩ ▸ ⁨Bibliothèque⁩ ▸ ⁨CoreServices⁩ ▸ ⁨Applications⁩ ▸ Partage d'écran (en Français ou Screen Sharing en Anglais).
P9 DesktopRPI.png

J'ai donc réussi avec cette dernière option à contrôler à distance la RP et à lancer ROS depuis mon mac !

Formation ROS Turtlebot

TurtleBot est un kit de robot avec un logiciel open source. TurtleBot a été créé au Willow Garage par Melonee Wise et Tully Foote en novembre 2010. Ce robot est capable de circuler dans votre maison, de voir en 3D la carte de la pièce. L'objectif de cette formation est de pouvoir rendre mon robot compatible avec ROS et donc de pouvoir le contrôler directement depuis la plateforme de ROS. Voici la page où l'on trouve le dossier nécessaire. Pour les personnes n'ayant pas le robot Turtlebot, il existe une simulation. Dans un premier temps, il faut lancer :

$ roscore
$ rosrun turtlesim turtlesim_node 
P9 turttlesim.png

L'objectif à présent est de déplacer cette tortue dans son environnement. Il est tout à fait possible de la déplacer en utilisant les touches ( up, down, right et left) avec la commande ci dessous :

$ rosrun turtlesim turtle_teleop_key 

Mais cela ne fonctionnera pas avec un vrai robot par la suite. Pour que ROS puisse contrôler un robot il envoie les coordonnées dans un message de type Twist :

geometry_msgs/Vector3 linear
geometry_msgs/Vector3 angular

C'est un message qui se compose de deux vecteurs, un vecteur linéaire avec trois valeurs et un second vecteur pour trois valeurs angulaires.

Il existe des limitations en ce qui concerne le robot:

  • La navigation n'est possible que pour des robots "diferential drive" et des robots holonomiques. => J'ai donc besoin de rajouter des encodeurs à mes roues motrices.
  • Le robot reçoit un message de type twist avec des vitesses X, Y et Thêta qui lui permettent de se déplacer. Si le robot n'est pas en mesure de les comprendre, il est possible créer un nœud ROS qui convertit un message vers un autre de type twist. => Il faut comprendre le fonctionnement des ces vecteurs sur le déplacement du robot.


Je vais donc opter pour le déplacement avec les deux vecteurs. J'ai fait des recherches concernant 'diferential drive' et je suis tombée sur ce site qui explique bien le fonctionnement mathématique avec un corrigé d'un exercice. J'ai remarqué que seules deux valeurs sur les 6 sont utiles. X permet d'avancer d'une distance et θz permet de tourner de l'angle indiqué.

Vecteur Linéaire Vecteur angulaire

x

y

z

θx

θy

θz

Pour déplacer la tortue avec ce type de message :

$ rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -1 -- '[ Xvalue, Yvalue, Zvalue  ]' '[θXvalue, θYvalue, θZvalue  ]'

Par exemple pour que le robot puisse se déplacer en réalisant la forme d'un carré, voici les commandes à réaliser quatre fois :

 $ rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -1 -- '[2,0,0]' '[0,0,0]' // Le robot avance de 2
 $ rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -1 -- '[0,0,0]' '[0,0,1.57]' // Le robot tourne sur lui même de Pi/2 (=1.57)
P9 turtle1 carre.png

Contrôle du robot avec ROS

J'aimerai pour la suite du projet, pouvoir contrôler le robot avec l'interface graphique de ROS. Pour le moment je contrôle les moteurs du robot avec en imposant une valeur entre 0 et 255 à chaque roue. (Pour rappel, le robot est équipé de 6 moteurs à courant continu).

Avec ROS, la seule façon de contrôler un robot c'est en lui imposant les valeurs de vitesse x, y et θ. Mais je n'ai pas d'encodeur sur mes moteurs. Pour le moment, il faut que je trouve une solution pour contrôler mon robot en vitesse.

Semaines 17 et 18  : 20 Janvier - 2 février

Comme dit précédemment, pour contrôler mon robot, j'ai besoin d'encodeurs sur les roues motrices. J'ai récupéré celui ci de chez Monsieur Redon.

Encodeurs incrémentals

La plupart des encodeurs pour robots mobiles utilisent des capteurs optiques. L’idée est de placer un disque alternant des zones transparentes (ou tout simplement des trous) et opaques (le disque lui-même) entre un capteur de lumière et un émetteur de lumière. Ce dernier se place sur l'axe de rotation de la roue. La fréquence d’apparition des zones blanches et noires devant le capteur de lumière va indiquer la vitesse de rotation. Le schéma suivant présente le principe de fonctionnement basique de l’encodeur :

P9 encodeur.png

Modélisations

Le problème qui se pose est que je n'ai pas suffisamment de place pour positionner l'encodeur sur l'axe du moteur.

> Modélisation 1

Dans un premier temps, j'ai modélisé une pièce 3D qui permettait de rallonger l'axe : (cf Modélisation 1) Cette solution n'a pas fonctionnée parce que les valeurs sont tellement petites (0,3 cm de diamètre) que ça n'a pas abouti. L'imprimante 3D n'a pas une précision assez bonne.

> Modélisation 2

J'ai ensuite modélisé deux pièces, un support pour accrocher l'encodeur à la structure du robot et une roue à la bonne échelle pour qu'elle puisse tenir sur l'axe des coupleurs plutôt que sur l'axe du moteur. (cf Modélisation 2)

Sur internet, il n y a pas de documentations concernant cette version d'encodeur. Mais il y a trois pins :

  • 5V
  • GRND
  • OUT : Ce qui indique qu'il un signal dès que la lumière de la LED est reçue par le capteur de lumière.

Voici le rendu :

Montage

J'ai imprimé les pièces en double afin de placer les encodeurs sur les deux roues à l'avant.

> Modélisation 3

[EDIT] Après avoir testé plusieurs fois, les résultats n'étaient pas convaincants, ils manquent de précisions. A force de déplacer les roues du robots. le centre du disque de l'encodeur s'élargie, ce qui implique qu'il ne tourne plus en même temps que la roue. Cela fausse donc les résultats. L'autre pièce est aussi problématique puisqu'elle s'accroche directement sur les suspensions du robot. Ce choix n'était pas judicieux puisque le robot est très peu droit. Il faut donc réaliser une nouvelle modélisation des pièces. (cf Modélisation 3)

Voici le rendu :

Montage 2

Récapitulatif

Modélisation 1 Modélisation 2 Modélisation 3
Rallonge axe moteur
P9 encodeur axe.png

Le but était de modéliser une pièce qui permettrait d'allonger l'axe du moteur. Ce n'est pas réalisable parce que son diamètre est de 4 mm. Il est impossible d'atteindre une telle précision avec une imprimante 3D.

Support encodeur
Support de l'encodeur
Ce robot n'a pas été conçu pour avoir des encodeurs. Il n'y a pas de place pour les faire tenir. Sur les suspensions du robot, il y a des trous, je voulais en profiter pour accrocher le support de l'encodeur.
Disque de l'encodeur
Roue
Roue à la bonne échelle pour qu'elle puisse tenir sur l'axe des coupleurs plutôt que sur l'axe du moteur.J'ai pris leurs dimensions, mais à force, le disque centrale s'élargie et n'est plus stable ce qui engendre des erreurs de distance.
Support encodeur v2

Support de l'encodeur

Pour éviter d'avoir des erreurs de calculs impacté par la mauvaise qualité des suspensions, cette dernière version du support est directement positionnée sur le moteur qui lui reste stable.
Disque de l'encodeur v2

Roue

Concernant le disque, j'ai également fais des modifications par rapport à la version 1. J'ai réduit le nombres de trous, parce que j'ai remarqué que lorsque le robot se déplaçait rapidement, il perdait des informations. (surement parce que les trous étaient trop rapprochés et petits) J'ai également rendu possible la fixation des vis directement sur la pièce 3D. Cela permettra de fixer la roue sur le coupleur !

Code Arduino

J'ai intégré le code de l'encodeur dans mon code Arduino qui permet de déplacer le robot.

#include "EncoderStepCounter.h"
#define ENCODER_PIN1 2
#define ENCODER_INT1 digitalPinToInterrupt(ENCODER_PIN1)
EncoderStepCounter encoder(ENCODER_PIN1, ENCODER_PIN1 );
 // Dans la fonction setup(), on définit les interruptions sur fronts montants 
 encoder.begin();
 attachInterrupt(ENCODER_INT1, interrupt, RISING);
// La fonction exécutée lorsqu'il y a une interruption 
void interrupt() {
 encoder.tick();
}
 // Dans la fonction loop()
 detachInterrupt(ENCODER_INT1);
 signed char pos = encoder.getPosition();
 if (pos != 0) {
   count += pos;
   encoder.reset();
   Serial.print(count);
   Serial.println(" cm");
 }
   if (count >= 100){
    count=0;
    moveit(0,0); // fonction qui permet d'arrêter la rotation des roues du robot
  }
   attachInterrupt(ENCODER_INT1, interrupt, RISING);

J'affiche la distance parcourue à l'aide de ce calcul : Il ya 25 trous sur le disque, les roues ont un rayon de 4 cm donc une circonférence de 25 cm(2 π R). Donc à chaque interruption (à chaque détection d'un trou) le robot avance d'un cm. Le soucis avec ces encodeurs c'est qu'ils ne prennent pas en compte le sens de rotation du disque.

Ce code ne prend en compte qu'un encodeur sur deux. L'objectif est de l'adapter pour deux, de faire une moyenne du nombre d'interactions pour minimiser les erreurs. Pour le moment mon code me permet d'avancer d'un mètre et d'arrêter le robot. Après plusieurs essais, mon robot avance réellement d'une valeur comprise entre 80 cm et 120 cm. Cette solution fonctionne mais n'est pas très fiable.

Compas

Voici la référence de l'accéléromètre que je vais utiliser pour ce projet : Adafruit Triple-axis Accelerometer+Magnetometer (Compass) Board - LSM303 [ADA1120]

P9 accele.png

J'ai utilisé la datasheet pour en savoir plus sur ce composant. Il permet d'avoir 3 variables de champ magnétique et 3 variables d'accélération. Il utilise le bus I2C pour envoyer les données en liaison série.

P9 accele info.png

Cablâge

Comme j'utilise déjà les pins SDA et SDL pour la carte extensive qui contrôle les moteurs du robot, je me suis renseignée sur internet pour trouver une solution. Sur ce site, j'ai trouvé une information intéressante : il est possible d'avoir à nouveau deux pins SDA et SDL en utilisant les pins A5 et A4.

"Ceux qui disposent de l'Arduino Uno ou d'une carte compatible utiliseront les connecteurs A4 pour SDA (les données) et A5 pour SCL (l'horloge)"

Donc en résumé :

  • VCC : 5V
  • GND : GND
  • SDA : A4
  • SDL : A5


Code Arduino

Une fois le câblage réalisé, j'ai récupéré une librairie du composant LSM303DHL sur le logiciel Arduino que j'ai inclus dans le code. J'ai exécuté un exemple qu'Arduino propose, voici le résultat du moniteur série :


P9 gyro moniteur.png
Je récupère bien les données x, y et z. Mais pour ROS ce n'est pas le format de données qu'il me faut. Il y a une relation mathématique qui me permet de convertir ces trois données en une donnée en degré:

angle= 180*atan2(magy,magx)/PI

P9 gyro moniteur2.png
A présent, j'obtiens bien les données en degrés. Les valeurs sont comprises entre 0 et 360°.

Semaine 19 : 3 - 9 Février

Liaison ROS et Robot pour commander le robot

> Comment est structuré ROS

ROS crée un réseau où tous les processus sont connectés. N'importe quel nœud du système peut accéder à ce réseau, interagir avec d'autres nœuds, voir les informations qu'ils envoient et transmettre des données au réseau:

Voici une liste des principaux éléments de ROS et quelques commandes utiles :

  • Nodes (Nœuds): les nœuds sont des processus où le calcul est effectué. Pour avoir un processus qui peut interagir avec d'autres nœuds, il faut créer un nœud avec ce processus pour le connecter au réseau ROS. Habituellement, un système aura de nombreux nœuds pour contrôler différentes fonctions. Il est préférable d'avoir de nombreux nœuds qui ne fournissent qu'une seule fonctionnalité, plutôt que d'avoir un grand nœud qui fait tout dans le système. Les nœuds sont écrits avec une bibliothèque client ROS, par exemple, roscpp ou rospy.
rosnode info NODE // Affiche des informations sur un noeud
rosnode kill NODE // Arrête un noeud en cours d'exécution 
rosnode list // Liste les noeuds actifs
rosnode ping NODE // Teste la connexion avec le noeud
  • Master (Le maître) : le maître fournit l'enregistrement des noms et le service de recherche au reste des nœuds. Il établit également des connexions entre les nœuds.
  • Messages : les nœuds communiquent entre eux par le biais de messages. Un message contient des données qui fournissent des informations à d'autres nœuds. ROS propose de nombreux types de messages, et vous pouvez également développer votre propre type de message à l'aide de types de messages standard.
rosmsg list // Affiche tous les massages 
rosmsg package // Affiche les messages d'un package
  • Topic : Chaque message doit avoir un nom pour être acheminé par le réseau ROS. Lorsqu'un nœud envoie des données, le nœud publie un sujet. Les nœuds peuvent recevoir des sujets d'autres nœuds en s'abonnant simplement au sujet. Un nœud peut s'abonner à un sujet même s'il n'y a aucun autre nœud publiant sur ce sujet spécifique. Il est important que les noms des sujets soient uniques pour éviter les problèmes et la confusion entre les sujets portant le même nom.
rostopic echo /topic // Affiche les messages 
rostopic find message_type // Trouver un Topic par son nom
rostopic hz /topic // Affiche la fréquence d'affichage
rostopic info /topic // Affiche des informations sur le topic, le type du message, le publishers et subscribers.
rostopic list // Affiche les topics en cours 
rostopic pub /topic type args // Permet de publier une donnée dans un topic
  • Bag : les bags sont un format pour enregistrer et lire les données des messages ROS. Les bags sont un mécanisme important pour stocker des données, telles que les données de capteurs, qui peuvent être difficiles à collecter mais qui sont nécessaires pour développer et tester des algorithmes.
rosbag // Permet d'enregistrer des données


> Première version de code ROS

A compléter

Déplacement non linéaire du robot

Lorsque je commande le robot pour qu'il avance d'un mètre vers l'avant, il a tendance à dévier vers la gauche. Ce qui n'est pas normal, puisque j'impose à chaque roue la même puissance. C'est possible que ça soit un problème du châssis, ou bien que certains coupleurs des moteurs ne soient pas bien visés. Je pense aussi que le problème pourrait venir de tous les éléments que j'ai rajouté sur le robot (batterie portable, Lidar, Raspberry Pi), leur poids doit avoir un impact sur l'inclinaison du robot ce qui pourrait imposer une force vers le bas d'un coté du robot. Pour essayer de voir si le problème vient de la dernière supposition, je vais modéliser un étage supplémentaire sur le robot. Cela permettra de mieux équilibrer les poids des équipements sur tout le robot.

Modélisations Voici le rendu :

Modélisation de l'étage 2
Pilier reliant les étages
P9 rendu Etage2.png

Ce n'était pas le problème, puisqu'en équilibrant le poids sur le robot, le robot dérive tout de même vers la gauche. Pour corriger ce problème, je vais donc mettre plus de puissance sur les roues de gauche pour essayer de corriger le parcours du robot. Pour avoir un retour du parcours des roues des deux côtés, je place un deuxième encodeur sur la roue du milieu à gauche.

REMARQUE : Le disque de l'encodeur ne peut pas être imprimé avec du PLA blanc, il faut que ca soit une couleur sombre.

P9 unSeulEncodeur.png P9 deuxEncodeur.png

On remarque bien sur la première photo que les roues de droite effectuent plus de tours que celles de gauches, environ une dizaine de plus. En modifiant le code et en augmentant la puissance sur les roues de gauches, on obtient les résultats sur la deuxième photo. Le robot avance droit maintenant.

Problème Boussole

La semaine dernière quand j'avais testé la boussole (pas sur le robot), je pensais avoir des résultats assez cohérents. Mais cette semaine en la plaçant sur le robot, je me suis rendue compte que les valeurs étaient pas utilisables. J'ai récupéré les valeurs que j'obtenais en fonction de l'orientation du robot (cf image à droite). J'ai ensuite resté la boussole e, dehors du robot, (cf à gauche), j'obtiens des valeurs plus cohérentes mais ce n'est pas suffisant pour bien contrôler le robot (un quart de cercle représente 180° et les trois autres 180°). Le problème vient des champs magnétiques présents sur le robot qui perturbent la boussole.

P9 boussole.png

Ajout de la caméra sur le site

pi@raspberrypi: ~/ sudo apt-get install motion
pi@raspberrypi: ~/mkdir /home/pi/motion
pi@raspberrypi: ~/chmod 755 /home/pi/motion

Il faut ensuite changer le fichier de configuration suivant :

pi@raspberrypi: ~/ sudo vim /etc/motion/motion.conf
     daemon on
     framerate 15
     webcam_motion on 
     webcam_localhost off
 pi@raspberrypi: ~/ sudo vim /etc/default/motion // modifier start_motion_daemon=yes


Pour lancer le broadcast :

 pi@raspberrypi: ~/ sudo service motion start 

Il suffit d'aller sur le lien suivant : localhost:8080 pour accéder à l'image de la caméra.

Publication de la carte sur un site web

A compléter

P9 cartographiev1.png P9 cartographiev2.png

Script bash pour lancer toutes les commandes

Pour lancer le noyau ROS, le Lidar, Hector, le serveur ROS, il y a 5 commandes a exécuter depuis le terminal. Pour améliorer le lancement, j'ai écris un script bash qui permet de tout lancer à distance. Il regroupe simplement les commandes suivantes :

roscore 
roslaunch ydlidar lidar.launch 
roslaunch hector_slam slamit.launch 
rosrun bridge server 
rosrun rosserial

Semaine 20 : 10 - 16 Février

Semaine 21 : 17 - 21 Février (Soutenance 2)

Lien : https://www.youtube.com/watch?v=HFoK1urJyv8

Documentation

Documentation du projet : Fichier:Documentation Projet.pdf

Code du projet : https://github.com/lmejbar/Slamit

Documents Rendus

Rapport intermédiaire : Fichier:P9 Rapport1 MEJBAR.pdf

Soutenance intermédiaire : Fichier:P9 RapportSoutenance MEJBAR.pdf

Rapport final : Fichier:P9 Rapport2 MEJBAR.pdf

Soutenance finale : Fichier:P9 Soutenance2 MEJBAR.pdf