Et on continue avec la suite de ceci.
Informatique
La partie logicielle est à mes yeux la plus large des trois. En effet, il y a mille et une manière de faire tout et n’importe quoi, et dans l’ensemble on peut faire et défaire tout comme on le souhaite. Entre les OS, les langages, les librairies, les algorithmes, les compétences et les préférences du développeur… Il y a autant de réponse à une question donnée qu’il y a de question !
Le système d’exploitation
Déjà, quel OS pour le Raspberry ? La question est vite réglé, malgré les diverses versions de GNU/Linux disponible : le choix est Raspbian, la version de Debian spécialement adapté au Pi. C’est la version la plus utilisée, et donc celle sur laquelle on a le plus de support. Ensuite, l’avantage de l’OS, c’est le fait de pouvoir lancer des process, rentre certaines taches automatiques simplement, la partie communication avec l’extérieur est simple (par exemple sauvegarder un fichier de log en local puis le synchroniser sur un répertoire partagé sur le réseau, c’est bête comme chou).
Langage de Programmation
Le Pi est, principalement, dédié à être utilisé avec des scripts en python. On trouve à ce titre des dizaines de trucs fait en python pour tout et n’importe quoi : piloter des GPIO, la caméra, faire des sauvegardes ou bien faire une interface console pour l’utilisateur. Mais parce qu’on a un OS Linux, on a aussi moyen d’utiliser n’importe quel langage. Et quand je parlais des préférences du développeur, c’est là que ça joue. Moi, le python… c’est pas trop ma tasse de thé, alors que le C, ça me branche vachement plus. Et je me débrouille mieux avec. Dans la mesure du possible donc, je tente de faire un maximum de chose en C, et d’utiliser d’autres options (script python tout prêt, Shell scripts, …) uniquement quand je n’ai pas le choix. Garder une certaine homogénéité me parait important pour pouvoir maintenir plus facilement l’ensemble du projet.
Accéder aux fonctions « spéciales » du Raspberry Pi
Sans rien autour de lui, le RPi est un ordinateur sous Linux, ni plus ni moins. Mais il dispose d’interface avec le monde extérieur qui nécessite un peu de boulot pour être exploités. La question se pose donc, dois-je développer moi-même mes propres fonctions pour y accéder ou existe-t-il des bibliothèques toutes faites pour me faciliter la vie ? Et ces bibliothèques valent-elles vraiment le coup ?
– GPIO
=> La fondation Raspberry a eu une bonne et une mauvaise idée. La bonne c’est de rapidement mettre à disposition des utilisateurs une librairie pour facilement utiliser toutes les fonctions des GPIO, que ça soit l’utilisation en mode brut (entrée/sortie numérique), ou bien l’utilisation des bus de communication disponible (I2C et SPI). La mauvaise, c’est que c’est en python. C’est là qu’intervient WiringPi, une librairie pour la gestion de GPIO en C. Pile poil ce que je voulais. Son auteur a le bon goût d’avoir rendu sa lib compatible avec le RPi B+ très rapidement.
J’ai procédé rapidement à quelques tests unitaires, pas de problème pour piloter les moteurs (utilisation des GPIOs), ni pour faire récupérer des données sur l’ADC (utilisation du bus SPI).
– Camera
=> Ici les choses se compliquent un peu. Je n’ai pas trouvé de librairies pour piloter la caméra directement. Ma principale expérience pour le moment est la réalisation d’un système de détection de mouvement, avec la caméra montée sur un moteur. Pour faire une simple capture, il faut taper une commande dans la console sous linux, dans laquelle on indique la résolution de sortie, le format, si on pivote l’image, si on applique un filtre, l’ISO, etc. C’est facile, mais la caméra met alors plus seconde pour faire une capture (indépendamment de la vitesse d’obturation). Pas terrible pour faire du suivi rapide d’objet en déplacement. L’astuce est alors lancé la capture en mode daemon (en lui précisant la taille, le format, etc.) et de lui envoyer par la suite un signal pour faire une capture. A ce moment, le cliché est instantané. Bref, c’est presque parfait, sauf que c’est en ligne de commande, et donc ça sent le Shell script. Première entorse à ma sacro-sainte homogénéité.
Capturer une image c’est bien, en faire quelque chose c’est mieux. Quand on parle traitement d’image sous Linux, il y a une bibliothèque qui vient rapidement à l’esprit, c’est OpenCV (Open Computer Vision). Elle dispose d’une myriade d’outils de traitement tout prêt pour faire tout et n’importe quoi avec vos images. Mais pour être honnête, c’est un mastodonte. Quand j’ai commencé mon détecteur de mouvement, j’ai eu envie de faire le traitement moi-même, histoire d’en apprendre un peu plus sur le traitement d’images. Au départ je voulais simplement comparer des images, mais de fils en aiguilles j’ai aussi implémenté la possibilité d’appliquer des filtres matriciels sur les images, calculer et afficher des histogrammes, ainsi que d’autre fonction simple comme de n’afficher que la luminance, ou bien une seul couleur en particulier. Bref, quand j’ai commencé à m’aventurer par-là, j’ai transformé mon soft de détection de mouvement en un sous-produit d’une librairie que j’ai appelé LLIPS (pour Light Library for Image ProcesS). Ça vaudrait un article à elle toute seule, mais je n’ai pas vraiment fini de la faire jolie avec les commentaires et la documentation qui va bien.
Pourquoi s’emmerder à faire une librairie qui existe déjà ? Simplement parce que je n’ai pas besoin de tout, et aussi parce que pour le coup, je peux adapter l’outil très rapidement. Pendant que je travaille sur le code RPi-kee, il m’arrive régulièrement de fignoler Llips, et de commiter à part les modifications.
– Généralité
=> Quand j’ai réalisé les premiers softs sur avec la caméra et le moteur, le cœur principal de mon programme était en Shell script (abordé un peu ici : ). Ce qui est bien avec les scripts c’est de pouvoir lancer des trucs en tache de fond. C’est très pratique car je peux lancer un mouvement moteur et faire autre chose pendant que ça tourne. De même, avec les traitements d’images, que ça soit des redimensionnements (que je faisais avec mogrify, vachement bien), des comparaisons, etc. Par contre j’ai un peu galéré pour la communication entre les différentes tâches. Ne serait-ce que pour lancer un chronomètre, je suis passé par un compteur écrit dans un fichier texte, qu’un programme écrit et qu’un autre relit. Bref, pas terrible.
Pour RPi-kee, j’ai rapidement compris que cette solution ne serait pas terrible, voir carrément à chier. De plus, si je m’amusais à coder un process pour faire des acquisitions, un autre pour les traiter, un autre pour faire du TCP et les transmettre, le coup des fichiers textes pour communiquer serait vraiment pourri. Il me restait donc deux solutions :
– La mémoire partagé entre les process, comme je l’avais fait il y a biiiieeeeeen longtemps à l’IUT
– Plusieurs tâches au sein d’un même process.
La deuxième solution m’attirait beaucoup plus, surtout que j’ai l’habitude d’utiliser un OS temps réel au boulot, et que je suis donc assez familier du sujet. J’ai donc déniché dans un coin la bibliothèque pthread !
Elle est disponible sous Linux nativement, et dispose également d’un port sous Windows, avec quelques restrictions mais qui n’impacte pas les fonctions de base. Ce port me permet donc de bosser également sous Windows. Pthread permet de définir fonction sous forme de thread tourne donc joyeusement en parallèle (dans la mesure du parallélisme tel qu’il existe dans un processeur de PC). Ce n’est pas une émulation ou une astuce du genre, quand on lance plusieurs threads on voit bien dans un gestionnaire de tâches le nombre de threads qu’on a lancé. Cela convient donc très bien à mes fonctions de pilotage moteur qui sont en fait une séquence ordonnée sur 4 GPIOs avec un délai entre chaque pas. Appeler la fonction « avancer » bloque donc ainsi l’exécution du programme. Avec un thread dédié par moteur, je peux donc lancer mes boucles en parallèle et faire autre chose en même temps. La possibilité de faire des threads change d’ailleurs grandement la façon d’implémenter une fonction. Si on prend l’exemple des acquisitions sur l’ADC; avec un seul thread, vous devez d’abord faire votre acquisition, qui vous prendra un temps T1, puis mettre en forme les données reçues, avec par exemple l’utiliser de look-up-table pour transformer la tension des capteurs IR en cm, traitement long d’une durée T2. Votre fonction prendra donc un temps T1+T2. Si vous travaillez maintenant avec des threads, vous synchronisez le début de la tâche « Calcul » avec la fin de votre première tâche « Acquisition ». Ainsi, lorsque la première acquisition sera finie, vous commencerez immédiatement la deuxième acquisition pendant que vous traiterez la première. Si T1 est plus grand que T2, votre fonction d’acquisition ne prendra plus qu’un temps T1. Si c’est T2 qui est plus grand, c’est le contraire (à quelques cas particuliers près qui se gèrent sans trop de difficulté).
S’il y en a deux qui suive au fond, ils n’auront pas manqué de noter que j’ai dit que je travaillais également sur l’OS de Microsoft. Et oui, c’est bien le cas.
Si j’ai réalisé plusieurs tests unitaires directement en développant sur le Raspberry Pi, à grand coup de putty et de WinSCP, quand j’ai commencé à créer le projet, j’ai préféré travailler avec un IDE. De plus, comme je développe ça sur mes heures perdues (dans le train, dans le salon sur mon portable, sur mon PC fixe, parfois même au boulot entre midi et deux), j’ai mis en place une compilation conditionnée à l’OS utilisé. Je fais donc la partie driver/bas niveau sur le Pi en SSH, et tout le haut niveau sur PC Windows. Evidemment je ne récupère pas toutes les valeurs analogiques et je ne peux pas utiliser WiringPi, mais cela me permet de faire les threads, le traitement d’image, la communication TCP et le contrôle commande de façon indépendante. Pour avoir les schémas électriques, les images de test et la doc technique, Google Drive est mon ami, et pour le code j’utilise un dépôt SVN … sous Github. Je leur dois au passage une reconnaissance éternelle d’avoir mis en place l’accès SVN à leurs dépôts ! En effet, je n’aime pas Git.
A l’heure actuelle, je n’ai pas encore terminé. La partie logicielle va être assez tordu à faire, avec toutes les tâches que j’ai prévu de faire, mais ça devrait m’occuper un bon moment. Il me reste également quelques soucis mécaniques à résoudre, et puis valider que les moteurs 12V peuvent faire leur boulot correctement. Au final, je suis quand même assez près de mon but, et j’espère pouvoir finir par avoir quelques choses de vraiment chouette. Dans les tâches annexes qu’il me reste à côté, j’aimerai également pouvoir utiliser un écran qui se plug directement sur le Raspberry Pi, histoire par exemple d’afficher en temps réel des infos sur le robot. Le problème étant surtout que l’écran utilise la même liaison SPI que l’ADC et qu’il y a de bonne chance qu’ils s’entre perturbent. Dans les à côté également, j’aimerai finir proprement Llips, histoire par exemple de le rendre multithread. Cela me permettrait surement de gagner encore un peu de temps de process, le cœur ARM11 du RPi n’étant un modèle de rapidité. Si j’avais VRAIMENT que ça à faire, il serait même probablement possible d’utiliser le GPU pour faire le boulot. Mais, du temps, c’est pas forcement ce dont je dispose le plus !
Et histoire d’illustrer un peu l’article parce que les images c’est cool, voilà le genre de traitement. On part d’une image avec une ligne (c’est un tapis ikea du meilleur goût), on cherche les zones de contraste, et on définit un certain nombre de point et de segment appartenant à cette zone de contrate.
On se servira ensuite des angles et positions pour définir la trajectoire du robot. Simple, non ?
Laisser un commentaire