torsdag den 29. oktober 2009

Braitenberg's Vehicles (TØ 8)

Plan for idag
  1. Først vil vi implementere Vehicle 1 [2] med en light sensor. Robotten skal kunne følge en lyskilde, dvs. holde stille ved normale lysforhold og køre ved øgede lysforhold.
  2. Herefter vil vi bygger Vehicle 2b [2] med 2 lyssensorer for at se om vi kan få robotten til at følge en lyskilde.
  3. ...


Vehicle 1
Vi konstruere en robot med 1 lys sensor der reagere på øget lysforhold. Reaktionen skal være at sætte motorne igang. Under normale lysforhold skal robotten holde stille.

Vi vil bruge en initiel kalibering af lyssensoren til at bestemme hvad det normale lysforhold er og vi vil bruge en lygte til at simulere øget lysforhold. Efter som robotten kun har en lyssensor vil vi bruge en single threaded control loop. I og med vi kun har 1 lyssensor og 2 mortorer, giver vi begge mortorer samme effekt, på baggrund af input fra lyssensoren.

Effekten til mortoren er konstant 100 i dette forsøg. Control loopet ser sådan ud (se neden stående). Først læses den nuværrende lysværdi fra lyssensoren. Ud fra denne værdi bestemmer
robotten om lysforholdene er øget vha. den kaliberet lysværdi plus en margin på 5* (NORMAL_VALUE_BUFFER). Hvis den nuværrende lysværdi er højrer en den kaliberet værdi plus marginen, sætter vi begge motorer til fuld power.

*Vi fandt marginen på 5 ud fra et forsøg hvor vi obseverede hvad lyssensoren målte uden vi lyste på den (55-57) og hvad den målte når vi lyste på den (60-90).

//Start main loop
LCD.clear();
while (! Button.ESCAPE.isPressed()) {
int currentLigthValue = lightSensor.readValue();
LCD.drawInt(currentLigthValue, 4, 10, 2);
LCD.refresh();

if (currentLigthValue > NORMAL_VALUE_BUFFER) {
leftMotor.controlMotor(100, forward);
rightMotor.controlMotor(100, forward);
} else {
leftMotor.controlMotor(0, stop);
rightMotor.controlMotor(0, stop);
}

Thread.sleep(10);
}

Forsøget gik some forventet - robotten holder stille under normale lysforhold (kaliberet) og kører når vi lyser med lygten på lys sensoren.



Vehicle 2b
Vi ønsker at få robotten til at følge en lys kilde. Hvis lyskilden er lige frem foran robotten skal robotten kører lige ud. Hvis lyskilden bliver holdt til højre for robotten skal den dreje til højre.

Vi vil benytte os af en reaktionel strategi, dvs. vi sætter effekten på motoren til at være proportinel med den målte lysværdi fra lyssensoren. Vi benytter os af 2 lyssensorer til at bestemme hvilken vej der skal drejes. Eftersom vi laver effekten på motoren ligefrem proportionel med den målte lysværdi, skal vi bruge et robot design som Vehicle 2b - se tegning herunder[2].


Vores control loop kan ses herunder. Vi har nu 2 lyssensorer og derfor 2 variable til sensorværdierne (currentLeft og currentRight). I Vehicle 2b "krydses" input og motor siderne, hvilket vi har gjort ved at sensor værdien fra den venstre sensor, bestemmer effekten på højre motor og omvendt for den højre sensor. Igen har vi vores margin for normale lysforhold og en motor vil kun køre hvis sensor værdien er over denne margin. Vi har bibeholdt denne egenskab for at robotten ikke skal blive for følsom overfor environmentet.

while (! Button.ESCAPE.isPressed()) {
int currentLeft = lightSensorLeft.readValue();
int currentRight = lightSensorRight.readValue();

if (currentLeft > NORMAL_VALUE_BUFFER) {
rightMotor.controlMotor(scaleLightInput(currentLeft), forward);
} else {
rightMotor.controlMotor(0, stop);
}

if (currentRight > NORMAL_VALUE_BUFFER) {
leftMotor.controlMotor(scaleLightInput(currentRight), forward);
} else {
leftMotor.controlMotor(0, stop);
}

Thread.sleep(10);
}

Resultatet af dette forsøg kan ses her:



Som det fremgår af videoen virker denne løsning ikke optimalt. Vi kunne se på robottens opførsel at reaktionen (kør eller stop) var for hård hvilket resulterede i den "vrikkende" kørsel.

Vi ændrede lidt på vores control loop:
while (! Button.ESCAPE.isPressed()) {
int currentLeft = lightSensorLeft.readValue();
int currentRight = lightSensorRight.readValue();
if (currentLeft > NORMAL_VALUE_BUFFER || currentRight > NORMAL_VALUE_BUFFER) {
rightMotor.controlMotor(scaleLightInput(currentLeft), forward);
leftMotor.controlMotor(scaleLightInput(currentRight), forward);
} else {
leftMotor.controlMotor(0, stop);
rightMotor.controlMotor(0, stop);
}

Thread.sleep(10);
}

Vi har ændret control loopet til først at tjekke om en af lyssensorne registere en lysværdi større end vores margin. Hvis det er tilfældet giver vi motorne den målte lysværdi skaleret*. Det resulterer i at hvis vi lyser lige på begge sensorer måler begge sensorer ca. samme værdi og robotten vil kører lige ud. Lyser vi til en af siderne vi den ene sensor måle en højere værdi end den anden. Det kombineret med det "krydsede" design i Vehicle 2b resultrere i at den ene motor vil kører hurtigere end den anden, pga. forskellen i målt lysstyrke af sensorne, og robotten vil dreje til den rigtige side. Det er altså udelukkende de målte lysværdier der kontrollere robotten, hvilket passer med en Braitenbreg model.

*skalerings metoden er ganske simpel. Den addere 30 til den målte lys værdi. Vi kom frem til 30 ud fra obsevationen at de målte lysværdier ligger imellem 60 og 90 (90 er kun hvis man lyser direkte ind i en sensor fra ca. 1 cm afstand) og faktum at værdierne motorne skal have for at kører ligger imellem 60 og 100. Vores typiske målte lys værdi ligger imellem 60-70, derfor hvis vi lægger 30 til det får vi en motor effekt på 90-100, hvilket passer perfekt på den ønskede motor effekt.

Resultatet af modifikationen af control loopet ses her:



Tidsforbrug:

3 timer


Referencer
  1. P. Maes, Modeling Adaptive Autonomous Agents,
    Artificial Life Journal, C. Langton, ed., Vol. 1, No. 1 & 2, MIT Press, 1994.
  2. Tom Dean, Introduction to Machina Speculatrix and Braitenberg Vehicles
  3. Tom Dean, Notes on construction of Braitenberg's Vehicles, Chapter 1-5 of Braitenbergs book

torsdag den 1. oktober 2009

Robot Race (TØ 6 + TØ 7)

Program for torsdag 08.10:
I dag fortsætter vi med sidste uges test af ColorSensoren og vil se på andre mulige løsninger, så Alishan banen kan gennemføres hurtigst muligt. Vi har overvejet følgende løsningsmodeller:

Robot med 4 ColorSensors på række:

Idéen bag denne robot var, at de to inderste sensorer skulle kunne følge en sort linie ved at korrigere robotten svagt i den rigtige retning hvis en sort farve registreres af én af sensorerne (dvs føre sensoren væk fra det sorte). I de tilfælde hvor robottens to yderste sensorer registrerer sort (sker i forbindelse med de forskellige plateauer) skal robotten korrigere ved at stoppe den ene motor og give fuld speed på den anden for at dreje kraftigt. Når robotten registrerer en målstreg (alle 4 sensorer måler sort) skal robotten vende 180 grader og så fortsætte ned af bakken.
Vi havde svært ved at løse problemet, når robotten skulle forcere det sidste stykke af en bakke. Sensorerne kom simpelthen for højt op og registrerede derfor alle farven sort eller grøn. Det problem løste vi med en løftearm, så alle sensorer konstant kørte lige over jorden. Vi måtte til sidst sande, at denne løsning ikke var optimal til den givne opgave, da hastigheden hvormed robotten kunne navigere korrekt var for lav. Vi måtte komme frem med en specialiseret robot, som var hurtig på den givne bane og ingen andre steder. Derfor overvejede vi nedenstående robotter.
Update: ved slutningen af denne TØ fandt vi ud af sensorport 4 på vores NXT Brick ikke virkede. Dette kan være skyld i, at robotten ikke reagerede som forventet.


Robot med 1 TrykSensor:
Idé: robotten skal når den kører op af en bakke være trykket i bund. Når den når et plateau og tryksensoren frigives, skal robotten blot følge en forudbestemt kurve indtil sensoren atter er i bund. Vi kom frem til, at denne løsningsmodel ikke var holdbar da sensoren ikke ville kunne fungere på vejen ned af bakken (vil altid være trykket i bund).



Robot med 2 ColorSensors og 1 afstandsmåler som hænger langt fremme på robotten:

Idé: Skab en robot som ved hjælp af ColorSensorer følger den sorte streg på vej op og ned af bakken. Når afstandsmåleren så registrerer et fald eller en stigning, skal robotten dreje i en forudbestemt kurve. Robotten skal programmeres efter de givne omgivelser, så den drejer korrekt, dvs. højre første gang og så venstre etc.
Problem: afstandsmåleren er for følsom overfor bilen bevægelser, hvilket resulterer i svingende værider som er for svære at arbejde med. Efter diverse tests kasserede vi denne model.





Hardcoding/state:

Realiteten er, at denne konkurrence ikke kan vindes med en "tænkende robot" som udelukkende reagerer på det data den samler ind. Man kan hardcode dele af banen ind i robotten, så den følger den sorte linie v.h.a. 2 ColorSensors indtil et plateau nåes og derefter skal robotten dreje efter vores hardcodede anvisninger (dvs. uden at tage højde for sorte streger og andre omgivelser). Denne metode nåede vi ikke at afprøve.

Kode - Control loop for 4 lightsensors:

if (sensorOne.white() && sensorFour.white()) {

if (sensorTwo.white() && sensorThree.white()) {
//Continue ahead
Car.forward(power, power);
} else if (sensorTwo.black() || sensorTwo.color()) {
Car.forward(power, turnPowerMiddle);
//Turn slight right
} else if (sensorThree.black() || sensorThree.color()) {
//Turn slight left
Car.forward(turnPowerMiddle, power);
} else {
Car.stop();
}

//Edge turns
} else if (!sensorOne.white() && sensorFour.white()) {
Car.forward(power, 0);
} else if (sensorFour.white() && !sensorOne.white()) {
Car.forward(0, power);
} else if (sensorOne.black() && sensorFour.black()) {
Car.stop();
}


Kode - Test kode for Ultra sonic sensor:

final int upSlopeDistance = 23;
final int downSlopeDistance = 8;
final int upPlatformDistance = 26;
final int downPlatforDistance = 6;
UltrasonicSensor sensorDistance = new UltrasonicSensor(SensorPort.S3);

if (sensorDistance.getDistance() == upPlatformDistance) {
Car.stop();
}


Tid brugt på TØ 7: 7 timer

Program for torsdag 01.10:

I dag skal vi eksperimentere med Lightsensoren og Linefollower programmet[1] for at blive klar til at bygge vores robot til Robot Racet. Den bane vi skal køre på i racet ser sådan ud:
Målfeltet (ved gulvet) er markeret med grøn tape. Derfor får vi brug for at kunne skelne imellem 3 farver; hvid, sort og grøn. Dagens program ser derfor sådan ud:
  1. Test den udleverede BlackWhiteSensor.java[2] og LineFollowerCal.java[1]
  2. Udvid BlackWhiteSensor.java til også at kunne læse grøn
  3. Udvid koden så robotten stopper når den kommer ind i det grønne målfelt
  4. Forbedre vores kode så vi får en hurtigste tid

1 - Test af udleveret kode
Vi testede BlackWhiteSensor.java og LineFollowerCal.java (uden modifikationer)Som forventet virkede denne linefollower som den skulle. Vi bemærkede dog at denne version ocillerede mindre end den første linefollower vi testede, for et par uger siden. Vi valgte ikke at gøre mere ud af dette, så vi kunne komme igang med det interessante.

2 - Fra BlackWhiteSensor til ColorSensor
Vi forsøgte først men en hurtig implementation der samplede den grønne farve og ud fra en konstant margin, kunne udregne hvid og sort. Dette virkede dog ikke. Vi besluttede os for at ved starten af programmet skal alle 3 farver samples. [beskrivelse af upper og lower bounds]. Derudover er sersor klassen lavet så den virker med alle farver (i teorien) og ikke kun hvid, sort og grøn.


import lejos.nxt.*;

public class ColorSensor {

private LightSensor ls;
private int colorLightValue;
private int blackLightValue;
private int whiteLightValue;
private int colorLowerBound;
private int colorUpperBound;
private final int MARGIN = 5;

public ColorSensor(SensorPort p)
{
ls = new LightSensor(p, true);
// Use the light sensor as a reflection sensor
// ls.setFloodlight(true);
}

private int read(String color){

int lightValue=0;

while (Button.ENTER.isPressed());

LCD.clear();
LCD.drawString("Press ENTER", 0, 0);
LCD.drawString("to callibrate", 0, 1);
LCD.drawString(color, 0, 2);
while( !Button.ENTER.isPressed() ){
lightValue = ls.readNormalizedValue();
LCD.drawInt(lightValue, 4, 10, 2);
LCD.refresh();
}
return lightValue;
}

public void calibrate(){
blackLightValue = read("Black");
whiteLightValue = read("White");
colorLightValue = read("Color");
colorLowerBound = (blackLightValue + colorLightValue)/2;
colorUpperBound = (whiteLightValue + colorLightValue)/2;
}

public int[] getCalibration() {
int[] tmp = new int[3];
tmp[0] = blackLightValue;
tmp[1] = whiteLightValue;
tmp[2] = colorLightValue;
return tmp;
}

public void setCalibration(int[] cal) {
blackLightValue = cal[0];
whiteLightValue = cal[1];
colorLightValue = cal[2];
colorLowerBound = (blackLightValue + colorLightValue)/2;
colorUpperBound = (whiteLightValue + colorLightValue)/2;
}

public boolean black(){
return (ls.readNormalizedValue() <> colorUpperBound);
}

public boolean color(){
return ((colorLowerBound <>

3 - Make it stop please
Vi har udviddet LineFollowerCal.java så robotten vil stoppe når den ser den samplede målfelts farve.

Ved første forsøg virkede dette efter hensigten, bortset fra at robotten ikke kører helt ind i feltet.

Herfra nåede vi ikke mere i denne TØ.

4 - Optimering af kode
(Nåede vi ikke)

Tid brugt på TØ 6: 3 timer

Referencer

[1]LineFollowerCal.java
[2]BlackWhiteSensor.java