PiRDF #2 : Préparation du matériel

PiRDF #2 : Préparation du matériel

Cet article permet de lister tous les éléments nécessaires à la préparation du Raspberry Pi pour l’application PiRDF que sous avons préciser l’article de présentation générale.
Nous allons voir point par point comment réaliser l’installation de toutes les dépendances nécessaires.

Préparation du système d’exploitation

Dans il est nécessaire d’installer un système Raspbian sur une carte SD. On ne le répétera jamais assez, il est très important que votre carte SD soit de qualité et ait des propriétés de vitesses importantes. J’utilise personnellement ce modèle des SanDisk classe 10 comme celle-ci :

On notera que 16GB est suffisant dans ce cas particulier.

Pour installer le système d’exploitation, vous devez télécharger la dernière image de Raspbian, ici celle du 09-10-2018 ce qui m’a permis de tester la dernière version.

Pour la copier, sur la carte, il suffit d’effectuer une copie sur la carte de l’img iso après l’avoir formatée. La copie s’effectue à l’aide de l’instruction dd selon le formalisme suivant :

dd bs=4M if=2018-10-09-raspbian-stretch.img of=/dev/sdb conv=fsync

l’argument if est utilisé pour les données d’entrée (ici l’image) et of pour la destination ici, /dev/sdb.

NB : l’installation les instructions ici sont celle d’un système Linux. Si vous effectuez l’installation depuis un système Windows (pratique que je déconseille), reportez-vous à rubrique d’aide spécifique du site officiel de Rapberry Pi’ c’est à dire :

Après avoir réaliser ces opérations de copie de l’image, il ne vous reste plus qu’à placer la carte SD dans le Raspberry Pi et de le démarrer.

Enfin, pour finir votre installation, il faut faudra configurer :

  • la langage du système
  • la timezone
  • le tytpe de clavier
  • le WIFI

Toutes ces informations vous seront demandées par le biais d’un assistant au premier démarrage. C’est une des nouveautés de la nouvelle version de Raspbian.

Après cela, il est temps de passer à l’installation des éléments liés à python.

La stack python

Outils de développement

Raspbian arrive avec des versions de python installer : la version de python 2.7.XX (je sais plus trop et j’en moque, je l’explique dans quelques lignes) et la version 3.5.3.
Je ne reviendrais par sur les raisons qui ont poussé la communauté de développeurs de maintenir pendant de trop nombreuses années 2 versions de python, mais le fait est que Python ne sera plus maintenu à partir de 2020 (voir le python countdown) et les librairies feront de même rapidement. Certaines librairies comme sci-kit-learn (que nous allons utiliser) l’ont déjà annoncé.
Pour ces raisons évidentes, je ne développe que du code compatible Python 3 et je n’assure aucune compatibilité pour Python!

A Python 3, j’ajoute deux outils de développements dont je considère qu’ils améliorent ma productivité :

  • ipython
  • ipdb

ipython

Ipython est environnement permettant d’exécuter du code de manière interactif, ce qui permet en d’autres choses d’accéder aux valeurs des différentes variables après exécutions, d’exécuter des commandes(dont certaines commandes consoles) directement et d’avoir accès à la coloration syntaxique et l’autocomplétion.

Pour l’installer, il suffit d’exécuter la ligne suivant :

pip3 install ipython

ipdb

Ipdb est un débugger un peu plus évolué que pdb (le débugger officiel). Il est surtout plus ergonomique que celui-ci. Pour installer, il faut également passer par pip :

pip3 install ipdb

Librairie scientifique

Pour réaliser les tâches de caractérisations des images et de prises de décisions automatiques, nous aurons besoin d’utiliser un certain nombre de librairies qu’on qualifie souvent de scientifique.

scipy et numpy

scipy et numpy sont inséparables, ce sont les deux librairies qui permet de réaliser tout ce qui est lié au calcul scientifique dont le calcul matriciel et l’algèbre linéaire.
Il ne faut pas envisager de les installer via pip car le risque de swap est très important et l’installation risquerait de ne pas aboutir.
Il faut mieux passer par le gestionnaire de paquet apt. Pour cela il faut saisir les commandes suivantes :

sudo apt-get install python3-scipy

sudo apt-get install python3-numpy

sci-kit-image

Sci-kit-image est la librairie qui permet d’accéder à un grand nombre de méthodes de traitement d’images disponible dans la littérature scientifique. Cette librairie possède également une couche IO (entrée/sortie) qui permet d’ouvrir de nombreux format d’images et de stocker les données dans des matrices de type numpy. De la même manière sci-kit-image permet un enregistrement de données brut en de nombreux formats d’images (png, jpeg, bmp…).
Pour l’installation, ça se passe pas apt :

sudo apt-get install python3-skimage

sci-kit-learn

Sci-kit-learn est une librairie spécifique d’outils d’intelligence artificielle. Nous utiliserons pour les phases d’apprentissage et de décision automatique. Pour l’installation, on passe encore une fois par apt :

sudo apt-get install python3-sklearn

Interfaçage de la caméra et éléments graphiques

Il ne nous reste plus qu’à installer les deux librairies permettant d’une part l’accès à la camera et à ses données grâce à picamera et la librairie pygame qui nous permettra de sans trop développement d’afficher le flux vidéo dans une interface graphique.

Pour l’installation, on passe cette fois par pip:

pip3 install picamera

et

pip3 install pygame

NB : On notera que dans la dernière version de Raspbian, ces deux librairies sont déjà installées

Et le hardware là-dedans

Après, le software, il nous est nécessaire de préparer le hardware. Il n’est pas nécessaire de préparer le hardware final pour ce qui est des switches. J’ai préféré passer par une phase de prototypage avec des microswitches, une « plaque lab » et quelques fils.

Pour aller un peu plus loin

Les autres articles du projet PiRDF :

Le matériel utilisé (liens affiliés — si vous commandez via ce lien, j’ai quelques % en commission pour payer l’hébergement de ce site l!):

PiRDF #1 : Présentation du projet

PiRDF #1 : Présentation du projet

Ce billet marque le début d’une série d’articles concernant la documentation d’un projet que je présenterai à la Maker Faire Paris sur le stand des éditions ENI qui est l’éditeur de mon livre intitulé « Python. Libérez le potentiel de votre Rapsberry Pi ».

Ce projet, je l’ai intitulé PiRDF. Pi en référence au Raspberry Pi et RDF pour le Reconnaissance De Formes. Le but de ce projet sera donc de construire une petite application qui permet d’apprendre et de reconnaitre des objets simples.
Tous les éléments logiciels seront développés en langage Python et publiés sur le « repository » Github : Projet PiRdf sur github. De cette manière quiconque pourra utiliser le code et/ou proposer des évolutions.

Le but de cette démo est montré au public qu’il est possible en très peu de lignes de construire des applications dont les fonctionnalités peuvent être complexes.

Enfin, on notera que l’ensemble de ces articles est une consignation des notes au cours de la réalisation. En effet, à l’heure où j’écris ces lignes le développement du code est loin d’être abouti, vous verrez sans doute des erreurs de conception qui vont nécessiter des refactoring d’un article à l’autre, j’essayerai donc de documenter toutes ces évolutions.

Dans la suite des cet article, je vais détailler le cahier des charges après une courte introduction de ce qu’est la reconnaissance de formes.

C’est quoi la reconnaissance de formes

La reconnaissance de forme est un des sous-domaines de la vision par ordinateur qui est elle-même une discipline à la croisée de nombreuses disciplines que sont :

  • l’informatique
  • les mathématiques appliquées
  • le traitement du signal et des images.

La reconnaissance de formes a pour but de reconnaitre au sein des images un ou des motifs appris au préalable. Ce motif peut être, par exemple, un objet, un visage…
La reconnaissance de formes fait donc appelle à des techniques comme l’apprentissage automatique et à la modélisation statistique. Pour le profane, on pourrait résumer le tout en disant que la reconnaissance de formes est une branche de l’intelligence artificielle même si le terme a en partie perdu de sa teneur à cause de la médiocrité de la vulgarisation de certains médias ces dernières années… Bref, ça, c’est un autre sujet…

Historiquement en sépare la reconnaissance d’objet en 2 familles :

  • l’approche globale
  • l’approche locale

l’approche locale

L’approche locale regroupe les différents techniques qui permettent de détecter et reconnaître un objet au sein d’une scène composée d’un ensemble d’objets.
Pour illustrer, on peut citer la détection de piéton (voir l’image ci-dessous) dans une scène urbaine qui trouve des applications dans le développement des véhicules autonomes.


Crédits : By Milwaukee_(WIS)N_5th_St« Tree,_Rain,_Wind« Pedestrian_1.jpg: vincent desjardinsderivative work: Indif (talk) – Milwaukee(WIS)N_5th_St« Tree,_Rain,_Wind« _Pedestrian_1.jpg, CC BY 2.0, https://commons.wikimedia.org/w/index.php?curid=11912052

Cette approche qui a beaucoup évolué en matière de performances depuis le début des années 2010, n’est toujours pas parfaite et le taux d’erreur reste toujours trop élevé pour certains types d’applications.
De plus, les dernières méthodes nécessitent, le plus souvent, des puissances de calcul très élevées. À titre d’illustrations, la voiture Tesla autonome possède la puissance de calcul de plusieurs MacBook Pro…
Il est par conséquent quasiment impossible d’espérer faire tourner ce genre de manip sur un Raspberry Pi qui possède des ressources matérielles limitées tant au niveau du processeur que de la mémoire de type RAM.

l’approche globale

À l’inverse, l’approche globale est plus simple et historiquement antérieure à l’approche globale.
Le but, ici, est de reconnaitre un unique objet au sein d’une image ou d’une région d’intérêt au sein d’une image.
Dans ce cas, le nombre de méthodes disponibles dans la littérature est plus nombreux et les performances atteintes par les meilleurs sur les bases de test sont de plus en plus proches de 100% de réussites.
Enfin, ils existent une multitude de méthodes qui présentent une complexité calculatoire diverse et variée. Dans tous les cas, de nombreuses méthodes peuvent être envisagées pour une implémentation en python sur le Raspberry Pi.

Cahier des charges — c’est quoi donc le projet PiRDF

L’application sera réduite au maximum en termes de fonctionnalités et d’interface graphique.
Dans tous les cas, elle sera découpée en 2 phases : 1- l’apprentissage 2- la décision. Les sections qui suivent vont permettre de définir plus précisément les éléments fonctionnels de chaque tâche.

Phase d’apprentissage

La phase d’apprentissage correspond à l’ajout d’un nouvel élément à une base de connaissance.
En pratique cela correspond à calculer un ensemble de caractéristiques à partir de l’image qui contient l’objet de calculer un modèle d’apprentissage à partir de la base de connaissance.

D’un point de vue pratique, on aura à disposition un bouton branché sur le GPIO qui permet de démarrer l’apprentissage.
L’appui sur ce bouton permet de commencer à calculer les caractéristiques pour les images arrivant de la caméra.
Toutes images ne serviront pas à l’apprentissage.
Nous réaliserons un suréchantillonnage du signal vidéo de l’ordre d’une image par seconde voir moins si le temps de traitement est trop long.

Pour mettre fin, à la phase d’apprentissage un second bouton sera installé sur le port GPIO.
Un appui sur ce bouton permet d’arrêter la phase de création et d’enrichissement de la base de connaissances et de réaliser la phase d’apprentissage à proprement dites et permet de créer un modèle de décision.

À partir de cette étape, nous possédons l’information nécessaire pour réaliser une décision vis-à-vis de l’objet présent devant la caméra.

Phase de décision

La phase de décision consiste à déterminer la classe d’appartenance d’un objet devant la caméra aux classes existantes dans le modèle.
Pour réaliser cela, on va calculer un ensemble de caractéristiques à partir de l’image et utiliser un modèle décisionnel pour déterminer la classe d’appartenance.

Enfin, le nom de la classe d’appartenance sera transmis un module de synthèse vocale qui permettra de restituer le nom de la classe d’un point de vue audio.

Par ailleurs, 2 boutons permettront de démarrer et d’arrêter la phase de décision.

À venir prochainement

Si vous jetez un oeil sur le repo Github vous verrez que le coeur du projet est bien avancé.
Vous pouvez donc fouiller pour voir l’évolution du projet en termes de code.
L’article qui suivra détaillera la configuration nécessaire du raspberry à partir d’une configuration vierge.

Le matériel utilisé (liens affiliés — si vous commandez via ce lien, j’ai quelques % en commission pour payer l’hébergement de ce site l!):

[Python] Interruption sur le port GPIO

[Python] Interruption sur le port GPIO

Avant d’aller plus loin, il est nécessaire de définir ce qu’est une interruption.
En informatique, informatique embarquée, ou électronique, une interruption est un processus qui est déclenché de manière asynchrone afin de permettre un éventuel traitement temps-réel des données.

Afin d’illustrer concrètement le phénomène, voici un exemple d’usage. Imaginez que vous vouliez allumer une LED lorsque vous appuyez sur un bouton qui est relié à une des bornes de votre GPIO.
Il existe pour cela plusieurs possibilités la manière brutale :

1- mettre une boucle de test dans le programme principale qui va tester l’état du bouton à chaque itération du programme principale
2- si l’état du bouton passe à l’état attendu qui peut être True(Vrai) ou False(Faux) alors on appelle une fonction de traitement

Le problème de ce genre de méthode est que si les tâches de votre programme principal sont alors le traitement de l’état du bouton sera alors effectué une fois par tâche.
Par conséquent, si le traitement de la tâche associée au bouton, il est impossible d’avoir une réaction immédiate (« temps-réel »).
Dans le pire des cas, on pourra même rater des phénomènes se produisant sur le bouton.
En effet, imaginons que le bouton sur le GPIO soit un bouton poussoir. L’appui sur le bouton produit un signal d’état positif (+5V. NB : Cela pour être 0V suivant le câblage de votre bouton) pendant quelques millisecondes, or si le traitement dans votre programme principal est de quelques secondes, la probabilité de rater le phénomène d’appui est plus importante que la probabilité de l’attraper au vol!!!!

Pour limiter tous ces problèmes, il est préférable d’utiliser le mécanisme d’interruption.
En pratique, cela va permettre d’exécuter une portion de code dès que le bouton sera pressé en quittant temporairement le programme principal puis en y retournant dès que cette portion de code sera exécutée.

Pour mettre en œuvre cela en python, on va utiliser l’instruction « add_event_detect » de la librairie RPi.GPIO qui permet de configurer une interruption.
Plus particulièrement, on va associer une broche à une fonction qui sera appelée que l’évènement va se produire sur la broche en question pour exemple :

 GPIO.add_event_detect(7, GPIO.RISING, callback=my_callback, bouncetime=300)

Dans ce sa on va déclencher l’appel à la fonction my_callback lors d’un passage à l’état haut (GPIO.RISING) sar la broche 7.
On notera que la paramètre bouncetime=300 correspond au temps pendant lequel l’interruption ne sera pas sensible [aux rebonds du bouton](TODO: mettre un lien vers un article de définition du rebond d’un bouton).

Par conséquent, on obtient à minima le code suivant :

import RPi.GPIO as GPIO


def init():
    # configuration de la broche 7 en entree
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(7, GPIO.IN)
    # definition de l'interruption
    GPIO.add_event_detect(7, GPIO.RISING, callback=my_callback, bouncetime=300)


def my_callback():
    # callback = function which call when a signal rising edge on pin 7
    print("Vous avez appuyez sur le bouton de la patte 7")


if __name__ == '__main__':

    # 1- initiation de la l'interruption
    init()
    # 2- boucle infini = tache principale
    while True:
pass

La fonction init initialise le GPIO et «accroche» la fonction my callback() à la broche 7.

Attention cependant, dans l’exemple qui précède, tous les traitements sont réalisés dans la fonction de callback.
Cette façon de faire est intéressante pour gagner un maximum de temps.
Cependant il est nécessaire d’avoir un minimum de chose à réaliser dans cette boucle.
En effet, le risque de freezer le programme dans son ensemble est important.

D’une manière générale, une bonne pratique est (lorsque l’exécution de la fonction n’est pas cruciale et que le temps-reél tient) de mettre un flag à True(Vrai) dans le callback et de lancer l’exécution de la fonction de traitement à partir du programme principal.

Le programme ci-dessous illustre le fonctionnement :

import RPi.GPIO as GPIO


# variable globale qui sera vue dans toutes les fonctions
flag_callback = False

def init():
    # configuration de la broche 7 en entree
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(7, GPIO.IN)
    # definition de l'interruption
    GPIO.add_event_detect(7, GPIO.RISING, callback=my_callback, bouncetime=300)
    # initialisation du flag
    flag_callback = False


def my_callback():
    # function qui sera appelé lorsque le programme sur interrompu
    flag_callback = True


def process_ny_callback():
    print("Interruption détectée sur la broche 7")

if __name__ == '__main__':

    # 1- initiation de la l'interruption
    init()
    # 2- boucle infini = tache principale
    while True:
        # 3- si une interruption c'est produite alors on lance le traitement c
        # adéquat
        if flag_callback == True:
            process_ny_callback()
    pass

Vous pourrez retrouver les sources sur le repo github suivant : https://github.com/clemaitre58/20181002_interruption.

Alors prêt à attaquer les interruptions avec Python?