====== Programmation de la voiture ====== Le dossier du projet : {{:isn:2017:projetmariokart:dossiermariokartv3.pdf|}} ===== Présentation ===== La programmation du déplacement de la voiture est gérée sur le principe d'une **architecture client-serveur** : * la partie **serveur** va **recevoir** les requêtes de commandes et activer les moteurs. Cette partie est constituée de **deux programmes Python** sur le Raspberry : * une partie **cliente** depuis laquelle on envoie des requêtes au serveur. Les requêtes sont par exemple faire avancer la voiture, freiner, reculer. Cette partie est gérée avec **une page html associée à un feuille de style CSS**. ==== Principe de la communication ==== * le **client** est un **ordinateur portable** qui se connecte en **wifi** sur le Raspberry. Le Raspberry est configuré comme **point d'accès Wifi** avec le logiciel **RaspAP**. * sur l'ordinateur on lance un **navigateur Internet** et on se connecte au site Web de pilotage de la voiture à l'URL [[http://10.3.141.1]] Sur le Raspberry, un serveur Web est exécuté et pour publier les pages du projet avec comme page principale la page index.html. * cette page **index.html** contient l'interface de pilotage de la voiture et crée le **websocket client** pour se connecter au **websocket serveur** du Raspberry à l' adresse [[ws://10.3.141.1:5678]] * sur le Raspberry, le programme **serveur.py** doit être lancé pour créer le **websocket serveur** et **attendre les requêtes clientes**. En **permanence** le **serveur** envoie la **distance** du premier obstacle situé devant la voiture ainsi que sa **vitesse**. ===== Partie serveur ===== ==== Le fichier Python serveur.py ==== Ce fichier gère le **websocket** et lance deux tâches : * une tâche (**gestion_reception_message(websocket)**) pour **écouter** les messages reçus. Cette tâche lance elle même deux autres tâches en **parallèle** : * une pour gérer le **déplacement** (**gestionmoteur(message)**) avancer/reculer/accelerer/ralentir/frein/arrêt * une pour gérer la **direction** (**gestiondirection(message)**) * une tâche (**gestion_envoi_message(websocket)**) pour **envoyer** des messages vers le client (la page web) afin **d'afficher** la distance et pourquoi pas la vitesse. ==== Le fichier python fonctions.py ==== Ce fichier contient les instructions pour : * **initialiser** le **GPIO** du Raspbery utilisé par la carte SB, * les **fonctions** pour **traiter** les messages reçus. ===== Partie cliente ===== Les fichiers sont dans le dossier **/var/www/isn** du serveur Web Le fichier **index.html** * définit l'**interface en HTML et CSS**, * gère **l'interaction** avec l'utilisateur et le **websocket** en **javascript**. * **création** du websochet client et **connexion** au serveur à l'adresse [[ws://10.3.141.1:5678]] * **affichage** en permanence de la distance et de la vitesse * **affichage** en permanence du **flux vidéo** qui est diffusé par le **logiciel motion** configuré sur le Raspberry à l'adresse [[http://10.3.141.1:8081/video]] * **interaction** avec l'utilisateur : * en utilisant soit les **touches du clavier**, soit **la souris**, * à **chaque action** de l'utilisateur, un **message est envoyé** au serveur pour indiquer ce qu'il faut faire (avancer, reculer, etc.) Le fichier de style **style.css** permet de gérer l'affichage côte à côte de la vidée et des commandes en laissant le **bloc div de la vidéo flotter** à gauche du bloc des commandes. ===== Le fichier serveur.py===== #!/usr/bin/env python3 import asyncio import websockets #import fonctions from fonctions import * fonctions.init() # Gestion de l'envoi des messages du serveur au client async def gestion_envoi_message(websocket): # boucle infinie pour envoyer toutes les secondes un message while True: await websocket.send("distance {} cm ; dc : {} ".format(distance(),dc)) await asyncio.sleep(1) # Gestion des messages recus du client async def gestion_reception_message(websocket): while True: # reception du message d'un client message = await websocket.recv() # lancement de deux taches en parallele # tache qui gere avancer, reculer, acelerer, ralentir, le freinage et l'arret asyncio.get_event_loop().create_task(gestionmoteur(message)) # tache qui gere tourner a droite et a gauche asyncio.get_event_loop().create_task(gestiondirection(message)) #print(message) # fonction lancee a chaque connexion d'un client async def echange(websocket,path): # definir la fonction comme asynchrone #envoyer des messages en parallele envoyer = asyncio.ensure_future(gestion_envoi_message(websocket)) #recevoir message en parallele recevoir = asyncio.ensure_future(gestion_reception_message(websocket)) #gère l'obstacle obstacle = asyncio.ensure_future(gestionobstacle()) termine, attente = await asyncio.wait( [envoyer, recevoir, obstacle], return_when = asyncio.FIRST_COMPLETED, ) # Definir la fonction qui sera appelee par le serveur a la connexion d'un client lancement_serveur = websockets.serve(echange,'10.3.141.1', 5678) # Creation de la boucle d'evenement (event loop) loop = asyncio.get_event_loop() loop.run_until_complete(lancement_serveur) loop.run_forever() loop.close() ===== Le fichier fonctions.py ===== import RPi.GPIO as GPIO import time import fonctions import asyncio GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) moteur1 = {"PWM":17, "Avancer":27, "Reculer":22} moteur2 = {"PWM":25 ,"Gauche": 23, "Droite":24} ultrason={"envoi":5, "echo":6} ultrasonarriere={"envoi":4, "echo":18} global dc, acceleration, moteurPWM, moteurDIRPWM dc=50 acceleration=15 def init(): global dc, acceleration, moteurPWM, moteurDIRPWM GPIO.setup(moteur1["PWM"], GPIO.OUT) GPIO.setup(moteur1["Avancer"], GPIO.OUT) GPIO.setup(moteur1["Reculer"], GPIO.OUT) GPIO.setup(moteur2["PWM"], GPIO.OUT) GPIO.setup(moteur2["Gauche"], GPIO.OUT) GPIO.setup(moteur2["Droite"], GPIO.OUT) GPIO.setup(ultrason["envoi"],GPIO.OUT) GPIO.setup(ultrason["echo"],GPIO.IN) GPIO.setup(ultrasonarriere["envoi"],GPIO.OUT) GPIO.setup(ultrasonarriere["echo"],GPIO.IN) moteurPWM = GPIO.PWM(moteur1["PWM"], 50) moteurPWM.start(0) moteurPWM.ChangeDutyCycle(dc) moteurDIRPWM = GPIO.PWM(moteur2["PWM"], 50) moteurDIRPWM.start(50) def distance(): GPIO.output(ultrason["envoi"], GPIO.HIGH) time.sleep(0.00001) GPIO.output(ultrason["envoi"], GPIO.LOW) start = time.time() while GPIO.input(ultrason["echo"])==0: pass debutImpulsion= time.time() while GPIO.input(ultrason["echo"])==1: pass finImpulsion = time.time() distance = round((finImpulsion - debutImpulsion) * 343*100/2,1) return round(distance) def distancearriere(): GPIO.output(ultrasonarriere["envoi"], GPIO.HIGH) time.sleep(0.00001) GPIO.output(ultrasonarriere["envoi"], GPIO.LOW) start = time.time() while GPIO.input(ultrasonarriere["echo"])==0: pass debutImpulsion= time.time() while GPIO.input(ultrasonarriere["echo"])==1: pass finImpulsion = time.time() distance = round((finImpulsion - debutImpulsion) * 343*100/2,1) return round(distance) async def gestionobstacle () : while True: if distance()<= 50 : print("ALERTE OBSTACLE !") GPIO.output(moteur1["Avancer"],GPIO.LOW) GPIO.output(moteur1["Reculer"],GPIO.LOW) await asyncio.sleep(0.2) async def gestionmoteur(message): global dc, acceleration, moteurPWM, moteurDIRPWM #avancer if message=='Zactive': #augmenter la vitesse de 15 acceleration=15 GPIO.output(moteur1["Avancer"],GPIO.HIGH) GPIO.output(moteur1["Reculer"],GPIO.LOW) if message=='Zdeactive': #baisser la vitesse de 30 acceleration=-30 GPIO.output(moteur1["Avancer"],GPIO.LOW) GPIO.output(moteur1["Reculer"],GPIO.LOW) #reculer if message=='Sactive': #augmenter la vitesse de 10 acceleration=10 GPIO.output(moteur1["Avancer"],GPIO.LOW) GPIO.output(moteur1["Reculer"],GPIO.HIGH) if message=='Sdeactive': #baisser la vitesse de 30 acceleration=-30 GPIO.output(moteur1["Avancer"],GPIO.LOW) GPIO.output(moteur1["Reculer"],GPIO.LOW) #arret if message=='Eactive': GPIO.output(moteur1["Avancer"],GPIO.LOW) GPIO.output(moteur1["Reculer"],GPIO.LOW) #accelerer if message=='Ractive': if dc<=95: dc=dc+5 moteurPWM.ChangeDutyCycle(dc) #ralentir if message=='Factive': if dc>=30: dc=dc-5 moteurPWM.ChangeDutyCycle(dc) async def gestiondirection(message): print("message : ",message) #gauche if message=='Qactive': GPIO.output(moteur2["Gauche"],GPIO.HIGH) time.sleep (0.1) GPIO.output(moteur2["Gauche"],GPIO.LOW) #droite if message=='Dactive': GPIO.output(moteur2["Droite"],GPIO.HIGH) time.sleep (0.1) GPIO.output(moteur2["Droite"],GPIO.LOW) def fin(): moteurPWM.stop() moteurDIRPWM.stop() GPIO.cleanup() ===== Le fichier index.html ===== Projet Mariokart

Projet Mariokart

Vidéo en direct :
Caméra Voiture
Commande :

==== Le code Javascript isolé pour avoir la coloration syntaxique==== ===== Le fichier style.css ===== #camera{ float:left; }