ROBOT SEGUIDOR DE LINEA


1) Propósito de la clase

  • Producto final: robot que sigue una línea negra sobre fondo blanco.

  • KUD (Know–Understand–Do)

    • Know: sensores IR reflectivos (TCRT5000 o similares), puente H (L298N), PWM, umbral.

    • Understand: el seguidor corrige su rumbo comparando izquierda vs derecha y ajustando la velocidad diferencial.

    • Do: cablear, calibrar sensores, cargar código, probar y optimizar (búsqueda y corrección).

2) Materiales

  • Arduino UNO/Nano + cable USB

  • Driver de motores L298N (o TB6612FNG)

  • Chasis con 2 motores DC + 1 rueda loca (o kit de auto)

  • 2 sensores IR reflectivos (TCRT5000 o módulos similares)
    (opción avanzada: arreglo QTR-8A/8RC)

  • Portapilas (6 V–7.4 V) independiente del Arduino (recomendado)

  • Jumpers hembra–macho, bridas, cinta doble faz

  • (Opcional) pulsador para autocalibración (con resistencia interna pull-up)

3) Esquema de cableo (texto)

Sensores

  • Sensor IzqA0

  • Sensor DerA1

  • VCC 5 V, GND común

L298N → Arduino

  • ENA → D5 (PWM)

  • IN1 → D8

  • IN2 → D9

  • ENB → D6 (PWM)

  • IN3 → D10

  • IN4 → D11

  • 12 V (o 7–12 V) al borne +12V del L298N (desde baterías de motores)

  • GND del L298N unido al GND de Arduino (masa común)

Pulsador de calibración (opcional)

  • Un lado a D12, otro a GND (usaremos INPUT_PULLUP)

Coloca los sensores a ~5–8 mm del suelo. Alinea el centro entre ruedas y que queden paralelos.

4) Secuencia didáctica (90’ × 2 sesiones)

Sesión 1 – Explora, conecta y calibra (90’)

  1. Hook (10’): videos/gifs de robots seguidores; lluvia de ideas: ¿cómo “ven” la línea?

  2. Mini-lección (15’): reflexión vs absorción; analogRead; umbral; PWM.

  3. Manos a la obra (45’): cableo completo y montaje sensores.

  4. Calibración (10’): medir blanco y negro; definir umbrales.

  5. Cierre (10’): checklist de pruebas y ticket de salida (qué funcionó/qué mejorar).

Sesión 2 – Programa, prueba y mejora (90’)

  1. Carga del código base (20’) y verificación serial.

  2. Pruebas en pista (30’): curvas en S, T, bifurcación; registro de observaciones.

  3. Optimización (25’): ajustar baseSpeed, umbrales; desafío “vuelta limpia”.

  4. Cierre (15’): retroalimentación tipo Purple Pen y breve demo.

5) Código base (analógico con autocalibración + lógica simple)

Diseñado para módulos TCRT5000 analógicos. Si tu módulo entrega digital invertido, invierte las comparaciones </> o usa digitalRead().

// Robot Seguidor de Línea - Arduino UNO
// Sensores analógicos en A0 (izq), A1 (der). L298N en pines indicados.
// Autocalibración con botón en D12 (a GND). Si no hay botón, usa timeout.

const int S_LEFT  = A0;
const int S_RIGHT = A1;

const int ENA = 5;   // PWM
const int IN1 = 8;
const int IN2 = 9;
const int ENB = 6;   // PWM
const int IN3 = 10;
const int IN4 = 11;

const int CAL_BTN = 12; // a GND (INPUT_PULLUP)

int thrLeft  = 500;  // valores por defecto (se sobreescriben al calibrar)
int thrRight = 500;

int baseSpeed = 140;   // 0..255
int turnBoost = 90;    // cuánto se acelera el motor opuesto al que pisa la línea

int lastTurn = 1;  // 1 = derecha, -1 = izquierda (para búsqueda cuando pierde la línea)

void setup() {
  pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT);
  pinMode(ENA, OUTPUT); pinMode(ENB, OUTPUT);

  pinMode(CAL_BTN, INPUT_PULLUP);

  Serial.begin(115200);
  delay(300);
  Serial.println("== Seguidor de Linea - Calibracion ==");

  calibrateSensors(3500); // espera botón o avanza con timeout (ms)

  Serial.print("Umbrales: L="); Serial.print(thrLeft);
  Serial.print("  R="); Serial.println(thrRight);

  // Dirección por defecto: adelante
  setDirForward();
}

void loop() {
  int leftVal  = analogRead(S_LEFT);
  int rightVal = analogRead(S_RIGHT);

  // Regla común en TCRT5000: negro refleja menos IR -> lectura mas BAJA.
  bool leftOnLine  = (leftVal  < thrLeft);
  bool rightOnLine = (rightVal < thrRight);

  if (leftOnLine && rightOnLine) {
    // Ambos sobre la línea -> seguir recto
    setSpeed(baseSpeed, baseSpeed);
  } else if (leftOnLine && !rightOnLine) {
    // izquierda pisa linea -> gira a la IZQUIERDA (freno izq, impulsa der)
    setSpeed(baseSpeed - turnBoost, baseSpeed + turnBoost);
    lastTurn = -1;
  } else if (!leftOnLine && rightOnLine) {
    // derecha pisa linea -> gira a la DERECHA
    setSpeed(baseSpeed + turnBoost, baseSpeed - turnBoost);
    lastTurn = 1;
  } else {
    // Ninguno ve la línea -> estrategia de búsqueda
    if (lastTurn > 0) {
      setSpeed(baseSpeed + turnBoost, 0); // giro suave a la derecha
    } else {
      setSpeed(0, baseSpeed + turnBoost); // giro suave a la izquierda
    }
  }

  // (Opcional) Telemetría por Serial para afinar
  // Serial.print(leftVal); Serial.print('\t'); Serial.println(rightVal);
  delay(5);
}

// ---------- Funciones de movimiento ----------
void setDirForward() {
  // Motores en avance
  digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW);
  digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW);
}

void setSpeed(int leftPWM, int rightPWM) {
  leftPWM  = constrain(leftPWM,  0, 255);
  rightPWM = constrain(rightPWM, 0, 255);
  analogWrite(ENA, leftPWM);
  analogWrite(ENB, rightPWM);
}

// ---------- Calibracion ----------
void calibrateSensors(unsigned long timeoutMs) {
  int wL=0, wR=0, bL=0, bR=0;

  Serial.println("1) Coloca sensores SOBRE BLANCO y pulsa el boton (o espera)...");
  waitBtnOrTimeout(timeoutMs);
  averageReadings(wL, wR);

  Serial.println("2) Ahora SOBRE LA LINEA NEGRA y pulsa (o espera)...");
  waitBtnOrTimeout(timeoutMs);
  averageReadings(bL, bR);

  // umbral = punto medio entre blanco y negro
  thrLeft  = (wL + bL) / 2;
  thrRight = (wR + bR) / 2;

  // Seguridad: si la diferencia es pequeña, usa defaults
  if (abs(wL - bL) < 30) thrLeft  = 500;
  if (abs(wR - bR) < 30) thrRight = 500;
}

void waitBtnOrTimeout(unsigned long timeoutMs) {
  unsigned long t0 = millis();
  while (millis() - t0 < timeoutMs) {
    if (digitalRead(CAL_BTN) == LOW) { delay(300); return; }
    delay(5);
  }
}

void averageReadings(int &outL, int &outR) {
  long sumL=0, sumR=0;
  for (int i=0; i<200; i++) { sumL += analogRead(S_LEFT); sumR += analogRead(S_RIGHT); delay(2); }
  outL = sumL / 200;  outR = sumR / 200;
}

Si tus sensores “leen al revés” (negro = valor alto), cambia las comparaciones a > en leftOnLine/rightOnLine.

Variante PID (extensión “visión de futuro”)

Para trayectorias más rápidas y suaves, sustituye la lógica por un controlador PD:

// Dentro de loop(), después de leer sensores (leftVal/rightVal)
// Normaliza 0..1000 (ajusta si usas otro rango)
int error = (rightVal - thrRight) - (leftVal - thrLeft); // signo define giro
static int lastError = 0;
float Kp = 0.22;   // empieza bajo
float Kd = 3.2;    // amortigua oscilaciones
int correction = (int)(Kp*error + Kd*(error - lastError));
lastError = error;

int L = constrain(baseSpeed - correction, 0, 255);
int R = constrain(baseSpeed + correction, 0, 255);
setSpeed(L, R);

6) Pruebas y calibración rápida

  1. Dibuja un circuito con cinta aislante negra de 18–20 mm sobre cartulina blanca.

  2. Ajusta altura de sensores: si están muy altos/ bajos, el contraste cae.

  3. Si “serpentea”, baja Kp/turnBoost. Si no corrige, súbelos.

  4. Si pierde la línea en curvas cerradas, reduce baseSpeed o acerca sensores.

7) Evaluación (rúbrica corta, 10 puntos)

  • Cableado correcto y seguro (2)

  • Calibración y registro de umbrales (2)

  • Robot sigue línea y corrige en curvas (4)

  • Iteración/optimización documentada (2)

8) Seguridad y buenas prácticas

  • GND común entre Arduino y driver.

  • Alimenta motores y Arduino por fuentes separadas o usa regulador dedicado.

  • No toques el L298N por la parte inferior (posibles cortos).

  • Retira hélices/ruedas al probar motores en la mesa.

9) Diferenciación y retos

  • Básico: umbral fijo, pista oval.

  • Medio: autocalibración + “S” pronunciada.

  • Avanzado: PD + cruce en T (decidir rumbo por última memoria).

  • Pro: arreglo QTR-8A y PID fino; o visión con HuskyLens.

10) Extensiones maker

  • Diseña el chasis en Google Slides (plantilla a escala) y corta en MDF 3 mm con tu láser.

  • Añade un buzzer para tonos al perder la línea.

  • Integra OLED 0.96" para ver L/R, umbrales y error.

Si quieres, te genero:

  • una pista imprimible A3,

  • una plantilla de chasis para láser,

  • o un poster-guía para tus estudiantes con pasos de calibración.

Comments

Popular posts from this blog

Python con Arduino + Interfaz

Te cuento mi aventura como mestra IB de Design y STEM

¡ParkingBot 3000! – Tu cochera con cerebro electrónico