Modo corretto di utilizzare Subsumption Architecture con Robot C


11

Ultimamente ho letto molte cose sull'architettura di Subsumption e ci sono diversi modi in cui le persone sembrano sostenere.

Ad esempio, alcune persone usano una variabile "flag" globale per fare in modo che un'attività prenda il controllo. Altri usano il endTimeSlice()e permettono all'arbitro di scegliere davvero. E penso che sia corretto.

Ho questa piccola sezione del codice RobotC su cui sto lavorando per un robot che segue la linea ma non sono sicuro di farlo nel modo giusto poiché attualmente il metodo di traccia prenderà sempre il metodo di ricerca. Il flusso corretto dovrebbe essere che find dovrebbe guidare il robot verso la linea usando un percorso a spirale per trovare la linea. Una volta trovata la linea, la traccia dovrebbe prendere il controllo.

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);
    }
}

Ho appena usato alcuni commenti qui piuttosto che il codice reale per mantenerlo breve. Le mie dichiarazioni if ​​non sono abbastanza buone come condizioni perché quando il robot è fuori linea, track()prende il sopravvento. Ciò è dovuto alla dichiarazione else all'interno della traccia? In tal caso, come track()eseguire i turni quando perde la linea senza subentrare al foraggio all'inizio del programma?


1
I numeri che dai StartTask, sono la priorità dell'attività? 9 avrà la massima priorità? In tal caso, non dovrebbe findavere più priorità di track? In effetti, la condizione finde la elsecondizione di tracksono le stesse. Quindi, come essere umano, se il valore del sensore è maggiore della soglia, cosa faresti? Vai a spirale o gira per regolare la linea?
Shahbaz,

So cos'è l'architettura di sussunzione e sono sicuro che i tuoi robot dovrebbero avere diversi moduli hardware che si assumono la responsabilità di compiti di calcolo di basso livello. E che il codice dovrebbe effettivamente assorbire il segnale piuttosto che solo ascoltare uno stato condizionale. Quindi non dovresti passare il segnale in un altro componente hardware? Ora il mio problema è che ho due Arduinos e voglio che Arduino A riesca a interrompere o inibire un segnale da Arduino B. Qualcuno ha idea di come sia fatto? Dato che posso collegare Arduino A per inviare un segnale ad Arduino B ma non sono sicuro di come riprendere o inibire il sig
Ambient Soda

Risposte:


7

Con Subsumption Architecture, dovresti progettare attentamente i tuoi comportamenti in modo tale che se assegni la Tpriorità delle attività n, allora Tdovrebbe essere ciò che il robot dovrebbe fare se tutte le attività con priorità più alta di quelle che nvengono ignorate.

Ordiniamo le tue attività di esempio, quindi scopriamo un modo per implementarle. I suoi compiti sono evade, finde track.

In generale, si desidera che il robot segua una linea. Tuttavia, se non è stato in grado di rilevare la linea, allora dovrebbe provare a trovarla. Soprattutto, dovrebbe eludere gli ostacoli. Questo ci dà il seguente ordine:

  • Massima priorità: evade
  • Poi: find
  • Poi: track

La ragione findha una priorità più alta di trackquella che, come ho già detto, lo faresti tracksolo se evadee findnon fossero necessari. Se metti findsotto track, significa che inizi a monitorare se non ci sono ostacoli, anche se non sei in linea.

Ora diamo un'occhiata alla tua implementazione:

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

task track(){

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

Ricorda che abbiamo dato finduna priorità più alta. Pertanto, se il robot non è in grado di rilevare lightSensor, andrà a spirale cercando di trovare la linea. Una volta fatto, trackentra in gioco. Come puoi vedere, la elsecondizione di tracknon accade mai.

Mentre funziona, il robot si muoverà molto goffamente. Non c'è molto che puoi fare al riguardo, data l'attuale build del tuo robot.


Anche se ho già risposto alla tua domanda, ma ecco un semplice miglioramento del monitoraggio della linea:

Invece di un sensore di luce, utilizzare due; ls_lefte ls_right. Usando (almeno) due sensori, puoi capire se sei totalmente fuori pista o se stai per uscire. Nel secondo caso, puoi facilmente girare nella direzione corretta e tornare in pista.

Il tuo findcompito è simile:

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

Cioè, vai a spirale solo se non senti nulla

Il tuo trackcompito ora diventa più efficiente:

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
    }
}

Ovviamente, con una matrice di sensori di luce, puoi giudicare meglio quanto stai andando fuori pista (cioè con quale angolo) e decidere meglio come tornare in pista (cioè con quale velocità angolare).


4

risposta breve; no, devi davvero fare le cose in modo leggermente diverso.

lunga risposta incompleta; Lascia che ti dia un codice psuedo appropriato per robotC, che ti mette su una strada migliore. Innanzitutto, non utilizzare le attività: questo NON è lo scopo delle attività robotC. Potrebbero essere fatti funzionare, forse, forse no (e hai bisogno di alcune modifiche anche solo per provare).

// 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();   
   }
}

ci sono un paio di cose qui; la priorità diventa irrilevante. Per quanto sembri avere compiti in robotC con priorità, non sono una buona scelta per l'implementazione di sussidi nella mia esperienza. Per motivi come, le priorità non sono sempre rispettate, le attività non possono essere interrotte (a volte), quindi quando si verifica un evento con priorità più elevata, non reagirà come previsto, robotC solo di recente è rientrato, quindi cose come l'accesso a un sensore da più di 1 attività può essere rischiosa (problemi di temporizzazione I2C) e in alcuni casi non lo è (sensori con polling automatico).

Puoi aggiungere la tua implementazione prioritaria al ciclo sopra mentre fai funzionare le cose, ma in realtà non è necessario per iniziare.

Il tuo commento "// inscatola l'ostruzione" descrive un comportamento balistico. Questi sono un po 'difficili da implementare utilizzando il multi-tasking. Il semplice ciclo che ho usato lo rende molto più semplice e migliore per i principianti / l'apprendimento.

L'altra cosa che ti lascerò, è che la sussunzione, pur essendo ordinata e appropriata per molte cose, non è un buon modo per attuare ciò che è meglio fare tradizionalmente. In effetti, la parte "eludere" può essere un buon candidato per l'assunzione, ma onestamente l'altro tuo compito dovrebbe essere chiamato "GoOnAboutYourBusiness". Dico questo perché probabilmente non vuoi cambiare dalla ricerca al seguire con la sussunzione. Gestisci quelli con i tradizionali loop di programmazione. Con un singolo sensore, - la luce rilevata è più scura o più chiara dell'ultimo ciclo? se è diventato più scuro (supponendo che la linea nera) continui a girare nella stessa direzione, se è diventato più chiaro, gira dall'altra parte, se è rimasto lo stesso, vai dritto. Probabilmente devi aggiungere un po 'di PID e usare una curva di sterzo invece di girare a sinistra e a destra per essere più fluido.

E sì, più sensori aiutano. http://www.mindsensors.com/ - sì, sono io nel film attualmente (11/10/2012)

Aggiornamento: codice attuale

Lo proverò tra poco, ma compila e illustra ciò che ho scritto sopra:

#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
    }
}

Sono d'accordo che questo problema è più facilmente risolto con un semplice ciclo. Non capisco perché qualcuno dovrebbe sottovalutare questo.
Shahbaz,

Non voglio lasciare l'impressione che sia più facile risolverlo con un semplice ciclo, ma piuttosto l'impressione che sia l'uso corretto della sussunzione per usare un semplice ciclo come uno dei compiti. Chiunque abbia declassato ha punti mod e nessuna comprensione della sussunzione. Non scoprirai che non ci sono molte persone che fanno un sussidio su un LEGO NXT (implicito usando robotC), quindi non aspettarti che il codice sia prontamente disponibile per essere incollato.
Spiked3

Sì, mi chiedo perché l'OP stava usando compiti per qualcosa di semplice come la sussunzione.
Rocketmagnet,

Perché è un errore per principianti molto molto molto molto molto comune con robotC: provare a utilizzare i compiti per tutto. Vorrei che lo spostassero in un'area solo avanzata.
Spiked3,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.