Seguidor de Linha ESP32 (MicroPython + PID)
🎯 Objetivos de aprendizagem
- Montar e programar (em MicroPython) um seguidor de linha de alto desempenho com ESP32: ler 8 sensores QTR-8RC, calcular a posição da linha e usar um controle PID para seguir curvas suavemente, com recuperação quando a linha é perdida.
🧭 Habilidades BNCC Computação (6)
Habilidades dos anos selecionados (9º ano, 1º ano (EM), 2º ano (EM), 3º ano (EM)) — Resolução CNE/CP nº 1/2022. Em turmas de Ensino Médio, mapear para EM13CO.
🧰 Materiais e componentes
🪜 Desenvolvimento da aula
Apresente o seguidor de linha avançado: ESP32 em MicroPython, 8 sensores (QTR-8RC) e controle PID. Pergunta: por que um robô simples ziguezagueia e este segue a curva suave? Resposta: o PID corrige proporcional ao tamanho do erro.
1) Montar chassi, motores e a régua de sensores na frente. 2) Ligar o TB6612 ao ESP32 (motor A: AIN1 G18, AIN2 G19, PWMA G4 · motor B: BIN1 G21, BIN2 G22, PWMB G5; STBY/VCC no 3V3; VM na bateria). 3) Ligar a régua QTR-8RC nos GPIO 13,12,14,27,26,25,33,32. Energia: bateria → regulador → ESP32; GND comum.
Instalar o MicroPython no ESP32 e enviar o código com a IDE Thonny. Explicar o PID: P corrige proporcional ao erro, D antecipa pela velocidade do erro, I corrige desvios acumulados. A posição da linha vira um número (0–7000, centro 3500). Ajustar Kp, Kd, Ki e a velocidade base.
Calibrar na pista: se ziguezagueia, baixar Kp ou subir Kd; se corrige devagar, subir Kp; se sai nas curvas, reduzir a velocidade base. Testar a recuperação quando a linha some.
Roda de conversa: o que é uma malha de controle (PID)? Onde mais existe (chuveiro, drone, carro)? Encerrar com uma corrida cronometrada e comparação de ajustes entre as equipes.
🔌 Montagem do robô
Placa: ESP32 (WROOM-32)
📋 Tabela de ligações (fio a fio)
🔧 Passo a passo de ligação
- Cérebro: ESP32 WROOM-32 rodando MicroPython.
- Motores: AIN1→G18, AIN2→G19, PWMA→G4 (motor A); BIN1→G21, BIN2→G22, PWMB→G5 (motor B). STBY/VCC no 3V3, VM na bateria.
- Sensores: régua QTR-8RC nos GPIO 13, 12, 14, 27, 26, 25, 33, 32 (8 canais) + VCC/GND.
- Energia: bateria Li-Po → VM (motores) e → regulador → 5V do ESP32; GND comum.
- Calibração: a posição vai de 0 a 7000 (centro = 3500). Ajuste Kp/Kd/Ki e a velocidade base (cruzero) na pista.
- Motores: esquerdo nas saídas AO1/AO2; direito nas BO1/BO2. Se um girar ao contrário, inverta os 2 fios dele.
💻 Código
# Seguidor de Linha — ESP32 (MicroPython) + driver TB6612 + régua QTR-8RC
# Controle PID com filtro de média móvel e recuperação de linha perdida.
# (código original; comentários traduzidos para o português)
from machine import Pin, PWM
import time
# ------------------ Pinos dos Motores (TB6612) ------------------
PWMA = PWM(Pin(4), freq=5000, duty=0) # PWM do motor A
PWMB = PWM(Pin(5), freq=5000, duty=0) # PWM do motor B
AIN1 = Pin(18, Pin.OUT)
AIN2 = Pin(19, Pin.OUT)
BIN1 = Pin(21, Pin.OUT)
BIN2 = Pin(22, Pin.OUT)
# ------------------ Pinos dos Sensores QTR-8RC ------------------
sensors = [
Pin(13, Pin.IN), Pin(12, Pin.IN), Pin(14, Pin.IN), Pin(27, Pin.IN),
Pin(26, Pin.IN), Pin(25, Pin.IN), Pin(33, Pin.IN), Pin(32, Pin.IN)
]
NUM_SENSORS = len(sensors)
# ------------------ Parâmetros do PID ------------------
cruzero = 300 # velocidade base (0–1023)
Kp = 0.2
Kd = 10.0
Ki = 0.005
PRO = 0
DER = 0
INT = 0
LAST = 0
# ------------------ Filtro dos sensores ------------------
history = [3500] * 5 # histórico inicial para a média móvel
def read_qtr_digital():
"""Devolve a posição com base nos sensores digitais (0 ou 1)."""
values = [s.value() for s in sensors]
weighted_sum = 0
total = 0
for i, v in enumerate(values):
weighted_sum += v * i * 1000
total += v
if total == 0:
return 3500 # centro por padrão
return int(weighted_sum / total)
def read_qtr_filtered():
"""Filtra a leitura dos sensores usando média móvel."""
pos = read_qtr_digital()
history.pop(0)
history.append(pos)
return sum(history) // len(history)
# ------------------ Controle dos motores ------------------
def set_motor(left, right):
"""Controla os motores esquerdo e direito com valores de -1023 a 1023."""
# Motor A (esquerdo)
if left >= 0:
AIN1.value(1); AIN2.value(0); PWMA.duty(min(left, 1023))
else:
AIN1.value(0); AIN2.value(1); PWMA.duty(min(-left, 1023))
# Motor B (direito)
if right >= 0:
BIN1.value(1); BIN2.value(0); PWMB.duty(min(right, 1023))
else:
BIN1.value(0); BIN2.value(1); PWMB.duty(min(-right, 1023))
# ------------------ Programa principal ------------------
print("Iniciando o seguimento de linha...")
time.sleep(2)
last_time = time.ticks_ms()
while True:
now = time.ticks_ms()
dt = max((now - last_time) / 1000, 0.001) # evita divisão por zero
last_time = now
# Leitura de posição filtrada
pos = read_qtr_filtered()
PRO = pos - 3500
# Linha perdida -> giros suaves para reencontrar
if PRO < -3500:
vel_loss = int(cruzero * 0.8)
set_motor(-vel_loss, vel_loss)
INT = 0 # zera o integral
elif PRO > 3500:
vel_loss = int(cruzero * 0.8)
set_motor(vel_loss, -vel_loss)
INT = 0
else:
# PID
DER = (PRO - LAST) / dt
INT += PRO * dt
INT = max(min(INT, 3000), -3000) # anti-windup
vel = (PRO * Kp) + (DER * Kd) + (INT * Ki)
vel = max(min(vel, cruzero), -cruzero)
# Ajuste dinâmico da velocidade base nas curvas
baseSpeed = cruzero
if abs(PRO) > 1500:
baseSpeed = int(cruzero * 0.7)
left = baseSpeed - vel
right = baseSpeed + vel
set_motor(int(left), int(right))
LAST = PRO
time.sleep_ms(5)
🔀 Fluxograma do algoritmo
🧱 Algoritmo em blocos
quando @greenFlag for clicado
configurar TB6612, PWM e os 8 sensores QTR
sempre
ler a posição da linha (8 sensores, com filtro de média móvel)
somar (erro) a (posição) - (3500)
se <linha perdida para um lado?> então
girar suave para reencontrar a linha
senão
somar (derivada) a (erro - erro anterior) / (dt)
somar (integral) a (integral) + (erro × dt)
somar (correção) a (Kp×erro) + (Kd×derivada) + (Ki×integral)
acionar motor esquerdo com (base - correção)
acionar motor direito com (base + correção)
end
end🖨️ Modelos 3D para impressão
Toque para ver em 3D · gire com o dedo.
🤖 Quer essa aula do SEU jeito?
O Co-professor adapta esta aula pro seu ano e contexto — ou a estica numa sequência didática de 2 a 4 encontros — e gera um PDF pronto pra você. A estrutura da aula é preservada. Cadastre-se grátis pra usar.
✨ Adaptar e gerar PDF💬 Comentários
Entre na sua conta ou crie uma grátis pra comentar.
Seja o primeiro a comentar.
Gostou desta aula?
Monte aulas como esta em minutos, com ajuda da IA — e compartilhe com professores do Brasil todo. É grátis.
Criar minha conta grátis📲 Compartilhar no WhatsApp