Différences entre versions de « Pololu-Senseur-QTR-Utiliser »

De MCHobby - Wiki
Sauter à la navigation Sauter à la recherche
 
(11 versions intermédiaires par le même utilisateur non affichées)
Ligne 2 : Ligne 2 :
  
 
== Référence des commandes QTRSensor ==
 
== Référence des commandes QTRSensor ==
{{traduction}}
 
 
 
{{ambox|text=Les précédentes versions de la bibliothèque s'appelaient '''PololuQTRSensors''' mais il a été modifié en '''QTRSensors''' pour la différencier de la bibliothèque senseur QTR [https://www.pololu.com/docs/0J17 Arduino pour le Robot Orangutan et 3pi]. A part ce changement de nom de bibliothèque (et de classe), la nouvelle bibliothèque QTRSensors est fonctionnellement identique aux précédentes versions.}}
 
{{ambox|text=Les précédentes versions de la bibliothèque s'appelaient '''PololuQTRSensors''' mais il a été modifié en '''QTRSensors''' pour la différencier de la bibliothèque senseur QTR [https://www.pololu.com/docs/0J17 Arduino pour le Robot Orangutan et 3pi]. A part ce changement de nom de bibliothèque (et de classe), la nouvelle bibliothèque QTRSensors est fonctionnellement identique aux précédentes versions.}}
  
Ligne 128 : Ligne 126 :
 
<syntaxhighlight lang="C">Constructor: QTRSensorsAnalog()</syntaxhighlight>
 
<syntaxhighlight lang="C">Constructor: QTRSensorsAnalog()</syntaxhighlight>
  
This version of the constructor performs no initialization. If this constructor is used, the user must call init() before using the methods in this class.
+
Cette version du constructeur -destiné au senseurs de type analogique- n'effectue aucune initialisation. Si ce constructeur est utilisé, l'utilisateur doit appeler la méthode init() avant l'utilisation des autres méthodes de cette classe.
  
 
<syntaxhighlight lang="C">Constructor: QTRSensorsAnalog(unsigned char* analogPins, unsigned char numSensors, unsigned char numSamplesPerSensor = 4, unsigned char emitterPin = QTR_NO_EMITTER_PIN)</syntaxhighlight>
 
<syntaxhighlight lang="C">Constructor: QTRSensorsAnalog(unsigned char* analogPins, unsigned char numSensors, unsigned char numSamplesPerSensor = 4, unsigned char emitterPin = QTR_NO_EMITTER_PIN)</syntaxhighlight>
  
This constructor just calls {{fname|init()}}, below.
+
Ce constructeur fait juste un appel à {{fname|init()}}, tel que détaillé ci-dessous.
  
 
<syntaxhighlight lang="C">void init(unsigned char* analogPins, unsigned char numSensors, unsigned char numSamplesPerSensor = 4, unsigned char emitterPin = QTR_NO_EMITTER_PIN)</syntaxhighlight>
 
<syntaxhighlight lang="C">void init(unsigned char* analogPins, unsigned char numSensors, unsigned char numSamplesPerSensor = 4, unsigned char emitterPin = QTR_NO_EMITTER_PIN)</syntaxhighlight>
  
Initializes a QTR-A (analog) sensor array.
+
Initialise un ensemble de senseur QTR-A (analogique).
 
+
* '''pins''' est un tableau de broches qui contient les broches d'entrées analogiques utilisées pour chaque senseur. Par exemple, si les pins sont {0, 1, 7}, le premier senseur est l'entrée analogique 0, le deuxième senseur est l'entrée analogique 1 et le troisième senser est l'entrée analogique 7.
* The array ''pins'' should contain the Arduino analog input pin number for each sensor. For example, if pins is {0, 1, 7}, sensor 1 is on analog input 0, sensor 2 is on analog input 1, and sensor 3 is on analog input 7.
+
* '''numSensors''' indique la longueur du tableau ''analogPins'' (le nombre de senseurs QTR-A utilisés). La valeur de ''numSensors'' ne peut pas dépasser 16.
* ''numSensors'' specifies the length of the analogPins array (the number of QTR-A sensors you are using). numSensors must be no greater than 16.
+
* '''numSamplesPerSensor''' indique le nombre d'échantillonnages 10-bit à réaliser par canal/senseur lors de chaque lecture (échantillons dont la bibliothèque fait une moyenne). Le nombre total de conversion analogique-vers-digital réalisés est également au numSensors * numSamplesPerSensor.<br />accroître ce paramètre améliore la suppression du bruit mais au coût d'un temps de capture plus important (accroissement du temps d'échantillonnage total). Ce paramètre ne peut pas excéder la valeur de 64. Pololu recommande la valeur 4.
* ''numSamplesPerSensor'' indicates the number of 10-bit analog samples to average per channel (per sensor) for each reading. The total number of analog-to-digital conversions performed will be equal to numSensors times numSamplesPerSensor. Increasing this parameter increases noise suppression at the cost of sample rate. This parameter must not exceed 64. Recommended value: 4.
+
* '''emitterPin''' est la broche digitale Arduino qui permet de contrôler les LEDs infrarouges (allumées ou éteintes). Cette broches est optionnelle et elle n'existe que sur les modèles de QTR 8A et QTR 8RC des senseurs de ligne. Si une broche valide est spécifiée, alors les diodes infrarouge ne seront allumées que durant la lecture. Si la valeur {{fname|QTR_NO_EMITTER_PIN}} (255) est utilisée pour ce paramètre alors vous pouvez laisser la broches des diode Infrarouge déconnectée et les LEDs seront constamment alimentées.
* ''emitterPin'' is the Arduino digital pin that controls whether the IR LEDs are on or off. This pin is optional and only exists on the 8A and 8RC QTR sensor arrays. If a valid pin is specified, the emitters will only be turned on during a reading. If the value QTR_NO_EMITTER_PIN (255) is used, you can leave the emitter pin disconnected and the IR emitters will always be on.
 
  
 
== Note d'usage ==
 
== Note d'usage ==
 
=== Calibration ===
 
=== Calibration ===
This library allows you to use the '''calibrate()''' method to easily calibrate your sensors for the particular conditions it will encounter. Calibrating your sensors can lead to substantially more reliable sensor readings, which in turn can help simplify your code since. As such, we recommend you build a calibration phase into your application’s initialization routine. This can be as simple as a fixed duration over which you repeated call the '''calibrate()''' method. During this calibration phase, you will need to expose each of your reflectance sensors to the lightest and darkest readings they will encounter. For example, if you have made a line follower, you will want to slide it across the line during the calibration phase so the each sensor can get a reading of how dark the line is and how light the ground is. A sample calibration routine would be:
+
La bibliothèque propose la méthode '''calibrate()''' pour facilement calibrer les senseurs dans les conditions particulières qu'il va rencontrer. La calibration des senseurs peu permet de produire des lectures sensiblement plus fiable, ce qui aura pour effet de simplifier votre propre code par la suite. En conséquence, nous recommandons de réaliser une phase de calibration dans la routine d'initialisation de votre application.  
 +
 
 +
Ce peu simplement être réaliser durant une période de temps fixe durant laquelle vous effectuer des appels récurrents à la méthode '''calibrate()'''. Durant cette phase de calibration, vous devrez exposer chaque senseur de réflectance aux lectures les plus claires et foncées auxquels il seront exposés. Par exemple, si vous réalisez un suiveur de ligne, vous aurez besoin de le faire glisser progressivement au dessus de la ligne durant la phase de calibration de sorte que chaque senseur puisse avoir l'occasion de faire une lecture sur la surface la plus réflective (la plus claire) et de la surface la moins réfléchissante (la plus sombre).
 +
 
 +
Un exemple de routine de calibration peut être:
  
 
<syntaxhighlight lang="C">#include <QTRSensors.h>
 
<syntaxhighlight lang="C">#include <QTRSensors.h>
 
   
 
   
// create an object for your type of sensor (RC or Analog)
+
// créer un objet pour votre type de senseur (RC ou Analogique)
// in this example we have three sensors on analog inputs 0 - 2, a.k.a. digital pins 14 - 16
+
// Dans cet exemple, il s'agit de trois senseur sur les
 +
// entrées analogiques 0 à 2 (donc les broches digitales de
 +
// 14 à 16)
 
QTRSensorsRC qtr((char[]) {14, 15, 16}, 3);
 
QTRSensorsRC qtr((char[]) {14, 15, 16}, 3);
 
// QTRSensorsA qtr((char[]) {0, 1, 2}, 3);
 
// QTRSensorsA qtr((char[]) {0, 1, 2}, 3);
Ligne 156 : Ligne 159 :
 
void setup()
 
void setup()
 
{
 
{
   // optional: wait for some input from the user, such as  a button press
+
   // optionnel: attendre une action de l'utilisateur comme
 +
  // la pression d'un bouton.
 
   
 
   
   // then start calibration phase and move the sensors over both
+
   // Puis démarrer la phase de calibration et déplacez le
   // reflectance extremes they will encounter in your application:
+
  // senseurs au dessus des deux surfaces avec les reflectances
 +
   // opposées (extrêmes) que votre application rencontrera:
 
   int i;
 
   int i;
   for (i = 0; i < 250; i++)  // make the calibration take about 5 seconds
+
   for (i = 0; i < 250; i++)  // Réaliser une pendant ~5 secondes
 
   {
 
   {
 
     qtr.calibrate();
 
     qtr.calibrate();
Ligne 167 : Ligne 172 :
 
   }
 
   }
 
   
 
   
   // optional: signal that the calibration phase is now over and wait for further
+
   // optionnel: signaler que la phase de calibration est
   // input from the user, such as a button press
+
  // achevée et attendre une action de l'utilisateur
 +
   // (comme par exemple, la pression d'un bouton)
 
}</syntaxhighlight>
 
}</syntaxhighlight>
  
=== Lire les senseurs ===
+
=== Lectures avec les senseurs ===
This library gives you a number of different ways to read the sensors.
+
Cette bibliothèque offre différentes approches pour lire les senseurs:
 +
# Vous pouvez obtenir les valeurs brutes des senseurs en utilisant la méthode '''read()''', qui reçoit un argument optionnel qui vous permet d'effectuer une lecture  avec les diodes émettrices éteintes (note: l'extinction des diodes émettrices est uniquement possible pour les senseurs QTR-8x).
 +
# Vous pouvez obtenir les valeurs calibrées en utilisant la méthode '''readCalibrated()''', qui dispose également d'un argument optionnel vous permetant de faire une lecture avec les LED IR éteintes. Les valeurs calibrées ont toujours une valeur située entre 0 à 1000, où 0 étant considérée comme la plus réfléchissante (ex: la plus blanche) que la surface la plus réfléchissante rencontrée durant la phase de calibration -ET- 1000 étant la surtface la moins réfléchissante (ex: plus noir) que la surface la moins réfléchissante rencontrée durant la phase de calibration.
 +
# Les applications de détection de ligne peuvent demander la position de la ligne en utilisant la méthode '''readLine()'''. '''readline()''' accepte un premier argument booléen optionnel qui indique la couleur de la ligne (ligne noire sur fond blanc -ou- ligne blanche sur fond noir). Le second argument optionnel indique si les LEDs Infrarouge doivent être allumées ou éteintes durant la mesure. '''readLine()''' utilise les valeurs de calibration pour chaque senseur et retourne un entier qui indique la position supposée de la ligne. Si vous utilisez N senseurs, une valeur 0 retournée signifie que la ligne est sous le senseur 0 (ou au-delà du senseur 0) et retourne une valeur 1000x(N-1) si la ligne est sous le senseur N-1 (ou entre le senseur N-1 -dernier senseur) ou au-delà de ce dernier).
 +
 
 +
Lorsque vous déplacez le senseur perpendiculairement à la ligne, la position de la ligne changera de façon monotone entre 0 et 1000 * (N-1) ou vice versa. Changer de façon {{underline|monotone}} indique le la valeur croit (ou décroit) constamment dans le même sens.  
  
# You can request raw sensor values using the '''read()''' method, which takes an optional argument that lets you perform the read with the IR emitters turned off (note that turning the emitters off is only supported by the QTR-8x reflectance sensor arrays).
+
Cette position de ligne peut être utilisée pour dans un contrôle PID en boucle fermée.
# You can request calibrated sensor values using the '''readCalibrated()''' method, which also takes an optional argument that lets you perform the read with the IR emitters turned off. Calibrated sensor values will always range from 0 to 1000, with 0 being as or more reflective (i.e. whiter) than the most reflective surface encountered during calibration, and 1000 being as or less reflective (i.e. blacker) than the least reflective surface encountered during calibration.
 
# For line-detection applications, you can request the line location using the '''readLine()''' method, which takes as optional parameters a boolean that indicates whether the line is white on a black background or black on a white background, and a boolean that indicates whether the IR emitters should be on or off during the measurement. '''readLine()''' provides calibrated values for each sensor and returns an integer that tells you where it thinks the line is. If you are using N sensors, a returned value of 0 means it thinks the line is on or to the outside of sensor 0, and a returned value of 1000 * (N-1) means it thinks the line is on or to the outside of sensor N-1. As you slide your sensors across the line, the line position will change monotonically from 0 to 1000 * (N-1), or vice versa. This line-position value can be used for closed-loop PID control.
 
  
A sample routine to obtain the sensor values and perform rudimentary line following would be:
+
Une routine élémentaire permettant de suivre une ligne en utilisant les valeurs du senseur et effectuer le suivit de ligne pourrait ressembler à ceci:
  
 
<syntaxhighlight lang="C">void loop()
 
<syntaxhighlight lang="C">void loop()
 
{
 
{
 
   unsigned int sensors[3];
 
   unsigned int sensors[3];
   // get calibrated sensor values returned in the sensors array, along with the line position
+
   // obtenir les valeurs calibrées du senseur (dans un tableau de senseur)
   // position will range from 0 to 2000, with 1000 corresponding to the line over the middle
+
  // ainsi que la position de la ligne dans une gamme de valeur de
  // sensor.
+
   // 0 à 2000, avec 1000 retournée pour la ligne au milieu du senseur.
 
   int position = qtr.readLine(sensors);
 
   int position = qtr.readLine(sensors);
 
   
 
   
   // if all three sensors see very low reflectance, take some appropriate action for this
+
   // Si tous les senseurs ont une très faible réflectance, alors prendre
   // situation.
+
   // un action appropriée pour cette situation.
 
   if (sensors[0] > 750 && sensors[1] > 750 && sensors[2] > 750)
 
   if (sensors[0] > 750 && sensors[1] > 750 && sensors[2] > 750)
 
   {
 
   {
     // do something. Maybe this means we're at the edge of a course or about to fall off
+
     // Faire une action. Cela peu signifier que l'on est à la fin
     // a table, in which case, we might want to stop moving, back up, and turn around.
+
     // de la course ou peut être tombé de la table, auquel cas, nous
 +
    // nous devrions arrêter de bouger, se retourner, aller à la
 +
    // recherche de la ligne.
 
     return;
 
     return;
 
   }
 
   }
 
   
 
   
   // compute our "error" from the line position. We will make it so that the error is zero
+
   // Calculer l' "erreur" par rapport à la position de la ligne.
   // when the middle sensor is over the line, because this is our goal. Error will range from
+
   // Nous allons faire en sorte que l'erreur = 0 lorsque la ligne est
   // -1000 to +1000. If we have sensor 0 on the left and sensor 2 on the right,  a reading of
+
  // placée sous le milieu du senseur (parce que c'est notre but).
   // -1000 means that we see the line on the left and a reading of +1000 means we see the
+
   // L'erreur aura une valeur entre -1000 et +1000.  
   // line on the right.
+
  // Si nous avons le senseur 0 à gauche et le senseur 2 à droite alors
 +
   // une lecture d'erreur de -1000 signifie que nous voyons la ligne
 +
  //  sur la gauche par rapport au centre du senseur alors qu'une
 +
  // lecture de +1000 signifie que la ligne est sur la droite
 +
   // par rapport au centre.
 
   int error = position - 1000;
 
   int error = position - 1000;
 
   
 
   
   int leftMotorSpeed = 100;
+
  // Vitesse des moteurs
   int rightMotorSpeed = 100;
+
   int leftMotorSpeed = 100; // gauche
   if (error < -500)  // the line is on the left
+
   int rightMotorSpeed = 100;// droite
     leftMotorSpeed = 0; // turn left
+
   if (error < -500)  // ligne sur la gauche
   if (error > 500)  // the line is on the right
+
    // tourner à droite
     rightMotorSpeed = 0;  // turn right
+
    // -> arrêter moteur gauche
 +
     leftMotorSpeed = 0;  
 +
   if (error > 500)  // ligne sur la droite
 +
    // tourner à gauche
 +
    // -> arrêter le moteur droit
 +
     rightMotorSpeed = 0;   
 
   
 
   
   // set motor speeds using the two motor speed variables above
+
   // Fixer la vitesse des moteurs en utilisant
 +
  // les valeurs de leftMotorSpeed et rightMotorSpeed
 
}</syntaxhighlight>
 
}</syntaxhighlight>
  
 
=== Contrôle PID ===
 
=== Contrôle PID ===
The integer value returned by '''readLine()''' can be easily converted into a measure of your position error for line-following applications, as was demonstrated in the previous code sample. The function used to generate this position/error value is designed to be monotonic, which means the value will almost always change in the same direction as you sweep your sensors across the line. This makes it a great quantity to use for PID control.
+
TLa valeur entière retournée par '''readLine()''' peu facilement être convertie la mesure de notre erreur de position pour les applications de suivit de ligne, comme démontré dans le précédent exemple de code. La fonction utilisée pour générer cette valeur de position/error est conçue pour être monotonique, ce qui signifie que la valeur change toujours dans la même direction au fur et a mesure que que les senseurs croisent la ligne. Cela en fait une grandeur utile pour réaliser un contrôle PID (''Proportionnel Intégram Dérivé'').
  
Explaining the nature of PID control is beyond the scope of this document, but [http://en.wikipedia.org/wiki/PID_controller wikipedia has a very good article] (''anglais'') on the subject.
+
Expliquer la nature du contrôle PID va au delà des objectifs de ce document, mais [https://fr.wikipedia.org/wiki/R%C3%A9gulateur_PID wikipedia offre un bon article] sur le sujet.
  
The following code gives a very simple example of PD control (I find the integral PID term is usually not necessary when it comes to line following). The specific nature of the constants will be determined by your particular application, but you should note that the derivative constant ''Kd'' is usually much bigger than the proportional constant ''Kp''. This is because the derivative of the error is a much smaller quantity than the error itself, so in order to produce a meaningful correction it needs to be multiplied by a much larger constant.
+
Le code suivant est un exemple très simple de contrôleur PD pour suivit de ligne (le terme intégrale "I" d'un régulateur PID n'est habituellement pas nécessaire dans le cadre d'un suivit de ligne). Les valeurs des différentes constantes PID (ou PD dans le cas présent) est généralement spécifique à votre propre application mais d'une façon générale vous pourriez noter que la constante de dérivation ''Kd'' est habituellement beaucoup plus grande que la constante proportionnelle ''Kp''. C'est parce que la dérivée de l'erreur (quantification de la ''variation de l'erreur'') est beaucoup plus petite que l'erreur elle même. Par conséquent, pour produire une correction signification il est nécessaire de multiplier le terme dérivé par une constante beaucoup plus grande.
  
 
<syntaxhighlight lang="C">int lastError = 0;
 
<syntaxhighlight lang="C">int lastError = 0;
Ligne 226 : Ligne 247 :
 
{
 
{
 
   unsigned int sensors[3];
 
   unsigned int sensors[3];
   // get calibrated sensor values returned in the sensors array, along with the line position
+
   // Obtenir les valeurs calibrées retournées dans
   // position will range from 0 to 2000, with 1000 corresponding to the line over the middle
+
  // tableau de senseur accompagné de la position  
   // sensor
+
   // de la ligne (valeur entre 0 et 2000) avec
 +
  // 1000 correspondant à la ligne au milieu du
 +
   // senseur
 
   int position = qtr.readLine(sensors);
 
   int position = qtr.readLine(sensors);
 
   
 
   
   // compute our "error" from the line position. We will make it so that the error is zero when
+
   // Calculer notre "erreur" depuis la position  
   // the middle sensor is over the line, because this is our goal.  Error will range from
+
  // de la ligne. Nous allons la calculer de sorte
   // -1000 to +1000. If we have sensor 0 on the left and sensor 2 on the right, a reading of
+
   // que l'erreur égale 0 si la ligne est sous la
   // -1000 means that we see the line on the left and a reading of +1000 means we see the
+
  // position centrale du senseur (ce qui est notre
   // line on the right.
+
   // but). L'erreur aura une valeur entre -1000 et
 +
  // +1000. Si nous avons le senseur 0 à gauche et
 +
  // le senseur 2 à droite, alors une valeur lue
 +
   // de -1000 signifie que la ligne est vue sur la
 +
  // gauche du senseur. Une valeur de +1000  
 +
   // signifie que que nous voyons la ligne sur
 +
  // la droite du senseur.
 
   int error = position - 1000;
 
   int error = position - 1000;
 
   
 
   
   // set the motor speed based on proportional and derivative PID terms
+
   // Baser la vitesse des moteurs sur base des
   // KP is the a floating-point proportional constant (maybe start with a value around 0.1)
+
  // termes proportionnel et dérivés d'un PID.
   // KD is the floating-point derivative constant (maybe start with a value around 5)
+
   // KP est la constante proportionnelle (commencer
   // note that when doing PID, it's very important you get your signs right, or else the
+
  //    avec une valeur 0.1)
   // control loop will be unstable
+
   // KD est la constance dérivée (vous pouvez peut-être
 +
  //    commencer avec un valeur autour de 5)
 +
   // Note: lors d'un asservissement PID, il est très
 +
  //  important d'avoir le bon signe pour les valeurs
 +
  //  sinon, la boucle de contrôle sera totalement
 +
  //  instable.
 +
   // motorSpeed peut avoir une valeur positive ou
 +
  //  négative en fonction du sens de la direction
 +
  //  prise.
 
   int motorSpeed = KP * error + KD * (error - lastError);
 
   int motorSpeed = KP * error + KD * (error - lastError);
 
   lastError = error;
 
   lastError = error;
 
   
 
   
   // M1 and M2 are base motor speedsThat is to say, they are the speeds the motors should
+
   // M1 et M2 sont les vitesses par défaut des moteurs.
   // spin at if you are perfectly on the line with no error. If your motors are well matched,
+
  // Ce sont les vitesses adéquate des deux moteurs
   // M1 and M2 will be equal. When you start testing your PID loop, it might help to start with
+
  //    pour suivre -SANS ERREUR- une ligne parfaitement
   // small values for M1 and M2.  You can then increase the speed as you fine-tune your
+
  //    droite.   
   // PID constants KP and KD.
+
  // Si vos moteurs sont identiques alors M1 et M2 devraient
 +
  //    être égale (ou presque, chaque moteur étant un peu
 +
   //   différent de l'autre).  
 +
   // Il est préférable de commencer avec une petite valeur
 +
  //    pour M1 et M2 lorsque vous commencez à tester votre
 +
  //    boucle de contrôle. En effet, l'erreur (écart par
 +
  //    rapport à la ligne) devient rapidement plus grande
 +
  //    si la vitesse est plus élevée.
 +
  //    Après avoir atteint un résultat satisfaisant à
 +
  //    faible vitesse, vous pouvez augmenter les valeurs
 +
   //   de M1 et M2 et poursuivre l'affinement des valeurs
 +
   //   des constantes KP et KD de votre PID.
 
   int m1Speed = M1 + motorSpeed;
 
   int m1Speed = M1 + motorSpeed;
 
   int m2Speed = M2 - motorSpeed;
 
   int m2Speed = M2 - motorSpeed;
 
   
 
   
   // it might help to keep the speeds positive (this is optional)
+
   // Cela peut aider de maintenir une vitesse positive  
   // note that you might want to add a similiar line to keep the speeds from exceeding
+
  // pour les moteurs (ce point est optionnel).
   // any maximum allowed value
+
   // Un test similaire pourrait être ajouté pour garder
 +
   // la vitesse des moteurs en dessous du maximum
 +
  // autorisé.
 
   if (m1Speed < 0)
 
   if (m1Speed < 0)
 
     m1Speed = 0;
 
     m1Speed = 0;
Ligne 262 : Ligne 312 :
 
     m2Speed = 0;
 
     m2Speed = 0;
 
   
 
   
   // set motor speeds using the two motor speed variables above
+
   // Fixer la vitesse des mmoteur en utilisant les
 +
  // variables m1Speed et m2Speed
 
}</syntaxhighlight>
 
}</syntaxhighlight>
  
 
{{Pololu-Senseur-QTR-TRAILER}}
 
{{Pololu-Senseur-QTR-TRAILER}}

Version actuelle datée du 27 mai 2018 à 20:39

Référence des commandes QTRSensor

Pour les senseurs analogiques QTR-xA, vous aurez besoin d'instancier un objet QTRSensorsAnalog et pour les senseurs digitaux QTR-xRC vous aurez besoin d'instancier un objet QTRSensorsRC. A part les constructeurs, ces deux objets fournissent les mêmes méthodes pour lire les valeurs du senseur (les deux classes dérivent la même classe abstraite de base). Le bibliothèque offre un accès aux valeurs brutes du senseur ainsi que des fonctions de haut niveau incluant la calibration et suivit de ligne.

Cette section de la bibliothèque définit un objet pour chacun des deux types de senseur QTR avec la classe QTRSensorsAnalog destinée aux senseurs QTR-xA et une classe QTRSensorsRC destinée au senseurs QTR-xRC. En interne, la bibliothèque prend en charge les différences entre les QTR-xA et QTR-xRC offrant ainsi une interface commune pour les deux senseurs. La seule différence visible depuis l'extérieur, c'est le constructeur. Cela est possible car les deux classes dérive d'une classe commune QTRSensors, celle-ci offre une interface abstraite qui doit être implémentée dans les classe dérivées. La classe abstraite QTRSensors ne doit pas être instanciée.

Les classes QTRSensorsAnalog et QTRSensorsRC doivent être instanciées avant d'être utilisée. Cela permet à plusieurs senseurs de ligne QTR dêtre contrôlé indépendamment les uns des autres.

Pour la calibration, la mémoire est allouée en utilisant la fonction malloc(). Cela préserve la RAM: si les 8 senseurs sont calibrées avec l'émetteur activé et désactivé, un total de 64 octets (sur les 2048 disponibles) sont dédiés au stockage de la calibration. Cependant, pour une application utilisant uniquement 3 senseurs avec des émetteurs toujours actifs durant la lecteur alors seuls 6 octets seront nécessaires.

En interne, la bibliothèque utilise toutes des fonctions standards comme micros() pour la gestion du temps et analogRead() ou digitalRead() pour obtenir les valeurs du senseur. De sorte, cette bibliothèque devrait fonctionner avec tous les Arduino sans conflit avec d'autres bibliothèques.

Les fonctions

read()

void read(unsigned int *sensorValues, unsigned char readMode = QTR_EMITTERS_ON)

Lit les valeurs brutes des senseurs dans un tableau (array). Le tableau DOIT avoir la taille correspondant aux valeurs des senseurs spécifiés dans le constructeur. Les valeurs retournées sont une mesure de la réflectance (facteur de réflexion) en unités qui dépend du type de senseur utilisé. Une valeur plus élevée indique une réflectance inférieure (une surface noire ou le vide). Les senseurs QTR-xA retournera une valeur brute entre 0 et 1023. Les senseurs QTR-xRC retournera une valeur entre 0 et un argument timeout (exprimé en microsecondes, indiqué dans le constructeur avec 2000 par défaut).

Toutes les fonction qui lisent des valeurs prennent un argument readMode en paramètre. Celui-ci spécifie le type de lecture qui est effectué. Plusieurs options sont possibles:

  • QTR_EMITTERS_OFF indique que la lecture doit être effectuée sans allumer les LEDs infrarouge (IR), ce qui permet d'évaluer le niveau de lumière ambiante près du senseur;
  • QTR_EMITTERS_ON indique que les diodes émettrices doivent être activées durant la lecture, ce qui permet de lire la réflectance;
  • QTR_EMITTERS_ON_AND_OFF indique que la lecture doit être réaliser avec les deux états (allumé et éteint). La résultat retourné par l'option QTR_EMITTERS_ON_AND_OFF correspond à allumé + max – éteint, où "allumé" correspond à une lecture avec les LEDs IR allumée, "éteint" correspond à la lecture avec les LED IR éteinte et "max" la valeur maximale lue par le senseur.
    Cette option permet de réduire la quantité d'interférence provenant des variations de lumière ambiante.

Exemple d'utilisation:

unsigned int sensor_values[8];
sensors.read(sensor_values);

emittersOn()

void emittersOn()

Allume les LEDs IR (InfraRouge). Principalement utilisée par la méthode read() et appeler cette fonction avant ou après la lecture du senseur n'a aucun effet sur la lecture. Vous devriez utiliser cette fonction uniquement pour effectuer des tests.

Cette fonction en fait quelque-chose que si la broche émetteur IR (emitter) à été spécifiée dans le constructeur (donc une valeur différente de QTR_NO_EMITTER_PIN).

emittersOff()

void emittersOff()

Désactive les LEDs Infrarouges. Comme pour emitterOn(), cette fonction est utilisée par read() et appeler cette fonction avant ou après une lecture de senseur n'a aucun effet sur les lectures (mais peut être utilisée pour effectuer des tests).

calibrate()

void calibrate(unsigned char readMode = QTR_EMITTERS_ON)

Effectue une lecture des senseurs pour calibration. Les valeurs du senseur ne sont pas retournée; à la place, les valeurs maximales et minimales sont stockées en interne et utilisé par la méthode readCalibrated() (lecture calibrée). Vous pouvez accéder aux valeurs de calibration (ex: les lectures min et max du senseur) via les pointeurs des membres publiques calibratedMinimumOn, calibratedMaximumOn, calibratedMinimumOff et calibratedMaximumOff. Notez que ces pointeurs pointent vers des tableaux ayant une longueur numSensors tel que spécifié dans le constructeur. Ces tableaux ne seront alloués qu'après l'appel de calibrate(). Si la la calibration est uniquement réalisée avec les LED IR allumées alors les tableaux de calibration destinés aux valeurs off (éteinte) ne seront pas alloués.

readCalibrated()

void readCalibrated(unsigned int *sensorValues, unsigned char readMode = QTR_EMITTERS_ON)

Retourne la lecture des senseurs calibrés avec une valeur entre 0 et 1000, où 0 corresponds à une lecture inférieure ou égale à la valeur minimale obtenue par calibrate() et 1000 correspond à une lecture supérieure ou égale à la valeur maximale. Les valeurs de calibration sont stockées séparément pour chacun des senseurs infrarouges, par conséquent, la différence entre les senseurs est automatiquement prise en compte.

readLine()

unsigned int readLine(unsigned int *sensorValues, unsigned char readMode = QTR_EMITTERS_ON, unsigned char whiteLine = 0)

Fonctionne de la même façon que readCalibrated mais cette fonction est conçue pour le suivit de ligne:

  • Cette fonction retourne une estimation de la position de la ligne.
  • L'estimation est faite en utilisant la moyenne pondérée des senseurs par indice, indice du senseur multiplié par 1000. De sorte, une valeur 0 indique que la ligne est sous le senseur 0 (ou qu'il a été sous le senseur 0 juste avant d'être perdu), une valeur de 1000 indique que le senseur est sous le senseur 1, 2000 indique sous le senseur 2, etc.

Une valeur intermédiaire indique indique que la ligne est entre deux senseurs. La formule est:

 0*value0 + 1000*value1 + 2000*value2 + ...
--------------------------------------------
     value0  +  value1  +  value2 + ...

Aussi longtemps que les senseurs ne sont pas trop espacés par rapport à la ligne alors cette fonction est conçue pour retourner une valeur monotone. Une fonction monotone reste toujours croissante ou décroissante (son sens de variation est constant, voir l'article Fonction monotone sur wikipedia.fr).

Une valeur monotone est une entrée idéale pour réaliser un asservissement PID en boucle fermée.

De surcroît, cette méthode se souvient où elle a vu la ligne la dernière fois, de sorte que si la ligne est perdue sur la gauche ou la droite, la position de la ligne sera indiquée du bon côté afin de diriger le robot dans la bonne direction pour rattraper la ligne.

Par exemple, si le senseur 4 est le senseur le plus à droite et que vous quittez la ligne par la gauche (dont la ligne est quelque par plus loin sur la droite), la valeur lue augmente progressivement jusque 4000 puis continuera à retourner 4000 lorsque le robot aura quitté la ligne.

Par défaut, cette fonction par du principe que la ligne est noir (valeur élevée) entourée de blanc (valeur faible). Si vous désirez utiliser des lignes blanches sur fonds noir alors vous pouvez placer le second argument whiteLine à true. Dans ce cas, chaque senseur sera remplacé par la valeur maximale du senseur moins sa valeur actuelle (avant le calcul de moyenne).

calibratedMinimumOn

unsigned int* calibratedMinimumOn

Les valeurs minimales de calibration pour chaque senseur (avec diode IR allumée). Les pointeurs ne sont pas alloués (et placés à 0) jusqu'à ce la méthode calibrate() soit appelée. Ensuite le pointeur pointera vers un tableau qui aura exactement la taille du nombre de senseur. En fonction du readMode utiliser avec calibrate() seules les valeurs On ou Off seront allouées (en fonction des besoins).

Cette variable et les suivantes sont rendues publiques pour que vous puissiez réaliser vos propres calculs et réaliser des t^aches comme sauvegarde en EEPROM, réaliser un contrôle sur les valeurs, etc

calibratedMaximumOn

unsigned int* calibratedMaximumOn

Les valeurs maximales de calibration mesurée pour chaque senseur (avec les diode IR allumées).

calibratedMinimumOff

unsigned int* calibratedMinimumOff

Les valeurs minimales de calibration pour chaque senseur, avec les diodes émettrices eteintes.

calibratedMaximumOff

unsigned int* calibratedMaximumOff

Les valeurs de calibrations maximales mesurées pour chaque senseur (avec les diodes IR éteintes).

~QTRSensors() - descructeur

Destructor: ~QTRSensors()

Destructeur de la classe QTRSensors qui libère la mémoire allouée par les tableaux de calibration.

QTRSensorsRC() - constructeur

Constructor: QTRSensorsRC()

Cette version du constructeur n'effectue aucune initialisation. Si ce constructeur est utiliser alors le code doit également appeler init() avant d'utiliser les méthodes de la classe.

Constructor: QTRSensorsRC(unsigned char* digitalPins, unsigned char numSensors, unsigned int timeout = 2000, unsigned char emitterPin = QTR_NO_EMITTER_PIN);

Cette version du constructeur appelle la méthode init() décrite ci-dessous.

void QTRSensorsRC::init(unsigned char* digitalPins, unsigned char numSensors, unsigned int timeout = 2000, unsigned char emitterPin = QTR_NO_EMITTER_PIN)

Initialise le tableau de senseur QTR-RC (digital).

  • Le tableau digitalPins doit contenir les broches digitales Arduino correspondant à chaque senseur.
  • numSensors spécifie la longueur du tableau digitalPins (le nombre de senseur QTR-RC que vous utilisez). numSensors ne doit pas excéder 16 positions.
  • timeout spécifie le nombre de microsecondes au-delà desquels la lecture du senseur sera considérée comme complètement noir! Cela signifie, si la longueur de l'impulsion pour une broche excède le timeout, la mesure de l'impulsion cesse et la lecture pour cette broche considère que le résultat est complètement noir. Pololu recommande un timeout entre 1000 et 3000 µs (0.001 et 0.003 secondes) dépendant de facteurs comme la hauteur du senseur et la lumière ambiante. Cela permet d'écourter le cycle de lecture des senseurs tout en maintenant une mesure utile de la reflectande.
  • emitterPin est la broche digitale Arduino qui contrôle l'allumage et l'extinction des LEDs Infrarouge. Cette broche est optionnelle, broche qui n'existe que sur les détecteurs de lignes QTR 8A et 8RC QTR. Si une broche valide est spécifié alors les LED InfraRouges ne sont allumée que durant les opérations de lecture. Si la valeur QTR_NO_EMITTER_PIN (255) est utilisé lors de l'appel alors vous pouvez laisser le broche emitter déconnectée sur votre senseur de ligne afin que les LED Infrarouge restent toujours allumées.

QTRSensorsAnalog() - constructeur

Constructor: QTRSensorsAnalog()

Cette version du constructeur -destiné au senseurs de type analogique- n'effectue aucune initialisation. Si ce constructeur est utilisé, l'utilisateur doit appeler la méthode init() avant l'utilisation des autres méthodes de cette classe.

Constructor: QTRSensorsAnalog(unsigned char* analogPins, unsigned char numSensors, unsigned char numSamplesPerSensor = 4, unsigned char emitterPin = QTR_NO_EMITTER_PIN)

Ce constructeur fait juste un appel à init(), tel que détaillé ci-dessous.

void init(unsigned char* analogPins, unsigned char numSensors, unsigned char numSamplesPerSensor = 4, unsigned char emitterPin = QTR_NO_EMITTER_PIN)

Initialise un ensemble de senseur QTR-A (analogique).

  • pins est un tableau de broches qui contient les broches d'entrées analogiques utilisées pour chaque senseur. Par exemple, si les pins sont {0, 1, 7}, le premier senseur est l'entrée analogique 0, le deuxième senseur est l'entrée analogique 1 et le troisième senser est l'entrée analogique 7.
  • numSensors indique la longueur du tableau analogPins (le nombre de senseurs QTR-A utilisés). La valeur de numSensors ne peut pas dépasser 16.
  • numSamplesPerSensor indique le nombre d'échantillonnages 10-bit à réaliser par canal/senseur lors de chaque lecture (échantillons dont la bibliothèque fait une moyenne). Le nombre total de conversion analogique-vers-digital réalisés est également au numSensors * numSamplesPerSensor.
    accroître ce paramètre améliore la suppression du bruit mais au coût d'un temps de capture plus important (accroissement du temps d'échantillonnage total). Ce paramètre ne peut pas excéder la valeur de 64. Pololu recommande la valeur 4.
  • emitterPin est la broche digitale Arduino qui permet de contrôler les LEDs infrarouges (allumées ou éteintes). Cette broches est optionnelle et elle n'existe que sur les modèles de QTR 8A et QTR 8RC des senseurs de ligne. Si une broche valide est spécifiée, alors les diodes infrarouge ne seront allumées que durant la lecture. Si la valeur QTR_NO_EMITTER_PIN (255) est utilisée pour ce paramètre alors vous pouvez laisser la broches des diode Infrarouge déconnectée et les LEDs seront constamment alimentées.

Note d'usage

Calibration

La bibliothèque propose la méthode calibrate() pour facilement calibrer les senseurs dans les conditions particulières qu'il va rencontrer. La calibration des senseurs peu permet de produire des lectures sensiblement plus fiable, ce qui aura pour effet de simplifier votre propre code par la suite. En conséquence, nous recommandons de réaliser une phase de calibration dans la routine d'initialisation de votre application.

Ce peu simplement être réaliser durant une période de temps fixe durant laquelle vous effectuer des appels récurrents à la méthode calibrate(). Durant cette phase de calibration, vous devrez exposer chaque senseur de réflectance aux lectures les plus claires et foncées auxquels il seront exposés. Par exemple, si vous réalisez un suiveur de ligne, vous aurez besoin de le faire glisser progressivement au dessus de la ligne durant la phase de calibration de sorte que chaque senseur puisse avoir l'occasion de faire une lecture sur la surface la plus réflective (la plus claire) et de la surface la moins réfléchissante (la plus sombre).

Un exemple de routine de calibration peut être:

#include <QTRSensors.h>
 
// créer un objet pour votre type de senseur (RC ou Analogique)
// Dans cet exemple, il s'agit de trois senseur sur les
// entrées analogiques 0 à 2 (donc les broches digitales de 
// 14 à 16)
QTRSensorsRC qtr((char[]) {14, 15, 16}, 3);
// QTRSensorsA qtr((char[]) {0, 1, 2}, 3);
 
void setup()
{
  // optionnel: attendre une action de l'utilisateur comme
  // la pression d'un bouton.
 
  // Puis démarrer la phase de calibration et déplacez le 
  // senseurs au dessus des deux surfaces avec les reflectances
  // opposées (extrêmes) que votre application rencontrera:
  int i;
  for (i = 0; i < 250; i++)  // Réaliser une pendant ~5 secondes
  {
    qtr.calibrate();
    delay(20);
  }
 
  // optionnel: signaler que la phase de calibration est 
  // achevée et attendre une action de l'utilisateur
  // (comme par exemple, la pression d'un bouton)
}

Lectures avec les senseurs

Cette bibliothèque offre différentes approches pour lire les senseurs:

  1. Vous pouvez obtenir les valeurs brutes des senseurs en utilisant la méthode read(), qui reçoit un argument optionnel qui vous permet d'effectuer une lecture avec les diodes émettrices éteintes (note: l'extinction des diodes émettrices est uniquement possible pour les senseurs QTR-8x).
  2. Vous pouvez obtenir les valeurs calibrées en utilisant la méthode readCalibrated(), qui dispose également d'un argument optionnel vous permetant de faire une lecture avec les LED IR éteintes. Les valeurs calibrées ont toujours une valeur située entre 0 à 1000, où 0 étant considérée comme la plus réfléchissante (ex: la plus blanche) que la surface la plus réfléchissante rencontrée durant la phase de calibration -ET- 1000 étant la surtface la moins réfléchissante (ex: plus noir) que la surface la moins réfléchissante rencontrée durant la phase de calibration.
  3. Les applications de détection de ligne peuvent demander la position de la ligne en utilisant la méthode readLine(). readline() accepte un premier argument booléen optionnel qui indique la couleur de la ligne (ligne noire sur fond blanc -ou- ligne blanche sur fond noir). Le second argument optionnel indique si les LEDs Infrarouge doivent être allumées ou éteintes durant la mesure. readLine() utilise les valeurs de calibration pour chaque senseur et retourne un entier qui indique la position supposée de la ligne. Si vous utilisez N senseurs, une valeur 0 retournée signifie que la ligne est sous le senseur 0 (ou au-delà du senseur 0) et retourne une valeur 1000x(N-1) si la ligne est sous le senseur N-1 (ou entre le senseur N-1 -dernier senseur) ou au-delà de ce dernier).

Lorsque vous déplacez le senseur perpendiculairement à la ligne, la position de la ligne changera de façon monotone entre 0 et 1000 * (N-1) ou vice versa. Changer de façon monotone indique le la valeur croit (ou décroit) constamment dans le même sens.

Cette position de ligne peut être utilisée pour dans un contrôle PID en boucle fermée.

Une routine élémentaire permettant de suivre une ligne en utilisant les valeurs du senseur et effectuer le suivit de ligne pourrait ressembler à ceci:

void loop()
{
  unsigned int sensors[3];
  // obtenir les valeurs calibrées du senseur (dans un tableau de senseur)
  // ainsi que la position de la ligne dans une gamme de valeur de
  // 0 à 2000, avec 1000 retournée pour la ligne au milieu du senseur.
  int position = qtr.readLine(sensors);
 
  // Si tous les senseurs ont une très faible réflectance, alors prendre
  // un action appropriée pour cette situation.
  if (sensors[0] > 750 && sensors[1] > 750 && sensors[2] > 750)
  {
    // Faire une action. Cela peu signifier que l'on est à la fin
    // de la course ou peut être tombé de la table, auquel cas, nous
    // nous devrions arrêter de bouger, se retourner, aller à la
    // recherche de la ligne.
    return;
  }
 
  // Calculer l' "erreur" par rapport à la position de la ligne.
  // Nous allons faire en sorte que l'erreur = 0 lorsque la ligne est 
  // placée sous le milieu du senseur (parce que c'est notre but).
  // L'erreur aura une valeur entre -1000 et +1000. 
  // Si nous avons le senseur 0 à gauche et le senseur 2 à droite alors
  // une lecture d'erreur de -1000 signifie que nous voyons la ligne 
  //  sur la gauche par rapport au centre du senseur alors qu'une 
  // lecture de +1000 signifie que la ligne est sur la droite 
  // par rapport au centre.
  int error = position - 1000;
 
  // Vitesse des moteurs
  int leftMotorSpeed = 100; // gauche
  int rightMotorSpeed = 100;// droite
  if (error < -500)  // ligne sur la gauche
    // tourner à droite
    // -> arrêter moteur gauche
    leftMotorSpeed = 0;   
  if (error > 500)  // ligne sur la droite
    // tourner à gauche
    // -> arrêter le moteur droit
    rightMotorSpeed = 0;  
 
  // Fixer la vitesse des moteurs en utilisant 
  // les valeurs de leftMotorSpeed et rightMotorSpeed
}

Contrôle PID

TLa valeur entière retournée par readLine() peu facilement être convertie la mesure de notre erreur de position pour les applications de suivit de ligne, comme démontré dans le précédent exemple de code. La fonction utilisée pour générer cette valeur de position/error est conçue pour être monotonique, ce qui signifie que la valeur change toujours dans la même direction au fur et a mesure que que les senseurs croisent la ligne. Cela en fait une grandeur utile pour réaliser un contrôle PID (Proportionnel Intégram Dérivé).

Expliquer la nature du contrôle PID va au delà des objectifs de ce document, mais wikipedia offre un bon article sur le sujet.

Le code suivant est un exemple très simple de contrôleur PD pour suivit de ligne (le terme intégrale "I" d'un régulateur PID n'est habituellement pas nécessaire dans le cadre d'un suivit de ligne). Les valeurs des différentes constantes PID (ou PD dans le cas présent) est généralement spécifique à votre propre application mais d'une façon générale vous pourriez noter que la constante de dérivation Kd est habituellement beaucoup plus grande que la constante proportionnelle Kp. C'est parce que la dérivée de l'erreur (quantification de la variation de l'erreur) est beaucoup plus petite que l'erreur elle même. Par conséquent, pour produire une correction signification il est nécessaire de multiplier le terme dérivé par une constante beaucoup plus grande.

int lastError = 0;
 
void loop()
{
  unsigned int sensors[3];
  // Obtenir les valeurs calibrées retournées dans 
  // tableau de senseur accompagné de la position 
  // de la ligne (valeur entre 0 et 2000) avec
  // 1000 correspondant à la ligne au milieu du 
  // senseur
  int position = qtr.readLine(sensors);
 
  // Calculer notre "erreur" depuis la position 
  // de la ligne. Nous allons la calculer de sorte
  // que l'erreur égale 0 si la ligne est sous la 
  // position centrale du senseur (ce qui est notre
  // but). L'erreur aura une valeur entre -1000 et 
  // +1000. Si nous avons le senseur 0 à gauche et
  // le senseur 2 à droite, alors une valeur lue
  // de -1000 signifie que la ligne est vue sur la 
  // gauche du senseur. Une valeur de +1000 
  // signifie que que nous voyons la ligne sur
  // la droite du senseur.
  int error = position - 1000;
 
  // Baser la vitesse des moteurs sur base des 
  // termes proportionnel et dérivés d'un PID.
  // KP est la constante proportionnelle (commencer
  //    avec une valeur 0.1)
  // KD est la constance dérivée (vous pouvez peut-être
  //    commencer avec un valeur autour de 5)
  // Note: lors d'un asservissement PID, il est très
  //   important d'avoir le bon signe pour les valeurs
  //   sinon, la boucle de contrôle sera totalement 
  //   instable.
  // motorSpeed peut avoir une valeur positive ou
  //   négative en fonction du sens de la direction 
  //   prise. 
  int motorSpeed = KP * error + KD * (error - lastError);
  lastError = error;
 
  // M1 et M2 sont les vitesses par défaut des moteurs.
  // Ce sont les vitesses adéquate des deux moteurs 
  //    pour suivre -SANS ERREUR- une ligne parfaitement
  //    droite.  
  // Si vos moteurs sont identiques alors M1 et M2 devraient
  //    être égale (ou presque, chaque moteur étant un peu 
  //    différent de l'autre). 
  // Il est préférable de commencer avec une petite valeur
  //    pour M1 et M2 lorsque vous commencez à tester votre 
  //    boucle de contrôle. En effet, l'erreur (écart par
  //    rapport à la ligne) devient rapidement plus grande
  //    si la vitesse est plus élevée.
  //    Après avoir atteint un résultat satisfaisant à 
  //    faible vitesse, vous pouvez augmenter les valeurs
  //    de M1 et M2 et poursuivre l'affinement des valeurs
  //    des constantes KP et KD de votre PID.
  int m1Speed = M1 + motorSpeed;
  int m2Speed = M2 - motorSpeed;
 
  // Cela peut aider de maintenir une vitesse positive 
  // pour les moteurs (ce point est optionnel).
  // Un test similaire pourrait être ajouté pour garder
  // la vitesse des moteurs en dessous du maximum
  // autorisé.
  if (m1Speed < 0)
    m1Speed = 0;
  if (m2Speed < 0)
    m2Speed = 0;
 
  // Fixer la vitesse des mmoteur en utilisant les 
  // variables m1Speed et m2Speed
}

Basé sur "Arduino Library for the Pololu QTR Reflectance Sensors" de Pololu (www.pololu.com/docs/0J19/1) - Traduit en Français par shop.mchobby.be CC-BY-SA pour la traduction
Toute copie doit contenir ce crédit, lien vers cette page et la section "crédit de traduction". Traduit avec l'autorisation expresse de Pololu (www.pololu.com)

Based on "Arduino Library for the Pololu QTR Reflectance Sensors" from Pololu (www.pololu.com/docs/0J19/1) - Translated to French by shop.mchobby.be CC-BY-SA for the translation
Copies must includes this credit, link to this page and the section "crédit de traduction" (translation credit). Translated with the Pololu's authorization (www.pololu.com)