6,2 Mo

publicité
Introduction
Dans ce projet, la tâche qui m’a été confiée était de développer sur un poste distant une
Interface Homme-Machine (IHM) de contrôle et de test des différentes fonctionnalités du
robot, telles que :
 L’asservissement numérique de position et de vitesse d'un moteur à courant continu
 L’asservissement de position et de vitesse d'un moteur pas-à-pas
 Le contrôle d'une caméra intelligente
 Le pilotage d'un télémetre Infra-Rouge
 Le pilotage d'une récepteur-démodulateur Infra-Rouge
 Le pilotage d'un modulateur-émetteur Infra-Rouge
Chacun de ces modules correspond à un ensemble de classes développées en C++ par les six
autres étudiants de ce projet.
Choix logiciels (langage et environnement de
développement)
Dans un premier temps, le choix du format de développement de l’IHM s’orientait vers MFC
(Microsoft Foundation Class) sous Visual Studio 6.0, ou à l’aide des librairies Qt sous linux ,
avec une programmation en C++ dans les deux cas. L’application aurait donc été stockée sur
un poste distant, avec une connexion au robot à l’aide du serveur web embarqué, en
l’occurrence BOA, package de µClinux (version embarquée de linux), système d’exploitation
utilisé sur les cartes ColdFire.
L’utilisation du CGI aurait donc été nécessaire pour permettre la communication entre les
programmes fonctionnant sur le robot et l’IHM. La Common Gateway Interface est une
technologie standard implémentée sur tous les serveurs Web. Elle permet l'échange de
données (dans les deux sens) entre un programme de votre cru et le contenu d'une page Web
visualisée par un internaute distant. Par extension, on appelle aussi "CGI" les programmes
utilisant cette interface. Un tel programme peut être écrit à l'aide de n'importe quel langage de
programmation : les plus utilisés sont C, Perl ou encore python. A la différence d'un script
Java, le programme CGI sera exécuté sur la machine hébergeant le serveur Web, et non sur la
machine du client.
Mais cette méthode impliquait un choix de système d’exploitation unique, et une application
stockée hors du robot, nécessitant de la transporter séparément et de l’installer avant chaque
utilisation. Java s’est donc imposé comme langage nécessaire à ce projet, puisqu’ étant multiplateformes, et portable si l’on utilise la méthode des applets stockées sur le robot et
téléchargées chez le client par l’intermédiaire du serveur web.
Une fois l’applet téléchargée, celle-ci communique avec les programmes exécutés sur le robot
via le réseau TCP/IP câblé (puis sans-fil WIFI par la suite), par l’intermédiaire d’une socket,
sorte de conduit qui permet la communication entre deux processus, ceux-ci pouvant être
actifs sur des machines différentes.
Le Java
Mis au point en 1991 par la firme Sun Microsystems, Java est un langage orienté objet
reprenant les caractéristiques principales du C++, le rendant moins encombrant et plus
portable, et éliminant ses points difficiles, ses caractéristiques critiques, telles que :
 les pointeurs (permettant d’écrire n’importe où en mémoire et de faire des dégâts)
 la surcharge d'opérateurs
 l'héritage multiple
 la libération de mémoire (qui est transparente pour l'utilisateur, il n'est plus nécessaire
de créer de destructeurs)
Il intègre de plus une meilleure gestion des erreurs.
Les chaînes et les tableaux sont désormais des objets faisant partie intégrante du langage.
Toutefois Java est beaucoup moins rapide que le langage C++, il perd en rapidité ce qu'il
gagne en portabilité…
Fichier source, compilation et machine virtuelle
Le fichier source d'un programme écrit en Java est un simple fichier texte dont l'extension est
par convention .java. Ce fichier source doit être un fichier texte non formaté, c'est-à-dire un
fichier texte contenant uniquement les caractères ASCII de base.
Contrairement aux langages compilés traditionnels, pour lesquels le compilateur crée un
fichier binaire directement exécutable par un processeur donné (c'est-à-dire un fichier binaire
contenant des instructions spécifiques à un processeur), le code source Java est compilé en un
langage intermédiaire (appelé pseudo-code ou bytecode) dans un fichier portant le même nom
que le fichier source à l'exception de son extension (.class).
Compilation traditionnelle :
Fichier
source
Compilation
Fichier compilé
sous Linux
Fichier compilé
sous Windows
Fichier compilé
sous UNIX
Exécution
Système Linux
Compilation Java :
Système
Windows
Système UNIX
Fichier
source
Précompilation
Pseudo
code
Machine
virtuelle Linux
Compilation
Exécution
Machine virtuelle
Windows
Machine
virtuelle UNIX
Exécution
Système Linux
Système
Windows
Système UNIX
Cette caractéristique est majeure, car c'est elle qui fait qu'un programme écrit en Java est
portable, c'est-à-dire qu'il ne dépend pas d'une plate-forme donnée. En réalité le code
intermédiaire n'est exécutable sur aucune plate-forme sans la présence d'une machine
virtuelle, un interpréteur (la machine virtuelle est d'ailleurs parfois appelée interpréteur Java)
tournant sur une plate-forme donnée, et capable d'interpréter le code intermédiaire.
Pour effectuer ces compilations, il est nécessaire de disposer du Java Development Kit. Le
JDK est le kit de développement de base que propose gratuitement la firme Sun Microsystem.
Téléchargeable sur le site de cette dernière (http://java.sun.com), le Kit de développement
comprend plusieurs outils, parmi lesquels:
 javac: le compilateur Java
 java: un interpréteur d'applications (machine virtuelle)
 applet viewer: un interpréteur d'applets
 jdb: un débogueur
 javap: un décompilateur, pour revenir du bytecode au code source
 javadoc: un générateur de documentation
 jar: un compresseur de classes Java
Applications et Applets
Java permet de créer deux types de programmes:
 Les applications : programmes tels qu'on les connaît, c'est-à-dire s'exécutant dans le
système d'exploitation à condition d'avoir installé une machine virtuelle.
 Les applets (prononcez Applettes, et traduisez Appliquettes en français) : il s'agit de
petites applications destinées à fonctionner sur un navigateur. Ainsi une applet a un
champ d'action beaucoup plus réduit qu'une application pour des raisons de sécurité
(une applet ne peut par exemple pas accéder au système sur lequel elle s'exécute...).
Voici donc le code d'une applet toute simple:
import java.awt.*;
public class FirstApplet extends java.applet.Applet {
public void init (){
add(new Label("Hello World"));
}
}
Le programme devrait en toute logique afficher le message "Hello World" à l'écran sur le
navigateur.
Les sockets
Il s'agit d'un modèle permettant la communication inter processus (IPC - Inter Process
Communication) afin de permettre à divers processus de communiquer aussi bien sur une
même machine qu'à travers un réseau TCP/IP. La communication par socket est souvent
comparée aux communications humaines. On distingue ainsi deux modes de communication:
 Le mode connecté (comparable à une communication téléphonique), utilisant le
protocole TCP. Dans ce mode de communication, une connexion durable est établie
entre les deux processus, de telle façon que l'adresse de destination n'est pas nécessaire
à chaque envoi de données. De plus, si un paquet se perd il sera automatiquement
retransmis. Malheureusement cela engendre une perte de temps.
 Le mode non connecté (analogue à une communication par courrier), utilisant le
protocole UDP. Ce mode nécessite l'adresse de destination à chaque envoi, et aucun
accusé de réception n'est donné. Ainsi, on communique plus vite car c’est un protocole
plus léger (utilisé en streaming vidéo, voix sur IP …).
Leur représentation dans le modèle OSI se situe juste au-dessus de la couche transport
(protocoles UDP ou TCP), elle-même utilisant les services de la couche réseau (protocole IP /
ARP).
Modèle des sockets
Application utilisant les sockets
UDP/TCP
IP/ARP
Ethernet, ...
Modèle OSI
7 Application
6 Présentation
5 Session
4 Transport
3 Réseau
2 Liaison
1 Physique
Comme dans le cas de l'ouverture d'un fichier, la communication par socket utilise un
descripteur pour désigner la connexion sur laquelle on envoie ou reçoit les données. Ainsi la
première opération à effectuer consiste à appeler une fonction créant un socket et retournant
un descripteur (un entier) identifiant de manière unique la connexion. Ainsi ce descripteur est
passé en paramètres des fonctions permettant d'envoyer ou recevoir des informations à travers
la socket.
Voici le schéma d'une communication en mode connecté (mode utilisé dans notre cas) :
Serveur
Pour implémenter les sockets en C/C++, nous
utilisons des fonctions et des structures
disponibles dans la librairie <sys/socket.h> :
socket()
Client
bind()
listen()
socket()
accept()
connect()
•Socket() : création de la socket
•Bind() : lier à une adresse et un port
•Listen() : en attente de connexion
•Accept() : accepte la connexion
•Connect() : établit une connexion avec un
serveur
•Recv() : lecture dans la socket
•Send() : écriture dans la socket
recv()
send()
send()
recv()
•Close() : fermeture de la socket
Environnement de développement
Pour réaliser l’interface et le code nécessaire à son
fonctionnement, je me suis aidé de l’ environnement de
développement NetBeans, un outil pour écrire, compiler,
déboguer et déployer des programmes, dans n’importe quel langage, dont Java – l’utilisation
du JDK se fait donc implicitement. Etant lui-même écrit en Java, son utilisation est identique
sous Windows et sous Linux. Cela m’a donc offert une liberté supplémentaire, puisque je
pouvais facilement m’adapter aux systèmes d’exploitations dont je disposais.
Pour le développement des programmes fonctionnant sur les cartes ColdFire (à l’intérieur du
robot), le langage étant le C++ et le compilateur étant spécifique au système d’exploitation
µClinux, je me servais d’un simple bloc-notes et d’une console pour compiler.
Analyse UML
Diagramme de déploiement
Robot
Moteurs CC
Carte coldfire
<<//>>
T CC
Client 1
Applet client 1
Moteurs PP
T PP
<<//>>
image.jpg
Client 2
Caméra
<<Sockets>>
<<RS 232>>
Applet client 2
T Cam
Télémètres
Concentrateur
<<TCP/IP>>
<<TCP/IP>>
Sockets
<<//>>
Serveur
T Telem
Réception IR
<<//>>
T Rec/Dem
Client N
Applet client N
T Mod/Emi
Modules me concernant
<<//>>
Emission IR
Cas d’utilisation
<<actor>>
Tester moteurs CC
Module CC
<<actor>>
Tester moteurs P/P
Module PP
Tester caméras
Module
caméra
<<actor>>
<<actor>>
Module
télémètres
Tester télémètres
Testeur
<<actor>>
Tester
récepteur/dem IR
Module
R/D IR
Tester
émetteur/mod IR
Module
E/M IR
<<actor>>
Diagramme de séquence
Testeur
Interface
:Module CC
:Module PP
:Module caméra
:Module télémètre
:Module R/D IR
:Module E/M IR
Entre infos test
Click sur Go!
Lance test
Affiche état mot
Retour. état mot.
Click sur stop
Lance ordre stop
Affiche état mot
Retour. état mot.
Lance mot.
Stop mot.
Click sur action
Lance action
Affiche état mot
Retourne état moteur
Effectue action
Capture ou Flux
Demande image
Affiche image
Retourne image
Capture image
Demande sauvegarde
Confirme
Sauvegarde
Click mesurer
Demande mesure
Affiche mesures
Retourne mesures
Mesure
Entre code
Click envoie!
Envoie code
Emet code
Affiche code
Retourne code
Démodule code
Module code
L’Interface Homme Machine
La partie servant à connecter les applet à l’aide de socket est interfacée de la même manière
sur chacune d’entre elles. Située dans la partie inférieure, cette partie demande deux
renseignements : le numéro de la carte à laquelle se connecter et le port du serveur de socket.
Module moteurs
Ce module concerne l’étudiant 1 et 2, puisque les données nécessaires pour piloter les moteurs
sont identiques. La distinction se fera donc au niveau du code, par l’analyse du paramètre
« type », renseigné par la balise <PARAM> contenue entre les balises <APPLET> et
</APPLET> dans le fichier html intégrant l’applet.
<PARAM NAME="type" VALUE="CC">
Les différents onglets, « haut niveau » et « bas niveau » permettent un contrôle différent selon
l’état d’avancement des modules.
Module caméra
Ce module concerne l’étudiant 3 pilotant la caméra.
Une fois connectée, l’applet offre la possibilité de capturer une image et de l’afficher dans la
partie supérieure.
Elle permet aussi, par l’intermédiaire du menu déroulant situé sur la droite, de choisir de
détecter le vert ou le rouge (selon la couleur des quilles) présent actuellement devant la
caméra. Celle-ci renverra donc les coordonnées d’un rectangle encadrant la zone où cette
couleur est majoritairement présente.
Module télémètres
Ce module concerne l’étudiant 4 chargé d’effectuer des mesures à l’aide des télémètres.
Dans l’onglet « Tous », c’est l’intégralité des télémètres, situés tout autour du robot sur deux
niveaux, qui sera interrogée, renvoyant une suite de valeurs en millimètres aussitôt affichée
aux emplacements prévus.
L’onglet « Unique » quant à lui sert à tester un télémètre à la fois, puisqu’ avant de gérer tous
les télémètres simultanément, l’étudiant 4 s’est concentré sur la commande d’un seul.
Module réception/démodulation IR
Ce module concerne l’étudiant 6, renvoyant la position et la direction de tous les robots
présents sur le plateau de jeu. En effet, lorsque l’applet est connectée à la carte, une pression
sur le bouton « Capturer » va demander au module réception/démodulation IR d’effectuer des
calculs à l’aide des informations de position émises par les balises, qu’il reçoit par
l’intermédiaire des récepteurs.
Derrière l’IHM
Pour faire communiquer cette interface avec les modules développés par chaque étudiant,
pilotant les éléments physiques du robot, une couche logicielle supplémentaire est nécessaire.
Située sur le robot, son rôle est d’attendre les connexions entrantes, pour les rediriger vers les
modules interrogés.
Pour se faire, la connexion entrante est attendue par un « serveur de rendez-vous », serveur de
socket ne faisant qu’attendre le client pour le rediriger. Mais, pour permettre plusieurs
connexions simultanées au robot, il est nécessaire de dédoubler le processus de rendez-vous
une fois la connexion établie. Permettant la communication avec le client dans le processus
« fils », le processus « père » termine cette communication avant de se remettre en attente
d’une nouvelle connexion. Ainsi, plusieurs clients peuvent communiquer avec le robot, sans
avoir à attendre qu’une communication précédente ne s’achève. Le programme est donc
multitâches et réentrant.
Serveur de rendez-vous
enum enumtype{CC,PP,CAM,TLM,
REC,EMI};
Début
Nb d’arguments >2
exécution du code lié au module :
CC : des moteurs a courant continu
PP : des moteurs pas à pas
CAM : des caméras
TLM : des télémètres
REC : des récépteur/démodulateur IR
EMI : des modulateur/émetteur IR
OUI
NON
Fils
Père
Création de la socket
Test du type
Attente de connexion
Exécution du code correspondant
Fermeture de la connexion
Réception du type du fils à créer
Execl du programme avec arguments
Fermeture de la connexion
Fin
Le premier test « Nb d’arguments >2 » ne sert qu’en cas de simulation, dans le cas où
l’étudiant n’aurait pas encore de module à tester. Pour ne pas créer autant de fichiers
exécutables que de modules à tester, j’ai préféré intégrer le code – servant à simuler les
modules – dans le même programme, précisant en argument le module à simuler lors de son
exécution, et entrant donc directement dans la zone de code adéquat (à l’aide de l’instruction
switch()).
Protocole de communication
Afin de normaliser les échanges de données entre l’interface et les modules, j’ai créé
différentes classes, dont CMessage, la classe de base de laquelle dérivent toutes les autres. Le
but de ces classes est de générer des trames, dont j’ai pu choisir librement le format. Une fois
les données membres renseignées, l’utilisateur n’a plus qu’à envoyer dans la socket la trame
que lui retourne la fonction membre prévue à cet effet.
CMessageCCbn
CMessageCChn
CMessagePPbn
CMessagePPhn
CMessageCamImg
CMessage
CMessageCamCouleur
CMessageTelemTous
CMessageTelemUnique
CMessageRecepteur
CMessageEmetteur
Les trames ont toute la même syntaxe, seules leurs données et leurs tailles diffèrent :
1er octet
2e octet
XX XXXXXX XXXX XXXX
Flag
Type
Taille des
(de 0 à 21) données à suivre
(en octets)
3e octet
…
XXXX XXXX
XXXX…
Données
Seule une trame générée par la classe de base CMessage diffère syntaxiquement, puisqu’elle
ne possède pas de taille de données, ses données étant facultatives et se limitant à un seul
octet, situé après le premier octet. Cette trame n’est donc constituée que de 2 octets.
XX XXXXXX XXXX XXXX
Flag
Type
(de 0 à 21)
Octet
d’information
(facultatif)
 Flag :
0 : trame unique
1 : début de « multi-trame »
2 : suite de « multi-trame »
3 : fin de « multi-trame »
(« multi-trame » = trame découpée en plusieurs petites car trop longue pour être envoyée en
une seule)
Ex : trame unique, de type 15, comportant 3 octets de données
00
001111
0000 0011
 Type :
0 : Fin de connexion
2 : (I) Moteur CC bas niveau
4 : (I) Moteur CC haut niveau
6 : (I) Moteur PP bas niveau
8 : (I) Moteur PP haut niveau
10 : (I) Caméra image
12 : (I) Caméra couleur
14 : (I) Télémètre unique distance
16 : (I) Télémètres distances
18 : (I) Récepteur robot
20 : (I) Emetteur code
0010 1010
1100 1001
0101 1111
1 : Connexion
3 : (R) Moteur CC bas niveau
5 : (R) Moteur CC haut niveau
7 : (R) Moteur PP bas niveau
9 : (R) Moteur PP haut niveau
11 : (R) Caméra image
13 : (R) Caméra couleur
15 : (R) Télémètre unique distance
17 : (R) Télémètres distances
19 : (R) Récepteur robot
21 : (R) Emetteur code
(I) : message « Impulsion » : utilisé comme ordre ou requête simple. C’est une instanciation
de la classe de base CMessage pouvant comporter 1 octet d’information sur l’ordre ou la
requête si nécessaire.
(R) : message « Renseigné » : utilisé comme retour de données en réponse à une requête, ou
comme ordre renseigné dans le cas d’instructions avec paramètres. C’est une instanciation de
la
classe
dérivée
adéquate.
Contraintes
Ecrire un programme pour uClinux équivaut à l’écrire pour linux, à quelques exceptions près.
Ainsi, de petits inconvénients comme l’absence du terme « const » fréquent en C++, mais
aussi plus important comme l’absence de la méthode fork(), ont impliqués quelques
changements de méthodes de programmation.
fork() / vfork()
La primitive fork() permet de créer un nouveau processus par duplication du processus
appelant. Ce qui signifie que le nouveau processus exécute une copie du programme mis en
œuvre par le processus père. La primitive fork renvoie un entier qui correspond au numéro du
pid du processus créé qui hérite d’un certain nombre d’attributs du processus père :
 Même programme
 Mêmes données
 Arguments et environnement identique
 Répertoire courant identique
 Priorité, descripteurs de fichiers, ressources, gestion de signaux identiques…
fork() = duplication
du processus :
même programme,
mêmes données ...
main()
vfork()
Père en attente
Fils
Reprise du père
exec() ou exit()
Mais uClinux ne fournit pas d’implémentation complète de
fork(), mais une implémentation simplifiée, celle du vfork()
de BSD (système d’exploitation de la famille UNIX). Avec
vfork(), l’exécution du processus père est suspendue jusqu’à
ce que le processus fils n’utilise la primitive exec() ou exit().
Dans la plupart des cas l’utilisation de vfork() n’affecte pas le
déroulement de programmes pour UNIX ou Linux.
Néanmoins, il est important de prendre en considération que
le processus fils partage, avec le processus père, la même pile
– zone mémoire – et les variables globales. Il est donc
important que le processus fils ne revienne pas (return) de la
fonction dans laquelle vfork() a été invoqué, n’appelle pas
d’autre fonction, ou ne modifie pas de variables du processus
père avant d’avoir invoqué avec succès la méthode exit() ou
une routine de la famille exec().
Conclusion
Ce projet m’a beaucoup appris, tant au niveau technique que méthodologique.
En effet l’aspect technique fût très enrichissant. Ayant opté pour certains choix logiciels
convenant mieux à nos besoins, j’ai eu l’opportunité d’apprendre un nouveau langage : le
Java. De plus, cette aventure m’a fait comprendre le fonctionnement d’un robot, impliquant
une réflexion permanente, tant au niveau de sa réalisation matérielle que logicielle
(déplacement, perception, stratégie…). Enfin j’ai pu découvrir de nombreuses techniques et
autres avancées technologiques utilisées en robotique en général.
Aussi, j’ai pu améliorer ma méthodologie, puisqu’un projet de ce type a pour but de nous
apprendre à travailler en équipe, mais aussi à être plus performant individuellement, sortant
du cadre d’un simple TP.
Par ailleurs, j’ai réalisé qu’un groupe ayant un objectif sans directives précises peut
difficilement être productif. C’est ainsi que j’ai pris conscience du comportement à adopter
dans ce type de projet, où réactivité, investissement et concentration sont des notions
capitales.
Téléchargement