Richtige Verwendung der Subsumption Architecture mit Robot C.

11

Ich habe in letzter Zeit viel über Subsumption Architecture gelesen und es gibt verschiedene Möglichkeiten, wie sich die Leute dafür einsetzen.

Zum Beispiel verwenden einige Leute eine globale "Flag" -Variable, damit eine Aufgabe die Kontrolle übernimmt. Andere benutzen das endTimeSlice()und erlauben dem Schiedsrichter, wirklich zu wählen. Und ich denke das ist richtig.

Ich habe diesen kleinen Abschnitt des RobotC- Codes, an dem ich für einen Zeilenfolge-Roboter arbeite, bin mir aber nicht sicher, ob ich es richtig mache, da derzeit die Track-Methode immer die Find-Methode übernimmt. Der richtige Fluss sollte sein, dass der Fund den Roboter über einen spiralförmigen Pfad zur Linie führt, um die Linie zu finden. Sobald die Linie gefunden ist, sollte die Spur übernehmen.

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

Ich habe hier nur einige Kommentare anstelle des eigentlichen Codes verwendet, um es kurz zu halten. Sind meine if-Aussagen als Bedingungen nicht gut genug, denn wenn der Roboter aus der Reihe ist, track()übernimmt. Liegt das an der else-Anweisung in der Spur? Wenn ja, wie kann track()man Züge durchführen, wenn die Leine verloren geht, ohne zu Beginn des Programms das Futter zu übernehmen?

James MV
quelle
1
Sind die Zahlen, die Sie eingeben StartTask, die Priorität der Aufgabe? Wird 9 die höchste Priorität haben? Sollte in diesem Fall nicht findmehr Priorität haben als track? In der Tat sind der Zustand von findund der elseZustand von trackgleich. Was würden Sie als Mensch tun, wenn der Sensorwert größer als der Schwellenwert ist? Auf Spirale gehen oder drehen, um die Linie anzupassen?
Shahbaz
Ich weiß, was Subsumtionsarchitektur ist, und ich bin sicher, dass Ihre Roboter unterschiedliche Hardwaremodule haben sollten, die die Verantwortung für Rechenaufgaben auf niedriger Ebene übernehmen. Und dass der Code tatsächlich das Signal absorbieren sollte, anstatt nur auf einen bedingten Zustand zu warten. Sollten Sie das Signal also nicht an eine andere Hardware weiterleiten? Jetzt ist mein Problem, dass ich zwei Arduinos habe und ich möchte, dass Arduino A ein Signal von Arduino B subsumiert oder sperrt. Hat jemand eine Idee, wie das gemacht wird? Da ich Arduino A verkabeln kann, um ein Signal an Arduino B zu senden, bin ich mir nicht sicher, wie ich das Signal subsumieren oder sperren soll
Ambient Soda

Antworten:

7

Mit Subsumtion Architektur, sollten Sie sorgfältig Ihr Verhalten in so gestalten , dass , wenn Sie assign Aufgabe TPriorität n, dann Tsollte das sein , was der Roboter tun soll , wenn alle Aufgaben mit höherer Priorität als nignoriert werden.

Lassen Sie uns Ihre Beispielaufgaben bestellen und dann einen Weg finden, sie zu implementieren. Ihre Aufgaben sind evade, findund track.

Im Allgemeinen möchten Sie, dass der Roboter eine Linie verfolgt. Wenn es die Linie jedoch nicht erkennen konnte, sollte es versuchen, sie zu finden. Vor allem sollte es Hindernissen ausweichen. Dies gibt uns die folgende Reihenfolge:

  • Höchste Priorität: evade
  • Dann: find
  • Dann: track

Der Grund findhat eine höhere Priorität als der, trackdass Sie, wie oben erwähnt, tracknur dann evadeund findunnötig sind. Wenn Sie findunten angeben track, bedeutet dies, dass Sie mit der Verfolgung beginnen, wenn kein Hindernis vorhanden ist, auch wenn Sie nicht in der Leitung sind.

Schauen wir uns nun Ihre Implementierung an:

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

task track(){

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

Denken Sie daran, dass wir findeine höhere Priorität gegeben haben. Wenn der Roboter das nicht erkennen kann lightSensor, wird er spiralförmig versuchen, die Linie zu finden. Sobald dies der Fall ist, tracktritt ein. Wie Sie sehen können, tritt der elseZustand von tracknie auf.

Während dies funktioniert, würde sich der Roboter sehr ungeschickt bewegen. Angesichts des aktuellen Aufbaus Ihres Roboters können Sie nicht viel dagegen tun.


Ich habe Ihre Frage zwar bereits beantwortet, aber hier ist eine einfache Verbesserung Ihrer Leitungsverfolgung:

Verwenden Sie anstelle eines Lichtsensors zwei. ls_leftund ls_right. Mit (mindestens) zwei Sensoren können Sie nachvollziehen, ob Sie sich vollständig außerhalb der Spur befinden oder kurz davor stehen, die Spur zu verlassen. Im zweiten Fall können Sie leicht in die richtige Richtung abbiegen und wieder auf Kurs kommen.

Ihre findAufgabe ist ähnlich:

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

Das heißt, Sie gehen nur dann spiralförmig vor, wenn Sie überhaupt nichts spüren

Ihre trackAufgabe wird jetzt effizienter:

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

Mit einer Matrix von Lichtsensoren können Sie natürlich besser beurteilen, wie schlecht Sie aus der Spur geraten (dh mit welchem ​​Winkel), und besser entscheiden, wie Sie wieder auf die Spur kommen (dh mit welcher Winkelgeschwindigkeit).

Shahbaz
quelle
4

kurze Antwort; Nein, Sie müssen die Dinge wirklich ganz anders machen.

lange unvollständige Antwort; Lassen Sie mich Ihnen einen für robotC geeigneten Pseudocode geben, der Sie auf einen besseren Weg bringt. Verwenden Sie zunächst keine Aufgaben - dies ist NICHT der Zweck von robotC-Aufgaben. Sie könnten zum Funktionieren gebracht werden, vielleicht, vielleicht auch nicht (und Sie müssen einige Änderungen vornehmen, um es überhaupt zu versuchen).

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

hier gibt es ein paar Dinge; Priorität wird irrelevant. So schön es scheint, Aufgaben in robotC mit Prioritäten zu haben, sie sind meiner Erfahrung nach keine gute Wahl für die Implementierung von Subsumtionen. Aus Gründen wie Prioritäten werden Prioritäten nicht immer eingehalten, Aufgaben können (manchmal) nicht unterbrochen werden. Wenn also ein Ereignis mit höherer Priorität eintritt, reagiert es nicht wie erwartet. RobotC wurde erst kürzlich wieder eingeführt, sodass beispielsweise auf einen Sensor zugegriffen werden kann von mehr als einer Aufgabe kann riskant sein (I2C-Timing-Probleme), und in einigen Fällen ist dies nicht der Fall (automatisch abgefragte Sensoren).

Sie können der obigen Schleife Ihre eigene Prioritätsimplementierung hinzufügen, wenn die Dinge funktionieren, aber sie wird für Starts wirklich nicht benötigt.

Ihr Kommentar "// Box the Obstruction" beschreibt ein ballistisches Verhalten. Diese sind mit Multitasking etwas schwierig zu implementieren. Die einfache Schleife, die ich verwendet habe, macht es viel einfacher und besser für Anfänger / Lernen.

Die andere Sache, die ich Ihnen überlassen werde, ist, dass die Subsumtion, obwohl sie ordentlich und für viele Dinge angemessen ist, kein guter Weg ist, um das umzusetzen, was traditionell besser gemacht wird. In der Tat mag der Teil "Ausweichen" ein guter Kandidat für die Subsumtion sein, aber ehrlich gesagt sollte Ihre andere Aufgabe "GoOnAboutYourBusiness" heißen. Ich sage das, weil Sie wahrscheinlich nicht von der Suche zur Verfolgung mit Subsumtion wechseln möchten. Behandeln Sie diese mit herkömmlichen Programmierschleifen. - Wird bei einem einzelnen Sensor das Licht dunkler oder heller wahrgenommen als bei der letzten Schleife? Wenn es dunkler wird (unter der Annahme einer schwarzen Linie), drehen Sie weiter in die gleiche Richtung. Wenn es heller wird, drehen Sie sich in die andere Richtung. Wenn es gleich bleibt, fahren Sie geradeaus. Sie müssen wahrscheinlich etwas PID hinzufügen und eine Lenkkurve verwenden, anstatt nur nach links und rechts zu drehen, um glatter zu sein.

Und ja, mehrere Sensoren helfen. http://www.mindsensors.com/ - Ja, das bin ich derzeit im Film (10.11.2012)

Update: aktueller Code

Ich werde dies in Kürze ausprobieren, aber es kompiliert und illustriert, was ich oben geschrieben habe:

#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
    }
}
Spiked3
quelle
Ich bin damit einverstanden, dass dieses Problem mit einer einfachen Schleife leichter gelöst werden kann. Ich verstehe nicht, warum jemand dies ablehnen sollte.
Shahbaz
Ich möchte nicht den Eindruck hinterlassen, dass es einfacher ist, mit einer einfachen Schleife zu lösen, sondern den Eindruck, dass es die richtige Verwendung der Subsumtion ist, eine einfache Schleife als eine der Aufgaben zu verwenden. Wer auch immer es herabgestuft hat, hat Mod-Punkte und kein Verständnis der Subsumtion. Sie werden nicht feststellen, dass es nicht viele Leute gibt, die eine Subsumtion auf einem LEGO NXT durchführen (impliziert durch die Verwendung von robotC). Erwarten Sie also nicht, dass Code zum Einfügen verfügbar ist.
Spiked3
Ja, ich frage mich, warum das OP Aufgaben für etwas so Einfaches wie Subsumtion verwendet hat.
Raketenmagnet
Weil es ein sehr sehr sehr sehr sehr häufiger Anfängerfehler bei robotC ist - zu versuchen, Aufgaben für alles zu verwenden. Ich wünschte, sie würden es in einen Bereich nur für Fortgeschrittene verschieben.
Spiked3