Sigue Líneas en RobotC

RobotC1En este artículo voy a explicar el uso básico del sensor de luz y a ponder un par de programas de prueba: el sigue líneas básico y el sigue líneas proporcional. Ambos programas ya han sido explicados en NXT-G y en LeJOS, por lo que podréis compararlos.

Uso básico del sensor de luz:

El sensor de luz tiene un manejo bastante sencillo en RobotC, primero se necesita declarar y luego ya se puede usar para tomar el valor de la luz que le entra. Es recomendable poner un pequeño retardo al principio del programa (unos 100ms por ejemplo) para dejar que se inicialize. Para inicializar el sensor es necesario poner la siguiente linea al principio del programa:

#pragma config(Sensor, num_sensor, lightSensor, sensorLightActive)

donde num_sensor es S1, S2, S3 o S4 según que puerto de sensores vayamos a utilizar. Para leer el valor de luz del sensor usaremos la función:

SensorValue[lightSensor]: Lee el valor de luz y lo devuelve en un entero. Su valor es parecido al devuelto en NXT-G.

No usaremos más funciones para el sigue líneas.

Uso básico del motor:

Hay bastantes funciones en RobotC para el manejo de motores, sin embargo me centraré solo en dos para este artículo y ya explicaré funcionamientos más avanzados en artículos posteriores. Los motores no hace falta declararlos, si no que ya están listos para su uso. La función para regular la potencia de un motor es:

motor[motorLetra] = %potencia: Regula la potencia del motorLetra (motorA, motorB o motorC) al % indicado (10, 20, 75, …).

La potencia sin embargo no regula la velocidad máxima. Para regular la velocidad máxima a la que queremos que vayan nuestros motores tendremos que modificar la siguiente variable:

nMaxRegulatedSpeedNxt = velocidad_max:Regula la velocidad máxima al valor indicado; hay que tener en cuenta que por defecto es 1000, por lo que si queremos reducirla a la mitad velocidad_max será 500.

No usaremos más funciones para el sigue líneas.

Sigue líneas básico en zigzag:

El sigue líneas es un problema que consiste en que el robot camine por una línea sin salirse. Esto se puede resolver mediante el uso de diferentes sensores, y de multitud de formas dentro de cada sensor. Una de las formas básicas de solucionarlo es que el robot se dirija hacia el borde de la linea negra esté en lo negro o en lo blanco. Como resultado parecerá que va haciendo un pequeño zigzag, ya que nunca será capaz de ir exactamente por el borde.

RobotC2

La primera parte del programa será, como en todos los sigue líneas, el calibrado. Este calibrado sirve para que el robot distinga entre que es negro, que es blanco, y que sería el borde de la línea. Lo que haremos en este caso para calcular ese borde de la linea será: med = (max + min)/2. Este punto medio seria el sitio ideal por donde debería ir el robot. Para calibrarlo dejaríamos al robot en el centro de la línea e iniciaríamos el programa; el programa tomaría el valor del negro, luego giraría hasta el blanco y tomaría su valor, calcularía el valor medio, volvería a girar a la posición inicial (o a una que calculéis más cercana al borde de la línea), y finalmente empezaría con la parte de sigue líneas. Dicho todo esto la parte de calibrado quedaría de la siguiente manera:

nMaxRegulatedSpeedNxt = 500;
int negro = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", negro);
wait1Msec(1000);
motor[motorB] = 60;
motor[motorC] = -60;
wait1Msec(300);

motor[motorB] = 0;
motor[motorC] = 0;
int blanco = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", blanco);
wait1Msec(1000);
int media = (negro + blanco)/2;

motor[motorB] = -60;
motor[motorC] = 60;
wait1Msec(200);

nxtDisplayCenteredTextLine(4, "%d", media);
motor[motorB] = 0;
motor[motorC] = 0;
wait1Msec(1000);
nMaxRegulatedSpeedNxt = 1000;

Una vez calibrado sabemos cual es el valor medio hacia el que tiene que moverse el robot, por tanto lo ponemos en marcha y vamos leyendo los valores del sensor de luz, haciendo que si el valor es superior al valor medio paremos el motor derecho (en este caso el MotorB) y aceleremos con el izquierdo (MotorC en este caso), lo cual nos hará rotar hacia la izquierda, donde estará la línea. Si en caso contrario estamos muy dentro de la línea y leemos un valor menor al valor medio con el sensor de luz, hay que hacer la operación inversa (parar el motor izquierdo, y mover el motor derecho). Todo esto metido en un bucle infinito hace que el robot se mueva por el borde de la línea. La parte de código de sigue líneas sería la siguiente:

while(true)
{
if(SensorValue[lightSensor] < media){ motor[motorB] = 60; motor[motorC] = 0; } else { motor[motorB] = 0; motor[motorC] = 60; } }

El programa completo sería (calibrado más sigue líneas):

#pragma config(Sensor, S1, lightSensor, sensorLightActive)

task main()
{
wait1Msec(100);
nMaxRegulatedSpeedNxt = 500;
int negro = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", negro);
wait1Msec(1000);
motor[motorB] = 60;
motor[motorC] = -60;
wait1Msec(300);

motor[motorB] = 0;
motor[motorC] = 0;
int blanco = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", blanco);
wait1Msec(1000);
int media = (negro + blanco)/2;

motor[motorB] = -60;
motor[motorC] = 60;
wait1Msec(200);

nxtDisplayCenteredTextLine(4, "%d", media);
motor[motorB] = 0;
motor[motorC] = 0;
wait1Msec(1000);
nMaxRegulatedSpeedNxt = 1000;
while(true)
{
if(SensorValue[lightSensor] < media){ motor[motorB] = 60; motor[motorC] = 0; } else { motor[motorB] = 0; motor[motorC] = 60; } } }

Y aquí os pongo un video demostrativo de como funcionaría un NXT con este programa:

Espero que os haya gustado. Ahora vamos a probar algo más complejo.

Sigue líneas proporcional:

Una manera más avanzada de resolver el problema del sigue líneas es mediante el método proporcional. En este método lo que haremos es aplicarle a los motores una potencia proporcional al error que tengamos (diferencia entre el valor medio hallado en la calibración y el valor actual del sensor de luz). De esta manera nos quedará un movimiento mucho más suave y preciso. Por supuesto la parte complicada es calcular la relación entre el error y la potencia que tenemos que dar a cada motor.

La parte del calibrado es la misma que la del sigue líneas anterior, por lo que no es necesario repetirla. La parte de sigue líneas será de la forma siguiente: una vez calibrado, el robot se pone en marcha; dentro de un búcle se calcula el error (valor medio - lectura actual) y se guarda en una variable; se modifica ese error para que pueda ser coherente a la hora de restárselo y sumarselo a la potencia de los motores*, y por último se resta ese valor a la velocidad de un motor (en este caso el izquierdo (MotorC)) y se le suma al otro (en este caso el derecho (MotorB)). Finalmente se repite este bucle infinitas veces.

*Por ejemplo supongamos que estamos completamente en lo negro y el error es de 3, no tiene sentido que si el motor B tiene una potencia de 80 le restemos solo 3, porque no va a modificar casi su trayectoria. Sin embargo si multiplicamos 3 por 6 tendremos 18, que ya si es una reducción bastante notable de potencia en ese motor. Por esto hay que tener en cuenta de que magnitud van a ser las unidades de este error. Tampoco conviene elevar mucho este valor, o acabará haciendo mucho Zig Zag y pareciendose al sigue línseas anterior. En este caso las lecturas de SensorValue[lightSensor] son de entre 64 lo más blanco, y unos 34 lo más negro, por lo que la media serán de aproximadamente 48. Los errores serán como mucho del orden de 20 (Ej.: 48 - 64 = -16), por lo que podemos restarlos directamente a las potencias de los motores si usamos potencias medias (Ej.: 50 - 1*(-16) = 66 , este motor acelerará claramente para tratar de corregir el error y situarse en el borde de la línea, mientras que el otro motor frenará: 50 + 1*(-16) = 34).

El programa completo con la parte de calibrado incluida quedaría de la siguiente manera:

#pragma config(Sensor, S1, lightSensor, sensorLightActive)

task main()
{
wait1Msec(100);
nMaxRegulatedSpeedNxt = 500;
int negro = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", negro);
wait1Msec(1000);
motor[motorB] = 60;
motor[motorC] = -60;
wait1Msec(300);

motor[motorB] = 0;
motor[motorC] = 0;
int blanco = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", blanco);
wait1Msec(1000);
int media = (negro + blanco)/2;

motor[motorB] = -60;
motor[motorC] = 60;
wait1Msec(200);

nxtDisplayCenteredTextLine(4, "%d", media);
motor[motorB] = 0;
motor[motorC] = 0;
wait1Msec(1000);
nMaxRegulatedSpeedNxt = 600;
int error;
while(true)
{
error = media - SensorValue[lightSensor];
motor[motorB] = 70 + 1.2*error;
motor[motorC] = 70 - 1.2*error;
}
}

Se ha regulado la velocidad máxima a 600 para una mayor precisión. Recordad que según como tengáis construido el robot necesitaréis calibrar el multiplicador del error y las potencias para que os funcione bien. Y aquí tenéis un vídeo demostración de este programa en ejecución:

Finalmente deciros que si tenéis alguna duda o pregunta podéis escribirla en el foro.

2 thoughts on “Sigue Líneas en RobotC

  1. hola !
    tengo unas dudas, soy nuevo con el lego y tengo que hacer el seguidor de linea sencillo, ya tengo el robot armado y también instale el robot c en mi lap.
    para poder configurar el sensor, en el menú robot/ motors and sensors setup, entre en la pestaña de sensores y en el S1 escribi lightSensor, es correcto de esta manera?, porque me marca error en

    #pragma config(Sensor, S1, lightSensor, sensorLightActive

    y tambien en la variable de la potencia del motor.

    nMaxRegulatedSpeedNxt = 500;

    me pueden ayudar.. gracias