Cómo programar un robot seguidor de líneas
este artículo, me gustaría cubrir las dos formas principales que la gente elige para programar un robot seguidor de línea y compararlas. Compararé, en detalle, el "seguimiento de línea simple" y el "seguimiento de línea PID". Eche un vistazo a estos videos: a bajas velocidades, un algoritmo de seguimiento de línea simple es perfectamente aceptable; a medida que aumenta la velocidad, el algoritmo de seguimiento de línea simple no puede acercarse a la potencia de los algoritmos de control PID.
En primer lugar, tenemos el seguimiento de línea simple. La premisa general detrás de este tipo de seguimiento de línea es que se tienen entre uno y unos pocos sensores y se codifica una respuesta del motor en función de qué sensor ve la línea. Este es generalmente el primer método que la gente aprende cuando se sigue una línea porque es fácil de entender y de programar. Primero, revisemos las opciones típicas de sensores.
Un sensor
La idea general detrás de un sensor que sigue una línea es que se configura un motor para que funcione a una velocidad ligeramente mayor o menor de modo que el robot favorezca una dirección (la dirección hacia la línea). Cuando el sensor detecta la línea, se acelera brevemente el motor más cercano a la línea para evitar que la cruce.
Ventajas
- Circuito simple
- Fácil de programar
- Hace el trabajo a un nivel mínimo
- No es una mala solución para líneas rectas.
Contras
- Si te pasas de la línea una vez puede ser desastroso.
- Sólo funciona bien en línea suave (sin giros bruscos)
- Se pierde tiempo recorriendo la distancia adicional (en lugar de conducir en línea recta)
- No puedo ir muy rápido en absoluto
CÓDIGO DE EJEMPLO
Lo siguiente debería darle una idea de cómo se vería este código, no he probado este código, así que proceda con precaución.
#define L_MOTOR 9
#define R_MOTOR 10
#define MAX_SPEED 200
#define SET_SPEED 150
#define MIN_SPEED 135
#define MIDDLE_IR A1
#define TAPE_THRESHOLD 350 / Cualquier valor menor es una reflectancia negra
void setup() {
// Intialize pins
pinMode(MIDDLE_IR, INPUT);
}
void loop() {
if (analogRead(MIDDLE_IR) < TAPE_THRESHOLD) {
// We are seeing the line under the middle sensor
analogWrite(L_MOTOR, SET_SPEED);
analogWrite(R_MOTOR, MAX_SPEED);
} else {
// Drive slightly right to hug the line
analogWrite(L_MOTOR, SET_SPEED);
analogWrite(R_MOTOR, MIN_SPEED);
}
}
Dos sensores
La idea general detrás del seguimiento de línea con dos sensores es que cuando un sensor ve la línea, se reduce la velocidad o se detiene el motor en el lado opuesto del sensor que vio la línea. Los sensores funcionan en tándem y mantienen al robot en el camino correcto (juego de palabras intencionado). Si usa este método, intente mantener los sensores juntos, cuanto más cerca estén, menos se tambaleará su robot cuando se conduzca, lo que le dará una apariencia de conducción más recta y perderá menos tiempo, ya que recorrerá una distancia menor desde su objetivo antes de ajustar.
Ventajas
- Circuito simple
- Fácil de programar
Contras
- Si pierde la línea, puede ser difícil recuperarla.
- Se pierde tiempo viajando de un lado a otro.
- No es bueno para altas velocidades
CÓDIGO DE EJEMPLO
#define L_MOTOR 9
#define R_MOTOR 10
#define MAX_SPEED 250
#define SET_SPEED 200
#define MIN_SPEED 50
#define LEFT_IR A0
#define RIGHT_IR A2
#define TAPE_THRESHOLD 350 / Cualquier valor menor es una reflectancia negra
void setup() {
// Intialize pins
pinMode(LEFT_IR, INPUT);
pinMode(RIGHT_IR, INPUT);
}
void loop() {
if (analogRead(LEFT_IR) < TAPE_THRESHOLD) {
//Estamos viendo la línea debajo del sensor izquierdo, giramos a la izquierda
analogWrite(L_MOTOR, MIN_SPEED);
analogWrite(R_MOTOR, SET_SPEED);
} else if (analogRead(RIGHT_IR) < TAPE_THRESHOLD) {
//Estamos viendo la línea debajo del sensor derecho, giramos a la derecha
analogWrite(L_MOTOR, SET_SPEED);
analogWrite(R_MOTOR, MIN_SPEED);
}
}
Tres o más sensores
La idea detrás del seguimiento de línea con tres sensores es que hace que los dos sensores sean un poco más robustos. Normalmente, se coloca el tercer sensor en el medio y, además de ajustarlo como lo haría con un seguidor de línea de dos sensores, se agrega un tercer y hasta un cuarto caso. El tercer caso diría que, si la línea está debajo del sensor del medio, se configuran ambos motores a la misma velocidad en un intento de mantener una trayectoria recta. El cuarto caso opcional diría que, si ninguno de los sensores ve la línea, se apagan los motores o tal vez se gira hasta que encuentra la línea nuevamente.
Además, si agrega aún más sensores, podría agregar niveles de ajustes. Digamos que tiene cuatro sensores, si el sensor más a la izquierda ve la línea, entonces podría apagar el motor derecho, mientras que si el segundo sensor desde la izquierda ve la línea, simplemente podría reducir la velocidad del motor derecho, de esta manera reaccionaría más abruptamente a las esquinas agudas o si se mueve más lejos de la línea por alguna razón.
Ventajas
- El método de seguimiento de línea simple más robusto
- Capaz de detectar si se pierde la línea
- Ajuste gradual versus estrictamente encendido/apagado o alta/baja velocidad
- Fácil de programar
Contras
- Todavía no hay una respuesta analógica verdadera al error (el robot a menudo tiembla mientras sigue la línea)
- No es bueno para altas velocidades
CÓDIGO DE EJEMPLO
#define L_MOTOR 9 #define R_MOTOR 10 #define MAX_SPEED 200 #define SET_SPEED 200 #define MIN_SPEED 50 #define STOP_SPEED 0 #define LEFT_IR A0 #define MIDDLE_IR A1 #define RIGHT_IR A2 #define TAPE_THRESHOLD 350 // Anything less is a Black reflectance void setup() { // Intialize pins pinMode(LEFT_IR, INPUT); pinMode(MIDDLE_IR, INPUT); pinMode(RIGHT_IR, INPUT); } void loop() { if (analogRead(LEFT_IR) < TAPE_THRESHOLD) { //Estamos viendo la línea debajo del sensor izquierdo, giramos a la izquierda
analogWrite(L_MOTOR, MIN_SPEED); analogWrite(R_MOTOR, SET_SPEED); } else if (analogRead(MIDDLE_IR) < TAPE_THRESHOLD) { //Estamos viendo la línea debajo del sensor del medio, siga recto
analogWrite(L_MOTOR, SET_SPEED); analogWrite(R_MOTOR, SET_SPEED); } else if (analogRead(RIGHT_IR) < TAPE_THRESHOLD) { //Estamos viendo la línea debajo del sensor derecho, giramos a la derecha
analogWrite(L_MOTOR, SET_SPEED); analogWrite(R_MOTOR, MIN_SPEED); } else { //No vemos la línea en absoluto, detengámonos
analogWrite(L_MOTOR, STOP_SPEED); analogWrite(R_MOTOR, STOP_SPEED); } }
Seguimiento de línea PID
Ahora, veamos un algoritmo de seguimiento de línea un poco más complejo pero mucho más preciso: el seguimiento de línea PID. He notado que muchos principiantes se sienten desanimados por la complejidad del seguimiento de línea PID y tienden a confiar en una biblioteca para realizar sus cálculos PID. Te recomiendo que comiences con tu propio algoritmo PID simple para que entiendas lo que está sucediendo. Claro, puedes salirte con la tuya usando una biblioteca PID, pero entonces no estás aprendiendo realmente cómo funciona, por lo que es difícil ajustar tus valores y saber exactamente cómo afectará eso al resultado.
Ventajas
- Seguimiento de línea muy suave y preciso.
- Generalmente se puede seguir la línea mucho más rápido que con el método “Simple”
- Mayor flexibilidad si desea seguir varias posiciones (seguir en el centro, ligeramente a la izquierda, ligeramente a la derecha)
Contras
- Requiere ajuste, lo que puede llevar tiempo.
- Puede parecer abrumador sin orientación (espero que este artículo solucione ese problema)
- Más sensores tardarán más tiempo en capturar lecturas.
¿Qué es PID?
En primer lugar, analicemos un poco qué es PID. PID es el acrónimo de proporcional, integral y derivativo, pero no se asuste: es muy fácil y no es necesario realizar ningún cálculo. La idea detrás de PID es que se tiene un valor real y un valor objetivo o un punto de ajuste. Se realizan algunos ajustes en el sistema en función de la diferencia entre el valor objetivo y el valor real.
P(procesal)
El primer aspecto de ese ajuste es la P, que es simplemente un coeficiente... es decir, es un número por el que multiplicas tu error para luego aplicarlo como ajuste. Digamos que estás conduciendo un coche y estás intentando mantener una velocidad constante de 55 km/H, si lees el velocímetro y ves que actualmente está en 55, sabes que vas cinco km/H por debajo, por lo que necesitas pisar el acelerador para volver a aumentar la velocidad. Digamos que revisas el velocímetro una vez por segundo y haces un ajuste, lo más probable es que sueltes lentamente el acelerador. Entonces, con un valor P necesitamos determinar un valor para convertir qué tan lejos estamos en cuánto necesitamos ajustar el acelerador.
Digamos, por ejemplo, que un movimiento de una pulgada del acelerador normalmente aumentará la velocidad en un km por hora en el transcurso de un segundo. Si hacemos un poco de matemática simple, podemos llegar a un algoritmo bastante sencillo para esto. Si multiplicamos nuestro error por un km por hora, obtendremos un valor P de 1.
ajuste = P*error
ajuste = 1*error
Voila, si la velocidad está seis km/H fuera, nuestro ajuste será (1*6) o 6 pulgadas, si está doce km/H fuera, (1*12) o 12 pulgadas para mover el acelerador. Del mismo modo, si está 8 km/H por encima, ajustaremos -8 pulgadas. El punto es que ahora tenemos un ajuste que se basa en cierto conocimiento y en el caso de nuestros motores, haríamos un ajuste más pequeño si el robot estuviera solo una pequeña cantidad fuera de la línea.
Cualquiera que haya conducido un vehículo sabe que no existe una relación lineal entre la distancia que se mueve el acelerador y la cantidad de aumento de velocidad en un segundo. Aquí es donde entran en juego los demás elementos del PID.
I(integral)
Una advertencia sobre I es que no suelo utilizar este término en un seguidor de línea. El término I es el más complicado de utilizar para principiantes, así que, a menos que tengas experiencia, aquí es donde utilizaría una biblioteca. Pero, de nuevo, no suelo utilizar I, así que te mostraré un ejemplo que no utiliza este término, pero que te permite seguir una línea de forma excelente.
El valor P es genial, pero no es un sistema perfecto, ¿lo ves? La verdad es que cuando el vehículo se mueve más lento, tu aceleración será diferente que cuando se mueve más rápido. Además, si tienes el pie en una posición de 6 pulgadas del acelerador, ¿qué sucede cuando llegas a una colina? Esa misma posición del acelerador no mantendrá tu velocidad. Aquí es donde el término I entrará en juego. Lo que pasa con el término I es que hace un seguimiento de tus errores y tiene en cuenta los factores externos. Si tu valor P no parece estar haciendo los cambios adecuados a un ritmo decente, el término I crecerá lentamente para obligar a que tu ajuste sea mayor porque está sumando cada error y aplicándolo a tu ajuste. Entonces, un algoritmo de controlador PI se vería así:
ajuste = P*error + I*sumaDeErrores
Donde sumOfErrors es la suma de errores cada vez que calculas tu ajuste. Entonces, volviendo al problema de la subida que mencioné, si eres capaz de navegar generalmente sin mover el acelerador, luego llegas a una colina, tu velocidad real contra tu objetivo va a crecer dándote un error, cada vez que tienes un error tu término I te va a decir que presiones más el acelerador hasta que finalmente alcances la velocidad. Sin embargo, una vez que llegas a la cima de la colina, ahora has agregado más acelerador pero la colina ha desaparecido, vas a tener que comenzar a soltar el acelerador, generalmente así es como el término I se estabilizaría de nuevo, estarías sobrepasando tu objetivo por lo que tu error sería negativo, restándose así a tu suma de valores I. El problema con I es que necesitas saber cuándo restablecerlo o posiblemente dejar que se estabilice por sí solo. Esta es la razón principal por la que no uso I: es difícil de comprender para los principiantes y puede afectar drásticamente el resultado sin proporcionar realmente un gran beneficio en el caso de un robot que sigue una línea.
En el caso de un robot que sigue una línea, la parte en la que el término I sería muy útil sería si el motor falla y el robot se desvía constantemente hacia un lado. El término I sumaría todos los errores y, con el tiempo, esa suma se estabilizaría en un punto en el que compensaría la debilidad del motor y aplicaría constantemente un ajuste adicional en esa dirección.
D(derivativo)
Un algoritmo de control proporcional sería excelente por sí solo, excepto por el hecho de que es tan errático que nunca tiende a establecerse muy bien en el valor objetivo. Esto se debe a que es solo un coeficiente simple que se aplica a un rango de errores cuando, de hecho, desearíamos que ese coeficiente pudiera volverse menos efectivo cuando estemos en nuestro objetivo. Aquí es donde entra en juego el valor D, que calcula la diferencia entre el último error y el error actual. Entonces, como ejemplo, si la primera lectura fue un error de 5 y la nueva lectura es un error de 1, entonces el ajuste de la derivada sería el coeficiente D multiplicado por la diferencia en el error (1-5) o -4. Si agregamos el término D a nuestro controlador PI, se vería así:
ajuste = P*error + I*sumaDeErrores + D*(error – últimoError)
O si simplemente tuviéramos un algoritmo de control de PD
ajuste = P*error + D*(últimoError – error)
Entonces, si tratamos de analizar este cálculo como se muestra con PD, podemos ver que nuestra parte P, como ya hemos aprendido, aplica un factor multiplicativo basado en la cantidad de error. Sin embargo, el valor D aplica un factor multiplicativo a la diferencia en el error, entonces, ¿qué significa esto? A medida que nos acercamos a nuestro objetivo, nuestro valor D amortiguará el efecto de P para que no lo sobrepasemos. Si notó, nuestro ejemplo tenía un valor negativo, por lo que esto efectivamente reducirá el ajuste a medida que nos acercamos a un error de 0.
También puedes pensarlo de esta manera: si el error está creciendo, entonces P no está haciendo un buen trabajo; en este caso, el nuevo error será mayor que el error anterior porque nos estamos alejando del objetivo. Un número mayor menos un número menor dará como resultado un valor positivo que se sumará a la parte P para ayudarnos a volver al camino correcto. Por otro lado, si nos estamos acercando correctamente al objetivo, tendremos un error actual que es menor que el error anterior y esto se restará de la parte P para asegurarnos de no sobrepasar nuestro objetivo. Observa la imagen de la derecha para obtener una mejor visualización del error en relación con la línea.
Algoritmo de seguimiento de línea PID
Ahora que tenemos una comprensión básica de qué es PID y cómo funciona, volvamos a hablar sobre cómo implementar esto en un robot que sigue una línea.
DIAGRAMA DE FLUJO
Comencemos por repasar un diagrama de flujo que cubre los aspectos básicos del seguimiento de línea PID. Con suerte, el diagrama de flujo te ayudará a comprender el algoritmo en relación con el código. Si entiendes mejor el código leyendo el código en sí, tengo algunos ejemplos de código que siguen al diagrama de flujo.

CÓDIGO DE EJEMPLO
Algunas cosas que necesito mencionar de antemano aquí, este código se basa en el uso de la matriz de sensores QTR-8RC de Pololu . Puede modificarlo fácilmente para la matriz QTR-8A o incluso para usarlo con sensores IR QTR-1RC o QTR-1A individuales , he usado tan solo cuatro sensores IR en un seguidor de línea PID con gran éxito. Simplemente ajuste NUM_SENSORS y los números de pin que se pasan al objeto QTRSensorsRC. Además, si tiene una cantidad diferente de sensores, si desea seguir en el centro, su objetivo será diferente. Para calcular el centro, use el siguiente algoritmo: (NUM_SENSORS – 1)*500. Por supuesto, no tiene que seguir en el centro, puede seguir en cualquier lugar dentro del rango de sensores.
Necesitarás tener la biblioteca de sensores QTR, que se puede encontrar aquí . El enlace es un enlace a mi versión personal de la biblioteca. Encontré un error que sugerí que solucionaran, pero aún no lo han hecho. Puede generar algunos valores extraños, por lo que te sugiero que uses mi biblioteca modificada con la solución.
La función readLine(sensorValues) devuelve un valor entre 0 y (NUM_SENSORS – 1)*1000. Si tienes 8 sensores, la lectura será de 0 a 7000, si tienes 6 sensores será de 0 a 5000. En cada caso, 0 representa el sensor más a la izquierda y cada incremento de 1000 después de eso representa otro sensor. 0 == primer sensor, 1000 == segundo sensor y también hay rangos intermedios. 500 significa que la línea está entre el primer y el segundo sensor, 1200 significa que está entre el segundo y el tercer sensor pero más cerca del segundo... ¿entiendes la idea, verdad?
Por último, es muy probable que mis valores KP y KD no funcionen con tu robot, ya que cada uno es diferente. Estos valores dependen de la velocidad del motor, el peso del robot, el tamaño de las ruedas, la posición del sensor con respecto a las ruedas, etc.
#include <QTRSensors.h> #define SETPOINT 3500 //El objetivo para readLine (centro)#define KP 0.2 // El valor P en PI
#define KP 0.2 // The P value in PID #define KD 1 // The D value in PID #define L_MOTOR 9 // Left motor pin #define R_MOTOR 10 // Right motor pin #define MAX_SPEED 200 // The max speed to set motors to #define SET_SPEED 200 // The goal speed to set motors to #define MIN_SPEED 0 // The min speed to set motors to #define NUM_SENSORS 8 // The number of QTR sensors #define TIMEOUT 2500 // Timeout for the QTR sensors to go low #define EMITTER_PIN 2 // Emitter pin for QTR sensor emitters // PID ************************************** int lastError = 0; // For storing PID error // SENSORS ********************************** // sensors 0 through 7 are connected to digital pins 3 through 10, respectively QTRSensorsRC qtrSensors((unsigned char[]) {3, 4, 5, 6, 7, 8, 9, 10}, NUM_SENSORS, TIMEOUT, EMITTER_PIN); unsigned int sensorValues[NUM_SENSORS]; // For sensor values of readLine() void setup() { // Initialize Pins pinMode(L_MOTOR, OUTPUT); pinMode(R_MOTOR, OUTPUT); } void loop() { // Take a reading unsigned int linePos = qtrSensors.readLine(sensorValues); // Compute the error int error = SETPOINT - linePos; // Compute the motor adjustment int adjust = error*KP + KD*(error - lastError); // Record the current error for the next iteration lastError = error; // Adjust motors, one negatively and one positively analogWrite(L_MOTOR, constrain(SET_SPEED - adjust, MIN_SPEED, MAX_SPEED)); analogWrite(R_MOTOR, constrain(SET_SPEED + adjust, MIN_SPEED, MAX_SPEED)); }
AJUSTE DE PID
El ajuste de los valores P y D de este algoritmo de control PD está fuera del alcance de este artículo. Como se mencionó, estos valores KP y KD probablemente no funcionarán con su robot, ya que cada robot es diferente. El ajuste de PID es un aspecto importante del algoritmo, cada robot debe ajustarse en función de una serie de parámetros específicos de ese robot. Incluiré un artículo en los próximos días y proporcionaré un enlace cuando esté listo. Como consejo hasta que salga ese artículo, configure KD en 0 (lo que le dará solo un controlador P) e intente obtener un valor KP que siga la línea, no se preocupe por KD hasta que tenga KP funcionando. Además, intente ajustar con sus motores configurados al menos a la mitad de la velocidad deseada, el PID es difícil de ajustar a toda velocidad a menos que su velocidad máxima ya sea lenta
No hay comentarios:
Publicar un comentario