Dans cet article nous allons apprendre au robot MR25 à éviter les obstacles en utilisant un neurone perception. Le perceptron est le neurone artificiel le plus simple, modèle de neurone biologique, il a été inventé en 1957 par Frank Rosenblatt au laboratoire d’aéronautique de l’université Cornell. Ce neurone reçoit plusieurs entrées, les combine avec des poids, puis prend une décision binaire.
Intelligence artificielle avec le robot MR25
Voici un exemple simple de neurone perceptron à 2 entrées utilisant les capteurs de proximité n°2 et n°4 du MR25.
Voici le principe de fonctionnement :
Entrées du neurone (capteurs de proximité) :
x1 = proxSensor(2)
x2 = proxSensor(4)
Sortie du neurone :
0 → arrêt du robot
1 → avance du robot
Le calcul effectué par le neurone est :
Avec :
x1 : valeur du capteur 2
x2 : valeur du capteur 4
w1 : poids associé au capteur 2
w2 : poids associé au capteur 4
b : biais du neurone
s : somme pondérée
Ensuite, une fonction d’activation à seuil décide de la sortie du neurone :
Le perceptron est entraîné avec quelques exemples :
obstacle proche → stop le robot
espace libre → avancer le robot
-> Si les deux capteurs détectent un espace libre (valeurs élevées), la somme est grande alors le robot avance.
-> Si un obstacle est proche (valeur faible), la somme diminue → le robot MR25 s’arrête.
Le Programme
# Macé Robotics # #!/usr/bin/python3 import MR25 import time
# ----------------------------- # Fonctions du perceptron # -----------------------------
def activation(x): """ Fonction d'activation de type seuil (step function). Retourne 1 si x >= 0 (avancer), sinon 0 (stopper). """ if x >= 0: return 1 return 0
# ----------------------------- # Initialisation des poids et biais # ----------------------------- # w1 : poids associé au capteur 2 (x1) # w2 : poids associé au capteur 4 (x2) # b : biais du perceptron (décalage du seuil de décision) w1 = 0.0 w2 = 0.0 b = 0.0
# Taux d'apprentissage : contrôle la vitesse de correction des poids # Une valeur trop grande peut rendre l'apprentissage instable eta = 0.1
# ----------------------------- # Jeu d'apprentissage (données d'entraînement) # ----------------------------- # # x1 = capteur de proximité 2 (distance normalisée entre 0 et 1) # x2 = capteur de proximité 4 (distance normalisée entre 0 et 1) # # Normalisation : 0 = obstacle très proche, 1 = espace libre # # Sortie attendue : # 0 = STOP (obstacle détecté, distances faibles) # 1 = AVANCE (voie libre, distances élevées) training_data = [ ([0.1, 0.1], 0), # Obstacle très proche sur les deux capteurs → STOP ([0.2, 0.3], 0), # Obstacle proche → STOP ([0.3, 0.2], 0), # Obstacle proche → STOP ([0.8, 0.8], 1), # Espace libre sur les deux capteurs → AVANCE ([0.7, 0.9], 1), # Espace majoritairement libre → AVANCE ([0.9, 0.7], 1), # Espace majoritairement libre → AVANCE ]
# ----------------------------- # Phase d'apprentissage (entraînement du perceptron) # ----------------------------- # On répète jusqu'à 100 époques (passages complets sur le jeu d'entraînement) # L'apprentissage s'arrête prématurément si tous les exemples sont bien classés for epoch in range(100): erreur_totale = 0 # Compteur d'erreurs pour cette époque
for inputs, target in training_data: x1, x2 = inputs
# Calcul de la sortie du perceptron : combinaison linéaire + activation y = activation(w1*x1 + w2*x2 + b) print("Y = ", y)
# Calcul de l'erreur : différence entre la sortie attendue et la sortie calculée erreur = target - y
# Mise à jour des poids selon la règle d'apprentissage du perceptron # Si erreur = 0 : pas de modification # Si erreur = +1 : poids augmentés (le perceptron devait dire 1) # Si erreur = -1 : poids diminués (le perceptron devait dire 0) w1 += eta * erreur * x1 w2 += eta * erreur * x2 b += eta * erreur # Le biais est mis à jour sans facteur d'entrée print("w1, w2 = ", w1, w2)
# Accumulation de l'erreur absolue sur cette époque erreur_totale += abs(erreur) print("Erreur totale = ", erreur_totale)
# Convergence atteinte : tous les exemples sont correctement classés if erreur_totale == 0: break
# Affichage des poids finaux après l'apprentissage print("Apprentissage terminé") print("w1 =", w1) print("w2 =", w2) print("b =", b)
# Pause avant de démarrer le pilotage (laisse le temps de lire les résultats) time.sleep(5)
# ----------------------------- # Pilotage du robot MR25 en temps réel # -----------------------------
# Distance maximale prise en compte (en mm) # Au-delà de SEUIL_MAX, la distance est considérée comme maximale (voie libre) SEUIL_MAX = 80.0 # mm
try: while True: # --- Lecture des capteurs de proximité --- # proxSensor retourne une distance en mm x1 = MR25.proxSensor(2) # Capteur 2 x2 = MR25.proxSensor(4) # Capteur 4
# --- Normalisation des distances dans l'intervalle [0, 1] --- # min(x, SEUIL_MAX) écrête les valeurs supérieures au seuil # La division ramène la valeur entre 0 et 1 x1 = min(x1, SEUIL_MAX) / SEUIL_MAX x2 = min(x2, SEUIL_MAX) / SEUIL_MAX
# --- Inférence : décision du perceptron --- # Utilise les poids appris pour décider d'avancer ou de s'arrêter sortie = activation(w1*x1 + w2*x2 + b)
# --- Exécution de la commande moteur --- if sortie == 1: MR25.forward(40) # Avance à 40% de vitesse print("AVANCE") else: MR25.stop() # Arrêt immédiat print("STOP")
# Fréquence de rafraîchissement : 10 Hz (une décision toutes les 100 ms) time.sleep(0.1)
except KeyboardInterrupt: # Arrêt propre du robot lors d'une interruption clavier (Ctrl+C) MR25.stop()
# end of file
La vidéo
Ce neurone perceptron constitue la base des réseaux de neurones : un réseau plus complexe n’est qu’un assemblage de nombreux perceptrons connectés entre eux.
Les améliorations
Le perceptron à 2 entrées est un bon point de départ, mais pour ce robot il existe plusieurs améliorations possibles. Voici quelques idées :
Ajouter les autres capteurs n°1, 3 et 5
Un neurone à 5 entrées permettrait une meilleure perception :
Ajouter les déplacements comme tourner à droite ou à gauche.
Par exemple :
Si obstacle à droite → Alors tourner à gauche
Si obstacle à gauche → Alors tourner à droite
Si voie libre → Alors avancer
Ajouter la valeur réels des capteurs
L’exemple précédent utilise un simple seul binaire qui fait perdre beaucoup d’information.
On peux faire dépendre la vitesse du robot de la sortie du neurone :
vitesse = int(sortie * 50) MR25.forward(vitesse)
Le robot MR25 ralentit lorsqu’il y a un obstacle.
Utiliser une fonction sigmoide comme fonction d’activation
La fonction d’activation précédent permet d’avoir une sortie binaire.
Avec cette fonction on obtient une probabilité en sortie du neurone :
Les déplacements du robot devient beaucoup plus fluide.
Utiliser un petit réseau de neurone
Au lieu d’utiliser un seul neurone, utiliser :
neurone 1 : obstacle à gauche
neurone 2 : obstacle devant
neurone 3 : obstacle à droite
Puis un neurone de décision choisit l’action :
Avancer
Tourner gauche
Tourner droite
Stop
Pour le MR25 équipé de 5 capteurs de proximité, l’amélioration la plus efficace est généralement : 5 entrées + 3 sorties (gauche, avance, droite) + apprentissage automatique des poids. Cela permet déjà d’obtenir un véritable comportement d’évitement d’obstacles basé sur un réseau neuronal simple.
Prendre une simple photo avec la camera et l’enregistrer dans une image, voic
#!/usr/bin/python
import picamera
camera = picamera.PiCamera()
# initialisation de la resolution
camera.resolution = (1920, 1080)
# capture d'une image
camera.capture('image.jpg')
Simple photo
Lecture d’une image
Un script pour la lecture d’une image avec OpenCv :
#!/usr/bin/python
import picamera
import cv2
camera = picamera.PiCamera()
# initialisation de la resolution
camera.resolution = (100, 100)
# capture d'une image
camera.capture('image.jpg')
image = cv2.read('image.jpg')
print image
Conversion en HSV
Conversion colorimétrique.
HSV : Hue Saturation Value
#!/usr/bin/python
import picamera
import cv2
camera = picamera.PiCamera()
# initialisation de la resolution
camera.resolution = (1920, 1080)
# capture d'une image
camera.capture('image.jpg')
# lecture de l'image
frame = cv2.imread('image.jpg')
# conversion RGB en HSV de l'image
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# enregistrer image hsv
cv2.imwrite('image_hsv.png', hsv)
Ce tutoriel présente l’utilisation du capteur HC-SR04 avec la carte Esus.
Carte Esus + HC-SR04
Le câblage
Les caractéristiques techniques du capteur HC-SR04 sont les suivantes :
Alimentation +5V
Consommation : 15 mA
Gamme de distance : 2 cm à 4 m.
Sortie : TTL
Pins du capteur:
VCC => sur + 5V de la carte Esus
Trig => sur IO2
Echo => sur IO3
GND => GND
Voici le schéma de câblage :
Le capteur ultrason HC-SR04 fonctionne par l’envoi d’une impulsion sur la pin ‘Trig‘, puis un signal impulsionnelle est retourné sur la pin ‘Echo’ en fonction de la distance d’un obstacle. La durée de cette impulsion représente la distance de l’obstacle.
Distance en cm = ( durée de l’impulsion ‘Echo’ en µs ) / 58
Le programme en Arduino
#include <esusBoard.h>// pins #define trig_pin 2#define echo_pin 3void setup() { // init de la carte Esus initEsusBoard(); // init liaison serie Serial.begin (9600); // init pin trig en sortie pinMode(trig_pin, OUTPUT); // init pin echo en entrer pinMode(echo_pin, INPUT); // init pin trig à l'état bas digitalWrite(trig_pin, LOW);}void loop() { long durer;long distanceCm; // mise à l'état haut de la pin trig digitalWrite(trig_pin, HIGH);
// pause de 10µs delayMicroseconds(10);
// mise à l'état bas de la pin trig digitalWrite(trig_pin, LOW);
// mesure de la durée du niveau haut du signal echo durer = pulseIn(echo_pin, HIGH);
// conversion de la durer en cm distanceCm = (durer/58); // envoi sur le port série de la distance Serial.print("distance= "); Serial.print(distanceCm); Serial.println("cm");
// pause 1 seconde delay(1000);}
Le résultat
Attention, pendant le téléchargement la pin IO3 doit être débrancher du capteur, car le téléchargement à besoin de cette pin (RX/IO3).
Ensuite vous pouvez compiler et télécharger le programme dans la carte Esus.
L’application Android
Maintenant, nous allons réaliser l’application Android grâce à App Inventor développé par Google et basée sur une interface graphique similaire à Scratch.