IMA4 2016/2017 P39 : Différence entre versions
(→Semaine 2) |
|||
(90 révisions intermédiaires par 2 utilisateurs non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
+ | <include nopre noesc src="/home/pedago/pimasc/include/video-ProjecteurLaser-iframe.html" /> | ||
__TOC__ | __TOC__ | ||
<br style="clear: both;"/> | <br style="clear: both;"/> | ||
+ | |||
==Cahier des charges== | ==Cahier des charges== | ||
Ligne 107 : | Ligne 109 : | ||
*Finalisation du cahier des charges avec les responsables | *Finalisation du cahier des charges avec les responsables | ||
+ | |||
*Acquisition de matériels : | *Acquisition de matériels : | ||
**Projecteur Laser | **Projecteur Laser | ||
**Galvanomètres | **Galvanomètres | ||
**Alimentation des galvanomètres | **Alimentation des galvanomètres | ||
+ | |||
*Listage du matériels à acquérir : | *Listage du matériels à acquérir : | ||
**Lunettes de sécurité | **Lunettes de sécurité | ||
Ligne 121 : | Ligne 125 : | ||
*Obtention de l'arduino et de la raspberryPI | *Obtention de l'arduino et de la raspberryPI | ||
+ | |||
*Démontage du projecteur laser | *Démontage du projecteur laser | ||
**Deux lasers de classe 3b (vert et rouge) | **Deux lasers de classe 3b (vert et rouge) | ||
**Circuit d'alimentation des deux lasers | **Circuit d'alimentation des deux lasers | ||
+ | |||
*Initialisation du port série de la raspberryPI | *Initialisation du port série de la raspberryPI | ||
**Modification du fichier ''config.txt'' dans la partition boot | **Modification du fichier ''config.txt'' dans la partition boot | ||
Ligne 130 : | Ligne 136 : | ||
===Semaine 3=== | ===Semaine 3=== | ||
− | *Installation du serveur apache sur la raspberryPI ( | + | *Installation du serveur apache sur la raspberryPI |
+ | **Modification du fichier ''/etc/network/interfaces'' (Static, Address, Netmask, Gateway) | ||
+ | **Modification du fichier ''/etc/resolv.conf'' (Adresse du serveur) | ||
+ | **Utilisation d'un proxy pour la récupération outils de programmation (Apache2, Php5) | ||
+ | |||
*Mise en place du point d’accès wifi de la raspberryPI | *Mise en place du point d’accès wifi de la raspberryPI | ||
+ | **Récupération des logiciels nécessaires (dnsmasq, hostapd) | ||
+ | **Mise en place d'une adresse IP statique pour le point d'accès par l'intermédiaire du fichier ''/etc/dhcpcd.conf'' | ||
+ | **Désactivation de la négociation automatique de bail dans le fichier ''/etc/network/interfaces'' | ||
+ | **Configuration du logiciel hostapd ''/etc/hostapd/hostapd.conf'' | ||
+ | **Activation du démarrage automatique du point d'accès ''/etc/default/hostapd'' | ||
+ | **Configuration du serveur DNS par ''/etc/dnsmasq.conf'' | ||
+ | |||
+ | *Instauration de la page d'accueil de l'interface web à l'adresse ''192.168.3.1'' | ||
+ | **Téléversement de fichiers images sur la page web et affichage de celle-ci | ||
+ | **Voyant de confirmation du bon fonctionnement du serveur web | ||
+ | |||
+ | *Développement du serveur websocket | ||
+ | |||
+ | Le développement du serveur websocket, initialement prévu en C (il était prévu de reprendre les sources utilisées l'année précédente dans le cadre des projet IMA3 SC), a finalement été réalisé en JAVA pour plusieurs raisons. | ||
+ | Premièrement, un seul programme met en œuvre le serveur websocket, les divers traitements d'image, ainsi que les contrôles des galvanomètres (par l'intermédiaire des DAC) via la liaison I²C de la Raspberry Pi. Le langage JAVA fournit nativement des bibliothèques d'objets pour manipuler assez simplement des images, il existe également des librairies pour contrôler le port GPIO de la Raspberry et il était également intéressant de travailler avec un langage de plus haut niveau que le C. | ||
+ | En outre, la totale portabilité de ce langage nous permet de travailler sur n'importe quelle machine et ensuite transférer notre programme sur la Raspberry Pi et l’exécuter sans même repasser par une phase de compilation. Et finalement, c'est le langage que nous étudions actuellement en cours de programmation orientée objet, ce qui permet de nous perfectionner. | ||
+ | |||
+ | |||
+ | ====Serveur WebSocket==== | ||
+ | |||
+ | N'ayant pas trouvé d'API WebSocket en Java satisfaisante (qui ne nécessitent pas l'installation et la configuration de nombreux outils additionnels), j'ai estimé plus commode et plus formateur de développer notre propre serveur WebSocket en se basant sur les librairies sockets de Java avec l'aide d'une documentation assez exhaustive [https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers]. | ||
+ | |||
+ | Les WebSockets désignent à la fois un protocole réseau qui étend le protocole TCP ainsi qu'une API du World Wide Web. Nous nous en servons afin d'établir une communication asynchrone et bidirectionnelle entre un navigateur web et le serveur, notamment pour envoyer l'image à projeter. L’implémentation du serveur repose sur plusieurs étapes. | ||
+ | |||
+ | 1) Connexion d'un client | ||
+ | |||
+ | 2) Handshake : permet de faire le lien entre le protocole HTTP et WS. Le client envoie une requête HTTP demandant un upgrade de la connexion en protocole WS et le serveur répond en configurant la connexion (détail dans la documentation [https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers]). | ||
+ | |||
+ | 3) Réception et décodage des messages : le serveurs reçoit des trames brutes, on extrait donc la taille du message ainsi que le message. De plus, les données son encodées (cryptage XOR) par un masque qui doit également être extrait. | ||
+ | |||
+ | 4) Fermeture de la communication. | ||
+ | |||
+ | |||
+ | Le serveur supporte le multi-client il est donc nécessaire qu'il effectue ces étapes en "parallèle". Nous utilisons donc les threads ou fils d’exécution. Un thread est donc responsable d'attendre qu'un nouveau client se connecte et effectue le "handshake", il crée ensuite un thread associé à ce client afin d'établir un canal de communication. Il y a donc au moins un thread par client. Par la suite il faudra également implémenter une déconnexion automatique d'un client notamment après un timeout. | ||
+ | |||
+ | Exemple d’exécution du serveur avec deux clients qui se connectent et envoient un message : | ||
+ | |||
+ | [[Fichier:testServeur.png]] | ||
+ | |||
+ | |||
+ | Les clients sont des navigateurs web. Pour les tests nous avons repris les exemples utilisés dans le cadre das projets IMA3 SC avec le titre de la page HTML qui se colore en vert lorsque le client est connecté, et un champ de saisie pour envoyer des messages au serveur : | ||
+ | |||
+ | [[Fichier:testClient.png]] | ||
+ | |||
+ | ===Semaine 4=== | ||
+ | |||
+ | *Mise en place d’algorithmes de traitement d'image | ||
+ | |||
+ | ====Détection de contour==== | ||
+ | |||
+ | L'objectif de notre projet est de pouvoir projeter n'importe quelle image envoyé grâce à l'application web. Pour cela nous allons tracer à une vitesse élevée les contours de l'image sur par exemple un mur grâce au laser. Il est donc nécessaire de détecter les contours de l'image grâce à plusieurs traitements. | ||
+ | |||
+ | =====Conversion en niveau de gris===== | ||
+ | |||
+ | Une des étapes préliminaires pour effectuer la détection de contour est de convertir l'image couleur en niveau de gris. Pour cela l'algorithme est simple : il suffit pour chaque pixel de l'image d'effectuer la moyenne des composantes rouge, verte, bleu de chaque pixel. En effet, si l'on considère une image matricielle (l'image est considérée comme un tableau à deux dimensions avec chaque case qui contient la couleur du pixel), chaque pixel contient les composantes RGB codées sur un octet. La couleur gris correspond aux composantes RGB égales. Le niveau de gris est donc codé de 0 à 255, avec 0 donnant du noir et 255 du blanc. | ||
+ | |||
+ | Voici le résultat obtenu : [[Fichier:testGrayScale.png]] | ||
+ | |||
+ | |||
+ | =====Filtre de Prewit et Sobel===== | ||
+ | |||
+ | Un contour est défini par un changement brutal d'intensité lumineuse, il s'agit donc de détecter selon plusieurs directions (au moins 2) ces changements. Il existe de nombreuses techniques afin de détecter les contours d'une image (gradient, Laplacien). L'une des techniques avec un rapport efficacité/temps d'exécution correct repose sur les algorithmes de Prewit ou Sobel. L'algorithme repose sur la convolution de matrices. | ||
+ | Cela consiste à appliquer sur chaque pixel de l'image la matrice de Prewit définie comme | ||
+ | :<math> | ||
+ | L_x = \begin{bmatrix} | ||
+ | -1 & 0 & +1 \\ | ||
+ | -1 & 0 & +1 \\ | ||
+ | -1 & 0 & +1 | ||
+ | \end{bmatrix} | ||
+ | </math> ainsi que sa transposée : <math> | ||
+ | L_y = \begin{bmatrix} | ||
+ | -1 & -1 & -1 \\ | ||
+ | 0 & 0 & 0 \\ | ||
+ | +1 & +1 & +1 | ||
+ | \end{bmatrix} | ||
+ | </math> | ||
+ | |||
+ | Convoluer ces deux filtres sur l'image permet de détecter respectivement les contours verticaux et horizontaux. | ||
+ | |||
+ | D'après wikipedia [https://fr.wikipedia.org/wiki/D%C3%A9tection_de_contours], on obtient de meilleurs résultats en appliquant un filtre triangulaire, on introduit donc la matrice de Sobel : | ||
+ | :<math> | ||
+ | L = \begin{bmatrix} | ||
+ | -1 & 0 & +1 \\ | ||
+ | -2 & 0 & +2 \\ | ||
+ | -1 & 0 & +1 | ||
+ | \end{bmatrix} | ||
+ | </math> | ||
+ | |||
+ | On obtient le résultat suivant : | ||
+ | |||
+ | [[Fichier:testOutline1.png]] | ||
+ | |||
+ | |||
+ | =====Pré-traitement : Flou Gaussien===== | ||
+ | |||
+ | Afin de réduire significativement le nombre de contours, il est nécessaire d'effectuer un pré-traitement avant la détection de contours. On utilise généralement un flou gaussien afin de réduire les transitions lumineuses et ainsi réduire le nombre de contours. L’algorithme de flou gaussien repose également sur la convolution de matrices sauf qu'ici la matrice filtre est générée grâce à une fonction gaussienne paramétrée par '''Sigma''' qui va directement impacter le niveau de flou. Plus la matrice de filtrage est grande plus la qualité du résultat est meilleure, mais le temps d’exécution devient également plus grand. | ||
+ | |||
+ | Voici la fonction de génération du filtre : <math>G(x,y) = \frac{1}{{2\pi \sigma^2}} e^{-\frac{x^2 + y^2}{2 \sigma^2}}</math> où x et y correspondent aux indices de la cellule de la matrice. | ||
+ | |||
+ | On obtient ce résultat : | ||
+ | |||
+ | [[Fichier:testGaussianBlur.png]] | ||
+ | |||
+ | On note un bien meilleur résultat. Cependant on remarque que les composantes horizontales ne sont pas correctement détectées il subsiste un problème avec le filtrage vertical que je n'ai pas réussi à identifier. De plus, il s'agit par sa suite d'éliminer les contours parasites ainsi que d'affiner le trait pour n'avoir plus qu'un pixel d'épaisseur. | ||
+ | |||
+ | =====Post-traitement : Algorithme de Canny===== | ||
+ | |||
+ | Afin d'obtenir un résultat exploitable il est nécessaire d'appliquer encore quelques traitements. Pour cela nous utilisons l’algorithme de Canny qui repose que celui de Sobel mais qui ajoute un algorithme de seuillage par hysteresis qui permet de retirer les contours secondaires et de ne garder que les contours connexes dans une seuil haut et un seuil bas définis. En outre, cette algorithme affine également le trait obtenu. Pour cet algorithme nous avons directement trouvé des sources libres de droits [http://www.tomgibara.com/computer-vision/canny-edge-detector] qui donne des résultats très corrects. | ||
+ | |||
+ | Par exemple : | ||
+ | |||
+ | [[Fichier:testCanny1.png]] | ||
+ | |||
+ | |||
+ | ou encore : | ||
+ | |||
+ | [[Fichier:testCanny2.png|1500px]] | ||
+ | |||
+ | ===Semaine 5=== | ||
+ | |||
+ | *Établissement d'un protocole de transfert d'image basé sur les WebSockets | ||
+ | **Envoie d'un requête "send" par le client dans une trame websocket texte | ||
+ | **Réponse serveur 'Y' si aucun autre client tente d'envoyer une image simultanément, 'N' sinon | ||
+ | **Si et seulement si le client reçoit 'Y' il débute le transfert en envoyant la largeur et la longueur de l'image sur 4 octets, puis les données de l'image soit 4 valeurs pour chaque pixels (r,g,b,alpha) | ||
+ | **Le serveur renvoie 'D' une fois le transfert achevé (à l'avenir, le serveur renverra l'image après filtrage). | ||
+ | **A tout moment, le serveur peut renvoyer 'E' si une erreur est survenue (ex : paquets perdus, nombre incorrects de données) | ||
+ | *Ajout du support des logs au niveau serveur | ||
+ | **Impression sur la sortie standard des messages | ||
+ | **Formatage en fonction de la sévérité du log (info, warning, error...) | ||
+ | **Incrustation de la date et heure | ||
+ | **Possibilité de redirection dans une fichier, avec configuration (écriture à la suite d'un fichier existant, écrasement...) | ||
+ | **Le tout dans une classe Java flexible et réutilisable pour d'autres projets | ||
+ | |||
+ | |||
+ | ===Semaine 6=== | ||
+ | |||
+ | *Installation de la librairie Java Pi4j sur la raspberry pi qui permet le contrôle des GPIO de celle-ci et notamment le port I²C (basé sur WiringPi) | ||
+ | *Amélioration coté serveur websocket | ||
+ | **Gestion de message plus longs (maximum des trames websockets soit 2^63 octets) | ||
+ | **Différenciation, à la réception de messages, des trames textes ou binaires des trames de contrôle comme les requête ping, pong ou de fermeture de connexion | ||
+ | **Ajout d'un mode de debug de trames | ||
+ | *Interface web | ||
+ | **Mise en place du code css pour améliorer le rendu visuel de la page | ||
+ | **Amélioration de l'envoie des données vers le serveur | ||
+ | |||
+ | ===Semaine 7=== | ||
+ | |||
+ | *Améliorations du serveur WebSocket | ||
+ | **Gestion de la fragmentation TCP | ||
+ | **Gestion de la fragmentation WebSocket | ||
+ | **Gestion des requêtes ping envoyés par le client en renvoyant une trames de contrôle pong afin de maintenir la connexion | ||
+ | **Gestion des déconnexion de clients afin d'optimiser le serveur | ||
+ | **Amélioration de l'émission de messages en augmentant la taille maximale pouvant être émise (2^63 octets) | ||
+ | |||
+ | *Amélioration du code css | ||
+ | *Mise en place d'une partie 'Home' et 'About' | ||
+ | **Home : Communication avec le serveur web | ||
+ | **About : Information complémentaire sur la page web | ||
+ | |||
+ | A ce stade du développement, le client et le serveur sont opérationnels et peuvent s'échanger des messages ainsi que des images que le serveur traite pour extraire les contours. | ||
+ | |||
+ | ===Semaine 8=== | ||
+ | |||
+ | |||
+ | *Montage projecteur | ||
+ | **Intégralité des composants fixés par vis dans le boîtier ABS | ||
+ | **Fusion des alimentations | ||
+ | **Présence d'une led témoin de mise en tension | ||
+ | **Position laser réglable grâce à une vis | ||
+ | **Présence d'un switch à bascule pour allumer et éteindre le laser | ||
+ | **Isolation et vérification des composants | ||
+ | **Tests empiriques de température et de fonctionnement des lasers et galvanomètres | ||
+ | |||
+ | Le montage boîtier est donc terminé et s’alimente en 230V par intermédiaire d'un câble secteur avec connecteur C14. Le résultat s'avère plutôt compact. Les quelques tests de température ont montrés que malgré le manque d'espace la température globale du boîtier ne dépasse pas les 50°C pendant plus d'une heure de fonctionnement. L'intégralité des connexions sont isolées avec de la gaine thermodynamique et les zones "sensibles" sont correctement vérifiés ainsi que protégées avec de la matière isolante. | ||
+ | |||
+ | [[Fichier:proj1.jpg|550px|Boîtier après perçage]] [[Fichier:proj2.jpg|550px|Boîtier finalisé]] [[Fichier:proj3.jpg|550px|Projecteur en fonctionnement]] | ||
+ | |||
+ | *Page web et serveur WebSocket | ||
+ | **Ajout d'un bouton "reboot" et "shutdown" sur la page web | ||
+ | ***Reboot : Permet le redémarrage de la Raspberry | ||
+ | ***Shutdown : Permet l'extinction de la Raspberry | ||
+ | **Ajout d'un slider pour le flou gaussien (échelle 1 à 100) | ||
+ | |||
+ | ===Semaine 9=== | ||
+ | *Installation du programme 'bind9' permettant de mettre en place un serveur DNS qui permet de remplacer les adresses IP par un nom | ||
+ | **Problème de configuration (à corriger) | ||
+ | *Prise en compte du message de retour du serveur | ||
+ | **Récupération d'un tableau de points représentants les contours de l'image envoyé par le client | ||
+ | *Mise à jour du slider du flou gaussien | ||
+ | **Double spider permettant de sélectionner un segment de valeur | ||
+ | **Echelle comprise entre 0 et 1, pour faciliter la lecture les valeurs sont écrites en pourcent | ||
+ | *Etude du pololu md08a en remplacement du DAC initialement prévu et dessin de la carte | ||
+ | |||
+ | ===Semaine 10=== | ||
+ | [[Fichier:Capture_du_2017-04-12_15-53-15.png|200px|thumb|right|Image envoyée -> Image laser]] | ||
+ | |||
+ | *Récupération d'un ensemble de points envoyés par le serveur permettant de tracer l'image telle que sera projetée par le laser | ||
+ | **Mise en place du canvas | ||
+ | **Traçage de l'image par l'intermédiaire de "lineto" puisqu'il s'agit d'un contour | ||
+ | *Ajout d'une sécurité sur les boutons "reset" et "shutdown" | ||
+ | **Pop-up de confirmation pour les deux actions | ||
+ | *Mise à jour du Double slider | ||
+ | **Redimensionnement | ||
+ | **Récupération des valeurs | ||
+ | **Envois des valeurs vers le serveur par un tableau de binaires | ||
+ | |||
+ | ===Semaines Supplémentaires=== | ||
+ | |||
+ | *Finalisation du code css de la page web | ||
+ | **Amélioration visuelle | ||
+ | **Rendu dynamique de la page | ||
+ | [[Fichier:Page_accueil.png|396px|Page d’accueil]] | ||
+ | [[Fichier:Page2.png|396px|Image chargée]] | ||
+ | [[Fichier:Page3.png|396px|Contours récupérés]] | ||
+ | *Finalisation de la carte Pololu md08a + Arduino Pro Mini | ||
+ | |||
+ | |||
+ | *Mise en place d'une liaison série Raspberry-Arduino | ||
+ | **Tests effectués avec I2C, SPI, UART. Solution retenue : I2C | ||
+ | *Contrôle des galvanomètres via PWM | ||
+ | |||
+ | |||
+ | [[Fichier:carre.jpg|550px|Exemple de projection]] | ||
+ | |||
+ | A terme, nous arrivons à projeter une image qui manque quelque peu de précision mais qui est tout de même reconnaissable. Néanmoins les données de positions arrivant par flux | ||
+ | l'Arduino, nous sommes limité par la vitesse de transmition de l'I2C et cela provoque un clignotement de l'image. | ||
== Fichiers Rendus == | == Fichiers Rendus == | ||
+ | |||
+ | [[Media:CptProjetIMA4.pdf |Compte-rendu Projet]] | ||
==Bibliographie== | ==Bibliographie== | ||
+ | *Galvanomètres et laser | ||
http://www.ebay.com/itm/20KPPS-30KPPS-laser-scanning-galvo-scanner-ILDA-Closed-Loop-max-30kpps-for-laser-/261517002270?hash=item3ce3a0c61e | http://www.ebay.com/itm/20KPPS-30KPPS-laser-scanning-galvo-scanner-ILDA-Closed-Loop-max-30kpps-for-laser-/261517002270?hash=item3ce3a0c61e | ||
+ | http://sciences.ulb.ac.be/printemps/download/2016/dossier_pedagogique/02.pdf | ||
+ | |||
+ | *Websocket | ||
+ | http://stackoverflow.com/questions/8125507/how-can-i-send-and-receive-websocket-messages-on-the-server-side | ||
+ | |||
+ | https://tools.ietf.org/html/rfc6455 | ||
+ | |||
+ | https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_a_WebSocket_server_in_Java | ||
+ | |||
+ | https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers | ||
+ | |||
+ | *Traitement d'image | ||
+ | https://en.wikipedia.org/wiki/Gaussian_blur | ||
+ | |||
+ | http://www.zebulon.fr/questions-reponses/traitement-d-image-detection-de-contours-55.html | ||
+ | |||
+ | http://patrick-bonnin.developpez.com/cours/vision/apprendre-bases-traitement-image/partie-5-segmentation-contour/ | ||
+ | |||
+ | http://www.isir.upmc.fr/UserFiles/File/clady_homepage/EPU/2-Contour | ||
+ | |||
+ | *Autre | ||
https://icn.fenelonlille.org/mediawiki/index.php/Projet_ICN_commun_2016/2017 | https://icn.fenelonlille.org/mediawiki/index.php/Projet_ICN_commun_2016/2017 | ||
https://www.g33k-zone.org/post/2016/05/11/Configurer-le-Raspberry-Pi-en-point-d-acc%C3%A8s-Wifi | https://www.g33k-zone.org/post/2016/05/11/Configurer-le-Raspberry-Pi-en-point-d-acc%C3%A8s-Wifi | ||
+ | |||
+ | https://blogs.oracle.com/acaicedo/resource/Parallax-I2C/Sensors.java | ||
http://stackoverflow.com/questions/22087076/how-to-make-a-simple-image-upload-using-javascript-html | http://stackoverflow.com/questions/22087076/how-to-make-a-simple-image-upload-using-javascript-html | ||
− | |||
− | |||
− | |||
− |
Version actuelle datée du 15 juin 2017 à 16:40
Sommaire
Cahier des charges
Présentation générale du projet
Contexte
Le laser est une technologie largement utilisé de nos jours dans de diverses applications. En effet, on retrouve cette technologie dans le domaine médical notamment pour la chirurgie oculaire ou encore dans industrie avec la découpe laser ou la soudure au laser.
Le laser est une émission de photons produit par excitation d'un milieu amplificateur (ex : CO2), le rayonnement produit est unidirectionnel et monochromatique. Ce sont ces propriétés que nous allons exploiter afin réaliser un projecteur laser qui mis en mouvement est capable de dessiner des formes sur une surface à l'instar de systèmes existants utilisés pour des effets lumineux lors de festivités.
Objectif du projet
L'objectif du projet est de créer un système de projection laser à plusieurs couleurs (au minimum deux) avec une application web pour programmer les formes à dessiner afin d'agrémenter des compétitions de gymnastique rythmique.
Description du projet
Le projet consiste à réaliser avec des lasers des figures géométriques par l'intermédiaire d'un système galvanométrique.
Notre premier travail sera de concevoir la partie projection. Pour cela, nous devrons aligner les lasers et les galvanomètres. En effet, ces derniers posséderont des miroirs à l'extrémité, ainsi les miroirs changeront d'inclinaison suivant le courant traversant les galvanomètres (similaire à l'image ci-contre). Il nous faudra également créer un système d'alimentation qui à partir d'une même source (réseau 230 V) sera capable d'alimenter les lasers et les galvanomètres ainsi qu'une Arduino et une Raspberry Pi.
Ensuite nous nous intéresserons à l'utilisation d'un système embarqué pour contrôler les galvanomètres et donc dessiner avec les lasers sur un plan 2D. En effet, le laser restera toujours fixe, seuls les galvanomètres, et donc les miroirs bougeront. Les galvanomètres étant contrôlés en courant il faudra réaliser une interface électronique pour convertir une commande en tension en commande de courant. La commande en tension sera assuré par une carte Arduino. Nous n'utiliserons qu'une seule paire de galvanomètres pour les deux laser. Il faudra donc multiplexer les deux faisceaux. La programmation des formes à effectuer, se fera par l'intermédiaire d'une application web PHP hébergée sur une RaspberryPi qui permettra de récupérer une image, d'y appliquer divers traitements et filtres afin d'en extraire les contours, et de calculer les commandes pour positionner les galvanomètres. Un serveur WebSocket également hébergé sur la RaspberryPi servira d'interface entre l'application web et L'arduino qui sera connecté via une liaison série sur la raspberryPI.
Comme animation de test, nous dessinerons le contour d'une gymnaste avec une couleur et son ballon d'une autre couleur. La gymnaste devra lancer son ballon, réaliser quelques difficultés et récupérer son ballon.
Choix techniques : matériel et logiciel
Nous utiliserons comme matériels :
- Deux lasers de différentes couleurs
- Une paire de galvanomètres à miroir
- Arduino
- Raspberry Pi3
- Alimentation 230V/5V 1A [1]
- Drivers galvanomètres x2
- Alimentation laser
- Lunette de sécurité x2 [2]
- Boitier ABS [3]
- DAC 4 canaux, avec interface I²C [4]
- Connecteur C14 [5]
- Câble secteur
Logiciel :
- Serveur web : Apache2, HTML5, PHP5, CSS
- Base de donnée : MySQL
- Serveur WebSocket : JQuerry, websocket
- Arduino : IDE Arduino
Calendrier prévisionnel
Liste des tâches à effectuer
Les différentes tâches à effectuer sont :
- Partie préliminaire : étude de fonctionnement des galvanomètres
- Déterminer comment est effectuée la commande des galvanomètres (protocole, tension...)
- Effectuer des tests avec l'Arduino ou la Raspberry Pi
- Effectuer des tests avec une LED à la place du laser
- Partie projection
- Fixer les lasers
- Aligner les lasers et les galvanomètres
- Créer le système d'alimentation
- Création d'un boitier
- Partie système embarqué
- Asservissement des galvanomètres avec l'Arduino
- Système électronique de commande en courant pour les galvanomètres
- Interface Web
- Installation et configuration Apache2
- Création d'une interface graphique simple
- Implémentation d'algorithmes de traitement d'image
- Mise en forme avec des CSS
- Sytème d'authentification avec mot de passe
- Utilisation d'une base de données pour enregistrer toutes les images déjà utilisés afin de les réutiliser
- Serveur Web Socket
- Récupération des données de l'interface web
- Mise en place de la liaison série avec l'Arduino
Calendrier
Répartition du temps consacré au projet (240h) :
- Partie projection : 80h
- Partie système embarqué : 40h
- Partie interface web et websocket: 120h
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 | Total |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Définition cahier des charges | 8 | 8 | 8 | 14 | 38 |
Avancement du Projet
Semaine 1
- Finalisation du cahier des charges avec les responsables
- Acquisition de matériels :
- Projecteur Laser
- Galvanomètres
- Alimentation des galvanomètres
- Listage du matériels à acquérir :
- Lunettes de sécurité
- DAC
- Alimentation 230V/5V 1A
- Boitier ABS
- ...
Semaine 2
- Obtention de l'arduino et de la raspberryPI
- Démontage du projecteur laser
- Deux lasers de classe 3b (vert et rouge)
- Circuit d'alimentation des deux lasers
- Initialisation du port série de la raspberryPI
- Modification du fichier config.txt dans la partition boot
- Ajout d'une ligne autorisant la communication série (enable_uart=1)
Semaine 3
- Installation du serveur apache sur la raspberryPI
- Modification du fichier /etc/network/interfaces (Static, Address, Netmask, Gateway)
- Modification du fichier /etc/resolv.conf (Adresse du serveur)
- Utilisation d'un proxy pour la récupération outils de programmation (Apache2, Php5)
- Mise en place du point d’accès wifi de la raspberryPI
- Récupération des logiciels nécessaires (dnsmasq, hostapd)
- Mise en place d'une adresse IP statique pour le point d'accès par l'intermédiaire du fichier /etc/dhcpcd.conf
- Désactivation de la négociation automatique de bail dans le fichier /etc/network/interfaces
- Configuration du logiciel hostapd /etc/hostapd/hostapd.conf
- Activation du démarrage automatique du point d'accès /etc/default/hostapd
- Configuration du serveur DNS par /etc/dnsmasq.conf
- Instauration de la page d'accueil de l'interface web à l'adresse 192.168.3.1
- Téléversement de fichiers images sur la page web et affichage de celle-ci
- Voyant de confirmation du bon fonctionnement du serveur web
- Développement du serveur websocket
Le développement du serveur websocket, initialement prévu en C (il était prévu de reprendre les sources utilisées l'année précédente dans le cadre des projet IMA3 SC), a finalement été réalisé en JAVA pour plusieurs raisons. Premièrement, un seul programme met en œuvre le serveur websocket, les divers traitements d'image, ainsi que les contrôles des galvanomètres (par l'intermédiaire des DAC) via la liaison I²C de la Raspberry Pi. Le langage JAVA fournit nativement des bibliothèques d'objets pour manipuler assez simplement des images, il existe également des librairies pour contrôler le port GPIO de la Raspberry et il était également intéressant de travailler avec un langage de plus haut niveau que le C. En outre, la totale portabilité de ce langage nous permet de travailler sur n'importe quelle machine et ensuite transférer notre programme sur la Raspberry Pi et l’exécuter sans même repasser par une phase de compilation. Et finalement, c'est le langage que nous étudions actuellement en cours de programmation orientée objet, ce qui permet de nous perfectionner.
Serveur WebSocket
N'ayant pas trouvé d'API WebSocket en Java satisfaisante (qui ne nécessitent pas l'installation et la configuration de nombreux outils additionnels), j'ai estimé plus commode et plus formateur de développer notre propre serveur WebSocket en se basant sur les librairies sockets de Java avec l'aide d'une documentation assez exhaustive [6].
Les WebSockets désignent à la fois un protocole réseau qui étend le protocole TCP ainsi qu'une API du World Wide Web. Nous nous en servons afin d'établir une communication asynchrone et bidirectionnelle entre un navigateur web et le serveur, notamment pour envoyer l'image à projeter. L’implémentation du serveur repose sur plusieurs étapes.
1) Connexion d'un client
2) Handshake : permet de faire le lien entre le protocole HTTP et WS. Le client envoie une requête HTTP demandant un upgrade de la connexion en protocole WS et le serveur répond en configurant la connexion (détail dans la documentation [7]).
3) Réception et décodage des messages : le serveurs reçoit des trames brutes, on extrait donc la taille du message ainsi que le message. De plus, les données son encodées (cryptage XOR) par un masque qui doit également être extrait.
4) Fermeture de la communication.
Le serveur supporte le multi-client il est donc nécessaire qu'il effectue ces étapes en "parallèle". Nous utilisons donc les threads ou fils d’exécution. Un thread est donc responsable d'attendre qu'un nouveau client se connecte et effectue le "handshake", il crée ensuite un thread associé à ce client afin d'établir un canal de communication. Il y a donc au moins un thread par client. Par la suite il faudra également implémenter une déconnexion automatique d'un client notamment après un timeout.
Exemple d’exécution du serveur avec deux clients qui se connectent et envoient un message :
Les clients sont des navigateurs web. Pour les tests nous avons repris les exemples utilisés dans le cadre das projets IMA3 SC avec le titre de la page HTML qui se colore en vert lorsque le client est connecté, et un champ de saisie pour envoyer des messages au serveur :
Semaine 4
- Mise en place d’algorithmes de traitement d'image
Détection de contour
L'objectif de notre projet est de pouvoir projeter n'importe quelle image envoyé grâce à l'application web. Pour cela nous allons tracer à une vitesse élevée les contours de l'image sur par exemple un mur grâce au laser. Il est donc nécessaire de détecter les contours de l'image grâce à plusieurs traitements.
Conversion en niveau de gris
Une des étapes préliminaires pour effectuer la détection de contour est de convertir l'image couleur en niveau de gris. Pour cela l'algorithme est simple : il suffit pour chaque pixel de l'image d'effectuer la moyenne des composantes rouge, verte, bleu de chaque pixel. En effet, si l'on considère une image matricielle (l'image est considérée comme un tableau à deux dimensions avec chaque case qui contient la couleur du pixel), chaque pixel contient les composantes RGB codées sur un octet. La couleur gris correspond aux composantes RGB égales. Le niveau de gris est donc codé de 0 à 255, avec 0 donnant du noir et 255 du blanc.
Filtre de Prewit et Sobel
Un contour est défini par un changement brutal d'intensité lumineuse, il s'agit donc de détecter selon plusieurs directions (au moins 2) ces changements. Il existe de nombreuses techniques afin de détecter les contours d'une image (gradient, Laplacien). L'une des techniques avec un rapport efficacité/temps d'exécution correct repose sur les algorithmes de Prewit ou Sobel. L'algorithme repose sur la convolution de matrices. Cela consiste à appliquer sur chaque pixel de l'image la matrice de Prewit définie comme
- ainsi que sa transposée :
Convoluer ces deux filtres sur l'image permet de détecter respectivement les contours verticaux et horizontaux.
D'après wikipedia [8], on obtient de meilleurs résultats en appliquant un filtre triangulaire, on introduit donc la matrice de Sobel :
On obtient le résultat suivant :
Pré-traitement : Flou Gaussien
Afin de réduire significativement le nombre de contours, il est nécessaire d'effectuer un pré-traitement avant la détection de contours. On utilise généralement un flou gaussien afin de réduire les transitions lumineuses et ainsi réduire le nombre de contours. L’algorithme de flou gaussien repose également sur la convolution de matrices sauf qu'ici la matrice filtre est générée grâce à une fonction gaussienne paramétrée par Sigma qui va directement impacter le niveau de flou. Plus la matrice de filtrage est grande plus la qualité du résultat est meilleure, mais le temps d’exécution devient également plus grand.
Voici la fonction de génération du filtre : où x et y correspondent aux indices de la cellule de la matrice.
On obtient ce résultat :
On note un bien meilleur résultat. Cependant on remarque que les composantes horizontales ne sont pas correctement détectées il subsiste un problème avec le filtrage vertical que je n'ai pas réussi à identifier. De plus, il s'agit par sa suite d'éliminer les contours parasites ainsi que d'affiner le trait pour n'avoir plus qu'un pixel d'épaisseur.
Post-traitement : Algorithme de Canny
Afin d'obtenir un résultat exploitable il est nécessaire d'appliquer encore quelques traitements. Pour cela nous utilisons l’algorithme de Canny qui repose que celui de Sobel mais qui ajoute un algorithme de seuillage par hysteresis qui permet de retirer les contours secondaires et de ne garder que les contours connexes dans une seuil haut et un seuil bas définis. En outre, cette algorithme affine également le trait obtenu. Pour cet algorithme nous avons directement trouvé des sources libres de droits [9] qui donne des résultats très corrects.
Par exemple :
ou encore :
Semaine 5
- Établissement d'un protocole de transfert d'image basé sur les WebSockets
- Envoie d'un requête "send" par le client dans une trame websocket texte
- Réponse serveur 'Y' si aucun autre client tente d'envoyer une image simultanément, 'N' sinon
- Si et seulement si le client reçoit 'Y' il débute le transfert en envoyant la largeur et la longueur de l'image sur 4 octets, puis les données de l'image soit 4 valeurs pour chaque pixels (r,g,b,alpha)
- Le serveur renvoie 'D' une fois le transfert achevé (à l'avenir, le serveur renverra l'image après filtrage).
- A tout moment, le serveur peut renvoyer 'E' si une erreur est survenue (ex : paquets perdus, nombre incorrects de données)
- Ajout du support des logs au niveau serveur
- Impression sur la sortie standard des messages
- Formatage en fonction de la sévérité du log (info, warning, error...)
- Incrustation de la date et heure
- Possibilité de redirection dans une fichier, avec configuration (écriture à la suite d'un fichier existant, écrasement...)
- Le tout dans une classe Java flexible et réutilisable pour d'autres projets
Semaine 6
- Installation de la librairie Java Pi4j sur la raspberry pi qui permet le contrôle des GPIO de celle-ci et notamment le port I²C (basé sur WiringPi)
- Amélioration coté serveur websocket
- Gestion de message plus longs (maximum des trames websockets soit 2^63 octets)
- Différenciation, à la réception de messages, des trames textes ou binaires des trames de contrôle comme les requête ping, pong ou de fermeture de connexion
- Ajout d'un mode de debug de trames
- Interface web
- Mise en place du code css pour améliorer le rendu visuel de la page
- Amélioration de l'envoie des données vers le serveur
Semaine 7
- Améliorations du serveur WebSocket
- Gestion de la fragmentation TCP
- Gestion de la fragmentation WebSocket
- Gestion des requêtes ping envoyés par le client en renvoyant une trames de contrôle pong afin de maintenir la connexion
- Gestion des déconnexion de clients afin d'optimiser le serveur
- Amélioration de l'émission de messages en augmentant la taille maximale pouvant être émise (2^63 octets)
- Amélioration du code css
- Mise en place d'une partie 'Home' et 'About'
- Home : Communication avec le serveur web
- About : Information complémentaire sur la page web
A ce stade du développement, le client et le serveur sont opérationnels et peuvent s'échanger des messages ainsi que des images que le serveur traite pour extraire les contours.
Semaine 8
- Montage projecteur
- Intégralité des composants fixés par vis dans le boîtier ABS
- Fusion des alimentations
- Présence d'une led témoin de mise en tension
- Position laser réglable grâce à une vis
- Présence d'un switch à bascule pour allumer et éteindre le laser
- Isolation et vérification des composants
- Tests empiriques de température et de fonctionnement des lasers et galvanomètres
Le montage boîtier est donc terminé et s’alimente en 230V par intermédiaire d'un câble secteur avec connecteur C14. Le résultat s'avère plutôt compact. Les quelques tests de température ont montrés que malgré le manque d'espace la température globale du boîtier ne dépasse pas les 50°C pendant plus d'une heure de fonctionnement. L'intégralité des connexions sont isolées avec de la gaine thermodynamique et les zones "sensibles" sont correctement vérifiés ainsi que protégées avec de la matière isolante.
- Page web et serveur WebSocket
- Ajout d'un bouton "reboot" et "shutdown" sur la page web
- Reboot : Permet le redémarrage de la Raspberry
- Shutdown : Permet l'extinction de la Raspberry
- Ajout d'un slider pour le flou gaussien (échelle 1 à 100)
- Ajout d'un bouton "reboot" et "shutdown" sur la page web
Semaine 9
- Installation du programme 'bind9' permettant de mettre en place un serveur DNS qui permet de remplacer les adresses IP par un nom
- Problème de configuration (à corriger)
- Prise en compte du message de retour du serveur
- Récupération d'un tableau de points représentants les contours de l'image envoyé par le client
- Mise à jour du slider du flou gaussien
- Double spider permettant de sélectionner un segment de valeur
- Echelle comprise entre 0 et 1, pour faciliter la lecture les valeurs sont écrites en pourcent
- Etude du pololu md08a en remplacement du DAC initialement prévu et dessin de la carte
Semaine 10
- Récupération d'un ensemble de points envoyés par le serveur permettant de tracer l'image telle que sera projetée par le laser
- Mise en place du canvas
- Traçage de l'image par l'intermédiaire de "lineto" puisqu'il s'agit d'un contour
- Ajout d'une sécurité sur les boutons "reset" et "shutdown"
- Pop-up de confirmation pour les deux actions
- Mise à jour du Double slider
- Redimensionnement
- Récupération des valeurs
- Envois des valeurs vers le serveur par un tableau de binaires
Semaines Supplémentaires
- Finalisation du code css de la page web
- Amélioration visuelle
- Rendu dynamique de la page
- Finalisation de la carte Pololu md08a + Arduino Pro Mini
- Mise en place d'une liaison série Raspberry-Arduino
- Tests effectués avec I2C, SPI, UART. Solution retenue : I2C
- Contrôle des galvanomètres via PWM
A terme, nous arrivons à projeter une image qui manque quelque peu de précision mais qui est tout de même reconnaissable. Néanmoins les données de positions arrivant par flux l'Arduino, nous sommes limité par la vitesse de transmition de l'I2C et cela provoque un clignotement de l'image.
Fichiers Rendus
Bibliographie
- Galvanomètres et laser
http://sciences.ulb.ac.be/printemps/download/2016/dossier_pedagogique/02.pdf
- Websocket
https://tools.ietf.org/html/rfc6455
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_a_WebSocket_server_in_Java
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
- Traitement d'image
https://en.wikipedia.org/wiki/Gaussian_blur
http://www.zebulon.fr/questions-reponses/traitement-d-image-detection-de-contours-55.html
http://www.isir.upmc.fr/UserFiles/File/clady_homepage/EPU/2-Contour
- Autre
https://icn.fenelonlille.org/mediawiki/index.php/Projet_ICN_commun_2016/2017
https://www.g33k-zone.org/post/2016/05/11/Configurer-le-Raspberry-Pi-en-point-d-acc%C3%A8s-Wifi
https://blogs.oracle.com/acaicedo/resource/Parallax-I2C/Sensors.java
http://stackoverflow.com/questions/22087076/how-to-make-a-simple-image-upload-using-javascript-html