IMA4 2016/2017 P30 : Voiture radiocommandée controlée par gant
Cahier des charges
Présentation générale du projet
Contexte
Les véhicules commandés par une commande radio existent depuis déjà bien longtemps. Cependant, peu de nouveautés au niveau du contrôle ont été apportés. Proposer une télécommande différente et intuitive à utiliser (avec le corps) peut donc s'avérer intéressant et toucher un large public. De plus, le fait d'embarquer un serveur web (sur lequel l'utilisateur trouverait une vidéo filmée en direct par la caméra sur le véhicule) fait intervenir la notion d'objet connecté, qui sont en pleine expansion depuis ces dernières années.
Objectif du projet
Le projet "Voiture radiocommandée contrôlée par gant" consiste à réaliser une télécommande pour piloter une voiture radiocommandée à distance. La commande à créer doit permettre de contrôler les mouvements de la voiture avec le corps. Dans notre cas, la commande sera un gant afin que l'utilisateur puisse faire bouger la voiture avec sa main.
Description du projet
Ce projet propose donc de réaliser entièrement le gant qui contrôlera la voiture, mais également de remplacer la commande déjà existante et par conséquent de contrôler directement les moteurs. Il se découpe donc en trois grandes parties :
- Création du gant et acquisition des données des capteurs.
- Contrôles des servomoteurs.
- Réalisation de la communication entre le contrôleur (gant) et les moteurs.
Choix techniques : matériel et logiciel
Afin de mener à bien le projet, nous aurons besoin du matériel ci-dessous :
- Un gant sur lequel nous collerons un accéléromètre. L'accéléromètre sera utilisé pour faire tourner la voiture à droite ou à gauche, mais également pour avancer et reculer. Lorsque l'utilisateur penchera la main vers l'avant ou vers l'arrière, la voiture avancera ou reculera. Il en sera de même pour faire tourner la voiture. Afin de récupérer et traiter les données issues des capteurs, nous aurons besoin d'une plateforme Arduino Nano accrochée au gant.
- Une voiture radiocommandée sur laquelle seront embarquées une Raspberry Pi. La Raspberry Pi aura pour fonction de recevoir les données envoyées par le premier et de commander les servomoteurs. Elle sera également utilisée pour héberger un serveur Web (qui sera ré-utilisée dans la cadre des TP IMA5). Nous pouvons aussi nous servir de la caméra de RPi pour que l'utilisateur ait une vue depuis sa voiture si il pour bouger d'une pièce à une autre dans son logement par exemple.
- Deux modules Xbee Shield seront montés sur l'Arduino et la Raspberry Pi afin de gérer la liaison sans fil entre la télécommande et la voiture. Le protocole Zigbee permet une transmission de données sur une portée de 30m en intérieur et jusqu'à 100m en extérieur, ce qui semble convenable pour le projet.
Pour réaliser, nous aurons également besoin d'un PC pour configurer les modules (tel que la RPi), mais aussi pour programmer. Pour cela, Arduino IDE devra être installé sur le PC. Enfin, nous pourrons recourir à des librairies supplémentaires en cours de projet si nous en trouvons l'utilité.
Calendrier prévisionnel
Liste des tâches à effectuer
- Partie télécommande (le gant)
- Récupération et traitement des valeurs issues des capteurs avec l'Arduino.
- Création du gant : fixer dessus les flex sensor, l'accéléromètre et l'Arduino.
- Partie embarqué (la voiture)
- Configuration de la Raspberry Pi et installation du serveur web (probablement Apache).
- Installation physique des modules embarqués sur la voiture.
- Contrôle des servomoteurs en fonction des données reçues.
- Communication sans fil
- Installation des modules Xbee sur les Arduinos.
- Prise en main du protocole.
- Tests de portée et d'envoi/réception de données.
- Mise en place de la liaison finale.
Calendrier
- Partie télécommande : 90h
- Partie embarqué : 90h
- Partie communication : 30h
- Coordination entre tout les éléments, tests et validation du prototype : 20h
- Finitions, remplissage du wiki : 10h
Feuille d'heures
Tâche | Prélude | Heures S1 | Heures S2 | Heures S3 | Heures S4 | Heures S5 | Heures S6 | Heures S7 | Heures S8 | Heures S9 | Heures S10 | Total |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Définition cahier des charges | 0 |
Avancement du Projet
Semaine 1
Durant la première séance de la semaine, nous avons revu avec nos enseignants le cahiers des charges initial afin d'affiner et de modifier certains choix. Après avoir vu sur internet que la Raspberry Pi pouvait réaliser une PWM et était compatible avec le module Xbee (choisi pour la transmission de données), nous avons décidé de supprimer l'Arduino prévue sur la voiture pour ne conserver qu'une Raspberry Pi. Cette dernière peut également prendre de la vidéo, comme cela est optionnellement prévu. Pour le gant, l'Arduino Uno étant trop grande, notre choix s'est tourné vers les modèles plus petits : Micro ou Mini.
Pour la deuxième séance, nous avons commencé à utiliser l'Arduino Mini. Cette carte est petite mais n'embarque pas de liaison pour la relier à un PC et y mettre du code. La première chose à faire à été essayer de brancher la carte via un programmateur FTDI suivant le montage suivant :
Après différent essais, nous ne parvenons pas à envoyer un code sur la carte avec Arduino IDE lorsque l'on sélectionne "Arduino Mini /w Atmega328". Lorsque l'on sélectionne "Arduino Micro" cependant, le programme est envoyé dans la carte mais ne fonctionne pas (les pins sont différentes entre les Arduino).
Après quelque recherches, on a vu qu'il était possible d'utiliser une Arduino Nano comme programmateur FTDI :
Les problèmes restent les mêmes qu'avec le FTDI utilisé précédemment. Finalement, la carte sera remplacée par une Arduino Nano, légèrement plus grande mais disposant d'une liaison USB déja intégrée. Si nous avons encore du temps à la fin, nous pourrons ré-essayer d'utiliser une carte plus petite.
Durant la dernière séance de la semaine, nous avons récupéré les composants tels que les modules Xbee, l'Arduino Nano et commencé à tester ces derniers. Nous avons également fait la liste du matériel.
Semaine 2
Pendant la deuxième semaine, avons échangé l'Arduino Micro par une Arduino Nano. La semaine à consisté en des tests sur les modules Xbee avec le logiciel X-CTU. Ce dernier propose une interface graphique et permet de paramétrer les modules, ainsi que de visualiser ce que ces derniers reçoivent. On peut également les faire envoyer des données. Le logiciel se présente sous la forme suivant :
Ici, deux modules sont connectés et via le bouton "console", on peut visualiser/envoyer des données émises/reçues. Une fois le logiciel pris en main, on a décidé de connecter un module Xbee à une Arduino Uno, et enfin à notre Arduino Nano. Le montage et le code associé à la partie "émission des données du capteurs via Arduino" est la suivante :
void setup() { Serial.begin(9600); } void loop() { //lecture des valeurs de l'axe X int val = analogRead(A0); Serial.print(10*val); Serial.print("\n"); delay(10); //lecture des valeurs de l'axe Y int val2 = analogRead(A1); Serial.print(10*val2); Serial.print("\n"); delay(10); }
Dans le montage, le Xbee est alimenté en 3,3V, sa pin Rx est reliée à la pin Tx du l'Arduino, et sa pin Tx est reliée à la pin Rx de l'Arduino. De cette manière, lorsque l'on utilise la liaison série série de l'Arduino, les valeurs envoyées sont transmises au Xbee associé. Comme nous avons mit les deux Xbee sur le même canal, le second Xbee reçoit bien les données du capteur (multipliées par 10 afin d'observer une plus grande différence) :
Semaine 3
Raspberry Pi
Pour ce qui concerne la configuration de la Raspberry Pi nous avons effectué les étapes suivantes:
- Nous avons commencé par monter les partitions de la carte SD pour retrouver le fichier config.txt. Une fois trouvé, nous avons ajouté la ligne de code "enable_uart=1" à la fin du fichier.
- À l'aide de la liaison série et l'outil minicom configuré comme suivant: à 115200 bauds 8N1(8 data bits, pas de bit de parité et 1 bit de stop), sur /dev/ttyUSBn avec n un numéro entier qui désigne le port su lequel la Raspberry est connecté et sans flux de contrôle. Nous avons ensuite pu modifier le fichier /etc/network/interfaces pour définir une addresse IP statique de l'appareil.
auto eth0 iface eth0 inet static address 172.26.78.112 netmask 255.255.240.0 gateway 172.26.79.254
- Tuer tout les processus en lien avec DHCP, grâce à la commande "killall -9 dhcpcd".
- Ajout de l'adresse IP du serveur DNS de l'école pour pouvoir se connecter à internet. À la fin du fichier /etc/resolv.conf nous avons ajouter "nameserver 193.48.57.34".
- Exécuter la commande export "http_proxy=http://proxy.polytech-lille.fr:3128" pour pouvoir utiliser le proxy de l'école.
- Il suffit ensuite d’exécuter la commande "ifup eth0" et se connecter en SSH sur la Raspberry Pi depuis un autre terminal ou un autre ordinateur(cela nous permet de libérer le port série pour autres usages).
Remarque : Les outils DHCP modifie lors de chaque démarrage de eth0 le fichier "/etc/resolv.conf". Pour avoir accès à internet nous sommes obligé donc de démarrer eth0 ("ifup eth0"), modifier le fichier mentionné, faire nos démarches sans faire "ifdown eth0" lors de la session. De plus, il faut refaire l'export à chaque démarrage si on veut télécharger des paquets.
Le mise en route de la Raspberry ayant pris plus de temps que prévu, nous avons juste eu le temps de brancher le module XBee en USB afin de visualiser sur minicom les données reçu par le Xbee. Nous recevons bien les données envoyées par le capteur. Enfin, il a fallu définir une protocole afin de bien comprendre les valeurs reçues : une valeur sur deux correspondra à l'axe X et l'autre à l'axe Y ; les valeurs seront reçues en décimale et chaque valeur aura pour taille 4 chiffres.
Remarque : Les valeurs de l'axe Y seront doublées par rapport à celle de l'axe X afin de bien faire la différence entre les deux.
La prochaine semaine, nous devrons réaliser un programme qui récupère les données arrivant sur le Xbee.
Semaine 4
Pendant tout cette semaine, nous avons travaillé sur la Raspberry Pi. Nous avons paramétré cette dernière en point d'accès, puis nous avons réalisé le programme pour récupérer les valeurs de la liaison série.
La méthode ci-dessus pour que le fichier "/etc/resolv.conf" ne se vide pas à chaque démarrage n'étant pas optimale (tuer dhcpcd...), notre tuteur nous a supprimé SystemD et a installé à la place SysV qui permet plus de "libertés".
Raspberry Pi en point d'accès
- Définir une adresse IP statique pour la carte
Dans le fichier /etc/network/interfaces, il faut ajouter :
allow-hotplug wlan0 iface wlan0 inet static address 192.168.100.1 netmask 255.255.255.0
L'adresse de notre Rpi sur son réseau sera donc 192.168.100.1
- Installation du serveur DHCP
Le serveur DHCP utilisé est isc-dhcp-server. On se rend ensuite dans le fichier de configuration du DHCP, à savoir /etc/dhcp/dhcpd.conf, afin d'ajouter une plage d'adresse qui pourra être distribuée :
subnet 192.168.100.0 netmask 255.255.255.0 { range 192.168.100.10 192.168.100.50; option broadcast-address 192.168.100.255; option routers 192.168.100.1; }
Enfin, dans le fichier /etc/default/isc-dhcp-server on ajoute wlan0 comme interface par défaut pour répondre aux requêtes DHCP.
- Création du point d'accès
Après avoir lu des articles et tutoriel sur le sujet, nous avons décidé d'installer "Hostapd", qui est un programme tournant en arrière plan afin de créer le point d'accès Wi-Fi. Une fois installé, il faut créer son fichier de configuration : /etc/hostapd/hostapd.conf dans lequel on met : (voir https://wiki.gentoo.org/wiki/Hostapd)
interface=wlan0 #interface utilisée ssid=gosse_hagui #nom du point d'accès hw_mode=g channel=6 #canal à utiliser wpa=2 wpa_passphrase=raspberry #mot de passe du point d'accès wpa_key_mgmt=WPA-PSK rsn_pairwise=CCMP
Il ne reste qu'a dire à Hostapd d'utiliser ce fichier en tant que fichier de configuration en ajout dans /etc/default/hostapd :
DAEMON_CONF="/etc/hostapd/hostapd.conf"
A partir de ce moment, la Raspberry est détectable en tant que point d'accès Wi-Fi par les machines dans le périmètre ; nous arrivons a nous connecter dessus et ouvrir dans un navigateur la page html de base d'Apache.
Récupération des données
Le code suivant en C, sur la Rpi, permet d'afficher sur l'écran les valeurs du capteur envoyées par l'Arduino :
#include <stdio.h> #include <stdlib.h> int main(void) { FILE* fichier = NULL; int val=0; fichier=fopen("/dev/ttyUSB0","r"); if(fichier == NULL) { perror("ouverture fichier:"); exit(1); } while(1) { fscanf(fichier,"%d",&val); printf("%d\n",val); } return 0; }
Sur la Rpi, on reçoit bien :
3xxx correspondent aux valeur de l'axe X et 7xxx à l'axe Y. La prochaine étape de projet va être de contrôler les pins de sortie de la Raspberry en modifiant le programme ci-dessus.
Semaine 5
L'envoi de données entre les deux entités sur projet étant réussi, nous avons décidé de réaliser les PWM sur la Rpi qui seront nécessaire au contrôle des moteurs. Dans un premier temps, nous avons vu des exemples en Python et cela fonctionnait. Cependant, après quelque recherche, nous avons trouvé une librairie qui s'appelle Wiring-Pi et qui permet de contrôler les GPIO (de la Rpi) grâce au langage C. Le code simple suivant permet donc de réaliser une PWM sur la pin 1 (GPIO 18) :
#include <stdio.h> #include <wiringPi.h> #include <softPwm.h> int main(void) { int pin =1; int i=0; if(wiringPiSetup()==-1) return 0; pinMode(pin,PWM_OUTPUT); while(1) { for(i=0;i<1024;i++) { pwmWrite(1,i); delay(1); } for(i=1023;i>=0;i--) { pwmWrite(1,i); delay(1); } } return 0; }
Nous avons câblé une LED afin de vérifier le bon fonctionnement de la PWM. Dans la deuxième partie de la semaine, nous avons décidé d'inclure la PWM au code qui reçoit les valeurs du capteur et commander l'allumage de notre LED en fonction de ce dernier :
while(1) { fscanf(fichier,"%d",&val); printf("%d\n",val); pwmWrite(1,val); delay(1); }
Un des problème que nous avons rencontré est que la librairie actuelle n'autorise qu'une seule PWM sur la pin 1. La prochaine étape du projet est de trouver comment réaliser une seconde PWM, et également d'améliorer le code pour que les PWM soient plus "lisses".
Semaine 6
Durant cette semaine nous avons continué de travailler sur la création de deux PWM par la RaspBerry Pi. Nous avons trouvé une librairie associé à WiringPi appelée softPwm qui inclus des fonctions nous permettant de faire ceci. Nous allons notamment utiliser les deux fonctions :
int softPwmCreate (int pin, int initialValue, int pwmRange);
Pour autoriser une pin à faire une PWM avec une valeur initiale donnée par le deuxième paramètre, et le troisième paramètre définit la plage de valeurs à envoyer. Cette fonction nous renvoi un 0 si tout s'est bien passé, tout autre valeur signifie une erreur. Ainsi que
void softPwmWrite (int pin, int value);
Qui va nous permettre de modifier la valeur sur la pin spécifié. La valeur doit respecter la plage précisée précédemment par la fonction softPwmCreate, sinon elle sera ignoré.
Nous avons intégré ces deux fonction dans notre code pour avoir une version qui ressemble à ceci.
#include <stdio.h> #include <stdlib.h> #include <wiringPi.h> #include <softPwm.h> #include <unistd.h> int main(void) { FILE* fichier = NULL; int val=0; if(wiringPiSetupGpio()==-1) return 0; fichier=fopen("/dev/ttyUSB0","r"); if(fichier == NULL){ perror("ouverture fichier:"); exit(1); } if(softPwmCreate(21,0,100)!=0){ perror("softPwmCreate"); exit(1); } if(softPwmCreate(20,0,100)!=0){ perror("softPwmCreate"); exit(1); } while(1){ fscanf(fichier,"%d",&val); printf("%d\n",val); //Notre programme reçoit 0(repos) ou 100(non-repos) pour l'axe X et 10(repos) ou 90(non-repos) pour l'axe Y. if(val==0 || val ==100){ softPwmWrite(20,val); } else{ //Si on reçoit 10 ou 90 pour l'axe Y on envoi 0 et 100 respectivement sur le pin. if(val==10){ softPwmWrite(21,0); } else{ softPwmWrite(21,100); } } } return 0; }
Notre programme contrôle ainsi deux LEDs. Une LED Rouge qui s'allume quand l’accéléromètre n'est pas en position de repos par rapport à l'axe Y et une LED Jaune qui s'allume par rapport à l'axe X.
Nous avons également créé un Makefile pour faciliter la compilation de notre code lors de sa modification.
CC = gcc CFLAGS = -Wall -lpthread -lwiringPi all : prog_controle_v2 prog_controle_v2 : controle_v2.c $(CC) controle_v2.c -o prog_controle_v2 $(CFLAGS) clean : rm prog_*