IMA4 2018/2019 P14 : Différence entre versions

De Wiki de Projets IMA
(Semaine 11)
 
(104 révisions intermédiaires par 2 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
 +
<include nopre noesc src="/home/pedago/pimasc/include/video-VoitureAutonomeIMA42018-iframe.html" />
 
__TOC__
 
__TOC__
 
<br style="clear: both;"/>
 
<br style="clear: both;"/>
Ligne 17 : Ligne 18 :
 
Dans le but de réaliser notre projet nous devrons remplir les objectifs suivants que nous pouvons répartir en 3 parties.
 
Dans le but de réaliser notre projet nous devrons remplir les objectifs suivants que nous pouvons répartir en 3 parties.
  
====Partie mécanique :====  
+
====Partie mécanique ====  
  
  
 
*Réaliser un support pour la webcam qui permette de l'élever à au moins 10 cm du sol (en prenant en compte la taille du châssis)
 
*Réaliser un support pour la webcam qui permette de l'élever à au moins 10 cm du sol (en prenant en compte la taille du châssis)
  
====Partie électronique :====  
+
====Partie électronique ====  
  
 
*Les moteurs seront commandés par une carte Arduino
 
*Les moteurs seront commandés par une carte Arduino
 
*La partie Deep Learning et l'analyse des images de la webcam seront traitées par un ordinateur connecté à une RaspBerry pi 3
 
*La partie Deep Learning et l'analyse des images de la webcam seront traitées par un ordinateur connecté à une RaspBerry pi 3
  
====Partie informatique :====
+
====Partie informatique ====
  
 
*Faire apprendre au robot un parcours en le pilotant manuellement via une manette
 
*Faire apprendre au robot un parcours en le pilotant manuellement via une manette
Ligne 140 : Ligne 141 :
  
 
==Liste des tâches à effectuer==
 
==Liste des tâches à effectuer==
====Partie mécanique :====  
+
====Partie mécanique ====  
  
 
*Réaliser un support par impression 3D pour la webcam qui permette de l'élever à au moins 10 cm du sol mais également de la protéger. Fixer ce support sur la partie avant de la voiture.  
 
*Réaliser un support par impression 3D pour la webcam qui permette de l'élever à au moins 10 cm du sol mais également de la protéger. Fixer ce support sur la partie avant de la voiture.  
  
====Partie électronique :====  
+
====Partie électronique ====  
  
 
*Raccorder les moteurs à la Raspberry pi en les connectant au shield qui sera posé sur celle-ci.
 
*Raccorder les moteurs à la Raspberry pi en les connectant au shield qui sera posé sur celle-ci.
 
*Connecter une batterie auxiliaire de 5V et 2A à la Raspberry pour l'alimenter (la puissance délivrée par la batterie du véhicule n'étant pas assez grande pour garantir que les moteurs tourneront bien à pleine puissance tout en alimentant la carte).  
 
*Connecter une batterie auxiliaire de 5V et 2A à la Raspberry pour l'alimenter (la puissance délivrée par la batterie du véhicule n'étant pas assez grande pour garantir que les moteurs tourneront bien à pleine puissance tout en alimentant la carte).  
  
====Partie informatique :====
+
====Partie informatique ====
  
 
*Configurer la Raspberry Pi (installation des librairies, configuration des ports de carte pour le shield, paramétrage de la caméra).
 
*Configurer la Raspberry Pi (installation des librairies, configuration des ports de carte pour le shield, paramétrage de la caméra).
Ligne 163 : Ligne 164 :
  
 
{| class="wikitable"
 
{| class="wikitable"
!Tâche !! Prélude !! Heures S1 !! Heures S2 !! Heures S3 !! Heures S4 !! Heures S5 !! Heures S6 !! Heures S7 !! Heures S8 !! Heures S9 !! Heures S10 !! Total
+
!Tâche !! Prélude !! Heures S1 !! Heures S2 !! Heures S3 !! Heures S4 !! Heures S5 !! Heures S6 !! Heures S7 !! Heures S8 !! Heures S9 !! Heures S10 !! Heures S11 !! Heures S12 et 13 !! Heures S14 !! Heures S15 !! Total
 
|-
 
|-
 
| Analyse du projet (recherche des concurrents, descriptions des objectifs, scénario d'usage, matériel)  
 
| Analyse du projet (recherche des concurrents, descriptions des objectifs, scénario d'usage, matériel)  
 
| 2h30
 
| 2h30
 
| 1h
 
| 1h
 +
|
 +
|
 +
|
 +
|
 
|
 
|
 
|
 
|
Ligne 192 : Ligne 197 :
 
|
 
|
 
|
 
|
 +
|
 +
|
 +
| 4h
 +
| 6h
 
|-
 
|-
 
| Remplissage du wiki (réponse à la question difficile, préparation du projet, choix techniques et matériels)
 
| Remplissage du wiki (réponse à la question difficile, préparation du projet, choix techniques et matériels)
Ligne 205 : Ligne 214 :
 
| 30 min
 
| 30 min
 
| 30 min
 
| 30 min
|
+
| 30 min
 +
| 5h
 +
|
 +
| 6h
 +
| 19h30
 
|-
 
|-
| Installation de l'OS sur la Rapberry / établissement de la communication entre la Rpi et une Wiimote
+
| Installation de l'OS sur la Rapberry / établissement de la communication entre la Rpi et une manette Xbox one
 
|  
 
|  
| 2h
+
| 3h
 +
|
 +
|
 +
|
 
|
 
|
 
|
 
|
Ligne 220 : Ligne 236 :
 
|
 
|
 
|
 
|
 +
| 3h
 
|-
 
|-
| Capture vidéo et prise d'image avec la Rpi
+
| Travail sur la prise d'image avec la Rpi
 
|  
 
|  
 
|  
 
|  
| 1h
+
| 2h
 
|  
 
|  
 
|
 
|
Ligne 234 : Ligne 251 :
 
|
 
|
 
|
 
|
 +
|
 +
|
 +
|
 +
| 4h
 
|-
 
|-
| Réception et traitement des données provenant de la manette (adresses des boutons)
+
| Réception et traitement des données provenant de la manette  
 
|  
 
|  
 
|  
 
|  
Ligne 248 : Ligne 269 :
 
|
 
|
 
|
 
|
 +
|
 +
| 2h
 +
|
 +
| 7h
 
|-
 
|-
 
| Gestion des moteurs du véhicule
 
| Gestion des moteurs du véhicule
Ligne 260 : Ligne 285 :
 
|
 
|
 
|
 
|
 +
| 2h
 
|
 
|
 
|
 
|
 +
|
 +
|
 +
| 5h30
 
|-
 
|-
 
| Initiation au Deep Learning
 
| Initiation au Deep Learning
Ligne 268 : Ligne 297 :
 
| 4h
 
| 4h
 
| 3h
 
| 3h
| 3h30
+
| 3h
 +
|
 +
|
 +
|
 
|
 
|
 
|
 
|
Ligne 276 : Ligne 308 :
 
|
 
|
 
|
 
|
 +
| 10h
 
|-
 
|-
 
| Prise en main de Keras/Tensorflow, création et test des datasets
 
| Prise en main de Keras/Tensorflow, création et test des datasets
Ligne 290 : Ligne 323 :
 
|
 
|
 
|
 
|
 +
|
 +
|
 +
|
 +
| 16h
 
|-
 
|-
 
| Modélisation 3D
 
| Modélisation 3D
Ligne 296 : Ligne 333 :
 
|  
 
|  
 
|  
 
|  
|
 
|
 
| 2h
 
|
 
|
 
 
|
 
|
 
|
 
|
 +
| 2h
 
|
 
|
|-
 
| Mise en marche et pilotage du véhicule via la manette Xbox
 
|
 
|
 
|
 
|
 
 
|  
 
|  
 
|  
 
|  
 
|  
 
|  
 
|
 
|
| 2h
 
 
|
 
|
 
|
 
|
 
|
 
|
 +
| 2h
 
|-
 
|-
| Test de la phase de prétraitement des données via la platforme Google Colab
+
| Test et travail sur la phase de prétraitement des données via la platforme Google Colab
|
 
|
 
|
 
 
|  
 
|  
 
|  
 
|  
 
|  
 
|  
 
|  
 
|  
| 3h
 
| 2h
 
 
|
 
|
 
|
 
|
 
|
 
|
 +
| 3h
 +
| 3h
 +
| 3h
 +
| 3h
 +
| 3h
 +
| 14h
 +
| 2h
 +
|
 +
| 31h
 
|-
 
|-
| Test et évaluation du temps de calcul lors du traitement des datasets via un PC
+
| Test sur circuit (prise d'image, entraînement, puis pilotage autonome)
|
 
 
|  
 
|  
 
|  
 
|  
Ligne 342 : Ligne 372 :
 
|  
 
|  
 
|
 
|
| 1h
 
 
|
 
|
 
|
 
|
 
|
 
|
|-
 
| Test sur circuit (prise d'image, puis pilotage autonome)
 
|
 
|
 
|
 
|
 
 
|  
 
|  
 
|  
 
|  
 +
|
 +
|4h
 +
|10h
 +
| 14h
 +
|-
 +
| Rédaction du Rapport
 
|  
 
|  
 
|
 
|
|
 
 
|
 
|
 
|
 
|
 
|
 
|
 +
|
 +
|
 +
|
 +
|
 +
|
 +
|
 +
|
 +
|
 +
|
 +
| 8h
 +
| 8h
 
|}
 
|}
  
Ligne 379 : Ligne 418 :
  
 
*Récupération des adresses de chaque bouton de la manette Xbox sur la Raspberry. Nous avons trouvé une librairie Python nous permettant d'utiliser notre manette Xbox One sans fils : evdev. Cette librairie permet de récupérer chaque événement associé à un périphérique en particulier (bouton pressé/relâché, mouvement du Joystick et etc)  
 
*Récupération des adresses de chaque bouton de la manette Xbox sur la Raspberry. Nous avons trouvé une librairie Python nous permettant d'utiliser notre manette Xbox One sans fils : evdev. Cette librairie permet de récupérer chaque événement associé à un périphérique en particulier (bouton pressé/relâché, mouvement du Joystick et etc)  
import evdev
+
<code>import evdev</code>
  
 
*Activation de la caméra de la Raspberry et prise en main des fonctions Python de la bibliothèque Picamera dans l'optique de réaliser une prise d'images toutes les 100ms, au format adapté. Pour l'instant, nous sommes capable de prendre des photos dans une boucle infini, en leur donnant un nom arbitraire et en les stockant dans un dossier défini.
 
*Activation de la caméra de la Raspberry et prise en main des fonctions Python de la bibliothèque Picamera dans l'optique de réaliser une prise d'images toutes les 100ms, au format adapté. Pour l'instant, nous sommes capable de prendre des photos dans une boucle infini, en leur donnant un nom arbitraire et en les stockant dans un dossier défini.
from picamera import PiCamera  
+
<code>from picamera import PiCamera </code>
  
 
*Initiation au Deep learning. Suivre le lien [http://vision.gel.ulaval.ca/~cgagne/enseignement/apprentissage/A2018/] pour avoir accès à des cours et des travaux pratiques centrés sur l'apprentissage approfondi.
 
*Initiation au Deep learning. Suivre le lien [http://vision.gel.ulaval.ca/~cgagne/enseignement/apprentissage/A2018/] pour avoir accès à des cours et des travaux pratiques centrés sur l'apprentissage approfondi.
Ligne 420 : Ligne 459 :
 
* Tests sur le servomoteur de direction et le contrôleur de vitesse de la voiture RC.  
 
* Tests sur le servomoteur de direction et le contrôleur de vitesse de la voiture RC.  
  
Après avoir effectué différents tests, nous nous sommes rendu compte que, bien que l'on soit en mesure de contrôler la direction parfaitement, on arrive seulement à faire tourner le moteur principal en plein régime, et seulement en marche avant. Puisque la voiture est incontrôlable en plein régime et que, même si on pourrait se passer de la marche arrière, il nous faut de toute manière pouvoir la faire rouler le plus lentement possible. C'est pourquoi, afin d'identifier le problème, nous décidons d'aller faire quelques analyses à l'analyseur de spectre, afin de comprendre l'allure des signaux supposés être reçu par le contrôleur de vitesse.
+
Après avoir effectué différents tests, nous nous sommes rendus compte que, bien que l'on soit en mesure de contrôler la direction parfaitement, on arrive seulement à faire tourner le moteur principal en plein régime, et seulement en marche avant. Puisque la voiture est incontrôlable en plein régime et que, même si on pourrait se passer de la marche arrière, il nous faut de toute manière pouvoir la faire rouler le plus lentement possible. C'est pourquoi, afin d'identifier le problème, nous décidons d'aller faire quelques analyses à l'analyseur de spectre, afin de comprendre l'allure des signaux supposés être reçus par le contrôleur de vitesse.
  
 
==Semaine 4==
 
==Semaine 4==
<gallery mode="packed-hover" heights="200px"  caption="Résultats des tests de la semaine dernière">
+
'''Résultats des tests de la semaine dernière'''
 +
<gallery mode="packed-hover" heights="200px"  caption="Résultats des tests à l'analyseur de spectre">
 
Servo_reverse.jpg  
 
Servo_reverse.jpg  
 
Servo_repos.jpg  
 
Servo_repos.jpg  
Ligne 436 : Ligne 476 :
  
  
 +
 +
[[Fichier:ManetteXbox.jpg|200px|thumb|right|La manette utilisée ]]
 
'''Autres tâches réalisées'''
 
'''Autres tâches réalisées'''
 
* Création d'un code ayant pour fonction de déterminer les angles des servomoteurs correspondant aux positions de repos, marche avant/arrière et direction gauche et droite. Nous avons pris soin de détacher la tige de direction du servomoteur afin de ne pas endommager la voiture en cas de saisie d'un angle trop éloigné. En effet si par exemple la plage de l'angle pour la direction varie de 60 à 140 par exemple (60 gauche absolue, 100 tout droit et 140 droite absolue), rentrer un angle de 280 et exécuter le code pourrait potentiellement causer des dommages au servomoteur alors bloqué ou aux autres pièces de la voiture.
 
* Création d'un code ayant pour fonction de déterminer les angles des servomoteurs correspondant aux positions de repos, marche avant/arrière et direction gauche et droite. Nous avons pris soin de détacher la tige de direction du servomoteur afin de ne pas endommager la voiture en cas de saisie d'un angle trop éloigné. En effet si par exemple la plage de l'angle pour la direction varie de 60 à 140 par exemple (60 gauche absolue, 100 tout droit et 140 droite absolue), rentrer un angle de 280 et exécuter le code pourrait potentiellement causer des dommages au servomoteur alors bloqué ou aux autres pièces de la voiture.
Ligne 442 : Ligne 484 :
  
 
* Suite de l'entraînement sur les réseaux de neurones : création d'un réseau test. Ce réseau avait pour objectif de classer des séries de 4 images dans 10 catégories prédéfinies selon le contenu de celles-ci (chien, chat, avion, voiture...) et d'estimer le pourcentage de précision du réseau ainsi que le temps de traitement approximatif (4 min environ pour une série de 4 images).
 
* Suite de l'entraînement sur les réseaux de neurones : création d'un réseau test. Ce réseau avait pour objectif de classer des séries de 4 images dans 10 catégories prédéfinies selon le contenu de celles-ci (chien, chat, avion, voiture...) et d'estimer le pourcentage de précision du réseau ainsi que le temps de traitement approximatif (4 min environ pour une série de 4 images).
 +
 +
  
  
Ligne 450 : Ligne 494 :
 
  gamepad = InputDevice('/dev/input/event3')  
 
  gamepad = InputDevice('/dev/input/event3')  
 
  ...
 
  ...
* A chaque redémarrage de la raspberry, il faut exécuter une ligne de code pour désactiver l'ERTM (Enhanced Re-Transmission Mode a L2CAP Bluetooth stack feature) sur la raspberry, sans quoi la manette ne peut se connecter. Aussi bete ce problème puisse paraître, nous avons chercher sur de nombreux forums et aucune des solutions proposées n'ont fonctionné. Ce mode ERTM se réactivant à chaque redémarrage, nous forçant à exécuter cette commande dans le terminal :
+
* A chaque redémarrage de la raspberry, il faut exécuter une ligne de code pour désactiver l'ERTM (Enhanced Re-Transmission Mode a L2CAP Bluetooth stack feature) sur la raspberry, sans quoi la manette ne peut se connecter. Aussi bête ce problème puisse paraître, nous avons chercher sur de nombreux forums et aucune des solutions proposées n'ont fonctionné. Ce mode ERTM se réactivant à chaque redémarrage, nous forçant à exécuter cette commande dans le terminal :
 
  sudo bash -c 'echo 1 > /sys/module/bluetooth/parameters/disable_ertm'
 
  sudo bash -c 'echo 1 > /sys/module/bluetooth/parameters/disable_ertm'
  
Ligne 497 : Ligne 541 :
  
 
*Nous avons finalisé la réalisation de la structure supportant la batterie, le raspberry et la camera, en y perçant des trous de manière à ce qu'elle puisse se fixer directement sur le châssis de la voiture à l'aide des clips métalliques, de la même manière que la carrosserie fournie avec la voiture.  
 
*Nous avons finalisé la réalisation de la structure supportant la batterie, le raspberry et la camera, en y perçant des trous de manière à ce qu'elle puisse se fixer directement sur le châssis de la voiture à l'aide des clips métalliques, de la même manière que la carrosserie fournie avec la voiture.  
*Nous avons ensuite adapté le code du contrôle manuel de la voiture à l'aide de la manette Xbox, puisque les moteurs diffèrent de ceux de notre voiture personnelle, que nous avons utilisé jusqu'à présent pour pouvoir avancer malgré la non-disponibilité de la voiture RC de Polytech dédiée aux projets. Nous avons donc réutilisé notre bonne vieille fonction de test de servomoteurs afin de vérifier leur bon fonctionnement et de déterminer la nouvelle plage de variation des angles, que nous renseignons dans un fichier : constantes.py. Cette étape de nous a pas pris beaucoup de temps puisque nous connaissions déjà la démarche à suivre.
+
*Nous avons ensuite adapté le code du contrôle manuel de la voiture à l'aide de la manette Xbox, puisque les moteurs diffèrent de ceux de notre voiture personnelle, que nous avons utilisé jusqu'à présent pour pouvoir avancer malgré la non-disponibilité de la voiture RC de Polytech dédiée aux projets. Nous avons donc réutilisé notre bonne vieille fonction de test de servomoteurs afin de vérifier leur bon fonctionnement et de déterminer la nouvelle plage de variation des angles, que nous renseignons dans un fichier : constantes.py. Cette étape ne nous a pas pris beaucoup de temps, puisque nous connaissions déjà la démarche à suivre.
 
*Nous avons ensuite testé le bon fonctionnement de tout cela avec un bref test de conduite à la manette de Xbox.
 
*Nous avons ensuite testé le bon fonctionnement de tout cela avec un bref test de conduite à la manette de Xbox.
  
Ligne 506 : Ligne 550 :
  
  
*Coté réseau de neurones : nous avons établi un programme permettant d'effectuer la phase de prétraitement des images en leur affectant un label à chacune. Le programme semble fonctionnel et prêt à l'emploi. Nous étant basé sur un set existant, il ne reste plus qu'à l'appliquer sur notre datasets.
+
*Coté réseau de neurones : nous avons établi un programme permettant d'effectuer la phase de prétraitement des images en leur affectant un label à chacune. Le programme semble fonctionnel et prêt à l'emploi. Nous étant basé sur un set existant, il ne reste plus qu'à l'appliquer sur notre dataset.
  
*Suite aux résultats peu satisfaisants obtenus lors de la précédente séance, nous avons effectué un nouvel entraînement sur notre réseau de neurones en nous inspirant d'un modèle existant. Le but de la manoeuvre était de voir le temps mis par le PC pour entraîner notre réseau de neurones en vue d'une possible optimisation de ce modèle. Pour cela, nous avons effectué une série de 10 entraînements à la suite et, globalement, nous avons observé un temps de calcul d'à peu près 5 min pour chaque entraînement ce qui est assez long.
+
*Suite aux résultats peu satisfaisants obtenus lors de la précédente séance, nous avons effectué un nouvel entraînement sur notre réseau de neurones en nous inspirant d'un modèle existant. Le but de la manœuvre était de voir le temps mis par le PC pour entraîner notre réseau de neurones en vue d'une possible optimisation de ce modèle. Pour cela, nous avons effectué une série de 10 entraînements à la suite et, globalement, nous avons observé un temps de calcul d'à peu près 5 min pour chaque entraînement ce qui est assez long.
  
 
==Semaines 10==
 
==Semaines 10==
Cette semaine nous avons de nouveau été amené à modifier notre code concernant le pilotage manuel de la voiture. En effet c'est lorsque nous avons voulu définir la forme du nom qui allé être donné à chaque photo prise par la camera, que nous nous sommes rendu compte que faire tourner la fonction de la camera et celle du contrôle de la voiture dans 2 codes séparés n'était pas une idée brillante...Puisque nous avons besoin de connaître l’état des bouton (ou des moteurs) pour labelliser correctement notre photo. Nous avons donc réalisés les tâches suivantes :
+
Cette semaine nous avons de nouveau été amené à modifier notre code concernant le pilotage manuel de la voiture. En effet c'est lorsque nous avons voulu définir la forme du nom qui allait être donné à chaque photo prise par la camera, que nous nous sommes rendu compte que faire tourner la fonction de la camera et celle du contrôle de la voiture dans 2 codes séparés n'était pas une idée brillante...Puisque nous avons besoin de connaître l’état des boutons (ou des moteurs) pour labelliser correctement notre photo. Nous avons donc réalisé les tâches suivantes :
 
* En utilisant des threads python nous avons regroupé les 2 codes en un seul.
 
* En utilisant des threads python nous avons regroupé les 2 codes en un seul.
 
<code>from threading import Thread</code>
 
<code>from threading import Thread</code>
Ligne 517 : Ligne 561 :
 
**On lance le code avec la commande :
 
**On lance le code avec la commande :
 
  python3 auto_datamining.py <délais de capture en seconde>
 
  python3 auto_datamining.py <délais de capture en seconde>
**Si aucune manette Xbox n'est connecté en bluetooth au raspberry, le code se ferme immédiatement. Sinon, on peut piloter la voiture avec la manette.  
+
**Si aucune manette Xbox n'est connectée en bluetooth au raspberry, le code se ferme immédiatement. Sinon, on peut piloter la voiture avec la manette.  
 
**On presse Y pour activer la capture d'images. Des images pourront alors être prises selon le délais renseigné en paramètre (max environ 14 photos/seconde).
 
**On presse Y pour activer la capture d'images. Des images pourront alors être prises selon le délais renseigné en paramètre (max environ 14 photos/seconde).
 
**Puisque dans le cadre de notre usage il n'existe pas de cas où la voiture n'avance pas, la capture s'effectue uniquement quand la voiture est en mouvement.
 
**Puisque dans le cadre de notre usage il n'existe pas de cas où la voiture n'avance pas, la capture s'effectue uniquement quand la voiture est en mouvement.
Ligne 530 : Ligne 574 :
 
==Semaine 11==
 
==Semaine 11==
 
Nous avons profité de cette semaine pour avancer sur la partie de code à exécuter sur Google Colab. Puisque le code de récupération de données en conduite manuelle est désormais 100% opérationnel, nous pouvons parfaire et tester le bon fonctionnement du près-traitement des données et celui de l'entraînement du réseau de neurones. Nous avons donc réalisé les tâches suivantes :
 
Nous avons profité de cette semaine pour avancer sur la partie de code à exécuter sur Google Colab. Puisque le code de récupération de données en conduite manuelle est désormais 100% opérationnel, nous pouvons parfaire et tester le bon fonctionnement du près-traitement des données et celui de l'entraînement du réseau de neurones. Nous avons donc réalisé les tâches suivantes :
* Rédaction du code capable d'uploader notre dataset sur le Notebook de Google Colab.
+
* Ajout du code capable d'uploader notre dataset sur le Notebook de Google Colab.
 
  !rm *.zip        //pour éviter les conflits  
 
  !rm *.zip        //pour éviter les conflits  
 
  from google.colab import files   
 
  from google.colab import files   
Ligne 536 : Ligne 580 :
 
  !unzip image.zip  
 
  !unzip image.zip  
 
  dataset = "image"  
 
  dataset = "image"  
* Rédaction d'une fonction qui, en fonction de leur nom, convertit les photos en 3 tableaux numpy (X : l'image, Y : la direction, Z : la vitesse (inutilisé pour l'instant, mais sera comme cela exploitable en cas de besoin) )
+
* Rédaction d'une fonction qui, en fonction de leur nom, convertit les photos en 3 tableaux numpy (X : l'image, Y : la direction, Z : la vitesse (inutilisée pour l'instant, mais sera comme cela exploitable en cas de besoin) )
 
  def load_photos(dataset)
 
  def load_photos(dataset)
 
     ...  
 
     ...  
 
     return X, Y, Z
 
     return X, Y, Z
 
* Le réseau de neurones CNN de Nvidia que nous avons récupéré fonctionnant pour des labels 5 directions, nous l'avons rapidement modifié pour l'adapter à nos labels 3 directions.
 
* Le réseau de neurones CNN de Nvidia que nous avons récupéré fonctionnant pour des labels 5 directions, nous l'avons rapidement modifié pour l'adapter à nos labels 3 directions.
* Nous avons tester le bon fonctionnement avec un dataset enregistré depuis notre voiture. Bien sûr ce dataset a été pris depuis notre salle de travail simplement pour tester le code et il ne correspond donc à rien de concret en réalité.  
+
* Nous avons tester le bon fonctionnement avec un dataset enregistré depuis notre voiture. Bien sûr ce dataset a été pris depuis notre salle de travail simplement pour vérifier que le code compile bien et il ne correspond donc à rien de concret en réalité.  
  
  
<gallery mode="packed-hover" heights="400px"  caption="Architecture du réseau et entrainement">
+
<gallery mode="packed-hover" heights="350px"  caption="Architecture du réseau et entrainement">
 
CNN_struct.JPG | Code du CNN
 
CNN_struct.JPG | Code du CNN
 
CNN_sum.JPG | résumé de l'architecture du CNN
 
CNN_sum.JPG | résumé de l'architecture du CNN
Ligne 550 : Ligne 594 :
 
</gallery>
 
</gallery>
  
==Semaines complémentaires==
+
'''Notes :'''
 +
Si on ne dispose pas d'une interface graphique (un écran à brancher sur le raspberry), voici la démarche à suivre
 +
pour récupérer les images de le raspberry depuis un ordinateur sur le même réseau:
 +
* Avec clé usb :
 +
> ssh pi@<IP raspberry>
 +
> <mot de passe>
 +
Puis avec '''mv -r''' ou '''cp -r''' on déplace le dataset sur la clé USB, pour le récupérer sur un pc par la suite.
 +
 
 +
* Sans clé usb :
 +
> scp -r pi@<IP raspberry>:~/Desktop/image .  #par exemple
 +
>  <mot de passe>
 +
 
 +
==Semaines 12 et 13==
 +
 
 +
Nous avons profité des vacances de printemps pour travailler sur notre projet et améliorer drastiquement notre code, notamment la partie ordinateur (Google Colab). Aussi avons nous réalisé les choses suivantes :
 +
* Ajout d'une partie dédiée à l'augmentation de la quantité de données pour l'entraînement du CNN. Pour l'instant elle n'est composée que de 2 fonctions : une qui double notre jeu de données en réalisant un effet miroir sur les images à l'aide de openCV, et une autre qui ajoute des effets de luminosité aléatoires (utilisant également openCV). Cependant on peut aisément ajouter de nouvelles fonctions si nous en trouvons d'autres qui pourraient être intéressantes (d'autres effets sur la luminosité, ajout d'ombres aléatoires ...). Notons que la fonction miroir est probablement la plus intéressante puisqu'elle permet de ce fait d'avoir exactement le même nombre d'images correspondant à des virages à droite que d'images correspondant à des virages à gauche.
 +
def mirror_image(X,Y):
 +
    ...
 +
    return X_mirror,Y_mirror
 +
 
 +
def random_brightness(X,Y):
 +
    ...
 +
    return X_bright, Y_bright
 +
 
 +
* Ajout de fonctions pour visualiser notre dataset (utile pour tester les fonctions d'augmentation de données).
 +
* Implémentation d'une partie post-traitement dédiée à la validation du modèle qui affiche notamment l'évolution des performances du réseau CNN à la fin de l'entrainement, et qui offre la possibilité de charger un autre jeu de données afin de tester le modèle (potentiellement une partie du dataset de départ, mais les résultat risques d'être plus optimistes que la réalité), en affichant le pourcentage de prédictions correctes.
 +
* Le code étant assez imposant, nous avons réorganisé le notebook et ajouté des intitulés et des commentaires afin de s'y retrouver plus facilement. On peut désormais facilement ajouter des fonctions ou modifier le code.
 +
<gallery mode="packed-hover" heights="400px"  caption="Aperçu du Notebook">
 +
Colab_organise.JPG
 +
</gallery>
 +
 
 +
 
 +
Afin de pouvoir réaliser quelques vérifications sur le code, nous avons rapidement tracé au sol un virage avec du scotch blanc et pris quelques images (programme de capture mais sans la voiture) et nous les avons uploadé sur le Notebook. Nous avions donc un petit dataset de 550 images (sans augmentation) avec le quel nous avons pu vérifier le bon fonctionnement du programme.
 +
[[Fichier:Virage_test.jpg|150px|thumb|right|Virage de test]]
 +
<gallery mode="packed-hover" heights="200px" >
 +
CNN_data.JPG | Répartition des données
 +
Virage_image3.JPG | image n°3
 +
Virage_image556.JPG | miroir de l'image n°3
 +
CNN_precis.JPG | gain de précision du CNN au cours des 50 epochs
 +
</gallery>
 +
 
 +
Au final, nous avons obtenu le modèle virage_test.h5, et en chargeant d'autres images pour la validation du modèle, nous avons obtenu le résultat suivant :
 +
 
 +
<code> 152 / 163 correctement prédits <=>  93.2515337423 % de réussite </code>
 +
 
 +
Ceci étant le résultat sans augmentation de données, nous avons donc réitéré l’expérience en utilisant nos 2 fonctions, quadruplant ainsi les données. Résultat :
 +
 
 +
<code> 160 / 163 correctement prédits <=>  98.1595092025 % de réussite </code>
 +
 
 +
On constate l'importance de la qualité du jeu de données. Dans tous les cas, les résultats sont très encourageants pour un simple test, avec bien moins de 10% d'erreurs.
 +
 
 +
==Semaine 14==
 +
 
 +
En cette semaine de reprise, nous avons donc décidé de réaliser un essai sur une piste avec la voiture, en utilisant le code que nous avons fini pendant les vacances. Nous avons donc acheté des rouleaux de scotch blanc avec lesquels nous avons tracé un parcours très basique : une simple boucle.
 +
 
 +
'''Déroulement du test'''
 +
[[Fichier:Polycircuit1.jpg|200px|thumb|right|Circuit réalisé]]
 +
 
 +
*Réalisation de notre circuit à Polytech, sous la passerelle entre les bâtiments B et E.
 +
*Nous avons démarré la raspberry et avons connecté en ssh un PC portable à cette dernière.
 +
*Mise en marche de la voiture et de la manette, puis lancement du programme d'entraînement.
 +
*Nous avons réalisé 3 tours dans chaque sens, en prenant garde à la qualité de notre conduite, puis avons réalisé quelques passages supplémentaires dans les virages.
 +
*Un dataset de un peu plus de 1000 images (avant traitement) a ainsi été obtenu, que nous avons chargé sur le notebook Google Colab pour y appliquer les près-traitements et ainsi quadrupler notre jeu de données.
 +
*Nous avons donc ensuite lancé l'entraînement du réseaux de neurones, ce qui a pris une bonne vingtaine de minutes.
 +
*Le modèle (fichier au format .h5) a ensuite été récupéré puis transmis à la raspberry pour pouvoir l'utiliser.
 +
*Enfin, nous avons lancé le code d’auto-pilotage, puis avons posé la voiture sur la piste pour la laisser se diriger toute seule (en restant toutefois très vigilant pour éviter la casse).
 +
 
 +
'''Résultats'''
 +
 
 +
Malheureusement la voiture ne réagit pas du tout comme prévu et semble effectuer des actions aléatoires. Cependant lorsqu'elle arrive sur une ligne, elle se met à alterner gauche et droite comme si elle essayait de la suivre. De manière générale, l'action "tout droit n'est presque jamais effectuée. Nous avons alors réalisé de une nouvelle tentative en veillant particulièrement à la qualité irréprochable de notre dataset, mais rien à faire, toujours le même résultat. Il faut bien l'admettre c'est de toute évidence un échec cuisant et il va falloir déterminer la source du problème afin de le solutionner.
 +
<gallery mode="packed-hover" heights="200px"  caption="Résultat peu convaincant">
 +
Echec1.gif
 +
</gallery>
 +
 
 +
 
 +
'''Analyses'''
 +
 
 +
Après ces infructueuses tentatives, nous avons donc réfléchi à quelle pouvait bien être la cause de cet échec. Nous émettons alors plusieurs hypothèses :
 +
*Premièrement nous nous rappelons que l'architecture du CNN de Nvidia que nous avons récupéré est prévue pour 5 directions (gauche, gauche légèrement, tout droit,... par exemple). Or nous avons adapté sa sortie pour seulement 3 directions. Il se pourrait alors qu'il ne réagisse plus correctement.
 +
*L'angle de la camera est peut-être mauvais. Mais au vu des résultats, ce détail ne peut pas justifier à lui tout seul le comportement de la voiture.
 +
*L'absence de pointillés sur notre parcours empêche la voiture de bien se repérer, notamment dans les virages. Encore une fois cela semble peut probable au vu du fait que même une ligne droite n'est pas reconnue.
 +
*La luminosité extérieure pose problème, à cause des forts écarts d'une minutes à l'autre (avec le passage des nuages par exemple). En effet puisque le réseaux met un peu plus de 20 minutes à s'entraîner, la luminosité a peut être fortement varié et ainsi le réseaux de neurones n'est plus en mesure de reconnaître quoi que ce soit. Sachant que les concours Ironcar se font en intérieur, et que les cameras de raspberry sont très sensibles à la luminosité, c'est la piste de l’éclairage que nous retenons.
 +
 
 +
==Semaine 15 et fin==
 +
 
 +
La semaine 14 étant fortement chargée en travail avec les partiels, nous nous retrouvons donc déjà à la semaine 15. Il ne nous reste donc plus qu'une semaine de travail et 2 jours pour finaliser notre projet, sachant que nous avons également un certain nombre d'examens. Bien décidés à  résoudre notre problème une bonne fois pour toute aux cours de cette dernière ligne droite, nous effectuons donc dès que l'occasion se présente et sans perdre de temps des séries de tests et de modifications.
 +
 
 +
===Essais et problèmes concernant la conduite automatique===
 +
 
 +
*'''Test 1 : En intérieur'''
 +
**'''Moyens mis en oeuvre : '''Tout d'abord, nous commençons par suivre la piste que nous avions retenue la semaine dernière et décidons d’effectuer nos tests en intérieur. Le mauvais temps de la semaine dernière a de toute façon totalement ravagé le circuit que nous avions conçu. Avec la permission d'un encadrant, nous nous plaçons donc en E300 dans une salle informatique et traçons de nouveau un circuit, ou plutôt un simple "U" puisque l'espace dans ces salles est assez restreint. De toute manière, pour vérifier le bon fonctionnement du modèle, un simple virage suffit en théorie. Ensuite nous prenons également soin de fermer les stores et les portes, et d'allumer les néons, pour garantir un éclairage uniforme.
 +
**'''Résultats : '''Sur le notebook, on observe que la précisions du modèle s'est amélioré, et sur la piste la voiture semble cette fois reconnaître les lignes droites. Cependant dans les virages les résultats sont très aléatoires. De plus, même en ligne droite le taux d'erreurs est bien supérieur à celui annoncé dans la validation du modèle. Nous pensons donc être sur la bonne voie et allons mettre en places d'autres éléments afin de tenter de réussir à faire rouler cette voiture en autonomie.
 +
 
 +
*'''Test 2 : Les 5 directions'''
 +
**'''Moyens mis en oeuvre : '''Comme expliqué en semaine 14 dans nos hypothèses sur les causes de notre échec, nous tentons donc de recommencer notre démarche en 5 directions. Pour ce faire il nous a fallu adapter tout le code en conséquence, ainsi que rajouter l'usage du joystick pour la phase de conduite manuelle. On a donc nos 3 codes principaux réadaptés : <code>manual_drive5.py</code>, <code>traitement5.ipynb</code> et <code>auto_drive5.py</code>. Nous pensons notamment que cet ajout permettra à la voiture de faire la distinction entre les virages réels et rectifications de trajectoire ou faible virages, en adaptant le rayon de braquage en conséquence.
 +
**'''Résultats : '''Cette fois-ci nous sommes loin d'une amélioration, c'est même bien pire qu'avant. En effet même si la précision du modèle n'a que légèrement baissé sur le notebook, une fois sur la piste en mode automatique, la trajectoire de la voiture est purement aléatoire. Si nous ne nous attendions pas à un tel raté, connaissant le fonctionnement du programme, il faut bien admettre que la non amélioration de la précision en utilisant 5 directions était un résultat prévisible. En effet il n'y avait pas de réelles raisons que changer la sorti de taille 5 en sortie de taille 3 affecte grandement le programme. De plus, utiliser 5 directions nécessite encore plus de rigueur lors de la conduite pour assurer un dataset utilisable.
 +
<gallery mode="packed-hover" heights="300px"  caption="Configuration des boutons">
 +
ManetteXbox2.jpg 
 +
</gallery>
 +
 
 +
 
 +
*'''Test 3 : Qualité du jeu de données'''
 +
**'''Moyens mis en oeuvre : '''Cette fois-ci, afin de compléter le premier test, nous réutilisons notre programme 3 directions, et choisissons de travailler la qualité de notre jeu de données afin d'être sûr qu'aucune image ne soit mal labellisée et que les images soient le plus précis possible. Pour ce faire nous avons donc amélioré l’arrondi de notre virage afin que l'écartement entre les 2 lignes soit, à quelques détails près, toujours le même. De plus nous avons ajouté des pointillés d'une couleur différente des bandes (bandes blanches et pointillés verts), pour aider la voiture à se repérer par rapport au centre de la piste. Enfin, après la phase de conduite manuelle durant laquelle nous avons pris soin de parcourir le virage dans les 2 sens et sous différents angles, nous avons inspecté le dataset image par image, en éliminant chaque image mal labellisée suite à une erreur de conduite de notre part par exemple. Nous avons donc en main en dataset d'un peu plus de 500 images (près-augmentation) pour un unique virage, et d'une qualité irréprochable.
 +
**'''Résultats : '''Après entrainement du réseau CNN sur Google Colab, les résultats sont impressionnants : On voit clairement sur le graphe affichant l'évolution de la précision du CNN qu'après 50 epochs, celle-ci fini par frôler si ce n'est atteindre le 100% de précision. Résultat confirmé lors de la validation du modèle : <code> 187 / 187 correctement prédits <=>  100.000000 % de réussite </code>. Toutefois, sur le circuit les résultats ne sont toujours pas satisfaisants.
 +
<gallery mode="packed-hover" heights="300px">
 +
Polypiste2.jpg | Virage amélioré
 +
Resultat100.png | Graphe de la précision obtenu
 +
</gallery>
 +
 
 +
 
 +
===Résolution du problème et résultat final===
 +
 
 +
Après le dernier test, nous sommes en réalité tombés à court d'idées : comment pouvait-on obtenir un réseau de neurones précis à presque 100% et n'observer aucun résultat concret lors de la phase d'auto-pilotage. En effet nous avons pourtant relu notre programme et avons dressé ce constat :
 +
*Le contrôle des moteurs suit le même principe que pour la phase manuelle et se situe dans la même boucle que la camera, sans délais. De toute façon même si la voiture n'agit pas comme attendu, les moteurs réagissent à la perfection aux ordres donnés. Il n'y a donc pas de problèmes à ce niveau.
 +
*La capture d'images se fait suffisamment rapidement, voire même plus vite qu'en mode manuel puisqu'on ne sauvegarde pas les images. Ici non plus, pas de problème concernant la fluidité de la capture.
 +
*La fonction de prédiction de la commande à réaliser en fonction de l'image et du modèle est la même que celle utilisée sur le notebook pour la validation du modèle, elle ne présente pas non plus de délais d’exécution particulièrement longs et, même si les versions de python et keras utilisées différent entre le code d'auto-pilotage du raspberry et celui de validation du modèle sur Google Colab, il est fort peu probable que cela impacte le fonctionnement puisque le modèle (.h5) ne change pas d'architecture d'une version à l'autre.
 +
 
 +
Pourtant malgré cela, force est de constater que le problème vient de la fonction <code>auto_drive.py</code> ou de <code>pivideostream.py</code> dont elle dépend. Et c'est à ce moment que nous avons compris d'où venait le problème, il se situe dans <code>pivideostream.py</code>, qui contient une fonction chargée de capturer des images à très grande vitesse (sans les enregistrer) en parallèle, et de retourner l'image capturée la plus récente à chaque tour de la boucle de l'auto-conduite. En effet notre picamera est montée à l'envers pour une question pratique à cause de la petite taille des câbles reliant la camera au raspberry. Il est donc nécessaire de rajouter la ligne de code suivante afin d'avoir une image droite.
 +
camera.rotation = 180
 +
Chose que nous avions faite dans <code>manual_drive.py</code> mais pas dans <code>pivideostream.py</code>...
 +
 
 +
Nous devons admettre que jamais nous n'avons pensé à ce détail, et à aucun moment depuis 2 semaines nous n'avons pu penser que le problème venait de là. Maintenant qu'on y pense, la voiture qui suit la ligne (cf gif semaine 14) aurait pu nous mettre sur la piste puisqu'elle cherche à se rapprocher de la ligne au lieu de s'en écarter. Mais puisque de temps en temps la voiture prenait un virage plus ou moins correctement, cela nous a induit en erreur et nous avons perdu du temps à chercher des problèmes où il n'y en avait pas.
 +
 
 +
Abstraction faites de notre déception pour tout ce temps perdu, nous avons sans perdre de temps réalisé un nouvel entraînement sur notre virage, dans les règles de l'art, et cette fois-ci : tout fonctionne correctement. La voiture est en effet capable de suivre la piste et a même réussi à suivre la 2ème partie de notre "U" sur laquelle il n'y avait pas de pointillés et où elle n'avait pas été entraînée.
 +
 
 +
<gallery mode="packed-hover" heights="400px"  caption="Modèle enfin fonctionnel">
 +
Reussite2.gif
 +
</gallery>
 +
 
 +
Nous n'irons malheureusement pas plus loin, les 2 jours restants étant consacrés à la vidéo de présentation, à la préparation de l'oral et la rédaction du rapport écrit ainsi que la finalisation du wiki.
 +
 
 +
==Résultat finaux et conclusion==
 +
 
 +
===Usage===
 +
Voici le fonctionnement final de notre projet, en partant du principe que le matériel utilisé est similaire au notre (manette et shield raspberry notamment) :
 +
 
 +
'''Réglages et pré-requis'''
 +
 
 +
*Il est tout d'abord nécessaire de télécharger les prérequis sur la raspberry (et sur le PC en cas de non-utilisation de Google Colab)
 +
*Connecter les moteurs au shield et effectuer les réglages nécessaires. Il est notamment indispensable de déterminer la plage d'angle d'utilisation des servomoteurs : angle min et angle max. Ceci peut être fait à l'aide de <code>servotest.py</code>.
 +
*Il faut désormais régler la camera, c'est à dire son inclinaison, sa hauteur (de manière à voir correctement le circuit) et enfin déterminer si elle est droite ou à l'envers.
 +
*Adapter le fichier constantes.py en fonctions des résultats précédents.
 +
 
 +
'''Utilisation (mode 3 directions)'''
 +
 
 +
*Connecter en ssh la raspberry. L'utilisation d'un écran est également possible mais moins pratique.
 +
*Allumer la manette et lancer sur la raspberry le programme suivant en précisant un délais, 0.1 par exemple.
 +
python3 manual_drive.py <délais de capture en secondes>
 +
*Mettre en marche la voiture, il faut alors activer la capture d'images et réaliser plusieurs tours de piste.
 +
*(optionnel)Pour assurer la qualité du dataset, il est possible d'insister dans les passages difficiles en réalisant plusieurs passages. Egalement on peut ensuite supprimer les images mal labellisées en les contrôlant.
 +
*De préférence sur Google Colab (jupyter également possible, avec un PC puissant), lancer le code <code>traitement.ipynb</code> et uploader le dataset. Suivre ensuite les différentes étapes sur ce code (les fonctions se lancent par "bloc", par simple pression sur l'icone exécuter, une par une et dans l'ordre.
 +
*Télécharger le modèle au format .h5 ainsi obtenu et le mettre sur la raspberry dans le même répertoire que les autres fonctions.
 +
*Poser la voiture sur la piste et exécuter le code d'auto-pilotage en précisant le modèle. Le modèle met une trentaine de secondes à charger.
 +
python3 auto_drive.py <nom du modèle>
 +
*Si tout s'est bien passé la voiture suit désormais la piste toute seule !
 +
 
 +
===Conclusion===
 +
 
 +
Au final notre projet est donc fonctionnel et conforme aux normes du concours Ironcar sur lequel il a été inspiré. La voiture est capable de suivre une piste en autonomie avec une certaine précision après un entraînement à la manette sur cette même piste ou potentiellement une autre dont les caractéristiques sont similaires.
 +
 
 +
Nous regrettons quand même d'avoir perdu autant de temps, en cherchant des solutions à un problème qui en réalité n'existait pas. Cela à cause d'un petit détail qui fut la caméra à l'envers pour la conduite autonome. Nous avions en effet également pour objectif de rajouter de la plus-value à notre projet en lui concevant une carrosserie digne de ce nom, et en cherchant à optimiser le code et la position de la caméra par exemple, afin de gagner en précision et faire le meilleur chrono possible sur un véritable circuit. Ce que nous aurions sans aucun doute réussi à faire si nous avions trouvé la source du problème dès le début.
 +
 +
Néanmoins, nous sommes contents d'avoir fini par résoudre le problème, puisque nous aurions pu ne jamais nous en apercevoir. Nous demeurons également fiers et heureux d'avoir finalement réussi à mener à bien ce projet, qui fut un réel enrichissement et nous a permis de nous familiariser avec le Deep-Learning et quelques notions d'intelligence artificielle. Nous souhaitons bonne chance aux IMA3 et laissons notre code en libre accès en espérant qu'il pourra être utile à de futures générations d'IMA.
  
 
=Documents Rendus=
 
=Documents Rendus=
 +
 +
Rapport de projet : [[Fichier:Rapport_Projet_IMA4_P14.pdf]]
 +
 +
[https://github.com/DejaElem/p14_Dejaegher_Elemva Lien du dépôt GitHub contenant le code de notre projet]

Version actuelle datée du 17 juin 2019 à 21:32


Vidéo HD


Présentation générale

  • Nom du projet : Voiture autonome en modèle réduit
  • Résumé : Le but du projet est de réaliser une voiture de taille réduite, capable de se déplacer en autonomie sur un circuit
  • Etudiants : Hugo DEJAEGHER et Brandon ELEMVA

Description

Notre projet consiste à réaliser une voiture modèle réduit, capable de réaliser des tours de piste en autonomie sur un circuit tracé au sol. Ce projet s'inspire du concours IronCar France, qui a vu le jour très récemment et auquel nous pourrions par ailleurs participer. Notre véhicule autonome sera réalisé à partir d'une voiture radio-commandée à l’échelle 1:16 ou 1:8, modifiée de façon à être contrôlée par une Raspberry pi 3 (et éventuellement une Arduino), connectée à une webcam et à un ordinateur sur lequel le code sera exécuté. Il est à noter qu'aucun capteur ne doit être utilisé, la webcam seule sera utilisée pour identifier le tracé du circuit.

Objectifs

Dans le but de réaliser notre projet nous devrons remplir les objectifs suivants que nous pouvons répartir en 3 parties.

Partie mécanique

  • Réaliser un support pour la webcam qui permette de l'élever à au moins 10 cm du sol (en prenant en compte la taille du châssis)

Partie électronique

  • Les moteurs seront commandés par une carte Arduino
  • La partie Deep Learning et l'analyse des images de la webcam seront traitées par un ordinateur connecté à une RaspBerry pi 3

Partie informatique

  • Faire apprendre au robot un parcours en le pilotant manuellement via une manette
  • Concevoir un programme permettant au véhicule de mémoriser le parcours préenregistré
  • Produire un véhicule prenant des décisions de façon autonome via plusieurs réseaux de neurones : méthode du Deep Learning (ou apprentissage approfondi)

Analyse du projet

Positionnement par rapport à l'existant

Les principaux concurrents de notre projet ne sont autres que les différents participants du concours IronCar, en particulier les gagnants des sessions précédentes, qui partagent par ailleurs leurs codes et liste de matériel sur le site du concours. Ces gagnants s’élèvent au nombre de 2 équipes, qui se partagent le sommet du podium à tour de rôle.

Analyse du premier concurrent

Il s'agit de l'équipe patate 42, gagnante de la course officielle de février 2018 ainsi que des courses d'entraînement de mai et octobre 2018. Les membres de cette équipe sont issus de l'école 42, un établissement supérieur d'autoformation, non reconnu par l’État et dont l'objectif est de former des développeurs. Leur meilleur temps pour un tour est de 25 secondes.

Analyse du second concurrent

Le deuxième concurrent est l'équipe Axionable, composée de membres issus de l'entreprise du même nom, spécialisée en Data Science et Data consulting, basée à Paris. Selon eux, leur force réside dans la qualité des données qu'ils utilisent pour la reconnaissance du circuit (qualité, diversité et labellisation semi-manuelle des photos). Ils ont gagné la course officielle de juin 2018 ainsi que les entraînements de mars et septembre 2018. Leur meilleur temps en un tour est de 29 secondes.

Scénario d'usage du produit ou du concept envisagé

La première étape consiste à poser la voiture sur la ligne départ puis à la piloter manuellement à l'aide d'une manette Xbox. Plusieurs tours de piste sont alors effectués pendant que la caméra de la raspberry prend une photo toutes les 0.1 secondes afin de constituer une base de données. À chaque photo correspond alors un label correspondant à une instruction : "droite", "gauche", "tout droit", "droite serrée" ... . Le but est de prendre un maximum d'images correctement labellisées afin d'affiner la précision de l'analyse du trajet.

Ensuite, ceci étant fait, une analyse de la base de données (les images et leur label) est hautement conseillée afin de corriger d’éventuelles erreurs de labellisation (une image correspondant à un virage nommée "tout droit" par exemple). Ces premières étapes sont primordiales et, associées à un bon code, réduiront considérablement les chances de sortie de piste. Le but étant d'arriver à un résultat égal ou proche de zéro.

Enfin, on peut alors positionner la voiture sur la ligne de départ et lancer le programme de pilotage automatique ainsi qu'un chronomètre pour mesurer sa performance. Le but est de réaliser 3 tours de pistes en moins de temps possible, avec une pénalité de 5 secondes à chaque sortie de piste.

Voyons cela sous un autre angle :

Judith, étudiante en IMA4 à Polytech Lille est une amatrice de course de mini voitures. Elle a participé à certaines de ces compétitions et a souvent fini très bien classée (sans pour autant en gagner). Judith s'intéresse également à l'univers de l'électronique et de l'informatique et souhaiterait pouvoir rendre sa voiture plus autonome car elle trouve répétitif et lassant de devoir piloter son véhicule sur un circuit. En effectuant des recherches, elle entend parler de la compétition IronCar et se dit que ça serait une excellente opportunité pour elle de réaliser son projet de voiture autonome. Alors, elle décide de prendre son ancienne voiture et de remplacer le circuit électrique s'y trouvant par une Arduino, une Raspberry et une webcam. Avec l'aide de ses enseignants et des documents obtenus lors de ses recherches, elle parvient à établir un programme permettant d'effectuer du Deep Learning. Grâce à ce programme, elle parvient à faire apprendre à sa voiture le parcours et l'optimise afin de permettre à son véhicule d'effectuer ce même parcours le plus rapidement possible. Elle espère bien gagner la compétition IronCar cette année.

Réponse à la question difficile

  • Est-il possible de faire les traitements sur la Rpi ou faut-il faire un pré-traitement off line sur un PC ? (quels soft, quels réseaux de neurones ?)

La Raspberry a une mémoire vive de 256Mo. Pour pouvoir réaliser du deep learning, Il faudrait disposer d'un ordinateur avec une mémoire vive suffisamment grande (32 Go sont fortement recommandés). De ce fait un traitement offline sur pc est requis avant de laisser la raspberry prendre le relais. Le langage utilisé sera le Python (langage par défaut de la Raspberry). Dans notre cas, nous utiliserons les réseaux de neurones convolutifs dits CNN (Convolutionnal Neural Network) ou plus précisément de Transfer Learning.

Les CNN sont, les modèles les plus performants pour classer les images. À son entrée, une image sous forme de matrice de pixels à laquelle elle attribue 2 dimensions pour les niveaux de gris et une 3e pour les couleurs RGB. Ils se décomposent en 2 parties :

  • Une partie convolutive : c'est une sorte d'extracteur de caractéristiques des images. En d'autres termes, une image est passée à travers plusieurs filtres d'affilé créant ainsi de nouvelles images appelées cartes de convolutions. Certains filtres intermédiaires réduisent la résolution de l’image par une opération de maximum local. Ainsi, les cartes de convolutions sont mises à plat et concaténées en un vecteur de caractéristiques, appelé code CNN;
  • Une partie perceptron multicouche à laquelle est connecté le code CNN: c'est un type de réseau neuronal formel organisé en plusieurs couches au sein desquelles une information circule de la couche d'entrée vers la couche de sortie uniquement, les couches sont entièrement connectées entre elles : c'est un réseau de propagation (chaque couche est constituée d'un nombre variable de neurones, les neurones de la dernière couche étant les sorties du système global). Les valeurs numériques obtenues sont généralement normalisées entre 0 et 1.
Convolutionnal Neuronal Network

De cette façon une image qui a une profondeur de 3 couches (le nombre 3 correspondant aux 3 canaux RGB) pourra ainsi résulter en une matrice d’une profondeur de 5, si le réseau convolutif est constitué de 5 filtres. Avec la technique du transfert learning, on réduit la complexité du CNN en utilisant des réseaux pré-entraînés (on exploite la connaissance acquise sur un problème de classification général pour l’appliquer de nouveau à un problème particulier).


  • Comment faire pour piloter efficacement les moteurs ?

Pour le contrôle des moteurs, nous utiliserons un shield contrôleur de servos PWM 16 canaux pour Raspberry Pi car elle n'est pas vraiment en mesure de contrôler des servos moteurs continu, ces moteurs nécessitant une impulsion répétitive très spécifique (avec un synchronisation précise) pour leur indiquer la position (l'angle).

Préparation du projet

Cahier des charges

Le véhicule modèle réduit devra entre autre répondre aux critères suivant:

Phase d'apprentissage

  • Pouvoir être conduit par le biais d'une manette sans fil.
  • Capturer une image toute les 0.1 seconde grâce à la camera implantée.
  • Stocker dans une base de données les images capturées et leur assigner un label (un titre faisant référence à une action à effectuer)
  • Appliquer des effets aléatoires aux images (ombres, miroir, luminosité par exemple) pour diversifier la base de données sans allonger la durée d'apprentissage.

Phase de conduite autonome

  • Etre capable de rouler en autonomie sur la même piste que celle où il a réalisé son apprentissage.
  • Reconnaître des virages et des lignes droites plus ou moins grandes et adapter sa vitesse en conséquence.
  • Détecter une sortie de piste sans avoir recours à des capteurs autres que la caméra.
  • Pouvoir faire exécuter le code de réseau de neurones par l'ordinateur suffisamment rapidement pour pouvoir réagir le plus rapidement possible (proche du temps réel).

De manière générale

  • Prévoir un support solide afin que la caméra ne bouge et ne tombe pas suite à une secousse.
  • Etre capable de communiquer sans fil avec un ordinateur, de manière fiable par le biais de la raspberry.

Choix techniques : matériel et logiciel

Logiciel

Contrôle des moteurs :

Nous avons choisi de ne pas utiliser d'arduino en complément du raspberry puisque celui-ci prendrait de la place et nous obligerait à rajouter des câbles et à nous doter d'une meilleure alimentation externe.

De plus, il ne présente pas d'avantages particuliers en comparaison avec un raspberry pi 3 doté d'un module pour le contrôle des servomoteurs.

En effet comme nous l'avons dit dans la réponse à la question difficile, un raspberry seul ne permet pas un bon contrôle de plusieurs servomoteurs mais l'utilisation d'un Hat PWM permet de régler efficacement le problème.

Partie informatique :

Le choix du python comme langage de programmation semble le plus indiqué dans le cadre d'un réseau de neurones. En effet même s'il est loin d'être "le plus rapide", la bibliothèque Numpy lui permet de rester compétitif.

Mais c'est surtout sa syntaxe facile et concise qui nous permettra de progresser plus rapidement et aisément que dans d'autres langages. C'est d'ailleurs un langages très utilisé dans les applications relatives à l'intelligence artificielle.

En outre, en utilisant Python, nous sommes certains de trouver de nombreuses librairies qualitatives et de la documentation. On peut aussi souligner qu'il s'agit du langage de base de la Raspberry et que des bibliothèques Python sont fournies avec le Hat PWM pour le contrôle des moteurs.

Liste du matériel

  • 1 Monster Truck radiocommandé électrique à l’échelle 1/10 de la marque T2M (commande passée en avance par les enseignants référents).
  • 1 manette de Xbox(one ou 360) sans fil (pour la phase d'apprentissage).
  • 1 Raspberry pi 3 [1].
  • 1 ordinateur/PC doté de suffisamment de RAM pour exécuter le code du réseau de neurones.
  • 1 camera pour raspberry à objectif "fisheye" et 10 fps grand minimum [2].


  • 1 set de jumpers mâle/femelle pour breadboard (pour relier les moteurs au shield du raspberry)[3].
  • 1 batterie externe capable de fournir 5V et au moins 2A pour l'alimentation de la raspberry[4].
  • 1 cable USB/micro USB pour relier la raspberry au pc.

Liste des tâches à effectuer

Partie mécanique

  • Réaliser un support par impression 3D pour la webcam qui permette de l'élever à au moins 10 cm du sol mais également de la protéger. Fixer ce support sur la partie avant de la voiture.

Partie électronique

  • Raccorder les moteurs à la Raspberry pi en les connectant au shield qui sera posé sur celle-ci.
  • Connecter une batterie auxiliaire de 5V et 2A à la Raspberry pour l'alimenter (la puissance délivrée par la batterie du véhicule n'étant pas assez grande pour garantir que les moteurs tourneront bien à pleine puissance tout en alimentant la carte).

Partie informatique

  • Configurer la Raspberry Pi (installation des librairies, configuration des ports de carte pour le shield, paramétrage de la caméra).
  • Apprentissage préalable du parcours par le robot : grâce à une manette de type Xbox 360, effectuer un tour du circuit en pilotant le robot. Durant ce tour, la caméra placée sur le robot prendra plusieurs captures d'image (1 image toutes les 100 millisecondes). La manette ne sera utile que pour le tour d'apprentissage. Pour la suite du travail, nous ferons sans elle.
  • Stocker les images capturées et les différentes positions prises par le joystick de la manette lors de ce tour d'apprentissage que nous associeront à l'image qui leur correspond dans une base de données ou un tableau labellisé.
  • Établir un programme de Deep Learning nous permettant d'analyser chaque image, ou plus précisément la position de la ligne du parcours sur l'image, vérifier la position du joystick à cet instant, et y associer une position du servomoteur servant à la direction des roues avant.
  • Effectuer une série de plusieurs test sur circuit (sans manette) en vue de valider ou non notre modèle. Si valide, l'optimiser pour le rendre plus précis.

Calendrier prévisionnel

Réalisation du Projet

Feuille d'heures

Tâche Prélude Heures S1 Heures S2 Heures S3 Heures S4 Heures S5 Heures S6 Heures S7 Heures S8 Heures S9 Heures S10 Heures S11 Heures S12 et 13 Heures S14 Heures S15 Total
Analyse du projet (recherche des concurrents, descriptions des objectifs, scénario d'usage, matériel) 2h30 1h
Préparation de l'oral 2h 4h 6h
Remplissage du wiki (réponse à la question difficile, préparation du projet, choix techniques et matériels) 1h30 30 min 30 min 1h 30 min 1h 1h 30 min 30 min 30 min 30 min 30 min 5h 6h 19h30
Installation de l'OS sur la Rapberry / établissement de la communication entre la Rpi et une manette Xbox one 3h 3h
Travail sur la prise d'image avec la Rpi 2h 2h 4h
Réception et traitement des données provenant de la manette 2h 3h 2h 7h
Gestion des moteurs du véhicule 3h30 2h 5h30
Initiation au Deep Learning 4h 3h 3h 10h
Prise en main de Keras/Tensorflow, création et test des datasets 4h 4h 4h 4h 16h
Modélisation 3D 2h 2h
Test et travail sur la phase de prétraitement des données via la platforme Google Colab 3h 3h 3h 3h 3h 14h 2h 31h
Test sur circuit (prise d'image, entraînement, puis pilotage autonome) 4h 10h 14h
Rédaction du Rapport 8h 8h

Prologue

Semaine 1

Nous avons débuté par une phase de recherche autour de la communication entre la Raspberry Pi et la manette Xbox : nous nous sommes penchés sur le programme qui nous permettra de récupérer les données des différents actionneurs (boutons et joysticks) de la manette. Pour le choix de la manette, nous avons pris une Wiimote car elle dispose d'un module Bluetooth intégré facilitant la communication avec la Rpi 3 qui dispose aussi, par défaut, d'une interface Bluetooth.

Mais avant cela, nous avons commencé par configurer (installation de l'OS) la Rp1. Pour connecter en série une Raspberry Pi à un PC, il faut connecter le fil noir (la masse) à la pin 6 (GND), le jaune (TX) à la pin 8 (RX Raspberry) et le orange (RX) à la 10 (TX Raspberry).

Une autre option consiste à connecter la Raspberry sur un écran afin d'accéder directement à son interface graphique, choix que nous avons fait afin de gagner du temps. Une fois la configuration effectuée, via le le terminal de la Rpi, nous avons établi la communication entre la Rpi et la Wiimote. La Rpi étant une interface "maître" et la Wiimote une interface "esclave", la communication est possible. Un smartphone étant une interface "maître", la connexion est impossible avec la Rpi, donc inutile d'effectuer des test avec un smartphone.

Suites aux essais effectués par nos collègues en 5A, nous avons découvert que le contrôleur moteur n'est pas une PWM comme prévu mais un modulateur de fréquence. Cela nous a ramené à la question de l'utilité du shield moteur commandé pour la Rpi qui, lui, est un shield PWM. Nous maintenons cependant ce choix.

Prochaine étape, récupérer et stocker les données concernant les boutons de la Wiimote (s'ils sont pressés ou non) sur la Rpi.

Semaine 2

Missions effectuées

  • Récupération des adresses de chaque bouton de la manette Xbox sur la Raspberry. Nous avons trouvé une librairie Python nous permettant d'utiliser notre manette Xbox One sans fils : evdev. Cette librairie permet de récupérer chaque événement associé à un périphérique en particulier (bouton pressé/relâché, mouvement du Joystick et etc)

import evdev

  • Activation de la caméra de la Raspberry et prise en main des fonctions Python de la bibliothèque Picamera dans l'optique de réaliser une prise d'images toutes les 100ms, au format adapté. Pour l'instant, nous sommes capable de prendre des photos dans une boucle infini, en leur donnant un nom arbitraire et en les stockant dans un dossier défini.

from picamera import PiCamera

  • Initiation au Deep learning. Suivre le lien [5] pour avoir accès à des cours et des travaux pratiques centrés sur l'apprentissage approfondi.
  • Pour installer Anaconda : [6]
  • Pour installer PyTorch : [7]

Notes

Les étudiants d'IMA5 utilisent la voiture radiocommandée dont nous avons également besoin pour travailler et nous ne pourrons par conséquent pas l'utiliser avant un certain temps. Heureusement, nous possédons une voiture radiocommandée personnelle que nous pourrons utiliser afin d'avancer correctement sur notre projet malgré cela. Cette voiture est un modèle assez différent : plus rapide mais moins maniable, elle ne correspond pas à nos besoin. De plus, suite à un léger accident, elle ne roule plus très droit non plus. Cependant, le montage électrique est le même et on peut accéder facilement aux différents câbles pour contrôler les moteurs. Elle sera donc bien suffisante pour réaliser nos premiers tests.


Semaine 3

Missions effectuées

  • Nous avons reçu le shield raspberry pour le contrôle des servomoteurs ( Adafruit 16-Channel 12-bit PWM/Servo HAT ). Les différents headers (2x20 pour le raspberry et 4 3x4 pour les câbles PWM) n'étant pas montés, nous les avons soudés. Puisque nous utiliserons uniquement 2 servomoteur, nous n'avons soudé qu'un seul des quatre 3x4 headers mâle. Nous avons également soudé le bloc serre-fils à vis pour l'alimentation du shield en 5V (nous avions prévu un boitier à 4 piles), mais nous nous rendrons compte plus tard qu'il ne nous servira pas en réalité.
Schéma type du câblage dans une voiture RC
  • Analyse de la bibliothèque python fournie par Adafruit, et des différentes fonctions qu'elle offre pour le contrôle des servomoteurs.
import board
import busio
import adafruit_pca9685
i2c = busio.I2C(board.SCL, board.SDA)
hat = adafruit_pca9685.PCA9685(i2c)
  • Test du Shield PWM à l'aide d'un simple programme python et d'un servomoteur.
  • Tests sur le servomoteur de direction et le contrôleur de vitesse de la voiture RC.

Après avoir effectué différents tests, nous nous sommes rendus compte que, bien que l'on soit en mesure de contrôler la direction parfaitement, on arrive seulement à faire tourner le moteur principal en plein régime, et seulement en marche avant. Puisque la voiture est incontrôlable en plein régime et que, même si on pourrait se passer de la marche arrière, il nous faut de toute manière pouvoir la faire rouler le plus lentement possible. C'est pourquoi, afin d'identifier le problème, nous décidons d'aller faire quelques analyses à l'analyseur de spectre, afin de comprendre l'allure des signaux supposés être reçus par le contrôleur de vitesse.

Semaine 4

Résultats des tests de la semaine dernière

Les tests réalisés à l'analyseurs de spectre la semaine dernière nous ont montré la forme des signaux PWM lors de la position repos, marche avant et marche arrière de la voiture, ainsi que pour les directions (signaux non photographiés). Nous avons alors réalisé qu'il s'agissait exactement des même signaux, ce qui était en réalité prévisible, mais cela nous a permis de comprendre notre erreur. Nous utilisions :

kit.continuous_servo[1].throttle = 1      //1 correspond à la vitesse max, -1 à la vitesse max dans l'autre sens et etc

au lieu de :

kit.servo[1].angle = 120       //120 est un angle choisi arbitrairement (entre 0 et 360)

En effet, la fonction kit.continuous_servo[].throttle sert à faire tourner un servomoteur de manière continue. Hors, le moteur que nous cherchons à contrôler n'est pas un servomoteur. Il s'agit d'un moteur contrôlé par le contrôleur moteur de la voiture (cf schéma Semaine 3), qui lui même reçoit un ordre du récepteur fm (ou du shield dans notre cas). Cet ordre n'est autre qu'un signal PWM classique, de la même forme que celui reçu par le servomoteur de la direction. C'est pourquoi en utilisant la même fonction que pour la direction (kit.servo[].angle), nous sommes désormais capables de commander notre voiture correctement.


La manette utilisée

Autres tâches réalisées

  • Création d'un code ayant pour fonction de déterminer les angles des servomoteurs correspondant aux positions de repos, marche avant/arrière et direction gauche et droite. Nous avons pris soin de détacher la tige de direction du servomoteur afin de ne pas endommager la voiture en cas de saisie d'un angle trop éloigné. En effet si par exemple la plage de l'angle pour la direction varie de 60 à 140 par exemple (60 gauche absolue, 100 tout droit et 140 droite absolue), rentrer un angle de 280 et exécuter le code pourrait potentiellement causer des dommages au servomoteur alors bloqué ou aux autres pièces de la voiture.
python3 servotest.py
  • Réalisation d'un code combinant le contrôle des moteurs et la lecture de l’état des boutons de la manette Xbox, à l'aide des librairies présentées précédemment. Nous sommes alors en mesure de contrôler les moteurs de la voiture avec la manette Xbox, par l’intermédiaire du raspberry avec le PWM hat.
  • Suite de l'entraînement sur les réseaux de neurones : création d'un réseau test. Ce réseau avait pour objectif de classer des séries de 4 images dans 10 catégories prédéfinies selon le contenu de celles-ci (chien, chat, avion, voiture...) et d'estimer le pourcentage de précision du réseau ainsi que le temps de traitement approximatif (4 min environ pour une série de 4 images).



Problèmes à résoudre Quelques problèmes de praticité subsistes concernant le code du contrôle manuel de la voiture :

  • A chaque connexion de la manette Xbox, le numéro d’événement est susceptible de changer et il faut alors changer la ligne de code associé pour que cela fonctionne. Cela ne devrait pas poser trop de problèmes à résoudre en cherchant un peu dans la bibliothèque evdev.
...
gamepad = InputDevice('/dev/input/event3') 
...
  • A chaque redémarrage de la raspberry, il faut exécuter une ligne de code pour désactiver l'ERTM (Enhanced Re-Transmission Mode a L2CAP Bluetooth stack feature) sur la raspberry, sans quoi la manette ne peut se connecter. Aussi bête ce problème puisse paraître, nous avons chercher sur de nombreux forums et aucune des solutions proposées n'ont fonctionné. Ce mode ERTM se réactivant à chaque redémarrage, nous forçant à exécuter cette commande dans le terminal :
sudo bash -c 'echo 1 > /sys/module/bluetooth/parameters/disable_ertm'

Semaine 5

  • Élaboration d'un dataset d'images (on a utilisé des images aléatoires sur internet) et test du programme python créé avec ce set d'images : test peu concluant, le problème vient de l'outil utilisé à savoir Tensorflow. Bien que très pratique en terme de traitement d'image, Keras semble être une bonne alternative en terme de facilité de prise en main et de rapidité de traitement de calcul. Nous allons donc nous tourner vers Keras.
  • Nous nous sommes aperçu que quelque chose que nous pensions acquis ne fonctionnait pas comme prévu : notre système de capture d'images ne franchissait pas le seuil des 2 images par seconde, malgré son exécution dans une boucle infinie sans délais, ce qui n'est pas du tout acceptable. Nous avons donc du revoir notre code afin de palier à ce problème pour de bon, ce que nous avons rapidement réussi à faire. Cependant, il nous a fallu installer Pillow, une bibliothèque pour le traitement d'image, ce qui nous a fait perdre pas mal de temps puisque celle-ci a mis beaucoup de temps à se compiler sur la raspberry (alors que 5 minutes suffisent sur un ordinateur).
sudo pip3 install Pillow
  • Nous avons finalement pu résoudre les quelques problèmes décrits en semaine 4
    • Nous avons ajouté quelques lignes pour trouver le numéro de l’événement associé à la manette. Comme prévu cela n'a pas posé trop de difficultés.
    • Le deuxième problème en revanche nous a fait perdre pas mal de temps. Il paraissait si ridicule et pourtant aucune des solutions que nous trouvions ne fonctionnait, jusqu'à un moment donné où nous avons trouvé quelqu'un qui avait exactement le même problème et avait fini par trouver une solution(créer un nouveau fichier composé d'une seule ligne), que voici:
sudo nano /etc/modprobe.d/bluetooth.conf
options bluetooth disable\_ertm=Y

Semaine 6

  • Création et impression du support 3D de la Rpi que l'on fixera sur le véhicule (edit : il ne sera finalement pas utilisé) .
  • Suite aux difficultés rencontrées lors des précédents essais, on a décidé de passer par l'outil Keras. L'outil est assez pratique à utiliser mais un peu plus complexe à prendre en main (sans trop de difficulté). Conception du code et test de lecture d'un dataset. Quelques erreurs apparentes au niveau de la compilation à résoudre.
  • Cette semaine n'a pas été très productive puisque nous avons perdu beaucoup de temps à nous renseigner sur les réseaux de neurones et a tenter de comprendre leur fonctionnement en détail afin de réaliser le notre par la suite. Toutefois, il s’avère que cette tâche n'est pas si facile et même les IMA5 ayant suivi 1 semestre de cours sur le sujet ont éprouvé des difficulté à en réaliser un eux-même. Nous avons donc discuté avec nos encadrants qui nous ont affirmé qu'il est peu probable que cet objectif soit réalisable par nos soins. Nous nous sommes donc renseignés et avons effectivement découvert que les gagnants des années précédentes du concours Ironcar avaient utilisé des modèles de réseau de neurones développés par Nvidia. Nous avons donc abandonné cette idée et allons désormais nous concentrer sur le reste du code.

Semaine 7

Cette semaine, alors que nous nous apprêtions à installer de nouvelles librairies pour Python, nous avons découvert qu'il existait un moyen bien plus simple pour réaliser notre code de la partie ordinateur (notamment traitements sur le dataset et entrainement du réseau de neurones) : Google Colaboratory, un environnement basé sur Jupyter notebook, exécuté dans le cloud.

En effet, Google Colaboratory présente plusieurs avantages :

  • Il est facile à prendre en mains.
  • Les librairies utiles au deep learning sont déjà installées et prêtes à être utilisées.
  • On est libre de choisir un environnement en Python 2.7 ou 3.5.
  • On peut visualiser les résultats en temps réel.
  • On dispose de près de 16 Go de RAM pour exécuter notre code, et on peut donc continuer notre travail depuis n'importe quel PC.

Le seul inconvénient que l'on pourrait citer est le fait de devoir uploader notre dataset d'images en ligne et, selon sa taille et la connexion disponible, cela est susceptible de prendre du temps. Mais soyons honnêtes, il s'agit d'un moindre mal comparé aux perspectives que nous offre un tel outil.

Cette séance a donc été consacrée à la prise en main de Google Colab et au test du code que nous avions réalisé jusqu'à présent, sur cette nouvelle plateforme.

Semaine 8

  • Cette semaine, les étudiants en IMA5 ayant fini leur projet, nous avons pu enfin accéder à la voiture radiocommandée de Polytech. De plus, nous avons obtenue une batterie externe de 20000 mAh, ainsi que la camera grand angle que nous avions commandée. Nous avons donc pu réaliser les mesures du châssis de la voiture afin de pouvoir réaliser la structure pour fixer la raspberry, la batterie et la caméra.
  • Une fois les mesures réalisées, nous avons décidé de réaliser cette structure en Lego (abandonnant ainsi le support pour raspberry imprimé la semaine précédente), en nous inspirant des boîtiers de raspberry Lego disponibles dans les salles E300 de Polytech. Ainsi, en utilisant les bonnes pièces, nous avons été en mesure de réaliser une structure sur laquelle nous pouvons disposer le raspberry et son shield, la batterie externe, ainsi que la camera grand angle. Cette structure en Lego, même si elle ne le paraît pas aux premiers abords, est relativement solide, ne nous a nécessité aucune vis ou point de colle et de plus, il est possible d'ajuster la position de la camera à volonté, grâce à un piston à vis Lego Technic. Il faut aussi ajouter que le fait d'avoir réalisé cette structure en Lego nous permet de la rendre extrêmement modulable (faudrait il changer la position des pièces ou bien changer la batterie), et nous laisse de vastes perspectives en ce qui concerne l'aspect personnalisation.

Semaine 9

  • Nous avons finalisé la réalisation de la structure supportant la batterie, le raspberry et la camera, en y perçant des trous de manière à ce qu'elle puisse se fixer directement sur le châssis de la voiture à l'aide des clips métalliques, de la même manière que la carrosserie fournie avec la voiture.
  • Nous avons ensuite adapté le code du contrôle manuel de la voiture à l'aide de la manette Xbox, puisque les moteurs diffèrent de ceux de notre voiture personnelle, que nous avons utilisé jusqu'à présent pour pouvoir avancer malgré la non-disponibilité de la voiture RC de Polytech dédiée aux projets. Nous avons donc réutilisé notre bonne vieille fonction de test de servomoteurs afin de vérifier leur bon fonctionnement et de déterminer la nouvelle plage de variation des angles, que nous renseignons dans un fichier : constantes.py. Cette étape ne nous a pas pris beaucoup de temps, puisque nous connaissions déjà la démarche à suivre.
  • Nous avons ensuite testé le bon fonctionnement de tout cela avec un bref test de conduite à la manette de Xbox.
Entrainement de notre réseau


  • Coté réseau de neurones : nous avons établi un programme permettant d'effectuer la phase de prétraitement des images en leur affectant un label à chacune. Le programme semble fonctionnel et prêt à l'emploi. Nous étant basé sur un set existant, il ne reste plus qu'à l'appliquer sur notre dataset.
  • Suite aux résultats peu satisfaisants obtenus lors de la précédente séance, nous avons effectué un nouvel entraînement sur notre réseau de neurones en nous inspirant d'un modèle existant. Le but de la manœuvre était de voir le temps mis par le PC pour entraîner notre réseau de neurones en vue d'une possible optimisation de ce modèle. Pour cela, nous avons effectué une série de 10 entraînements à la suite et, globalement, nous avons observé un temps de calcul d'à peu près 5 min pour chaque entraînement ce qui est assez long.

Semaines 10

Cette semaine nous avons de nouveau été amené à modifier notre code concernant le pilotage manuel de la voiture. En effet c'est lorsque nous avons voulu définir la forme du nom qui allait être donné à chaque photo prise par la camera, que nous nous sommes rendu compte que faire tourner la fonction de la camera et celle du contrôle de la voiture dans 2 codes séparés n'était pas une idée brillante...Puisque nous avons besoin de connaître l’état des boutons (ou des moteurs) pour labelliser correctement notre photo. Nous avons donc réalisé les tâches suivantes :

  • En utilisant des threads python nous avons regroupé les 2 codes en un seul.

from threading import Thread

  • Nous en avons profité pour apporter les dernières améliorations à ce code, afin de le rendre parfaitement fonctionnel. Voici désormais son usage :
    • On lance le code avec la commande :
python3 auto_datamining.py <délais de capture en seconde>
    • Si aucune manette Xbox n'est connectée en bluetooth au raspberry, le code se ferme immédiatement. Sinon, on peut piloter la voiture avec la manette.
    • On presse Y pour activer la capture d'images. Des images pourront alors être prises selon le délais renseigné en paramètre (max environ 14 photos/seconde).
    • Puisque dans le cadre de notre usage il n'existe pas de cas où la voiture n'avance pas, la capture s'effectue uniquement quand la voiture est en mouvement.
    • Les images sont nommées en fonction de la vitesse (inutile pour l'instant car 1 seule vitesse, mais possibilité de changer par la suite),de la direction (gauche, droite, tout droit) et du temps courant.
  • Nous avons commencé à réaliser le code pour la conduite automatique du véhicule et téléchargé les librairies nécessaires à son exécution sur le raspberry. Nous ne pourrons cependant le tester qu'après avoir entraîné la voiture, ce sera donc pour les semaines à venir.


Semaine 11

Nous avons profité de cette semaine pour avancer sur la partie de code à exécuter sur Google Colab. Puisque le code de récupération de données en conduite manuelle est désormais 100% opérationnel, nous pouvons parfaire et tester le bon fonctionnement du près-traitement des données et celui de l'entraînement du réseau de neurones. Nous avons donc réalisé les tâches suivantes :

  • Ajout du code capable d'uploader notre dataset sur le Notebook de Google Colab.
!rm *.zip         //pour éviter les conflits 
from google.colab import files  
files.upload()    
!unzip image.zip 
dataset = "image" 
  • Rédaction d'une fonction qui, en fonction de leur nom, convertit les photos en 3 tableaux numpy (X : l'image, Y : la direction, Z : la vitesse (inutilisée pour l'instant, mais sera comme cela exploitable en cas de besoin) )
def load_photos(dataset)
    ... 
    return X, Y, Z
  • Le réseau de neurones CNN de Nvidia que nous avons récupéré fonctionnant pour des labels 5 directions, nous l'avons rapidement modifié pour l'adapter à nos labels 3 directions.
  • Nous avons tester le bon fonctionnement avec un dataset enregistré depuis notre voiture. Bien sûr ce dataset a été pris depuis notre salle de travail simplement pour vérifier que le code compile bien et il ne correspond donc à rien de concret en réalité.


Notes : Si on ne dispose pas d'une interface graphique (un écran à brancher sur le raspberry), voici la démarche à suivre pour récupérer les images de le raspberry depuis un ordinateur sur le même réseau:

  • Avec clé usb :
> ssh pi@<IP raspberry>
> <mot de passe>

Puis avec mv -r ou cp -r on déplace le dataset sur la clé USB, pour le récupérer sur un pc par la suite.

  • Sans clé usb :
> scp -r pi@<IP raspberry>:~/Desktop/image .  #par exemple
>  <mot de passe>

Semaines 12 et 13

Nous avons profité des vacances de printemps pour travailler sur notre projet et améliorer drastiquement notre code, notamment la partie ordinateur (Google Colab). Aussi avons nous réalisé les choses suivantes :

  • Ajout d'une partie dédiée à l'augmentation de la quantité de données pour l'entraînement du CNN. Pour l'instant elle n'est composée que de 2 fonctions : une qui double notre jeu de données en réalisant un effet miroir sur les images à l'aide de openCV, et une autre qui ajoute des effets de luminosité aléatoires (utilisant également openCV). Cependant on peut aisément ajouter de nouvelles fonctions si nous en trouvons d'autres qui pourraient être intéressantes (d'autres effets sur la luminosité, ajout d'ombres aléatoires ...). Notons que la fonction miroir est probablement la plus intéressante puisqu'elle permet de ce fait d'avoir exactement le même nombre d'images correspondant à des virages à droite que d'images correspondant à des virages à gauche.
def mirror_image(X,Y):
    ...
    return X_mirror,Y_mirror 
def random_brightness(X,Y):
   ...
   return X_bright, Y_bright
  • Ajout de fonctions pour visualiser notre dataset (utile pour tester les fonctions d'augmentation de données).
  • Implémentation d'une partie post-traitement dédiée à la validation du modèle qui affiche notamment l'évolution des performances du réseau CNN à la fin de l'entrainement, et qui offre la possibilité de charger un autre jeu de données afin de tester le modèle (potentiellement une partie du dataset de départ, mais les résultat risques d'être plus optimistes que la réalité), en affichant le pourcentage de prédictions correctes.
  • Le code étant assez imposant, nous avons réorganisé le notebook et ajouté des intitulés et des commentaires afin de s'y retrouver plus facilement. On peut désormais facilement ajouter des fonctions ou modifier le code.


Afin de pouvoir réaliser quelques vérifications sur le code, nous avons rapidement tracé au sol un virage avec du scotch blanc et pris quelques images (programme de capture mais sans la voiture) et nous les avons uploadé sur le Notebook. Nous avions donc un petit dataset de 550 images (sans augmentation) avec le quel nous avons pu vérifier le bon fonctionnement du programme.

Virage de test

Au final, nous avons obtenu le modèle virage_test.h5, et en chargeant d'autres images pour la validation du modèle, nous avons obtenu le résultat suivant :

152 / 163 correctement prédits <=> 93.2515337423 % de réussite

Ceci étant le résultat sans augmentation de données, nous avons donc réitéré l’expérience en utilisant nos 2 fonctions, quadruplant ainsi les données. Résultat :

160 / 163 correctement prédits <=> 98.1595092025 % de réussite

On constate l'importance de la qualité du jeu de données. Dans tous les cas, les résultats sont très encourageants pour un simple test, avec bien moins de 10% d'erreurs.

Semaine 14

En cette semaine de reprise, nous avons donc décidé de réaliser un essai sur une piste avec la voiture, en utilisant le code que nous avons fini pendant les vacances. Nous avons donc acheté des rouleaux de scotch blanc avec lesquels nous avons tracé un parcours très basique : une simple boucle.

Déroulement du test

Circuit réalisé
  • Réalisation de notre circuit à Polytech, sous la passerelle entre les bâtiments B et E.
  • Nous avons démarré la raspberry et avons connecté en ssh un PC portable à cette dernière.
  • Mise en marche de la voiture et de la manette, puis lancement du programme d'entraînement.
  • Nous avons réalisé 3 tours dans chaque sens, en prenant garde à la qualité de notre conduite, puis avons réalisé quelques passages supplémentaires dans les virages.
  • Un dataset de un peu plus de 1000 images (avant traitement) a ainsi été obtenu, que nous avons chargé sur le notebook Google Colab pour y appliquer les près-traitements et ainsi quadrupler notre jeu de données.
  • Nous avons donc ensuite lancé l'entraînement du réseaux de neurones, ce qui a pris une bonne vingtaine de minutes.
  • Le modèle (fichier au format .h5) a ensuite été récupéré puis transmis à la raspberry pour pouvoir l'utiliser.
  • Enfin, nous avons lancé le code d’auto-pilotage, puis avons posé la voiture sur la piste pour la laisser se diriger toute seule (en restant toutefois très vigilant pour éviter la casse).

Résultats

Malheureusement la voiture ne réagit pas du tout comme prévu et semble effectuer des actions aléatoires. Cependant lorsqu'elle arrive sur une ligne, elle se met à alterner gauche et droite comme si elle essayait de la suivre. De manière générale, l'action "tout droit n'est presque jamais effectuée. Nous avons alors réalisé de une nouvelle tentative en veillant particulièrement à la qualité irréprochable de notre dataset, mais rien à faire, toujours le même résultat. Il faut bien l'admettre c'est de toute évidence un échec cuisant et il va falloir déterminer la source du problème afin de le solutionner.


Analyses

Après ces infructueuses tentatives, nous avons donc réfléchi à quelle pouvait bien être la cause de cet échec. Nous émettons alors plusieurs hypothèses :

  • Premièrement nous nous rappelons que l'architecture du CNN de Nvidia que nous avons récupéré est prévue pour 5 directions (gauche, gauche légèrement, tout droit,... par exemple). Or nous avons adapté sa sortie pour seulement 3 directions. Il se pourrait alors qu'il ne réagisse plus correctement.
  • L'angle de la camera est peut-être mauvais. Mais au vu des résultats, ce détail ne peut pas justifier à lui tout seul le comportement de la voiture.
  • L'absence de pointillés sur notre parcours empêche la voiture de bien se repérer, notamment dans les virages. Encore une fois cela semble peut probable au vu du fait que même une ligne droite n'est pas reconnue.
  • La luminosité extérieure pose problème, à cause des forts écarts d'une minutes à l'autre (avec le passage des nuages par exemple). En effet puisque le réseaux met un peu plus de 20 minutes à s'entraîner, la luminosité a peut être fortement varié et ainsi le réseaux de neurones n'est plus en mesure de reconnaître quoi que ce soit. Sachant que les concours Ironcar se font en intérieur, et que les cameras de raspberry sont très sensibles à la luminosité, c'est la piste de l’éclairage que nous retenons.

Semaine 15 et fin

La semaine 14 étant fortement chargée en travail avec les partiels, nous nous retrouvons donc déjà à la semaine 15. Il ne nous reste donc plus qu'une semaine de travail et 2 jours pour finaliser notre projet, sachant que nous avons également un certain nombre d'examens. Bien décidés à résoudre notre problème une bonne fois pour toute aux cours de cette dernière ligne droite, nous effectuons donc dès que l'occasion se présente et sans perdre de temps des séries de tests et de modifications.

Essais et problèmes concernant la conduite automatique

  • Test 1 : En intérieur
    • Moyens mis en oeuvre : Tout d'abord, nous commençons par suivre la piste que nous avions retenue la semaine dernière et décidons d’effectuer nos tests en intérieur. Le mauvais temps de la semaine dernière a de toute façon totalement ravagé le circuit que nous avions conçu. Avec la permission d'un encadrant, nous nous plaçons donc en E300 dans une salle informatique et traçons de nouveau un circuit, ou plutôt un simple "U" puisque l'espace dans ces salles est assez restreint. De toute manière, pour vérifier le bon fonctionnement du modèle, un simple virage suffit en théorie. Ensuite nous prenons également soin de fermer les stores et les portes, et d'allumer les néons, pour garantir un éclairage uniforme.
    • Résultats : Sur le notebook, on observe que la précisions du modèle s'est amélioré, et sur la piste la voiture semble cette fois reconnaître les lignes droites. Cependant dans les virages les résultats sont très aléatoires. De plus, même en ligne droite le taux d'erreurs est bien supérieur à celui annoncé dans la validation du modèle. Nous pensons donc être sur la bonne voie et allons mettre en places d'autres éléments afin de tenter de réussir à faire rouler cette voiture en autonomie.
  • Test 2 : Les 5 directions
    • Moyens mis en oeuvre : Comme expliqué en semaine 14 dans nos hypothèses sur les causes de notre échec, nous tentons donc de recommencer notre démarche en 5 directions. Pour ce faire il nous a fallu adapter tout le code en conséquence, ainsi que rajouter l'usage du joystick pour la phase de conduite manuelle. On a donc nos 3 codes principaux réadaptés : manual_drive5.py, traitement5.ipynb et auto_drive5.py. Nous pensons notamment que cet ajout permettra à la voiture de faire la distinction entre les virages réels et rectifications de trajectoire ou faible virages, en adaptant le rayon de braquage en conséquence.
    • Résultats : Cette fois-ci nous sommes loin d'une amélioration, c'est même bien pire qu'avant. En effet même si la précision du modèle n'a que légèrement baissé sur le notebook, une fois sur la piste en mode automatique, la trajectoire de la voiture est purement aléatoire. Si nous ne nous attendions pas à un tel raté, connaissant le fonctionnement du programme, il faut bien admettre que la non amélioration de la précision en utilisant 5 directions était un résultat prévisible. En effet il n'y avait pas de réelles raisons que changer la sorti de taille 5 en sortie de taille 3 affecte grandement le programme. De plus, utiliser 5 directions nécessite encore plus de rigueur lors de la conduite pour assurer un dataset utilisable.


  • Test 3 : Qualité du jeu de données
    • Moyens mis en oeuvre : Cette fois-ci, afin de compléter le premier test, nous réutilisons notre programme 3 directions, et choisissons de travailler la qualité de notre jeu de données afin d'être sûr qu'aucune image ne soit mal labellisée et que les images soient le plus précis possible. Pour ce faire nous avons donc amélioré l’arrondi de notre virage afin que l'écartement entre les 2 lignes soit, à quelques détails près, toujours le même. De plus nous avons ajouté des pointillés d'une couleur différente des bandes (bandes blanches et pointillés verts), pour aider la voiture à se repérer par rapport au centre de la piste. Enfin, après la phase de conduite manuelle durant laquelle nous avons pris soin de parcourir le virage dans les 2 sens et sous différents angles, nous avons inspecté le dataset image par image, en éliminant chaque image mal labellisée suite à une erreur de conduite de notre part par exemple. Nous avons donc en main en dataset d'un peu plus de 500 images (près-augmentation) pour un unique virage, et d'une qualité irréprochable.
    • Résultats : Après entrainement du réseau CNN sur Google Colab, les résultats sont impressionnants : On voit clairement sur le graphe affichant l'évolution de la précision du CNN qu'après 50 epochs, celle-ci fini par frôler si ce n'est atteindre le 100% de précision. Résultat confirmé lors de la validation du modèle : 187 / 187 correctement prédits <=> 100.000000 % de réussite . Toutefois, sur le circuit les résultats ne sont toujours pas satisfaisants.


Résolution du problème et résultat final

Après le dernier test, nous sommes en réalité tombés à court d'idées : comment pouvait-on obtenir un réseau de neurones précis à presque 100% et n'observer aucun résultat concret lors de la phase d'auto-pilotage. En effet nous avons pourtant relu notre programme et avons dressé ce constat :

  • Le contrôle des moteurs suit le même principe que pour la phase manuelle et se situe dans la même boucle que la camera, sans délais. De toute façon même si la voiture n'agit pas comme attendu, les moteurs réagissent à la perfection aux ordres donnés. Il n'y a donc pas de problèmes à ce niveau.
  • La capture d'images se fait suffisamment rapidement, voire même plus vite qu'en mode manuel puisqu'on ne sauvegarde pas les images. Ici non plus, pas de problème concernant la fluidité de la capture.
  • La fonction de prédiction de la commande à réaliser en fonction de l'image et du modèle est la même que celle utilisée sur le notebook pour la validation du modèle, elle ne présente pas non plus de délais d’exécution particulièrement longs et, même si les versions de python et keras utilisées différent entre le code d'auto-pilotage du raspberry et celui de validation du modèle sur Google Colab, il est fort peu probable que cela impacte le fonctionnement puisque le modèle (.h5) ne change pas d'architecture d'une version à l'autre.

Pourtant malgré cela, force est de constater que le problème vient de la fonction auto_drive.py ou de pivideostream.py dont elle dépend. Et c'est à ce moment que nous avons compris d'où venait le problème, il se situe dans pivideostream.py, qui contient une fonction chargée de capturer des images à très grande vitesse (sans les enregistrer) en parallèle, et de retourner l'image capturée la plus récente à chaque tour de la boucle de l'auto-conduite. En effet notre picamera est montée à l'envers pour une question pratique à cause de la petite taille des câbles reliant la camera au raspberry. Il est donc nécessaire de rajouter la ligne de code suivante afin d'avoir une image droite.

camera.rotation = 180 

Chose que nous avions faite dans manual_drive.py mais pas dans pivideostream.py...

Nous devons admettre que jamais nous n'avons pensé à ce détail, et à aucun moment depuis 2 semaines nous n'avons pu penser que le problème venait de là. Maintenant qu'on y pense, la voiture qui suit la ligne (cf gif semaine 14) aurait pu nous mettre sur la piste puisqu'elle cherche à se rapprocher de la ligne au lieu de s'en écarter. Mais puisque de temps en temps la voiture prenait un virage plus ou moins correctement, cela nous a induit en erreur et nous avons perdu du temps à chercher des problèmes où il n'y en avait pas.

Abstraction faites de notre déception pour tout ce temps perdu, nous avons sans perdre de temps réalisé un nouvel entraînement sur notre virage, dans les règles de l'art, et cette fois-ci : tout fonctionne correctement. La voiture est en effet capable de suivre la piste et a même réussi à suivre la 2ème partie de notre "U" sur laquelle il n'y avait pas de pointillés et où elle n'avait pas été entraînée.

Nous n'irons malheureusement pas plus loin, les 2 jours restants étant consacrés à la vidéo de présentation, à la préparation de l'oral et la rédaction du rapport écrit ainsi que la finalisation du wiki.

Résultat finaux et conclusion

Usage

Voici le fonctionnement final de notre projet, en partant du principe que le matériel utilisé est similaire au notre (manette et shield raspberry notamment) :

Réglages et pré-requis

  • Il est tout d'abord nécessaire de télécharger les prérequis sur la raspberry (et sur le PC en cas de non-utilisation de Google Colab)
  • Connecter les moteurs au shield et effectuer les réglages nécessaires. Il est notamment indispensable de déterminer la plage d'angle d'utilisation des servomoteurs : angle min et angle max. Ceci peut être fait à l'aide de servotest.py.
  • Il faut désormais régler la camera, c'est à dire son inclinaison, sa hauteur (de manière à voir correctement le circuit) et enfin déterminer si elle est droite ou à l'envers.
  • Adapter le fichier constantes.py en fonctions des résultats précédents.

Utilisation (mode 3 directions)

  • Connecter en ssh la raspberry. L'utilisation d'un écran est également possible mais moins pratique.
  • Allumer la manette et lancer sur la raspberry le programme suivant en précisant un délais, 0.1 par exemple.
python3 manual_drive.py <délais de capture en secondes>
  • Mettre en marche la voiture, il faut alors activer la capture d'images et réaliser plusieurs tours de piste.
  • (optionnel)Pour assurer la qualité du dataset, il est possible d'insister dans les passages difficiles en réalisant plusieurs passages. Egalement on peut ensuite supprimer les images mal labellisées en les contrôlant.
  • De préférence sur Google Colab (jupyter également possible, avec un PC puissant), lancer le code traitement.ipynb et uploader le dataset. Suivre ensuite les différentes étapes sur ce code (les fonctions se lancent par "bloc", par simple pression sur l'icone exécuter, une par une et dans l'ordre.
  • Télécharger le modèle au format .h5 ainsi obtenu et le mettre sur la raspberry dans le même répertoire que les autres fonctions.
  • Poser la voiture sur la piste et exécuter le code d'auto-pilotage en précisant le modèle. Le modèle met une trentaine de secondes à charger.
python3 auto_drive.py <nom du modèle>
  • Si tout s'est bien passé la voiture suit désormais la piste toute seule !

Conclusion

Au final notre projet est donc fonctionnel et conforme aux normes du concours Ironcar sur lequel il a été inspiré. La voiture est capable de suivre une piste en autonomie avec une certaine précision après un entraînement à la manette sur cette même piste ou potentiellement une autre dont les caractéristiques sont similaires.

Nous regrettons quand même d'avoir perdu autant de temps, en cherchant des solutions à un problème qui en réalité n'existait pas. Cela à cause d'un petit détail qui fut la caméra à l'envers pour la conduite autonome. Nous avions en effet également pour objectif de rajouter de la plus-value à notre projet en lui concevant une carrosserie digne de ce nom, et en cherchant à optimiser le code et la position de la caméra par exemple, afin de gagner en précision et faire le meilleur chrono possible sur un véritable circuit. Ce que nous aurions sans aucun doute réussi à faire si nous avions trouvé la source du problème dès le début.

Néanmoins, nous sommes contents d'avoir fini par résoudre le problème, puisque nous aurions pu ne jamais nous en apercevoir. Nous demeurons également fiers et heureux d'avoir finalement réussi à mener à bien ce projet, qui fut un réel enrichissement et nous a permis de nous familiariser avec le Deep-Learning et quelques notions d'intelligence artificielle. Nous souhaitons bonne chance aux IMA3 et laissons notre code en libre accès en espérant qu'il pourra être utile à de futures générations d'IMA.

Documents Rendus

Rapport de projet : Fichier:Rapport Projet IMA4 P14.pdf

Lien du dépôt GitHub contenant le code de notre projet