Tutorial · Iniciante · 20 min de leitura

Construa um robô seguidor de linha: dos sensores ao controle PID

Como funciona um robô seguidor de linha: sensores infravermelhos, erro ponderado e um laço PID que comanda a tração diferencial com suavidade.

Um robô seguidor de linha é o primeiro robô autônomo clássico: ele lê uma linha sob seus sensores e comanda a direção para permanecer sobre ela. Por baixo, há um pequeno problema de controle — transformar leituras dos sensores em uma correção de direção — o que torna essa uma ótima maneira de aprender controle por realimentação. Acompanhe no simulador de robô seguidor de linha.

Detecte a linha

Um sensor de reflectância infravermelha projeta luz infravermelha para baixo e mede quanto dela retorna. Uma linha escura reflete menos que a superfície clara ao redor, portanto cada sensor informa se está sobre a linha. Uma barra com vários sensores na frente do robô informa não apenas se ele está sobre a linha, mas também onde a linha está em relação ao centro.

Cinco sensores são um bom ponto de partida. Três funcionam em baixa velocidade; robôs de competição costumam usar oito para obter maior resolução.

Cinco sensores de linha em uma fileira, cada um rotulado com um peso de menos dois à esquerda, passando por zero no centro, até mais dois à direita, e cada um mostrando abaixo uma leitura de um ou zero. Os dois sensores com pesos zero e mais um fazem uma leitura um e são destacados; os demais fazem leituras zero. A fórmula da média ponderada calcula a soma dos pesos multiplicados pelas leituras dividida pela soma das leituras, resultando em zero mais um sobre dois, ou +0,5. Um aviso informa que todos os valores zero significam que a linha foi perdida, que é um caso diferente e não um erro zero.
Cinco leituras de um e zero se reduzem a um único número com sinal sobre o qual o laço de controle pode atuar. O sinal indica a direção, e o valor indica o quanto o robô está desviado. Baixar SVG

Transforme as leituras em um único número de erro

Em vez de reagir a sensores individuais, combine-os em uma única posição ponderada. Dê a cada sensor um valor de posição (negativo à esquerda, zero no centro, positivo à direita) e calcule a média ponderada pela intensidade com que cada sensor vê a linha:

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

O resultado é seu erro: zero significa que a linha está centralizada, um valor negativo significa que ela está à esquerda e um valor positivo, à direita. Um sinal contínuo e suave é exatamente o que um controlador precisa.

Controle a direção com PID

Alimente esse erro a um controlador PID. A correção acelera uma roda e desacelera a outra, fazendo o robô voltar para a linha:

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

O ganho proporcional realiza a maior parte da correção de direção; o ganho derivativo amortece as oscilações nas curvas; e um pequeno termo integral elimina desvios constantes. Se você está começando a escolher ganhos, veja primeiro como ajustar um controlador PID.

Corrija as falhas comuns

  • Oscilação em torno da linha: o ganho proporcional está alto demais ou o derivativo baixo demais. Reduza primeiro a velocidade e depois reajuste. Na implementação real, um delay() longo no laço adiciona tempo morto que prejudica o termo derivativo.
  • Perda da linha em curvas fechadas: o robô percorre uma distância maior do que seus sensores conseguem atualizar. Diminua a velocidade, aumente a largura da barra ou reutilize a última direção conhecida da linha para que o robô vire de volta, em vez de seguir em frente e sair da pista.
  • Desvio para um lado: calibre os sensores. Passe-os sobre a linha e o fundo, registre o valor mínimo e o máximo de cada sensor e normalize as leituras em tempo real em relação a essa faixa.

Todo o laço em um único lugar

Os fragmentos acima só formam um robô funcional quando a temporização, a calibração e o caso de perda de linha são tratados em conjunto:

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);
}

Cinco detalhes fazem a diferença entre um robô que segue uma linha e outro que quase segue.

dt real. Tanto I quanto D dividem pelo tempo decorrido. Um laço cuja duração muda à medida que você adiciona recursos — e vai mudar — calcularia os dois termos com base em uma informação falsa se assumisse um período constante.

O limiar v > 100. Sem ele, cinco canais que registram um pequeno valor de fundo arrastam a média ponderada para o centro, independentemente de onde a linha esteja. O sintoma é um robô que acompanha bem uma linha forte, mas se comporta mal em uma linha apagada.

Manter o último erro ao perder a linha. Zero significa “centralizado”, então um robô que zera o erro segue em frente à velocidade máxima — afastando-se da linha, que quase certamente estava curvando. Manter o último erro faz o robô continuar virando na direção em que já estava virando.

Derivativo sobre a medição. Derivar o erro faz uma alteração na referência produzir um pico enorme. Aqui a referência é sempre zero, então a diferença é pequena, mas vale a pena criar o hábito.

Integração condicional. Quando a correção satura, continuar acumulando o termo integral apenas armazena um sobrepasso que será compensado mais tarde.

Uma ordem de ajuste que funciona

Não ajuste os três ao mesmo tempo. Defina KI = 0 e KD = 0 e então:

Etapa Mudança Observe Pare quando
1 Aumente KP a partir de um valor pequeno O robô começa a responder à linha Ele acompanha uma curva suave, com uma pequena serpentação
2 Continue aumentando KP A serpentação cresce e se transforma em oscilação Ele oscila de forma constante — anote este valor como Ku
3 Defina KP como aproximadamente metade de Ku A resposta é rápida e há sobrepasso —
4 Aumente KD A serpentação é amortecida As retas ficam estáveis; aumentar mais KD torna o robô agitado
5 Somente se ele continuar acompanhando a linha fora do centro, adicione um pouco de KI O robô se recentraliza Ele se recentraliza — KI raramente é necessário em um robô seguidor de linha
6 Aumente a velocidade BASE Todo o comportamento se degrada Reajuste KP e KD na nova velocidade

A etapa 6 é a que as pessoas esquecem: os ganhos dependem da velocidade. Um ajuste perfeito com ciclo de trabalho de 140 oscila com 220, porque a mesma correção de direção agora produz uma mudança maior na orientação por unidade de tempo. Espere reajustar sempre que mudar a velocidade de forma significativa.

O termo integral realmente raramente é útil aqui. A referência de um robô seguidor de linha é zero e as perturbações são transitórias, portanto normalmente não existe um desvio constante que KI precise eliminar. Se você vir um desvio persistente, suspeite de ajuste desigual dos motores ou sensor não calibrado antes de aumentar o ganho integral.

A taxa do laço limita tudo silenciosamente

Taxa do laço Intervalo Distância percorrida a 0,4 m/s
20 Hz 50 ms 20 mm entre decisões
50 Hz 20 ms 8 mm
200 Hz 5 ms 2 mm
500 Hz 2 ms 0,8 mm

Um robô que decide a cada 20 mm não consegue acompanhar uma curva com precisão, por mais bem ajustados que estejam os ganhos, e nenhum termo derivativo recupera informação que nunca foi amostrada.

As causas habituais são fáceis de eliminar: um delay() em qualquer lugar do laço, Serial.print de mais de alguns valores a 9600 baud e a leitura de sensores que você não usa. Removê-las normalmente leva uma primeira montagem de 50 Hz para várias centenas de Hz, e o acompanhamento melhora sem alterar nenhum ganho. Meça a taxa antes de ajustar, não depois.

Solução de problemas

Sintoma Causa provável Correção
Serpenteia nas retas KP alto demais ou KD baixo demais Divida KP pela metade e depois aumente KD
Oscila cada vez mais rápido KP acima do ganho-limite Reduza até estabilizar
Corta curvas Velocidade alta demais para o avanço do sensor Diminua a velocidade ou mova a matriz para a frente
Sai da pista ao perder a linha Erro zerado ao perder a linha Mantenha o último erro
Acompanha bem em uma direção e mal na outra Matriz não nivelada ou motores desajustados Nivele a matriz e ajuste cada motor
Desvia para um lado Sensores não calibrados ou motores diferentes Calibre cada canal e depois faça o ajuste
Funciona na bancada, mas vagueia no chão A distância do sensor ao piso mudou Use 3–8 mm, suporte rígido e recalibre
Funcionou ontem, mas está lento hoje Queda de tensão da bateria Os ganhos são propriedades do sistema — reajuste ou feche um laço de velocidade
Vibra apenas em baixa velocidade Abaixo da zona morta dos motores Meça o ciclo de trabalho de partida de cada motor e compense
Nada responde a KD Há um delay() no laço O tempo morto prejudica completamente a ação derivativa

Tente por sua conta

No simulador de robô seguidor de linha você pode desenhar uma pista, alterar a quantidade de sensores e a velocidade e ajustar os ganhos enquanto observa o erro e o tempo de uma volta completa. Tudo o que você aprender sobre o erro ponderado e o laço de controle se aplica diretamente a um robô Arduino.

Explore o grafo

Faz parte destas montagens

Projetos e caminhos de aprendizagem que incluem este tutorial.

Leitura complementar

Referências