IMA5 2018/2019 P21 : Pilotage automatique d'un drone
Présentation générale
Description
Pour réaliser ce projet, nous avons à notre disposition le drone Parrot Bebop 2. Nous ferons donc initialement des essais de vols du drone de manière à le maitriser à l'aide de la manette de pilotage ou alors tout simplement de notre smartphone avec l'application FreeFlight Pro.
Notre projet portera principalement sur l'aspect détection d'objets et analyse de l'environnement en utilisant le traitement d'images. Le développement des applications pourra se faire sur MATLAB en passant par ROS (Robot Operating System) sous Linux, et/ou via une interface homme machine implémenté sur Python.
Une démarche de prospection et recherche bibliographique doit être nécessairement entreprise sur tous ces outils en début de projet.
Pour mener au mieux ce projet, nous avons mis en place une réunion hebdomadaire avec nos encadrants, où nous leur présenterons nos avancées, et choisirons par la suite ce qu'il y a à faire.
Nous travaillerons la plupart du temps en C006 ou C008, mais aussi en C305, car la salle sera équipée prochainement de filet afin de manipuler et faire des tests avec le drone en tout sécurité.
Objectifs
L'objectif de ce projet est double :
- Analyse de l'environnement à l'aide des différents capteurs disponibles, notamment le capteur image
- Pilotage automatique du drone développé spécifiquement par les étudiants
Préparation du projet
Choix techniques : matériel et logiciel
• Drone Parrot Bebop 2
• Environnement Linux
• ROS : Robot Operating System
• Parrot SDK
• Matlab Simulink avec en particulier, la boîte à outils Robotics System
• Python pour créer une IHM (Interface Homme/Machine)
Liste des tâches à effectuer
- Découverte, prise en main et pilotage du drone
- Recherche/Documentation sur les différents composants du drone
- Réalisation d'un schéma bloc pour identifier les différents organes du drone et connaitre les différentes interactions qu'il peut y avoir entre eux
- Recherche/Documentation pour définir quels outils nous allons choisir
- Stabilisation du drone
- Gérer le traitement d'images
- Réalisation d'un rapport final exploitable afin que les personnes souhaitant travailler sur ce projet puissent ajouter eux même ce qu'ils souhaitent, et mettre en avant leurs connaissances et compétences
Réalisation du Projet
Semaine 1
Durant la première semaine, nous nous sommes entretenues avec nos encadrants de manière à mieux définir le contexte du projet et leurs attentes quant à ce projet. Nous avons ensuite effectuer des recherches générales sur le drone pour connaître ses capacités et être plus familier avec les différents capteurs qu'il possède ainsi que des recherches un peu plus approfondies sur les différentes technologies utilisables avec le drone pour préparer le développement futur.
Cette première semaine a aussi été l'occasion de commencer à prendre en main le drone en effectuant des premiers vols en utilisant l'application Android proposée par Parrot.
Recherches sur le drone :
Inventaire des composants/capteurs
•Une caméra verticale pour la stabilisation du drone qui prend une photo du sol toutes les 16 millisecondes et la compare avec la précédente pour déterminer la vitesse du drone.
• Un capteur ultrason qui analyse l'altitude de vol jusqu'à 5 mètres.
• Un capteur de pression qui mesure la pression de l'air et analyse l'altitude de vol au-delà de 5 mètres.
• Un gyroscope 3 axes qui mesure l'angle d'inclinaison du drone.
• Un accéléromètre qui mesure la position du drone sur 3 axes et sa vitesse linéaire.
• Un magnétomètre 3 axes qui aide à définir la position d'un drone comme une boussole.
• Un système mondial de navigation par satellite qui combine un GPS et un GLONASS (système de positionnement par satellite d'origine soviétique) pour la géolocalisation du drone et aide à mesurer la vitesse de drone pour plus de stabilité à haute altitude.
Boîte à outils MATLAB
Nous allons devoir utiliser MATLAB dans le cadre de ce projet pour communiquer avec ROS, mais nous ne savions pas s'il fallait une boîte à outils en particulier ou non. Après plusieurs recherches nous avons trouvé qu'il ne fallait pas de boîte à outils propre au drone à notre disposition. De ce fait, la seule boîte à outils dont nous aurons besoin est : Robotics System Toolbox™
Celle-ci fournit une interface entre MATLAB et Simulink et le système d'exploitation robotique (ROS) qui vous permet de communiquer avec un réseau ROS, d'explorer de manière interactive les capacités des robots et de visualiser les données des capteurs. Il permet de développer, tester et vérifier des algorithmes et applications robotiques. Robotics System Toolbox permet également de créer un réseau ROS autonome directement dans MATLAB et Simulink et importer des fichiers journaux ROS (rosbags) pour visualiser, analyser et post-traiter les données consignées. Ces fonctionnalités vont nous permettre de développer des algorithmes de robotique dans MATLAB et Simulink, tout en nous permettant d'échanger des messages avec d'autres nœuds du réseau ROS. Avec Embedded Coder, nous allons pouvoir générer du code C ++ à partir d'un modèle Simulink pour une application ROS autonome pouvant s'exécuter sur toute plate-forme Linux sur laquelle ROS est installé.
Semaine 2
Lors de la deuxième semaine, nous avons effectué des premiers tests de connexion du drone à un ordinateur. Pour cela, nous avons utilisé le SDK fourni par Parrot ARDroneSDK3 qui possède un code d'exemple permettant de contrôler le Bebop2. Une fois l'ordinateur connecté au wifi du drone, on peut exécuter le programme d'exemple pour faire décoller le drone.
Tutoriel d'utilisation de SDK : Fichier:Parrot pour développeurs.pdf
Une fois cette partie réalisée, nous intéressons à la manière de connecter le drone à Matlab. Pour cela nous avons besoin d'utiliser ROS qui est un ensemble d'outils informatique permettant de réaliser des logiciels pour la robotique. ROS permet de faire la passerelle entre Matlab et le drone.
SDK
Le SDK offre une couche d'abstraction sur le bas niveau du drone ainsi peu d'informations issues des capteurs et des actionneurs sont accessibles et il n'est pas possible de piloter directement les moteurs. Néanmoins, Parrot fournit une grande quantité de messages permettant de réaliser des actions complexes (déplacement d'un point A à un point B, retour à la maison, ...) ou des figures acrobatiques (parabole, flip, ...).
Liste des entrées accessibles :
• latitude (double): Position en latitude (en degré)
• longitude (double): Position en longitude (en degré)
• altitude (double): Altitude (en mètre)
• speedX (float): Vitesse relative au nord (en m/s)
• speedY (float): Vitesse relative à l'est (en m/s)
• speedZ (float): Vitesse selon l'axe z (en m/s)
• longitude_accuracy (i8): Erreur de localisation en longitude (en mètre)
• latitude_accuracy (i8): Erreur de localisation en latitude (en mètre)
• altitude_accuracy (i8): Erreur de localisation pour l'altitude (en mètre)
• picture (caméra avant)
• Etat de la batterie : Battery state => percent (u8): Pourcentage de batterie
Liste des commandes offertes par le SDK :
• flip: Le drone fait un flip
• horizontal_panorama: Le drone tourne horizontalement sur lui-même
• dronie: Le drone vole sur une distance donnée avec un angle calculé
• horizontal_reveal: Le drone commence à regarder vers le bas, puis avance tout en regardant lentement à l'horizon
• vertical_reveal: Le drone commence à regarder vers le bas, puis se déplace vers le haut tout en regardant lentement à l'horizon
• parabola: Le drone fait une parabole au-dessus de sa cible et se termine de l'autre côté de celui-ci.
• candle: Le drone vole horizontalement en direction de la cible puis s'envole.
• take off : Décollage
• land : Atterrissage
Le détail sur tous les états et toutes les commandes se trouve ici : https://developer.parrot.com/docs/reference/bebop_2/index.html#bebop-2-
Semaine 3
Pour cette semaine, nous venons d'avoir l'accès au git d'un groupe de chercheurs travaillant également sur le bebop 2, ce qui nous permet d'avoir des informations supplémentaires sur le drone, notamment de la documentation.
Sur ce git, il y a notamment un prototype d'une IHM (Interface Homme/Machine) que nous allons étudier afin de trouver comment nous pouvons récupérer les données du drone en temps réel. Malheureusement, après étude de l'IHM, nous nous sommes rendus compte que la personne n'avait réaliser que l'interface "visuelle" et donc, là où nous pensions trouver des commandes pour récupérer des données, ce n'est qu'en réalité une zone de texte, où la personne a mis des valeurs aléatoires.
Nous sommes donc toujours à la recherche de commandes afin de récupérer des données, notamment via SDK.
ROS
De plus, nous avons profité de cette semaine pour nous intéresser à un autre moyen de communiquer avec le drone: ROS. ROS est une plateforme de développement logicielle pour les robots qui est compatible avec les drones Bebop 2 de Parrot. Nous voulons utiliser cette plateforme pour faire la passerelle wifi entre le drone et Matlab ou un autre langage de programmation.
La prise en main de ROS n'est pas forcément très évidente ce pourquoi nous avons commencé à suivre les tutoriels pour débutant proposés sur le site de ROS.
Qu’est ce que ROS?
ROS (Robot Operating System) est un système open source permettant à un utilisateur de contrôler un robot à partir d’un PC. Il n’a pas de langage de programmation défini et peut être programmé via plusieurs langages. Un système ROS comprend un certain nombres de noeuds indépendants qui communiquent avec les autres noeuds via une messagerie de type publication/abonnement. Par exemple, le driver d’un capteur peut être implémenté comme un noeud qui publie les valeurs du capteur dans un flux de messages. Ces messages pourraient être utilisés par n'importe quel nombre d’autres noeuds, comme des filtres, des collecteurs de données ou des systèmes haut niveau comme des systèmes de guidage ou de recherche de chemin.
Pourquoi utiliser ROS?
Sans ROS, lire/écrire des ressources rapidement est compliqué avec un grand système multi-threadé. Il y a d’autre moyens de résoudre ce problème mais ROS simplifie le processus en s’assurant que les différents threads n’essaient pas de lire ou d’écrire des ressources partagées mais plutôt qu’il publie et s’abonnent à des messages.
ROS nous permettra d'accéder facilement et rapidement aux valeurs des différents capteurs du drone que ce soit directement avec ROS ou en passant par des programmes en Python ou C++ pour pouvoir traiter les données. Et de contrôler le drone de la même manière.
Comment fonctionne ROS?
Le calcul dans ROS est effectué à l'aide d'un réseau de processus appelé nœuds ROS. Ce réseau d'exécution peut être appelé graphe de calcul. Les principaux concepts du graphe sont les nodes ROS, le master, le parameter server, les messages, les topics, les services et les bags. Chaque concept du graphique contribue au réseau d'une manière qui lui est propre.
Nodes: Les nodes sont les processus qui effectuent les calculs. Chaque node ROS est écrit à l'aide d'une bibliothèque pour ROS tel que roscpp (pour le C++) et rospy (pour le python). Dans un robot, il peut y avoir plusieurs nodes pour exécuter différents types de tâches. En utilisant la méthode de communication de ROS, les nodes peuvent communiquer entre eux et échanger des données. Un des buts des nodes ROS est de découper un grand processus avec toutes les fonctionnalités en plusieurs petit processus avec une fonctionnalité.
Master: Le Master ROS fournit un registre des noms des nodes activés et surveille les changements au niveau des nodes. Les nodes ne sont pas capables de trouver les autres nodes, d'échanger des messages ou d'invoquer des services sans Master.
Parameter Server: Le parameter server permet à l'utilisateur de sauvegarder les données dans un endroit centralisé. Tous les nodes peuvent accéder à ses valeurs et les modifier.
Messages: Les nodes communiquent entre eux en utilisant des messages. Les messages sont de simples structures de données contenant des champs typés qui peuvent contenir un ensemble de données qui peuvent être envoyé à un autre node. Il y a des types standards de données (integer, floating point, boolean ...) et ces types sont supportés par les messages ROS. Il est aussi possible de créer des types personnalisés de messages à partir de ces types.
Topics: Chaque message dans ROS est transporté à l'aide de bus nommé appelé topics. Quand un node envoie un message au travers d'un topic, on dit qu'il publie (publish) un topic. Quand un node reçoit un message au travers d'un topic, on dit qu'il s'abonne (subscribe) à un topic. Le node qui envoie et celui qui s'abonne ne sont pas au courant de l’existence l'un de l'autre. Chaque topic à un nom unique et n'importe quel node y a accès et peut envoyer des données au travers de ce dernier tant qu'ils utilisent le bon type de message.
Services: Pour certains cas d’application, un modèle de communication publish/subscribe n'est pas suffisant il est alors nécessaire d'utiliser une interaction requête/réponse. Les services ROS permettent de mettre en place une interaction bi-directionnelle de type requête/réponse. On peut définir un service comme contenant deux parties: une pour la requête et l'autre pour la réponse. En utilisant les services ROS, il est possible de créer un node serveur et un node client. Le node serveur fournit le service sous un nom et le client envoie des message de requêtes à ce serveur qui répondra en envoyant le résultat au client.
Bags: Le Bag est un format de fichier permettant de sauvegarder et rejouer les données des messages ROS. Le fichier Bag est un mécanisme important pour la sauvegarde de données telles que les données provenant des capteurs qui peuvent être difficiles à acquérir mais qui sont nécessaires pour développer et tester les algorithmes.
source: Mastering ROS for Robotics Proramming de Lentin Joseph
Le schéma suivant décrit le comportement de communication d'un Master et de deux nodes sous ROS.
Semaine 4
Au fil de nos recherches, nous avons pu remarquer que les technologies que nous souhaitons utiliser sont assez différentes. Nous avons d'un côté ROS, qui est très ouvert, où l'on peut trouver beaucoup d'informations, mais ça reste néanmoins un outil très généraliste dans la robotique.
Alors que SDK de son côté, c'est un peu l'inverse, c'est-à-dire qu'il est très fermé, car nous avons du mal à trouver certaines informations, mais pour autant, c'est l'outil le plus proche du drone.
Cette semaine, nous avons donc focalisé nos recherches sur la récupération de données via SDK, car pour l'instant il n'y a qu'avec cet outil que nous avons réussi à le manipuler (hors smartphone et télécommande).
ROS
De plus, nous avons continué notre exploration de ROS en recherchant comment interagir avec cet outil avec un langage de programmation comme le Python par exemple.
Pourquoi utiliser un langage de programmation avec ROS?
Comme on l'a vu la semaine précédente, on utilise ROS avec des langages de programmation pour pouvoir programmer les comportements des différents nodes du réseau. Il y a trois langages de programmation officiel pour ROS:
Python avec la bibliothèque rospy qui favorise la vitesse d’implémentation plutôt que la performance d’exécution de manière à ce que les algorithmes puissent être prototypés et testés rapidement sous ROS.
C++ avec la bibliothèque roscpp qui permet aux programmeurs de rapidement interfacer avec les topics, services et parameters en C++. roscpp a été réalisé pour être une bibliothèque ROS très performante.
Lisp avec la bibliothèque roslisp qui permet d’écrire des nodes ROS en Common Lisp. La bibliothèque permet de réaliser des nodes rapidement et facilement et permet un débogage interactif.
Comment ça fonctionne?
Ainsi, il existe deux moyens de communication au sein d'un réseau ROS : un fonctionnement publisher/subsciber au travers de topics et un fonctionnement Service/Client. Nous avons donc la possibilité de créer 4 types de nodes pour décrire les différentes actions nécessaires au fonctionnement du système. Il sera possible, par exemple, de programmer un node pour qu'il récupère les données du capteur d'altitude qui publiera les valeurs sur le réseau et de créer des nodes pour les différents moteurs du drone qui s'abonneront à cette publication pour pouvoir réguler l'altitude du drone.
Exemple de Python:
Nous avons donc décidé d’explorer la programmation de nodes avec Python car c’est un langage assez simple à utiliser et proposant de nombreux outils pour le traitement d’images qui nous seront utiles pour la suite du projet.
Dans un premier temps, nous avons réalisé un node publisher qui enverra un message en continu sur un topic et un node subscriber qui s'abonnera à ce topic pour écouter le message publié.
Publisher:
Initialisation : dans un premier temps, il est nécessaire d'initialiser le node en mode publisher. Pour cela, la fonction nécessite le nom du topic sur lequel les messages vont être envoyés, le type du message ainsi que la taille de la queue pour les messages envoyés.
pub = rospy.Publisher(topic_name, msg_class, queue_size)
Envoi d'un message: une fois le publisher correctement initialisé il est possible d'envoyer des messages en utilisant la fonction puvblish qui prend en paramètre le message à envoyer.
pub.publish(std_msgs.msg.String("hello world"))
Subscriber:
Initialisation : de la même manière que pour le publisher il est nécessaire d'initialiser le node en mode subscriber. Cette fois ci, la fonction prend en paramètre le topic auquel on s'abonne, le type du message et la fonction a appelé lorsque le message est reçu.
rospy.Subscriber(topic_name, msg_class, callback)
Ensuite, nous avons réalisé un serveur qui réalise une fonction d'addition de deux entiers et un client qui en se connectant au service enverra deux entiers au serveur pour qu'il effectue l'addition.
Service:
Initialisation : pour initialiser un service il faut lui donner un nom, le type du service et la fonction qu'il doit exécuter.
s = rospy.Service(service_name, service_type, function)
Client:
Connexion au service : dans un premier temps le client doit attendre que le service soit disponible pour pouvoir s'y connecter.
rospy.wait_for_service('service_name')
Déclaration de la fonction : une fois que la connexion est établie on peut déclarer la fonction que l'on veut utiliser sur le service.
function = rospy.ServiceProxy('service_name', service_type)
Envoie de requêtes : et enfin lui envoyer des requêtes.
answer = function(arguments)
Semaine 5
SDK
Concernant le SDK, nous avons profité de cette semaine pour chercher un moyen de récupérer les données des capteurs. Pour cela, nous avons repris l'exemple de programme proposé par Parrot pour le Bebop: dans ce programme on reçoit les valeurs des capteurs lorsqu'il y a un changement d'état cependant ces valeurs ne sont jamais affiché ou sauvegardé. Nous avons donc ajouté l'écriture de ces valeurs dans un fichier. Cependant, lors de la lecture du fichier les données ne sont pas cohérentes: toutes les valeurs proviennent du même capteur et elles ne changent jamais. De plus, les capteurs sont identifiés par un nombre et non par un nom on ne peut donc pas identifier quel capteur envoie des données.
ROS
Durant cette semaine, nous avons commencé à explorer la toolbox Matlab Robotics System qui permet d'interagir avec un environnement ROS déjà existant de manière à communiquer avec des systèmes robotiques ou de créer un environnement ROS sous Matlab. Cependant, pour pouvoir utiliser ROS avec Matlab il nous faudrait passer plus de temps à apprendre des commandes ROS que ce soit pour ROS en lui même ou pour Matlab et nous ne sommes pas sûres que Matlab ait des avantages sur Python pour l'application qu'on veut en faire.
Semaine 6
Le but de cette semaine est de se connecter au drone via ROS pour cela il est nécessaire d'installer le driver du bebop pour ROS. Nous avons donc installé bebop_autonomy qui est le driver pour les nouveaux drones Parrot pour ROS basé sur le SDk de Parrot ARDroneSDK3.
Installation de bebop_autonomy
Dans un premier temps, il est nécessaire de créer un workspace catkin qui permet d'organiser les différents packages nécessaires au fonctionnement du projet ROS. Il suffit ensuite de récupérer le projet git du driver et de construire le projet ROS.
Pour cela, il faut d'abord installer les 3 packages d'Ubuntu suivants : build-esstential, python-rosdep, python-catkin-tool. En ligne de commande cela donne :
sudo apt-get install build-essential python-rosdep python-catkin-tools
Pour compiler à partir de la source, il faut cloner le code source dans un espace de travail catkin nouveau ou existant, puis utiliser rosdep pour installer des dépendances et enfin compiler l'espace de travail à l'aide de catkin. Les commandes suivantes illustrent cette procédure dans un nouvel espace de travail Catkin.
mkdir -p ~/bebop_ws/src && cd ~/bebop_ws catkin init git clone https://github.com/AutonomyLab/bebop_autonomy.git src/bebop_autonomy rosdep update rosdep install --from-paths src -i catkin build
Utilisation de bebop_autonomy
Une fois le driver installé, il est possible de le lancer en tant que node autonome ou que nodelet, cependant, seul le nodelet est conseillé dans le cas ou l'on veut effectuer un traitement sur le flux vidéo provenant du bebop. Une fois le projet ROS correctement, il est possible d'envoyer des commandes basiques au drone pour le contrôler en publiant des messages du on type sur le topic correspondant. Par exemple, pour faire décoller le drone il faut envoyer des messages de type std_msgs/Empty au topic takeoff de la manière suivante:
$ rostopic pub --once [namespace]/takeoff std_msgs/Empty
Cependant, il faut faire attention lorsque l'on contrôle le drone de cette manière car lors de la réception d'une commande pour faire avancer, reculer ou tourner le drone il l'effectue jusqu'à la réception d'une nouvelle commande.
Semaine 7
Récupération de données avec Python
Notre projet se basant sur le traitement d'images, il pourrait être avantageux de travailler avec du Python car la bibliothèque de traitement d'images OpenCV est performante avec Python.
PyParrot
PyParrot a été conçu afin de programmer différents drones Parrot, dont le Bebop2, en utilisant Python. Initialement, cette interface a été développée pour enseigner aux enfants de tous âges les concepts STEM (programmation, mathématiques, etc.) en leur permettant de programmer un drone pour qu'il vole de manière autonome. Toutes les informations liés à cette bibliothèque se trouve ici.
- Installation
Au préalable, il faut vérifier que nous avons bien Python 3 car la bibliothèque ne fonctionne qu'avec cette version, ce qui était bien notre cas. Ensuite, pour analyser les fichiers XML dans le SDK de Parrot nous utilisons la commande suivante :
pip install untangle
Une fois ceci fait, il suffit de cloner et installer le git correspondant au projet (à retrouver ici).
- Études et application
Dans un premier temps, notre but est de réussir à récupérer des données du drone, peu importe laquelle exactement. Sur ce projet nous pouvons trouver un dossier contenant plusieurs exemples de code Python afin de contrôler le drone. En étudiant plusieurs d'entre eux, notamment demoBebopIndoors.py et demoBebopTricks.py, nous avons pu trouver les fonctions essentielles pour contrôler le drone : le connecter par exemple. En s'inspirant de ces 2 fichiers, nous avons créé un programme Python, dans le but de récupérer l'état du drone. En effet, dans le code demoBebopTricks, on peut remarquer qu'il est demandé de retourner l'état du drone. En allant chercher plus d'explications dans la documentation, plus précisément au niveau des Bebop sensors, nous avons pu apprendre que le paramètre utilisé dans le code exemple correspondait à la position du drone et pouvait prendre comme "valeur" : "landed", "takingoff", "hovering", "flying", "landing", "emergency", "usertakeoff", "motor_ramping", "emergency_landing". A la suite de cette étude, nous avons créé le programme suivant :
En le testant sur le drone nous avons obtenu le résultat suivant :
Nous pouvons remarquer tout d'abord que nous récupérons des données dont nous ne savons pas à quoi elles correspondent (dû à l'appel de la fonction bebop.ask_for_state_update()). Mais sur la suite nous récupérons bien la position du drone "landed", ce qui est cohérent car le drone est resté au sol pour ce test afin d'éviter tous risques de dommages. Nous pouvons également récupérer le pourcentage de la batterie, et nous pouvons voir qu'à la fin du test le drone se déconnecte correctement.
Pour cette semaine, nous n'avons pas encore trouvé comment récupérer d'autres données, cela sera à approfondir par la suite.
Nous avons donc essayé de nous connecter au drone pour le contrôler et pour récupérer les valeurs des différents capteurs. Nous avons testé plusieurs bibliothèques Python fonctionnant avec les drones Parrot mais certaines se basent sur le SDK de Parrot et ont donc les mêmes limites que le SDK.
Récupération des données avec ROS
La semaine dernière, nous avons réussi à nous connecter au drone avec ROS et à le contrôler, cependant, nous n'étions pas parvenues à récupérer les valeurs des différents capteurs nous avons donc cherché à obtenir la vidéo du drone et les valeurs des capteurs en temps réel.
Dans un premier temps, nous avons cherché à afficher la liste de tous les topics sur lesquels le drone publie des valeurs pour savoir ce qu'il est possible de récupérer.
rostopic list
Une fois la liste des topics connue, nous avons cherché à afficher la vidéo provenant du drone. Pour cela, ROS propose l'outil rqt qui peut s'abonner aux topics sur lesquels des vidéos sont publiées. Nous avons donc utilisé cet outil pour visualiser la vidéo du drone, on constate un léger délai lors de la visualisation de cette vidéo.
Après avoir réussi à récupérer la vidéo, nous nous sommes intéressées aux valeurs publiées par les différents capteurs et à comment les afficher. Pour cela, nous avons tout d'abord afficher l'état de la batterie.
On reçoit une nouvelle valeur dès qu'il y a changement d'état de la batterie. Cette valeur est accompagnée d'un numéro de séquence ainsi que du timestamp du moment où le message a été envoyé. En plus de récupérer la valeur de la vitesse du drone, de son altitude et de sa position. Nous avons réalisé une acquisition de l'altitude du drone en vol, cette fois ci, on ne reçoit pas un paquet à chaque changement de valeur mais à une certaine fréquence. Encore une fois, l'entête du message est composé du timestamp et d'un numéro de séquence puis nous avons la valeur de l'altitude. Sur l'image ci-dessous on peut voir deux paquets reçus après avoir donné au drone l'instruction d'atterrir.
Après avoir récupéré nos données en ligne de commande, nous avons utilisé un subscriber en Python qui s'abonne au topic d'odométrie, récupère les valeurs de position linéaire en x et y du drone ainsi que son angle d'inclinaison et les affichent dans le terminal. Il nous faut maintenant travailler sur la manière de traiter les données que l'on reçoit et comment transmettre les données au drone en fonction de ce qu'on a reçu.
Semaine 8
Récupération des valeurs avec ROS
Nous avons, dans un premier temps, cherché à utiliser des fichiers pour stocker les données que l'on reçoit du drone pour pouvoir les traiter. Cependant, étant donné l'architecture des programmes sous ROS, il serait idéal d'avoir un programme qui lit les données (Subscriber) et un autre qui traite les données et envoie les instructions (Publisher) nous aurons donc un problème avec l'aspect temps réel du projet si on conserve un fonctionnement avec des fichiers. Il faut donc que l'on réfléchisse à la mise en place d'une file de message pour échanger les données entre les deux programmes.
Traitement de la vidéo
Nous avons commencé à nous intéresser au traitement des images pour détecter des objets dans le flux vidéo provenant du drone. Pour réaliser ceci, il est nécessaire de convertir le format de la vidéo publié sur le topic image_raw qui est propre à ROS et n'est pas compréhensible par la bibliothèque de traitement d'images OpenCV en un format vidéo propre à OpenCV pour pouvoir traiter la vidéo. Nous avons donc réalisé un premier programme qui s'abonne au topic image_raw, convertit la vidéo et dessine un cercle dans le coin supérieur gauche, nous affichons ensuite la vidéo obtenue pour s'assurer que tout s'est bien passé. Une fois que nous savons que le programme précédent fonctionne, nous avons réalisé un deuxième programme nous permettant de détecter un cercle dans la vidéo et de dessiner un cercle autour de celui-ci, ainsi qu'un point en son centre. Pour cela, nous utilisons une fonction proposée par ROS : la transformée de Hough. Nous avons remarqué que le traitement de la vidéo par cette fonction est très long et ne pourrait donc pas être utilisé si on veut conserver un aspect temps réel.
Pour la suite, il nous faudrait poser proprement ce qu'on cherche à détecter dans l'image et quelle démarche à mettre en place pour détecter ces objets.
Récupération de la vidéo avec Python (suite)
Pour notre projet, nous allons devoir utiliser la vidéo pour lui appliquer différents traitements d'images. C'est donc une des données principales que nous devons essayer de récupérer. Pour ceci, nous passons toujours par la bibliothèque PyParrot. En effet, celle-ci comporte un fichier exemple concernant la vision et donc la vidéo du drone. Nous allons de ce fait, travailler sur cet exemple afin d'essayer de récupérer la vidéo de la caméra du drone en passant uniquement par Python.
On peut remarquer que ce programme utilise notamment OpenCV2, mais aussi un programme inclut dans la bibliothèque PyParrot : DroneVision, celui-ci utilise notamment ffmpeg qui est une collection de logiciels libres destinés au traitement de flux audio ou vidéo. En termes simples, ce programme vise à :
- Se connecter au drone
- Si la connexion s'est déroulée correctement, de commencer à enregistrer la vidéo de la caméra
- Si la vidéo a bien commencé, un message pour confirmer ceci, et après pour éviter de faire voler le drone, il sera demandé de bouger le drone manuellement
- On a alors au total 30 sec pour bouger le drone, avant qu'il n'arrête la vidéo
- Une fois ceci terminé, on déconnecte le drone
Avant de tester tel quel cet exemple, nous avons dû installer dans un premier temps OpenCV2, puis ffmpeg. Ces deux logiciels installés, nous pouvons passer au test de ce programme directement sur le drone. La première partie concernant la connexion se passe normalement. Par contre, le programme ne va pas plus loin et cela nous sort une erreur concernant ffmpeg. Après diverses recherches sur les forums, nous nous rendons compte que cela vient sûrement de notre version de ffmpeg qui n'est pas la plus récente. Il faudra donc mettre à jour le logiciel et tester à nouveau le programme.
Semaine 9
Traitement de la vidéo
Pour cette semaine nous avons défini que nous allions travailler avec 4 flèches de couleurs différentes en définissant que chaque couleur correspond à un déplacement différent. Pour reconnaître les différentes flèches, nous allons donc travailler en décomposant les différentes composantes couleurs de l'image de manière à ne conserver que les flèches de la couleur de la composante et les flèches grises. Les flèches de couleur de la composante sont en blanc et les flèches grises sont du même gris dans chaque composante.
Une fois les composantes isolées, on peut travailler avec l'histogramme de l'image pour déterminer la couleur des objets présents dans l'image.
Récupération de la vidéo avec Python (suite)
Il nous faut cette semaine mettre à jour ffmpeg afin de voir si c'est la cause de notre erreur lors du lancement de notre programme exemple. Après mise à jour du logiciel ffmpeg, il n'y a plus d'erreur, le programme se déroule correctement et va jusqu'au bout. Notre programme crée alors un fichier bebop.sdp, nous ne savions pas au départ ce que cela était, et pensions alors que c'était la vidéo sortante du drone car l'ordinateur essayait de la lire comme une vidéo. Mais la lecture de ce fichier était impossible avec un lecteur vidéo (Vidéos, VLC, ...). Nous nous sommes alors renseignées sur ce qu'était un fichier sdp et voici ce que l'on a retenu :
SDP est l’abréviation de Session Description Protocol. Le Session Description Protocol définit un standard qui décrit les paramètres pour l’échange de média (souvent média en streaming) entre deux (normalement) points. En termes simples, le protocole SDP est une déclaration par un terminal habilité a transmettre des données média de ses capacités de réception. Une déclaration type devrait nous donner les informations suivantes :
- Quelle adresse IP est préparée pour recevoir le flux média entrant
- Quel numéro de port attend le flux média entrant
- Quel type de média le terminal s’attend à recevoir (typiquement audio)
- Quel protocole le terminal attend pour échanger l’information (typiquement RTP)
- Quel codage de compression le terminal est capable de décoder (codec)
Le SDP ne transmet pas lui-même de média, mais se limite à la négociation de paramètres d’échange de média compatibles. Le flux média lui même est géré par un autre canal et protocole. On s'est donc rendu compte que ce fichier n'allait pas être notre vidéo mais seulement les paramètres d'échange de celle-ci. En ouvrant maintenant ce fichier avec un bloc notes (ou autres applis de traitement de texte), nous avons connaissances de ces paramètres qui sont les suivants :
• Le paramètre c correspond à l'adresse IP du drone
• Le paramètre m correspond au port
• Le paramètre a correspond au protocole rtp
Semaine 10
Traitement de la vidéo
Nous avons imprimé les flèches utilisées la semaine dernière pour tester la reconnaissance de couleurs puis nous avons enregistré des images contenant les flèches provenant du drone. Après avoir passé les images ainsi obtenues au même traitement que celui utilisé la semaine dernière, nous nous sommes rendues compte que l'intensité des flèches de couleurs est beaucoup moins nette que sur le test réalisé précédemment et nous ne pourrons donc pas utiliser uniquement cette méthode pour identifier nos flèches.
Nous nous sommes donc intéressées à la détection de features dans l'image pour nous aider à trouver les flèches. Dans un premier temps, nous avons utilisé la détection de corners à l'aide de l'algorithme de détection de Harris. Un corner est un point dont les voisins sont dans au moins deux directions de bordures différentes. Ils peuvent être interprétés comme la jonction de deux bordures où une bordure est un soudain changement de luminosité dans l'image.
En réglant le seuil d'ouverture de la fonction on est capable de détecter les coins de toutes les flèches.
Nous nous sommes ensuite intéressées à la détections des edges de l'image en utilisant l'algorithme de Canny qui nous permet d'extraire la structure des objets présents dans l'image pour réduire le montant de données à traiter.
Sur l'image ci-dessus,on voit que les contours des flèches sont bien formés. On pourrait essayer de nettoyer les contours du reste de l'image pour ne garder que la flèche ou sélectionner une région d'intérêt correspondant à la feuille sur laquelle est imprimée la flèche pour réduire la zone à traiter et ne conserver que la partie de l'image qui nous intéresse.
Semaine 11
Cette semaine, Mme. Lecocq nous a donné un cours de traitement d'images de manières à nous informer des différentes techniques utilisables pour trouver une forme précise dans une image. Il nous faut maintenant déterminer quelles sont les caractéristiques qui définissent une flèche tout en gardant en tête que les images proviendront du drone et qu'on ne sera pas tout le temps à la même distance de la flèche qui ne sera donc pas toujours de la même taille. Il faut donc trouver des caractéristiques qui ne dépendent pas de la taille de la flèche.
Semaine 12
Durant cette semaine, nous avons continuer à explorer les options proposées par OpenCV pour extraire les caractéristiques des flèches. Nous avons donc commencé à réaliser une application permettant à l'utilisateur d'effectuer le traitement voulu sur l'image qu'il a spécifié (ou une image par défaut). A la fin de la semaine, l'application permettait de réaliser les traitements suivants :
*Détecter un objet bleu/rouge/vert et possibilité de combiner les images obtenues *Détecter et dessiner les contours des objets présents dans l'image *Trouver et dessiner les centres de gravité des objets présents dans l'image *Trouver et remplir les surfaces des objets dans l'image *Dessiner le plus petit rectangle possible permettant d'entourer les objets de l'image *Dessiner le plus petit cercle possible permettant d'entourer les objets de l'image *Réaliser la squelettisation des objets
Il nous reste à ajouter d'autres fonctions que nous avons déjà testé à l'application ainsi que la possibilité de sauvegarder les images suite aux traitements effectués.
Semaine 13
Durant cette semaine, nous avons continué notre application en ajoutant les différents traitements que nous avions déjà utilisé au cours du projet mais qui n'étaient pas encore inclus dans l'application. De plus, nous avons ajouté une fonctionnalité nous permettant de détecter des formes basiques au sein d'une image en recherchant les contours des objets puis en les approximant à des polygones. Une fois les contours approximés, on compte le nombre de côtés du polygone pour déterminer la forme dont il s’agit. Ainsi, nous avons défini que les polygones à trois côtés sont des triangles, ceux à quatre côtés sont des carrés, ceux à cinq côtés sont des pentagones, ceux à sept côtés sont des flèches et ceux à plus de 19 côtés sont des cercles. Cette fonction ne nous permet donc pas de différencier les carrés des rectangles ou des losanges et on considère que tous les heptagones sont des flèches.
Sur l'image ci-dessus, on a colorié en rose les flèches, en bleu les cercles, en blanc les carrés, en vert les pentagones et en cyan.
Semaine 14
Durant cette semaine, nous avons effectuer la une légère correction dans notre fonction permettant la détection de la flèche verte qui n'était pas considérée comme une flèche auparavant. Nous avons ensuite réfléchi à un algorithme qui nous permettrait d'isoler les flèches et de récupérer leurs couleurs. Pour cela, nous avons identifié deux options différentes :
- Utiliser l'algorithme de détection de forme pour détecter les flèches - Créer un masque avec les flèches détectées les plus grandes pour supprimer les autres formes - Appliquer le masque sur l'image en couleur pour récupérer les couleurs - Utiliser l'algorithme qui isole les objets de couleurs pour trouver la couleur des flèches
Il est aussi possible de remplacer les deux premières étapes de l'algorithme en utilisant l'image obtenue en faisant un seuillage et en nettoyant les pixels qui sont connectés au bord de l'image.
Les tâches qu'il nous reste à faire concernant le traitement d'image sont donc:
- La mise en place de l'algorithme ci-dessus
- Le pré-traitement des images extraites de la vidéo
- Des tests dans différentes conditions
Semaine 15
Contrôle du drone
En parallèle du travail de traitement d'images, nous devons également nous occuper de la partie contrôle du drone, puisqu'une fois le traitement d'images terminé, il faudra l'appliquer au drone et savoir quelle donnée lui envoyer pour qu'il "réagisse" correctement. Pour cela, nous avons analysé les différents topics présents grâce à la commande rostopic list. Nous avions déjà connaissance du topic Odometry qui nous permettait de récupérer les positions linéaires et angulaires du drone. Cela nous sera utile pour savoir quel type de données nous allons devoir envoyer pour déplacer le drone. Cependant, le drone ne fait que publier sur ce topic donc ce n'est pas celui ci qui nous permettra de le déplacer en lui envoyant des données.
Le topic qui nous sera utile pour cela, sera Cmd_vel. En effet, grâce à la commande rostopic info et rosmsg show nous avons pu remarquer que le drone souscrivait à ce topic et que celui-ci avait aussi comme données les positions linéaires et angulaires.
Traitement d'images
Durant cette semaine, nous avons mis en place l'algorithme évoqué la semaine précédente en utilisant l'algorithme de détection des formes. On obtient le résultat ci-contre.
Nous avons ensuite utilisé l'algorithme nous permettant d'extraire les objets de couleur pour extraire les flèches rouge, bleu et verte nous avons ensuite soustrait les trois images ainsi obtenues à l'image des flèches isolées pour obtenir la flèche grise. De plus, lors de la réalisation de l'algorithme, nous avons tenu à jour un document contenant les fonctions que nous avons utilisées ainsi qu'une description de leur fonctionnement.
Une fois l'algorithme mis en place, nous nous sommes intéressées au prétraitement de nos images avec dans un premier temps, l'application de différents filtres pour trouver le plus approprié c'est à dire le filtre qui nous permettra de supprimer le bruit tout en gardant les contours des objets les plus précis possible. Nous obtenons des résultats prometteurs avec le filtre bilatéral. Nous avons ensuite essayer de corriger la luminosité de l'image de manière à faire ressortir les couleurs et à faciliter la détection des couleurs lorsque l'on passe dans le domaine HSV.
Semaine 16
Traitement d'images
Nous avons dans un premier temps, réalisé un programme comprenant la chaîne complète de traitement des images : le pré-traitement, le traitement pour trouver la couleur de la flèche et enfin la prise de décision. Une fois le programme réalisé, nous nous sommes constituées une banque d'images de test composée de 11 images. Nous avons donc testé notre algorithme pour ces 11 images et on parvient à extraire les flèches et trouver leur couleur sauf dans trois cas. Dans deux des cas qui ne fonctionnent pas, on est parvenu à trouver le problème : position de la main qui tient la feuille et bord blanc entre le bord de la feuille et le bord de la flèche trop petit, ce qui fait que les contours des flèches ne sont pas propres. Pour la troisième image nous ne sommes pas parvenues à trouver le problème. De plus, dans un soucis de pouvoir effectuer le programme en temps réel, nous avons chronométré l'exécution du traitement, la chaîne complète prend donc entre 0.03 seconde et 0.1 seconde en fonction de l'ordinateur qui exécute le programme.
Une fois que l'algorithme sur les couleurs nous a donné des résultats corrects, nous nous sommes intéressées à la direction de la flèche. Pour cela, nous avons exploré deux pistes:
-
Travailler avec les centres de gravité de la flèche et du plus petit rectangle dans lequel elle est inscrite. Ainsi, la position du centre de gravité par rapport à la position du centre de gravité du rectangle nous indique si la flèche est vers le haut, le bas, la droite ou la gauche. Cette option ne fonctionne cependant pas lorsque la flèche est inclinée ce qui change l'emplacement du centre de gravité de la flèche.
-
La deuxième option utilise les points extrêmes en x et y. Ainsi, on considère que la distance entre la pointe de la flèche et l'un des "bras" de la flèche est plus petite que la distance entre le bord de la flèche et un des "bras" ce qui nous donne la direction de la flèche.
Contrôle du drone
Pour l'instant, nous contrôlons le drone uniquement avec des lignes de commande ROS via un terminal, notamment pour le décollage et l'atterrissage. Donc pour commencer à contrôler le drone correctement uniquement en lançant un programme, nous nous sommes intéressées dans un premier temps à comment décoller et atterrir avec un programme utilisant comme langage de programmation Python.
Nous avons tout d'abord dû regarder comment publier via ROS, car pour l'instant nous ne faisions que souscrire à des topics (pour récupérer les valeurs de position par exemple). Pour cela, nous avons regardé si nous pouvions avoir des exemples d'une fonction pour décoller grâce au topic takeoff, ce qui nous donnera la structure d'une fonction pour publier sur un topic, et cela sera directement appliqué à ce que nous avons besoin.
Voici l'exemple dont nous nous sommes inspirées pour commencer notre programme. Une fois testé celui-ci fonctionne bien. Pour autant, en essayant de faire la même chose pour atterrir, c'est-à-dire, créer une nouvelle fonction en modifiant juste le topic sur lequel on publiait, cela ne fonctionnait plus car le drone réalisait bien le décollage, mais rien de plus. En analysant le code, nous nous sommes rendues compte que nous avions un problème de boucle infinie avec la fonction Takeoff, donc il ne réalisait pas la fonction d'atterrissage. Nous avons alors eu l'idée dans un premier temps de mettre une autre condition pour la boucle while. En regardant les topics, nous avons vu qu'il existait un message FlyingStateChanged, qui nous paraissait bien comme condition car il suffit alors de demander le décollage uniquement quand le drone était au sol. Malheureusement, le topic utilisant ce message est impossible à récupérer en souscrivant à celui-ci, et nous avons remarqué que c'est le cas pour tous les topics faisant intervenir un état. Nous ne savons pas expliquer pourquoi ce message n'est pas récupérable, car il figure bien parmi tous les messages répertoriés dans le dossier bebop_msgs/msg/autogenerated.
Il nous faut alors une autre idée, et la suivante a été d'utiliser une variable x, que l'on incrémente simplement une fois le takeoff réalisé, mais encore une fois après de nombreux essais, ce fut un échec.
Nous avons essayé par la suite d'utiliser un timer pour effectuer l'action du décollage pendant un certain temps avant de faire autre chose, ce qui pourrait permettre de sortir de cette boucle infinie. Cependant, après plusieurs recherches, que ce soit en passant par ROS ou en Python3, la création de timer est assez complexe, surtout pour l'utilisation que l'on en aura. Nous avons même essayé au final, de juste publier la fonction takeoff sans condition, mais la publication ne se réalisait pas. Nous avons alors compris que le problème ne venait pas que de la boucle infinie, mais surtout que la structure pour publier n'était pas tout à fait correcte.
N'étant pas expertes de ROS, nous avons demandé de l'aide à Vincent Coelen pour résoudre ce problème et pouvoir avancer sur ce programme. Après un bon moment de questionnement, nous avons enfin trouvé le réel problème ensemble. Il s'agissait au final d'un problème de timing dans la publication. En effet, la structure pour publier sur un topic se divise en 2 lignes de code :
- pub = rospy.Publisher("bebop/topic", type_msg, queue_size=x)
- pub.publish(type_msg)
Et si nous exécutons ces 2 lignes à la suite, les informations nécessaires pour publier ne parviennent pas assez rapidement. Une solution possible est alors de mettre une tempo entre ces 2 lignes.
Une autre solution qui fonctionne également proposée par Vincent, est d'ajouter un paramètre à la première ligne de code pour publier : latch. Celui-ci, quand il est à True, permet en quelque sorte de passer la publication comme prioritaire et ROS la verra forcément entre toutes les publications demandées. Il est utilisé principalement pour des publications réalisées une fois, ou de façon peu fréquente.
Dans un souci d'optimisation du code, nous avons opté pour la seconde solution qui nous permet d'avoir moins de lignes de code. Notre programme pour décoller et atterrir à la suite, ressemble alors à ceci :
Nous avons ajouté des fonctions sleep de 4 secondes dans chacune des fonctions, car de base, les fonctions takeoff et land, une fois lancées, ne prennent aucune autre instruction pendant 3 secondes.
Semaine 17
Traitement d'images
Durant cette semaine, nous avons réalisé une banque de test de 80 images de manières à tester le programme en entier avec la détection de la couleur et de la direction des flèches. Pour cela, nous avons imprimé de nouvelles flèches. Après avoir passé toutes les images dans l'algorithme, nous nous sommes rendues compte que la détection de la direction fonctionne correctement mais pas celle de la couleur. Même en changeant les limites des valeurs HSL nous n'obtenons pas les résultats attendus. Nous avons donc réfléchi à de nouvelle manière de détecter la couleur de la flèche :
- Trouver la couleur avec des fourchettes de valeurs RGB et non plus HSL.
- Extraire la couleur du pixel au centre de gravité de l'image pour déterminer la couleur de la flèche.
Contrôle du drone
Fonctions selon la direction
Maintenant que nous avons la base de notre programme pour décoller et atterrir, nous pouvons maintenant nous intéresser aux différents déplacements dont nous avons besoin, c'est-à-dire, aller à gauche, à droite, en haut, et en bas. Pour cela, nous utilisons la même structure pour publier que les fonctions précédentes (takeoff, land), mais cette fois-ci nous allons utiliser le topic cmd_vel car il permet d'agir sur linear x, y, z et sur angular x, y, z. Linear permet d'avancer selon les axes x,y,z et Angular permet de tourner selon les mêmes axes. Nous allons donc pouvoir créer une fonction pour chaque direction.
Pour savoir dans quel sens tourne le drone lorsqu'on lui impose une valeur en angular z, nous faisons un test simple sur le drone. Nous observons alors que lorsque z>0, le drone tourne sur sa gauche, et quand z<0, il tourne sur sa droite. En sachant ça, nous pouvons déjà créer les deux fonctions pour aller à droite et à gauche : on instaure une valeur en angular z pour la rotation puis en linear x pour qu'il avance dans cette direction. Cependant, il faut faire attention de bien réaliser ces 2 instructions l'une après l'autre et non pas en même temps, sinon nous ne pouvons pas tourner correctement. Également, pour que ce soit visible, il faut répéter ces instructions plusieurs fois à une certaine fréquence. C'est pour quoi on initialise une fréquence à 10Hz, et avec plusieurs tests, on décide de faire 90 fois l'instruction angular z, car elle nous permet avec une fréquence à 10Hz, de tourner convenablement à 90°. Pour l'instruction linear x nous imposons une valeur (arbitraire) pour l'instant de 0,1 à effectuer 20 fois, mais celle-ci sera modifiée plus tard car nous souhaitons faire un programme qui permet d'être modulable et donc de choisir si on veut aller à une certaine vitesse ou une certaine distance. Au niveau des fonctions pour aller vers le haut et vers le bas, c'est plus simple, il suffit uniquement de jouer sur linear z.
Pour l'instant, nous implémenterons le choix de la direction en tant que demande d'utilisateur en attendant de fusionner avec le traitement d'images qui nous donnera la direction des flèches.
Modularité : choix entre contrôle de vitesse ou de distance
Pour ajouter une fonction supplémentaire à notre programme, nous avons décidé de laisser à l'utilisateur le choix de contrôler le drone en vitesse ou en distance, c'est-à-dire, que pour chaque déplacement, il sera demandé à l'utilisateur de choisir entre distance et vitesse, puis de choisir à quelle distance ou à quelle vitesse le drone fonctionnera.
Pour réaliser cela, plusieurs tests ont dû être effectué afin de connaître la valeur a imposer en linear x pour les différentes directions. Concernant la vitesse, les tests ayant déjà été réalisé par Alexis et Abass dans le cadre de leur projet, j'ai pu récupérer leurs résultats afin de connaître le ratio entre la vitesse réelle en m/s et la valeur a mettre en linear x. Celui-ci est de 9 donc dans le main on demande la valeur de la vitesse que l'on stockera dans une variable et qu'on utilisera dans chacune des fonctions de direction. Pour la distance, nous avons réalisé plusieurs tests en imposant une fréquence et un nombre de fois où il allait réaliser l'instruction et nous avons imposé des linear x différents (de 0,1 à 0,7) et pour chaque essai, nous avons mesuré la distance parcourue. Avec ceci, nous avons pu en déduire un ratio entre la distance parcourue et la valeur de linear x. Donc nous avons modifié encore une fois le linear x dans chacune des fonctions de direction selon ce ratio. Bien sur dans les fonctions de direction, nous mettons des conditions pour savoir si on utilise le contrôle en vitesse ou en distance. Voici alors ce que donne, par exemple, la fonction pour aller à droite :
Optimisation
Quelques modifications ont été apporté au programme afin qu'il soit optimisé, par exemple, plutôt que les "sleep" soient réalisés dans le main après les fonctions takeoff et land, ils ont été inclus directement dans les fonctions dédiées, ce qui permet d'alléger le main et d'avoir un code plus propre.
Récupération d'images via le flux vidéo du drone
Cette semaine, nous avons cherché comment il était possible de récupérer dans un premier temps le flux vidéo de la caméra du drone. NOus allons créer un programme juste pour ça afin de ne pas perturber le programme principal, et nous l'ajouterons après. Après quelques recherches, nous avons pu déterminer que nous allons devoir utiliser le topic image_raw. Il faudra donc réfléchir à comment ne prendre que quelques photos par seconde afin de réaliser le traitement d'images. En effet, la camera du drone a une fréquence de 30 images/sec, mais nous ne pouvons pas traiter autant d'images, d'autant plus, que ce n'est pas nécessaire. On s'arrêtera alors à récupérer entre 5 et 10 images/sec.
Tout d'abord, nous voulions trouver un moyen de pouvoir sauvegarder uniquement des images à partir du flux vidéo. Nous avons alors trouver la fonction imwrite d'OpenCV, qui permet de sauvegarder une image en prenant en paramètre, le nom de l'image qui sera créé et l'image récupérée. Cependant, le format des images que l'on récupère et son utilisation avec une fonction OpenCV n'est pas possible tel quel, nous devons donc mettre en place un "pont", afin de modifier le format de l'image. Voici les lignes de code nécessaires :
bridge = CvBridge() cv2_img = bridge.imgmsg_to_cv2(msg, "bgr8")
Cependant, si nous utilisons un simple nom pour enregistrer l'image, nous allons avoir le problème que l'image prise sera écrasée par la précédente et ainsi de suite car nous garderons le même nom. Pour résoudre ce problème, nous avons donc décider d'utiliser la date et l'heure actuelle ( afin de nommer notre image. Ceci est possible grâce à l'utilisation de la fonction python :
date = datetime.datetime.now()
Cela nous permet d'une part de sauvegarder plusieurs images, mais aussi de pouvoir vérifier le nombre d'images que l'on a par secondes. Il nous faut maintenant trouver comment nous pouvons faire pour ne pas récupérer toutes les images sortantes du drone. Pour cela, nous allons réutiliser la fonction datetime afin de récupérer les msec de l'heure actuelle. Cependant, nous récupérons 6 chiffres de msec, la précision est trop grande et ne nous est pas utile pour le traitement d'images, seul 2 chiffres nous sont nécessaires. Pour récupérer seulement les deux premiers chiffres sur les 6, nous allons simplement diviser par 1000 la valeur reçue puis la convertir en entier, que nous enregistrons dans une variable. En faisant ça nous pouvons donc ajouter une condition pour régler la fréquence de sauvegarde d'images :
if msec%4==0
Semaine 18
Contrôle du drone
Récupération d'images via le flux vidéo du drone (suite)
Nous avons ajouté le programme réalisé la semaine dernière dans notre programme principal, nous n'avons pas eu de souci particulier en intégrant celui ci en ajoutant simplement une fonction.
Programme en continu
Notre programme pour le moment, n'exécute qu'une fois les instructions du main, il faut donc créer une boucle afin de pouvoir utiliser en continu, tout en prenant garde de pouvoir arrêter le drone assez régulièrement pour des mesures de sécurité. Pour cela, nous faisons une boucle while, où auparavant nous donnons comme valeur True a une variable, celle ci sera la condition de la boucle, et on la passe alors à False lorsque l'on a finit l'instruction donnée par la direction. Pour arrêter le programme nous avons ajouté de ce fait à la suite des instructions, la possibilité de continuer ou non le pilote du drone. Si l'utilisateur décide d'arrêter l'utilisation du drone, il lui suffira d'appuyer sur la touche "e" et le drone entamera alors son atterrissage. Par contre, si on appuie sur n'importe quelle autre touche, le programme recommencera au niveau du choix de contrôle du drone (vitesse/distance). Afin de récupérer l'appui sur la touche "e" nous avons du implémenter nous même la fonction getch() qui n'est pas d'office sur python. Par la même occasion nous avons ajouté une fonction stop() à notre programme qui permet de remettre le drone à 0 selon toutes ses variables linear et angular, ce qui est plus sur avant d’atterrir.
Intégration du traitement d'images dans le contrôle du drone
Maintenant que nos programmes pour contrôler le drone, et celui du traitement d'images fonctionnent indépendamment, nous pouvons réunir les deux. Et pour faire ça proprement et éviter un énorme programme nous allons plutôt faire appel au programme du traitement d'images au sein du programme de contrôle du drone. Pour cela, il a fallu dans un premier temps installer toutes les libraires nécessaires au fonctionnement du traitement d'images. Puis rechercher comment faire un appel à un programme et son utilisation sur python.
Il y a eu ensuite à faire quelques ajustements au sein des deux programmes afin que l'ensemble fonctionne correctement. Comme par exemple intégrer la sauvegarde des images dans la fonction appelée par le programme principal, ou encore ajouter le fait d'arrêter de souscrire au topic image_raw une fois que l'instruction d'une direction a été donné, afin d'éviter de surcharger la mémoire et le travail du traitement d'images qui se ferait en continu sinon.
Test
Lors du test final, nous pouvons remarquer que l'ensemble fonctionne plutôt bien, le traitement d'images est réalisé correctement et nous donne la bonne direction de flèche, le contrôle du drone effectue à son tour le bon déplacement. On notera juste un manque de précision pour les directions de flèche haut et bas, car le drone ne se déplace pas assez pour atteindre la distance demandée.