vendredi 27 avril 2018

Chapitre 27 : Logique floue et GPIO du Raspberry


Le chapitre 2 du livre précédemment cité sur l’Intelligence Artificielle propose un exemple de logique floue. Cela m’a donnée l’idée de l’appliquer au GPIO du Raspberry. Pour cela, je voudrais gérer la luminosité d’une LED en fonction de la lumière reçue par 2 cellules photoélectriques.
C’est un projet complexe car outre l’adaptation de la logique floue à l’assembleur, il faut aussi arriver à récupérer les intensités lumineuses de chaque photo résistance puis moduler la luminosité de la LED au travers des instructions du GPIO.
Donc je vais le décomposer en 3 parties : un programmepour tester la récupération de la luminosité et connaitre l’échelle de variation possible. Un deuxième programme pour tester la variation de la lumière émise par la LED et là aussi connaitre l’échelle des valeurs possibles et enfin un 3ième programme pour gérer les ensembles et règlesflous.
Comme pour le programme sur le système expert, je vous renvoie pour les concepts théoriques au livre en question.
En premier, recherche sur Internet du montage pour lire les valeurs d’une photo résistance et d’un exemple de programme associé pour comprendre le principe. Le montage est assez simple. J’adapte le programme déjà écrit précédemment pour intégrer la lecture de l’état de la photo résistance.
Pour la Led, je recherche comment gérer sa luminosité. C’est moins simple car il faut alternativement l’allumer et l’éteindre avec des durées différentes. Et il va falloir donc trouver une solution pour garder la luminosité constante pendant un laps de temps jusqu’au calcul suivant.
Enfin je traduis en assembleur les routines données en Java dans le livre précédent. Pour simplifier la saisie, je développe toute une partie pour récupérer les variables d’entrées, de sortie et les règles dans un fichier de configuration. Les routines de gestion seront rassemblées dans un fichier objet qui sera lié à un programme maitre. Seul celui-là sera à modifié et à recompiler en fonction des valeurs d’entrées nécessaires à la résolution.
Les routines de gestion des ensembles floues sont assez compliquées. En plus, j’ai choisi d’utiliser des listes chainées pour gérer toutes les données nécessaires. Cela oblige d’utiliser un grand nombre de pointeurs ce qui ne facilite pas la lecture du programme. Puis c’est du brut de fonderie, je n’ai pas réfléchi à toutes les améliorations possibles.
Au début du programme, nous trouvons les descriptions des structures nécessaires : une pour les points, une pour les ensembles flous, une pour le contrôleur, une pour les variables et enfin la dernière pour les valeurs. Vous remarquerez qu’en plus des données propres, des pointeurs vers la structure suivante ont été ajoutés pour gérer les listes chainées.
Ensuite nous trouvons les descriptions des libellés nécessaires puis les réservations de place des variables. Peu de variables sont déclarées car toutes les données seront créées dans un tas et accessibles seulement par des pointeurs.Dans ce programme nous utiliserons le tas du système (heap) en récupérant l’adresse de début du tas par la fonction call system brk de Linux. Nous ajouterons notre taille nécessaire et nous fixerons la nouvelle taille du tas par le même call system brk. Cette solution permet de créer plusieurs contrôleurs avec des configurations différentes dans le même programme ( cas de figure non testé à ce jour).
La première routine FLOUcreationcontroleur concerne la création du contrôleur à partir du nom du fichier de configuration passé en paramètre dans le registre r0. Nous commençons par lire le fichier de configuration pour vérifier s’il existe bien. Puis nous créons dans le tas, le contrôleur à l’aide de la description de sa structure. Pour tout ce qui concerne les pointeurs de débuts de liste chainées, nous les initialisons avec une adresse du tas contenant une valeur sentinelle (cad que le pointeur contenant l’entité suivante contient en fait sa propre adresse). Nous aurions pu nous contenter de mettre une valeur nulle dans le pointeur mais cette solution permet de s’assurer que les listes sont correctement alimentées et terminées par une sentinelle de fin. Une valeur à zéro pouvant être aussi une erreur de programmation.
Puis nous passons à une sous routine d’analyse du fichier de configuration pour créer les variables d’entrées, et leurs valeurs associées, la variable de sortie et ses valeurs associés, et les règles de détermination. Cette programmation n’est pas le top et a été un peu améliorée par rapport au programme sur le petit système expert (voir chapitre 21). J’ai écrit une sous sous routine pour boucler sur les blancs présents dans le buffer de lecture et une autre pour analyser chaque mot, mettre un 0 binaire de fin de chaine et avancer jusqu’au prochain mot valide (ou la fin de fichier) en éliminant les blancs, le caractère « : » et les caractères de fin de ligne (soit 0D0A mais à vérifier suivant votre système et votre éditeur de texte adoré). Chaque variable, valeur ou règle est stockée dans la liste chainée adéquate du contrôleur.
Après mise à jour en mémoire du pointeur du tas, la routine retourne l’adresse du contrôleur au programme appelant.
Une autre routine concerne l’ajout de valeurs déterminant le problème à résoudre. Les paramètres en entree sont l’adresse du contrôleur, le nom de la variable concernée et la valeur associée. Si la variable n’est pas trouvée dans les variables d’entrée données par le fichier de configuration, un libellé d’erreur est affiché.
Puis nous trouvons la sous routine résoudre qui est l’exact reflet des routines décrites dans le livre de MME MATHIVET. Quelques petites différences quand même : pour éviter des calculs en virgule flottante, la valeur Y de chaque point est 100 et non pas 1. Ce qui entraine qu’un résultat de 77 par exemple correspond à un résultat de 0,77 du livre. Les sous routines semblent complexes, mais j’ai mis le maximum de commentaires pour expliquer les calculs et opérations réalisées. A ce jour, j’ai testé ces routines avec les données du livre, et il y a une petite différence dans les calculs pour un des cas. Malgré mes recherches, je n’ai pas trouvé l’erreur et je suspecte qu’une entrée du livre est incorrecte !!!
Enfin la dernière sous routine concerne la réinitialisation des données du problème pour effectuer un nouveau calcul. Et pour éviter tout accroissement du tas, cette routine remet l’adresse du tas à sa valeur après l’analyse du fichier de configuration.
J’ai ajouté d’autres sous routines pour afficher les variables d’entrées, de sortie et les règles, ce qui permet de vérifier la bonne prise en compte du fichier de configuration.
Après compilation, le fichier objet sera linké avec le programme appelant ainsi que le fichier des sousroutines de gestion du gpio

Ici dans notre cas, le programme appelant va appeler la routine de création du contrôleur en lui passant le nom du fichier de configuration récupère dans la ligne de commande comme déjà vu dans des chapitres précédents. Puis va appeler dans une boucle, la routine de lecture de la photorésistance 1 la routine de lecture de la photo résistance 2, passer les 2 valeurs au contrôleur par la sous routine FLOUajoutervaleur,, appeler la résolution, puis appeler la routine d’allumage de la LED avec le résultat précèdent en paramètre. Puis attendre un certain temps avant de boucler à nouveau après avoir réinitialiser l’état du contrôleur a son état après la prise en compte des règles.
Le jour où j’écris ces lignes, je ne sais pas encore si tout cela va fonctionner correctement !! Je pense que cela va nécessiter plusieurs ajustements tant au niveau des valeurs qu’au niveau des règles. Bon je vais déjà aller tester la partie GPIO avec les montages proposés.
Quelques jours après : après quelques erreurs de montage, les 2 premiers programmes ont bien fonctionné. J’ai pu obtenir les variations de la cellule photoélectrique ( de 5000 à 80000) et j’ai donc appliqué un facteur de réduction de 1000 pour rester dans une plage de valeur de 5 à 800. Pour le réglage de la luminosité de la LED, les réglages ont été plus délicats et ne sont pas encore tout à fait satisfaisants. Mais cela suffit pour effectuer un test final.
J’effectue soigneusement le montage des 2 cellules photorésistantes (attention au sens des condensateurs) l’ajout de la led (attention aussi à son sens), je modifie le programme principal pour que les pins du GPIO correspondent bien à ceux du programme (18 pour la LED, 4 et 22 pour les cellules) J’adapte le fichier de configuration pour déterminer les différentes valeurs et je crée quelques règles de génération de la luminosité (voir plus bas la syntaxe du fichier de configuration à respecter). Et cela fonctionne !!! En passant la main devant les cellules, la luminosité de la LED varie (imparfaitement mais tout est dans le fignolage des réglages).
Pour lancer le programme il faut taper :  pilotageLedFinal configFlGpio.txt

Et maintenant ?   restent à fignoler les règles et réglages, à réécrire les routines pour améliorer l’utilisation des registres et la lisibilité.

Syntaxe du fichier de configuration (fichier texte) :
1ère ligne : nom du contrôleur ( ne sert à rien !!!mais est obligatoire)
2ième ligne : libellé Entrées:     indique le début de la liste des variables d’entrées
Ligne Variable:nom_de_la variable:valeur mini:valeur maxi
Pour chaque variable plusieurs lignes valeurs
Valeur:nom_de_la valeur :type_ensemble_flou :valeur1 :valeur2 :valeurn :
Avec type d’ensemble flou : EFTG   : Ensemble Flou Trapèze Gauche suivi de 4 valeurs en ordre croissant dont la première est égale à la valeur mini et la dernière à la valeur maxi
                                               EFTD   : Ensemble Flou Trapèze Droit suivi de 4 valeurs en ordre croissant dont la première est égale à la valeur mini et la dernière à la valeur maxi
EFTRA   : Ensemble Flou TRApèze suivi de 5 valeurs en ordre croissant dont la première est égale à la valeur mini et la dernière à la valeur maxi
EFTRI   : Ensemble Flou TRIangle suivi de 5 valeurs en ordre croissant dont la première est égale à la valeur mini et la dernière à la valeur maxi
Puis le libellé Sortie : avec une seule variable et ses valeurs comme une variable d’entrée.
Puis le libellé Règles :
Puis chaque règle codifiée comme suit
SI nom_variable_entrée EST nom_de la valeur [ET nom_variable_entrée EST nom_de la valeur…] ALORS nom_variable_sortie EST nom_d’une_valeur_de_sortie
Attention : les contrôles de cette syntaxe sont très légers !!! et donc vérifier bien l’affichage lors de l’execution pour contrôler la prise en compte.


Aucun commentaire:

Enregistrer un commentaire