Robot Seguidor de Línea con Control PID e Integración Android
Componentes y materiales
| Componente | Cantidad |
|---|---|
| Arduino Nano R3 | 1 |
| Sensor seguidor línea SparkFun RedBot | 1 |
| Módulo sensor IR 4 canales TCRT5000 | 1 |
| Módulo Bluetooth HC-06 | 1 |
| Dispositivo Android | 1 |
| Portapilas 4xAA | 2 |
| Servo rotación continua RobotGeek (SM-S4303R) | 2 |
Aplicaciones y servicios
- Arduino IDE
- MIT App Inventor 2 – Para crear la app Android de ajuste PID.
Descripción del proyecto
Construye un robot seguidor de línea con control PID y usa un dispositivo Android para ajustar fácilmente los parámetros de control (Kp, Ki, Kd) y lograr una puesta a punto rápida. Es la primera parte de un proyecto mayor: en la segunda fase se convertirá en un resolvedor de laberintos con IA simple.
Paso 1: Lista de materiales (BOM)
Robot muy económico (~75 USD):
- Chasis: 2 cuadrados madera 80×80mm, 3 clips carpeta, 2 ruedas madera 50mm, 1 rueda loca (ball caster), 9 gomas elásticas, tira 3M Command, soportes plásticos sensores.
- Electrónica: Protoboard + cableado, 2 packs 4×NiMH (5V c/u), 2 servos rotación continua SM-S4303R, Arduino Nano, HC-06 Bluetooth, 5 sensores línea (módulo 4ch TCRT5000 + 1 sensor suelto), 1 LED, 1 pulsador.
Paso 2: Configurar los servos
Dos servos continuos SM-S4303R pegados en bloque (cinta 3M/cola/doble cara). Funcionan por ancho de pulso:
- 1.5 ms (1500 µs) → parado (neutral).
- 1.0 ms (1000 µs) → velocidad máx. sentido 1 (~70 RPM).
- 2.0 ms (2000 µs) → velocidad máx. sentido opuesto.
- Valores intermedios → velocidad proporcional.
Conexiones:
- Servo izquierdo → Pin 5 Arduino
- Servo derecho → Pin 3 Arduino
Alimentación servos: pack 5V exclusivo (red lateral protoboard). GND común con Arduino.
Primer test: enviar 1500 µs a ambos. Si giran, ajustar tornillo amarillo bajo el servo hasta parar. Si no tiene tornillo, cambiar 1500 en código hasta lograr paro total.
#include <Servo.h>
Servo leftServo, rightServo;
void setup() {
leftServo.attach(5);
rightServo.attach(3);
leftServo.writeMicroseconds(1500);
rightServo.writeMicroseconds(1500);
}
void loop() {}
Paso 3: Montar chasis y test movimiento
- Fijar servos a base madera con cinta 3M.
- Superponer 2ª madera con clips carpeta (ajustar longitud).
- Rueda loca con clip.
- Pack baterías servos entre protoboard y chasis.
- Conectar GND pack → GND Arduino.
- Servos: Izq→Pin5, Der→Pin3.
- LED → Pin13 (señalización/test).
- Pulsador → Pin9 (inicio/test).
Rangos de velocidad (servos en oposición):
- Der: 1500µs (parado) → 2000µs (máx adelante).
- Izq: 1500µs (parado) → 1000µs (máx adelante).
Código test movimiento (avance, retroceso, giros):
while(digitalRead(buttonPin)) {} // Espera pulsar botón
motorTurn(LEFT, 500);
motorTurn(RIGHT, 500);
Paso 4: Módulo Bluetooth HC-06 (opcional)
- TX HC-06 → Pin 10 Arduino (RX SoftwareSerial)
- RX HC-06 → Pin 11 Arduino (TX SoftwareSerial)
- VCC/GND → 5V/GND Arduino
El robot funciona con o sin Bluetooth. Si no se activa BT, usa parámetros por defecto. La app Android permite ajuste remoto PID y control manual.
Paso 5: Sensores de línea IR
Conexiones pines Arduino:
- Sensor 0 (izq) → Pin 12
- Sensor 1 → Pin 18 (A4)
- Sensor 2 (centro) → Pin 17 (A3)
- Sensor 3 → Pin 16 (A2)
- Sensor 4 (der) → Pin 19 (A5)
- Fijar 5 sensores en barra plástica (fotos).
- Etiquetar 0 (izq) a 4 (der).
- Cables bajo chasis con gomas elásticas (evitar ruedas/rueda loca).
- 2º pack baterías 5V → Vin Arduino.
Uso módulo 4 sensores integrado + 1 suelto; equivalentes a 5 independientes.
Paso 6: Lógica sensores IR
Cada sensor: LED IR + fotodiodo IR. Luz reflejada → voltaje proporcional a reflectancia (alto en claro, bajo en oscuro). Módulo integra comparador + potenciómetro: salida digital HIGH = oscuro (línea), LOW = claro.
Array 5 sensores montado para que:
- Si línea centrada en sensor 2 → solo sensor 2 = HIGH.
- Espaciado permite que 2 sensores cubran ancho línea → ambos HIGH.
Estados posibles del array (0=claro, 1=oscuro):
0 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 ← CENTRADO (error = 0) 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0
Variable error según posición línea:
0 0 1 0 0 → error = 0 0 0 1 1 0 → error = 1 0 0 0 1 0 → error = 2 0 0 0 1 1 → error = 3 0 0 0 0 1 → error = 4 0 1 1 0 0 → error = -1 0 1 0 0 0 → error = -2 1 1 0 0 0 → error = -3 1 0 0 0 0 → error = -4
Implementación en Arduino:
const int lineFollowSensor0 = 12;
const int lineFollowSensor1 = 18;
const int lineFollowSensor2 = 17;
const int lineFollowSensor3 = 16;
const int lineFollowSensor4 = 19;
int LFSensor[5] = {0,0,0,0,0};
void readLFSsensors() {
LFSensor[0] = digitalRead(lineFollowSensor0);
LFSensor[1] = digitalRead(lineFollowSensor1);
LFSensor[2] = digitalRead(lineFollowSensor2);
LFSensor[3] = digitalRead(lineFollowSensor3);
LFSensor[4] = digitalRead(lineFollowSensor4);
if (LFSensor[0]==0 && LFSensor[1]==0 && LFSensor[2]==0 && LFSensor[3]==0 && LFSensor[4]==1) error = 4;
else if (LFSensor[0]==0 && LFSensor[1]==0 && LFSensor[2]==0 && LFSensor[3]==1 && LFSensor[4]==1) error = 3;
else if (LFSensor[0]==0 && LFSensor[1]==0 && LFSensor[2]==0 && LFSensor[3]==1 && LFSensor[4]==0) error = 2;
else if (LFSensor[0]==0 && LFSensor[1]==0 && LFSensor[2]==1 && LFSensor[3]==1 && LFSensor[4]==0) error = 1;
else if (LFSensor[0]==0 && LFSensor[1]==0 && LFSensor[2]==1 && LFSensor[3]==0 && LFSensor[4]==0) error = 0;
else if (LFSensor[0]==0 && LFSensor[1]==1 && LFSensor[2]==1 && LFSensor[3]==0 && LFSensor[4]==0) error = -1;
else if (LFSensor[0]==0 && LFSensor[1]==1 && LFSensor[2]==0 && LFSensor[3]==0 && LFSensor[4]==0) error = -2;
else if (LFSensor[0]==1 && LFSensor[1]==1 && LFSensor[2]==0 && LFSensor[3]==0 && LFSensor[4]==0) error = -3;
else if (LFSensor[0]==1 && LFSensor[1]==0 && LFSensor[2]==0 && LFSensor[3]==0 && LFSensor[4]==0) error = -4;
}
Paso 7: Control proporcional (P)
Definir iniMotorPower = 250 (medio gas):
rightServo.writeMicroseconds(1500 + iniMotorPower); // 1750 µs
leftServo.writeMicroseconds(1500 - iniMotorPower); // 1250 µs
Si robot deriva izquierda (línea "va derecha") → error = +1. Corregir: reducir velocidad derecho (pulso ↓), aumentar izquierda (pulso ↓):
rightServo.writeMicroseconds(1500 + iniMotorPower - Kp*error);
leftServo.writeMicroseconds(1500 - iniMotorPower - Kp*error);
Kp (constante proporcional) amplifica la corrección. Ej: Kp=50 → error 1 corrige 50 µs.
Paso 8: Control PID completo (opcional)
PID (Proporcional, Integral, Derivativo) es el control industrial estándar. Analogía: un muelle que tiende a recuperar su longitud original.
- Error (e): setpoint - valor_actual (aquí: error de posición).
- P = error. Magnitud del cambio necesario. Determina tiempo de subida.
- I = Σ error. Elimina error en estado estacionario. En robots pequeños a veces se omite (complica ajuste).
- D = error - error_anterior. Frena acercamiento al setpoint. Reduce sobrepaso (overshoot).
Ecuación: PIDvalue = (Kp*P) + (Ki*I) + (Kd*D)
Ajuste manual (trial-error):
- Kd=0, Ki=0. Ajusta Kp solo. Kp=25 buen inicio (Kp=50 funcionó bien).
- Una vez respuesta razonable: Kp=Kp/2, Kd=Kp/2. Aumenta Kd para reducir overshoot; baja si inestable.
- Si 반응 lento → sube Kp/Kd. Si inestable/oscilante → baja.
- Frecuencia muestreo (delay en loop) afecta mucho. Ajustar por prueba-error.
Implementación:
float Kp=50, Ki=0, Kd=0;
float P, I, D, PIDvalue, previousError, error;
void calculatePID() {
P = error;
I = I + error;
D = error - previousError;
PIDvalue = (Kp*P) + (Ki*I) + (Kd*D);
previousError = error;
}
void motorPIDcontrol() {
int leftMotorSpeed = 1500 - iniMotorPower - PIDvalue;
int rightMotorSpeed = 1500 + iniMotorPower - PIDvalue;
leftServo.writeMicroseconds(leftMotorSpeed);
rightServo.writeMicroseconds(rightMotorSpeed);
}
Paso 9: Código final integrado
Estados (mode):
STOPPED (0): todos sensores ven línea (1 1 1 1 1) → parada total.NO_LINE (2): ningún sensor ve línea (0 0 0 0 0) → giro 180° o búsqueda.FOLLOWING_LINE (1): normal → PID.
#define STOPPED 0
#define FOLLOWING_LINE 1
#define NO_LINE 2
int mode = STOPPED;
void loop() {
readLFSsensors();
// Detectar condiciones especiales
if (LFSensor[0]==1 && LFSensor[1]==1 && LFSensor[2]==1 && LFSensor[3]==1 && LFSensor[4]==1) mode = STOPPED;
else if (LFSensor[0]==0 && LFSensor[1]==0 && LFSensor[2]==0 && LFSensor[3]==0 && LFSensor[4]==0) mode = NO_LINE;
else mode = FOLLOWING_LINE;
switch (mode) {
case STOPPED: motorStop(); break;
case NO_LINE: motorStop(); motorTurn(LEFT, 180); break;
case FOLLOWING_LINE: calculatePID(); motorPIDcontrol(); break;
}
}
El código completo (con inicializaciones, funciones motorForward/Backward/Turn/Stop, manejo Bluetooth, etc.) está en el repositorio GitHub.
Paso 10: Ajuste PID con App Android
En robotDefines.h:
float Kp=50; float Ki=0; float Kd=0;
La app (MIT App Inventor 2) tiene:
- Botones manuales: FW, BW, Izq, Der, Stop → envían 'f','b','l','r','s' por BT.
- 3 sliders: Kp ("p/XXX"), Ki ("i/XXX"), Kd ("d/XXX") donde XXX = 0-100.
- Botón "Go" (equivalente a pulsador Pin 9).
Archivos: .aia (editable en App Inventor) y .apk (instalable directo).
Paso 11: Modificar código para ajuste PID remoto
En setup(), bucle de configuración antes de empezar:
while (digitalRead(buttonPin) && !mode) {
checkBTcmd(); // ¿comando BT recibido?
manualCmd(); // Ejecuta comando manual
command = "";
}
checkPIDvalues(); // Actualiza Kp,Ki,Kd desde variables globales
mode = STOPPED;
Función comandos manuales + PID:
void manualCmd() {
switch (command[0]) {
case 'g': mode = FOLLOWING_LINE; break;
case 's': motorStop(); break;
case 'f': motorForward(); break;
case 'r': motorTurn(RIGHT, 30); motorStop(); break;
case 'l': motorTurn(LEFT, 30); motorStop(); break;
case 'b': motorBackward(); break;
case 'p': Kp = command.substring(2).toFloat(); break; // "p/50"
case 'i': Ki = command.substring(2).toFloat(); break;
case 'd': Kd = command.substring(2).toFloat(); break;
}
}
Paso 12: Conclusión
Primera parte completada: seguidor de línea PID + ajuste Bluetooth/Android. Próxima fase: resolvedor de laberintos con IA simple, basado en este hardware.
Archivos actualizados en GitHub - Mjrovai/MJRoBot-Line-Follower.
Más tutoriales en MJRoBot.org.
¡Saludos desde el sur del mundo! – Marcelo
Código (fragmentos clave)
Ver sección "Code" del proyecto original para archivos completos (.ino, .h). Aquí se incluyen los fragmentos esenciales mostrados en los pasos.
Esquemático
Proceso de manufactura
- Sensor de seguimiento de línea con Raspberry Pi – Detector simple KY‑033 para puerta/línea
- Control de sensores y actuadores con Raspberry Pi: acelerómetro, servo y streaming de datos
- Construye un Robot Controlado por WiFi con Raspberry Pi y Smartphone Android
- Mando universal Android con Arduino, 1Sheeld y control IR
- Robot industrial seguidor de línea para transporte de materiales
- Robot seguidor de línea V4 – Sistema avanzado con Arduino Nano y PCB a medida
- Control PID para Pinza Robótica 3D en MATLAB/Simulink: Tutorial Avanzado
- Wittmann Battenfeld lanza el WX153: Línea avanzada de robots para automatización de inyección de alta fuerza
- FANUC amplía su línea de robots SCARA: seis modelos, cargas de 3‑20 kg, alcances de 400‑1100 mm
- Optimice la Garantía de Calidad del Empaquetado con Sistemas de Control Automatizados Avanzados
- Robótica en el siglo XXI: Sistemas distribuidos, telepresencia y exploración espacial