← Sair
Robô Seguidor de Linha (PID)
Modo Bancada · Plugada

Robô Seguidor de Linha (PID)

Robô Seguidor de Linha (PID)

🎯 O que vamos aprender
  • Construir e programar um robô que segue uma linha sozinho: ele lê uma régua de sensores de refletância e corrige a trajetória em tempo real com um controle proporcional-derivativo (PID) — desenvolvendo algoritmo, calibração de sensores e depuração.
🧰 O que vamos usar

1× Arduino Uno 1× Ponte H (L298N) 1× Régua de sensores de linha (QTR / IR, 6 canais) 2× Motor DC com caixa de redução 1× Roda + roda boba (esfera) 1× Bateria 9V (ou pack de pilhas) 1× Chassi (impressão 3D) 1× Pista: fita isolante preta sobre fundo branco 1× Jumpers, parafusos e interruptor

🪜 Passo 1 de 5

Introdução / Contextualização

Apresente o desafio: um robô que segue uma linha preta sozinho, sem controle remoto. Levante a pergunta: como ele 'enxerga' a linha e se corrige em tempo real?

🪜 Passo 2 de 5

Montagem do Circuito / Hardware

1) Montar o chassi 3D, fixar os 2 motores e as rodas. 2) Ligar a Ponte H L298N ao Arduino (motor A: D9/D7/D8 · motor B: D3/D5/D4). 3) Ligar a régua de sensores (6 canais em A0–A3, D11 e D12; emissor no D13) e a alimentação: bateria de 9V no +12V do L298N, o 5V do L298N alimenta o Arduino e todos os GND no mesmo ponto (terra comum).

🪜 Passo 3 de 5

Programação / Codificação

Carregar o código. Explicar a lógica: ler a POSIÇÃO da linha, calcular o ERRO (distância do centro) e corrigir com um controle PID. CALIBRAR passando os sensores sobre a linha e o fundo. Ajustar os ganhos (KP, KD) e a velocidade base.

🪜 Passo 4 de 5

Teste, Depuração e Ajustes

Testar na pista. Depurar em equipe: se o robô oscila (zig-zag), baixe a velocidade base ou o ganho proporcional; se ele corta as curvas, aumente o ganho derivativo.

🪜 Passo 5 de 5

Encerramento / Socialização

Roda de conversa: o que é um controle em 'malha fechada' (medir → corrigir → repetir)? Onde mais isso aparece no dia a dia (termostato, piloto automático, chuveiro elétrico)? Como melhorar o robô?

🔌 Montagem do robô

Placa: Arduino Uno

⬇ baixar SVG
🧰 Montagem do robôPlaca: Arduino Uno · 4 componentes · 22 ligações detectadas do códigoUSBATMEGA328PLARDUINO UNOAREFGND131211109876543210DIGITAL (PWM ~)IOREFRESET3V35VGNDGNDVINPOWERA0A1A2A3A4A5ANALOG INArduino Uno⚡ Barramento de alimentação · 5V (+) / GND (–)D12 · leitura da linhaD11 · leitura da linhaD13 · liga os LEDs IRA0 · leitura da linhaA1 · leitura da linhaA2 · leitura da linhaA3 · leitura da linhaRégua de sensores(QTR, 6 canais)D9 · PWM velocidade AD7 · IN1 (direção A)D8 · IN2 (direção A)D5 · IN3 (direção B)D4 · IN4 (direção B)D3 · PWM velocidade BPonte H (L298N)OUT1·OUT2Motor DC esquerdoOUT3·OUT4Motor DC direitoLEGENDA DOS FIOSAlimentação (+5V)Terra (GND)DigitalAnalógico (leitura)PWM (velocidade)Saída de motor
🧰 Montagem do robôArduino Unoplaca controladoraRégua de sensores (QTR, 6…A0A1A2A3D12D11D135VGNDPonte H (L298N)D9D7D8D5D4D3bateria +GND5V (Arduino)Motor DC esquerdoOUT1OUT2Motor DC direitoOUT3OUT4
💻 O código
aula.ino
/*
 * Robô Seguidor de Linha (PID) — Arduino UNO
 * Driver: Ponte H L298N   |   Sensores: régua de refletância (QTR, 6 canais)
 * Lê a posição da linha, calcula o erro e corrige a trajetória em tempo real (PID).
 * (código original adaptado; comentários traduzidos para o português)
 */

// ===== Pinos do driver (Ponte H L298N) =====
#define PWMA 9   // ENA - velocidade do motor A
#define AIN2 8   // IN2 - direção do motor A
#define AIN1 7   // IN1 - direção do motor A
#define BIN1 5   // IN3 - direção do motor B
#define BIN2 4   // IN4 - direção do motor B
#define PWMB 3   // ENB - velocidade do motor B

int velocidadeBase = 250;   // velocidade de cruzeiro (0 a 255)

int erro = 0;               // erro atual (termo proporcional)
int derivada = 0;
int integral = 0;
int erroAnterior = 0;
float correcao;

#include <QTRSensors.h>

#define NUM_SENSORES 6
#define TIMEOUT 2500
#define PINO_EMISSOR 13     // liga os LEDs infravermelhos da régua

QTRSensorsRC qtr(
  (unsigned char[]){17, 16, 15, 14, 12, 11},   // A3, A2, A1, A0, D12, D11
  NUM_SENSORES,
  TIMEOUT,
  PINO_EMISSOR
);

unsigned int leiturasSensores[NUM_SENSORES];
unsigned int posicao = 0;

void setup() {
  Serial.begin(115200);

  pinMode(PWMA, OUTPUT);
  pinMode(AIN1, OUTPUT);
  pinMode(AIN2, OUTPUT);
  pinMode(PWMB, OUTPUT);
  pinMode(BIN1, OUTPUT);
  pinMode(BIN2, OUTPUT);

  // Calibração: passe a régua sobre a linha preta e o fundo branco por alguns segundos
  Serial.println("== CALIBRANDO ==");
  digitalWrite(PINO_EMISSOR, HIGH);
  for (int i = 0; i < 400; i++) {
    qtr.calibrate();
    delay(5);
  }
  digitalWrite(PINO_EMISSOR, LOW);
  Serial.println("== PRONTO ==");
}

void loop() {
  qtr.read(leiturasSensores);
  posicao = qtr.readLine(leiturasSensores, QTR_EMITTERS_ON, 0);
  erro = posicao - 3500;    // 3500 = centro (régua de 6 sensores -> faixa 0..7000)

  // ===== Monitor serial (depuração dos sensores) =====
  Serial.print("POS: "); Serial.print(posicao);
  Serial.print("  ERRO: "); Serial.print(erro);
  Serial.print("  CORR: "); Serial.print(correcao);
  Serial.print("  S: ");
  for (int i = 0; i < NUM_SENSORES; i++) {
    Serial.print(leiturasSensores[i]);
    Serial.print(" ");
  }
  Serial.println();

  // Linha bem à esquerda -> gira no lugar para reencontrá-la
  if (erro < -3500) {
    digitalWrite(AIN1, LOW);  digitalWrite(AIN2, HIGH); analogWrite(PWMA, 255);
    digitalWrite(BIN1, LOW);  digitalWrite(BIN2, HIGH); analogWrite(PWMB, 255);
  }
  // Linha bem à direita -> gira no lugar para o outro lado
  else if (erro > 3500) {
    digitalWrite(AIN1, HIGH); digitalWrite(AIN2, LOW);  analogWrite(PWMA, 255);
    digitalWrite(BIN1, HIGH); digitalWrite(BIN2, LOW);  analogWrite(PWMB, 255);
  }
  // Sobre a linha -> o controle PID ajusta as duas velocidades
  else {
    derivada = erro - erroAnterior;
    integral = erro + erroAnterior;

    correcao = (erro * 0.005) + (derivada * 15) + (integral * 0.125);

    if (correcao > velocidadeBase)  correcao = velocidadeBase;
    if (correcao < -velocidadeBase) correcao = -velocidadeBase;

    digitalWrite(AIN1, HIGH); digitalWrite(AIN2, LOW);
    analogWrite(PWMA, velocidadeBase - correcao);

    digitalWrite(BIN1, HIGH); digitalWrite(BIN2, LOW);
    analogWrite(PWMB, velocidadeBase + correcao);

    erroAnterior = erro;
  }

  delay(5); // evita saturar o monitor serial
}
🔀 Como o programa pensa
flowchart TD A(["Início"]) --> CAL["Calibrar os sensores"] CAL --> B["Ler a posição da linha"] B --> C{"Linha muito à esquerda?"} C -->|Sim| D["Girar no lugar (à esquerda)"] C -->|Não| E{"Linha muito à direita?"} E -->|Sim| F["Girar no lugar (à direita)"] E -->|Não| G["Calcular a correção (PID) e ajustar a velocidade de cada motor"] D --> B F --> B G --> B
🧱 Em blocos
quando @greenFlag for clicado
calibrar os sensores
sempre
  ler a posição da linha
  se <linha muito à esquerda> então
    girar no lugar à esquerda
  senão
    se <linha muito à direita> então
      girar no lugar à direita
    senão
      calcular a correção (PID)
      ajustar a velocidade dos dois motores
    end
  end
end
🖨️ Peça 3D

Toque para ver em 3D · gire com o dedo.

Chassi do seguidor
Pra fechar

Você chegou ao fim! 🎉

O que foi mais difícil? O que você faria diferente? Conte pra turma!

✓ Sair