Prawidłowy sposób korzystania z architektury Subsumption z robotem C.

11

Ostatnio dużo czytałem o architekturze Subsumption i istnieje kilka różnych sposobów, za którymi ludzie się opowiadają.

Na przykład niektóre osoby używają globalnej zmiennej „flag”, aby mieć kontrolę nad zadaniem. Inni używają endTimeSlice()i pozwalają arbitrom naprawdę wybrać. I myślę, że to prawda.

Mam tę małą sekcję kodu RobotC , nad którą pracuję dla linii następującej po robocie, ale nie jestem pewien, czy robię to dobrze, ponieważ obecnie metoda śledzenia zawsze przejmuje metodę znajdowania. Prawidłowy przepływ powinien być taki, że znalezienie powinno prowadzić robota do linii za pomocą spiralnej ścieżki, aby znaleźć linię. Po znalezieniu linii tor powinien przejąć kontrolę.

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

Po prostu użyłem tutaj kilku komentarzy zamiast faktycznego kodu, aby był krótki. Czy moje stwierdzenia nie są wystarczająco dobre jako warunki, ponieważ gdy robot jest poza linią, track()przejmuje kontrolę. Czy wynika to z oświadczenia else w ramach śledzenia? Jeśli tak, to w jaki sposób track()wykonywać skręty, gdy straci linię bez przejmowania paszy na początku programu?

James MV
źródło
1
Podane liczby StartTasksą priorytetem zadania? Czy 9 będzie najwyższym priorytetem? W takim przypadku nie powinno findmieć większego priorytetu niż track? W rzeczywistości stan findi elsestan tracksą takie same. Więc jako człowiek, jeśli wartość czujnika jest większa niż próg, co byś zrobił? Iść spiralnie lub obrócić, aby ustawić linię?
Shahbaz,
Wiem, co to jest architektura subskrypcji, i jestem pewien, że twoje roboty powinny mieć różne moduły sprzętowe odpowiedzialne za zadania obliczeniowe niskiego poziomu. I że kod powinien faktycznie absorbować sygnał, a nie tylko nasłuchiwać stanu warunkowego. Więc nie powinieneś przekazywać sygnału do innego sprzętu? Teraz moim problemem jest to, że mam dwa Arduino i chcę, aby Arduino A pochłaniał lub hamował sygnał z Arduino B. Czy ktoś ma pojęcie, jak to się robi? Jak mogę podłączyć Arduino A, aby wysłać sygnał do Arduino B, ale nie jestem pewien, jak zasłonić lub zahamować sygnał
Ambient Soda

Odpowiedzi:

7

Z subsumcji Architektury, należy starannie zaprojektować zachowań w taki sposób, że jeśli przypisać zadanie Tpriorytetowe n, to Tpowinno być to, co robot powinien zrobić, jeśli wszystkie zadania z wyższym priorytetem niż nsą ignorowane.

Zamówmy przykładowe zadania, a następnie wymyślmy sposób ich realizacji. Twoje zadania są evade, findi track.

Zasadniczo chciałbyś, aby robot śledził linię. Jeśli jednak nie może wykryć linii, powinien spróbować ją znaleźć. Przede wszystkim powinien omijać przeszkody. To daje nam następujące zamówienie:

  • Najwyższy priorytet: evade
  • Następnie: find
  • Następnie: track

Powodem findma wyższy priorytet niż trackjest to, że, jak już wspomniano powyżej, będzie tracktylko wtedy, gdy evadei findsą niepotrzebne. Jeśli umieścisz findponiżej track, oznacza to, że zaczynasz śledzić, jeśli nie ma przeszkody, nawet jeśli nie jesteś na linii.

Teraz spójrzmy na twoją implementację:

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

task track(){

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

Pamiętaj, że nadaliśmy findwyższy priorytet. Dlatego jeśli robot nie może wyczuć lightSensor, pójdzie spiralnie, próbując znaleźć linię. Kiedy to nastąpi , trackrozpoczyna się. Jak widać, elsestan tracknigdy się nie zdarza.

Podczas gdy to działa, robot poruszałby się bardzo niezręcznie. Biorąc pod uwagę obecną wersję robota, niewiele można z tym zrobić.


Chociaż już odpowiedziałem na twoje pytanie, ale oto prosta poprawa śledzenia linii:

Zamiast jednego czujnika światła użyj dwóch; ls_lefta ls_right. Korzystając z (co najmniej) dwóch czujników, możesz zrozumieć, czy jesteś całkowicie poza torem, czy zamierzasz z niego wyjść. W drugim przypadku możesz łatwo skręcić we właściwym kierunku i wrócić na właściwy tor.

Twoje findzadanie jest podobne:

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

Oznacza to, że wchodzisz w spiralę tylko wtedy, gdy niczego nie wyczuwasz

Twoje trackzadanie staje się teraz bardziej wydajne:

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

Oczywiście dzięki matrycy czujników światła możesz lepiej ocenić, jak źle zboczysz z trasy (tj. Pod jakim kątem) i lepiej zdecydować, jak wrócić na tor (tj. Z jaką prędkością kątową).

Shahbaz
źródło
4

krótka odpowiedź; nie, naprawdę musisz robić rzeczy całkiem inaczej.

długa niepełna odpowiedź; Pozwól, że dam ci kod psuedo odpowiedni dla robota C, który stawia cię na lepszej ścieżce. Po pierwsze, nie używaj zadań - nie do tego służą zadania robotC. Można je zmusić do pracy, a może nie (i trzeba sporo zmian, aby spróbować).

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

jest tu kilka rzeczy; priorytet staje się nieistotny. Choć wydaje się, że zadania w robocie C mają priorytety, wydaje się, że z mojego doświadczenia nie są dobrym wyborem do wdrożenia subskrypcji. Z powodów takich jak priorytety nie zawsze są honorowane, zadania nie mogą być przerywane (czasami), więc gdy wystąpi zdarzenie o wyższym priorytecie, nie zareaguje tak, jak się spodziewasz, robotC dopiero niedawno ponownie wszedł, więc rzeczy takie jak dostęp do czujnika od więcej niż jednego zadania może być ryzykowne (problemy z synchronizacją I2C), aw niektórych przypadkach nie jest (automatycznie odpytywane czujniki).

Możesz dodać własną implementację priorytetu do powyższej pętli, gdy zaczniesz działać, ale tak naprawdę nie jest potrzebna na początku.

Twój komentarz „// zakreśl przeszkodę” opisuje zachowanie balistyczne. Te są nieco trudne do wdrożenia przy użyciu wielozadaniowości. Prosta pętla, którą zastosowałem, sprawia, że ​​jest to znacznie łatwiejsze i lepsze dla początkujących / uczących się.

Inną rzeczą, którą ci zostawiam, jest to, że zaspakajanie, chociaż jest schludne i odpowiednie dla wielu rzeczy, nie jest dobrym sposobem na wdrożenie tego, co lepiej zrobić tradycyjnie. Rzeczywiście część „unikania” może być dobrym kandydatem do subskrypcji, ale szczerze mówiąc, twoje inne zadanie powinno nazywać się „GoOnAboutYourBusiness”. Mówię to, ponieważ prawdopodobnie nie chcesz zmieniać z wyszukiwania na podążanie za subskrypcją. Obsługuj te z tradycyjnymi pętlami programowania. Za pomocą jednego czujnika - czy światło jest ciemniejsze lub jaśniejsze niż w ostatniej pętli? jeśli zrobiło się ciemniej (zakładając czarną linię), kontynuuj skręcanie w tym samym kierunku, jeśli stało się jaśniejsze, obróć w drugą stronę, jeśli pozostało to samo, idź prosto. Prawdopodobnie trzeba dodać PID i użyć krzywej sterowania zamiast po prostu skręcać w lewo i prawo, aby być płynniejszym.

I tak, wiele czujników pomaga. http://www.mindsensors.com/ - tak, to ja w filmie obecnie (11/10/2012)

Aktualizacja: aktualny kod

Wypróbuję to za chwilę, ale kompiluje i ilustruje to, co napisałem powyżej:

#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
    }
}
Kolczasty 3
źródło
Zgadzam się, że ten problem można łatwiej rozwiązać za pomocą prostej pętli. Nie rozumiem, dlaczego ktoś miałby głosować za tym.
Shahbaz,
Nie chcę pozostawić wrażenia, że ​​łatwiej jest rozwiązać za pomocą prostej pętli, ale raczej wrażenie, że JEST to prawidłowe użycie subskrypcji, aby użyć prostej pętli jako jednego z zadań. Ktokolwiek obniżył to, ma punkty modowe i nie rozumie subumcji. Nie przekonasz się, że niewiele osób robi subskrypcje na LEGO NXT (implikowane przez użycie robotaC), więc nie oczekuj, że kod będzie łatwo dostępny do wklejenia.
Spiked3
Tak, zastanawiam się, dlaczego OP używa zadań do czegoś tak prostego jak subskrypcja.
Rocketmagnet
Ponieważ jest to bardzo, bardzo, bardzo, bardzo powszechny błąd początkujący w robotC - próba użycia zadań do wszystkiego. Chciałbym, żeby przenieśli go do obszaru tylko dla zaawansowanych.
Spiked3