{"id":979,"date":"2013-06-14T11:20:03","date_gmt":"2013-06-14T10:20:03","guid":{"rendered":"http:\/\/telefab.fr\/?p=979"},"modified":"2026-04-30T17:34:00","modified_gmt":"2026-04-30T15:34:00","slug":"scanner-3d-kinect","status":"publish","type":"projet","link":"https:\/\/hub.imt-atlantique.fr\/fablab\/projet\/scanner-3d-kinect\/","title":{"rendered":"Scanner 3D Kinect"},"content":{"rendered":"<p>Projet r\u00e9alis\u00e9 par :<br \/>\nAdrien Moreau<br \/>\nAyoub Hadfat<br \/>\nChristophe Masoni<br \/>\nThomas Gaydier<\/p>\n<h2>1.Introduction<\/h2>\n<p>Depuis quelques ann\u00e9es, T\u00e9l\u00e9com Bretagne dispose d&rsquo;un FabLab appel\u00e9 T\u00e9l\u00e9fab, lieu de cr\u00e9ation de divers projets alliant m\u00e9canique, \u00e9lectronique et informatique. Le FabLab s&rsquo;est dot\u00e9 il y a peu de temps d&rsquo;une imprimante 3D pour imprimer des objets. Il souhaiterait \u00e9galement pouvoir imprimer des objets \u00e0 partir d&rsquo;objets existants, en les scannant au pr\u00e9alable. Ainsi notre groupe de projet a entrepris la conception, \u00e0 partir d&rsquo;une Kinect, d&rsquo;un syst\u00e8me permettant de reproduire virtuellement et de mani\u00e8re tridimensionnelle la forme d&rsquo;un objet que l&rsquo;on d\u00e9sirerait scanner.\u00a0Plus pr\u00e9cis\u00e9ment, l&rsquo;objectif est de r\u00e9cup\u00e9rer, \u00e0 partir d&rsquo;un objet de dimensions adapt\u00e9es aux capacit\u00e9s techniques de la Kinect, un nuage de points destin\u00e9 ensuite \u00e0 \u00eatre trait\u00e9 afin de reconstituer une image en 3D visualisable sur un \u00e9cran.<\/p>\n<p>Ce projet est constitu\u00e9 de 3 parties :<\/p>\n<ul>\n<li><span style=\"line-height: 13px;\">Le plateau tournant : permet de faire tourner l&rsquo;objet.<\/span><\/li>\n<li>La Kinect : r\u00e9cup\u00e8re le nuage de point.<\/li>\n<li>Un PC : g\u00e8re le plateau (gr\u00e2ce \u00e0 Arduino) et traite le nuage de point pour en faire un mod\u00e8le 3D.<\/li>\n<\/ul>\n<h3><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1172 aligncenter\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/2-300x182.png\" alt=\"2\" width=\"300\" height=\"182\" \/><\/a><\/h3>\n<p style=\"text-align: center;\"><em>Figure 1 : Les diff\u00e9rentes parties du syst\u00e8me<\/em><\/p>\n<p style=\"text-align: left;\">Chaque partie sera trait\u00e9 individuellement, la mise en relation se fera \u00e0 la fin.<\/p>\n<h2>2.Le plateau tournant<\/h2>\n<h3>2.1.Objectifs<\/h3>\n<p>Dans un premier temps, pour obtenir simplement une image 3D de l&rsquo;objet de bonne qualit\u00e9, nous avons d\u00e9cid\u00e9 de prendre 3 photos de l&rsquo;objet \u00e0 0\u00b0, 120\u00b0, et 240\u00b0 (que nous nommerons par la suite \u00ab\u00a0positions-cibles\u00a0\u00bb de l&rsquo;objet). Le plateau tournant doit donc permettre la rotation de l&rsquo;objet \u00e0 scanner pour r\u00e9cup\u00e9rer les images aux bons angles.<\/p>\n<h3>2.2.Le tourne-disque\u00a0: base du plateau<\/h3>\n<p>Nous avons utilis\u00e9 comme base du plateau tournant un ancien tourne-disque. Cette partie peut bien s\u00fbr \u00eatre r\u00e9alis\u00e9 en construisant le plateau tournant.<\/p>\n<p style=\"text-align: center;\"><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/03\/Nouvelle-image-bitmap2.bmp\" alt=\"Nouvelle image bitmap\" \/><i>Figure 2 : Photos du tourne-disque en vue de dessus (\u00e0 gauche) et de dessous (\u00e0 droite)<\/i><\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Nous avons d\u00fb scier la pointe en m\u00e9tal au centre du plateau tournant qui servait initialement \u00e0 mettre le disque sur le plateau tournant (voir le r\u00e9sultat sur la photo de gauche).<\/p>\n<h3>2.3.Moteur continu<\/h3>\n<p><span style=\"font-size: 13px;\">Le tourne-disque fonctionne avec un moteur continu aliment\u00e9 par 12V. L&rsquo;arbre moteur entra\u00eene une courroie qui fait tourner le plateau.<\/span><\/p>\n<p>Il est aussi possible d&rsquo;utiliser\u00a0un moteur pas-\u00e0-pas en mettant\u00a0en place un r\u00e9gulateur pour moteur \u00e0 l&rsquo;aide d&rsquo;un pont en H.<\/p>\n<p>N\u00e9anmoins, il faut garder en t\u00eate que le plateau tournant devra s&rsquo;arr\u00eater aux positions-cibles. Ainsi, il faudra prendre en compte l&rsquo;inertie restante apr\u00e8s d\u00e9salimentation du moteur. Pour le moteur continu utilis\u00e9, nous avons l&rsquo;avons sous aliment\u00e9 (alimentation \u00e0 3.3V) pour limiter le ph\u00e9nom\u00e8ne d&rsquo;inertie.<\/p>\n<h3>2.4.Comment d\u00e9clencher la prise de photo de la Kinect aux trois \u00ab\u00a0positions-cibles\u00a0\u00bb de l&rsquo;objet (0\u00b0, 120\u00b0, 240\u00b0)\u00a0?<\/h3>\n<p><span style=\"font-size: 13px;\">Nous avons opt\u00e9 pour un syst\u00e8me \u00abLED+photor\u00e9sistances\u00bb.<\/span><\/p>\n<h4>2.4.1.Principe<\/h4>\n<p>On d\u00e9tecte les 3 positions gr\u00e2ce \u00e0 une LED qui passe devant une photor\u00e9sistance aux trois positions voulues, entra\u00eenant un pic d&rsquo;intensit\u00e9. Il suffit alors de demander \u00e0 la Kinect de d\u00e9clencher une photo lorsque l&rsquo;intensit\u00e9 passe au-dessus d&rsquo;un certain seuil.<\/p>\n<p>\u00c9tudions le principe sur un exemple.<\/p>\n<p>Ici l&rsquo;objectif est de faire clignoter une LED plus ou moins vite selon la luminosit\u00e9 ambiante.<\/p>\n<p>Mat\u00e9riel\u00a0:<\/p>\n<ul>\n<li>des fils de connexion<\/li>\n<li>une breadboard (ou plaque d&rsquo;essai sans soudure)<\/li>\n<li>une r\u00e9sistance de 10 k\u03a9<\/li>\n<li>une photo-r\u00e9sistance<\/li>\n<li>une LED<\/li>\n<li>une carte Arduino<\/li>\n<\/ul>\n<p><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/03\/Nouvelle-image-bitmap3.bmp\"><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/03\/Nouvelle-image-bitmap3.bmp\" alt=\"Nouvelle image bitmap\" \/><\/a><\/p>\n<p style=\"text-align: center;\"><em>Figure 3 :\u00a0illustration de l&rsquo;utilisation d&rsquo;Arduino par un exemple simple<\/em><\/p>\n<p style=\"text-align: left;\">Le code Arduino :<\/p>\n<pre>\/*Lit la luminosit\u00e9 etfait clignoter une LED \u00e0 vitesse variable.*\/\n\n\/\/ Num\u00e9ro de la broche du capteur de luminosit\u00e9const int luminosityPin = A0;\n\n\/\/ Num\u00e9ro de la broche de la LEDconst int ledPin = 9;\nvoid setup() {\/\/ Initialise la communication avec l'ordinateur\nSerial.begin(9600);\n\n\/\/ Indique que la broche de la LED est une sortie\n\npinMode(ledPin, OUTPUT);\n\n}\n\nvoid loop() {\n\n\/\/ Lit la valeur du capteur de luminosit\u00e9\n\nint sensorValue = analogRead(luminosityPin);\n\n\/\/ affiche la valeur du capteur dans le \"moniteur\" sur l'ordinateur\n\nSerial.print(\" Valeur du Capteur = \" );\n\nSerial.print(sensorValue);\n\n\/\/ Convertit la valeur en dur\u00e9e d'attente pour la LED\n\n\/\/ il sera n\u00e9cessaire d'adapter les valeurs du \"mapping\" en\n\n\/\/ fonction de la luminosit\u00e9 ambiante\n\nint waitTime = map(sensorValue, 0, 1023, 20, 1000);\n\n\/\/ Envoie le r\u00e9sultat vers l'ordinateur\n\nSerial.print(\"\\t Delai de clignotement (millisecondes) = \");\n\nSerial.println(waitTime);\n\n\/\/ Fait clignoter la LED au rythme de la luminosit\u00e9\n\ndigitalWrite(ledPin, HIGH); \/\/ allume la LED\n\ndelay(waitTime); \/\/ attends la dur\u00e9e choisie\n\ndigitalWrite(ledPin, LOW); \/\/ \u00e9teinds la LED\n\ndelay(waitTime); \/\/ attends la dur\u00e9e choisie\n\n}<\/pre>\n<p style=\"text-align: left;\">La photor\u00e9sistance (entre les fils noir et vert sur la figure 3) est branch\u00e9e en parall\u00e8le avec\u00a0une r\u00e9sistance. Elle est aliment\u00e9e par le 5V de l&rsquo;Arduino (fil rouge) et est reli\u00e9e \u00e0 une entr\u00e9e\u00a0analogique de l&rsquo;Arduino (fil vert). La photor\u00e9sistance est reli\u00e9e \u00e0 un port de sortie. Le code ci-dessus, \u00e9crit\u00a0sur l&rsquo;interface Arduino permet de faire clignoter la LED en fonction de la luminosit\u00e9 de la\u00a0photor\u00e9sistance.<br \/>\nLa partie haute du montage (LED) ne nous int\u00e9resse pas. Nous ne voulons en effet pas faire\u00a0clignoter une LED. En revanche nous voulons d\u00e9clencher la prise de photographies de la Kinect\u00a0en fonction des valeurs de luminosit\u00e9 des LED. La partie basse du montage sera donc exactement\u00a0la m\u00eame pour notre projet, et sera faite trois fois (pour les trois photor\u00e9sistances).<\/p>\n<h4>2.4.2.Choix de la valeur de la r\u00e9sistance en s\u00e9rie avec la photor\u00e9sistance<\/h4>\n<p>Le choix de la r\u00e9sistance est important, il d\u00e9termine les valeurs de la luminosit\u00e9 capt\u00e9e par\u00a0la photor\u00e9sistance lorsqu&rsquo;elle est sous une luminosit\u00e9 ambiante (telle que l&rsquo;on peut avoir au\u00a0FabLab), et lorsqu&rsquo;elle est \u00e9clair\u00e9e par une LED.<br \/>\nL&rsquo;important est que ces deux valeurs soient bien distinctes pour pouvoir d\u00e9tecter sans\u00a0ambigu\u00eft\u00e9 lorsque la photor\u00e9sistance est \u00e9clair\u00e9e par la LED, ind\u00e9pendamment des contraintes de\u00a0l&rsquo;environnement (\u00e9clairage accidentel&#8230;).<br \/>\nNous avons test\u00e9 pour diff\u00e9rentes valeurs du r\u00e9sistor la valeur de la r\u00e9sistance de la\u00a0photor\u00e9sistance lorsque celle-ci est \u00e9clair\u00e9e par la LED ou pas. Nous avons choisi le r\u00e9sistor qui\u00a0permettait d&rsquo;avoir le meilleur contraste de r\u00e9sistance, \u00e0 savoir 100k\u03a9.<\/p>\n<h4>2.4.3.Que mettre sur la partie tournante\u00a0: LED ou photor\u00e9sistance\u00a0?<\/h4>\n<p>Au d\u00e9but, nous avions l&rsquo;intention de mettre une photor\u00e9sistance sur la partie tournante et\u00a0trois LEDs sur la partie fixe (aux trois positions correspondant \u00e0 0\u00b0, 120\u00b0 et 240\u00b0) (voir figure ci-dessous).<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1174 aligncenter\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/1-300x135.png\" alt=\"1\" width=\"300\" height=\"135\" \/><\/a><br \/>\n<em>Figure 4 : Sch\u00e9ma du montage initialement pr\u00e9vu<\/em><\/p>\n<p style=\"text-align: left;\">La photor\u00e9sistance doit \u00eatre reli\u00e9e \u00e0 la breadboard puisque c&rsquo;est gr\u00e2ce aux pics de sa\u00a0luminosit\u00e9 que l&rsquo;on d\u00e9clenchera la prise de photos de la Kinect. En revanche, les LEDs doivent\u00a0\u00eatre uniquement aliment\u00e9es par source de tension.<br \/>\nOr le fait que la photor\u00e9sistance, donc la partie reli\u00e9e \u00e0 la breadboard, soit sur la partie\u00a0tournante du plateau pose probl\u00e8me. En effet, les fils s&rsquo;enrouleront autour de l&rsquo;objet au fur et \u00e0\u00a0mesure que le plateau tournera, ce qui n&rsquo;est \u00e9videmment pas souhaitable. Le probl\u00e8me est le\u00a0fait passer les fils par \u00ab en dessous \u00bb<\/p>\n<p>C&rsquo;est donc pour cela que nous avons plut\u00f4t choisi de faire le montage ci-dessous qui r\u00e9sout<br \/>\nces probl\u00e8mes. On a invers\u00e9 LED et photor\u00e9sistances (qui sont maintenant trois).<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/11.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-1176\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/11-300x135.png\" alt=\"1\" width=\"300\" height=\"135\" \/><\/a><br \/>\n<em>Figure 5 : Sch\u00e9ma du montage finalement r\u00e9alis\u00e9<\/em><\/p>\n<p>Pour \u00e9viter les probl\u00e8mes de fils, il suffit alors d&rsquo;alimenter la LED avec une pile qui sera fix\u00e9e avec<br \/>\ncelle-ci sur la partie tournante.<\/p>\n<h4>2.4.4.Fixation de la LED et r\u00e9alisation du circuit d&rsquo;alimentation<\/h4>\n<p>Pour g\u00eaner le moins possible la prise de photos de la Kinect, nous avons d\u00e9cid\u00e9 de fixer le\u00a0circuit en dessous du plateau et de simplement faire ressortir la LED en per\u00e7ant un trou.\u00a0Mais sous le plateau il y a peu de place et il faut que le circuit soit assez plat pour ne pas rentrer\u00a0en collision avec l&rsquo;arbre du moteur (moins de 8mm environ).<br \/>\nLa solution retenue permettant de concilier \u00e0 la fois la petite taille, et la forme plate, est d&rsquo;utiliser comme alimentation une pile plate.<br \/>\nUn autre probl\u00e8me s&rsquo;est pos\u00e9 : on ne peut pas se contenter de souder la pile directement \u00e0\u00a0la LED. Il faut pouvoir changer la pile quand celle-ci est us\u00e9e.<br \/>\nNous avons donc utilis\u00e9 un morceau de connecteur masculin-f\u00e9minin. Apr\u00e8s d\u00e9coupage\u00a0d&rsquo;un morceau de plaque et soudure, voici le r\u00e9sultat ci-dessous (Figure 6).<\/p>\n<p align=\"left\"><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/03\/Nouvelle-image-bitmap4.bmp\"><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/03\/Nouvelle-image-bitmap4.bmp\" alt=\"Nouvelle image bitmap\" \/><\/a><\/p>\n<p align=\"center\"><i>Figure 6 : Circuit d&rsquo;alimentation de la LED<\/i><\/p>\n<h4>2.4.5.R\u00e9alisation des circuits des photor\u00e9sistance<\/h4>\n<p>Ce qui nous int\u00e9resse est de faire la partie du circuit encadr\u00e9e en jaune trois fois, pour les\u00a0trois \u00ab positions-cibles \u00bb.<\/p>\n<p><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/12.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-1177 aligncenter\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/12-300x211.png\" alt=\"1\" width=\"300\" height=\"211\" \/><\/a><\/p>\n<p align=\"center\"><i>Figure 7 : Montage \u00e0 r\u00e9aliser pour relier chaque photor\u00e9sistance \u00e0 l&rsquo;Arduino<\/i><\/p>\n<p>\u00a0Ce qui donne apr\u00e8s un peu de soudure\u00a0:<\/p>\n<p align=\"center\"><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/03\/Nouvelle-image-bitmap6.bmp\"><img decoding=\"async\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/03\/Nouvelle-image-bitmap6.bmp\" alt=\"Nouvelle image bitmap\" \/><\/a><br \/>\n<i><\/i><\/p>\n<p align=\"center\"><i>Figure 8 : Photos du montage de la figure 7<\/i><\/p>\n<p>Les trois photor\u00e9sistances seront reli\u00e9es au 5V de l&rsquo;Arduino, et les trois masses sur le port GND de\u00a0l&rsquo;Arduino, comme respectivement les fils rouge et noir sur la figure 15. Les fils \u00ab verts \u00bb seront reli\u00e9s \u00e0 trois\u00a0entr\u00e9es analogiques diff\u00e9rentes(ANALOG IN sur la figure 15), pour mesurer ind\u00e9pendamment les valeurs\u00a0des r\u00e9sistances des trois photor\u00e9sistances.<\/p>\n<p>Les trois circuits sont fix\u00e9s sur le plateau aux positions-cibles en utilisant de la colle.<\/p>\n<h3>2.5.Alimentation du moteur<\/h3>\n<p>L&rsquo;alimentation est faite gr\u00e2ce \u00e0 un g\u00e9n\u00e9rateur. Le moteur est aussi reli\u00e9 \u00e0 l&rsquo;Arduino pour pouvoir l&rsquo;arr\u00eater aux positions-cibles. Un probl\u00e8me persiste : l&rsquo;intensit\u00e9 utilis\u00e9 par le moteur est \u00e9lev\u00e9e et risquerait d&rsquo;endommager l&rsquo;Arduino pour \u00e9viter cela il faut mettre un transistor entre ces deux \u00e9l\u00e9ments.<\/p>\n<h3>2.6.Comment fixer les objets sur le plateau ?<\/h3>\n<p>Cette question se pose puisqu&rsquo;elle correspond \u00e0 l&rsquo;une des contraintes que l&rsquo;on s&rsquo;est fix\u00e9e :\u00a0l&rsquo;objet ne doit pas bouger lorsque le plateau tourne.<br \/>\nLa meilleure solution retenue pour sa simplicit\u00e9 est d&rsquo;utiliser de la p\u00e2te \u00e0 modeler que l&rsquo;on\u00a0aplatira entre le plateau et l&rsquo;objet.<\/p>\n<h3>2.7.Code et montage final<\/h3>\n<p>Voici le code final de l&rsquo;arduino :<\/p>\n<pre>\/\/ Num\u00e9ro de la broche du capteur de luminosit\u00e9\nconst int luminosityPin1 = A0;\nconst int luminosityPin2 = A1;\nconst int luminosityPin3 = A2;\n\/\/ Seuil de luminosit\u00e9\nconst int seuil1 = 155;\nconst int seuil2 = 175;\nconst int seuil3 = 130;\n\/\/ Num\u00e9ro de la broche de la LED\nconst int ledPin = 9;\n\/\/numero de la branche transistor\nconst int traPin = 2;\n\n\/\/ Variable g\u00e9rant l'intervalle de prise de valeurs\nlong previousMillis = 20;\nlong interval = 20;\n\n\/\/\u00e9tat du moteur ( 0= arret , 1 = marche )\nint state = 1;\n\nboolean depasseSeuil = false;\n\nvoid setup(){\nSerial.begin(9600);\npinMode (traPin, OUTPUT);\ndigitalWrite(traPin, HIGH);\n\n} \n\nvoid loop() {\nunsigned long currentMillis = millis();\nif(currentMillis - previousMillis &gt; interval) {\npreviousMillis = currentMillis; \n\n\/\/ Lit la valeur du capteur de luminosit\u00e9\nint sensorValue1 = analogRead(luminosityPin1);\nint sensorValue2 = analogRead(luminosityPin2);\nint sensorValue3 = analogRead(luminosityPin3);\n\nif (sensorValue1  &gt; seuil1)  depasseSeuil = true; \nelse if (sensorValue2 &gt; seuil2)  depasseSeuil = true; \nelse if (sensorValue3 &gt; seuil3)  depasseSeuil = true; \n\n\/\/ affiche la valeur du capteur dans le \"moniteur\" sur l'ordinateur\nSerial.print(\"\\n \" );\nSerial.print(sensorValue1); \/\/envoit la valeur du capteur a Processing\nSerial.print(\" \/  \" );\nSerial.print(sensorValue2); \/\/envoit la valeur du capteur a Processing\nSerial.print(\" \/  \" );\nSerial.print(sensorValue3); \/\/envoit la valeur du capteur a Processing\n\nif(depasseSeuil){\nstate = 0;\ndigitalWrite(traPin, LOW);\/\/stop le moteur\ndelay(3000);\ndigitalWrite(traPin, HIGH);\n\nSerial.println(\"start\");\n}\nelse\n{\n  state = 1;\ndigitalWrite(traPin, HIGH);\/\/relance le moteur\n}\n\ndepasseSeuil=false;\n\/\/ V\u00e9rifie les valeurs envoy\u00e9s par Processing\n\/\/checkSerial(); \n\nif(state==1){\ndigitalWrite(traPin, HIGH);\n}\n}\n}\n\nvoid checkSerial(){ \/\/ v\u00e9rifie les donn\u00e9es envoy\u00e9s par Processing\nif (Serial.available() &gt; 1) {\nchar trigger = Serial.read();\nif(trigger == 'S'){\nstate = 1;\n}\n}\n\n}<\/pre>\n<p>Voici le montage final de la partie plateau tournant :<\/p>\n<p><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/sch\u00e9ma-montage_bb.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1194 aligncenter\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/sch\u00e9ma-montage_bb-300x191.png\" alt=\"sch\u00e9ma montage_bb\" width=\"300\" height=\"191\" \/><\/a><\/p>\n<p style=\"text-align: center;\"><em>Figure 9 : Sch\u00e9ma du montage de la partie plateau tournant<\/em><\/p>\n<h2>3.Kinect<\/h2>\n<p>Attention, dans la partie suivante nous avons d\u00e9cid\u00e9 de travailler sous Ubuntu 12.04 avec un ordinateur ne contenant aucun USB3.0 car la Kinect ne fonctionne pas avec de tels ports USB. Nous avons aussi utilis\u00e9 Processing 1.5.1, Arduino 1.0.5.<\/p>\n<h3>3.1.Pr\u00e9sentation de la Kinect<\/h3>\n<p>La Kinect, initialement appel\u00e9e sous le nom de code Project Natal, est un p\u00e9riph\u00e9rique qui fut d\u00e9velopp\u00e9 par PrimeSense puis commercialis\u00e9 par Microsoft d\u00e9but novembre 2010 comme contr\u00f4leur de jeux vid\u00e9o. Tr\u00e8s vite les programmeurs du monde entier ont r\u00e9alis\u00e9 le plein potentiel de l\u2019appareil et en d\u00e9cembre 2010 pour \u00e9viter la multiplication des drivers non officiel, PrimeSense \u00e0 mis \u00e0 disposition \u00e0 la communaut\u00e9 des drivers OpenSource. Comme ceux de l\u2019API OpenNI ensuite compl\u00e9t\u00e9 par ceux de l\u2019API NITE augmentant ainsi les capacit\u00e9s de l\u2019appareil.<\/p>\n<p>Notons qu\u2019ils existent deux types de Kinect\u00a0: Kinect XBOX 360 ( reconnaissable par le XBOX 360 sur sa fa\u00e7ade) et Kinect for Windows ( reconnaissable par le XBOX sur sa fa\u00e7ade). Nous avons travaill\u00e9 avec une Kinect XBOX 360. L\u2019autre type de Kinect pouvant rencontrer diff\u00e9rents types d\u2019erreur avec les diff\u00e9rentes librairies.<\/p>\n<p>La Kinect permet de r\u00e9cup\u00e9rer plusieurs informations sur son environnement. Elle capte trois types d\u2019images diff\u00e9rentes\u00a0: une image de profondeur, une image RGB et une image infrarouge. On peut la sch\u00e9matiser de la mani\u00e8re suivante\u00a0:<\/p>\n<p align=\"center\"><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/03\/Nouvelle-image-bitmap.bmp\"><img decoding=\"async\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/03\/Nouvelle-image-bitmap.bmp\" alt=\"Nouvelle image bitmap\" \/><\/a><\/p>\n<p style=\"text-align: center;\"><em>Figure 10 : Sch\u00e9ma de la Kinect<\/em><\/p>\n<p><span style=\"font-size: 13px;\">La r\u00e9solution de l\u2019image RGB et de l\u2019image IR est de 640&#215;480 pixels. Les informations de profondeur sont r\u00e9cup\u00e9r\u00e9es \u00e0 partir du capteur infrarouge et mod\u00e9lis\u00e9 par des niveaux de gris allant de 2048 (blanc) \u00e0 0 (noir).<\/span><\/p>\n<p>La Kinect est dot\u00e9e de plusieurs pilotes\u00a0: un pour la camera et un pour le moteur.<\/p>\n<p>Les pilotes ne font pas de post-traitement sur \u200b\u200bles donn\u00e9es, ils nous donnent juste acc\u00e8s au flux de donn\u00e9es brutes de la Kinect et nous permettent de contr\u00f4ler la LED et le moteur.<\/p>\n<p>Par la suite des API OpenSource ont \u00e9t\u00e9 d\u00e9velopp\u00e9s pour r\u00e9cup\u00e9rer et travailler ces donn\u00e9es. Parmi eux on peut en citer 3\u00a0et leur associ\u00e9s l\u2019environnement de travail associ\u00e9\u00a0: en effet ces diff\u00e9rentes API ne fonctionnent pas toujours sous tous les syst\u00e8mes d\u2019exploitation.<\/p>\n<table border=\"0\" width=\"880\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\"><b>Librairies<\/b><\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\"><b>Auteur<\/b><\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\"><b>API<\/b><\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\"><b>OS<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">Openkinect<\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">Daniel Shiffman<\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">OpenKinect\/Libfreenect<\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">Mac OS X<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">dLibs<\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">Thomas Diewald<\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">OpenKinect\/Libfreenect<\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">Windows<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">Simple-OpenNI<\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">Max Rheiner<\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">OpenNI\/NITE<\/p>\n<\/td>\n<td nowrap=\"nowrap\" width=\"220\">\n<p align=\"center\">Mac OS X, Windows, Linux<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Nous avons d\u00e9cid\u00e9 de travailler sous le syst\u00e8me d\u2019exploitation Linux car les ordinateurs du fablab ont une puissance limit\u00e9 et tourneront sous Linux.<\/p>\n<p>C\u2019est pourquoi nous nous sommes tourn\u00e9s vers la biblioth\u00e8que de Max Rheiner pour d\u00e9velopper la partie logicielle de notre scanner. Int\u00e9ressons-nous de plus pr\u00e8s \u00e0 Simple-OpenNi.<\/p>\n<h3>3.2.Pr\u00e9sentation de Simple-OpenNI<\/h3>\n<p>OpenNi ( Open Natural Interaction ) est une API qui fournit un soutien pour la reconnaissance visuel.<\/p>\n<p>NITE permet de suivre les mouvements d\u2019un squelette.<\/p>\n<p>Simple-OpenNi est un paquet de fonction associ\u00e9 \u00e0 la plateforme de programmation Processing, utilisant OpenNi et NITE. Toute les fonctions de OpenNi ne sont pas prises en charge mais il est destin\u00e9 \u00e0 un usage plus simple de la biblioth\u00e8que de fonctions. Il permet l\u2019utilisation de la Kinect avec le langage de programmation Procesing.<\/p>\n<h3>3.3.Pr\u00e9sentation du langage de programmation utilis\u00e9<\/h3>\n<p>Nous avons en plus utilis\u00e9 le langage de programmation Processing pour communiquer et traiter les donn\u00e9es entre le PC et la kinect. En effet, nous avons h\u00e9sit\u00e9 entre deux langages de programmation\u00a0: Processing et C#. Le C# est un langage qui se rapproche du Java \u00e9tudi\u00e9 \u00e0 Telecom Bretagne. Cependant, il existe avec Processing des mod\u00e8les de fonctions construits \u00e0 partir de la librairie libfreenect permettant de travailler facilement avec la Kinect. Ce choix nous permettait aussi de nous appuyer sur la documentation apport\u00e9 par l\u2019ouvrage Arduino and Kinect Projects.<\/p>\n<p>On r\u00e9cup\u00e8re gr\u00e2ce \u00e0 des fonctions d\u00e9j\u00e0 pr\u00e9d\u00e9finies dans cette librairie, les informations de profondeurs. On peut les repr\u00e9senter ensuite gr\u00e2ce \u00e0 un code couleur RGB\u00a0: plus l\u2019objet est proche ( resp. loin ) et plus la couleur sera chaude ( resp. froide ).<\/p>\n<p>Actuellement, la biblioth\u00e8que est capable de nous rendre les donn\u00e9es disponibles de quatre fa\u00e7ons\u00a0:<\/p>\n<ul>\n<li><span style=\"font-size: 13px;\">Une image RVB (image o\u00f9 chaque pixel est d\u00e9fini par une valeur de Bleu, Rouge et Vert) de la cam\u00e9ra Kinect comme une PImage.<\/span><\/li>\n<li><span style=\"font-size: 13px;\">\u00c9chelle de gris prise \u00e0 partir de l&rsquo;image de la cam\u00e9ra IR (infrarouge) en tant que PImage<\/span><\/li>\n<li><span style=\"font-size: 13px;\">L&rsquo;image en niveaux de gris avec la luminosit\u00e9 de chaque pixel cartographi\u00e9 \u00e0 la profondeur (plus clair = plus proche).<\/span><\/li>\n<li><span style=\"font-size: 13px;\">Donn\u00e9es de profondeur brutes (nombre de 11 bits entre 0 et 2048) en tant que int [] array<\/span><\/li>\n<\/ul>\n<h3>3.4.Fonctions suppl\u00e9mentaires<\/h3>\n<p>Cependant pour reconstituer l\u2019objet en 3D nous avons d\u00e9cid\u00e9 de travailler avec un nuage de point. Nous l\u2019obtenons en ne r\u00e9cup\u00e9rant qu\u2019un pixel sur 4 permettant ainsi de reconstituer notre nuage de point avec une perte d\u2019information et de qualit\u00e9 minimum. Toute l\u2019information que r\u00e9cup\u00e8re la Kinect n\u2019est pas non plus essentielle pour reconstituer notre objet. En effet notre objet scann\u00e9 poss\u00e8de une taille maximale et il existe une distance minimale entre l\u2019objet et la kinect\u00a0; on peut donc consid\u00e9rer que toute les donn\u00e9es de profondeur en dehors d\u2019un cube pr\u00e9d\u00e9fini (de taille 400*400*400 centr\u00e9 par rapport \u00e0 la kinect) ne nous int\u00e9resse pas.<\/p>\n<p>De plus ce scanner sera utilis\u00e9 pour l\u2019imprimante 3D qui reconstitue l\u2019objet scann\u00e9 sans se soucier de sa couleur, les informations RGB de l\u2019objet ne sont donc pas n\u00e9cessaire. Il est donc inutile de r\u00e9cup\u00e9rer toute les donn\u00e9es de la Kinect.<\/p>\n<h3>3.5.Installation de Processing sous Linux<\/h3>\n<p>T\u00e9l\u00e9chargez l\u2019archive avec la version de Processing qui convient sur le lien\u00a0: <a href=\"http:\/\/www.processing.org\/download\/\">http:\/\/www.processing.org\/download\/<\/a><\/p>\n<p>La version de Processing 2.0 \u00e9tant encore \u00e0 l\u2019\u00e9tat de b\u00e9ta, ce qui veut dire que certains bug peuvent exister, nous travaillerons avec la version stable\u00a0: Processing 1.5.1.<\/p>\n<p>Pour un Linux 32 bit\u00a0: <a href=\"http:\/\/processing.googlecode.com\/files\/processing-1.5.1-linux.tgz\">http:\/\/processing.googlecode.com\/files\/processing-1.5.1-linux.tgz<\/a><\/p>\n<p>Pour d\u00e9compresser l\u2019archive dans votre dossier personnel utilisez la commande\u00a0:<\/p>\n<div>\n<p>tar -zxvf processing-xxx.tgz<\/p>\n<\/div>\n<p>Un dossier sera automatiquement cr\u00e9er\u00a0: \/home\/MonDossierPersonnel\/processing-1.5.1.<\/p>\n<p>Pour ex\u00e9cutez Processing, placez-vous dans le r\u00e9pertoire suivant\u00a0:<\/p>\n<div>\n<pre>cd \/home\/VotreDossierPersonnel\/Processing-xxx\/<\/pre>\n<\/div>\n<p>Puis pour autorisez l\u2019ex\u00e9cution de Processing sur l\u2019ordinateur\u00a0:<\/p>\n<div>\n<pre>chmod +x processing<\/pre>\n<\/div>\n<p>Maintenant pour ex\u00e9cuter Processing sur l\u2019ordinateur il suffira de se placer dans le dossier \/home\/MonDossierPersonnel\/processing-1.5.1 et d\u2019entrer la commande\u00a0:<\/p>\n<div>\n<pre>.\/processing<\/pre>\n<\/div>\n<p>Processing requiert Java pour fonctionner, si Java n\u2019est pas install\u00e9 sur l\u2019ordinateur entrez la commande suivante pour l\u2019obtenir\u00a0:<\/p>\n<div>\n<pre>sudo apt-get install openjdk-6-jdk<\/pre>\n<\/div>\n<h3>3.6.Installation de Simple-OpenNI sous Linux<\/h3>\n<p>Suivez les diff\u00e9rentes \u00e9tapes suivantes pour obtenir les pilotes OpenNi et PrimeSense pour pouvoir travailler avec la Kinect sous une distribution Ubuntu (12.04 x32 lors de notre projet). Si l\u2019on travaille avec une architecture 64 bits, il faudra t\u00e9l\u00e9charger les dossiers associ\u00e9s<\/p>\n<p>1\u00e8re \u00e9tape\u00a0:<\/p>\n<p>Vous aurez besoin de plusieurs logiciels\u00a0; entrez la commande suivante\u00a0pour les installer :<\/p>\n<div>\n<pre>sudo apt-get install git-core cmake freeglut3-dev pkgconfig build-essential libxmu-dev libxi-dev libusb-1.0-0-dev doxygen graphviz mono-complete<\/pre>\n<\/div>\n<p>Ensuite t\u00e9l\u00e9chargez la source contenant OpenNi \u00e0 partir de Github et stockez l\u00e0 dans un r\u00e9pertoire kinect\u00a0:<\/p>\n<div>\n<pre>mkdir ~\/kinect\ncd ~\/kinect\ngit clone https:\/\/github.com\/OpenNI\/OpenNI.git<\/pre>\n<\/div>\n<p><i>\u00a0<\/i><\/p>\n<p>Puis executez le script RedistMaker dans le dossier Platform\/Linux et installez les \u00ab\u00a0outpu binairies\u00a0\u00bb\u00a0:<\/p>\n<div>\n<pre>cd OpenNI\/Platform\/Linux\/CreateRedist\/\nchmod +x RedistMaker\n.\/RedistMaker\ncd ..\/Redist\/OpenNI-Bin-Dev-Linux-x32-v1.5.2.23\/\nsudo .\/install.sh<\/pre>\n<\/div>\n<p>Ensuite copiez le dossier SensorKinect provenant de Github dans le r\u00e9pertoire Kinect\u00a0:<\/p>\n<div>\n<pre>cd ~\/kinect\ngit clone git:\/\/github.com\/avin2\/SensorKinect.git<\/pre>\n<\/div>\n<p>Puis executez le script RedistMaker dans le dossier Platform\/Linux et installez les \u00ab\u00a0outpu binairies\u00a0\u00bb\u00a0:<\/p>\n<div>\n<pre>cd SensorKinect\/Platform\/Linux\/CreateRedist\/\nchmod +x RedistMaker\n.\/RedistMaker\ncd ..\/Redist\/Sensor-Bin-Linux-x32-v5.1.0.25\/\nchmod +x install.sh\nsudo .\/install.sh<\/pre>\n<\/div>\n<p>Ensuite t\u00e9l\u00e9charger le dossier <i>NITE-Bin-Dev-Linux-x32-v1.5.2.21<\/i> et extrayez l\u2019archive dans le r\u00e9pertoire ~\/kinect. L\u2019archive peut \u00eatre obtenue en cliquant sur le lien suivant\u00a0: <a href=\"https:\/\/code.google.com\/p\/simple-openni\/downloads\/detail?name=OpenNI_NITE_Installer-Linux32-0.27.zip&amp;can=2&amp;q=\">https:\/\/code.google.com\/p\/simple-openni\/downloads\/detail?name=OpenNI_NITE_Installer-Linux32-0.27.zip&amp;can=2&amp;q=<\/a><\/p>\n<p>Puis allez dans le r\u00e9pertoire des donn\u00e9es qu\u2019il contient\u00a0:<\/p>\n<div>\n<pre>cd NITE-Bin-Dev-Linux-x32-v1.5.2.21\/Data<\/pre>\n<\/div>\n<p>Pour que Simple-OpenNi fonctionne il faut maintenant modifier la licence. Pour cela modifiez \u00e0 l\u2019aide d\u2019un \u00e9diteur de texte chaque fichiers\u00a0: Sample-Scene.xml, Sample-Tracking.xml et ample-user.xml.<\/p>\n<p>Changez\u00a0: <i>&lt;license Vendor=\u00a0\u00bbPrimeSense\u00a0\u00bb key=\u00a0\u00bb\u00a0\u00bb\/&gt;<\/i><\/p>\n<p>Avec\u00a0: <i>&lt;license Vendor=\u00a0\u00bbPrimeSense\u00a0\u00bb key=\u00a0\u00ab\u00a00KOIk2JeIBYCIPWVnMoRKn5cdY4=\u00bb\/&gt;<\/i><\/p>\n<p>Ensuite revenez dans le r\u00e9pertoire NITE\u00a0:<\/p>\n<div>\n<pre>Cd ..<\/pre>\n<\/div>\n<p>Et ex\u00e9cutez le script d\u2019installation\u00a0:<\/p>\n<div>\n<pre>sudo\u00a0.\/install.sh<\/pre>\n<\/div>\n<p>C\u2019est tout\u00a0en ce qui concerne Simple-OpenNi\u00a0!<\/p>\n<h3>3.7.Code Processing final<\/h3>\n<p>Le code processing ayant permis de r\u00e9cup\u00e9rer et de repr\u00e9senter les donn\u00e9es de la Kinect est le suivant. Il est compos\u00e9 de deux fonctions principales\u00a0: setup() qui permet d\u2019initialiser les param\u00e8tres du code et draw() qui est une boucle infinie et permet de r\u00e9cup\u00e9rer constamment les donn\u00e9es envoy\u00e9s par la Kinect. On d\u00e9finit ensuite les fonctions secondaires ayant \u00e9t\u00e9 utilis\u00e9es dans le code principale.<\/p>\n<p>Voici le code final de Processing :<\/p>\n<pre>import processing.opengl.*;\n\nimport SimpleOpenNI.*;\n\nimport kinectOrbit.*;\n\n\/\/ Initialisation des param\u00e8tres Orbit and simple-openni\n\nKinectOrbit myOrbit;\n\nSimpleOpenNI kinect;\n\n<span style=\"font-size: 13px;\">\/\/ Initialisation des listes de vecteurs pour le nuage de points et les couleurs associ\u00e9s<\/span>\n\nArrayList&lt;PVector&gt; scanPoints = new ArrayList&lt;PVector&gt;(); \/\/ Nuage de Points\n\nArrayList&lt;PVector&gt; objectPoints = new ArrayList&lt;PVector&gt;(); \/\/ Nuage de Points\n\n<span style=\"font-size: 13px;\">\u00a0<\/span>\n\n\/\/ Variable d'espace\n\nfloat baseHeight = -5; \/\/ hauteur de la base du mod\u00e8le\n\nfloat modelWidth = 400;\n\nfloat modelHeight = 400;\n\n\/\/tout objet contenue dans ce cube sera scann\u00e9\n\nPVector axis = new PVector(0, baseHeight, 1050);\n\n\/\/ Param\u00e8tres du scan\n\nint scanLines = 200; \/\/ d\u00e9finie la largeur du scan (pour une valeur de 1 : une seule ligne verticale de l'objet est scann\u00e9)\n\nint scanRes = 1; \/\/ d\u00e9finie le nombre de pixel que nous prenons dans le scan ( 1 est la valeur correspondant \u00e0 la plus haute r\u00e9solution)\n\nboolean scanning; \/\/ valeur bool\u00e9enne associer \u00e0 l'\u00e9tat de num\u00e9risation\n\nboolean arrived; \/\/ valeur bool\u00e9enne : sommes nous \u00e0 une position devant \u00eatre scann\u00e9 ?\n\nfloat[] shotNumber = new float[3]; \/\/ nombre de scan que nous comptons effectuer : ici 3\n\nint currentShot = 0; \/\/ valaur du nombre de prise de vue d\u00e9j\u00e0 prise\n\npublic void setup() {\n\nsize(800, 600, OPENGL);\n\n\/\/ Orbit\n\nmyOrbit = new KinectOrbit(this, 0, \"kinect\");\n\nmyOrbit.drawCS(true);\n\nmyOrbit.drawGizmo(true);\n\nmyOrbit.setCSScale(200);\n\nmyOrbit.drawGround(true);\n\n\/\/ Simple-openni\n\nkinect = new SimpleOpenNI(this);\n\nkinect.setMirror(false);\n\nkinect.enableDepth();\n\nfor (int i = 0; i &lt; shotNumber.length; i++) {\n\nshotNumber[i] = i * (2 * PI) \/ shotNumber.length;\n\n}\n\n<span style=\"font-size: 13px;\">}<\/span>\n\npublic void draw() {\n\nkinect.update(); \/\/ Actualise les donn\u00e9es de la Kinect\n\nbackground(0);\n\nmyOrbit.pushOrbit(this); \/\/ fonction permettant de g\u00e9rer la perspective\n\nupdateObject(scanLines, scanRes); \/\/ actualise les positions des donn\u00e9es de l'objet\n\n<span style=\"font-size: 13px;\">\u00a0<\/span>\n\nif ( scanning) { \/\/scan \u00e0 la main\n\nscan();\n\nscanning = false;\n\n<span style=\"font-size: 13px;\">} \/\/ si on doit scann\u00e9 et que l'on est sur la bonne position alors on scan<\/span>\n\nstroke(255,255,255);\n\ndrawObjects(); \/\/ on dessine les points d\u00e9j\u00e0 pris auparavant\n\ndrawBoundingBox(); \/\/ on dessine le cube autour de l'objet scann\u00e9\n\nkinect.drawCamFrustum(); \/\/ on dessine l'image de la Kinect\n\nmyOrbit.popOrbit(this); \/\/ arr\u00e8te la fonction permettant de g\u00e9rer la perspective\n\n}\n\nvoid drawPointCloud(int steps) { \/\/ Cr\u00e9ation du nuage de point \"grossier\" scann\u00e9\n\nint index;\n\nPVector realWorldPoint;\n\nstroke(255);\n\nfor (int y = 0; y &lt; kinect.depthHeight(); y += steps) {\n\nfor (int x = 0; x &lt; kinect.depthWidth(); x += steps) {\n\nindex = x + y * kinect.depthWidth();\n\nrealWorldPoint = kinect.depthMapRealWorld()[index];\n\nstroke(150);\n\npoint(realWorldPoint.x, realWorldPoint.y, realWorldPoint.z);\n\n}\n\n}\n\n}\n\nvoid drawObjects() { \/\/ Dessine les points scann\u00e9\n\npushStyle();\n\nfor (int i = 1; i &lt; objectPoints.size(); i++) {\n\npoint(objectPoints.get(i).x, objectPoints.get(i).y, objectPoints.get(i).z + axis.z);\n\n}\n\nfor (int i = 1; i &lt; scanPoints.size(); i++) {\n\npoint(scanPoints.get(i).x, scanPoints.get(i).y, scanPoints.get(i).z + axis.z);\n\n}\n\npopStyle();\n\n}\n\nvoid drawBoundingBox() { \/\/ dessine le cube ou l'objet est scann\u00e9\n\nstroke(255, 0, 0);\n\nline(axis.x, axis.y, axis.z, axis.x, axis.y + 100, axis.z);\n\nnoFill();\n\npushMatrix();\n\ntranslate(axis.x, axis.x + baseHeight + modelHeight \/ 2, axis.z);\n\nbox(modelWidth, modelHeight, modelWidth);\n\npopMatrix();\n\n}\n\nvoid scan() { \/\/ num\u00e9rise l'objet et ne prend en consid\u00e9ration que les nouveaux points \u00e0 une distance de plus d'1mm des autres\n\nfor (PVector v : scanPoints) {\n\nboolean newPoint = true;\n\nfor (PVector w : objectPoints) { \/\/ on regarde si les pixels de l'objet scann\u00e9 ne sont pas trop proches de la prise de vue pr\u00e9c\u00e9dente\n\nif (v.dist(w) &lt; 1)\n\nnewPoint = false;\n\n}\n\nif (newPoint) {\n\nobjectPoints.add(v.get());\n\n}\n\n}\n\nif (currentShot &lt; shotNumber.length-1) {\n\ncurrentShot++;\n\n}\n\nelse {\n\nscanning = false;\n\n}\n\narrived = false;\n\n}\n\n\/\/ Fonctions auxiliaires\n\nvoid updateObject(int scanWidth, int step) { \/\/ Fonction actualisant l'image et les valeurs scann\u00e9s\n\nint index;\n\nPVector realWorldPoint;\n\nscanPoints.clear();\n\nfloat angle = shotNumber[currentShot];\n\npushMatrix();\n\ntranslate(axis.x, axis.y, axis.z);\n\nrotateY(angle);\n\nline(0, 0, 100, 0);\n\npopMatrix();\n\nint xMin = (int) (kinect.depthWidth() \/ 2 - scanWidth \/ 2);\n\nint xMax = (int) (kinect.depthWidth() \/ 2 + scanWidth \/ 2);\n\nfor (int y = 0; y &lt; kinect.depthHeight(); y += step) {\n\nfor (int x = xMin; x &lt; xMax; x += step) {\n\nindex = x + (y * kinect.depthWidth());\n\nrealWorldPoint = kinect.depthMapRealWorld()[index];\n\nif (realWorldPoint.y &lt; modelHeight + baseHeight &amp;&amp; realWorldPoint.y &gt; baseHeight) {\n\nif (abs(realWorldPoint.x - axis.x) &lt; modelWidth \/ 2) { \/\/ V\u00e9rification de x\n\nif (realWorldPoint.z &lt; axis.z + modelWidth \/ 2 &amp;&amp; realWorldPoint.z &gt; axis.z -\n\nmodelWidth \/ 2) { \/\/ V\u00e9rification de z\n\nPVector rotatedPoint;\n\nrealWorldPoint.z -= axis.z;\n\nrealWorldPoint.x -= axis.x;\n\nrotatedPoint = vecRotY(realWorldPoint, angle);\n\nscanPoints.add(rotatedPoint.get());\n\n}\n\n}\n\n}\n\n}\n\n}\n\n}\n\nPVector vecRotY(PVector vecIn, float phi) { \/\/ Fonction permettant de prendre en compte la rotation de l'objet lors de la repr\u00e9sentation sur ordinateur\n\n\/\/ On consid\u00e8re une rotation autour de l'axe Y\n\nPVector rotatedVec = new PVector();\n\nrotatedVec.x = vecIn.x * cos(phi) - vecIn.z * sin(phi);\n\nrotatedVec.z = vecIn.x * sin(phi) + vecIn.z * cos(phi);\n\nrotatedVec.y = vecIn.y;\n\nreturn rotatedVec;\n\n}\n\npublic void keyPressed() { \/\/ Fonction permettant de d\u00e9finir des actions avec les touches du clavier\n\nswitch (key) {\n\ncase 'r': \/\/ 'r' reinitialise le scan\n\nobjectPoints.clear();\n\ncurrentShot = 0;\n\nscanning = false;\n\nbreak;\n\ncase 's': \/\/ 's' d\u00e9marre le scan de l'objet\n\nscanning = true;\n\narrived = false;\n\nbreak;\n\ncase 'e': \/\/ 'e' exporte le nuage de point ( format PLY )\n\nexportPly('0');\n\nbreak;\n\ncase '+': \/\/ '+' augmente la r\u00e9solution du nuage de points ( scanned lines ++ )\n\nscanLines++;\n\nprintln(scanLines);\n\nbreak;\n\ncase '-': \/\/ '-' diminue la r\u00e9solution du nuage de points ( scanned lines -- )\n\nscanLines--;\n\nprintln(scanLines);\n\nbreak;\n\n}\n\n}\n\nvoid exportPly(char key) { \/\/ Fonction permettant d'exporter le nuage de point au format PLY\n\nPrintWriter output;\n\nString viewPointFileName;\n\nviewPointFileName = \"myPoints\" + key + \".ply\";\n\noutput = createWriter(dataPath(viewPointFileName));\n\noutput.println(\"ply\");\n\noutput.println(\"format ascii 1.0\");\n\noutput.println(\"comment This is your Processing ply file\");\n\noutput.println(\"element vertex \" + (objectPoints.size()-1));\n\noutput.println(\"property float x\");\n\noutput.println(\"property float y\");\n\noutput.println(\"property float z\");\n\noutput.println(\"end_header\");\n\nfor (int i = 0; i &lt; objectPoints.size() - 1; i++) {\n\noutput.println((objectPoints.get(i).x \/ 1000) + \" \"\n\n+ (objectPoints.get(i).y \/ 1000) + \" \"\n\n+ (objectPoints.get(i).z \/ 1000) + \" \");\n\n}\n\noutput.flush(); \/\/ On \u00e9crit les donn\u00e9es dans le fichier\n\noutput.close(); \/\/ On termine le fichier\n\n}<\/pre>\n<h2>4.Traitement des donn\u00e9es re\u00e7ues par l&rsquo;ordinateur pour former le nuage de points<\/h2>\n<p>Apr\u00e8s avoir r\u00e9cup\u00e9r\u00e9 le nuage de points, il faut d\u00e9sormais reconstruire la surface initiale.\u00a0Pour cela plusieurs algorithmes existent, ainsi que des logiciels \u00ab open source \u00bb qui permettent de\u00a0r\u00e9aliser cette op\u00e9ration.<br \/>\nPour notre projet nous avons utilis\u00e9 MeshLab qui est un logiciel de traitement et d\u2019\u00e9dition de\u00a0maillage 3D.<br \/>\nMeshLab lit les fichiers .ply, il suffit donc d\u2019ouvrir le fichier pr\u00e9c\u00e9demment obtenu. Pour\u00a0certains fichiers, nous avons rencontr\u00e9 une erreur \u00ab Unexpected end of file \u00bb.Cette erreur survient\u00a0dans le cas o\u00f9 le nombre de points affich\u00e9s est diff\u00e9rent du nombre de points d\u00e9clar\u00e9s dans le\u00a0fichier .ply. Dans ce cas, il faut suivre les \u00e9tapes suivantes :<\/p>\n<ul>\n<li><span style=\"font-size: 13px;\">\u00a0Activer la vue des points (Points view) dans la barre du haut.<\/span><\/li>\n<li><span style=\"font-size: 13px;\">\u00a0Aller au menu Rendu (Render) et choisir Couleur\/Par Sommet (Colors\/Per Vertex).<\/span><\/li>\n<\/ul>\n<p>Apr\u00e8s cette manipulation, le nuage de points s\u2019affichera et sera color\u00e9. (figure 11)<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/13.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1195 aligncenter\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/13-300x161.png\" alt=\"1\" width=\"300\" height=\"161\" \/><\/a><em>Figure 11: Nuage de points 3D<\/em><\/p>\n<p><span style=\"font-size: 13px;\">On peut cependant observer des points incoh\u00e9rents, qu&rsquo;il faudra supprimer manuellement\u00a0<\/span>avant de lancer le processus de reconstruction de la forme. Ces points sont parfois dus \u00e0 un\u00a0mauvais calibrage de la Kinect par rapport \u00e0 la plate-forme tournante. Pour supprimer ces points, il\u00a0faut utiliser l\u2019outil S\u00e9lectionner Sommets (Select Vertexes) dans la barre du haut. Apr\u00e8s avoir\u00a0s\u00e9lectionn\u00e9 les points ind\u00e9sirables, il faut cliquer sur Supprimer Points S\u00e9lectionn\u00e9s (Delete\u00a0Selected Vertices) dans la barre du haut. Il est possible de s\u00e9lectionner plusieurs points en\u00a0gardant enfonc\u00e9e la touche Control du clavier.(figure 12)<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/21.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-1196\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/21-300x161.png\" alt=\"2\" width=\"300\" height=\"161\" \/><\/a><br \/>\n<em>Figure 12 : Points incoh\u00e9rents du nuage de points 3D<\/em><\/p>\n<p>La prochaine \u00e9tape est le processus de reconstruction. Tout d\u2019abord, il faut g\u00e9n\u00e9rer\u00a0le vecteur perpendiculaire \u00e0 la surface voisine de chaque point. Ceci permet \u00e0 l\u2019algorithme d\u2019avoir\u00a0les informations sur la topologie de l\u2019objet n\u00e9cessaires \u00e0 la reconstruction. Il faut aller au menu\u00a0Filtres\/Jeu de points (Filters\/Point Set) et choisir G\u00e9n\u00e9rer les normales aux jeux de points\u00a0(Compute normal for point sets). Dans le menu qui appara\u00eet, il faut choisir 16 comme nombre de\u00a0points voisins. \u00c0 la fin du processus, pour afficher les normales, il faut activer Afficher les\u00a0normales aux sommets (Show Vertex Normals) dans le menu Rendu (Render).<br \/>\nIl ne reste plus qu\u2019\u00e0 lancer le processus de reconstruction en allant dans Filtres\/Jeu de\u00a0points (Filters\/Point Set) et en choisissant Reconstruction de Surface : Poisson (Surface\u00a0Reconstruction : Poisson). Un menu s\u2019affiche alors, contenant plusieurs param\u00e8tres qu\u2019il faut\u00a0renseigner. Ces param\u00e8tres influent sur la pr\u00e9cision de la reconstruction, les valeurs qu\u2019il faut<br \/>\nrentrer d\u00e9pendent de l\u2019objet et de la qualit\u00e9 du scan. Il faut donc essayer de trouver les param\u00e8tres\u00a0optimaux pour chaque objet.<\/p>\n<p>Le mod\u00e8le 3D est g\u00e9n\u00e9r\u00e9 \u00e0 la fin du processus. (figure 13)<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/5.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-1199\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/5-300x161.png\" alt=\"5\" width=\"300\" height=\"161\" \/><\/a><br \/>\n<em>Figure 13 : Mod\u00e8le 3D<\/em><\/p>\n<p>Pour le colorer, il faut aller dans Filtres\/\u00c9chantillonnage (Filters\/Sampling) et choisir\u00a0Transfert des attributs des points (Vertex Attributes Transfer), choisir comme source le nuage de\u00a0points et comme cible le maillage Poisson, cocher Transfert de couleur (Transfer Color) puis\u00a0appliquer. Si le r\u00e9sultat n\u2019est pas satisfaisant, il faut transf\u00e9rer la g\u00e9om\u00e9trie aussi dans le menu<br \/>\npr\u00e9c\u00e9dent. Il suffit donc de cocher g\u00e9om\u00e9trie aussi avant d\u2019appuyer sur appliquer. (figure 14)<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1199 aligncenter\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/5-300x161.png\" alt=\"5\" width=\"300\" height=\"161\" \/><\/p>\n<p style=\"text-align: center;\"><em>Figure 14 : Mod\u00e8le 3D color\u00e9<\/em><\/p>\n<p style=\"text-align: left;\">Ensuite il faut lisser le mod\u00e8le, pour cela il faut aller dans Filtres\/Lissage, Car\u00e9nage,\u00a0D\u00e9formation (Filters\/Smoothing, Fairing, Deformation) et appliquer HC Lissage Laplacien (HC\u00a0Laplacian Smooth) autant de fois qu\u2019il le faut pour avoir une surface suffisamment lisse. (figure 15)<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/6.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-1200 aligncenter\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/6-300x161.png\" alt=\"6\" width=\"300\" height=\"161\" \/><\/a> <em>Figure 15 : Mod\u00e9le 3D color\u00e9 et liss\u00e9<\/em><\/p>\n<p><span style=\"font-size: 13px;\">MeshLab permet d\u2019exporter ce mod\u00e8le dans diff\u00e9rents formats pouvant \u00eatre utilis\u00e9s par un<\/span><br \/>\nlogiciel de mod\u00e9lisation 3D, mais aussi par une imprimante 3D.<\/p>\n<h2>5.Le syst\u00e8me Final<\/h2>\n<p><a href=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/Nouvelle-image-bitmap.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1202 aligncenter\" src=\"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-content\/uploads\/archives\/2013\/06\/Nouvelle-image-bitmap-300x165.jpg\" alt=\"Nouvelle image bitmap\" width=\"300\" height=\"165\" \/><\/a><\/p>\n<p style=\"text-align: center;\"><em>Figure 16:Sch\u00e9ma du syst\u00e8me global<\/em><\/p>\n<p>Les diff\u00e9rentes parties pr\u00e9sent\u00e9es pr\u00e9c\u00e9demment communiquent entre elles pour r\u00e9aliser\u00a0le scan de l&rsquo;objet. L&rsquo;ordinateur commande au plateau de tourner. Quand la lumi\u00e8re est d\u00e9tect\u00e9e\u00a0par l&rsquo;une des photor\u00e9sistances, l&rsquo;Arduino envoie un message \u00e0 l&rsquo;ordinateur. D\u00e8s lors l&rsquo;ordinateur\u00a0envoie un ordre \u00e0 la Kinect pour lancer la capture. Le nuage de points r\u00e9sultant est enregistr\u00e9\u00a0gr\u00e2ce \u00e0 Processing. Ce processus est r\u00e9it\u00e9r\u00e9 trois fois pour r\u00e9cup\u00e9rer trois nuages de points qui\u00a0sont ensuite trait\u00e9s par Processing pour construire le nuage 3D.<br \/>\nCe nuage est enfin trait\u00e9 sur Meshlab pour g\u00e9n\u00e9rer le mod\u00e8le 3D.<\/p>\n","protected":false},"author":1,"featured_media":984,"parent":0,"menu_order":0,"template":"","meta":{"_acf_changed":false,"footnotes":""},"categories":[15,20],"type_projet":[],"class_list":["post-979","projet","type-projet","status-publish","has-post-thumbnail","hentry","category-formation","category-projets"],"acf":{"objectifs":"","categorie":false,"fichiers_associes":null},"_links":{"self":[{"href":"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-json\/wp\/v2\/projet\/979","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-json\/wp\/v2\/projet"}],"about":[{"href":"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-json\/wp\/v2\/types\/projet"}],"author":[{"embeddable":true,"href":"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-json\/wp\/v2\/users\/1"}],"version-history":[{"count":1,"href":"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-json\/wp\/v2\/projet\/979\/revisions"}],"predecessor-version":[{"id":10537,"href":"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-json\/wp\/v2\/projet\/979\/revisions\/10537"}],"wp:attachment":[{"href":"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-json\/wp\/v2\/media?parent=979"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-json\/wp\/v2\/categories?post=979"},{"taxonomy":"type_projet","embeddable":true,"href":"https:\/\/hub.imt-atlantique.fr\/fablab\/wp-json\/wp\/v2\/type_projet?post=979"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}