torsdag den 19. november 2009

Lesson 11: Opførsel

I denne uge vil vi benytte LeJOS subsumption API´en og specifikt Bahaviour interfacet og klassen Arbitrator som bestemmer udførselsrækkefølgen.

I første omgang lagde vi BumperCar.java programmet ind på NXT´en og observerede opførslen. Bilen kører autonomt fremad så længe ingen forhindringer ses eller mærkes. Hvis takeControl() metoden i DetectWall returnerer true kaldes suppress() metoden i DriveForward og _suppressed bliver sat til true hvilket medfører at begge motorer stopper. DriveForward overtager kontrollen igen så snart DetectWall's takeControl() metode igen returnerer false. Følgende videoer viser undertrykkelsen i aktion:




Med tryksensoren trykket ind konstant vil bilen udføre sin rotate manøvre igen og igen indtil modstanden fjernes. DriveForward er altså konstant undertrykt.

Vi implementerede nu klassen Exit som skal undertrykke de andre behaviours hvis ESCAPE knappen trykkes ned. Koden ser således ud:

import lejos.nxt.Button;
import lejos.robotics.subsumption.Behavior;

class Exit implements Behavior {

public Exit() {}

public boolean takeControl() {
return Button.ESCAPE.isPressed();
}

public void suppress() {
//Since this is highest priority behavior, suppress will never be called.
}

public void action() {
System.exit(0);
}
}


Det lader til at bilen stopper med det samme uanset om bilen er i DriveForward eller DetectWall opførsel. Men hvis vi ændrer Sound.pause(20) til Sound.pause(2000) får vi følgende opførsel når ESCAPE knappen trykkes ned:

Det vi ser er, at Sound.pause(2000) blokerer for sensormålingerne inde i takeControl() metoden i 2ooo ms. så der ikke omgående bliver registeret tryk på ESCAPE eller at robotten er ved at køre ind i noget.

Vi arbejdede med implementering af tråde inde i DetectWall klassen for at forhindre forsinkelse/blokering. Med følgende løsning er takeControl() metoden ikke længere langsom og robotten reagerer igen korrekt. Koden ses her:

import lejos.nxt.SensorPort;
import lejos.nxt.Sound;
import lejos.nxt.UltrasonicSensor;

public class SonarThread extends Thread {
private UltrasonicSensor sonar;
public int distance = 0;

public SonarThread() {
sonar = new UltrasonicSensor(SensorPort.S3);
}

public void run() {
while(true) {
sonar.ping();
Sound.pause(2000);
distance = sonar.getDistance();
}
}
}

----------------------------------------------------------------------------------

import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.nxt.Sound;
import lejos.nxt.TouchSensor;
import lejos.nxt.UltrasonicSensor;
import lejos.robotics.subsumption.Behavior;

public class DetectWall implements Behavior{

public DetectWall() throws InterruptedException
{
touch = new TouchSensor(SensorPort.S1);

t = new SonarThread();
t.start();
}

public boolean takeControl()
{
return touch.isPressed() || t.distance < 25;
}

public void suppress()
{
Motor.A.stop();
Motor.C.stop();
}

public void action()
{
Motor.A.rotate(-90, true);// start Motor.A rotating backward
Motor.C.rotate(-360);// rotate C farther to make the turn
}
private TouchSensor touch;

private SonarThread t;
}


Tidsforbrug: 3 timer

torsdag den 12. november 2009

Lego lab lesson 10: Navigation

Plan for idag:
  1. Test af Blightbot (benytter TachoNavigator). Findes i noterne [1]
  2. Diskussion af, hvorledes man evt. kan navigere samtidig med at undgå objekter
  3. Navigation ved hjælp af kompas sensor.
Test af Blightbot:
Vi fandt ud af, at denne version af lejos ikke understøttede TachoNavigator og dermed goTo() metoden. Vi benytte derfor et TachoPilot(...) objekt og metoden travel(int distance). Koden ses herunder:

import lejos.nxt.*;
import lejos.robotics.navigation.*;

public class Blighbot {

public static void main(String[] args) {

TachoPilot robot = new TachoPilot(5.8f, 11.5f, Motor.C, Motor.B, false);

robot.travel(200);
robot.rotate(180);
robot.travel(100);
robot.rotate(-90);
robot.travel(100);
robot.rotate(90);
robot.travel(150);
robot.rotate(180);
robot.travel(50);
robot.rotate(90);
robot.travel(100);
}
}


Planen var, at robotten skulle vandre rundt i rummet (på Zuse's blå gulv) for til sidst at ende ved udgangspunktet. I koden ses det, at robotten efter første vandring skal rotere 180 grader, men som nedenstående video viser er dette ikke tilfældet (10 sekunder inde i filmen):



Denne række af små fejl leder til drift (større fejl) og robotten vender derfor ikke tilbage til udgangspunktet. Disse fejl kan skyldes underlaget (dårligt fæste) eller unøjagtige målinger af hjulstørrelse eller afstand mellem disse. Vi testede også robotten på et gulvtæppe, men her var drift endnu mere tydeligt.

Diskussion af, hvorledes man evt. kan navigere samtidig med at undgå objekter
En overordnet strategi for hvordan dette kan opnås; Vi kunne benytte CompassBlighBot[1] og lave compass navigationen om til et behaviour. Derudover skal der være et "obstacle avoidance" behaviour, som vha. subsumption kan undertrykke navigations behaviouret. Ideen er at robotten vil kører den rute der er defineret i navigationen samtidig med at "obstacle avoidance" parallelt med tjekker for om robotten er ved at køre ind i noget (vha. en ultrasonic sensor). Hvis robotten er på kollisionskurs skal "obstacle avoidance" behaviouret overtage styringen og guide robotten uden om forhindingen. Herefter skal navigations niveauet tage over igen. Som sagt er dette en overordnet strategi og der vil være problemer, som skal håndteres. F.eks. vi "obstacle avoidance" opførselen også flytte robotten, så robotten vil være et andet sted end hvor navigationen forventer.

Navigation ved hjælp af kompas sensor
Under denne test, havde vi en forventning om at robotten ikke ville lade sig påvirke af underlaget. Denne video viser, at robotten roterer (eller korrigerer i små hak) som ønsket og at denne løsning er mere præcis til navigation uden forhindringer:



Det ses dog i nedenstående video, at robotten påvirkes af visse objekter som kommer for tæt på. I videoen er dette illustreret ved, at robotten vandrer mod en computer:



For at give et lidt bedre billede af hvordan robotten kørte, monterede vi en tusch og kørte oven på et whiteboard. Som vi kan se fra videon påvirkes kompassen meget af omgivelserne. Den kan ikke engang køre lige ud, men trækker enten den ene eller den anden vej. Vi satte afstandene ned så robottens bane kunne være indenfor whiteboardet.



[1], Brian Bagnall, Maximum Lego NXTBuilding Robots with Java Brains, Chapter 12, Localization, p.285 - p.302.

Brugt tid: ca 3 timer.

torsdag den 5. november 2009

Lego lab lesson 9: Car that exhibits several behaviors.

Plan for idag:
  1. Afprøv programmet SoundCar.java og besvarelse af de stillede spørgsmål i ugens opgave
    beskrivelse.
  2. Tilføj ny opførsel som følger en lyskilde. Vi vil eksperimentere med denne opførsel ved at lade denne opførsel undertrykke alle andre opførsler og derefter placere den forskellige steder i hierakiet.
  3. Ombytning af opførsels niveauer: AvoidFront > FollowLight.
Afprøvning af SoundCar.java:
Vi kan aflæse på displayet, at bilen i åbne omgivelser kører ved hjælp af RandomDrive.java. Hvert 10. sekund bliver denne tråd blokeret af PlaySounds tråden som stopper bilen og afspiler en lyd. Samtidig ser vi, at RandomDrive tråden kan undertrykkes af AvoidFront tråden, hvis bilen kommer for tæt på en genstand.

Vurdering af suppress:
Vi ser i koden, at suppress() undertrykker tråde som befinder sig lavere i hierakiet. Hierakiet defineres inde i SoundCar.java, hvor vi ser at RandomDrive tråden befinder sig nederst i hierakiet (oprettet med under-niveau værdien null). RandomDrive tråden kan altså ikke undertrykke andre tråde, da den er nederst i hierarkiet. Over RandomDrive findes AvoidFront tråden som kan undertrykke RandomDrive hvis en genstand kommer for tæt på (denne afstand måles med en UltraSonicSensor). Øverst i hierakiet finder vi PlaySound tråden, som kan undertrykke de to andre tråde og gør dette hvert 10. sekund.

Suppress funktionaliteten er implementeret vha. variablen suppressCount. Denne variable holder styr på hvor mange gange et givet niveau er undertrygt af højere niveauer. En tråd får kun adgang til actuators (motorne) hvis dens suppressCount variablen er 0. F.eks. hvis robotten kører rundt grundet opførselen i RandomDrive niveauet, men kommer for tæt på en stol. AvoidFront niveauet vil opdage dette og undertrykke RandomDrive niveauet, for at fortage en undvige manøvre. RandomDrive har nu en suppressCount på 1 - AvoidFront niveauet undertrykker RandomDrive. Imens undvige manøveren bliver fortaget af AvoidFront, beslutter PlaySound niveauet sig for at spille en yndig mellodi. PlaySound niveauet undertrykker nu AvoidFront og RandomDrive niveauerne. AvoidFronts suppressCount er nu på 1 og RandomDrive's suppressCount er på 2. Når PlaySound er færdig med at spille musik, løfter den sin undertrykkelse af de nedre niveauer. AvoidFronts suppressCount er nu på 0 og kan dermed få adgang til motorne og fortsætte undvige manøvren. RandomDrive har stadigvæk en suppressCount på 1, da den stadigvæk er undertrykt af AvoidFront. Når AvoidFront løfter sin undertrykkelse vil RandomDrive igen få adgang til motorne (suppressCount på 0) og robotten vil fortsætte med at udvise denne opførsel.

"Kør mod lys" opførsel:
Vi har benyttet resultater og kode fra vores LightFollower fra sidste uge. Denne nye tråd ligger vi øverst i hierakiet og vi vil teste om suppress() virker korrekt ved, at lade bilen følge en lyskilde helt ind i en væg. Hvis dette forsøg virker, antager vi at AvoidFront tråden undertrykkes. Om PlaySound tråden undertrykkes er nemt at aflæse, da lyden ikke afspilles hvert 10. sekund.

LightFollower opførslen er baseret på følgende kode:

import lejos.nxt.LightSensor;
import lejos.nxt.SensorPort;


public class FollowLight extends Behavior {

private LightSensor lsl;
private LightSensor lsr;

private static int NORMAL_VALUE = 55;
private static int NORMAL_VALUE_BUFFER = NORMAL_VALUE+5;

public FollowLight(String name, int LCDrow, Behavior subsumedBehavior) {
super(name, LCDrow, subsumedBehavior);
lsl = new LightSensor(SensorPort.S2, true);
lsr = new LightSensor(SensorPort.S3, true);
}

public void run() {

while (true) {
int currentLeft = lsl.readValue();
int currentRight = lsr.readValue();

if (currentLeft > NORMAL_VALUE_BUFFER || currentRight > NORMAL_VALUE_BUFFER) {
suppress();
drawString("l");
drawInt(currentLeft);

forward(currentRight+30, currentLeft+30);

release();
}}}}


Resultat af "kør mod lys" opførslen placeret øverst i hierakiet:
Bilen reagerer helt som forventet. Vi kan tvinge bilen ind i en væg ved at placere en kraftig lyskilde foran UltraSonicSensoren. Så snart lyskilden fjernes, vil AvoidFront (eller PlaySound) opførslen tage over og navigere bilen væk fra væggen. Herefter tager RamdomDrive tråden så over igen. Se video:



Som det fremgår af videoen kan vi tvinge robotten ind i en væg ved at få den til at følge en lyskilde. FollowLight opførselen undertrykker nemlig AvoidFront opførselen. Bemærk dog at der bliver spillet musik imens robotten følger lyset - 00:35 inde i videoen. Dette er dog ikke pga. PlaySound niveauet ikke er undertrykt når der følges en lyskilde. Musikken starter med at spille fordi lyskilden et kort sekundt ikke bliver rettet imod lyssensorne. Det får FollowLight niveauet til at release og PlaySound niveauet får lov at spille musik. Grunden til der omgående bliver spillet musik er at PlaySound tråden har været aktiv selvom dens adgang til actuators har været blokeret af FollowLight tråden. Det vil sige PlaySound tråden er klar til at spille når den får lov. Musikken fortsætter med at spille selvom FollowLight niveauet igen overtager, da FollowLight niveauet ikke bruger eller blokerer lyd actuatoren.

Efter at have flyttet AvoidFront opførselen til det højeste niveau, optog vi den her video:


Som det kan ses, følger robotten lyset så længe ingen forhindring kan ses. Men når UltraSonicSensorn måler at der er et objekt indenfor 20 cm afstand aktiveres AvoidFront og robotten bakker.

Tidsforbrug: 3 timer