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

Friday, October 8, 2010

Lab 06 - Robot Race

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

1. Formål
Formålet med denne øvelse var at bygge en LEGO robot, der kunne følge bane "The Alishan train track" op til toppen og ned igen.



Følgende regler blev opstillet for gennemførslen af banen:

1) Robotten skal starte i startområdet
2) Ved tryk på Enter skal robotten følge banen op til toppen og tilbage til startområdet, hvor gennemløbstiden udskrives til displayet.
3) På toppen skal robotten være helt inden i "retrace" området, inden den vender om.


2. Øvelsesgennemgang

Foranalyse
Starten af denne øvelse gik med at overveje, hvilke erfaringer fra de tidligere øvelser med hhv. lyssensoren, LEGO bil og "Line Follower" vi kunne bruge til at bygge en robot der kunne gennemløbe "The Alishan train track" hurtigst muligt.

Konklusionen af denne foranalyse var følgende:

Den hurtigste robot vil være baseret på en open-loop controller, da der derved ikke skal reageres på input fra omverdenen, men blot følges et i forvejen bestemt sæt regler.
Det vil dog resultere i en løsning, der er meget følsom overfor ændringer i miljøet omkring den, da den ikke vil kunne kompensere for disse ændringer.
Det kunne bl.a. være; startsposition og -vinkel, batteriniveau samt motorbelastning grundet samlingen mellem LEGO klodserne.
Gruppen besluttede at lave en closed-loop løsning baseret på en PID regulator, vel vidende at vi muligvis vil blive slået af en godt justeret open-loop løsning. Dette gav os mulighed for at benytte den tidligere arbejde med en PID regulering og lave en mere generel løsning.

Størst topfart opnås enten ved at benytte store hjul eller gearing som medfører større belastning af motorerne.
Grundet LEGO motorernes lave drejningsmoment, vil hastighedsforøgelsen ved at benytte gearing være på bekostning af acceleration, og da gennemkørslen af "The Alishan train track" kræver 5 opbremsninger ved sving og relativt korte distancer, besluttede gruppen at benytte store hjul på robotten.

Mht. sensorerne, indså vi bl.a. på baggrund af artiklen A PID Controller For Lego Mindstorms Robots, at et stor detekteringsområde vil give robotten mulighed for at køre hurtigere, uden at miste banen af syne. Derfor besluttede vi at koble 4 lyssensorer til robotten, og kombinere deres sensorværdier til en enkelt værdi, der repræsenterer den sorte stregs position i forhold til robotten.

Design
Den første store opgave der skulle løse, var hvordan de 4 sensorer bedst kunne benyttes til at bestemme, hvor langt robotten var fra den sorte linie som den skulle følge.
Løsningen skulle give en enkelt værdi mellem -100 og 100, som repræsenterer hvor langt robottens midtpunkt er fra den sorte linie, som kan bruges til input til PID regulatoren.
Derudover ønskes et monotont stigende output fra sensorblokken for monotont stigende afstand fra den sorte linie, hvilket sikrer en jævn regulering.

Sensor arrayet blev opbygget af de gamle LEGO sensorer, da de kunne placeres tættest på hinanden, hvilket er nødvendigt for at sikre at den tynde sorte linie på "The Alishan train track", ikke blev "tabt" mellem detektionsområdet for to sidestillede sensorer.
Selv med disse mindre sensorer var det vanskeligt at detektere linien når den lå mellem to sensorer, og vi undersøgte derfor to mulige konfigurationer af sensorarrayet:

1. Sensorarray


2. Sensorarray

Vi besluttede at benytte den første mulighed, da det gav et større detektionsområde, og problemet med at opnå en pålidelig detektering af linien når den lå mellem to sensorer kunne løses i software.

Følsomheden af lyssensorerne blev undersøgt, ved at placere dem i en lukket beholder over et hvidt stykke papir, og derefter se hvilken værdi de returnerede fra getNormalizedLightValue() når deres lys var tændt. Dette gav vidt forskellige værdier for de 4 sensorer, så i stedet for at forsøge at finde 4 sensorer, der havde samme følsomhed, besluttede vi at implementere en individuel sensor normalisering baseret på floating point værdier for hver af de 4 sensorer.

Dette blev gjort ved at gemme en værdi for hvidt og sort underlagt for hver sensor, når robotten kalibreres inden start og derefter beregne en normaliseret sensorværdi for hver af de 4 sensorer ud fra følgende formel (i funktionen RobotRace::Normalize()):

normalized = 100*( 1 - (value - black)/(white - black) )

Herved opnås en værdi mellem 0 og 100 for hver af de 4 sensorer, hvor 0 repræsenterer hvidt og 100 repræsenterer sort. Årsagen til denne invertering af værdien bliver klargjort i efterfølgende.


Med det valgte sensorarray, blev der fortaget målinger for forskellige placeringer af den sorte linie i forhold til sensorarrayet som vist herunder:

Sensor 1: 1
Sensor 2: 4
Sensor 3: 3
Sensor 4: 97



Sensor 1: 3
Sensor 2: 2
Sensor 3: 15
Sensor 4: 70



Sensor 1: 3
Sensor 2: 4
Sensor 3: 34
Sensor 4: 32



Sensor 1: 3
Sensor 2: 5
Sensor 3: 67
Sensor 4: 13


Ud fra disse målinger indså vi at summen af to sidestående sensorer altid vil være tæt på 100 når den sorte streg er et sted mellem dem. Derfor gemmes værdien af Sensor1 + Sensor2, Sensor2 + Sensor3 og Sensor3 + Sensor4, hvorved der altid vil være mindst en variable med en værdi tæt på 100.
Ved at lade den sorte værdi udbrede sig mod venstre, ved at overskrive venstrestående sensorværdier med en højrestående, hvis den højrestående er større, opnås en monotont stigende værdi, når den sorte streg bevæges fra venstre mod højre på sensorarrayet.

Disse beregninger er implementeret i funktionen RobotRace::sensorValue(), der tager de 4 normaliserede sensor værdier som input, og beregner den sorte stregs placering.
Et udsnit af funktionen der viser den centrale funktionalitet er vist herunder:

sensor1 = sensor1 + sensor2;
sensor2 = sensor2 + sensor3;
sensor3 = sensor3 + sensor4;
sensor4 = sensor4;

if (sensor4 > sensor3) {
sensor3 = sensor4;
}
if (sensor4 > sensor2) {
sensor2 = sensor4;
}
if (sensor4 > sensor1) {
sensor1 = sensor4;
}

if (sensor3 > sensor2) {
sensor2 = sensor3;
}
if (sensor3 > sensor1) {
sensor1 = sensor3;
}

if (sensor2 > sensor1) {
sensor1 = sensor2;
}

return (sensor1 + sensor2 + sensor3 + sensor4)/4;


Returværdien fra denne funktion bruges som input til en PID regulator der er implementeret i classen PIDController.
For at bestemme værdien af de forskellige konstanter i PID regulatoren, benyttede vi Ziegler–Nichols metode, hvor der startes med en P-regulator, hvor Kp sættes til en størrelse der resulterer i oscillation. Denne Kp benævnes 'Ku', og oscillations perioden benævnes 'Pu'. Vi bestemte denne Pu ved at måle den samlede tid for 10 oscillationer når robotten følger en lige streg på banen.
Derudover blev PID loopets gennemløbstid 'dT' målt, ved at beregne den tid det tager for NXT bricken at gennemløbe beregningerne af sensorarray og opdatering af PID-controlleren 10000 gange i funktionen RobotRace::TestLoopRuntime(), vis funktionalitet er gengivet herunder:

int loopTime = (int)System.currentTimeMillis();
for (int i = 0; i != 10000; i++)
{
readout1 = Normalize(sensor1.getNormalizedLightValue(), 1);
readout2 = Normalize(sensor2.getNormalizedLightValue(), 2);
readout3 = Normalize(sensor3.getNormalizedLightValue(), 3);
readout4 = Normalize(sensor4.getNormalizedLightValue(), 4);

weightedRead = sensorValue(readout1, readout2, readout3, readout4);

error = weightedRead - 40.0F;
PIDValue = controller.controlLoop(error);
controlCar(PIDValue);
}
loopTime = (int)System.currentTimeMillis() - loopTime;

LCD.clear();
LCD.drawString("10K loop runtime (ms):", 0, 0);
LCD.drawInt(loopTime, 0, 1);
LCD.refresh();


Der blev målt følgende værdier: Ku = 900, Pu = 0,4s, dT = 0,5ms.
Ved at følge Ziegler–Nichols metode gav det os følgende PID konstanter:

Kp = 540
Ki = 0,81
Kd = 27

Vi endte dog med at øge vores Kp til 1000, for at opnå et hurtigere indsving, hvilket gjorde at robotten kunne håndtere svingene hurtigere, men stadig stabilt.


Den sidste del af designet bestod i at håndtere de forskellige zoner på "The Alishan train track", hhv. startzonen, retrace zonen og mål zonen.

Da robottens opførsel skulle være anderledes i disse zoner, besluttede vi at implementere en tilstandsmaskine, der træffer beslutninger om robottens tilstand ud fra sensorarray's værdier, inden PID regulatoren kaldes.
Derved er det muligt at tilsidesætte PID controllerens regulering af motorne, fx når robotten befinder sig i retrace zonen og derfor skal lave en 180 graders drejning inden den kører ned af banen igen.
Da vi benytter 4 sensorer, kan overgangen ind til retrace og mål zonen detekteres, ved at hode øje med om 3 eller flere sensorer ser en sort linie på samme tid, og for at undgå at svingene kunne trigge dette tilstandsskift, benyttede en timer, der sikrer at robotten har passeret begge sving inden den holder øje med om den er i retrace eller mål zonen.


3. Resultat
Videon af vores gennemløb kan ses herunder:


Tiden for gennemløb blev 40,6 sekunder (40652 ms).

3. Konklusion
Det lykkedes at bygge en robot baseret på closed-loop regulering vha. en PID controller, der kunne gennemføre "The Alishan train track" på 40,6 sekunder.
Denne tid blev opnået ved at benytte et sensorarray med stort detekteringsområdet, samt en tilstandsbaseret styring foran PID regulatoren til at holde styr på, hvilket område af banen som robotten befinder sig i, og herudfra justere dens opførsel.

4. Kildekode
Kildekoden til RobotRace robotten kan findes her.

Thursday, September 30, 2010

Lab 05 - Line Follower with Calibration

Dato: 30/9 2010
Varighed: 11-14
Deltagere: Daniel, Leni, Martin A og Martin N
1. Formål
2. Øvelsegennemgang
Billede af LEGO 9797 Line Follower with Calibration car:
Del 1 - Black White Detection
Denne del opgaver går ud at danne os nogle erfaringer med at anvende lys sensoren, ved at bruge BlackWhiteSensor.java programmet.
Programmet anvender readCalue() på sensoren denne værdi er den normaliserede værdi som tager den rå værdi og omregner den til et tal mellem 0 og 100, hvor hvid ligger mellem 55 - 65 og sort omkring 35. Som man kan se er dette et meget lille spektrum vi her arbejder i og andre farver ville være meget svære at finde frem til.
BlackWhiteSensor koden fungerer ved at man kalibrere sensoren ved først at læse sort ind og derefter hvid herefter ligges disse to værdier sammen og middel værdien bliver den som bestemmer om det er sort eller hvid robotten ser, denne metode fungerer ok hvis motoren ikke accelerer alt for meget så robotten kommer for langt væk fra linjen.
  • Black value: 357
  • White value: 550
  • Green value: 370-400
De tre værdier ovenfor viser resultaterne fra aflæsning af lys sensoren med metoden getNormalisedValue() som returnere de rå værdier fra lys sensoren, dette giver en bedre opløsning af sensorens aflæsninger dette havde i spørgsmålet omkring sort og hvid programmet ingen betydning da robotten alligevel delte spektrumnet i to dele tolkede alt som enten sort eller hvidt.
Men når programmet skal udvides til at finde flere farver ville et større spektrum gøre det nemmere at skelne mellem de enkelte farver.

Del 2 - Line Follower with Calibration
Video af, hvordan Line Follower with Calibration kørte:
I denne del brugte vi koden fra BlackWhiteSensor som udgangspunkt for at se om robotten var i stand til at anvende givne program til at følge linjen. og her viser problemstillingen nævnt ovenfor sig hurtigt i form af hvis robotten kommer for langt væk fra den sorte linje er den tabt og køre i ring altså robotten reagerer kun på farve ændringer.

Næste forsøg var så bruge regulering til at få robotten til bedre at kunne følge linjen og denne opgave blev gjort til vores aflevering 6 og robot kapløbet så for mere information omkring dette kig på afleveringen til opg. 6.

Del 3 - ColorSensor with Calibration

Med udgangspunkt i BlackWhiteSensor var opgaven her at udvide koden til at kunne kende forskel på mere end sort og hvidt. For at gøre dette skulle algoritmen omkring de to målte værdier og bare tage middelværdien af disse udskiftes.

Første forsøg var at tage udgangspunkt i målinger foretaget i del 1. og derefter bruge lidt af den samme tankegang som sort hvid metoden, nemlig ved at inddele spektrumnet i en sort hvid og grøn zone hvor alle værdier over grøn betraktes som hvid alt under grøn som sort og derefter alt i zonen som grøn.

Problemet her er at grøns værdi ikke ligger særligt langt væk fra sort og derfor kan det være meget svært at få robotten til at skelne mellem sort og grønt, dette gjorde at robotten en gang i mellem mens den kørte lavede nogle stop sekvenser i overgangsfasen mellem hvid og sort da disse overgange er flydende sker der nemlig det at på de rigtige tidspunkter vil blandingen af den sorte og hvide halvdel på banen ville blive til grøn og derfor signalerer stop til robotten.

For at gøre det sværere for robotten at læse værdien grøn mens den køre på banen blev der lavet først en cirkulær buffer hvor de aflæste værdier blev gemt og derefter midlet for at se om denne værdi var grøn og så reagerer på dette, men det samme problem var stadig til stede da en middel værdi ikke ændre på det faktum at der er tale om et flydende værdigrundlag fra sensoren og at en middelværdi bare ville kunne give flere områder hvor værdien grøn ville kunne blive fortolket af robotten.

det næste forsøg tog også udgangspunkt i en cirkulær buffer hvor der i stedet for en middel værdi blev forsøgt at huske på de sidste 5 værdier og så opstille en regel om at de sidste 5 værdier skulle være grønne før at det blev tolket som en grøn kommando. dette havde en delvis effekt robotten stoppede med at lave tilfældige stop kommandoer mens den kørte, fordi en tilfældig grøn værdi ikke længere ville blive tolket som stop kommando, men problemet nu var at 5 værdier i grøn zone var meget afhængig af om solen var bag en sky eller om skidt kom ind foran sensoren, og dette gjorde det meget tilfældig om robotten fik tolket rigtigt mens den var i den grønne zone.
Del 4 - Line Follower that stops in a Cole Zone
forsøget her blev ikke til en kørende robot da opgave 6 tog lidt over og resultatet kan ses i denne opgave.

Men i stedet lavede vi her en række test hvor vi i displayet skrev hvilken zone robotten kunne se via sensoren og dette blev brugt til at forsøge de ovennævnte metoder til at genkende den grønne farve i forhold til de sorte og hvide farver.

Et forsøg vi ikke fik nået at lave var at dedikere en sensorer til kun at kigge efter grøn ved at placerer en sensor løftet op i luften i forhold til de sort hvide sensor ville man kunne hæve det grønne spektrum op midt i mellem sort og hvid man ville kunne bruge aflæsninger over tid (e.g cirkulær buffer ) samt lave et større spekter for sensoren at tolke grønt i og give robotten en meget mere stabilt grundlag til at genkende grøn.
3. Konklusion

Opgaven var et godt grundlag til opg. 6 og blev også til grundlaget for den kode vi lavede til den opgave. der blev ligeledes gjort en masse erfaringer med anvendelsen af lys sensoren, dels omkring præcision og dels omkring problemstilligner omkring tolkning af sensorenes værdier.
4. Referencer

Lab 04 - Self-balancing robots



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

1. Formål
Formålet med denne øvelse var at bygge en LEGO SegWay, dvs. en selv balancerende robot. Der findes tre sensor opsætninger som kan anvendes til en sådan robot:
  • En traditionel lyssensor
  • Et accelerometer
  • Et gyrometer
Ved at anvende enten et accelerometer eller et gyrometer er det muligt at få det mest stabile respons og derigennem en robot hvis evne til at opretholde sin egen balance er forøget i forhold til kun at benytte en lyssensor.
Denne øvelse har imidlertid til opgave at give et indblik i hvorledes et selvregulerende system kan fungerer, hvortil en lyssensor vælges for at simplificerer opgaven.

2. Øvelsesgennemgang
Den selvbalancerende robot blev opbygget ud fra et design af Philippe Hurbain's NXTway, som er en portering af Steve Hassenplug's Legway fra RCX til NXT.


Et billede af den færdige robot kan ses herunder:


Selve reguleringen af robotten foregår igennem en PID regulator hvor kildekoden bygger på elementer fra Brian Bagnall's bog "Maximum Lego NXTBuilding Robots with Java Brains".


Den færdige robot fungerer ved at programmet startes, hvorefter robottens balance position findes. Når dette punkt er fundet, trykkes ENTER knappen ned for at definerer et offset.
Proportional regulatoren måler herefter de konstante ændringer som opstår ved at robottens motor med små ryk får den til at bevæge sig frem og tilbage. Den aktuelle værdi trækkes fra balance offsettet, og udgører sammen med Kp (proportional konstanten) P vægten i PID regulatoren.


De øvrige regulerings elementer vil ikke blive forklaret i dette afsnit, istedet er det muligt at se en video af hvordan den ovenfor byggede robot holder balancen:


Det er tydeligt at robotten har svært ved at holde balancen, hvor det faktum at robottens balance punkt ikke befinder sig ved 90grader har en stor del af skylden. Ligeledes blev det observeret at når balance punktet skulle indstilles, så var det svært at justerer det 100% korrekt da trykket på ENTER fik robotten til at rykke sig en smule.


Løsning af balance problem - SegWay Rider
Der var i gruppen enighed om at såfremt man justerede Kp, Ki og Kd så burde det være muligt at opnå et mere stabilt respons. Men istedet for at spilde en masse ressourcer på en LEGO model som efter vores opfattelse var "dårlig", valgte vi at opbygge en ny model. Grundlaget herfor var at vi ønskede en robot hvis naturlige balance punkt var placeret ved 90grader for at opnå en mere stabil robot.


En LEGO model af en reel Segway PT designet af Dave Parker blev brugt som grundmodel til vores SegWay Rider. Informationer samt bygge instruktioner kan findes på følgende side:
http://nxtprograms.com/segway/index.html


Selve koden til at få denne robot til at balancerer var bygget til LEGO's egen udviklingsplatform, men vi vurderede at den eksisterende PID regulator ville kunne justeres til at fungerer med denne LEGO model istedet.
Et billede af SegWay Rider kan ses herunder:


Fordelen ved denne model er at dens naturlige balance punkt befinder sig i en vinkel på 90grader, samt at den har sin vægt placeret "højt" over hjulene, hvorved den får et meget mere stabilt tyngdepunkt.


For at undgå at skulle benytte ENTER til at kalibrerer et balance punkt, blev denne robot designet således at man har 4 sekunder fra programmet startes til at finde balance punktet. Herefter samples den aktuelle værdi fra lyssensoren og benyttes som offset for alle fremtidige målinger. Dette viste sig at gøre det meget nemmere at gentage balance indstillingen med større præcision end før, da man kun skulle fokuserer på at opretholde robotten i dens naturlige balance punkt.
Koden til at foretage denne kalibrering kan ses herunder:

public void getBalancePos() throws Exception {
// turn up system volume to ensure user hears the beeps
Sound.setVolume(75);


// wait for 4 seconds before determining balance position (offset)
int timeOut = 3;
while (timeOut != 0) {
Sound.beep();
Thread.sleep(1000);
timeOut--;
}
// SegWay Rider must be balanced when we reach 0!!
offset = ls.readRawValue();
Sound.beepSequenceUp();
}

Selve reguleringen af SegWay Rider bygger på den tidligere omtalte PID regulator, hvor præcisionen af de forskellige målinger og del resultater blev forøget ved at anvende float istedet for int.


Herunder vil P, I og D leddene blive gennemgået for at forklare hvorledes disse udregnes:

float pError = avgValue - offset;

if (pError < 0)
pError = pError * 1.8F;

integralError = ((integralError + pError) * 2)/3;

float derivError = pError - prevError;
prevError = pError;

float PIDValue = KP * pError + KI * integralError + KD * derivError;

Først findes proportional fejlen som den aktuelle måling minus det fastsatte balance offset. Derefter afgøres det hvorvidt fejlen er negativ (svarende til at robotten tilter bagud). Er det tilfældet så justeres proportional fejlen med en faktor 1,8 for at kompenserer for den mindre mængde lys som sensoren kan se i forhold til et fremad tilt.
Integrations fejlen findes som summationen af alle tidligere fejl vægtet med 2/3, for at sikrer at de første målinger vil påvirke denne summation mindre og mindre som tiden går. Hvis denne skalering ikke anvendes vil værdierne løbe løbsk og få robotten til at oscillerer ud af kontrol.
Differential fejlen findes som den aktuelle proportional fejl minus den forrige fejl.


Disse tre led som samlet udgører PID regulatoren summeres efter deres individuelle konstanter er blevet ganget på, som det også fremgår af den sidste kodelinie ovenfor.


Forbedringer af stabiliteten
For at forbedrer stabiliteten af robotten var det nødvendigt at se på udsvingene i input værdierne til lyssensoren. Et plot af sensor målinger kan ses herunder for en NXT 2.0 farve lyssensor:

Målingerne er foretaget vha. den tidligere benyttede datalogger fra øvelse 3 på proportional fejlen. Plottet illusterer et udsving mellem +/- 20grader i forhold til robottens balance punkt. Det ses tydeligt at der er mange små skift i de målte værdier, men at kurve formen eller passer overens med det forventede. Endvidere er det værd at observerer at sample værdierne mellem 500-2500 begynder at bevæge sig mod nul selvom robotten stadig bevæger sig fremad.

Altså kan det konkluderes at såfremt robotten får et udsving på mere end ca. 10grader i fremadgåede retning, så vil målingerne ikke afspejle den reele retning. Det er derfor vigtigt at PID regulatoren fungerer hurtigt nok til at kunne udgå udsving større end +/- 10grader for at opretholde et stabilt respons.


De små udsving kan fjernes ved at lave en midling af de foregående samples, et såkaldt moving average filtrering. Efter nærmere test, kunne det konkluderes at det kun var muligt at midle over de 2 sidst målte samples for at robotten ikke kom ud over den ovenfor beskrevede +/- 10graders begræsning (grundet den højere sampletid).
Et plot af proportional fejlen for et 2. ordens moving average filtreret sensor input kan ses herunder:

Det ses udfra plottet at de små udsving tydeligt blevet fjernet, hvorfor det blev valgt at benytte denne midlede sample værdi. Koden til realisering af MA(2) filteret kan ses herunder:

public float MAFilter(float value) {
// update the delayline top->bottom and add new sensor value
delayline[0] = delayline[1];
delayline[1] = value;

// return the calculated average
return (delayline[0]+delayline[1])/2;
}

Hvor delayline er et array indeholdende 2 pladser som begge er initialiseret til 0.


Plot af henholdsvis: proportional, integration og differential fejlene for robotten under balance kan ses herunder, sammen med et plot af den samlede PID regulator værdi:

De indivudelle konstanter Kp, Ki og Kd blev justeret, således at proportional og diffential fejlene ville få mest indflydelse på PID regulator værdien.

Til at styre motorens retning blev følgende kode benyttet:
if (PIDValue > 100)
PIDValue = 100;
if (PIDValue < -100)
PIDValue = -100;

int normPower = (int)(45 + (Math.abs(PIDValue) * 55) / 100);
Motor.B.setPower(normPower);
Motor.C.setPower(normPower);

if (PIDValue > 0) {
Motor.B.forward();
Motor.C.forward();
} else {
Motor.B.backward();
Motor.C.backward();
}

Variablen normPower styrer kraften til de to motorer B og C (disse blev valgt da de sad på samme H bro hvorved de udsættes for de samme delays og derfor skulle reagerer på samme tid). normPower er normalizeret således at PID regulator værdien (som er begrænset til værdier mellem +/- 100) højst vil bedrage med 55% af motor kraften, mens de resterende 45% svarer til den kraft som motorerne konstant udsættes for selvom robotten er i perfekt balance. Det var nødvendigt at sætte den minimale motor kraft så højt for at få motorerne til at reagerer kraftigt nok til at kunne opretholde sit balance punkt.


Herunder kan en video af hvor effektivt den nye SegWay Rider kørte:


Det var muligt at få robotten til at balancerer i 2 minutter inden videoen blev startet, samt 3 minutter efter. Den væltede først da Martin gik ind foran lampen som oplyste gulvet hvorved lysniveauet ændrer sig så meget at det fastsatte offset ikke længere var aktuelt.

3. Konklusion
Det lykkedes at få lavet en LEGO model (SegWay Rider) med et stabilt respons ved at benytte en lyssensor.


Konklusionen på effektiviteten af denne type balancerende robot er at det er rigtig svært at få indstillet balance punktet korrekt, hvis det blot er en smule skævt eller hvis sampleværdien springer i det øjeblik offsettet udmåles vil robotten bevæge sig ud af balance.
Det er ligeledes meget svært at lave en robot som vil virke på alle typer overflader, samt er det ikke muligt (som beskrevet ovenfor) at ændrer alt for meget på miljøet omkring robotten mens den balancerer (dvs. lys, underlag etc.) uden at den ikke vælter.


Såfremt man ønskede at lave en mere effektiv balancerende robot, vil det være mere praktisk at anvende et gyro meter, og så evt. en lyssensor hvis man ønsker at få robotten til at følge en linie.


4. Kildekode
Link til kildekoden for SegWay Rider kan hentes her!