Forma correcta de utilizar la arquitectura de subsunción con Robot C

11

Últimamente he estado leyendo mucho sobre la arquitectura de subsunción y hay algunas maneras diferentes en que las personas parecen abogar.

Por ejemplo, algunas personas usan una variable global de "bandera" para que una tarea tome el control. Otros usan endTimeSlice()y permiten que el árbitro realmente elija. Y creo que esto es correcto.

Tengo esta pequeña sección del código de RobotC en la que estoy trabajando para una línea que sigue al robot, pero no estoy seguro de que lo esté haciendo bien, ya que actualmente el método de seguimiento siempre se hará cargo del método de búsqueda. El flujo correcto debe ser que find debe guiar al robot a la línea usando una ruta en espiral para encontrar la línea. Una vez que se encuentra la línea, la pista debe tomar el control.

task evade(){
    if(SensorValue(forwardSonarSensor) > threshold){
            //box the obstruction
    }
}

task find(){
    if(SensorValue(lightSensor) > threshold){
            //spiral the robot
    }
}

task track(){

    if(SensorValue(lightSensor) < threshold){
            //go straight
    }else{
                //execute turns to follow the line
    }
}

task main(){
    while(true){
        StartTask(evade,9);
        StartTask(track,8);
        StartTask(find,7);
        wait1Msec(250);
    }
}

Acabo de usar algunos comentarios aquí en lugar del código real para mantenerlo breve. Son mis declaraciones if no lo suficientemente buenas como condiciones porque cuando el robot está fuera de línea, se track()hace cargo. ¿Es esto debido a la declaración else dentro de la pista? Si es así, ¿cómo track()realizar giros cuando pierde la línea sin hacerse cargo del forraje al comienzo del programa?

James MV
fuente
1
Los números que entregas StartTask, ¿son la prioridad de la tarea? ¿9 va a ser la máxima prioridad? En ese caso, ¿no debería findtener más prioridad que track? De hecho, la condición findy la elsecondición de trackson las mismas. Entonces, como humano, si el valor del sensor es mayor que el umbral, ¿qué harías? ¿Ir en espiral o girar para ajustar la línea?
Shahbaz
Sé lo que es la arquitectura de subsunción, y estoy seguro de que sus robots deben tener diferentes módulos de hardware que se encarguen de las tareas informáticas de bajo nivel. Y que el código debería absorber la señal en lugar de solo escuchar un estado condicional. Entonces, ¿no deberías pasar la señal a otra pieza de hardware? Ahora mi problema es que tengo dos Arduinos y quiero que Arduino A subsuma o inhiba una señal de Arduino B. ¿Alguien tiene alguna idea de cómo se hace esto? Como puedo cablear a Arduino A para enviar una señal a Arduino B, pero no estoy seguro de cómo subsumir o inhibir la señal
Ambient Soda

Respuestas:

7

Con la arquitectura de subsunción, debe diseñar cuidadosamente sus comportamientos de tal manera que si asigna la Tprioridad de la tarea n, Tdebería ser lo que el robot debería hacer si nse ignoran todas las tareas con mayor prioridad .

Ordenemos sus tareas de ejemplo, luego busquemos una manera de implementarlo. Sus tareas son evade, findy track.

En general, desearía que el robot rastree una línea. Sin embargo, si no pudo detectar la línea, entonces debería intentar encontrarla. Sobre todo, debe evadir los obstáculos. Esto nos da el siguiente orden:

  • Más alta prioridad: evade
  • Entonces: find
  • Entonces: track

La razón findtiene mayor prioridad que trackes que, como he mencionado anteriormente, lo haría tracksólo si evadey findson innecesarias. Si pones findabajo track, eso significa que comienzas a rastrear si no hay obstáculo, incluso si no estás en la línea.

Ahora veamos su implementación:

task find(){
    if(SensorValue(lightSensor) > threshold){
            //spiral the robot
    }
}

task track(){

    if(SensorValue(lightSensor) < threshold){
            //go straight
    }else{
                //execute turns to follow the line
    }
}

Recuerde que le dimos finduna mayor prioridad. Por lo tanto, si el robot no puede detectar el lightSensor, irá en espiral tratando de encontrar la línea. Una vez que lo hace, trackentra en acción. Como puede ver, la elsecondición de tracknunca ocurre.

Mientras esto funciona, el robot se movería muy torpemente. De hecho, no hay mucho que pueda hacer al respecto, dada la construcción actual de su robot.


Aunque ya respondí tu pregunta, aquí hay una mejora simple para tu seguimiento de línea:

En lugar de un sensor de luz, use dos; ls_lefty ls_right. Usando (al menos) dos sensores, puede comprender si está totalmente fuera de la pista o si está a punto de salir de la pista. En el segundo caso, puede girar fácilmente en la dirección correcta y volver a la pista.

Tu findtarea es similar:

task find(){
    if (SensorValue(ls_left) > threshold
        && Sensorvalue(ls_right) > threshold){
            //spiral the robot
    }
}

Es decir, vas en espiral solo si no sientes nada

Su tracktarea ahora se vuelve más eficiente:

task track(){

    if (SensorValue(ls_left) < threshold
        && SensorValue(ls_right) < threshold){
            //go straight
    } else if (SensorValue(ls_left) < threshold
        && SensorValue(ls_right) > threshold){
            //turn left
    } else if (SensorValue(ls_left) > threshold
        && SensorValue(ls_right) < threshold){
            //turn right
    } else {
            // shouldn't happen, but go on spiral anyway
    }
}

Obviamente, con una matriz de sensores de luz, puede juzgar mejor qué tan mal se está desviando (es decir, con qué ángulo) y decidir mejor cómo volver a la pista (es decir, con qué velocidad angular).

Shahbaz
fuente
4

respuesta corta; no, realmente necesitas hacer las cosas de manera bastante diferente.

respuesta larga e incompleta; Permíteme darte un código de psuedo apropiado para robotC, que te coloca en un mejor camino. Primero, no use tareas, esto NO es para lo que son las tareas robotC. Podrían hacer que funcionen, tal vez, tal vez no (y necesita bastantes cambios para intentarlo).

// global variables
int distance;
int light;

main() {
   while (true) {
   distance = read_distance;
   light = read_light;
   if (task1_wantsToRun())
     task1_run();
   if (task2_wantsToRun())
     task2_run();   
   }
}

hay un par de cosas aquí; La prioridad se vuelve irrelevante. Por agradable que parezca tener tareas en robotC con prioridades, en mi experiencia no son una buena opción para la implementación de subsunción. Por razones como, las prioridades no siempre se cumplen, las tareas no se pueden interrumpir (a veces), por lo que cuando se produce un evento de mayor prioridad, no va a reaccionar como se esperaba, robotC se volvió a ingresar recientemente, por lo que cosas como acceder a un sensor de más de 1 tarea puede ser arriesgado (problemas de sincronización I2C), y en algunos casos no lo es (sensores sondeados automáticamente).

Puede agregar su propia implementación prioritaria al bucle anterior a medida que las cosas funcionan, pero realmente no es necesario para comenzar.

Su comentario "// recuadro la obstrucción" describe un comportamiento balístico. Es un poco complicado implementarlo mediante la multitarea. El bucle simple que utilicé lo hace mucho más fácil y mejor para principiantes / aprendizaje.

La otra cosa con la que te dejaré es que la subsunción, aunque es ordenada y apropiada para muchas cosas, no es una buena manera de implementar lo que se hace mejor tradicionalmente. De hecho, la parte 'evadir' puede ser un buen candidato para la subsunción, pero, sinceramente, su otra tarea debería llamarse 'GoOnAboutYourBusiness'. Digo esto porque probablemente no quieras cambiar de búsqueda a seguimiento con subsunción. Manejar aquellos con bucles de programación tradicionales. Con un solo sensor, ¿la luz se siente más oscura o más clara que el último bucle? si se oscureció (suponiendo una línea negra) siga girando en la misma dirección, si se aclara, gire hacia el otro lado, si se mantuvo igual, siga recto. Probablemente necesite agregar algo de PID y usar una curva de dirección en lugar de simplemente girar a izquierda y derecha para ser más suave.

Y sí, múltiples sensores ayudan. http://www.mindsensors.com/ - sí, ese soy yo en la película actualmente (10/11/2012)

Actualización: código real

Probaré esto dentro de poco, pero compila e ilustra lo que escribí arriba:

#pragma config(Sensor, S1,     S_LIGHT,        sensorLightActive)
#pragma config(Sensor, S2,     S_DISTANCE,     sensorSONAR)
#pragma config(Motor,  motorB,          LEFT,          tmotorNXT, PIDControl, encoder)
#pragma config(Motor,  motorC,          RIGHT,         tmotorNXT, PIDControl, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

int distance_value, light_value;

bool evade_wantsToRun()
{
    return distance_value < 30;
}

void evade_task()
{
    // full stop
    motor[LEFT] = 0;        
    // evade the object ballistically (ie in full control)  
    // turn left, drive
    nSyncedTurnRatio = 0;
    motor[LEFT] = -20;
    Sleep(500);
    nSyncedTurnRatio = 100;
    Sleep(1000);
    // turn right, drive
    nSyncedTurnRatio = 0;
    motor[LEFT] = 20;
    Sleep(500);
    nSyncedTurnRatio = 100;
    Sleep(1000);
    // turn right, drive
    nSyncedTurnRatio = 0;
    motor[LEFT] = 20;
    Sleep(500);
    nSyncedTurnRatio = 100;
    Sleep(1000);
    // turn left, resume
    nSyncedTurnRatio = 0;
    motor[LEFT] = 20;
    Sleep(500);
    motor[LEFT] = 0;
}

///////////////////////////////

void TurnBySteer(int d)
{
    // normalize -100 100 to 0 200
    nSyncedTurnRatio = d + 100; 
}
///////////////////////////////

typedef enum programPhase { starting, searching, following, finished };
programPhase phase = starting;

// these 'tasks' are called from a loop, thus do not need to loop themselves

void initialize()
{
    nSyncedTurnRatio = 50;
    nSyncedMotors = synchBC;
    motor[LEFT] = 30;       // start a spiral drive
    phase = searching;
}

void search()
{
    if (light_value < 24)
    {
        nSyncedTurnRatio = 100;
        phase = following;
    }
}

int lastLight = -1;
int currentSteer = 0;
void follow()
{
    // if it is solid white we have lost the line and must stop
    // if lightSensors detects dark, we are on line
    // if it got lighter, we are going more off line
    // if it got darker we are headed in a good direction, slow down turn in anticipation
    // +++PID will be even smoother
    if (light_value > 64)
    {
        motor[LEFT] = 0;
        phase = finished;
        return;
    }
    if (light_value < 24)
        currentSteer = 0;
    else if (light_value > lastLight)
        currentSteer += sgn(currentSteer) * 1;
    else    // implied (light_value < lastLight)
        currentSteer -= sgn(currentSteer) * 1;      

    TurnBySteer(currentSteer);
}

bool regularProcessing_wantsToRun()
{
    return phase != finished;
}

void regularProcessing_task()
{
    switch (phase)
    {
    case starting:
        initialize();
        break;
    case searching:
        search();
        break;
    case following:
        follow();
    }
}

task main()
{
    // subsumption tasks in priority oder
    while (true)
    {
        // read sensors once per loop
        distance_value = SensorValue[S_DISTANCE];
        light_value = SensorValue[S_LIGHT];
        if (evade_wantsToRun())
            evade_task();
        if (regularProcessing_wantsToRun())
            regularProcessing_task();
        else
            StopAllTasks();
        EndTimeSlice();     // give others a chance, but make it as short as possible
    }
}
Claveteado3
fuente
Estoy de acuerdo en que este problema se resuelve más fácilmente con un bucle simple. No entiendo por qué alguien menospreciaría esto.
Shahbaz
No quiero dejar la impresión de que es más fácil de resolver con un bucle simple, sino más bien la impresión de que ES el uso correcto de la subsunción usar un bucle simple como una de las tareas. Cualquiera que haya sido degradado tiene puntos de modificación y no comprende la subsunción. No encontrará que no hay muchas personas haciendo subsunción en un LEGO NXT (implicado mediante el uso de robotC), así que no espere que el código esté fácilmente disponible para pegarse.
Clavado3
Sí, me pregunto por qué el OP estaba usando tareas para algo tan simple como la subsunción.
Rocketmagnet
Porque es un error de principiante muy muy muy muy común con robotC: intentar usar tareas para todo. Desearía que lo trasladaran a un área avanzada solamente.
Clavado3