Table des matières

Programmation de la voiture

Le dossier du projet : dossiermariokartv3.pdf

Présentation

La programmation du déplacement de la voiture est gérée sur le principe d'une architecture client-serveur :

Principe de la communication

Partie serveur

Le fichier Python serveur.py

Ce fichier gère le websocket et lance deux tâches :

Le fichier python fonctions.py

Ce fichier contient les instructions pour :

Partie cliente

Les fichiers sont dans le dossier /var/www/isn du serveur Web

Le fichier index.html

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

<!DOCTYPE html>
<html>
 <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
	<title>
	Projet Mariokart
	</title>
 </head>
 <body>
  <h1>Projet Mariokart</h1>
  <div id="camera">
   <fieldset>
   <legend>Vidéo en direct :</legend>
    <section class="streaming">
      <img name="PiCamera" src="http://10.3.141.1:8081/video" 
      width="520" height="'440" alt="Caméra Voiture" 
      style="background-color: #009999" />
    </section>
   </fieldset>
  </div>
  <fieldset>
  <legend>Commande :</legend>
  <div id="codage">
    <input type="button" name="flecheAvancer" id="flecheAvancer" value="Z" />
	<label for="flecheAvancer">Avancer</label><br />
    <input type="button" name="flecheReculer" id="flecheReculer" value="S" /> 
	<label for="flecheReculer">Reculer<label><br />
    <input type="button" name="flecheDroite" id="flecheDroite" value="D" /> 
	<label for="flecheDroite">Droite</label><br />
    <input type="button" name="flecheGauche" id="flecheGauche" value="Q" /> 
	<label for="flecheGauche">Gauche</label><br />
    <input type="button" name="frein" id="frein" value="E" /> 
	<label for="frein">Frein</label><br /> 
    <input type="button" name="frein" id="accelerer" value="R" /> 
	<label for="accelerer">Accelérer</label><br />  
    <input type="button" name="ralentir" id="ralentir" value="F" /> 
	<label for="ralentir">Ralentir</label><br />  	
    <span id="messagerecu">Message</span><br />
  </div>
  </fieldset>
  <script>
   // Gestion de la souris
   //  * ajout des gestionnaire d'evenement sur les boutons activés par la souris
   //  * au clic gauche de la souris
   //  * et au relachement du bouton gauche de la souris
   var flecheAvancer = document.getElementById('flecheAvancer');
   flecheAvancer.addEventListener('mousedown',clicSouris);
   flecheAvancer.addEventListener('mouseup',relacheSouris);
   var flecheReculer = document.getElementById('flecheReculer');
   flecheReculer.addEventListener('mousedown',clicSouris);
   flecheReculer.addEventListener('mouseup',relacheSouris);
   var flecheDroite = document.getElementById('flecheDroite');
   flecheDroite.addEventListener('mousedown',clicSouris);
   flecheDroite.addEventListener('mouseup',relacheSouris);
   var flecheGauche = document.getElementById('flecheGauche');
   flecheGauche.addEventListener('mousedown',clicSouris);
   flecheGauche.addEventListener('mouseup',relacheSouris);
   var frein = document.getElementById('frein');
   frein.addEventListener('mousedown',clicSouris);
   frein.addEventListener('mouseup',relacheSouris);
   var accelerer = document.getElementById('accelerer');
   accelerer.addEventListener('mousedown',clicSouris);
   accelerer.addEventListener('mouseup',relacheSouris);
   var ralentir = document.getElementById('ralentir');
   ralentir.addEventListener('mousedown',clicSouris);
   ralentir.addEventListener('mouseup',relacheSouris);
 
   // gestion du clic gauche de la souris
   // envoi d'un message contenant : le nom de la touche et le mot active
   function clicSouris(event) {
    //alert(event.target.value +'active');
	ws.send(event.target.value +'active');
   }
 
   // gestion du relachemement du bouton gauche de la souris : sert avancer/reculer ; droite/gauche
   // envoi d'un message contenant : le nom de la touche et le mot deactive
   function relacheSouris(event) {
    //alert(event.target.value +'deactive');
	ws.send(event.target.value +'deactive');
   }
 
 //ajout des gestionnaotes d'evenemebt pour le clavier
  document.addEventListener('keydown', toucheAppuyee);
  document.addEventListener('keyup', toucheRelachee );
 
  // Gestion du clavier
  // Gestion de l'appui d'une touche 
  // envoi d'un message contenant : le nom de la touche et le mot active 
  function toucheAppuyee(event) {
    // touche Z appuyée code 90 -> avancer
    if(event.keyCode == '90') {
       ws.send('Z' + 'active');
    }
    // touche S appuyée code 83 -> reculer
    if(event.keyCode == '83') {
       ws.send('S' + 'active');
    }
    // touche Q appuyée code 81 -> tourner a gauche
    if(event.keyCode == '81') {
       ws.send('Q' + 'active');
    }
    // touche D appuyée code 68 -> tourner a droite
    if(event.keyCode == '68') {
       ws.send('D' + 'active');
    }
    // touche E appuyée code 69 -> freiner
    if(event.keyCode == '69') {
       ws.send('E' + 'active');
    }	
    // touche R appuyée code 82 -> accelerer
    if(event.keyCode == '82') {
       ws.send('R' + 'active');
    }	
    // touche F appuyée code 70 -> ralentir
    if(event.keyCode == '70') {
       ws.send('F' + 'active');
    }	
  }
 
  // Gestion du relachement d'une touche uniquement pour 
  //  * avancer et reculer
  //  * droite et gauche pour ramener les roues au centre
  // et envoi d'un message contenant : le nom de la touche et le mot deactive
  function toucheRelachee(event) {
    // touche Z relachée code 90 -> desactiver avancer
    if(event.keyCode == '90') {
       ws.send('Z' + 'deactive');
    }
    // touche S relachée code 83 -> desactiver reculer
    if(event.keyCode == '83') {
       ws.send('S' + 'deactive');
    }
    // touche Q relachée code 81 -> tourner a gauche
    if(event.keyCode == '81') {
       ws.send('Q' + 'deactive');
    }
    // touche D relachée code 68 -> tourner a droite
    if(event.keyCode == '68') {
       ws.send('D' + 'deactive');
    }
  }
 
  // Création du Websocket client vers le serveur du Raspberry Pi
  var ws = new WebSocket("ws://10.3.141.1:5678/");
  // envoi d'un premier message lors de la connexion au serveur
   ws.onopen = function (event) {
      ws.send("J'envoie un premier message au serveur."); 
  };
  var messagerecu = document.getElementById('messagerecu');
  ws.onmessage = function (event) {
     //affiche le message recu dans la balise <span>
     messagerecu.innerHTML = event.data;  
  };
 </script>
 </body>
</html>

Le code Javascript isolé pour avoir la coloration syntaxique

  <script>
   // Gestion de la souris
   //  * ajout des gestionnaire d'evenement sur les boutons activés par la souris
   //  * au clic gauche de la souris
   //  * et au relachement du bouton gauche de la souris
   var flecheAvancer = document.getElementById('flecheAvancer');
   flecheAvancer.addEventListener('mousedown',clicSouris);
   flecheAvancer.addEventListener('mouseup',relacheSouris);
   var flecheReculer = document.getElementById('flecheReculer');
   flecheReculer.addEventListener('mousedown',clicSouris);
   flecheReculer.addEventListener('mouseup',relacheSouris);
   var flecheDroite = document.getElementById('flecheDroite');
   flecheDroite.addEventListener('mousedown',clicSouris);
   flecheDroite.addEventListener('mouseup',relacheSouris);
   var flecheGauche = document.getElementById('flecheGauche');
   flecheGauche.addEventListener('mousedown',clicSouris);
   flecheGauche.addEventListener('mouseup',relacheSouris);
   var frein = document.getElementById('frein');
   frein.addEventListener('mousedown',clicSouris);
   frein.addEventListener('mouseup',relacheSouris);
   var accelerer = document.getElementById('accelerer');
   accelerer.addEventListener('mousedown',clicSouris);
   accelerer.addEventListener('mouseup',relacheSouris);
   var ralentir = document.getElementById('ralentir');
   ralentir.addEventListener('mousedown',clicSouris);
   ralentir.addEventListener('mouseup',relacheSouris);
 
   // gestion du clic gauche de la souris
   // envoi d'un message contenant : le nom de la touche et le mot active
   function clicSouris(event) {
    //alert(event.target.value +'active');
	ws.send(event.target.value +'active');
   }
 
   // gestion du relachemement du bouton gauche de la souris : sert avancer/reculer ; droite/gauche
   // envoi d'un message contenant : le nom de la touche et le mot deactive
   function relacheSouris(event) {
    //alert(event.target.value +'deactive');
	ws.send(event.target.value +'deactive');
   }
 
 //ajout des gestionnaotes d'evenemebt pour le clavier
  document.addEventListener('keydown', toucheAppuyee);
  document.addEventListener('keyup', toucheRelachee );
 
  // Gestion du clavier
  // Gestion de l'appui d'une touche 
  // envoi d'un message contenant : le nom de la touche et le mot active 
  function toucheAppuyee(event) {
    // touche Z appuyée code 90 -> avancer
    if(event.keyCode == '90') {
       ws.send('Z' + 'active');
    }
    // touche S appuyée code 83 -> reculer
    if(event.keyCode == '83') {
       ws.send('S' + 'active');
    }
    // touche Q appuyée code 81 -> tourner a gauche
    if(event.keyCode == '81') {
       ws.send('Q' + 'active');
    }
    // touche D appuyée code 68 -> tourner a droite
    if(event.keyCode == '68') {
       ws.send('D' + 'active');
    }
    // touche E appuyée code 69 -> freiner
    if(event.keyCode == '69') {
       ws.send('E' + 'active');
    }	
    // touche R appuyée code 82 -> accelerer
    if(event.keyCode == '82') {
       ws.send('R' + 'active');
    }	
    // touche F appuyée code 70 -> ralentir
    if(event.keyCode == '70') {
       ws.send('F' + 'active');
    }	
  }
 
  // Gestion du relachement d'une touche uniquement pour 
  //  * avancer et reculer
  //  * droite et gauche pour ramener les roues au centre
  // et envoi d'un message contenant : le nom de la touche et le mot deactive
  function toucheRelachee(event) {
    // touche Z relachée code 90 -> desactiver avancer
    if(event.keyCode == '90') {
       ws.send('Z' + 'deactive');
    }
    // touche S relachée code 83 -> desactiver reculer
    if(event.keyCode == '83') {
       ws.send('S' + 'deactive');
    }
    // touche Q relachée code 81 -> tourner a gauche
    if(event.keyCode == '81') {
       ws.send('Q' + 'deactive');
    }
    // touche D relachée code 68 -> tourner a droite
    if(event.keyCode == '68') {
       ws.send('D' + 'deactive');
    }
  }
 
  // Création du Websocket client vers le serveur du Raspberry Pi
  var ws = new WebSocket("ws://10.3.141.1:5678/");
  // envoi d'un premier message lors de la connexion au serveur
   ws.onopen = function (event) {
      ws.send("J'envoie un premier message au serveur."); 
  };
  var messagerecu = document.getElementById('messagerecu');
  ws.onmessage = function (event) {
     //affiche le message recu dans la balise <span>
     messagerecu.innerHTML = event.data;  
  };
 </script>

Le fichier style.css

#camera{
float:left;
}