Thursday, November 18, 2010

Lab 10 - BumperCar (Behavior-based Architecture)

Dato: 18/11 2010
Varighed: 11-14
Deltagere: Daniel, Leni, Martin A og Martin N

1. Formål
Formålet med denne øvelse er at arbejde med behavior-based arkitektur i form af LejOS' indbyggede subsumption klasse og implementere blocking og non-blocking behaviors.

2. Øvelsesgennemgang

Press the touch sensor and keep it pressed. What happends ? Explain.

Bilen kører baglæns og drejer til venstre, holder en kort pause, og kører derefter baglæns og drejer til venstre igen. Dette fortsætter så længe knappen er holdt inde.

Dette kan forklares ved at DetectWall klassens takeControl() funktion bliver kørt, hvilket tager et kort øjeblik grundet ultralydssensoren. Når denne funktion returnerer true (da knappen er trykket ind), bliver klassens action() udført, hvilket er at dreje den ene motor 180 grader, og den anden 360 grader. Dette resulterer i at bilen bakker og derefter drejer til venstre.
Programmet fortsætter nu med at afvikle Arbitratorens løkke der leder efter den behavior med højest prioritet der returnerer true fra takeControl(). Da DetectWall klassen har højest prioritet, bliver dens takeControl() først afviklet, og da den returnerer true, bliver DriveForward klassens takeControl() ikke afviklet.


Both DriveForward and DetectWall have a method takeControl that are called in the Arbitrator. Investigate the source code for the Arbitrator and figure out if takeControl of DriveForward is called when the triggering condition of DetectWall is true.

Som beskrevet ovenfor, så bliver DriveForward klassens takeControl() ikke afviklet når DetectWall (der har højere prioritet) returnerer true fra dens takeControl().

Dette sker i Arbitratorens Monitor loop:

for (int i = maxPriority; i >= 0; i--)
{
  if (_behavior[i].takeControl())
  {
    _highestPriority = i;
    break;
  }
}

Implement a third behavior, Exit. This behavior should react to the ESCAPE button and call System.Exit(0) if ESCAPE is pressed. Exit should be the highest priority behavior. Try to press ESCAPE both when DriveForward is active and when DetectWall is active. Is the Exit behavior activated immediately ?

Når DriveForward er aktiv, så slutter programmet med det samme når der trykkes på Escape, men hvis DetectWall er aktiv, skal Escape holdes nede i lidt tid inden programmet afsluttes.

What if the parameter to Sound.pause(20) is changed to 2000 ? Explain

Nu bliver bilen ved med at køre fremad et stykke tid efter den har ramt væggen, og Escape skal holdes nede i lang tid for at afslutte programmet.
Dette skyldes at DetectWall klassens takeControl() tager 2 sekunder at afvikle, og i denne tid bliver der hverken reageret på Escape eller på tryksensoren.

To avoid the pause in the takeControl method of DetectWall a local thread in DetectWall could be implemented that sample the ultrasonic sensor every 20 msec and stores the result in a variable distance accessible to takeControl. Try that.

Ved at implementere denne tråd, bliver takeControl() i DetectWall simplere og hurtigere at udføre:

DetectWall::takeControl()
{
  return touch.isPressed() || detector.getDistance() < 25; 
}

Vores implementering af tråden til at overvåge ultralydssensoren ser således ud:

class UltraSoundDetector extends Thread
{
  private int Distance;
  private UltrasonicSensor sensor;
  public UltraSoundDetector(SensorPort port) {
    sensor = new UltrasonicSensor(port);
  }
  public void run() {     while(true) {
      Distance = sensor.getDistance();
      try {Thread.sleep(20);} catch (Exception e) {}     }
  }
  public int getDistance() {
    return Distance;
  }
}

Ulempen ved denne metode er dog at Arbitratoren igen blokeres i 1 sekund af gangen når DetectWall's action udføres.

Try to implement the behavior DetectWall so it can be interrupted and started again e.g. if the touch sensor is pressed again while turning.

Denne funktionalitet er besværlig at implementere med subsumption arkitekturen, men det lykkedes at få det til at virke, ved at implementere en Dummy behaviour med en prioritet lige under DetectWall. Ved at lave et unblocking RotateTo() kald i DetectWall, og derefter lade Dummy klassen returnere true fra takeControl() så længe DetectWall er ved at udføre sin motor kommando (tjekkes med Motor.C.isBackward(), da Motor.C.isRotating() ikke er implementeret i vores version af LejOS...), er det muligt for højere prioritets behaviors at interrupte DetectWall's "kør baglæns og drej" funktionalitet.

Hvis vi ikke havde inkluderet denne Dummy klasse, så ville DriveForward få kontrollen over motorene, så snart DetectWall ikke længere modtager input fra sensorne, og derfor vil DetectWall ikke nå at køre særligt langt tilbage eller dreje, inden DriveForward vil sætte motorerne til at køre fremad igen. Resultatet ville være at bilen køre frem og tilbage ind i muren uden at komme væk.

How could takeControl be programmed to give high values when touch is pressed and lower values when it is ok to reactivate the action method ?

Dette kunne implementeres ved at returnere en høj værdi når sensorene er aktive, og en lavere værdi når Motor.A.rotating() er aktiv.

3. Konklusion
Det lykkedes at implementere en bil der indeholde 3 forskellige uafhængige behaviours og en central Arbitrator. Ved at benytte en Dummy behaviour kunne vi desuden lave samtlige behaviours non-blocking, og derved sikre at en højere prioritets behaviour bliver kørt så snart dens trigger condition er opfyldt.

4. Referencer
The leJOS Tutorial, Behavior Programming

Thursday, November 11, 2010

Lab 09 - Navigation

Dato: 11/11 2010
Varighed: 11-14
Deltagere: Daniel, Leni, Martin A og Martin N

1. Formål
Formålet med denne øvelse er at lave eksperimentelle tests som tydeliggøre effektiviteten af NXT motorernes tacho countere til at holde styr på henholdsvis position og retning. Derudover vil tests blive udført for at teste TachoPilot samt SimpleNavigator (tidligere TachoNavigator) klasserne som er implementeret direkte i leJOS API'et.

Robotten som anvendes til denne øvelse benytter differential drive, og modellen er opbygget ud fra LEGO Car 9797 byggevejledningen.

2. Øvelsesgennemgang
Øvelsen vil være opdelt i tre dele:
   1) Præcision for henholdsvis TachoPilot og SimpleNavigator klasserne
   2) Undvigelse af objekter under navigation
   3) Forbedring af robottens evne til at navigerer


Del 1 - Præcision af simple navigation
Indledningvis vil vi forsøge at implementerer adfærden fra Brian Bagnall's robot "Blightbot", hvor vi udnytter den eksperimentelle dataopsamling beskrevet af Maja Mataric's journal om robotten "Toto".

Robottens opgave vil derfor være at benytte henholdsvis TachoPilot og SimpleNavigator klasserne fra leJOS API'et til kontinuert at navigere i 4 taller , mens en tudspen fastgjort til robottens front optegner den rute som følges, således at bevægelserne danner et timeglas. Ved at lade robotten gennemløbe adskillige 4 taller, vil det være muligt eksperimentelt at afgøre hvilken af de to implementerede klasser er den mest præcise.

Brian Bagnall gør sig den observation at hans robot "Blightbot" (som udnytter TachnoNavigator klassen, der er forgængeren til SimpleNavigator klassen) i praksis måler de korrekte afstande mens den navigerer rundt, men at dens svaghed (i form af dårligere præcision) opstår når den skal dreje (roterer). Dette spørgsmål vil blive forsøgt besvaret til sidst i dette afsnit, for bedre at kunne inddrage egne observationer fra de udførte tests.

Implemetering
De indbyggede TachoPilot og SimpleNavigator klasser kræver begge kendskab til robottens hjuldiameter, afstand fra center-center mellem de to hjul samt hvilke motor porte der benyttes.

Afstanden mellem de to hjul blev fundet til værende 11,15cm, mens hjul diameteren blev målt til 3,8cm. Derfor initialiseres klasserne på følgende måder:


public TachoNav(String type) {
        float wheelDiameter = 3.8f;
        float trackWidth = 11.15f;
        
        if (type.equals("SimpleNavigator")) {
            navigator = new SimpleNavigator(wheelDiameter, trackWidth, Motor.C, Motor.A);
        } else {
            pilot = new TachoPilot(wheelDiameter, trackWidth, Motor.C, Motor.A);
        }
}


Da hjul diameteren samt hjul afstanden opgives i cm, vil de målte/ønskede afstande også fremgå i cm.
For ikke at skulle loade ny sourcekode over på NXT'en for at skifte mellem benyttelse af TachoPilot og SimpleNavigator under testene blev følgende main rutine dannet:

private static void main(String [] args) {
        String navigationType = "TachoPilot";


        buttonListen = new ButtonListen();
        Button.ESCAPE.addButtonListener(buttonListen);


        LCD.drawString("Nav style:", 0, 0);
        while (!Button.ENTER.isPressed()) {
            LCD.drawString(navigationType, 0, 1);


            if (Button.LEFT.isPressed() || Button.RIGHT.isPressed()) {
                if (navigationType.equals("SimpleNavigator")) {
                    navigationType = "TachoPilot     ";
                } else {
                    navigationType = "SimpleNavigator";
                }
                while(Button.LEFT.isPressed() || Button.RIGHT.isPressed());
            }
        }
        while (Button.ENTER.isPressed());


        TachoNav tn = new TachoNav(navigationType);


        if (navigationType.equals("SimpleNavigator")) {
            tn.SimpleNavigation(50.0f);
        } else {
            tn.PilotNavigation(100.0f);
        }
}




TachoPilot
TachoPilot implementeringen fungerer ved at benytte funktionskaldene travel(distance) samt rotate(degrees). Den ønskede bevægelse kan ses ud fra figuren i forrige afsnit.
Når travel() funktionen kaldes, benyttes tacho counterne seperat på de to motorer, til at udregne hvor langt den pågældende motor har kørt. Fordelen ved at benytte tacho counterne er at selvom motorerne ikke bevæger sig lige hurtigt (hvis den ene motor eksempelvis belastes mere eller er ældre), skulle dette blive udkompenseret da motorerne ikke bare er tændt i en given periode, hvilket også skulle få robotten til at køre mere ligeud.


private void PilotNavigation(float travelDist) {
        int loops = 5;
        float rotation = 135.0f;
        float turnSpeed = 60.0f;    // degress per second
        float speed = 20.0f;        // wheel diameter units per second


        pilot.setMoveSpeed(speed);
        pilot.setTurnSpeed(turnSpeed);


        LCD.drawString("Press ENTER", 0, 0);
        while ( !Button.ENTER.isPressed() );


        LCD.drawString("LET GO TO START!", 0, 0);
        while ( Button.ENTER.isPressed() );


        LCD.clear();


        LCD.drawString("Travelling...   ", 0, 0);
        LCD.drawString("Speed: ", 0, 1);
        LCD.drawString("Status:", 0, 3);
        LCD.drawString("Loops remaining: ", 0, 6);
        
        for(int i=loops; i!=0; i--) {
            LCD.drawInt(i, 0, 7);
            LCD.drawInt((int)speed, 8, 1);


            LCD.drawString("Travel          ", 0, 4);
            LCD.drawInt((int)travelDist, 8, 4);
            pilot.travel(travelDist);
            LCD.drawString("Rotating        ", 0, 4);
            LCD.drawInt((int)rotation, 10, 4);
            pilot.rotate(rotation);


            LCD.drawString("Travel          ", 0, 4);
            LCD.drawInt((int)travelDist, 8, 4);
            pilot.travel(travelDist*(float)Math.sqrt(2)/2);
            LCD.drawString("Rotating        ", 0, 4);
            LCD.drawInt((int)rotation, 10, 4);
            pilot.rotate(rotation);
            LCD.drawString("Travel          ", 0, 4);
            LCD.drawInt((int)travelDist, 8, 4);
            pilot.travel(travelDist);


            LCD.drawString("Rotating        ", 0, 4);
            LCD.drawInt((int)-rotation, 10, 4);
            pilot.rotate(-rotation);
            LCD.drawString("Travel          ", 0, 4);
            LCD.drawInt((int)travelDist, 8, 4);
            pilot.travel(travelDist*(float)Math.sqrt(2)/2);


            LCD.drawString("Rotating        ", 0, 4);
            LCD.drawInt((int)-rotation, 10, 4);
            pilot.rotate(-rotation);
        }
}    


SimpleNavigator
SimpleNavigator implementeringen benytter et internt koordinat system, samt tacho counterne til at holde styr på dens placering i forhold til dens udgangspunkt. Udgangspunktet vil altid være (0,0) i koordinatsystemet, hvorved robotten kan blive flyttet til en anden placering ved at benytte funktionerne goTo(x, y).

private void SimpleNavigation(float travelDist) {

        int loops = 5;
        float turnSpeed = 60.0f;    // degress per second
        float speed = 20.0f;        // wheel diameter units per second


        navigator.setMoveSpeed(speed);
        navigator.setTurnSpeed(turnSpeed);


        LCD.drawString("Press ENTER", 0, 0);
        while ( !Button.ENTER.isPressed() );


        LCD.drawString("LET GO TO START!", 0, 0);
        while ( Button.ENTER.isPressed() );


        LCD.drawString("Travelling...   ", 0, 0);
        LCD.drawString("Speed: ", 0, 1);
        LCD.drawInt((int)speed, 8, 1);
        LCD.drawString("Goto:  (   ,   )", 0, 4);
        LCD.drawString("Loops remaining:", 0, 6);


        for (int i=loops; i!=0; i--) {
            LCD.drawInt(i, 0, 7);


            LCD.drawInt((int)(2*travelDist), 3, 9, 4);
            LCD.drawInt(0, 3, 13, 4);
            navigator.goTo(2*travelDist, 0.0f);


            LCD.drawInt((int)(travelDist), 3, 9, 4);
            LCD.drawInt((int)(travelDist), 3, 13, 4);
            navigator.goTo(travelDist, travelDist);


            LCD.drawInt((int)(travelDist), 3, 9, 4);
            LCD.drawInt((int)-(travelDist), 3, 13, 4);
            navigator.goTo(travelDist, -travelDist);


            LCD.drawInt(0, 3, 9, 4);
            LCD.drawInt(0, 3, 13, 4);
            navigator.goTo(0.0f, 0.0f);
        }
}


Test resultater
Begge implementeringer blev testet eksperimentelt ved fastgøre en tudspen til fronten på robotten og placerer den på et whiteboard.
Robottens bevægelse blev derefter optaget med et kamera og kan ses herunder for både TachoPilot og SimpleNavigator klasserne:

Video af TachoPilot

Video af SimpleNavigator

Som det fremgår af begge videoer er bevægelserne stortset ens når de to klasser benyttes. Fælles for dem er at de i begge tilfælde med tiden bevæger sig mere og mere nedad. Dette vurderer vi skyldes at der opsamles mere støv på hjulene jo længere tid robotten kører. Herved mindskes hjulenes evne til at køre rundt på overfladen og pålideligheden af målingerne fra tacho counterne bliver derved mindre.

Betragter vi istedet billeder af robottens bevægelser ved brug af de to klasser er det muligt at se en lille forskel:

Billede af TachoPilot

Billede af SimpleNavigator

Vi ser at nøjagtigheden når robotten roterer ved at benytte SimpleNavigator klassen ser ud til at være marginalt bedre end ved TachoPilot. En forklaring herpå kunne være at SimpleNavigator ved at benytte det interne koordinatsystem giver en bedre kortlægning af robottens bevægelser end ved kun at benytte tacho counterne til at måle rotationerne.

Som tidligere nævnt observerer Brian Bagnall at hans robot BlightBot har en mindre præcision når den skal roterer end når den kører ligeud. XXX noget med tacho counterne, ingen bud!

Del 2 - Undvigelse af objekter under navigation
Test

Del 3 - Forbedring af robottens evne til at navigerer
Vi nåede aldrig at gennemfører dette punkt, da vi brugte en del tid på at få robotten til at kunne undvige objekter under navigering. Dette blev prioriteret da vi følte at dette aspekt kunne blive aktuelt i forhold til eksamensprojektet.

3. Konklusion
XXX

4. Referencer
XXX

Lab 08 - Subsumption architecture

Dato: 07/10 2010
Varighed: 11-14
Deltagere: Daniel, Leni, Martin A og Martin N

1. Formål
Formålet med denne øvelse er at implementere forskellige observerbare opførseler på baggrund af subsumption arkitekturen.

2. Øvelsesgennemgang
Del 1:
Første del af øvelsen bestod i at afprøve et i forvejen implementeret subsumption program på LEGO bilen og se om de forskellige opførseler kunne identificeres.

Vi iagttog 3 forskellige scenarier, hvor følgende output ses på LCD displayet:

#1 Bilen kører fremad:
Sound Car
Drive 0 f
Avoid 0 45
Play 0

#2 Bilen er tæt på en væg, bilen bakker:
Sound Car
Drive 1 s
Avoid 0 9 b
Play 0

#3 Der afspilles en lyd fra bilen:
Sound Car
Drive 1 s
Avoid 1 41
Play 0 s

Ud fra disse resultater kan vi konkludere, at når der ikke foregår andet, kører bilen fremad.
Når der detekteres et objekt foran bilen, stopper den fremadkørende opførsel, og bilen bakker væk fra objektet og drejer mod venstre.
Hver 10. sekund stopper bilen uanset, hvordan den omgivelser er, og afspiller en lyd.

På LCD displayet ses om de 3 opførsler vil have bilen til hhv. at køre fremad (f), bagud (b) eller stoppe (s). Hvis der er et 1-tal ud for en opførsel, bliver dens output ignoreret.
Derudover vises afstanden målt med ultralydssensoren.

Del 2:
Ved at kigge i kildekoden, kan vi se at RandomDrive opførslen afvikles hele tiden, og konstant forsøger at opnå adgang til motorerne.

AvoidFront klassen har en betingelse der skal være opfyldt (distance > tooCloseThreshold), før den forsøger at få adgang til motorne. Når dette sker, blokeres RandomDrive klassen adgang til motorerne med et kald til funktionen suppress().

PlaySounds klassen aktiveres hvert 10. sekund, hvorefter først AvoidFront og RandomDrive klassens adgang til motorerne blokeres ved kald til supress(). Derefter afspilles en lyd, og endelig får de to undertrykkede klasser givet adgang til motorerne igen.

Del 3.1:
Alle klasserne der implementerer en opførsel, opretter en tråd til at afvikle denne opførsel parallelt med de andre. Disse tråde er oprettet som "daemon" tråde, hvilket sikrer at de stopper når hovedprogrammet lukkes.

Del 3.2:
Hver klasse, der implementerer en opførsel, indeholder metoder, der gør det muligt for højere prioriterede klasser at blokere dens adgang til motorerne.
Dette opnås ved, at den højere prioriterede klasse kan inkrementere variablen "suppressCount", hvorved den blokerede klasses adgang til motorerne forhindres. Når en klasse blokeres er det dens ansvar at blokere alle lavere prioriterede klasser som den selv har mulighed for at blokere; herved sikres det at kun den højest prioriterede tråd har adgang til de delte ressourcer.

Ved at bruge en integer i forbindelse med implementeringen af denne blokering, er det muligt for forskellige høj-prioritets tråde, at blokkere den samme lavere prioritets tråd, og sikre at den først frigives, når alle høj-prioritets opførsler er færdige.

I modsætning til arbiter metoden beskrevet af Fred Martin [1], er det ved suppression mekanismen de enkelte trådes opgave at blokere deres adgang til den delte ressource, hvorimod arbiteren håndtere al adgang til den delte ressource fra ét sted.

Ulempen ved at have denne adgangskontrol fordelt over flere klasser, er at der kan opstår problemer, hvis en klasse glemmer at blokere de lavere prioriterede tråde inden den selv skriver til den delte ressource.

Del 4:

3. Konklusion

4. Referencer
1: Fred Martin, [4, page 214-218]

Lab 07 - Braitenberg Vehicles

Dato: 28/10 2010
Varighed: 11-14
Deltagere: Leni, Martin A og Martin N

1. Formål
Formålet med opgaven er at få robotten til at reagerer på udefrakommende stimuli fra kilder som lyd og lys. Ved at koble sensorne direkte på robottens motorer ville en udefra observerende person kunne se en vis personlighed i robotten. I denne opgave skal der laves simple udgaver af denne form for reaktioner i robotten ved at lade robotten reagerer på lydstyrken eller hvor kraftig en lyskilde der opfanges.





2. Øvelsesgennemgang
Del 1:
I denne del opgave skal robotten reager på lyd ved at koble lyd sensorens output direkte på motorens indput og derved få robotten til at reagerer på dels lyd men også lydstyrken.






I videoen kan vi se at robotten alt efter lyd styrken reagerer med både at køre frem af samtidig med at hastigheden også er bestemt alt efter hvor højt støj niveauet er.

Del 2:
I denne del opgave skal vi anvende lys sensor til at få robotten til dels at køre mod lyset eller væk fra lyset.

Ser vi på billedet af robotten øverst på siden. Kan vi se at vi har placeret to lys-sensor, en i hver side af robotten. hver sensor bliver på samme måde som del 1 koblet direkte på motorerne, en sensor til hver motor. outputtet fra lys-sensoren bliver brugt til at sætte hastigheden på motoren.

Alt efter om vi vil have robotten til at køre mod lyset eller væk fra lyset byttes der bare om på kablerne fra lys sensorne så siden med den højeste værdi altså der hvor lyskilden er vil sætte den modsatte motors hastighed hvis vi vil køre væk og vice versa.




Del 3:
I denne del opgave skulle vi anvende lys-sensor der kørte væk fra lyset som vist i videoen under del 2: vi skulle yderligere placerer en lampe på robotten således at lys-kilden ville kunne få andre robotter til at undvige vores.

Ved at udnytte lys som stimuli til robotten var opgaven her at en udefra observerende person ville kunne se adfær og liv i robotten. lampen skulle give andre robotter en advarsel om at de skulle undvige en og vi ville kunne få en masse robotter til at køre rundt mellem hinanden og observerer at de ikke ville ramme hinanden.

Problemet er dog her at lyskilden ikke er ret skarp og derfor ikke advare robotten i tide om at den skal undvige. Der ud over skal robotterne være ens designet således at lyskilden og sensorne er placeret i samme højde så de rent faktisk ville kunne se hinanden køre rundt. disse faktorer gjorde at vi ikke fik denne opgave til at virke godt nok til at lave en video at det


3. Konklusion

Ved at bruge forskellige sensorer og stimuli kan man få ens robotter til dynamisk at reagerer på et dynamisk miljø og derved give robotten personlighed. så at sige give den liv.
Det er nogle simple proof of concepts der bliver lavet i denne opgave men er alligevel med til at vise konceptet i anvendelsen af omgivelserne til at styre en s robots handlinger.

4. Referencer

Introduction to Machina Speculatrix and Braitenberg Vehicles


Notes on construction of Braitenberg's Vehicles, Chapter 1-5 of Braitenbergs book