Dossier sur la communication et l`interface de contrôle

Transcription

Dossier sur la communication et l`interface de contrôle
Projet Pluritechnique encadré : Quadricoptère
- Partie communication -
Achille DANTZ, En groupe avec DUSSOURD Nicolas et DIETRICH Lucas
Lycée J.B. Schwilgué, Sélestat – Session 2014
Plan :
1) Introduction
2) Etude fonctionnelle
3) Le système et les solutions techniques
a. Piloter le drone
b. Récupérer les informations
c. Visualiser les informations
4) Le fonctionnement de la communication
a. Communication Utilisateur -> Drone
b. Communication Drone -> Utilisateur
5) L’évolution du projet
Introduction :
Pour réaliser notre premier Quadricoptère, nous avions comme objectif de le
réaliser à partir du matériel le plus basique, et non pas de solutions déjà existantes
dans le commerce. C’est dans cette optique qu’a été réalisée la partie de la
communication entre le drone et l’utilisateur. Cette partie, qui m’a été assignée,
comprend plusieurs aspects, que nous allons explorer ici.
Cette partie a été cruciale au projet, car en plus de permettre de piloter le
drone, elle a aussi permis de visualiser les informations du drone, pour analyser et
modifier son comportement en conséquence.
Le travail dans cette partie comprend le choix et l’exploitation du contrôleur
pour piloter le drone, l’envoi des commandes au drone, la réception des informations
du drone et la visualisation de ces dernières.
Etude fonctionnelle :
I.
Problématique
Le Drone construit a pour but la prise de vue aérienne de photos ou de vidéos,
par exemple pour l’examen en toute sécurité d’un ouvrage en hauteur. Pour réaliser
une telle mission, le drone doit être capable d’être conforme pour une utilisation de
type professionnel. Cela inclut, par exemple, une autonomie supérieure à 5 minutes,
et une portée suffisante. Pour la partie communication, les points essentiels sont la
sécurité, la portée et la fiabilité, tout en offrant une expérience de pilotage intuitive,
le tout à un coût maitrisé.
FAST (partie communication) :
On voit ici l’ensemble des fonctions de la communication, ainsi que les solutions techniques
adoptées (détaillés ci-après).
Le système et les solutions technologiques
I. Piloter le drone
Le premier enjeu de la communication est bien évidemment de pouvoir piloter le drone.
Pour ce faire, il a fallu faire le choix d’un contrôleur adéquat.
La solution technique retenue a été un contrôleur de jeu, initialement destinée à être utilisé
avec le système de jeu Playstation®3. Ce contrôleur est idéal car il inclut 2 gâchettes analogiques
(utilisées pour augmenter/diminuer l’altitude du drone), 2 joysticks analogiques, mettant à
disposition 4 axes, utilisés pour les mouvements de ROLL, PITCH, YAW*, ainsi que d’une multitude
de boutons.
(* : Tangage, roulis, lacet)
Le contrôleur utilisé
Crédit image : skatter.com
Une fois le choix du contrôleur fait, il
reste à pourvoir récupérer les
commandes de l’utilisateur. La manette présentée agit comme un périphérique d’interface humaine
(HID) standard. Pour pouvoir communiquer avec, il faut donc disposer d’un hôte USB. Cet hôte
pourra communiquer avec la manette et ainsi récupérer les commandes utilisateurs.
Le module ci-dessous fournit un hôte USB et est capable de communiquer avec les microcontrôleurs
Arduino.
Crédit image : circuitsathome.com
Une fois ces informations extraites du contrôleur, il reste à les interpréter. Pour ce faire, nous avons
recours à un microcontrôleur Arduino MEGA 2560, principalement pour la raison qu’il intègre 4 ports
de communication série, dont un sera utilisé pour le drone et un pour l’interface graphique. Il offre
également l’avantage d’être alimenté en USB sans problème de sous-intensité. Ce microcontrôleur
permet de communiquer avec le module ci-dessus grâce au code disponible en annexe et à une
librairie, modifiée spécifiquement pour le drone.
Il est à noter que le projet utilise trois Arduino : deux sur le drone et un au sol.
Le microcontrôleur utilisé
Crédit image : australianrobotics.au
Le microcontrôleur ici présenté est le « centre des opérations » de la communication. Il assure la
communication entre le drone, l’utilisateur, et l’interface graphique.
Pour transmettre les commandes au drone, plutôt que d’utiliser des modules radios dédiés au
modélisme, et qui ne fonctionnent que dans un sens, nous avons optés pour des modules de
télémétrie Xbee®, qui sont des Zigbee (manufacturés par Digi intl.) adaptés à l’Arduino.
Ces modules émulent un port de communication série asynchrone entre les 2 points du réseau (les
deux modules), et permet ainsi de profiter d’une communication à double sens. La communication
entre les deux modules est basée sur une liaison radio utilisant le protocole Wi-Fi.
Un ensemble au sol manette-Arduino-Xbee sera donc placé au sol et peut fonctionner de manière
autonome (alimenté par batterie ou autre source électrique) ou bien de concert avec un ordinateur
équipé de l’interface graphique spécialement créée pour.
L’interface graphique est programmée en VB.net et permet de communiquer avec l’arduino par le
biais d’un port série virtuel, puisque ce langage intègre une puissante librairie pour la communication
série, ainsi que des contrôles permettant une visualisation agréable et rapide des informations.
Partie 2 : le fonctionnement de la communication
Les deux modules communiquant (un sur le drone, un au sol) seront considérés par les
microcontrôleurs comme une liaison série asynchrone simple. La vitesse (baudrate) de cette liaison a
été fixée à 9600 bits/seconde, ce qui signifie que le signal est capable de changer d’état 9600 fois par
seconde. Si l’on augmente cette vitesse, on remarque, même à faible distance, la perte de paquets,
ce qui peut s’avérer très problématique pour le pilotage d’un drone.
Les commandes envoyées au drone se composent de deux parties : l’adresse et le paramètre.
L’adresse est codée sur 6bits et le paramètre sur 8 bits (la table des commandes et des paramètres
correspondants sont disponibles en annexe).
Une commande envoyée vers le drone sera sous la forme :
AAAABBAABBBBBB
Où A est un bit d’adresse et B un bit de paramètre. Cet agencement non linéaire de la commande
permet de détecter une commande incohérente, et de notifier l’Arduino au sol de l’erreur.
Lors de l’appui d’une touche, ou de la modification d’un paramètre de la manette, on envoie
une commande, à une fréquence maximale de 5Hz, ce qui correspond, en phase de pilotage normal,
à un envoi maximal, toutes commandes comprises, de l’ordre de 15Hz (15 commandes par
secondes). La réception sur le drone est réalisée par le Xbee placée sur le drone, qui peut
Communiquer directement avec l’ArduIMU. Il a donc fallu, de concert avec Lucas DIETRICH, établir
une table des commandes, disponibles en annexe, qui permet d’envoyer les bonnes commandes. Il
est cependant à noter que la plupart des commandes sont utilisées pour le débogage du drone, et ne
sera pas utile pour un utilisateur « standard ».
Une fois ces commandes envoyées au drone, il réagit en conséquence, et pour visualiser ces
réactions, et l’état général du drone, ce dernier est en mesure d’envoyer un rapport de statut
comprenant plusieurs paramètres, comme les angles, l’altitude, les vitesses de rotation des moteurs
etc…
Pour un rapport de statut, 34 octets (checksum inclus) transitent depuis le drone vers le sol, à
une fréquence (modifiable), actuellement de 2Hz, ce qui engendre un trafic de données à un débit de
78octets/seconde, sur un maximum de 9600/8 = 1200 octets/seconde. On a donc une marge de
manœuvre confortable et on n’a pas à craindre une saturation de la mémoire tampon (buffer) de
l’interface série.
Ces données sont formatées pour pouvoir être analysées. Chaque donnée est codée sur 1 à 4
octets. Voici ces paramètres :
Nom
Angle Phi
Angle Thêta
Angle Psi
Altitude
Fréquence
Usage processeur
Commandes reçues
Commandes envoyées
Events
Timers
Overflows
Niveau d’alerte général
Mu (strictement debug)
checksum
Taille en octets
2
2
2
2
3
1
3
3
4
4
4
2
2
1
L’interprétation de ces informations n’est pas réalisée par l’Arduino, mais par l’interface
graphique : Les données ne font que transiter par l’Arduino, même s’il est possible de les analyser,
mais cela ne présente pas d’intérêt.
Chaque nouvelle chaîne est séparée de la précédente par trois retours à la ligne (code ASCII
10). On évite ainsi la mauvaise détection de séparateurs (par exemple si l’on prend un seul retour à la
ligne une donnée peut prendre 10 comme valeur, mais la probabilité que 3 octets valent 10 est
négligeable). En connaissant la longueur du rapport (34 octets), on est en mesure de séparer les
différents octets pour les interpréter.
Le programme intégrant une interface graphique qui se charge de cela est codé en VB.NET, et
utilise un port série de type RS232 virtuel pour communiquer avec l’Arduino terrestre. Ce
programme est principalement destiné à afficher les données du rapport, mais on peut également
envoyer rapidement des commandes au drone par cet intermédiaire.
Des contrôles visuels ont été créés spécialement pour visualiser les informations du drone de
manière adaptée, comme par exemple des jauges ou barres de progrès (code disponible en annexe),
qui, en plus de contrôles existants ou modifiés (comme les contrôles « chart » qui sont à la base des
graphiques visibles sur l’interface.
Voici un diagramme résumant la structure de la communication :
6)
L’évolution du projet
I.
Le cahier des charges
Nom
Portée
Contrainte
Portée minimale de 100m
(illégal de piloter le drone à
plus de 100m)
Utilisation
Etre facile d’utilisation et
pilotable de façon sécurisée
Conformité
Etre conforme aux normes
européennes et françaises sur
la radiocommunication
II.
Validation
La portée a été testée avec
succès en terrain demi-dégagé
à une distance d’environ 150m
sans interruption de la
communication.
Pilotage par contrôleur de jeu :
intuitive et facile.
Logiciel portable et aisé à
configurer
Utilise les fréquences et
protocoles 802.15.4 avec une
puissance de 60mW : conforme
à la législation. Egalement
conforme RoHS
Difficultés rencontrées et évolution du projet
La partie de la communication du drone a connu beaucoup de changements. Plusieurs solutions
techniques ont été envisagées et rejetées durant les phases de prototypage, comme par exemple
l’utilisation d’une manette « faite maison », qui se serait certainement avérée de mauvaise qualité et
peu pratique, alors que la solution retenue est versatile et complète. L’interface graphique, elle aussi,
a d’abord été codée en python, qui se trouve être excellent pour la liaison série, mais très peu
pratique quand il s’agit d’afficher plusieurs graphiques et autres contrôles.
Globalement, le résultat de la communication d’avère être conforme à l’idée de départ : un
pilotage avec une manette, une liaison à double sens et une interface graphique pratique pour
visualiser l’état du drone. Cependant plusieurs difficultés ont fait surface : par exemple la noncompatibilité de la manette (résolue en modifiant la librairie) ou bien encore le comportement des
Xbee, qui changeaient d’adresse à chaque mise sous tension, et qu’il a fallu « reflasher ».
L’expérience de créer un produit en partant de composants ordinaires, sans avoir recours à des
solutions technologiques courantes a été très enrichissant. Le travail de groupe, en particulier avec
Lucas, a été primordial pour la réussite du projet, et bien que cela n’ait pas toujours été chose aisée,
le résultat final est satisfaisant. Avec le groupe, nous envisageons, et dès cet été, de poursuivre ce
projet, afin de l’améliorer et de le terminer.
ANNEXE 1 : Extrait du tableau de commandes
Adresse
car
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
!
"
#
$
%
&
'
(
)
*
param
000000
000001
000010
000011
000100
000101
000110
000111
001000
001001
001010
001011
001100
001101
001110
001111
010000
010001
010010
010011
010100
010101
010110
010111
011000
011001
011010
011011
011100
011101
011110
011111
100000
100001
100010
100011
100100
100101
100110
100111
101000
101001
101010
+
,
.
/
0
101011
101100
101101
101110
101111
110000
-
commande
CMD_RESERVED_1
CMD_RESERVED_2
CMD_COM_OK
CMD_COM_ERROR
CMD_COM_BUSY
CMD_COM_READY
CMD_COM_INCREASE_FREQ
CMD_PROC_EMERGENCY
CMD_PROC_LANDING
CMD_PROC_TAKEOF
CMD_PROC_BATTERY
CMD_MANUAL_LEFT
CMD_MANUAL_RIGHT
CMD_MANUAL_FORWARD
CMD_MANUAL_BACKWARD
CMD_MANUAL_UP
CMD_MANUAL_DOWN
CMD_MANUAL_TURN_LEFT
CMD_MANUAL_TURN_RIGHT
CMD_CFG_ABOUT_TURN
CMD_STOP
CMD_WARN_EMERGENCY
CMD_CFG_AZIMUT
CMD_CFG_ALTITUDE
CMD_CFG_PITCH
CMD_CFG_ROLL
CMD_CHANGE_PR_KP
CMD_CHANGE_PR_KI
CMD_CHANGE_PR_KD
CMD_CHANGE_AVG_THROTTLE
CMD_CHANGE_STATUS
CMD_PITCH_SETPOINT
CMD_ROLL_SETPOINT
CMD_YAW_SETPOINT
CMD_ALTITUDE_SETPOINT
CMD_PID_MODE
MASK1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
1
1
1
1
1
1
1
MASK2
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
1
1
0
0
0
0
0
0
description
0° nord
0 au sol
0 : -20
0 : -20
pitch roll
pitch roll
pitch roll
on change avg
undefined (0), config (1), on (2), pause (3),
emergency (4)
pitch setpoint
roll setpoint
yaw setpoint
altitude setpoint
start or stop 1 or 0
ANNEXE 2 : EXTRAIT PROCESSUS D’ENVOI SOL > DRONE et DRONE >SOL
#include <PS3USB.h> # On importe la librairie PS3 modifiée
#endif
unsigned
unsigned
unsigned
unsigned
unsigned
long
long
long
long
long
lastcmd = 0; //Pour limiter la fréquence d’émission
lastcomLHX = 0;
lastcomLHY = 0;
lastcomRHX = 0;
lastcomRT =0;
USB Usb; //On declare le “shield” hôte
PS3USB PS3(&Usb); // On declare le contrôleur
uint8_t state = 0;
int inter = 50; // Délai mini. Entre deux émissions
void send_IMU(byte cmd, byte param) // Cette fonction envoie les données formatées
au port 1 (Drone)
{
// cmd < 64 - param < 255
byte serial_cmd = CMD_FIRST_BIT << 7 | cmd << 1 | MSB(param);
byte serial_param = PARAM_FIRST_BIT << 7 | LSBS(param) << 0;
Serial1.write(serial_cmd);
Serial1.write(serial_param);
Serial.println(String(cmd) + "
" + String(param));
}
void setup() {
Serial.begin(115200); //Liaison à 115200bauds avec l’ordinateur (port 0)
Serial1.begin(9600); // 9600bauds pour le drone (port 1)
while (!Serial); // On vérifie que la liaison série est effectuée
if (Usb.Init() == -1) {
Serial.print(F("PAS DE LIAISON SERIE"));
while (1);
}
Serial.print(F("INITIALISE")); //On peut commencer à communiquer
}
void loop() {
Usb.Task();
if (PS3.PS3Connected) {
[…]
if (getAnalogHat(L1) > 137 || getAnalogHat(L1) < 117) //valeur centrée :
127
{
send_IMU(46, getAnalogHat(L1)) //YAW(46) = valeur joystick
}
[…]
// On vérifie si la mémoire contient un rapport
if (Serial1.available() > 0)
{
if (Serial1.read() == 59) // 59 correspond à ';' (remplacé par 3 fois
10 (triple retour à la ligne))
{
Serial1.readBytes(buffer, 34); //on enregistre 34 caractères dans le
buffer (tampon)
Serial.print('@');// séparateur de début de rapport
for (int j = 0; j < 34; j++)
{
Serial.print(byte(buffer[j]), DEC);
Serial.print("-");
}
}
ANNEXE 3 : Aperçu de l’interface graphique
Contrôles « compteur » (voir ci-dessous)
Contrôles « barres de progression »
Espace « debug »
Contrôles « Chart »
ANNEXE 3.5 : Code du contrôle « compteur » (VB.NET)
Public Function SetVal(ByVal cible As Panel, ByVal value As Integer)
pan = cible.Width
target = cible.CreateGraphics
target.ResetTransform()
target.Clear(Color.White)
target.TranslateTransform(pan / 2, pan / 2)
target.RotateTransform(50)
target.RotateTransform(value)
target.FillEllipse(Brushes.Black, New Rectangle(-pan / 2, -pan / 2,
pan, pan))
target.FillEllipse(Brushes.White, New Rectangle(-pan / 2 + 2, -pan
/ 2 + 2, pan - 4, pan - 4))
target.FillEllipse(Brushes.SteelBlue, New Rectangle(-pan / 4, -pan
/ 4, pan / 2, pan / 2))
target.FillEllipse(Brushes.White, New Rectangle(-pan / 8, -pan / 8,
pan / 4, pan / 4))
target.FillEllipse(Brushes.Navy, New Rectangle(-pan / 20, -pan /
20, pan / 10, pan / 10))
target.FillRectangle(Brushes.Navy, New Rectangle(-pan / 100, 0, pan
/ 50, pan / 3))
End Function
ANNEXE 4 : Extraits du code d’acquisition (VB.net)
a. Déclaration du port série
Try
s.Close()
s.PortName = "com13"
s.BaudRate = 115200
s.DataBits = 8
s.ReadTimeout = 1000
s.Parity = Parity.None
s.StopBits = StopBits.One
s.Handshake = Handshake.None
s.Encoding = System.Text.Encoding.Default
s.Open()
s.RtsEnable = True
Label1.Text = "Drone connecté"
Label1.ForeColor = Color.Green
''Timer1.Start()
Catch ex As Exception
Label1.Text = "Erreur de com."
Label1.ForeColor = Color.Red
End Try
For k As Integer = 1 To 20
Chart1.Series(0).Points.Add(Math.Sin(k))
Chart2.Series(0).Points.Add(Math.Sin(k))
Chart2.Series(1).Points.Add(Math.Sin(k + 4))
Next
End Try
b.
Interprétation du rapport de statut
7) Dim Roll As Double '' Les 3 en degrés - 2 octets
8)
Dim Pitch As Double
9)
Dim Yaw As Double
10)
11)
Dim Altitude As Integer '' En cm - 2o
12)
Dim Frequence As Double '' 3o
13)
Dim CPU_use As Integer '' 1o
14)
Dim cmd_received As Integer '' 3o
15)
Dim cmd_sent As Integer '' 3o
16)
Dim eventscount As Integer ''4o
17)
Dim timers As Integer ''4o
18)
Dim events_overflow As Integer ''4o
19)
Dim warning As Integer ''2o
20)
Dim mu As Integer ''2o
21)
22)
Dim motor1 As Integer
23)
Dim motor2 As Integer
24)
Dim motor3 As Integer
25)
Dim motor4 As Integer
Try
Dim msg As String = s.ReadExisting
If msg.Length > 0 Then
TextBox1.Text = msg
params = msg.Split("-")
If params(0) = "@" And params(50) = "#" Then
Pitch = CInt(params(1)) * 256 + CInt(params(2))
Roll = CInt(params(3)) * 256 + CInt(params(4))
Yaw = CInt(params(5)) * 256 + CInt(params(6))
Altitude = CInt(params(7)) * 256 + CInt(params(8))
motor1
motor2
motor3
motor4
=
=
=
=
CInt(params(9))
CInt(params(10))
CInt(params(11))
CInt(params(12))
Frequence = CInt(params(13)) * 65536 + CInt(params(14)) * 256 +
CInt(params(15))
CPU_use = CInt(params(16))
cmd_received = CInt(params(18)) * 65536 + CInt(params(18)) * 256 +
CInt(params(19))
cmd_sent = CInt(params(20)) * 65536 + CInt(params(21)) * 256 +
CInt(params(22))
eventscount = CInt(params(23)) * 16777216 + CInt(params(24)) * 65536 +
CInt(params(25)) * 256 + CInt(params(26))
timers = CInt(params(27)) * 16777216 + CInt(params(28)) * 65536 +
CInt(params(29)) * 256 + CInt(params(30))
events_overflow = CInt(params(31)) * 16777216 + CInt(params(32)) * 65536 +
CInt(params(33)) * 256 + CInt(params(34))
warning = CInt(params(35)) * 256 + CInt(params(36))
mu = CInt(params(37)) * 256 + CInt(params(38))
Label23.Text
Label22.Text
Label21.Text
Label27.Text
Label28.Text
Label31.Text
Label32.Text
Label36.Text
Label37.Text
Label38.Text
=
=
=
=
=
=
=
=
=
=
warning.ToString
Frequence.ToString + "Hz"
CPU_use.ToString
cmd_received.ToString
cmd_sent.ToString
eventscount.ToString
events_overflow
(Roll / 65535.0 * 360).ToString
(Pitch / 65535.0 * 360).ToString
(Yaw / 65535.0 * 360).ToString
''Label49.Text = params(1)
''SI MOTOR ENABLED
Label43.Text = motor1.ToString
Label44.Text = motor2.ToString
Label45.Text = motor3.ToString
Label46.Text = motor4.ToString
Panel15.Size
Panel16.Size
Panel17.Size
Panel18.Size
=
=
=
=
New
New
New
New
Point(motor1,
Point(motor2,
Point(motor3,
Point(motor4,
20)
20)
20)
20)
SetVal(Panel25, CPU_use, Brushes.LimeGreen)
SetVal(Panel24, Frequence / 65793, Brushes.LimeGreen)
SetVal(Panel1,
SetVal(Panel4,
SetVal(Panel5,
SetVal(Panel6,
motor1,
motor2,
motor3,
motor4,
Brushes.SteelBlue)
Brushes.SteelBlue)
Brushes.SteelBlue)
Brushes.SteelBlue)
Label49.Text = params(39).ToString ''39
Panel3.Width = params(46)
Panel9.Width = params(47)
Panel7.BackColor = Color.FromArgb(params(42) * 255, 0, 0)
Panel11.BackColor = Color.FromArgb(params(43) * 255, 0, 0)
Panel12.BackColor = Color.FromArgb(params(44) * 255, 0, 0)
Panel13.BackColor = Color.FromArgb(params(45) * 255, 0, 0)
Chart1.Series(0).Points.Add(params(48))
Chart2.Series(0).Points.Add(params(49))
If Chart1.Series(0).Points.Count > 100 Then
Chart1.Series(0).Points.RemoveAt(0)
Chart2.Series(0).Points.RemoveAt(0)
End If
End If
End If
Catch ex As TimeoutException
Catch ex As Exception
End Try
ANNEXE 5: Drone complet