Robot sigue líneas con Arduino
Este artículo trata sobre la construcción y programación de un robot que sigue líneas con Arduino y un sensor de infrarojos.
Construcción del Robot
Materiales
El chasis que verás en las fotos es específico, creado a medida por la empresa What's Next? para el proyecto [Robots Boost Skills]. El resto de componentes son genéricos y se pueden comprar Arduinos oficiales, What's Next Yellow o cualquier clon compatible.
Esta es la lista de materiales:
- Chasis que permita 2 ruedas con motor DC analógicos y una rueda delantera.
- 2 Motores DC analógicos con reducción y ruedas.
- Arduino Uno o equivalente.
- 1 Sensor Pololu QTR-8A o QTR-8RC
- Arduino Motor Shield o alguno que tenga el mismo Chip L298
- Baterías, entre 9V y 12V
Construcción del Chasis
En el caso del robot del ejemplo, el chasis tiene todos los elementos necesarios. Si lo tienes que construir, aquí tienes algunos consejos:
- Se recomienda cuidar el centro de gravedad de robot para que no plante rueda y tenga la adherencia necesaria. Por ejemplo, las baterías deberían estar entre las ruedas motrices y la rueda delantera.
- La distancia al suelo del sensor infrarojo es muy importante, ha de estar muy cerca, pero no rozar.
- Hay que dejar espacio para los cables y para poder modificar las conexiones sin necesidad de desmontar todo el robot.
El sensor
En nuestro caso, el sensor QTR-8[A-RC] es un conjunto de 8 emisores y sensores infrarrojos en un mismo circuito. Esto permite una gran precisión ,ya que tienes 8 lecturas cada vez. Estos sensores tienen una distancia recomendada de 3mm y un máximo de 6mm para el QTR-A y 9.5mm para el QTR-8RC.
La salida de los dos sensores es diferente, (analógica o digital), por lo que es importante distinguirlos y decidir cual vamos a usar. En nuestro caso, tenemos el QRT-8RC, por lo que, a partir de aquí, todo el manual se basa en este.
Puesto que vamos a usar 5V para alimentarlo, no es necesario unir los pines de 3.3V Bypass
Podemos conectar el pin LEDON que permite indicar con HIGH, LOW o PWM el estado de los LEDs. Si los apagamos, podemos consumir menos energía cuando no está leyendo.
El QTR8-RC mide la reflectancia con el tiempo entre un estado HIGH y uno LOW.
La lectura típica en el QTR8-RC es la siguiente:
- Encender los LEDs (opcional)
- Poner la linea I/O a una salida y ponerla en HIGH.
- Dejar al menos 10 μs al sensor para que arranque.
- Poner la I/O a Input
- Medir el tiempo que tarda el voltaje en caer esperando a la I/O a que vuelva a LOW.
- Apagar los LEDs (opcional)
Estos pasos se pueden hacer en varias líneas I/O al mismo tiempo. Con mucha reflectividad, el tiempo de bajada a LOW debe ser mínimo. Con poca (superficie negra) el tiempo debe mayor. Se recomienda usar el LEDON para no gastar batería hasta en un 90% del tiempo para lecturas a baja frecuencia, unos (100Hz).
Si se necesitan lecturas a mucha frecuencia, se recomienda que el sensor esté muy cerca del suelo, recomendado 3,5 mm i máximo 9 mm.
Cuanto más lejos peor lecturas y cuanta más luz ambiental peor. Si es necesario, bloquearemos la luz ambiental con cinta aislante o similar.
De esta manera, el esquema de conexiones del robot queda de la siguiente manera:
Programación del robot
Leyendo del sensor
Para que funcione, necesitamos la biblioteca correspondiente. En los últimos Arduino IDE es tan simple como ir al gestor de Bibliotecas y importar la QTRSensors.
La biblioteca funciona tanto para el QTR-8RC como el QTR-8A, pero cambia la manera de configurarlo:
// Crear un objeto para 8 sensores en los pines digitales 2,4,5,6,7,10 y en los analogicos A4 A5
QTRSensors qtr;
qtr.setTypeRC();
qtr.setSensorPins((const uint8_t[]){19,18,2,4,5,6,7,10}, 8);
qtr.setEmitterPin(16);
Calibrar los sensores es lo primero que debería hacer el robot. Así, en la rutina de inicialización (setup), se recomienda lanzar este código:
void setup()
{
pinMode(A3, OUTPUT); // LED para indicar que está calibrando
// configure the sensors
qtr.setTypeRC();
qtr.setSensorPins((const uint8_t[]){19,18,2,4,5,6,7,10}, SensorCount);
qtr.setEmitterPin(16);
delay(500);
//pinMode(A3, OUTPUT);
digitalWrite(A3, HIGH); // turn on Arduino's LED to indicate we are in calibration mode
// 2.5 ms RC read timeout (default) * 10 reads per calibrate() call
// = ~25 ms per calibrate() call.
// Call calibrate() 400 times to make calibration take about 10 seconds.
for (uint16_t i = 0; i < 400; i++)
{
qtr.calibrate();
}
digitalWrite(A3, LOW); // turn off Arduino's LED to indicate we are through with calibration
// print the calibration minimum values measured when emitters were on
Serial.begin(9600);
for (uint8_t i = 0; i < SensorCount; i++)
{
Serial.print(qtr.calibrationOn.minimum[i]);
Serial.print(' ');
}
Serial.println();
// print the calibration maximum values measured when emitters were on
for (uint8_t i = 0; i < SensorCount; i++)
{
Serial.print(qtr.calibrationOn.maximum[i]);
Serial.print(' ');
}
Serial.println();
Serial.println();
delay(1000);
}
Para leer del sensor, se recomienda usar la función readCalibrated() o read(). Con readCalibrated(), los valores obtenidos serán entre 0 (blanco) y 1000 (negro).
Para la detección de líneas, se usar la función readLine() a la que se puede decir si la línea es blanca o negra. El resultado de esta función es 0 si la línea está dentro o fuera de sensor 0 y 1000*(N-1) para cada sensor. Los valores para 8 sensores pueden ser, por tanto, 0, 1000, 2000 ... 8000 dependiendo de la posición de la línea. Código para analizar los valores que obtiene el sensor con los 8 sensores:
#include <QTRSensors.h>
// This example is designed for use with eight RC QTR sensors. These
// reflectance sensors should be connected to digital pins 3 to 10. The
// sensors' emitter control pin (CTRL or LEDON) can optionally be connected to
// digital pin 2, or you can leave it disconnected and remove the call to
// setEmitterPin().
//
// The setup phase of this example calibrates the sensors for ten seconds and
// turns on the Arduino's LED (usually on pin 13) while calibration is going
// on. During this phase, you should expose each reflectance sensor to the
// lightest and darkest readings they will encounter. For example, if you are
// making a line follower, you should slide the sensors across the line during
// the calibration phase so that each sensor can get a reading of how dark the
// line is and how light the ground is. Improper calibration will result in
// poor readings.
//
// The main loop of the example reads the calibrated sensor values and uses
// them to estimate the position of a line. You can test this by taping a piece
// of 3/4" black electrical tape to a piece of white paper and sliding the
// sensor across it. It prints the sensor values to the serial monitor as
// numbers from 0 (maximum reflectance) to 1000 (minimum reflectance) followed
// by the estimated location of the line as a number from 0 to 5000. 1000 means
// the line is directly under sensor 1, 2000 means directly under sensor 2,
// etc. 0 means the line is directly under sensor 0 or was last seen by sensor
// 0 before being lost. 5000 means the line is directly under sensor 5 or was
// last seen by sensor 5 before being lost.
QTRSensors qtr;
const uint8_t SensorCount = 8;
uint16_t sensorValues[SensorCount];
void setup()
{
//Setup Channel A
pinMode(12, OUTPUT); //Initiates Motor Channel A pin
pinMode(9, OUTPUT); //Initiates Brake Channel A pin
//Setup Channel B
pinMode(13, OUTPUT); //Initiates Motor Channel A pin
pinMode(8, OUTPUT); //Initiates Brake Channel A pin
pinMode(A3, OUTPUT);
// configure the sensors
qtr.setTypeRC();
qtr.setSensorPins((const uint8_t[]){19,18,2,4,5,6,7,10}, SensorCount);
qtr.setEmitterPin(16);
delay(500);
//pinMode(A3, OUTPUT);
digitalWrite(A3, HIGH); // turn on Arduino's LED to indicate we are in calibration mode
// 2.5 ms RC read timeout (default) * 10 reads per calibrate() call
// = ~25 ms per calibrate() call.
// Call calibrate() 400 times to make calibration take about 10 seconds.
for (uint16_t i = 0; i < 400; i++)
{
qtr.calibrate();
}
digitalWrite(A3, LOW); // turn off Arduino's LED to indicate we are through with calibration
// print the calibration minimum values measured when emitters were on
Serial.begin(9600);
for (uint8_t i = 0; i < SensorCount; i++)
{
Serial.print(qtr.calibrationOn.minimum[i]);
Serial.print(' ');
}
Serial.println();
// print the calibration maximum values measured when emitters were on
for (uint8_t i = 0; i < SensorCount; i++)
{
Serial.print(qtr.calibrationOn.maximum[i]);
Serial.print(' ');
}
Serial.println();
Serial.println();
delay(1000);
}
void loop()
{
// read calibrated sensor values and obtain a measure of the line position
// from 0 to 5000 (for a white line, use readLineWhite() instead)
uint16_t position = qtr.readLineBlack(sensorValues);
// print the sensor values as numbers from 0 to 1000, where 0 means maximum
// reflectance and 1000 means minimum reflectance, followed by the line
// position
for (uint8_t i = 0; i < SensorCount; i++)
{
Serial.print(sensorValues[i]);
Serial.print('\t');
}
Serial.println(position);
delay(250);
}
Ejemplo del manual oficial del sensor para un sigue líneas simple con 3 sensores:
void loop()
{
unsigned int sensors[3];
// get calibrated sensor values returned in the sensors array,
// along with the line position.
// position will range from 0 to 2000,
// with 1000 corresponding to the line over the middle sensor.
int position = qtr.readLine(sensors);
// if all three sensors see very low reflectance,
// take some appropriate action for this situation.
if (sensors[0] > 750 && sensors[1] > 750 && sensors[2] > 750)
{
// do something.
// Maybe this means we're at the edge of a course or about to fall off
// a table, in which case, we might want to stop moving, back up, and turn around.
return;
}
// compute our "error" from the line position.
// We will make it so that the error is zero
// when the middle sensor is over the line,
// because this is our goal. Error will range from
// -1000 to +1000.
// If we have sensor 0 on the left and sensor 2 on the right, a reading of
// -1000 means that we see the line on the left
// and a reading of +1000 means we see the
// line on the right.
int error = position - 1000;
int leftMotorSpeed = 100;
int rightMotorSpeed = 100;
if (error < -500) // the line is on the left
leftMotorSpeed = 0; // turn left
if (error > 500) // the line is on the right
rightMotorSpeed = 0; // turn right
// set motor speeds using the two motor speed variables above
}
- (Obsoleto) Manual de la biblioteca del sensor Referencia de la biblioteca
- (Versión 4.) Manual Referencia
- Tutorial en castellano