En este artículo voy a explicar el como hacer el sigue líneas de RobotC con el Tetrix. En gran parte se parece al sigue líneas con el NXT, pero la dificultad radica en que debido a la extremada potencia de los motores de Hitechnic es bastante difícil manejar con al robot precisión.
Uso básico del sensor de luz:
El sensor de luz se maneja igual que con el NXT, por lo que os recomiendo que os leáis este artículo.
Uso básico del motor:
Para poder manejar los motores del Tetrix en RobotC necesitamos configurarlos. Todo el proceso de configuración de motores para un programa lo podéis encontrar en el artículo Tetrix: Mantis. Una vez configurado podréis usar ya las funciones que tiene RobotC ya preparadas para Tetrix, como por ejemplo:
motor[motorLetra] = %potencia: Regula la potencia del motorLetra (motorA, motorB o motorC, y los motores motorD y motorE en Tetrix si están configurados) al % indicado (10, 20, 75, …).
La potencia sin embargo no regula la velocidad máxima, lo cual es un gran problema en el Tetrix, ya que para manejar la velocidad necesitamos un encoder, o crear un programa de control de velocidad nosotros mismos. Puesto que la velocidad es proporcional a la potencia, se puede manejar indirectamente, pero sin mucha precisión.
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.
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:
wait1Msec(100);
int negro = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", negro);
wait1Msec(1000);
motor[motorD] = 60;
motor[motorE] = -60;
wait1Msec(200);
motor[motorD] = 0;
motor[motorE] = 0;
int blanco = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", blanco);
wait1Msec(1000);
int media = (negro + blanco)/2;
motor[motorD] = -60;
motor[motorE] = 60;
wait1Msec(150);
nxtDisplayCenteredTextLine(4, "%d", media);
motor[motorD] = 0;
motor[motorE] = 0;
wait1Msec(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)
{
nxtDisplayCenteredTextLine(4, "%d", SensorValue[lightSensor]);
if(SensorValue[lightSensor] < media){
motor[motorD] = 100;
motor[motorE] = 0;
}
else
{
motor[motorD] = 0;
motor[motorE] = 100;
}
}
Fijaros en este sigue líneas podemos usar la máxima potencia sin problemas, ya que el hecho de apagar y encender los motores controlará muy bien el hecho de que el robot siga por la línea. El programa completo sería (calibrado más sigue líneas):
#pragma config(Hubs, S1, HTMotor, HTServo, none, none)
#pragma config(Motor, mtr_S1_C1_1, motorD, tmotorNormal, PIDControl, encoder)
#pragma config(Motor, mtr_S1_C1_2, motorE, tmotorNormal, PIDControl, reversed, encoder)
#pragma config(Sensor, S2, lightSensor, sensorLightActive)
task main()
{
wait1Msec(100);
int negro = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", negro);
wait1Msec(1000);
motor[motorD] = 60;
motor[motorE] = -60;
wait1Msec(200);
motor[motorD] = 0;
motor[motorE] = 0;
int blanco = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", blanco);
wait1Msec(1000);
int media = (negro + blanco)/2;
motor[motorD] = -60;
motor[motorE] = 60;
wait1Msec(150);
nxtDisplayCenteredTextLine(4, "%d", media);
motor[motorD] = 0;
motor[motorE] = 0;
wait1Msec(1000);
while(true)
{
nxtDisplayCenteredTextLine(4, "%d", SensorValue[lightSensor]);
if(SensorValue[lightSensor] < media){
motor[motorD] = 10;
motor[motorE] = 0;
}
else
{
motor[motorD] = 0;
motor[motorE] = 10;
}
}
}
Y aquí os pongo un video demostrativo de como funcionaría un Tetrix con montaje Mantis 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, especialmente con Tetrix, ya que posée una potencia desmesurada.
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 (MotorE)) y se le suma al otro (en este caso el derecho (MotorD)). 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). En el caso del Tetrix usaremos potencias muy bajas para que el robot no se desboque, por lo que multiplicaremos el valor del error por valores decimales.
El programa completo con la parte de calibrado incluida quedaría de la siguiente manera:
#pragma config(Hubs, S1, HTMotor, HTServo, none, none)
#pragma config(Sensor, S2, lightSensor, sensorLightActive)
#pragma config(Motor, mtr_S1_C1_1, motorD, tmotorNormal, openLoop)
#pragma config(Motor, mtr_S1_C1_2, motorE, tmotorNormal, openLoop, reversed)
#pragma config(Servo, srvo_S1_C2_1, , tServoNormal)
task main()
{
wait1Msec(100);
int negro = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", negro);
wait1Msec(1000);
motor[motorD] = 60;
motor[motorE] = -60;
wait1Msec(200);
motor[motorD] = 0;
motor[motorE] = 0;
int blanco = SensorValue[lightSensor];
nxtDisplayCenteredTextLine(4, "%d", blanco);
wait1Msec(1000);
int media = (negro + blanco)/2;
motor[motorD] = -60;
motor[motorE] = 60;
wait1Msec(150);
nxtDisplayCenteredTextLine(4, "%d", media);
motor[motorD] = 0;
motor[motorE] = 0;
wait1Msec(1000);
float error;
while(true)
{
error = media - SensorValue[lightSensor];
motor[motorD] = 8 + 0.2*error;
motor[motorE] = 8 - 0.2*error;
}
}
El hecho de que sin un encoder solo podamos manejar potencia y no volumen hace que sea complicado el calibrar que potencia hay que aplicarle al motor, y por cuanto multiplicamos el error. Aquí tenéis un video demostración de este programa con el montaje Mantis:
Por ultimo decir como siempre que si tenéis alguna duda o pregunta podéis escribirla en el foro. Nos vemos :P.