IMA3/IMA4 2018/2020 P18 : Différence entre versions
(→Initialisation des valeurs et tableaux) |
(→La boucle d’affichage) |
||
Ligne 1 215 : | Ligne 1 215 : | ||
Chaque pixel sera affiché sous la forme d’un caractère qui correspondra à la valeur du pixel. | Chaque pixel sera affiché sous la forme d’un caractère qui correspondra à la valeur du pixel. | ||
On a créé un tableau pixels[ ] qui contient les caractères à afficher | On a créé un tableau pixels[ ] qui contient les caractères à afficher | ||
− | static const char* pixels =" .0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | + | static const char* pixels = " .0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
Comme il n’est pas possible d’avoir autant de caractères différents que de valeurs de gradient, on fait un écrêtage à 32 (cette valeur aurait pu être augmenté à 37 avec la table que nous avons définie). | Comme il n’est pas possible d’avoir autant de caractères différents que de valeurs de gradient, on fait un écrêtage à 32 (cette valeur aurait pu être augmenté à 37 avec la table que nous avons définie). | ||
+ | |||
for (y = 0; y < rows; y++) | for (y = 0; y < rows; y++) | ||
Ligne 1 235 : | Ligne 1 236 : | ||
rows et row_length correspondent une ligne et colonne de l’image divisé par notre ratio 8*5. Nous allons initialiser pixel_index avec notre tableau Sr[ ][ ]/8 , 8 est notre ratio de la hauteur. | rows et row_length correspondent une ligne et colonne de l’image divisé par notre ratio 8*5. Nous allons initialiser pixel_index avec notre tableau Sr[ ][ ]/8 , 8 est notre ratio de la hauteur. | ||
− | Ensuite il suffit de déterminer un seuil à ne pas dépasser pour le contour final. Si notre tableau est supérieur à 32 caractères alors il sera égal à 32 pour ne pas déborder dans la recherche d’un élément en dehors des limites du tableau pixel[ ]. Puis on affiche notre structure pour visualiser les contours. | + | Ensuite il suffit de déterminer un seuil à ne pas dépasser pour le contour final. Si notre tableau est supérieur à 32 caractères alors il sera égal à 32 pour ne pas déborder dans la recherche d’un élément en dehors des limites du tableau pixel[ ]. Puis on affiche notre structure pour visualiser les contours. |
====Limites==== | ====Limites==== |
Version du 2 mai 2020 à 15:20
Sommaire
- 1 Présentation générale
- 2 Analyse du projet
- 3 Préparation du projet
- 4 Réalisation du Projet
- 4.1 Projet S6
- 4.2 Projet S7
- 4.3 Projet S8
- 4.4 Semaine 1
- 4.5 Semaine 2
- 4.6 Semaine 3
- 4.7 Semaine 4
- 4.8 Semaine 5
- 4.9 Configuration et intsallation du SDK sur la raspberry
- 4.10 Code de détection de Profondeur (Controle capteur)
- 4.11 Algorithme
- 4.12 Mise au point de l'avancement du projet et tâches à réaliser (18/03/2020)
- 4.13 Code Capteur (final)
- 4.13.1 Test de récupération des données de profondeur
- 4.13.2 Traitement du stream d'images de profondeurs
- 4.13.3 Autre méthode de traitement d’image
- 4.14 Exécution des codes après modification
- 4.15 Le Hat
Présentation générale
Point important
Le projet a été proposé pour venir en aide à Florian, un adolescent malvoyant de 16 ans suivi à l'Institut d’Éducation Motrice (IEM) de Lille. Le dispositif et les choix de fonctionnement dépendent donc des besoins spécifiques de cet utilisateur. Nous avons eu l'occasion de le rencontrer le 7 mai 2019 et de mieux appréhender ce qu'il attendait du projet.
Ainsi notre travail avant cette date a été fait en supposant en grande partie les besoins de l'adolescent avec qui nous n'avions pas eu de contact préalable. Dans la suite, se trouve notre interprétation première du projet puis les modifications suite à la rencontre.
Description
Le projet consiste à réaliser un dispositif permettant le déplacement en toute sécurité et autonomie d'une personne malvoyante. Le dispositif donne la possibilité de se déplacer sur des terrains aussi bien plats qu'accidentés sans autre aide extérieure. Placé sur son l'épaule, la personne malvoyante est en mesure de se diriger par l'intermédiaire des commandes vocales et des pressions, rappelant celles d'une main d'un accompagnateur humain, qu'elle reçoit du dispositif.
- Suite à la rencontre, nous avons appris que le dispositif sera utilisé uniquement dans un contexte de promenades en forêt ou en montagne.
Objectifs
Le dispositif permet à la personne malvoyante une grande autonomie en étant prévenue lorsqu'un obstacle se présente à elle - qu'il soit fixe ou mobile - et lui indique comment l'éviter ou le contourner. Il s'agit d'être une alternative accessible à tous aux solutions existantes telles que les cannes ou les chiens guides, en étant aussi performante tout en apportant un aspect humain via les pressions sur l'épaule. Tout en conservant l'autonomie, la sensation sécurisante d'avoir une personne à ces côtés est présente. Cela est notamment important pour les enfants qui se sentent souvent plus rassurés avec un accompagnateur.
- L'adolescent n'utilise pas de dispositif d'aide au déplacement (ni canne, ni chien guide). Il n'est pas en mesure d'appréhender le relief, mais il parvient à se déplacer seul sur un terrain plat et dans un environnement connu. En cas de dénivelé il a besoin de la présence d'un accompagnateur près de lui et se déplace avec appréhension. Le but du dispositif est alors d'indiquer la différence de relief immédiat et la présence d'obstacles au sol en conservant le côté rassurant des pressions sur l'épaule et préventif par le biais indications sonores.
Analyse du projet
Positionnement par rapport à l'existant
Plusieurs dispositifs permettent de réduire l’appréhension du monde par les personnes malvoyantes. En effet, l'environnement, dans lequel on évolue, regorge d'obstacles mobiles ou non. Une rapide analyse est alors nécessaire pour augmenter la sécurité des utilisateurs. Tout d'abord, l'Homme semble être le plus adapté à évoluer dans la société, il se révèle alors être l'accompagnant le plus sécurisant. Cependant cela réduit considérablement l'autonomie de l'individu et reste coûteux si cette assistance est professionnelle. Une alternative peut être l'appel à un animal, tel que le chien, qui offre plus d'autonomie. Des alternatives technologiques existent. Par exemple, le boîtier BuzzClip qui permet de détecter les obstacles; néanmoins il détecte seulement les obstacles à la hauteur de son installation. Ou encore, l'Ultracane qui permet le signalement d'obstacles dans l'axe de la canne.
Analyse du premier concurrent
Nos premiers concurrent sont les chiens d'aveugles.
Le chien est reconnu pour sa sensibilité à l’environnement et aussi sa fidélité à son maître. Le dressage lui permet d'analyser et de réagir, tout en prévenant son maître, à une situation particulière. Cependant cette formation a un coût élevé : de 15 000 euros à 19 000 euros. L'animal demande aussi une attention supplémentairement (vétérinaire, nourriture...). De plus, l'accompagnement d'un animal peut être limité ou interdit dans certains lieux ce qui peut diminuer la liberté de l'utilisateur dans ses mouvements.
Analyse du second concurrent
Notre deuxième concurrent est l'Ultracane.
La cane est répandue pour sa facilité d'utilisation. L'Ultracane permet, de plus, de détecter les obstacles à moins de 4 mètres (environ 2 mètres au bout de la canne qui mesure 1,60 mètre) grâce à la technologie des ultrasons. Un obstacle se traduit par des vibrations dans la canne pour prévenir l'utilisateur. Cependant ces obstacles doivent se situer dans l'axe de la canne. Ceci force alors l'utilisateur à mettre en mouvement sa canne autour de lui ce qui peut diminuer la compréhension des vibrations ; mais aussi porte l'attention sur le malvoyant. Ainsi, ce dispositif permet une autonomie complète, une sécurité plus ou moins établie, néanmoins l'utilisation n'est pas discrète.
Scénario d'usage du produit ou du concept envisagé
Dans notre scénario d'usage on prend le cas d'une jeune malvoyante nommée Léa. Elle désire un moment de solitude et décide de s'aventurer dans la foret. Pour autant elle veut se sentir en sécurité face à d’éventuels obstacles ou imprévus sur son chemin. Elle active alors son "système d'aide au déplacement", le pose sur son épaule et commence sa promenade. Après une semaine particulièrement pluvieuse et venteuse, la foret est pleine d'obstacles. Dès l’entrée de Lea sur le sentier, celle-ci ressent une pression au milieu de son épaule et entend "attention obstacle en hauteur", elle comprend qu'elle doit être vigilante par rapport à un obstacle tel qu'une branche et lève donc sa main pour la repousser. En s'aventurant plus profondément, elle ressent des pressions successives sur son épaule et entends "attention obstacle mobile à gauche à 40m". Elle s’arrête alors, recule et prends la meilleure décision pour contourner l'obstacle en toute sécurité sachant que si l'obstacle représentait encore un danger, son "système d'aide au déplacement" lui aurait signalé sa présence par un nouveaux signal auditif. Elle continue donc sa promenade en suivant les pressions (gauche, droite, vers le bas) et indications de son "système d'aide au déplacement" par rapport aux différents obstacles et le type de terrain (exemple "attention obstacle en hauteur" ou "terrain en pente glissant"...). Léa a donc pu profiter d'une après midi très apaisante, en autonomie complète grâce a son "système d'aide au déplacement". Elle se relevera demain pour une nouvelle aventure en ville ou en campagne toujours guidée par son "système d'aide au déplacement".
Question(s) difficile(s)
- Comment gérer l'autonomie du dispositif pour éviter à l'utilisateur de devoir le recharger trop régulièrement ?
- Comment réduire l'encombrement du dispositif pour une utilisation aussi discrète que possible ?
Réponse à la question difficile
- Le dispositif étant destiné à un usage occasionnel, sur la durée d'une balade ou d'une petite randonnée soit une à quatre heures, il peut y avoir deux solutions. L'usage de piles à recharger ou d'une batterie externe légère peut-être envisagé.
- Nous avions envisagé un patch pour une utilisation discrète. Néanmoins, suite à la rencontre avec l'adolescent, un dispositif sous forme de main placé sur un harnais ne le gêne pas. Il s'agit donc de placer l'ensemble du dispositif sur le harnais. La mains étant imprimée en 3D, la taille est réglable pour correspondre aux dimensions l'épaule de l'adolescent. Lors des semestres 7 et 8, nous réaliserons un PCB pour limiter au maximum la place occupée par les différents composants électronique ce qui permettra de réduire l'encombrement qui en découle.
Bibliographie et webographie
Préparation du projet
Cahier des charges du groupe
Notre dispositif doit permettre d'aider une personne malvoyante à se déplacer. Il est constitué de deux parties communicant entre elles par Bluetooth. La première est placée sur l'épaule et sert à prévenir l'utilisateur de la présence d'obstacles par l'intermédiaire de vibrations et d'indications vocales. Elles proposent également à l'utilisateur un moyen de les éviter. La seconde partie contient les capteurs servant à analyser l'environnement. Ils doivent permettre de détecter des obstacles immobiles proches et ceux en mouvement, déterminer la présence de dénivelé (capteur de distance). Éventuellement de déterminer où se situent les limites de la route ou du chemin qu'emprunte l'utilisateur (par traitement d'images).
À l'issue du semestre 6 :
- Réaliser un prototype du dispositif en vue d'une rencontre avec l’ergothérapeute à l'origine du sujet et l'adolescent malvoyant et leur présenter nos deux idées de dispositif. La présentation nous permettra d'avoir des retours sur notre vision du projet et de l'adapter concrètement aux besoins de l'utilisateur durant la quatrième année, par exemple en ne gardant qu'une seule version ou en les combinant.
- La première version est un dispositif discret sous la forme d'un patch à placer sur l'épaule. Il guide par le biais de vibrations (moteurs vibrants) dans la direction à emprunter accompagnées par des informations sonores sur l'environnement, la nature de la position des obstacles. En cas de danger imminent l'utilisateur est prévenu par une alerte sonore couplée aux vibrations.
- La seconde est une main réalisée à l'imprimante 3D dans le but de conserver l'aspect rassurant de la main d'un accompagnateur. Comme le patch elle utilise les vibrations. La main applique des pressions sur l'épaule en cas de détection d'un danger et est légèrement chauffante pour rappeler le contact humain.
Modification du cahier des charges suite à la rencontre avec l'adolescent
- La détection d'obstacles en hauteur, mobiles ou immobiles n'est plus une priorité. Le dispositif doit avant tout prévenir du changement de relief.
- Le côté chauffant de la main ou du patch n'est pas souhaité par Florian.
- Les deux dispositifs pourront être combinés. La main avec les pressions pouvant se placer sur un manteau et être utilisée même par temps froid. Le patch avec les vibrations disposé sur l'épaule ou un t-shirt.
Projet IMA4 :
- Réaliser le dispositif sous ses deux formes, le patch et la main, qui pourront se combiner.
- La main effectue des pressions plus ou moins importantes en fonction du danger et peut se placer sur un manteau
- Le patch vibre selon les obstacles et peut se placer sur la peau ou un t-shirt
- Faire le choix définitif des capteurs à utiliser.
- Associer la partie détection de l'environnement par les différents capteurs au patch et à la main grâce à une liaison directe ou Bluetooth.
- Réaliser la partie indications sonores :
- Liaisons avec les capteurs et la main ou le patch
- Enregistrement des phrases types
- Utilisation d'oreillettes
Cahier des charges des équipes
Equipe 1: Travail sur le dispositif (main et/ou patch)
La partie principale de notre projet consiste a élaborer un dispositif portable permettant de guider une personne malvoyante, celui ci doit être :
- Portable et léger (cette partie doit pouvoir être positionnée sur l’épaule d'un adolescent de 16 ans)
- Peu encombrant mais solide : Le dispositif sera utilisé lors de balades en foret, donc d'activités physique; il doit donc être suffisamment solide et stable pour résister a l’activité et aux mouvements de l'enfant sans le gêner.
- Discret et esthétique.
- Remplacer tout ce que la main humaine représente de part son cote rassurant (chaleur), ses indication (indications sonores) et ses pressions face a un danger.
Equipe 2: Travail Capteurs
La seconde partie du projet et la plus technique est la captation de l'environnement. Notre dispositif doit:
- Capter l'environnement de la personne malvoyante
- Mesurer la distance séparant l'utilisateur a l'obstacle
- Analyser les données observées pour déterminer la nature de l'obstacle, sa position et comment l’éviter
- Communique avec la deuxième partie du projet pour transmettre, a l'utilisateur, les bonnes informations (les plus utiles, pour que l'adolescent puisse correctement les interpréter)
Liste des tâches à effectuer
Equipe 1: Travail sur le dispositif (main et/ou patch)
Projet S6:
L'objectif à la fin de ce semestre est d'avoir un premier prototype fonctionnel démontrant le fonctionnement général du projet:
- Effectuer des recherches pour trouver une matière suffisamment souple pour se posée discrètement sur l’épaule et fine pour faire passer les vibrations.
- Trouver un design de main articulée à imprimer en 3D
- Élaborer le code pour que le dispositif réagisse en fonction des indication extérieurs:
- Main : la main en 3D doit se resserrer lorsqu'un obstacle est trop proche, les moteurs vibrant qui lui sont intégrés doivent vibrer en indiquant vers quelle direction est l'obstacle.
- Patch : Le patch fonctionne comme les tableaux a LED : en fonction de la position de l'obstacle , une partie des moteurs vibreurs vont s'activer pour indiquer a l'utilisateur la provenance de l'obstacle (pour lui permettre de réagir) exemple: si l'obstacle est devant a gauche, seule une partie spécifique des moteurs ( a gauche vers arrière de l’épaule ) seront mobilisés.
- Trouver le microprocesseur adapter pour avoir le dispositif le moins encombrant réunissant toutes les fonctions nécessaires:
- il doit commander une vingtaine de moteurs vibrants
- Doit avoir la possibilité de communication en bluetooth (intégrée ou a l'aide d'un module à commander)
- Penser au design final du dispositif et étudier sa stabilité sur l’épaule de l’adolescent
Projet IMA4:
A la fin de cette année, le dispositif doit être complètement fonctionnel et adapté aux besoins de Florian. Ainsi, elle consistera à :
- Imprimer la version finale de la main 3D
- Sourcing: Commander tous les composants nécessaires pour faire le patch ET la main.
- Mettre en place la connexion bluetooth ou la communication directe (câble) pour envoyer les indications sonores
- Compléter le code pour que le dispositif réagisse correctement au données envoyées par la partie capteur
- Programmer le microprocesseur pour commander le patch
- Faire évoluer le design final du dispositif pour qu'il soit le plus stable et adaptable a la personnalité, aux envies et besoin de Florian.
Equipe 2: Travail Capteurs
Projet S6:
L'objectif de ce semestre est de réunir dans un rapport clair une étude des différents capteurs présents sur le marché.
- Identifier les capteurs potentiellement intéressants pour le projet
- Pour chaque capteur expliciter:
- Le principe de fonctionnement (technique)
- Utilisation
- Le type de données renvoyées
- Comment récupérer et analyser les données renvoyées par le capteur (traitement des données)
- Leur poids et prix
Projet IMA4:
L’élaboration de la partie capteur sera le plus gros enjeux de cette année. On devra:
- Se baser sur l'étude effectuée au S6 pour sélectionner le meilleur capteur pour la problématique de Florian (principalement la détection des reliefs)
- Implémenter le processus de traitement de données
- Implémenter le processus de communication avec le dispositif sur l’épaule (communication bluetooth ou filaire)
- Création du boitier + harnais contenant tout le dispositif de captation
Choix techniques : matériel et logiciel
Equipe 1 : Travail sur le dispositif (main et/ou patch)
- Matériel :
- Mini moteurs vibrants
- Servomoteur
- Carte Arduino → remplacée en IMA4 par un PCB et les composants nécessaires (microcontrôleur)
- Logiciel :
- Arduino
- Onshape pour la modélisation de la main
- Fritzing pour la réalisation du PCB en IMA4
Equipe 2 : Travail Capteurs
- Matériel :
- Capteur à ultrason
- Lidar
- Kinect
- Caméra
- Logiciel :
- Skanect
- ReconstructMe
- OpenCV
Autre
- Matériel :
- Module Bluetooth
- Harnais
Calendrier prévisionnel
Nous avons décider de rassembler toutes les taches que nous avons a faire dans un trello , pour rendre plus facile la manipulation et le suivi de l'avancement (réel) de chaque taches.
Lien vers notre Board Trello: [1]
Réalisation du Projet
Projet S6
Semaine 4
Réflexion
Face à la problématique qui nous a été dressé la première étape de notre projet sera la réflexion sur la forme et le comportement du dispositif. Nous devons intégrer à notre projet le caractère unique et spécifique du à l'handicap de l’utilisateur.
Concernant la forme de notre dispositif nous envisageons deux possibilités, sachant qu'un combinaison des deux serait tout à fait possible :
- Main : Un impression en 3D d'une main se rapprocherait de la réalité. Cette main devra être rassurante. Elle exercera une pression sur l'épaule de l'utilisateur pour indiquer un danger potentiel. De plus, au bout des doigts se trouvera des moteurs vibrants permettant de modérer l'information par rapport au degré de danger.
- Patch : Ce patch sera en contact avec la peau de l'utilisateur sur l'épaule.Tout autour se trouvera des moteurs vibrants. En divisant en 4 parties on pourra distinguer 4 indications différentes.
Pour compléter le système d'indication nous avons pensé à une indication auditive. Ainsi un système d’oreillettes semble être adapté et discret.
Pour indiquer un danger potentiel le systèmes doit analyser des données. Ces données peuvent être de nature différentes. On pourra utilisés différents capteurs : ultrasons,lidar,analyse d'image...
Semaine 5
Séance design
Lors de cette séance, nous avons rencontré une intervenante extérieure pour centrer notre projet autour de l'utilisateur.En effet, notre dispositif est destiné à être utilisé par un adolescent malvoyant, il est donc primordial de se mettre à sa place pour développer une solution pertinente.
La séance a commencé par une réflexion de groupe pour décrire notre projet et en donner nos perceptions. Cela nous a permis de nous rendre compte que si nous avions la motivation d'aider l'adolescent, nous étions déjà focalisées sur les solutions techniques. Il est important de prendre en compte que l'utilisateur peut s’être adapté et qu'il est trouvé une alternative à une fonction de notre dispositif, elle deviendra alors inutile.L'usager doit rester un point central.
À partir des informations du aux recherches, observations et rencontres, il s'agit de dégager les traits caractéristiques pour obtenir un usager type. C'est l'étape de la collecte. Nous avons eu ensuite la présentation de plusieurs méthodes a fin de réutiliser ces données pour mieux comprendre notre utilisateur :
- le Persona : fiche de présentation détaillée de l'utilisateur type qui doit constituer sa réalité reflétant sa personnalité,ses activités. Cette fiche a pour but de mettre en évidence les problématiques de l'utilisateur en lien avec sa fragilité.
- carte d'empathie : fiche décrivant toutes les sensations de l’utilisateur, ce qu’il entend, voit, dit, ressent, pense…
Après avoir mis l'usager au centre de notre réflexion, on peut établir un meilleur scénario d'usage.Ce dernier peut être réalisé sous différentes formes : un texte narratif, une bande dessinée ou un storyboard, une vidéo...
Pour appréhender de manière plus concrète notre scénario d'usage, nous avons testé la méthode Lego® Serious Play®. À l'aide d'un nombre limité de pièces de Lego nous avons recréé le scénario d'usage, ce qui nous a permis de réfléchir à certains cas particuliers que nous n'avions pas envisagé. Par exemple en cas d'obstacle mobile, comment notre dispositif doit réagir. À l'issue de cette séance, une rencontre avec l'adolescent semble indispensable afin de répondre pleinement au cahier de charge.
Semaine 6
Capteurs
Réflexion sur le type de capteur que l'on pourrait utiliser :
1. Capteur de distance :
- capteur à ultrasons HC-SR04 (celui utilisé habituellement avec un arduino) : renvoie la distance d'un obstacle immobile ou mobile (on peut en déduire la vitesse)
- capteur laser LIDAR-Lite v3 : même utilisation que le capteur à ultrason, permettrait d'avoir plus de précision.
Pour que les capteur de distance prennent en compte une plus large partie de l'environnement, nous réfléchissons à placer le capteur sur un servomoteur. Ainsi le capteur pourrait pivoter de haut en bas et prendre en compte la présence d'obstacles à la hauteur de la personne et les changements de dénivelé, les trous.
2. Camera (Stéréoscopie):
- détecter les formes dans le but de différencier un trou d'un rocher par exemple, éventuellement nommer les obstacles, pour que l'utilisateur se repère mieux (annonces vocales type : Attention, trou à 1m)
- détecter les zones de couleurs (par exemple en forêt : différencier un chemin gris du vert des plantes)
En utilisant une camera, se pose les questions de consommation et de puissance de calcul pour traiter les images en temps réel.
Lien: [2]
Semaine 7
Programme
Durant cette semaine nous nous sommes penchés sur le code qui permettra au prototype de fonctionner. Ce prototype est une illustration très générale et simplifiée du projet. Nous tenons à ce que ce soit le plus accessible pour le présenter aux utilisateurs (l'adolescent malvoyant/aveugle). Le code se décompose donc en 2 partie :
- la première pour lancer la prise de mesure par le capteur ultrasonore
- la seconde pour faire réagir la mains ( les servo et les moteurs vibrants) correctement
Le principe de fonctionnement de la main est qu'elle se refermera plus ou moins rapidement lorsqu'un obstacle est captée à une distance inférieure à une distance minimale. Tant que le dispositif est allumé on prend des mesures permettant de déterminer la distance entre l'obstacle et le capteur;on compare ensuite cette distance (et/ou vitesse) a des valeurs minimales préalablement enregistrées pour que la main réagisse plus rapidement si la vitesse est grande ou exerce une simple pression si l'obstacle est simplement immobile.
Pour ce faire, nous avons utilisé 2 servo-moteurs ainsi que 2 moteur vibrant (ceux disponibles). Après quelques soudures nous avons pu avancer sur le code.
Code a la fin de la semaine 7 :Fichier:Code main.zip
Code mesure de distance et alimentation du moteur vibreur.
Semaine 8
Composants
Pour limiter l'encombrement du dispositif, nous voulons réaliser un PCB. Une partie de l'équipe s'est donc intéressée au fonctionnement du logiciel Frizing.Au sein du PCB, nous prévoyons de placer un microcontrôleur ATMEGA2560. Nous avons regardé quelles pins utiliser pour relier les 24 moteurs vibrants prévus pour le patch.
Nous avons fais quelques recherches sur les composants qui nous seront nécessaire à la réalisation de notre projet. Ainsi, voici la liste des composants électroniques :
Composant | Référence | Fabricant | Image |
---|---|---|---|
Capteur à ultrasons |
HC-SR04 |
OSEPP Electonics |
|
Moteur Vibrateur en Disque 2.0 mm |
316040001 |
Seeed Studio |
|
Micro Servo Moteur SG90 9g |
TPSG90 |
Tower Pro |
|
Arduino MEGA 2560 |
A000067 |
Arduino |
Cependant à la fin de la séance, après avoir discuté avec notre encadrant, nous avons décidé de nous concentrer sur la réalisation du prototype de la main à présenter à l'adolescent avec le matériel déjà à notre disposition (une carte Arduino UNO, trois moteurs vibrants, deux servomoteurs) et de finalement réaliser le PCB et commander les autres composants durant l'année d'IMA4.
Nous avons sélectionné un modèle de main sur le site Thingiverse dans le but d'imprimer cette dernière avec une machine 3D du FABRICARIUM de l'école.
Lien du modèle de la main: [4]
Semaine 9
Modélisation de la main
Nous avions, la séance dernière, sélectionné un modèle de main. Malheureusement nous n'avons pas réussi à l'imprimer dû à un dysfonctionnement de la machine durant la nuit. La durée d'impression de cette main devenait alors trop importante par rapport au temps que nous disposions avant la rencontre avec l'adolescent. Ainsi nous avons dû modéliser une main simplifiée nécessitant moins d'heures d'impression et correspondant tout de même à notre cahier des charges. Nous l'avons modélisé sur le logiciel Onshape.
Cette modélisation rend approximativement compte des articulations d'une main humaine. Les dimensions ont été prises à partir d'une main réelle. Nous avons décidé de ne pas imprimer le pouce qui se révèle n’être pas nécessaire à notre prototype. Nous avons placé des encoches à l’intérieur des doigts afin de pouvoir y insérer des fils qui seront entraînés par les servomoteurs. Pour ces derniers nous avons créé des emplacements pour qu'ils s’intègrent au dispositif.
Semaine 10
Impression 3D de la main
La séance dernière nous avons réussi à finaliser notre fichier de la modélisation de notre main. Nous avons pu au cours de la semaine commencer l'impression. Nous avons alors pour cette séance toutes les pièces de notre main. Nous remarquons que la taille des encoches accueillant les servomoteurs est trop juste. Nous nous sommes donc rendus au FABRICARIUM pour modifier cela. Les différentes parties de la main peuvent donc ensuite être assemblées. On définit l’état initiale de la main comme sa position à plat.Pour permettre aux doigts de revenir de sorte que la main soit à son état initial on doit fixer des élastiques sur chaque doigts aux niveaux des 8 articulations.Ensuite nous avons installé les fil pour relier les doigts aux servomoteurs. Ces derniers,en tournant, permettent de s’opposer à la forces des élastiques. En parallèle, nous nous sommes chargé de réaliser un support pour présenter le prototype à l'équipe pédagogique de l'adolescent et lui même.
Semaine 11
Finalisation du prototype
Durant cette séance, nous avons finalisé notre prototype pour pouvoir le présenter durant la séance prochaine à Florian et son équipe pédagogique. Dans un premier temps nous avons testé le code. Celui-ci permet la rotation des servomoteurs lorsque notre capteur à ultrason détecte un obstacle à une distance inférieur à une distance que nous avons paramétré dans le code. Cette rotation entraîne alors le mouvement des doigts de façon à ce que la main reproduise les pressions exercées par un accompagnateur face à un obstacle. De plus, nous avons effectué quelques modifications sur la structure de la main pour que le prototype soit opérationnel.
Pour préparer notre rencontre avec Florian nous avons décidé de faire une présentation à l'aide de l'outil Powtoon afin d'expliquer de façon pédagogique notre dispositif et pouvoir en discuter sans inclure la partie technique de notre projet.
Lien Powtoon: [5]
Semaine 12
Rencontre avec Florian
Nous nous sommes rendu à l'Institut d'Education Motrice de Lille,école spécialisée Jules Ferry, afin de rencontrer Florian. Dans une première approche, nous nous sommes présentés afin de faire connaissance. Nous avons alors pu en savoir un peu plus sur les spécificités du handicap de Florian. Ce dernier ne distingue pas les reliefs. En effet, lorsqu’il se promène,son accompagnateur doit lui indiquer lorsqu'un trou ou un rocher se trouve sur le chemin. Seul, Florian peut marcher sur un chemin large, néanmoins il longe le côté pour se rassurer.L'indication exacte des distances n'est pas utile. Florian porte un corset, notre dispositif doit donc s'y adapter, un harnais semble être une bonne option.
Nous lui avons ensuite présenté une vidéo qui met en scène notre dispositif et aussi notre prototype de main. Florian semble préférer la main au patch. Certaines remarques de ce dernier nous permettent d'affiner notre cahier des charges. Florian possède une sensibilité qui devra être prise en compte au niveau des vibrations des moteurs vibrants. Florian démontre une certaine appréhension concernant la puissance de la main lorsque cette dernière exerce une pression. Ainsi,nous devrons alors être capable de régler la puissance de la main ainsi que la vitesse de vibration des moteurs vibreurs. De plus, il a été décidé que ces deux paramètres devront être proportionnels au danger. A noter que l'idée d'un fil chauffant n'est pas utile selon l'accompagnatrice de Florian. Florian donne au système audio une place très importante. Florian est un adolescent qui réagit vite selon son accompagnatrice et connaît parfaitement sa droite et sa gauche.Il sait alors se situer dans l'espace. Ainsi les indications apportées par l'audio peuvent être des phrases construites et précises. Florian aimerait que la voix soit celle d'un garçon de son âge, 16 ans, et aussi deux oreillettes.Le volume des indications doit être progressif en fonction du danger. Par exemple lorsque l'obstacle s’approche, le son augmente. De plus, Florian possède un téléphone, on pourra donc avoir accès au GPS.Ceci nous permettra d’améliorer notre dispositif. En effet,on pourra penser qu'en cas de danger important la géolocalisation permettra de prévenir un responsable.Un téléchargement de la carte du parcours désiré par Florian peut être effectué pour indiquer, rassurer ou même optimiser un parcours avec le moins de danger possible.
Travail supplémentaire
Nous avons rajouté au prototype de la main un moteur vibrant qui se déclenche en même temps que les pressions. Dans le code a été ajouté la détection de vitesses différentes. Si un obstacle arrive avec une faible vitesse, la main se referme une fois. Si la vitesse dépasse un certain seuil, la main réagit en se refermant deux fois de suite. Nous avons également ajouté un moteur vibrant sur le bout de la main. Il se déclenche dès qu'un obstacle est détecté et vibre en continu le temps que la main se plie et se déplie avant de se stopper.
Vidéo de la main lorsqu'un obstacle arrive à faible vitesse puis rapidement.
Recherche sur les différents types de capteurs que l'on pourrait utiliser :
Fichier:Capteur analyse environnement.pdf
Documents Rendus
Rapport pour le semestre 6 :
Fichier:Rapport Projet P18 IMA3 4 2018 2019.pdf
Projet S7
Semaine 1
La décision de fusionner avec le projet P17 a été prise.En, effet ces derniers conçoivent un dispositif d'aide au déplacement pour un enfant. Ainsi, les fonctions principales telle que le traitement d'informations ou encore le système de communication sont communes à nos projet.
Dans un premier temps nous nous sommes rassemblés pour présenter nos projets ainsi que les objectifs de chacun.Nous avons alors pu distinguer quatre axes de travail: les capteurs, les actionneurs, la gestion d'énergie et la réalisation d'un PCB.
Partie | P17&P18 | P18 |
---|---|---|
Capteurs |
Distance |
Relief, profondeur |
Actionneurs |
Minis moteurs vibrants |
Moteurs DC pour la main |
Gestion énergie |
Taille limitée |
|
PCB gérant les actionneurs, les indications sonores et la main pour P18 |
|
|
Nous nous sommes répartis dans plusieurs groupes.
Équipe 1 Partie sonore, actionneurs : Laurine, Clara
Équipe 2 Choix définitif des capteurs : Nour, Caroline, Émilie
Équipe 3 Gestion de l'énergie : Jing, Ming
La réalisation de la carte dépend des choix des éléments ci-dessus. Nous nous répartirons sa réalisation par la suite.
Semaine 2
Équipe 1 :
Notre premier travail a été de réfléchir sur les axes d'améliorations concernant le prototype proposé lors du semestre 6. En effet, la rencontre avec Florian nous a permis d'affirmer que le dispositif sous la forme d'une main lui plaisait. Ainsi,dans un premier temps, notre premier objectif a été d'améliorer l'aspect fonctionnel de notre dispositif, comme par exemple un choix de moteur plus performants et plus adapté ou encore trouver le moyen de faire varier l'intensité des moteurs vibrants. Nous pouvons également évoqué toute la partie sonore qui n’était pas encore présente sur le premier prototype. Puis dans un deuxième temps nous nous sommes penchées sur l’aspect design de la main mais aussi de tout le dispositif comportant la RPi,la batterie et la caméra.
Équipe 2 : Recherches pour choisir entre les capteurs D435 et D415 d'Intel
- D415
- D435
Semaine 3
Équipe 1 :
- Gestion des moteurs vibrants: Nous avons défini trois niveau de dangerosité d'un obstacle. Chaque niveau correspondra à une séquence de vibrations spécifique. Par exemple,pour un simple indication (danger faible), les vibrations seront espacées par un délai important.Pour un obstacle ou un changement de relief plus important,ce délai sera raccourcis. Et enfin, en cas de grand danger,les moteurs vibreront en continu.
- Pour avertir l’utilisateur des potentiels dangers nous avons décidé d’utiliser 4 moteurs vibrants. Chacun sera disposé sur la main afin de représenter, chacun, une direction : devant, derrière, droite et gauche.Nous avons, alors, décidé de garder un modèle de main gardant le pouce dans le but d'avoir quatre zones bien définis où placer les moteurs vibrants.
- Concernant la partie sonore, nous avons choisis le module MP3 DFR0299 pour stocker et exploiter les enregistrements d'indications sonores[6]. De plus,pour connecter les écouteurs,un port jack serait directement soudé sur le PCB.[7]
Équipe 2 :
- Besoin d'une Raspberry Pi 4 pour traiter les données du capteur D435.
- Angle de vision du D435 : 90°. On le positionne vers le bas pour déterminer les changements de reliefs. C'est ce sur quoi nous allons nous concentrer puisque la reconnaissance du dénivelé est ce qui fait défaut à Florian. Les obstacles en hauteur (type branche), pourraient être détectés par l'ajout d'un capteur à ultrason ou Lidar.
Semaine 4
- Les commandes de matériels se terminant fin novembre, nous avons constitué la liste des composants nécessaires pour les différentes parties du projet en cherchant les références exactes de chaque élément.
Peut etre mettre la liste des paniers ?
Semaine 5
- Création des composants nécessaires sur Altium.
- Définition du modèle de main et recherche pour son fonctionnement en fonction de notre choix de moteurs. Il s'agira de modifier le modèle sur Onshape pour créer l'espace nécessaire aux moteurs.
Possibilité de modèle de la main : [8] ,[9]
- Écriture du Wiki
Semaine 6
- Equipe 1
Selection definitve de la main : [10]
Tuto pour réaliser la main : [11]
La paume de la main peut contenir un PCB de 60mm x 45mm. L'impression 3D de cette main utilise du filament flexible (200 g) et du filament PLA (100 g).
Semaine 7
Documents Rendus
https://www.mouser.fr/datasheet/2/408/TB6612FNG_datasheet_en_20141001-708260.pdf schéma = p7
http://www.picaxe.com/docs/spe033.pdf schéma = p10
Projet S8
Réaction de la main selon différents cas rencontrés à faire valider par les encadrants de Florian :
Semaine 1
Equipe1
Récupération des fichiers source s de la main à imprimer en 3D: nous pouvons ainsi les modifier pour inclure l'espace nécessaire pour nos moteurs. Schéma main : à rajouter Prise en compte des dimensions des moteurs et du passage des fils pour fermer et ouvrir la main, emplacements pour les quatre minis moteurs vibrants au bout des doigts et sur le bord de la main, passage des fils d'alimentation des différents moteurs.
Sélection filament pour l'impression 3D :
Equipe2
Semaine 2
Nous avons récupéré une partie du matériel commandé au S7. Le capteur D435 Intel RealSense, les trois motoréducteur 2 axes et les contrôleurs moteurs.
Equipe 1
Les installations sur la Raspberry étant en cours, nous avons réalisé le circuit moteur et contrôleur moteur et nous nous sommes familiarisées avec leur fonctionnement à l'aide d'une Arduino.
Code de test du fonctionnement des moteurs sur Arduino :
// initialisation int PWMA=3; int AIN1=1; int AIN2=2; int a; // rapport cyclique entre 0 et 255 void setup() { // broches 3 (PWMA), 1 (AIN1), 2 (AIN2) en sorties pinMode(PWMA,OUTPUT); pinMode(AIN1,OUTPUT); pinMode(AIN2,OUTPUT); } void loop() { // on ne travaille que dans le sens 1 : AIN1=1, AIN2=0 digitalWrite(AIN1,0); digitalWrite(AIN2,1); // fixation d'une valeur pour la PWM a=200; analogWrite(PWMA, a); delay(200); a=10; analogWrite(PWMA, a); }
Equipe 2
Configuration de la raspberry pi3 pour faire les premiers Installation des paquets pour
- la librealsense2
- OpenCV
- Cmake...
Semaine 3
Equipe 2
Configuration de la raspberry:
Dans etc/network/interfaces rajouter
auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 172.26.145.199 netmask 255.255.255.0 gateway 172.26.145.254
ifdown/ifup eth0
NE PAS OUBLIER:
export https_proxy=http://proxy.plil.fr:3128
Semaine 4
Montage :
Semaine 5
Réaction de la main selon différents cas rencontrés à faire valider par les encadrants de Florian :
Configuration et intsallation du SDK sur la raspberry
Configuration de la raspberry
Sur la machine linux (zabeth) vérifier/activer la transmission de connexion réseau aux périphs:
iptables -P FORWARD ACCEPT
Raspberry pi 4
- la première étape consiste à configuer les interfaces réseaux :
Dans le fichier etc/network/interfaces:
auto lo iface lo inet loopback
auto eth0 iface eth0 inet static address 172.26.145.33 netmask 255.255.255.0 gateway 172.26.145.254
Dans le fichier etc/resolv.conf indiquer le serveur DNS de l'école:
nameserver 193.48.57.48
Pour enregistrer les changements:
ifup eth0 ifdown eth0
Pour vérifer les changements :
ip a
Ne pas oublier de :
Exporter le proxy de l'école : export https_proxy=http://proxy.plil.fr:3128
- Seconde étape :
Dans le menu raspi-config activer le mode experimental pour OpenGL ( dans Advanced options)
Raspberry pi 3+
La mémoire RAM d'une raspberry pi 3 est bien trop faible pour la compilation du SDK. Pour contourner ce problème on peut utiliser la cross-compilation https://solarianprogrammer.com/2018/05/06/building-gcc-cross-compiler-raspberry-pi/ ou regarder la crosscompile directement integrée sur ubuntu
Raspberry Pi 3 – Autre possibilité avec Ubuntu Mate 18.04
Configuration de la raspberry:
sudo raspi-config
→ Interfacing options → ssh → Advanced option → expand filesystem
Update Ubuntu.
Clone du répertoire librealsense :
git clone https://github.com/IntelRealSense/librealsense.git
Dans le dossier librealsense, installation des packets de base requis pour construire les binaires librealsense:
sudo apt-get install git libssl-dev libusb-1.0-0-dev pkg-config libgtk-3-dev sudo apt-get install libglfw3-dev libgl1-mesa-dev libglu1-mesa-dev
Exécutez le script d'autorisations Intel Realsense situé à partir du répertoire racine librealsense:
./scripts/setup_udev_rules.sh
Créez et appliquez des modules de noyau corrigés pour:
./scripts/patch-realsense-ubuntu-lts.sh
Le module de suivi nécessite le module du noyau hid_sensor_custom pour fonctionner correctement :
echo 'hid_sensor_custom' | sudo tee -a /etc/modules
Création du dossier build et exécuter Cmake :
cmake ../ -DBUILD_EXAMPLES=true - Construit librealsense avec les démos et les tutoriels
Recompiler et installer les binaires librealsense :
sudo make && sudo make install
Si problème lors de la compilation du à la mémoire RAM trop faible de la raspberry Pi3, un swapfile peut être effectué afin de rajouter par exemple 3 Go de RAM
Désactiver l’utilisation du swap :
sudo swapoff -a
Création du swapfile avec 3 Go :
# dd if=/dev/zero of=/swapfile bs=3072 count=1048576
Autorisation Root :
sudo chmod 600 /swapfile
Marquer le fichier comme espace swap :
sudo mkswap /swapfile
Activer le Swap :
sudo swapon /swapfile
Vérifier si le swap à été crée :
sudo swapon –show
Vérifier la structure finale de la partition :
free -h
Définir le fichier Swap comme permanent :
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Installation du SDK
Les étapes a suivre sont :
- git clone https://github.com/IntelRealSense/librealsense.git
- création d'un dossier build
- cmake -L .. 2> error
- Installation des libraries nécessaires manquantes indiquée dans le fichier error
- make
Pour tester : vérfier la présence des executables dans le dossier tools et/ou examples (test : connexion D435 et lancer rs-enumerate-devices)
Code de détection de Profondeur (Controle capteur)
Algorithme
L'algorithme de traitement des données se décompose en 3 "grandes" parties:
- Etape 1: Detection des devices et selection du premier
- Etape 2: Récupération des données
- Etape 3: Algorithme de traitement
Etape en amont:
l'idée serait de subdiviser l'image (frame) recueillie en 3 ou 4 plages de profondeurs ; On va faire un premier test sur un sol uniformément plat (dans une salle) en fixant le capteur au même niveau que l'aurait Florian; une fois le frame obtenu on détermine, pour chaque plage de profondeur la moyenne de tous les pixels concernés pour déterminer la valeur représentant le sol dans cette partie là. Puis on stocke ces valeurs ci dans des variables globales qui seront ré-utilisées dans le reste du code.
- Calcul de l'écart entre chaque pixel et la moyenne de sol de la zone du pixel, si cet écart est supérieur au seuil prédéfini, l'indice de ce pixel est ajoutée dans un tableau pixels_suspects.
- On teste ensuite l'étendue du l'obstacle en regardant la zone de pixels autours (un périmètre de 30 pixels autour) puis on garde la moyenne de profondeur de cette zone.
- On compare la moyenne des zones détectées pour en déterminer l'obstacle le plus "dangereu".
- une fois celui ci déterminé, on garde ces valeurs pour faire un tracking de cette obstacle le temps qu'il sorte du champ de vision ou qu'un obstacle plus dangereu soit détecté.
- on génère ensuite les 3 chiffres qui vont être transmis à l'autre partie du code (actionneurs)
- Le premier indiquera si c'est un obstacle en hauteur ou un trou (signe de la moyenne des écarts;
- Le second relèvera la dangerosité de l'obstacle : proportionnelle à la moyenne d'écart préalablement calculée;
- Le troisième indiquera la position dans l'espace (voir photo exemple)
La zone de captation du capteur peut être limitée grâce à:
The default scale of an Intel RealSense D400 device is one millimeter, allowing for a maximum expressive range of ~65 meters. The depth scale can be modified by calling rs2_set_option(...) with RS2_OPTION_DEPTH_UNITS, which specifies the number of meters per one increment of depth. 0.001 would indicate millimeter scale, while 0.01 would indicate centimeter scale.
Fonctions utilisées:
https://github.com/IntelRealSense/librealsense/wiki/Projection-in-RealSense-SDK-2.0*
http://docs.ros.org/kinetic/api/librealsense2/html/rs__advanced__mode_8h_source.html
Fixer l'echelle:
/** When called on a depth sensor, this method will return the number of meters represented by a single depth unit * \param[in] sensor depth sensor * \param[out] error if non-null, receives any error that occurs during this call, otherwise, errors are ignored * \return the number of meters represented by a single depth unit */ float rs2_get_depth_scale(rs2_sensor* sensor, rs2_error** error);
Filtrer les données:
/** * \brief sets the active region of interest to be used by auto-exposure algorithm * \param[in] sensor the RealSense sensor * \param[in] min_x lower horizontal bound in pixels * \param[in] min_y lower vertical bound in pixels * \param[in] max_x upper horizontal bound in pixels * \param[in] max_y upper vertical bound in pixels * \param[out] error if non-null, receives any error that occurs during this call, otherwise, errors are ignored */ void rs2_set_region_of_interest(const rs2_sensor* sensor, int min_x, int min_y, int max_x, int max_y, rs2_error** error);
Récupérer les données de profondeurs
/* Sets new values for STDepthTableControl, returns 0 if success */ void rs2_set_depth_table(rs2_device* dev, const STDepthTableControl* group, rs2_error** error);
/* Gets new values for STDepthTableControl, returns 0 if success */ void rs2_get_depth_table(rs2_device* dev, STDepthTableControl* group, int mode, rs2_error** error);
Mise au point de l'avancement du projet et tâches à réaliser (18/03/2020)
Partie matérielle
- Matériel disponible pour le travail à distance (Raspberry, capteur, montage, coque capteur)
- POINTS INCERTAINS :
- Récupération du hat pour souder les composants
- Impression 3D de la main puis sa récupération
- Liaison des différentes parties sur le harnais
Partie réalisation
- Code du capteur : Fonction de moyennage faite. Il reste à délimiter les différentes zones, étalonner, découper les frames reçues, détecter des trous et des reliefs en utilisant les fonctions déjà réalisées
- Code des actionneurs : lancer tous les threads et les fermer comme souhaité, notamment au niveau de l'audio
- Faire la liaison entre les deux codes
- Coque du capteur imprimée. Correction d'imperfections d'impression grâce à une fine coque en silicone servant également à la protection
- Début de réalisation du harnais
Organisation
- Création d'un git
- Lien : https://archives.plil.fr/nourekhlas/P18.git
- Ajout de tous les codes
- Réalisation d'un document précisant le découpage du code de gestion du capteur permettant la répartition du travail Fichier:Modularite code p18.pdf
Code Capteur (final)
Test de récupération des données de profondeur
Traitement du stream d'images de profondeurs
Le code c'est finalement articulé sous 4 grandes parties:
Récupération des données du flux et leur transformation en matrice de profondeur:
- Cette partie a été relativement rapide, on s'est principalement basée sur les fonctions fournies par le SDK librealsense et les exemples. Des fonctions spécifiques nous permettre de configurer le flux de données (résolution,format,fps...) rs2_config_enable_stream, puis rs2_get_video_stream_resolution pour obtenir le profil réel du flux; puis enfin rs2_get_frame_data.
La fonction get frame data nous permet de récupérer un tableau de la profondeur correspondant a chaque pixel du frame. (rs2_get_frame_data renvoie un unint16_t *)
Premier filtrage pour cibler la zone voulue:
- Après notre discussion avec Florian et ses accompagnatrices nous avons décidez de se focaliser principalement sur les reliefs et sur le sol. Cette décision nous a permis de :
- Fortement limiter le nuage de points
- Définir une position claire du capteur sur le corps de Florian -> Facilite la conception du harnais.
Pour une personne de la taille de florian, avoir une vue plus ou moins complète de 50cm a 3m devant semblait correcte. Nous avons donc fixer la plage de traitement aux profondeurs se situant entre 1m et 3m de profondeurs (avec une marge). Ces paramètres sont fixes en tant que Macros aux début du code et peuvent être modifiés après les essais pratiques. Pour déterminer l’unité de mesure correspondant a 1 metres on fixe une variable one_metre en utilisant une fonction également fournie par le sdk get_depth_unit_value() (1m = 1.0/get_depth_unit_value()). On effectue après un premier tri du tableau de profondeur que nous possédons en fixant a 0 tous les pixels a extérieure de notre intervalle [z_min;z_max]. Grace a ce premier filtrage on divise en moyenne de 2-3 le nombre de valeurs a traiter dans les phases suivantes.
Élimination du sol:
- Théorie
Nous sommes parties de l’hypothèse que le capteur sera correctement fixe sur le harnais pour vérifier les distances fixées sur ce croquis a droite (Valeurs fixées en macros). Nous avons ensuite diviser l'image de profondeur perçue en NB_ZONES différentes. Chaque zone est espacée de l'autre d'un pas_zone qui nous permet en utilisant une simple règle de Pythagore de déterminer la profondeur moyenne de la zone.
Après avoir déterminer ces seuils, on élimine le sol en fixant a 0 toutes les valeurs a l’intérieur de l'intervalle [seuil_zone[zone]-dz ; seuil_zone[zone]+dz]
A la fin de ces deux filtrage il ne reste plus dans cette matrice que les anomalies ou les profondeurs qui semblent suspects selon nos critères de sélection.
On obtient donc une matrice comme celle ci ou les X correspondent a des valeurs non nulles de profondeurs.t Cette dispositions de valeurs nous fait donc penser a des clusters (rassemblements de données présentant les mêmes caractéristiques).
- Limites
- Cette méthode de détection de sol n'est absolument pas la méthode idéale. L’idéal serait de faire une détection dynamique (en temps réel) en revanche en augmentant le nombre de zones (en diminuant le pas) on obtient un résultat plutôt correct.
- Le second point négatif de cet méthode est la détermination de la marge dz que l'on fixe. A travers les tests pratiques on pourras déterminer un dz cohérent avec le type de sol sur lequel se balade Florian et des conditions d'utilisations.
Détection des Clusters dans la matrice et leur traitement:
Ce qui était parti d'une intention de détection de clusters c'est transformer en une détection de voisins. L’idée principale est donc de détecter un pixel dont la valeur est différent de 0, une fois celui ci détecté, une structure Cluster_t est initialisée:
typedef struct cluster{ int moyenne; int zone; int type; int debut_y; int debut_x; int taille; int danger; } Cluster_t
typedef struct list_c{ struct cluster c[MAX_CLUSTER]; int nb_clusters; }List_c;
Ces clusters sont ensuite identifies par une fonction clust_detec pour en déterminer la taille et la valeur moyenne et donc en ressortir un indice de dangerosité. Les Clusters sont tous ensuite classes dans une liste ordonnée dans l'ordre croissant de dangerosité puis le(s) clusters les plus dangereux seront signalés, sous la forme de 3 digits, au code des actionneurs grâce a une FDM IPC.
- Récursivité et ses limites
La première approche que j'ai eu , était pour moi la plus intuitive : la récursion.
void clust_detec(int ** depth,int rows,int row_length,int x,int y,int * taille,int * moyenne) { if(x<0||x>=rows||y<0||y>=row_length) return; int ind= x*row_length+y; if((*depth)[ind]==0) return; *moyenne += (*depth)[ind]; (*taille)=(*taille)+1; (*depth)[ind]=0; //Itération sur les voisins clust_detec(depth,rows,row_length,x+1,y,taille,moyenne); clust_detec(depth,rows,row_length,x-1,y,taille,moyenne); clust_detec(depth,rows,row_length,x,y+1,taille,moyenne); clust_detec(depth,rows,row_length,x,y-1,taille,moyenne); }
Ce code est fonctionnel et donne des résultats correctes mais il est extrêmement coûteux en termes de mémoire sur la stack. En effet la pile d’exécution est inondée par des appels récursifs de clust_detec et génère donc, au bout d'un certain moment, une erreur de segmentation fault (stack overflow).
- Non récursif
- Création d'une stack / queue qui remplace la file d’exécution.
Pour régler ce problème de stack overflow et après des recherches intensive sur internet, j'ai eu la brillante idée de créer une structure stack qui me permettra de simuler/remplacer le comportement de la file d’exécution mais dans un espace mémoire plus grand.
struct stack { int maxsize; // define max capacity of stack int top; struct coordonnees *items; };
struct stack* newStack(int capacity) { struct stack *pt = (struct stack*)malloc(sizeof(struct stack));
pt->maxsize = capacity; pt->top = -1; pt->items = (struct coordonnees *)malloc(sizeof(struct coordonnees) * capacity);
return pt; }
Le code source a été trouvé sur internet puis remodelisé pour s'adapter a notre utilisation de la stack. Les fonctionnalités push(SP,x,y); pop(SP); isEmpty ... sont présentes dans l'archive git (cf lien) En C++ , C# ou java on aurait pu directement utiliser la classe stack implémentée.
- Flood fill non récursif
Pour transformer l'algorithme cluster_detec en un algorithme non récursif je me suis inspirée des algorithmes Flood Fill. En effet ces algorithmes sont utilisés pour détecter les pixel d'une couleur spécifique puis changer sa couleur et celles de tous ses voisins si elles ne correspondent pas à la couleur désirée. Souvent implémenté de manière récursive, cet algorithme récursif a montré ses limites lorsqu'on traite d'un grand nombre de données. c'est pour cela que des algorithmes non récursif de Flood fill ont émergés [14] A l'aide de l'algorithme présenté comme réponse sur ce lien j'ai pu élaborer une version non récursive de notre cluster_detec qui possède des résultats identiques sans les problèmes de stack overflow.
void clust_detect_nr(int ** depth,int rows,int row_length,int x,int y,int * taille,int * moyenne) { static const int dx[4] = {0, 1, 0, -1}; static const int dy[4] = {-1, 0, 1, 0}; struct stack * sp=newStack(rows*row_length); int i=0; push(sp,x,y); while(!isEmpty(sp)) { struct coordonnees c = pop(sp); //printf("pop\n"); for(int i = 0; i < 4; i++) { int vx = c.x + dx[i]; int vy = c.y + dy[i]; if(vx >= 0 && vx < rows && vy >= 0 && vy < row_length && (*depth)[vx*row_length+vy] !=0) { *moyenne += (*depth)[ vx*row_length+vy]; (*taille)=(*taille)+1; (*depth)[vx*row_length+vy]=0; push(sp,vx,vy); } }} free(sp->items); free(sp); return; }
Transmission des données par IPC:
On utilise les librairies systèmes (valables sur raspbian)
#include <sys/wait.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/msg.h> #include <sys/stat.h>
libipc :Fichier:Libipc.zip
//IPC #define CLE_MSG (key_t)1000 #define TYPE_REPONSE_CLIENT 3
typedef struct {
int digit_y; int digit_type; int digit_danger; } infos_t;
typedef struct { int zone; int type; int danger; } donnees_t;
/* Structures pour les reponses */ typedef struct { long type; donnees_t donnees; } reponseClient_t;
int recuperationFDM (void) { int msgid; if((msgid = msgget((key_t)CLE_MSG, 0)) == -1) { perror("Erreur de recuperation de la FDM\n"); exit(EXIT_FAILURE); } printf("msgidClient %d\n",msgid); return msgid; }
void envoiReponseClientFDM (donnees_t donnees) { reponseClient_t reponse; /* Récupération de la file de messages */ int msgid = recuperationFDM (); /* Envoi de la reponse */ reponse.type=TYPE_REPONSE_CLIENT; reponse.donnees=donnees; if(msgsnd(msgid, &reponse, sizeof(reponseClient_t) - sizeof(long), 0) == -1) { perror("Erreur d'envoi de la réponse\n"); exit(EXIT_FAILURE); } printf("Réponse envoyée\n"); }
Envoi des informations après chaque fin de traitement d'image de profondeurs
donnees_t donnees; donnees.danger = clusts.c[clusts.nb_clusters-1].danger; donnees.type = clusts.c[clusts.nb_clusters-1].type; if(clusts.c[clusts.nb_clusters-1].debut_y>=0 && clusts.c[clusts.nb_clusters-1].debut_y<width/3) donnees.zone = 1;//Gauche else if(clusts.c[clusts.nb_clusters-1].debut_y>=width/3 && clusts.c[clusts.nb_clusters-1].debut_y<(2*width/3)) donnees.zone = 2;//Milieu/Devant else if(clusts.c[clusts.nb_clusters-1].debut_y>=(2*width/3) && clusts.c[clusts.nb_clusters-1].debut_y<width) donnees.zone = 3;//Droite envoiReponseClientFDM(donnees);
LIMITES
La majorité du traitement repose sur les deux premières phases de filtrages qui seront , dans l'état actuel, affinés et modifiés après plusieurs essais pratiques pour augmenter la précision et la pertinence des valeurs traitées lors de la détection de cluster. Malheureusement ces essais ne pourront être effectués à cause du confinement.
Autre méthode de traitement d’image
Il existe des méthodes pour la détection du contour des objets dans une image, et une solution serait d’utiliser ces méthodes afin de localiser les bords des obstacles.
Filtre de Sobel
Cette méthode consiste à utiliser un filtre de Sobel, celui-ci va calculer le gradient de l’intensité (de la distance) de chaque pixel. Un contour se caractérise par une rupture d’intensité dans l’image, ces changements d’intensités se traduisent par des discontinuités dans la profondeur. Ce filtre comporte deux dérivées, une dérivée pour la direction horizontale et une dérivée pour la direction verticale. Ceci permet d’éliminer l’inclinaison du sol par rapport à la caméra et conserver uniquement les contours des objets. Il suffit de détecter pour chaque pixel sa variation par rapport à ses voisins de gauche et de droite et de dessus et dessous. La méthode consiste à calculer le gradient en H puis en V autour du pixel.
Donc à chaque position (x, y) d’un pixel, il faut regarder la différence entre les 3 pixels à sa gauche avec les 3 à sa droite pour déterminer le gradient en x, les 3 pixels au dessus avec les 3 pixels en dessous pour avoir le gradient en y. Le pixel fait partie d’un contour si son contour est élevée.
Initialisation des valeurs et tableaux
int gx; // Gradient suivant l’axe x int gy; // Gradiant suivant l’axe y int g; // Module du gradient int E[height][width];// Structure d'entrée (image de profondeur) int Sobel[height][width]; // Structure de la transformée de Sobel int Sr[rows][row_length]; // Image réduite en résolution pouvant être affichée sur une console en mode texte static const char* pixels = " .0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // valeurs à afficher en mode texte
L’initialisation comporte les deux dérivées des directions gx et gy, le module du gradient est calculé avec g. On initialise un tableau d’entrée E[ ][ ] qui sera traité avec le filtre de Sobel Sobel[ ][ ], et enfin un dernier tableau Sr [ ][ ] de dimension plus réduite pour permettre l’affichage du résultat en mode texte.
Ici l’image sera chargée dans le tableau E, on va faire l’analyse des ligne et colonne.
for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) E[y][x] = *depth_frame_data++; }
La création du filtre de Sobel se fait de la façon suivante:
// printf("Début Sobel"); for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { if (x==0 || y==0 || x==width-1 ||y==height-1) g=0; // mise à 0 du bord else { //derivee en x gx=(- (E[y-1][x-1] + E[y][x-1] + E[y+1][x-1]) + (E[y-1][x+1] + E[y][x+1] + E[y+1][x+1]));
//derivee en y gy=(- (E[y-1][x-1] + E[y-1][x] + E[y-1][x-1]) + (E[y+1][x-1] + E[y+1][x] + E[y+1][x+1])); //amplitude g=sqrt((float)gx*gx+(float)gy*gy); // if (g>255) g=255; //écrêtage à 255 pour avoir un résultat sur un seul octet } Sobel[y][x]=g; // Et voici la structure contenant la transformée de Sobel Sr[y / HEIGHT_RATIO][x / WIDTH_RATIO] += (g/poids);
Quelques recherche sur internet ont été très utiles pour la création du filtre, celui-ci contient les dérivées en x et y , ces dérivées sont des matrices 3*3 comme on a pu le voir précédemment, celles-ci vont ensuite aider au calcul du gradient avec g=sqrt(gx*2+gy*2). En sortant de la boucle on obtient notre filtre Sobel[ ][ ], on peut par la suite déterminer une image à l’affichage Sr[ ][ ] avec un ratio de 8*5 une image plus grande que celle proposée dans le code original de l’exemple “depth.c” du SDK de la caméra D435. Dans le tableau Sr [ ][ ] on va faire la somme des HEIGHT_RATIO x WIDTH_RATIO pixels encadrant du tableau Sobel [ ][ ] et la diviser par le nombre de pixels défini dans la variable poids = HEIGHT_RATIO x WIDTH_RATIO ( 8*5 = 40 avec les réglages actuels).
La boucle d’affichage
La boucle d’affichage de l’image en mode texte se fait comme suit: Chaque pixel sera affiché sous la forme d’un caractère qui correspondra à la valeur du pixel. On a créé un tableau pixels[ ] qui contient les caractères à afficher static const char* pixels = " .0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; Comme il n’est pas possible d’avoir autant de caractères différents que de valeurs de gradient, on fait un écrêtage à 32 (cette valeur aurait pu être augmenté à 37 avec la table que nous avons définie).
for (y = 0; y < rows; y++) { for (x = 0; x < row_length; x++) {int pixel_index = (Sr[y][x]/8); // on divise par 8 pour avoir une échelle de correspondant aux nombre de valeurs pouvant être affichées if (pixel_index > 32) pixel_index=32; *out++ = pixels[pixel_index]; // on remplit la case buffer[out] du tableau buffer. a supprimer du code } *out++ = '\n'; // on insère un retour à la ligne à la fin de chaque ligne a supprimer du code } *out++ = 0; // On remet l’index du tableau buffer à 0 printf("\n%s", buffer); // On imprime l’image sur le terminal
rows et row_length correspondent une ligne et colonne de l’image divisé par notre ratio 8*5. Nous allons initialiser pixel_index avec notre tableau Sr[ ][ ]/8 , 8 est notre ratio de la hauteur. Ensuite il suffit de déterminer un seuil à ne pas dépasser pour le contour final. Si notre tableau est supérieur à 32 caractères alors il sera égal à 32 pour ne pas déborder dans la recherche d’un élément en dehors des limites du tableau pixel[ ]. Puis on affiche notre structure pour visualiser les contours.
Limites
Grâce à cette méthode nous pouvons détecter les obstacles et on remarque qu’ils sont assez facilement reconnaissables. On constate qu’il y a du bruit dans l’image et qu’il faudrait éventuellement supprimer ce bruit et améliorer ce filtre pour avoir une très bonne précision pour l’analyse des reliefs sur terrain accidentés. La dangerosité peut être déterminée par l’amplitude du gradient. La localisation des objets est relativement facile à effectuer par rapport aux coordonnées de l’objet dans l’image. La distance de l’objet peut être déterminée par rapport à sa projection dans l’image, ou mieux en récupérant la valeur de la distance dans le tableau E[ ][ ]. A cet effet, un tableau réduit Er[ ][ ] pourra être ajouté.
Le résultat du filtre de Sobel
Exécution des codes après modification
Lorsqu'il s'agit de modifier le code pour pourvoir faire des tests, nous devons procéder à plusieurs manipulations. Tout d'abord on doit se rendre dans le dossier /librealsense/examples/. Pour la suite on prendra exemple de code "distance", celui ci se situe dans /librealsense/examples/C/. On garde le dossier original dont on va faire une copie "distance2" que l'on va pouvoir modifier.
Compilation
La compilation donnera une erreur si le CMakeLists.txt n'a pas était corrigé. Comme vu précédemment avec le hello_realsense2, nous devons reprendre la même forme du CMakeLists.txt.
L'ajout de la ligne suivante est indispensable:
#Find librealsense2 installed package find_package(realsense2 REQUIRED)
Ensuite il suffit de créer un dossier build puis d’exécuter les commandes suivantes
Erreur de compilation
Lorsque l'on souhaite modifier le code en ajoutant une librairie qui n'est pas indiqué dans le code du SDK, nous avons l'erreur suivante.
Après avoir fait plusieurs recherches sur internet et en comparant les codes fourni par le SDK, j'ai découvert que le problème était dans le CMakeLists.txt. En effet il s'agit d'ajouter la librairie qui a été ajouté dans le code. Dans le code c'est la librairie math.h qui a été ajouté, comme on peut le voir dans l'erreur il ne reconnaît pas les formules mathématiques utilisées.
Sur certains forum ils indiquaient l'ajout de la librairie m dans le fichier CMakeLists.txt. La ligne de code est la suivante
target_link_libraries(rs-depth m ${realsense2_LIBRARY})
En changeant cette ligne et en y ajoutant la librairie m correspondant à math.h, nous n'avons plus d'erreur de compilation.
Le Hat
Partie soudure
La partie soudure du hat était délicate du point du vue de la précision de soudure afin ne pas déborder sur les pins du même composant qui sont espacées de 0,5 mm seulement. Plusieurs essais ont dus être fait, les premières soudures ont été faites sur les condensateurs, l'étain fourni était du sans plomb, j'ai trouvé des difficultés à appliquer l'étain sur le pins des composants. la panne s'est vite oxydée, le problème venait peut être de l'étain ou du nettoyage de la panne sur une éponge humide. Après plusieurs essais de nettoyages de la panne, celle-ci est restés noir. Une éponge métallique et de l'étain avec du plomb que je trouve plus facile à manipuler ont suffit pour retrouver une panne brillante.
Les conseils de Monsieur Redon sur la soudure des CMS ont étaient très utiles, et m'ont permis d’acquérir la technique de soudure de ces composants. Après plusieurs séances d’entraînement, les CMS ont été soudés proprement. Le plus dur a été de souder les contrôleurs moteur, il fallait être rapide pour ne pas trop chauffer le composant mais aussi précis.
Après avoir soudé les contrôleurs sur une nouvelle plaque à cause des échecs de soudure, il a fallut souder les condensateurs, les connecteurs pour les moteurs et moteurs vibrants et enfin les connecteurs pour la Raspberry, ces composants ont été fourni par Monsieur Redon afin d'aboutir au mieux à la finalisation du hat. Le hat est donc composé de six capacités, deux contrôleurs moteurs, de huit connecteurs pour les moteurs commandant l'ouverture et la fermeture de la main et de moteurs vibrants. Enfin de connecteurs pour la connexion à la Raspberry. On note que d'après le schématique, il faut faire des ponts de soudure sur l’arrière de la plaque pour mettre les moteurs M3 et M8 en service. Les moteurs M8 et M2 sont commandé ensemble. Grâce à un moteur DC et de vibreurs, le hat a pu être testé avec une Raspberry Pi3 Model B.
Partie code
L'implémentation du code pour faire le test du hat s'est faite sur une Raspberry Pi3. Le test à été fait avec un seul moteur, c'est à dire, qu'un seul connecteur pour moteur à été soudé afin de vérifier le premier contrôleur puis un deuxième connecteur pour le deuxième moteur pour vérifier le second contrôleur. Une fois la vérification faite les deux autres connecteurs moteurs ont été soudé.
La librairie de commande des GPIO que nous avons utilisées avec la Raspberry Pi, nécessite non pas d’initialiser avec les numéros des pins de celle-ci mais avec les numéros des GPIO qui se trouve sur la Datasheet de la Raspberry. L’initialisation des moteurs avec les GPIO correspondants sont les suivants.
Remarque : Lors du lancement du code il est nécessaire de préciser sudo avant l’exécution du programme sinon une erreur apparaît ne reconnaissant pas les fonctions GPIO utilisées dans le code. Ceci est une particularité de la librairie piggpio qui a été utilisée.