Tutoriel · Débutant · 20 min de lecture

Construire un suiveur de ligne : capteurs et pilotage PID

Comment marche un suiveur de ligne : capteurs de réflectance, erreur de position pondérée, et boucle PID qui pilote un châssis différentiel en douceur.

Un robot suiveur de ligne est le premier robot autonome classique : il lit une ligne sous ses capteurs et braque pour rester dessus. En dessous, c’est un petit problème de commande — transformer des lectures de capteurs en correction de braquage — ce qui en fait une façon idéale d’apprendre la commande par rétroaction. Suivez dans le simulateur de suiveur de ligne.

Détecter la ligne

Un capteur de réflectance éclaire vers le bas en infrarouge et mesure combien se réfléchit. Une ligne sombre réfléchit moins que la surface claire autour, donc chaque capteur indique s’il est au-dessus de la ligne. Une barre de plusieurs capteurs en travers de l’avant du robot dit non seulement si vous êtes sur la ligne, mais où la ligne se trouve par rapport au centre.

Cinq capteurs sont un bon point de départ. Trois suffisent à basse vitesse ; les robots de compétition en utilisent souvent huit pour une résolution plus fine.

Cinq capteurs de ligne en rangée, chacun légendé avec un poids de moins deux à gauche à plus deux à droite en passant par zéro au milieu, et chacun affichant une lecture un ou zéro en dessous. Les deux capteurs aux poids zéro et plus un lisent un et sont en surbrillance ; les autres lisent zéro. La formule de moyenne pondérée calcule la somme des poids fois les lectures divisée par la somme des lectures, donnant zéro plus un sur deux égale plus zéro virgule cinq. Un avertissement indique que tout à zéro signifie ligne perdue, un cas différent et non une erreur nulle.
Cinq lectures tout ou rien se réduisent en un nombre signé sur lequel la boucle de braquage peut agir. Son signe est la direction, sa taille est l'écart. Télécharger le SVG

Transformer les lectures en un seul nombre d’erreur

Au lieu de réagir à chaque capteur, combinez-les en une seule position pondérée. Donnez à chaque capteur une valeur de position (négative à gauche, zéro au milieu, positive à droite) et prenez la moyenne pondérée par l’intensité avec laquelle chacun voit la ligne :

// positions: {-2, -1, 0, 1, 2}; readings: reflectance per sensor
float weightedPosition = sum(position[i] * reading[i]) / sum(reading[i]);

Le résultat est votre erreur : zéro signifie centré, négatif signifie la ligne est à gauche, positif à droite. Une erreur lisse et continue est exactement ce qu’il faut à un contrôleur.

Braquer avec un PID

Injectez cette erreur dans un contrôleur PID. La correction accélère une roue et ralentit l’autre, ramenant le robot vers la ligne :

float correction = kp * error + ki * integral + kd * derivative;
setMotorSpeeds(baseSpeed - correction, baseSpeed + correction);

Le gain proportionnel fait l’essentiel du braquage ; le gain dérivé amortit l’oscillation dans les courbes ; un petit terme intégral efface toute dérive constante. Si choisir les gains est nouveau pour vous, étudiez d’abord comment régler un contrôleur PID.

Corriger les pannes courantes

  • Oscillation autour de la ligne : gain proportionnel trop haut ou dérivé trop bas. Réduisez d’abord la vitesse, puis réglez à nouveau. Sur du vrai matériel, un long delay() dans la boucle ajoute un temps mort qui neutralise le terme dérivé.
  • Perte de la ligne dans les virages serrés : le robot dépasse son rythme d’actualisation des capteurs. Ralentissez, élargissez la barre, ou réutilisez la dernière direction connue de la ligne pour que le robot revienne au lieu de filer tout droit.
  • Dérive vers un côté : étalonnez les capteurs. Balayez-les sur la ligne et le fond, enregistrez les min et max de chacun, et normalisez les lectures en direct sur cette plage.

Toute la boucle, au même endroit

Les fragments ci-dessus ne deviennent un robot qui fonctionne que si la temporisation, l’étalonnage et le cas de perte de ligne sont tous traités ensemble :

const int N = 5;
const int PIN[N] = {A0, A1, A2, A3, A4};
const int POS[N] = {-2000, -1000, 0, 1000, 2000};   // weights, in the error's units

int minv[N], maxv[N];
long lastError = 0;
float integral = 0;
unsigned long lastMicros = 0;

const float KP = 0.10f, KI = 0.0f, KD = 1.6f;
const int   BASE = 140;                              // out of 255

void loop() {
  // 1. Real elapsed time — never assume the loop period.
  const unsigned long now = micros();
  const float dt = (now - lastMicros) * 1e-6f;
  if (dt < 0.002f) return;                           // cap the rate at 500 Hz
  lastMicros = now;

  // 2. Position from the weighted average.
  long weighted = 0, total = 0;
  for (int i = 0; i < N; i++) {
    const int v = normalized(analogRead(PIN[i]), i);
    if (v > 100) { weighted += (long)v * POS[i]; total += v; }
  }

  // 3. Line lost: hold the last error, do NOT zero it.
  const long error = (total == 0) ? lastError : (weighted / total);

  // 4. PID, with derivative on measurement and conditional integration.
  const float derivative = -(float)(error - lastError) / dt;
  const float candidate = integral + error * dt;
  const float raw = KP * error + KI * candidate + KD * derivative;
  const float correction = constrain(raw, -BASE, BASE);
  if (raw == correction) integral = candidate;

  lastError = error;

  // 5. Differential steering.
  setMotors(BASE - correction, BASE + correction);
}

Cinq points là-dedans séparent un robot qui suit une ligne d’un qui y arrive presque.

Le vrai dt. I et D divisent par le temps écoulé. Une boucle dont la durée change à mesure que vous ajoutez des fonctions — et elle changera — calcule les deux sur un mensonge si vous supposez une période constante.

Le seuil v > 100. Sans lui, cinq voies lisant chacune un petit plancher tirent la moyenne pondérée vers le centre quelle que soit la position de la ligne. Le symptôme est un robot qui suit bien une ligne marquée et mal une ligne pâle.

Conserver la dernière erreur en cas de perte de ligne. Zéro signifie « centré », donc un robot qui met à zéro fonce tout droit à pleine vitesse — loin de la ligne, qui était presque sûrement en courbe. Garder la dernière erreur le maintient en virage dans le sens où il tournait déjà.

Dérivée sur la mesure. Dériver l’erreur signifie qu’un changement de consigne produit un pic énorme. Ici la consigne vaut toujours zéro donc cela change peu, mais l’habitude vaut la peine d’être prise.

Intégration conditionnelle. Une fois la correction saturée, continuer d’accumuler l’intégrale ne fait que stocker un dépassement à rembourser plus tard.

Un ordre de réglage qui fonctionne

Ne réglez pas les trois à la fois. Posez KI = 0 et KD = 0, puis :

Étape Changement Observez Arrêtez quand
1 Montez KP depuis une petite valeur Le robot commence à réagir à la ligne Il suit une courbe douce, en louvoyant un peu
2 Continuez de monter KP Le louvoyage devient oscillation Il oscille régulièrement — notez cette valeur comme Ku
3 Mettez KP à environ la moitié de Ku La réponse est vive, dépassement présent —
4 Montez KD Le louvoyage s’amortit Les lignes droites sont propres ; plus de KD le rend nerveux
5 Seulement s’il suit constamment décentré, ajoutez un peu de KI L’écart se referme Il se referme — KI est rarement utile sur un suiveur de ligne
6 Montez la vitesse BASE Tout se dégrade ensemble Réglez à nouveau KP et KD à la nouvelle vitesse

L’étape 6 est celle qu’on oublie : les gains dépendent de la vitesse. Un réglage parfait à 140 de rapport cyclique oscillera à 220, parce que la même correction de braquage produit désormais un plus grand changement de cap par unité de temps. Prévoyez de régler à nouveau chaque fois que vous changez sensiblement la vitesse.

L’intégrale est vraiment rarement utile ici. La consigne d’un suiveur de ligne est zéro et les perturbations sont transitoires, donc il n’y a généralement pas d’écart permanent à refermer pour KI. Si vous voyez un écart persistant, suspectez un trim moteur inégal ou un capteur non étalonné avant de toucher au gain intégral.

La fréquence de boucle plafonne tout, discrètement

Fréquence de boucle Intervalle Distance parcourue à 0,4 m/s
20 Hz 50 ms 20 mm entre décisions
50 Hz 20 ms 8 mm
200 Hz 5 ms 2 mm
500 Hz 2 ms 0.8 mm

Un robot qui décide tous les 20 mm ne peut pas suivre une courbe serrée quels que soient les gains, et aucun terme dérivé ne récupère une information jamais échantillonnée.

Les coupables habituels sont faciles à éliminer : un delay() n’importe où dans la boucle, des Serial.print de plus de quelques valeurs à 9 600 bauds, et la lecture de capteurs que vous n’utilisez pas. Les retirer fait typiquement passer un premier montage de 50 Hz à plusieurs centaines, et le suivi s’améliore sans toucher un gain. Mesurez la fréquence avant de régler, pas après.

Dépannage

Symptôme Cause probable Correctif
Louvoie sur les lignes droites KP trop haut ou KD trop bas Divisez KP par deux, puis montez KD
Oscille de plus en plus vite KP au-dessus du gain ultime Réduisez jusqu’à stabiliser
Coupe les virages Trop vite pour l’anticipation des capteurs Ralentissez, ou avancez la barre
File tout droit quand il perd la ligne Erreur mise à zéro en perte de ligne Gardez la dernière erreur
Suit bien dans un sens, mal dans l’autre Barre pas de niveau, ou trim moteur Mettez la barre de niveau ; ajustez chaque moteur
Dérive vers un côté Capteurs non étalonnés, ou moteurs disparates Étalonnez par voie, puis ajustez
Parfait sur l’établi, divague au sol Hauteur de roulage changée 3–8 mm, montage rigide, réétalonnez
Marchait hier, mou aujourd’hui Tension de batterie Les gains sont des propriétés du système — réglez à nouveau ou bouclez la vitesse
Saccade à basse vitesse seulement Sous la bande morte des moteurs Mesurez le rapport de démarrage de chaque moteur et compensez
Rien ne répond à KD Un delay() dans la boucle Le temps mort neutralise entièrement l’action dérivée

Essayez vous-même

Dans le simulateur de suiveur de ligne, vous pouvez dessiner une piste, changer le nombre de capteurs et la vitesse, et régler les gains en regardant l’erreur et le temps au tour. Tout ce que vous apprenez sur l’erreur pondérée et la boucle de braquage se transpose directement à un robot Arduino.

Explorer le graphe

Fait partie de ces réalisations

Les projets et parcours d'apprentissage qui incluent ce tutoriel.

Pour aller plus loin

Références