IMA4 2018/2019 P38

De Wiki de Projets IMA


Vidéo HD

Sommaire



Présentation générale : Interface Graphique Robotino 2 Upgradé

Etudiants : François BRASSART & Jérome HAON

Encadrants : Vincent COELEN & Thomas DANEL

Description

Lors de notre 2ème année de PEIP, nous avons eu l'occasion d'utiliser des robotinos lors d'un bureau d'études afin de découvrir la spécialité IMA. Les robotinos sont des robots mobiles produits par Festo, à entrainement omnidirectionnel ce qui leur permet de se déplacer dans toutes les directions et de tourner sur eux-mêmes. Ils sont dotés de différents capteurs (infrarouges, optiques, inductifs...) ainsi que d'une Webcam et d'un gyroscope. Ils peuvent être contrôlés et programmés sur un PC via une connexion Wi-Fi ou directement grâce à l'interface Homme-Machine (IHM) intégrée.

Nous avons constaté durant ce bureau d'étude que les robotinos, utilisés depuis une dizaine d'années en salle de TP commençaient à vieillir. L'objectif de notre projet de est moderniser ces robots afin de les remettre au gout du jour. Les robotinos bénéficieront d'un nouvel ordinateur embarqué, d'un nouveau logiciel, de nouvelles batteries et d'un écran tactile 7 pouces.

Objectifs

La modernisation des robotinos a déjà été commencée avant le début de notre projet. Les nouveaux composants ont déjà été choisis et l'installation est en cours. Le but de notre projet est de concevoir la nouvelle interface Homme-Machine. Il s'agira d'une interface graphique directement accessible via l'écran tactile, programmée grâce à Web Toolkit qui permettra entre autres de :

  • Configurer le robotino au réseau : pouvoir modifier facilement la configuration réseau du robot (choix d'un réseau sans fil).
  • Afficher l’état et les données des différents capteurs (camera, laser, sharps, bumper) afin de faciliter la programmation et le débogage des programmes.
  • Lancer des programmes de démonstration que nous aurons préalablement programmés.

Cette interface graphique devra évidemment être fonctionnelle et agréable à utiliser.

Analyse du projet

Positionnement par rapport à l'existant

Les Robotinos utilisés depuis 10 ans en salle de TP sont des Robotinos de génération 2. Il existe actuellement des Robotinos de génération 3. Nos 2 concurrents sont donc le Robotino 2 et le Robotino 3.

Notre Robotino "amélioré" sera équipé d'un ordinateur embarqué Intel NUC, avec l'OS Ubuntu 18.04. Il disposera d'une IHM intégrée notamment grâce à un écran tactile 7 pouces que nous allons créer.

Analyse du premier concurrent : Robotino 2

Le robotino 2 est équipé d'un ordinateur embarqué AMD Geode, avec l'OS Ubuntu 9.04.

Il dispose également d'une IHM sur son châssis composé d'un petit écran et de plusieurs boutons. Elle permet de  :

  • Sélectionner la langue,
  • Afficher l'état des batteries
  • Sélectionner les adresses du réseau
  • Sélectionner des programmes de démonstrations pré-enregistrés.
Configuration Hardware Robotino 2
IHM Robotino 2

Analyse du second concurrent : Robotino 3

Le Robotino 3 est équipé d'un ordinateur embarqué Intel Core i5 ou Intel Atom, avec l'OS Ubuntu 12.04.

Il ne dispose pas d'IHM intégrée, il doit être contrôlé à distance par un PC.

Configuration Hardware Robotino 3


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

Samedi 2 février 2019, Journée Portes Ouvertes à Polytech Lille.

Pierre, étudiant en IMA est chargé de faire la démonstration de l'utilisation des robotinos sur le terrain de Hockey/Football de la salle C305. Dès que tous les futurs étudiants et leurs parents sont rentrés dans la salle, il n'a qu'à choisir en cliquant sur l'écran tactile d'un robotino le programme qu'il désire dans l'onglet "programme". Il peut ainsi commencer sa présentation pendant que le robot suivra une ligne, contournera un obstacle ou fera demi-tour quand il rencontrera un mur.


Vendredi 25 Janvier 2019, Bureau d'études IMA

Léna et Clément, étudiants en PEIP2, ont choisi le bureau d'études IMA afin de découvrir cette spécialité et finaliser leur orientation. Il consiste en la commande d'un robotino. La première mission est de le faire suivre une ligne. Léna et Clément ont beaucoup de mal à comprendre le fonctionnement de ce Robot. Clément a mis des sur-chaussures pour pouvoir marcher sur le terrain pendant que Lina commande manuellement le robot depuis le PC. En cliquant sur l'onglet "états des capteurs", Clément peut directement voir sur l'écran du robot l'état des capteurs, et ainsi comprendre leur fonctionnement en fonction de la position du robot et de son environnement.

Réponse à la question difficile

Question : Positionnement de Web Toolkit WT par rapport à d’autres systèmes (ROS par exemple) mériterait d’être explicité. De même la difficulté (ou pas) de prise en main de Web Toolkit WT n’est pas abordée.

Réponse :

  • Robot Operating System (ROS), est un ensemble d'outils informatiques open source permettant de développer des logiciels pour la robotique. Il s'agit d'un méta-système d'exploitation qui peut fonctionner sur un ou plusieurs ordinateurs et qui fournit plusieurs fonctionnalités : abstraction du matériel, contrôle des périphériques de bas niveau, mise en œuvre de fonctionnalités couramment utilisées, transmission de messages entre les processus et gestions des packages installés.
  • Web Toolkit est une interface web qui est met à disposition des fonctions pour le contrôle, la configuration et la maintenance du système de robot. WT est une bibliothèque d'interface graphique Web en C ++.

Notre objectif étant de concevoir une interface graphique sur l'écran tactile des robotinos, WT semble plus adapté que ROS, qui est principalement axé sur la programmation des robots. De plus, connaissant déjà très bien le langage C, il nous parait plus réaliste d'utiliser Web Toolkit.

Préparation du projet

Cahier des charges

Nous devrons répondre aux différents besoins des divers utilisateurs. D'abord, l'interface graphique devra permettre de configurer le robotino au réseau. Les robotinos sont normalement connectés à une borne wifi présente en salle robotino. Notre interface devra permettre de s'y connecter facilement et d'afficher son adresse IP. Ensuite, pour faciliter la programmation, l'interface pourra afficher les états et les données des différents capteurs. Enfin, une interface de lancement des programmes de démonstration devra être programmée. Cette interface graphique devra évidemment être fonctionnelle et agréable à utiliser.

Ce cahier des charges est susceptible d'évoluer en fonction de l'avancée de notre projet. En effet, nous pouvons envisager d'ajouter des fonctionnalités à notre interface graphique.

Choix techniques : matériel et logiciel

Comme indiqué dans la réponse à la question difficile, nous avons choisi d'utiliser le Web Toolkit WT. Les choix matériels ont déjà été réalisés par nos encadrants :

  • PC embarqué INTEL NUC,
  • écran tactile 7 pouces,
  • le choix des nouvelles batteries est en cours de réflexion.

Liste des tâches à effectuer

La liste des tâches à effectuer est pour le moment floue, nous allons programmer une rencontre avec nos encadrants pour les préciser. Nous estimons qu'il nous faudra d'abord apprendre à connaître les nouveaux composants (mais également les anciens), puis apprivoiser le Web Toolkit pour finalement créer l'interface graphique finale.

Réalisation du Projet

Prologue

Lors du semestre 7, nous avons commencé par une analyse détaillée de notre projet, en effectuant dans un premier temps la présentation de notre projet, ensuite nous avons positionné notre projet par rapport à l'existant et nous avons défini un scénario d'usage, enfin nous avons déterminé notre cahier des charges.

Nous avons également rencontré à plusieurs reprises nos encadrants afin d’approfondir au fur et à mesure les objectifs de notre projet. Les rencontres avec nos encadrants étaient aussi un moyen pour nous de voir l'avancement de la construction des robotinos qui aiguisait notre curiosité.

Planning prévisionnel

Janvier

  • Apprentissage du langage C++ en autonomie à travers des cours et vidéos tutoriels sur internet et des livres empruntés à la bibliothèque universitaire.
  • Alimenter notre wiki.

Février

  • Téléchargement de la librairie Web Toolkit sur le site internet de WT.
  • Autoformation sur Web Toolkit à travers des vidéos internet et des exemples d'application donnés lors du téléchargement de Web Toolkit.
  • Se renseigner sur la méthode de récupération des données des capteurs et des acteurs de robotino dans l'API2.
  • Faire une première esquisse sur papier de notre interface graphique en pensant aux différentes fonctionnalités que nous voudrions implémenter.
  • Alimenter notre wiki.

Mars (début de mois)

  • Commencer la programmation de notre interface graphique.
  • Télécharger la librairie API2 sur wiki.openrobotino.
  • Alimenter notre wiki.

Mars (fin de mois)

  • L'interface graphique est terminée ou en cours d'être terminée.
  • Commencer à récupérer les données des capteurs du robotino et les intégrer à notre interface graphique.
  • Alimenter notre wiki.

Avril (début de mois)

  • Finir l'interface graphique si elle n'est pas tout à fait terminée.
  • Continuer la récupération des données de l'API2.
  • Commencer à configurer la connexion réseau du robotino.
  • Alimenter notre wiki.

Avril (fin de mois)

  • Finir la récupération des données.
  • Commencer à importer les programmes de démonstration sur le nouveau robotino.
  • Finaliser la connexion réseau.
  • Alimenter notre wiki.

Mai

  • Finir d'importer les nouveaux programmes de démonstration dans le robotino upgradé.
  • Alimenter notre wiki.
  • Soutenance de fin de projet.

Feuille d'heures

Tâche Prélude Heures S1 Heures S2 Heures S3 Heures S4 Heures S5 Heures S6 Heures S7 Heures S8 Heures S9 Heures S10 Heures S11 Heures S12 Heures S13 S14 Total
Analyse du projet 3 3
Organisation, Brainstorming 2 2 1 5
RDV avec encadrants 1 1 1 1 1 1 1 7
Autoformation C++, Web Toolkit 6 6 3 15
Montée en connaissance sur robotino, API2 3 3
Présentation journée immersion des terminales 3 3
Programmation du bandeau et menu principal 1 4 6 4 1 1 17
Récupération de l'état des capteurs, intégration à l'interface 4 6 6 4 20
Gestion de la connexion réseau 2 4 4 10
Programmes de démonstrations 3 5 3 11
Tests 2 1 1 2 1 2 7 16
Remplissage Wiki 1 1 1 1 1 1 1 1 1 1 1 1 3 15
Préparation Soutenance & Rapport 8 8


Semaine 1

Objectifs : organisation, approche de C++ & WT.

Capture 5.PNG

Lors de cette première semaine, nous avons commencé par organiser un planning de travail pour les mois à venir, en nous imposant des objectifs afin de se repartir au mieux la charge de travail.

Nous avons installé une Machine Virtuelle sur notre ordinateur afin de pouvoir travailler en dehors des séances de projets. De plus nous avons téléchargé le paquetage Web ToolKit avec les différents exemples d'utilisation, et effectué des recherches sur son fonctionnement avant de commencer à programmer notre interface graphique.

Enfin, nous avons pris rendez-vous avec nos encadrants de projet afin de nous renseigner sur l’avancement de l'assemblage du robotino, leur expliquer l'organisation des tâches que nous nous sommes donnée et vérifier que notre cahier des charges est complet.

Semaine 2

Objectifs : Documentation sur C++ & WT.

Nous avons commencé cette deuxième séance par un rendez-vous avec nos 2 tuteurs : Vincent Coelen et Thomas Danel. Ils nous ont rassuré sur l'avancée du prototype du robotino. L'écran a été commandé et devrait arriver rapidement. Il sera également monté au plus vite, pour avoir le premier prototype prêt à fonctionner dans 2 semaines minimum. Nous avons convenu de faire une réunion toutes les 2 semaines avec nos encadrants. Cela leur permettra de se tenir au courant de l'avancée de notre travail et nous permettra également de leur poser des questions et de faire un point sur l'avancée de la "construction" du robotino amélioré.

Le nouvel ordinateur embarqué est relié au micro-contrôleur du robotino via une liaison série RS232. Lorsqu'un programme de démonstration est lancé (directement depuis l'ordinateur embarqué ou via le logiciel RobotinoView depuis un ordinateur), les commandes sont envoyées en TCP au démon RPCd., qui est chargé de les transmettre au bon destinataire. La Webcam ou le laser sont reliés à l'ordinateur embarqué. Les autres capteurs et actionneurs (moteurs, roues...) sont reliés au microcontrôleur du robotino.

SchemaLiaison.jpg

Après ce RDV, nous avons suivi un tutoriel pour apprendre le langage C++. tuto C++. En effet, pour utiliser Wt, nous allons coder en C++. Connaissant déjà très bien le C et ayant des bases de Java, le langage C++ n'est pas très compliqué à comprendre et à assimiler. Nous avons également commencé à suivre des tutos sur WT. tuto 1 tuto 2

Nous avons ainsi étudié quelques exemples d'applications Web réalisés avec Web Toolkit. Nous avons pu découvrir le fonctionnement de WT : la possibilité d'ajouter des Widgets prédéfinis sur une interface. Ces tutoriels et exemples seront une bonne base pour le début de la programmation de notre interface graphique. Chaque Widget peut émettre un signal lorsque l'on clique dessus par exemple (permettant la communication déclenchée par un événement entre les objets du programme).

Semaine 3

Objectifs : Documentation sur WT, brainstorming sur le design de l'interface.

Documentation sur WT

Lors de cette troisième semaine, nous avons continué à nous perfectionner, en analysant les exemples donnés via la plateforme Web Toolkit. En parallèle nous avons commencé à nous documenter sur wiki.openrobotino pour avoir un accès complet aux capteurs et aux actionneurs du robotino via l'API2. Une API (interface de programmation applicative) est un ensemble normalisé de classes, de méthodes ou de fonctions qui sert de façade par laquelle un logiciel offre des services à d'autres logiciels. Dans notre cas, l'API2 du robotino est une API permettant d’accéder aux différents éléments du robotino. Cela nous permettra de récupérer les données et de les afficher sur notre interface graphique plus tard. api2

Présentation des robotinos lors de la journée d'immersion des terminales

Le 30 janvier a eu lieu la journée d'immersion des terminales. Nous y avons participé en expliquant à chaque groupe de terminale le contenu de notre formation IMA. Nous étions en salle robotino et avons lancé des programmes de démonstration sur les robotinos 2. Cela nous a permis de découvrir comment se passait la présentation et la démonstration des robotinos sur l'IHM présente ainsi que les programmes de démonstration déjà existants. L'IHM des robotinos 2 est peu agréable à utiliser. Nous avons ainsi une idée plus claire des fonctionnalités que doit intégrer notre interface graphique et pouvons commencer à réfléchir au programme de démonstration que nous allons créer en fonction de ceux qui existent déjà.

Réflexion sur le design de l'interface

Ensuite est venu le temps d'imaginer à quoi pourrait ressembler notre interface graphique, avec les différentes fonctionnalités que nous pourrions implémenter (voir schéma ci-dessous).

Le menu principal sera composé de différentes fonctionnalités :

  • Aide : expliquer les différentes fonctionnalités de notre interface comme par exemple comment connecter le robot au wifi, comment lire l'état des capteurs ou alors lancer un programme de démonstration.
  • Configuration réseau : modifier facilement la configuration réseau du robot en listant les réseaux wifi disponibles et pouvant s'y connecter facilement.
  • Visualisation état capteurs : afficher les données des différents capteurs du robotino directement sur l'interface graphique, par exemple lors du suivi d'un mur nous pourrions voir en temps réel quel capteur de distance est utilisé ainsi que la valeur qu'il renvoie.
  • Lancement programme de démonstration : exécuter un programme de démonstration directement enregistré dans le robotino parmi une liste prédéfinie.

Ensuite, il y aura une page pour chaque fonctionnalité.

IMG 4517.jpg IMG 4518.jpg IMG 4519.jpg

Semaine 4

Objectifs : Finaliser le design de l'interface, commencer la programmation avec WT.

RDV avec encadrants : finalisation du design

Nous avons commencé cette semaine par un rendez-vous avec nos 2 tuteurs afin de faire un compte rendu de notre avancement sur le projet. Cela a été l'occasion pour nous de leur présenter l'ébauche de visuel de l'interface que nous avons réalisé précédemment. Nos encadrants nous ont aidé et ont soulevé quelques points manquants et améliorations possibles.

Nous allons donc rajouter un "bandeau" en haut de notre interface, qui sera toujours visible et qui affichera en continu les informations importantes telles que :

  • l'adresse IP
  • le programme en cours
  • l'adresse wifi
  • l'état de la batterie

Egalement, pour des questions de praticité, il est plus judicieux, lors du choix des programmes de démonstration, de faire un menu déroulant plutôt que des boutons. Cela sera plus pratique pour l'utilisateur. De plus, il sera plus simple dans le futur d'ajouter des nouveaux programmes de démonstration pour les personnes n'ayant pas implémenté l'interface.

Nous avons donc fait un nouveau visuel pour l'interface graphique :

Menu principal.png Choix capteur.png Choix demo.png

Début de la programmation de l'interface : WTDesigner

Pour nous faciliter la tâche de programmation de l'interface, nous avons pensé utiliser le logiciel WtDesigner, qui permet de créer une interface graphique avec WT facilement. [WTDesigner] Cependant, après plusieurs tests, nous n'avons pas été en mesure de récupérer le code source de l'interface ainsi créée (nous avons uniquement accès au fichier .h). Cela n'est donc pas compatible avec l'utilisation de l'interface que nous souhaitons. Sans le code source, nous ne pouvons utiliser l'API2 et ajouter des fonctions pour récupérer l'état des capteurs et actionneurs du robotino. Ceci a donc été une perte de temps dans notre projet.

WTdesigner.JPG

Semaine 5

Objectifs : programmation du premier menu WT.

Programmation du premier menu WT

Suite à l'échec de l'utilisation du logiciel WtDesigner, nous avons commencé à coder nous-même l'interface en C++.

Nous nous penchons d'abord sur le menu principal, composé du bandeau et des 3 boutons permettant de sélectionner les 3 fonctions principales de notre interface : la configuration réseau, l'état des capteurs et les programmes de démonstration. Nous partons sur la base de l'exemple Hello World de WT, qui contient principalement un texte et un bouton. Code source HelloWorld

Nous choisissons de créer 2 WContainerWidget dans chaque partie de l'interface. Un WContainerWidget permet de séparer l'écran et d'ajouter des widgets dans chaque container. Ainsi, nous allons créer un WContainerWidget pour le bandeau et un pour le "corps". Le WContainerWidget du bandeau sera présent sur chaque page de notre interface. Pour le moment, nous avons ajouté du texte "statique", mais plus tard, le contenu sera modifié en fonction du robot (adresse IP, batterie, programme en cours...).

Nous avons choisi de réaliser le bandeau et le corps dans 2 fichiers différents. Le fichier main initialisera une instance de bandeau et une instance de corps.

Nous avons également ajouté les 3 boutons permettant de lancer les 3 fonctionnalités principales de notre interface. Un bouton est créé en ajoutant une instance de WPushButton. Pour l'ajouter dans un WContainer particulier, nous utilisons la méthode addWidget.

WPushButton *btn = container->addWidget(std::make_unique<Wt::WPushButton>("Etat des capteurs"));

Intégration d'une feuille de style CSS

Malgré de nombreux tests, nous n'arrivons pour le moment pas à intégrer une feuille de style CSS. Cela est très problématique car, comme le montre la capture ci-dessous, le rendu n'est pas du tout esthétique. Nous pouvons néanmoins modifier la taille, la couleur... des Widgets grâce à des fonctions de WT. Cependant, il n'existe pas de fonctions de WT permettant de centrer et d'espacer le texte, d'adapter la taille des boutons en fonction de l'écran (taille en pourcentage de l'écran)... L'utilisation d'un fichier CSS parait donc être obligatoire pour mener à bien notre projet et nous devons trouver une solution.

Interface20190226.JPG

Semaine 6

Objectifs : Git, ajout du CSS, finaliser le menu principal, création des "sous-menus".

RDV avec encadrants : GIT, finalisation du design de l'interface, organisation des fichiers

Cette semaine a débuté par un rendez-vous avec Vincent Coelen. Lors de cet entretien nous avons commencé par présenter l'avancement de notre projet que nous avons effectué pendant les vacances et il a pu répondre à nos questions et nous apporter des solutions pour le futur. Le montage des nouveaux composants du robotino est également terminé, il nous a fait une démonstration pour l'allumer et l'éteindre et montré les différentes fonctionnalités de l'écran tactile et de l'écran embarqué.

De plus pour faciliter la communication entre nous et aussi avec notre tuteur, sur les lignes de code que nous pouvons effectuer et de garder dans le futur ce que nous avons fait, un GIT a été mis en place : Lien vers le GIT

Compilation de l'interface, intégration CSS

Nous avons continué la programmation de notre interface. Nous avons réussi à utiliser une feuille de style CSS. il faut pour cela utiliser la fonction

useStyleSheet("stylesheet.css")

Nous avions déjà essayé d'utiliser cette fonction auparavant, mais sans succès. Cela ne fonctionnait pas car, lors de la compilation, il faut ajouter une option permettant d'indiquer où se trouvent les fichiers ressources nécessaires à notre interface : feuilles de styles, images... Ainsi pour compiler, il faut utiliser la commande

./main --docroot ./ressources --http-address 0.0.0.0 --http-port 8080
  • --docroot ./ressources permet d'indiquer les ressources se trouvent dans le dossier ./ressources
  • --http-address 0.0.0.0 permet d'indiquer l'adresse sur laquelle nous voulons accéder à notre interface. Pour le moment, nous choisissons l'adresse locale 0.0.0.0
  • --http-port 8080 permet d'indiquer le port que nous souhaitons utiliser. Nous utilisons 8080 car cela correspond à un port qui n'est pas réservé.

Nous avons organisé notre projet dans des fichiers et dossiers différents, afin de mieux s'y retrouver.

  • dossier ressources : contient les images, feuilles de style CSS...
  • dossier bandeau : contient la classe qui crée le bandeau, qui est présent sur tous les menus.
  • dossier corps : contient la classe qui crée le "corps" de notre interface, c'est à dire le contenu principal de l'interface

Le fichier main.C est le programme principal qui initialisera la connexion avec le robotino, et qui créera des instances de bandeau et de corps.

Enfin cette semaine nous avons fini de programmer le menu principal de l'interface graphique. Pour le bandeau, nous n'avons finalement pas utilisé un WContainerWidget mais avons opté pour un WHBoxLayout. Ce type de widget permet d'organiser horizontalement des widgets, dans notre cas du texte et des images. Cela nous a permis de régler correctement l'espacement et le centrage des informations contenues dans le bandeau.

MenuPrincipalSemaine7.jpg

Programmation de l'interface : Sous-Menus, navigation entre les menus

Nous avons ensuite commencé à créer les "sous-menus" suivants et les avons liés au menu principal, après un clic sur le bouton correspondant. Pour cela, nous créons pour chaque sous-menu un WContainerWidget qui contiendra les informations nécessaires.

Nous nous sommes renseignés sur la manière de passer entre les différents menus avec WT. Voici l'explication que nous avons trouvée sur le site de WT :

"Wt allows the application to define internal paths and handle changes of internal paths. Because a Wt application is not composed of pages, a URL does not define to particular page, but rather a particular application state"

Pour naviguer entre les menus, nous allons changer le chemin interne (l'URL) de l'application. Quand l'URL est modifiée, un signal est envoyé. Nous implémentons une fonction qui permet de capter le signal, et en fonction de l'URL afficher le menu correspondant.

WApplication *app = WApplication::instance();
corps->clear();
if (app->internalPath() == "/capteurs")
        capteurs(corps);

Ainsi, lors d'un clic sur le bouton "Etat des Capteurs", l'URL devient localhost:8080/capteurs et notre fonction handlePathChanged supprime les wigets présents dans la partie du corps et appelle la fonction capteurs qui va afficher le menu d'état des capteurs. Cette fonction prend en paramètre le WContainerWidget corps, qui est un attribut de la classe corps.

Le Widget WApplication représente une instance d'application pour une session donnée (une connexion à l'interface).La méthode internalPath() renvoie l'URL de l'application.

Semaine 7

Objectifs : ajustement de l'interface graphique sur le robotino, connexion au robotino, récupération état capteurs.

Premiers tests sur robotino, Mise à jour Machine Virtuelle et WT

Nous avons désormais accès au robotino qui est dans une salle en AIP afin de pouvoir travailler dessus quand nous le souhaitons. Nous testons alors l'interface sur le robotino que nous avions réalisé sur ordinateur. Au début de ce projet nous avions téléchargé une machine virtuelle Debian. Cependant, sur Debian la dernière version disponible de WT était la version 3.3.6. Lorsque nous avons voulu afficher l'interface graphique sur le robotino, un problème est survenu. En effet le robotino embarque une machine virtuelle Ubuntu, qui lui permet d'avoir la dernière version de WT (4.0.3). Lors de la compilation nous avons eu de nombreuses erreurs de syntaxe du fait de la mise à jour. Pour coller au mieux avec le processus embarqué par le robotino nous avons donc choisi de télécharger une nouvelle machine virtuelle Ubuntu (celle utilisée par le robotino) et de télécharger la même version de WT, pour ne plus avoir de problème dans l'avenir. Nous avons réussi en très peu de temps à corriger les erreurs de compilation dues à la nouvelle mise à jour et nous pouvons commencer nous se connecter et récupérer les données des capteurs et des actionneurs du robotino. Nous visualisons avec succès notre interface sur l'écran tactile du robotino.

Connexion au robotino

Avant de pouvoir commencer à retourner l'état des capteurs et des actionneurs, nous devons connecter le robotino à notre programme. Afin d'y parvenir nous avons analysé les exemples donnés lors du téléchargement de l'API2. Nous avons testé l'exemple circle sur le robotino et analysé son fonctionnement. Ce programme commence par créer une communication avec le robotino. Une fonction init crée une instance de la classe com et appelle plusieurs de ses méthodes afin de se connecter au robotino :

  • setAddress pour donner l'adresse IP du robotino.
  • connectToServer pour s'y connecter.

Ensuite, il crée une instance de la classe Omnidrive, afin de pouvoir contrôler la trajectoire du robot.

Nous allons ainsi utiliser une fonction similaire à init pour nous connecter au robotino, et allons créer des instances de classes de chaque capteur pour utiliser leurs méthodes permettant de récupérer leur état. A priori, nous avons réussi à nous connecter. Nous allons essayer d'afficher l'état d'un capteur pour être sûr que la connexion est effectivement réalisée.

Récupération de l'état du bumper

Nous avons choisi pour commencer de récupérer l'état du bumper du robotino. Le bumper est un capteur de collision situé sur tout le tour de la coque du robotino. Quand il subit une pression, il renvoie un signal, cela permet de configurer le robotino pour qu'il s'arrête au moindre choc. Avec la fonction bool rec::robotino::api2::Bumper::value on obtient :

  • True quand le robotino heurte quelque chose (un mur par exemple)
  • False sinon

Nous créons donc dans le main une variable globale qui crée une instance de bumper, que nous utilisons dans le programme du corps de l'interface. Il nous suffit donc d'afficher dans le menu de l'état du bumper : bumper.value() afin d'afficher True ou False.

A la fin de cette séance nous avons réussi à connecter le robotino à notre programme et à récupérer létat du bumper. Cependant, l'état s'affiche lors du chargement du menu correspondant, mais ne s'actualise pas en direct. Notre objectif pour l'interface est d'afficher l'état des capteurs en direct sans actualiser la page. Nous avons essayé de mettre l'affichage de l'état du capteur dans une boucle while, mais cela ne fonctionne pas. Nous supposons que la boucle infinie empêche notre interface graphique de fonctionner en même temps. Nous réfléchissons donc à un autre moyen d'afficher la valeur du bumper en instantané.

Actualisation des données : utilisation d'un timer

Durant le week-end, nous avons trouvé une solution. WT propose un widget WTimer qui crée un signal à intervalle de temps régulier. Nous choisissons d'utiliser un timer de 10ms. Dans notre programme, nous exécutons la fonction bumper.value() et actualisons l'affichage à chaque fois que ce signal est émis.

 mytimer->timeout().connect([=] {
 if(bumper.value()==true)
 	state->setText("True");
 else if (bumper.value()==false)
 	state->setText("False");
 });

Semaine 8

Objectifs : Récupération état des capteurs, mise à jour de l'interface en conséquence.

Test de l'actualisation de l'état du bumper sur robotino, adaptation du CSS

Nous testons sur le robotino si l'état du bumper s'actualise bien en direct avec l'utilisation du WTimer, ce qui est le cas. Nous mettons à jour la feuille de style CSS afin que l'écran soit vert quand le bumper n'est pas touché et rouge si le bumper subit un choc.

Récupération de l'état de l'odométrie

Nous continuons cette séance en récupérant, l'état de l'odométrie. L'odométrie fournit la position momentanée par rapport à la position de départ. Les codeurs des moteurs permettent de savoir combien de tours le moteur a effectué et donc de calculer, à l’aide du diamètre de la roue, la distance parcourue. Cette méthode fournit localement des résultats relativement précis mais l’erreur croît fortement sur de longues distances.

Avec la méthode: void rec::robotino::api2::Odometry::readings(double *x, double *y, double *phi, unsigned int *sequence = 0)const on obtient :

  • la position x
  • la position y
  • l’orientation phi

Nous testons et cela fonctionne. Nous essayons d'ajouter un timer pour l'actualisation en continu des données. Cependant, cela ne fonctionne pas. Nous nous rendons compte qu'à la fin du prototype de cette méthode de récupération de l'état de l'odométrie, il y à un const() a la fin. le mot clé const() signale que la fonction membre s'engage à ne pas modifier l'objet au départ duquel elle est appelée Pour réussir à modifier l’état des variables, nous avons alors déclaré à nouveau les variables à l’intérieur du timer.

  mytimer->timeout().connect([=] {
   unsigned int sequence =0;
   double x,y,phi;
   odometry.readings(&x,&y,&phi,&sequence);
   stateX->setText("x = "+std::to_string(x)+" m");
   stateY->setText("y = "+std::to_string(y)+" m");
   statePhi->setText("phi = "+std::to_string(phi)+" °");
 });


De la même manière que le bumper, nous créons donc dans le main une variable globale correspondant à l'odométrie, que nous utilisons dans le programme du corps de l'interface, pour afficher l'état du capteur odométrique.

Récupération de l'état du gyroscope, des capteurs de distance et des moteurs

Ensuite nous nous sommes occupés du gyroscope du robotino, qui est utilisé pour augmenter la précision de détermination des positions. Le gyroscope détermine la variation de l’orientation du robotino. Dès que le système d’exploitation du robotino détecte le gyroscope, il utilise son signal pour corriger les positions sur la base du système odométrique. L’odométrie est considérablement améliorée par le capteur gyroscopique.

Comme précédemment nous créons dans le main une variable globale correspondant au gyroscope, que nous utilisons dans le programme du corps de l'interface. Il nous suffit donc d'afficher dans le menu de l'état du gyroscope avec la fonction suivante : float rec::robotino::api2::Gyroscope::angle ()const qui retourne la valeur actuelle de l'angle.

De la même manière que l'odométrie, nous actualisons l'affichage du gyroscope à chaque fois que le timer émet un signal.


Nous nous sommes occupés ensuite des capteurs de distance infrarouge. Les capteurs de distance déterminent la distance d’objets situés dans l’entourage du robotino (entre 4cm et 30cm). Le robotino possède au total neuf capteurs infrarouge, répartis par pas de 40° autour de la base. Chaque capteur de distance délivre une tension en volts, dont l’amplitude dépend de la distance par rapport à un objet réfléchissant. Les capteurs de distance infrarouge sont constitués d’un signal de sortie analogique. Nous créons toujours dans le main une variable globale, qui cette fois correspond aux capteurs de distance, que nous utilisons dans le programme du corps de l'interface. Il nous suffit donc d'afficher les valeurs dans le menu de l'état du Capteur de distance retournées la fonction void rec::robotino::api2::DistanceSensorArray::headings (float *readings)const qui renvoient la valeur de chaque capteur de distance.


Le robotino dispose de trois moteurs indépendants entraînant trois roues omnidirectionnelles. Sur chaque moteur est monté un codeur incrémental mesurant la rotation du moteur. Les trois roues permettent de faire évoluer le système dans toutes les directions et de faire tourner le robotino sur place dans une position donnée. Nous créons donc dans le main une variable globale, correspondant à la vitesse des roues, que nous utilisons dans le programme du corps de l'interface. Il nous suffit donc d'afficher dans le menu de vitesse des roues void rec::robotino::api2::MotorArray::actualVelocities (float *readings)const


Problème de batterie des robotinos

Après avoir laissé le robotino éteint un week-end, les batteries étaient complètement déchargées et ne fonctionnaient plus. En effet, même éteint, le robotino consomme quand même de la batterie. Des nouvelles batteries ont donc été installées ainsi qu'un bouton d'arrêt total afin que le robotino ne soit plus alimenté lorsqu'il est éteint.

Semaine 9

Objectifs : Récupération des derniers capteurs et actionneurs, renseignements sur la configuration réseau.

Récupération de l'état des laser, des entrées et sorties digitales et des entrées analogiques

En ce début de semaine, nous terminons la récupération des derniers états des capteurs et actionneurs. Nous nous occupons du capteur Laser, celui-ci n’est pas sur le robotino, il est optionnel. Il permet de maîtriser les défis tels que la cartographie, la localisation et la navigation.

Nous ajoutons donc dans le main une variable globale, correspondant au capteur laser, que nous utiliserons dans le programme du corps de l’interface. Pour afficher la valeur du capteur laser dans le menu, nous utilisons la fonction suivante : LaserRangeFinderReadings rec::robotino::api2::LaserRangeFinder::readings ()const

Comme pour les fonctions précédentes, pour afficher les valeurs du capteur laser en instantanée avec un Timer.

L’interface d’E/S de robotino permet de raccorder des extensions, telles que capteurs et actionneurs. L’interface met à disposition des entrées/sorties numériques (TOR), des entrées analogiques. L’interface d’E/S est directement montée sur la platine principale de l’unité de commande.

Robotinoioconnector 2.png

Entrées analogiques et digitales Les entrées permettent de connecter des capteurs ou des actionneurs, par exemple : Les capteurs optiques gauche et droit permettent de détecter diverses surfaces et couleurs sur la base des différences de coefficient de réflexion. Ils s'exploitent par l'intermédiaire des entrées digitales. Le capteur inductif quant à lui permet de détecter des objets métalliques, il s'exploite par l’intermédiaire des entrées analogiques. Nous ajoutons donc dans le main une variable globale, correspondant aux entrées digitales et analogiques, que nous utiliserons dans le programme du corps de l’interface. Il nous faut donc afficher dans le menu capteur entrées digitales et analogiques : void rec::robotino::api2::DigitalInputArray::values(int *readings)const qui retourne la valeur des entrées digitales. void rec::robotino::api2::AnalogInputArray::values(float *readings)const qui retourne la valeur actuelle des entrées analogiques.


Sorties digitales Les sorties digitales envoient la valeur True si la sortie est activée ou alors False si la sortie est désactivée au robotino. Pour pourvoir envoyer les valeurs au robotino, nous avons fait le choix de faire des boutons dans notre interfaces graphiques (car ils émettent un signal), pour avoir la possibilité de voir quelle sortie nous activons ou désactivons. Pour envoyer la valeur au robotino nous utilisons la méthode void rec::robotino::api2::DigitalOutputArray::setValues(const int *values, unsigned int size) qui permet de changer la valeur de la sortie.

Cette méthode modifie toutes les sorties digitales en même temps. Afin de conserver en mémoire les sorties digitales déjà activées auparavant, nous utilisions un tableau en variable globale qui mémorise leur état. Pour chaque sortie, nous ajoutons un bouton qui permet de l'activer ou de le désactiver. L'appui sur un de ces boutons appelle la méthode pour activer les sorties. Si la sortie est désactivée, le bouton est rouge, si elle est activée, il est vert.

Pour optimiser l'espace sur l'interface, nous mettons en forme ce menu avec un WGridLayout, qui permet de placer les widgets sous forme de "grille". Nous mettons donc le nom de l'entrée sur une première ligne et juste en dessous les boutons correspondants.


RDV avec encadrants

Nous avons pris rendez-vous avec notre tuteur, pour connaître ce qu'il attendais de nous pour le menu configuration réseau, afin d'y réfléchir. Il nous faudra donc dans une première partie lister tous les réseaux wifi et dans une seconde partie enregistrer un réseau sur le robotino en rentrant son identifiant, le mot de passe, le type et préciser si l'on veut se connecter de manière manuelle ou en DHCP. Nous avons alors fait des recherches afin de déterminer comment on pourrait réaliser cela. Nous nous renseignons sur la librairie ioctl et sur les commandes linux à exécuter pour se connecter à un réseau wifi.

Affichage de la caméra

Nous nous renseignons sur la caméra du robotino afin de déterminer comment nous afficher les images sur notre interface graphique. A l'heure actuelle, WT ne permet pas de diffuser un flux vidéo en direct. Nous nous résolvons donc à afficher des images, qui défileront à un intervalle de temps régulier, de manière à ce que le changement soit invisible. Nous avons un programme qui permet de prendre une photo de la caméra toutes les 10ms et l'enregistre dans un fichier image. Nous allons donc lancer ce programme en parallèle de notre interface et afficher les images.

Semaine 10

Objectifs : Affichage caméra, renseignements sur la configuration réseau

Configuration réseau en ligne de commande

Lister les Réseaux wifi disponibles

Avant de pouvoir commencer la configuration réseau, il nous faut connaître le nom de notre carte réseau. Nous utilisons la commande

 iwconfig

La carte réseau sans fil du robotino est nommée wlp1s0.

Pour commencer nous devons déconnecter le network-manager afin de configurer manuellement le réseau en ligne de commande. Network-manager est l'outil de gestion des connexions réseau d'Ubuntu. Pour cela, nous utilisons la commande

sudo service network-manager stop

Si l'on souhaite réactiver network-manager, il faut utiliser la commande

sudo service network-manager start

Dans un second temps, il nous faut lister tous les réseaux wifi. Après plusieurs recherches, il faut utiliser la commande

iwlist wlp1s0 scan

Cette commande renvoie tous les réseaux wifi détectés ainsi que des informations les concernant. Nous utilisons ensuite des commandes sed et grep afin de retourner uniquement les noms de réseaux wifi.

iwlist wlp1s0 scan | grep ESSID | sed -e "s/ESSID:\"//g"| sed -e "s/\"//g" | sed "/x00/d"

Connexion a un réseau wifi

Pour se connecter à un réseau wifi, nous utilisons les commandes

wpa_passphrase nom_reseau_wifi > ~/wpa.conf
wpa_supplicant -B -c ~/wpa.conf -i wlp1s0    

La première ligne de commande sert à enregistrer le nom du réseau sur lequel on souhaite se connecter ainsi que son mot de passe, et la deuxième ligne de commande permet de s'enregistrer.

Cependant, il apparaît que cela ne marche pas tout le temps. Après plusieurs recherches, nous avons découvert qu'il fallait utiliser la commande

sudo killall wpa_supplicant

qui permet de tuer les processus wpa_supplicant déjà existants et de relancer la connexion.

Et une fois connecté et reconnu par la borne wifi, on va demander une nouvelle adresse IP avec le protocole DHCP grâce à la commande

dhclient wlp1s0


Affichage de la caméra dans interface

Nous avons essayé d'ajouter une image provenant de la caméra, et grâce à un timer, d'actualiser son contenu. Cependant, lors du lancement de l'application web, l'image est chargée et reste la même jusqu’à ce que l'on quitte. Nous cherchons donc un autre moyen d'afficher la caméra. Après de nombreuses recherches, nous avons découvert l'existence du widget WFileResource. Il s'agit d'une ressource qui crée des données provenant d'un fichier local en continu. Nous créons donc un WFileRessource à partir du fichier "camera.jpg" généré par le programme de caméra toutes les 10ms. Nous lions cette ressource à une WImage.

auto imageFile = std::make_shared<Wt::WFileResource>("image/jpg", "./ressources/camera.jpg");
auto image = std::make_unique<Wt::WImage>(Wt::WLink(imageFile)); 

Ainsi, à l'aide d'un timer, nous sommes capables d'afficher le flux vidéo en direct. Avec un intervalle de temps de 10ms, nous ne voyons pas qu'il ne s'agit en fait que d'images successives.

Semaine 11

Objectifs : configuration réseau, design de l'interface graphique grâce au CSS, programmes de démonstration

Intégration de la configuration réseau à notre interface

Choix graphiques

Nous avons choisi d'utiliser un WSelectionBox, qui affiche une liste de textes et permet d'en sélectionner un. Nous ajoutons un bouton permettant de valider le choix du réseau wifi.

Une fois le réseau wifi sélectionné, nous affichons un WLineEdit permettant de rentrer du texte. Nous le configurons en mode Password afin de ne pas afficher le mot de passe en clair sur l'interface. Nous ajoutons un bouton permettant de valider le mot de passe et de lancer la connexion.

Lancement des commandes : utilisation de super

Pour lancer les commandes, nous devons utiliser la fonction popen pour lister les réseaux wifi et les récupérer, et la fonction system pour lancer la connexion et la récupération d'une adresse IP.

Cependant, les commandes de connexions doivent être exécutées en super utilisateur. Nous ne voulons pas lancer l'interface graphique en super utilisateur car il y a des risques que les utilisateurs puissent lancer d'autres commandes en super utilisateur. Nous avons donc pensé à utiliser super, que nous avions utilisé en 3e année en systèmes et réseau. Super permet à un utilisateur non root de lancer des commandes prédéfinies comme s'il était root.

Nous avons donc réalisé un script permettant de lister les réseaux wifi et un script permettant de se connecter au réseau wifi, que nous avons placé dans un dossier scripts. Nous avons modifié le fichier de configuration de super (/etc/super.tab) en ajoutant nos scripts. Exemple pour la liste des wifis :

listeWifi /home/robotino/work/robotino2_gui/scripts/listeWifi.sh robotino uit=root

Ainsi, pour lister les réseaux wifi, il faut simplement écrire la commande

super listeWifi

Lancement des programmes de démonstration

Pour lancer les programmes de démonstration, nous ajoutons un WSelectionBox ainsi qu'un bouton permettant de valider le choix. Les programmes de démonstration sont placés dans le dossier /usr/local/robotino/examples/bin/. Pour ajouter un nouveau programme de démonstration, il faut l'ajouter à la boite de sélection dans le fichier corps/interface.C, dans la méthode ChoixDémo :

model->addString(<nom du prog>);
model->setData(<index suivant>, 0, std::string("<chemin vers le programme> 127.0.0.1"), Wt::ItemDataRole::User);

Pour lancer le programme, nous utilisons par exemple pour la démonstration circle :

system("/usr/local/robotino/examples/bin/example_circle 127.0.0.1 &");

Le nom du programme à lancer est récupéré en fonction de la sélection. Le programme est lancé en arrière plan pour ne pas interférer sur notre interface graphique.

Lorsqu'un programme de démonstration est lancé, son nom est récupéré et affiché dans le bandeau. Nous avons ajouté dans le bandeau un timer afin d'actualiser le nom du programme en cours.

Arrêt des programmes de démonstration

Nous souhaitons ajouter la possibilité d’arrêter le programme de démonstration. Nous avons donc pour objectif de récupérer le PID du programme en cours. Après plusieurs recherches, nous avons trouvé la commande qui permet de récupérer le PID d'un processus en cours :

pidof -x "<nom du processus>"

Ainsi, pour arrêter un programme de démonstration, nous lançons la commande

kill -9 $(pidof -x "<nom du processus>")

Récupération de l'adresse IP, du wifi connecté et de la qualité du wifi

Dans le bandeau, nous désirons afficher l'adresse IP et le réseau wifi auquel le robotino est actuellement connecté. Pour récupérer l'adresse IP, nous lançons la commande

ip a | grep "inet.*wlp1s0" | cut -f6 -d" " | sed "s|\([0-9.]*\).*|\1|g

ip a liste toutes les interfaces réseau et leurs caractéristiques. Nous récupérons donc les informations concernant l'interface réseau sans fil wlp1s0 pour en extraire l'adresse ip. Nous utilisons popen pour lancer cette commande, récupérer le résultat et l'afficher dans le bandeau.


De même pour récupérer le wifi auquel le robotino est connecté, nous lançons la commande

iwconfig wlp1s0 | grep ESSID | sed -e "s/.*ESSID:\(.*\)/\1/g"| sed "s/\"\(.*\)\"/\1/"

iwconfig permet de connaitre la configuration de l'interface réseau sans fil wlp1s0. Nous extrayons ensuite le réseau wifi. Nous utilisons popen pour lancer cette commande, récupérer le résultat et l'afficher dans le bandeau.


Enfin, pour récupérer la qualité du wifi, nous procédons de même grâce à iwconfig qui affiche la qualité

iwconfig wlp1s0 | grep Quality | sed -e "s/.*Quality=\([0-9]*\).*/\1/"

Nous utilisons popen pour lancer cette commande, récupérer le résultat et l'afficher dans le bandeau.


Design de l'interface graphique

Nous passons du temps cette semaine à tester différents designs pour notre interface. Nous modifions pour cela le CSS et changeons parfois l'agencement des widgets lors de l'affichage de certaines données capteurs. L'objectif est d'obtenir une belle interface, agréable à utiliser. Nous avons fait en sorte que son affichage s'adapte en fonction de l'écran. Pour cela, nous avons ajusté les attributs CSS width et height en fonction de la taille de l'écran. Par exemple,

.btnCapteurs {
    height: 11vh;
    width:49vw;
}

Les boutons ayant pour classe CSS "btnCapteurs" auront une hauteur de 11% de la hauteur de l'écran et une largeur de 49% de la largeur de l'écran. Ainsi, notre interface est optimale même si on l'affiche sur un autre ordinateur ou un smartphone.

Semaine 12

Nous sommes venus travailler sur le robotino durant les vacances, afin de terminer et tester tout ce que l'on avait fait en semaine 11

Objectifs avant semaine 12 (pendant les vacances) : avoir fini la partie configuration réseau, et lancement des programmes de démonstration. Il ne nous restera ainsi à finaliser l'esthétique de notre interface et créer notre programme de démonstrations avant de faire les tests finaux.

Réalisation du programme de démonstration

L'interface graphique du robotino étant terminée, nous devons maintenant nous charger de coder un nouveau programme de démonstration. Nous n'avions pas encore d'idée sur le programme que nous voulions faire,alors nous sommes allés regarder sur les robotinos en salle API les programmes de démonstrations déjà existants. Nous voulions que le programme que nous allions implémenter ne ressemble pas à un programme existant. Les programmes de démonstration déjà existant sont les suivants :

  • Circle
  • Suivie de mur
  • Exploration
  • Évitement d'obstacle

Nous nous sommes rendu compte qu'aucun programme de démonstration n'utilisait la caméra, alors nous avons pensé à un robotino joueur de hockey.

Principe du programme de démonstration :

Le robotino tourne sur lui-même jusqu'à ce que la caméra détecte un palet rouge. Une fois détecté, le robotino avance vers lui pour ensuite tirer dedans.

Pour commencer, nous avons effectué du traitement d'image, tel qu'étudié cette année en cours de TIM (Transmission d'Information Multimédia). Pour la détection du palet, nous voulions une fonction qui renvoie :

  • True si le robotino détecte le palet rouge
  • False sinon

Nous avons commencé par implémenter une fonction qui compte le nombre de pixel rouge dans l'image que capte la caméra. Nous avons pour cela repris le principe de l'exemple caméra. Au lieu d'écrire chaque pixel dans un fichier image pour obtenir la photo, nous regardons si le pixel est de couleur rouge. Un compteur permet de connaître le nombre de pixels rouges.

Pour détecter un pixel rouge nous avons utilisé la condition suivante :

if(R>150 && G<100 && B<100)
   nombre_pixel_rouge++;

Maintenant que le robotino détecte la couleur rouge, il faut que celui-ci s’arrête en face du palet pour aller le chercher (que le palet se trouve au centre de l'image). Pour cela nous voulions que le robotino ne compte que les pixels rouge uniquement sur une bande horizontale au centre de l'image (320*240 pixels) alors nous avons modifié la condition suivante :

if(i>longueur/2-20 && i<longueur/2+20 && R>150 && G<100 && B<100)
   nombre_pixel_rouge++;

Ainsi, le compteur ne détecte que les pixels rouges se trouvant au centre de la caméra.

En fonction du nombre de pixels détecté, le robotino arrête le traitement d'image et avance vers le palet. Après plusieurs tests, nous avons fixé le seuil de pixels rouge à 200. Nous faisons ensuite avancer le robotino jusqu’à ce que le capteur de distance avant le détecte dans la pince. Puis on effectue des mouvements afin de tirer dans le palet.

Fichier:VideoDemo.mp4

Semaine 13 et 14

Objectifs : tests, modifications, corrections des bugs et erreurs, préparation de la soutenance, rédaction rapport En cette fin de projet, nous réalisons tous les tests nécessaires, corrigeons les éventuels bugs afin que notre interface soit totalement fonctionnelle. Nous remplissons le Wiki, rédigeons le rapport et préparons la soutenance.

Compilation et Execution

Le robotino sous API2 utilise le démon RPCD pour contrôler à peu près tout. La communication entre le robotino et RPCD se fait, en local, sur une Unix Domain Socket. En démarrant, RCPD crée un lien symbolique vers la socket au chemin suivant : /tmp/__REC__RPC__12080__. Problème, nous n'avons pas les droits suffisants pour écrire dessus. Nous lançons donc la commande :

sudo chmod a+w /tmp/__REC__RPC__12080__

Pour obtenir les droits dès le lancement de RPCD, il faut modifier /etc/init/rpcd.conf et ajouter :

post-start script
  sleep 5
  chmod g+w /tmp/__REC__RPC__12080__
end script

Compilation

Pour compiler le projet, il suffit d'utiliser le Makefile dans le dossier racine du projet

Exécution

Pour exécuter, il faut lancer l’exécutable exec situé dans le dossier racine du projet. Ensuite, lancer navigateur et entrer l'adresse localhost:8080. Si on veut afficher l'interface sur un autre ordinateur connecté au même réseau, entrer l'adresse : <@IP du robotino>:8080

Rendu final de l'interface

Notre encadrant Vincent Coelen a pu voir une démonstration de notre interface et a pu tester la connexion au réseau wifi, la visualisation de l'état des capteurs ainsi que le lancement des programmes de démonstration. Nous avons également tourné une vidéo de présentation. Voici des photos du rendu de l'interface :

Ecran d'acceuil.jpg


Selection capteur.jpg


Bumper false.jpg


Bumper true.jpg


Capteur de distance.jpg


Odometrie.jpg


Moteur robotino.jpg


Entree analogiques.jpg


Sorties digitales.jpg


Camera robotino.jpg


Programme demonstration.jpg


Selction Wifi.jpg


MDP Wifi.jpg

Tutoriel Web Toolkit

Etant donné que nous avons eu du mal à trouver des tutoriels sur Web Toolkit (nous avons uniquement la documentation de la bibliothèque en anglais qui n'est pas évidente à comprendre), nous exposons ci-après un "mini-tutoriel" qui reprend tous les widgets que nous avons utilisés pour notre projet ainsi que le fonctionnement général de WT. Cela pourra être utile si des personnes veulent mieux comprendre notre code ou mettre à jour notre interface graphique.

Main : lancement de l'application

Dans notre fichier main.C, la fonction main retourne la fonction WRun qui prend en paramètre une fonction createApplication.

int main(int argc, char **argv)
{
  return WRun(argc, argv, &createApplication);
}

La fonction WRun lance le serveur de l'application. Le paramètre createApplication est une fonction qui crée une instance de l'application pour un nouveau visiteur de l'interface. C'est dans cette fonction que nous créons une instance de la classe bandeau et une instance de la classe interface, qui est le corps de l'interface.

std::unique_ptr<WApplication> createApplication(const WEnvironment& env)
{
 appl->useStyleSheet("main.css");
 std::unique_ptr<WApplication> appl= cpp14::make_unique<WApplication>(env);
 auto band = appl->root()->addWidget(cpp14::make_unique<bandeau>());
 band->setStyleClass("bandeau");
 auto corps= appl->root()->addWidget(cpp14::make_unique<interface>(env));
 corps->setStyleClass("corps");
 appl->setTitle("Robotino GUI");
 return appl;
}

Classes crées

Nous avons créé des classes de bandeau et d'interfaces. Ces classes sont des sous-classes de WContainerWidget. Elles créent donc un WContainerWidget et le remplissent.

class bandeau : public WContainerWidget {
public:
	bandeau();
};

Widgets utilisés

Voici la liste des Widgets principaux de WT que nous avons utilisés. Cette liste est évidemment non exhaustive.

WContainerWidget

Permet de séparer les widgets dans des parties différentes de l'écran. Nous avons créé une fonction nous permettant d'ajouter et de paramétrer facilement un WContainerWidget.

WContainerWidget* ajoutContainer(Wt::WContainerWidget *parent,const char* classeCSS)
{
  WContainerWidget* container=parent->addWidget(std::make_unique<Wt::WContainerWidget>());
  if(classeCSS!=NULL)
    container->setStyleClass(classeCSS);
  return container;
}

WText

Pour afficher du texte, il faut utiliser la classe WText(). Etant donné que nous utilisons souvent ce widget, nous avons créé une fonction permettant de créer et paramétrer ce widget

WText* ajoutText(WContainerWidget *container,const char* text,const char* classeCSS, int centered)
{
  auto texte = container->addWidget(std::make_unique<Wt::WText>(text));
  if (centered==1)
    texte->setTextAlignment(Wt::AlignmentFlag::Center);
  texte->setInline(0);
  if(classeCSS !=NULL)
    texte->setStyleClass(classeCSS);
  return texte;
}

WPushButton

Permet d'ajouter un bouton. Nous avons créé une fonction nous permettant d'ajouter et de paramétrer facilement un WPushButton.

WPushButton* ajoutBouton(WContainerWidget *container,const char* text, const char* id, const char* classeCSS, const char* path){
  WPushButton *btn = container->addWidget(std::make_unique<Wt::WPushButton>(text));  
  btn->setTextFormat(TextFormat::XHTML);
  if (id!=NULL)
    btn->setId(id);
  if(classeCSS!=NULL)
    btn->setStyleClass(classeCSS);
  btn->setLink(WLink(Wt::LinkType::InternalPath,path));
  return btn;
}

Le path entré en paramètre permet de changer l'URL lorsque le bouton est cliqué et d'envoyer un signal InternalPathChanged

Pour éxécuter d'autres fonctions lorsqu'un bouton est cliqué, nous utilisons

btn->clicked().connect([=] { /*code à executer lorsque le bouton est cliqué*/ });

La méthode setIcon() permet d'ajouter une image dans le bouton.

WImage

Permet d'ajouter une image. Nous avons créé une fonction nous permettant d'ajouter et de paramétrer facilement une WImage.

void ajoutImage(WContainerWidget *container,const char* name,const char* classeCSS){
  auto image =Wt::cpp14::make_unique<Wt::WImage>(Wt::WLink(name));
  if(classeCSS!=NULL)
    image->setStyleClass(classeCSS);
  container->addWidget(std::move(image));
}

WTimer

Permet de créer un timer qui crée un signal à intervalle de temps régulier. Nous avons créé une fonction permettant d'ajouter et de paramétrer facilement un WTimer. Cette fonction configure le timer et le lance.

WTimer* addStartTimer(WContainerWidget *container, int time_ms){
  WTimer *timer=container->addChild(std::make_unique<Wt::WTimer>());
  timer->setInterval(std::chrono::milliseconds(time_ms));
  timer->start();
  return timer;
}

Pour récupérer le signal et exécuter des fonctions à intervalle de temps régulier, nous

mytimer->timeout().connect([=] { /*code à executer à intervalle de temps du timer*/ });

WBreak

Permet d'ajouter un espace dans un WContainerWidget.

void ajoutBreak(WContainerWidget *container)
{
  container->addWidget(Wt::cpp14::make_unique<Wt::WBreak>());
}

WSelectionBox et WListSringModel

Permet d'afficher une liste de texte et d'en sélectionner un. Utilisation de WStringListModel pour lister les textes à afficher dans le sélectionneur ainsi qu'une valeur correspondant à chaque texte.

auto sélection =<container>->addWidget(Wt::cpp14::make_unique<Wt::WSelectionBox>());
auto model = std::make_shared<Wt::WStringListModel>();
model->addString( <1er texte>);                                      //Le texte affiché à l'indice 0
model->setData(1, 0, <valeur> , Wt::ItemDataRole::User);             //la valeur correspondant à l'indice 0
model->addString(<2e texte>);
model->setData(1, 0, <valeur> , Wt::ItemDataRole::User);
selection->setModel(model);
selection->setCurrentIndex(0);                                          //Le texte d'indice 0 sera par défaut selectionné

Navigation entre les menus

Chaque fois que l'on veut changer ce qui est affiché à l'écran (lors de l'appui sur un bouton principalement), le "path" ou l'URL est modifiée et un signal InternalPathChanged est envoyé. Pour cela, nous récupérons une instance de l'application WApplication actuelle et définissons la fonction à appeler lorsque l'URL est modifiée.

WApplication *app = WApplication::instance();
app->internalPathChanged().connect(this, &interface::handlePathChange);
void interface::handlePathChange()
{
   WApplication *app = WApplication::instance();
   corps->clear(); 
   if (app->internalPath() == <chemin1>)
     <fonction à appeler quand URL est chemin1>;
   else if (app->internalPath()== <chemin2>)
     <fonction à appeler quand URL est chemin1>; 
}

Cette fonction supprime tous les widget du WContainerWidget principal corps grâce à la méthode clear(), puis appelle une fonction qui va remplir ce container avec de nouveaux widgets.

Layouts : Agencement des Widgets dans un WContainerWidget

Les widgets sont ajoutés au WContainerWidget dans leur ordre d'ajout et les uns à la suite des autres. Ils seront côte à côte ou les uns dessous des autres en fonction de leur disposition. Il existe des layouts permettant de réaliser d'autres configurations.

WGridLayout

Permet de ranger les widgets dans une "grille", un tableau. Pour cela, il faut ajouter les widgets au layout en précisant la colonne et la ligne désirées dans le tableau.

auto layout = std::make_unique<Wt::WGridLayout>();
layout->addWidget(<Widget>,<ligne>,<colonne>);

WHboxLayout

Permet de ranger les widgets dans des cases à l'horizontal sur une ligne. Existe aussi WVBoxLayout qui permet de ranger les widgets sur une colonne à la verticale.

auto hbox = std::make_unique<Wt::WHBoxLayout>();
hbox->addWidget(<Widget>);

Définir le layout d'un WContainerWidget

Une fois le layout défini, il suffit d'indiquer au container que l'on veut utiliser ce layout.

<container> -> setLayout(std::move(<layout>));

Bibliographie

Se connecter à un réseau wifi WPA en ligne de commande sous Linux Connexion au réseau

Documentation sur WT : Library Overview Exemples WT Liste des classes et leurs attributs widgets

API2 robotino API2

Documents Rendus

Fichier:2018-11-26 Présentation du projet.pdf

Fichier:Rapport Projet IMA4 P38.pdf

Fichier:Robotino2 gui.zip

Fichier:Rapport P38 2019.pdf