torsdag den 8. november 2007

NXT Programming, Lesson 8

Varighed: 8½ timer, Deltagere: Mads og Janus

Målet med dagens øvelser er at undersøge hvordan multiple opførsler (jvf. Brooks kan implementeres på NXTen.

Planen er, grundet sygdom, lidt udviddet i forhold til tidligere:

  • Find et NXT byggesæt (pga. manglende nøgle til vores forrige sæt)
  • Installlér lejOS firmwaren i NXTen
  • Opsætning af Bluetooth-kommunikation i sammenhæng med Eclipse (der er ikke flere USB-ledninger, så vi er tvunget til at bruge Bluetooth
  • Afprøvning af programmet fra opgaven
  • Undersøge hvordan opførsel er implementeret og hvilke klasser der benyttes, se på hvordan dette evt. kunne implementeres anderledes.

Forarbejdet

Efter lidt fodarbejde lykkedes det at fremskaffe et ekstra NXT-sæt, dog uden USB-ledning og uden en ekstra kasse med legoklodser. Efter at have installeret lejOS firmwaren på enheden måtte vi af denne grund sætte os ind i hvordan Bluetooth kunne bruges til at uploade programmer til NXT-enheden.
Det største problem omkring Bluetooth var at få computeren og enheden til at forbinde til hinanden. Den Windows-maskine vi benyttede kom ikke frem på NXTen når vi lavede en søgning og når vi lavede en søgning fra Windows-maskinen fandt vi samtlige NXTer i rummet (flere andre grupper havde deres Bluetooth slået til selv om de ikke brugte det). Det viste sig at Windows kan vise Bluetoothenheders MAC-adresse (en kort tekststreng som f.eks. "00:16:53:02:da:de") og ved at skrive samtlige adresser ned for en søgning og efterfølgende lave en ny søgning med vores NXT slukket fandt vi MAC-adressen for vores enhed og kunne starte en opkobling.
Næste problem var at lejOS ikke understøtter passphrase challenge-response når det ikke er den selv der starter opkoblingen. På magisk vis lykkedes det dog at gætte passphrasen i enheden ("1234"), og vi kunne derfor fuldende opkoblingen.
Nu kunne det lade sig gøre at compile og uploade programmer til enheden. Det eneste problem var at BlueCove (softwaren som uploader data til enheden) automatisk forsøger at uploade til alle Bluetooth-enheder i området. Dette er dårligt på flere måder:

  1. De andre grupper er ikke nødvendigvis interesserede i at modtage vores data - godt nok kunne de blot slukke for deres Bluetooth forbindelse, men dette ville være en nødløsning.
  2. Det tager meget lang tid for BlueCove at scanne hele Bluetooth-netværket for enheder og uploade til hver enkelt enhed. En typisk upload tager omkring 2-5 minutter på denne måde.
Efter en større undersøgelse af lejOS kildekoden og diverse .bat-scripts lykkedes det at finde en løsning, hvor vi kunne styre BlueCove således at når vi fra Eclipse valgte "Link and Download" kalder den en modificeret udgave af nxj.bat (i lejOS bibltioteket under /bin), som sender MAC-adressen på lige præcis vores enhed videre til BlueCove.
Den modificerede kode i "/lejos_nxj/bin/nxj.bat" er her:
@echo off
if "%NXJ_HOME%" == ""  goto homeless

set THIRDPARTY_LIBS=%NXJ_HOME%\3rdparty\lib
set LINK_CLASSPATH=.;%THIRDPARTY_LIBS%\bcel-5.1.jar;%THIRDPARTY_LIBS%\commons-cli-1.0.jar;%NXJ_HOME%\lib\pctools.jar;%NXJ_HOME%\lib\pccomm.jar;%NXJ_HOME%\lib\jtools.jar;%THIRDPARTY_LIBS%\bluecove.jar;%NXJ_HOME%\lib\classes.jar

REM java -Djava.library.path=%NXJ_HOME%\bin -Dnxj.home=%NXJ_HOME% -classpath %LINK_CLASSPATH% lejos.pc.tools.NXJLinkAndUpload --writeorder LE --classpath %LINK_CLASSPATH% %*
java -Djava.library.path=%NXJ_HOME%\bin -Dnxj.home=%NXJ_HOME% -classpath %LINK_CLASSPATH% lejos.pc.tools.NXJLinkAndUpload --writeorder LE --classpath %LINK_CLASSPATH% -d 00:16:53:02:da:de %*

goto end

:homeless
echo NXJ_HOME not defined

:end
Bemærk teksten "-d 00:16:53:02:da:de". Det er her vi vælger hvilken NXT MAC-adresse der skal sendes til.

Behaviour test

Eftersom sidste uges robot er låst inde i et skab har vi valgt at bygge en ny bil baseret på standard-bilen 9797 i byggevejledningshæftet. Vi har udviddet bilen ved at tilføje en ultralydssensor på toppen (som angivet på s. 28-30 i vejledningen).

Efter at have downloadet BehaviourTest1.java til NXT-enheden kunne vi se at robotten skiftede imellem 3 forskellige måder at opføre sig på. Den spillede et lille stykke musik ca. hver 10 sek og kørte ellers tilfældigt rundt. Hvis ultralydssensoren målte at der var et objekt tæt på robotten skiftede robotten opførsel ved at bakke hurtigt væk fra det og dreje.

På LCD displayet viser programmet

  1. Programmets navn
  2. Suppress-værdier for randomdrive, avoid og play
  3. Afstanden målt af ultralydssensoren
  4. En indikator for hvad den enkelte opførselsklasse er igang med lige nu

For bedre at kunne se hver enkelt opførsel testede vi programmet med RandomDrive som den eneste aktiverede opførselsklasse. Resultatet var at robotten kørte lidt tilfældigt rundt og ikke reagerede på at ting kom tæt på sensoren. Robotten kører i denne tilstand kun fremad. I den næste test vi udførte havde vi tilføjet AvoidFront. Robotten startede med at køre tilfældigt rundt, men nu reagerede den på objekter foran og bakkede væk og drejede. Den sidste test havde alle opførselsklasserne aktiveret og opførslen var som beskrevet ovenfor.

Implementationsdetaljer

Hver opførselsklasse er nedarvet fra Behaviour.java, som igen nedarver fra Thread. Formålet med dette er at threads kan betragtes som kode, der afvikles parallelt - hvorved vi opnår den ønskede effekt fra Brooks et al. (dvs. at de forskellige opførsler i bund og grund kører sideløbende, men påvirker hinanden ved at undertrykke andre opførsler når det er nødvendigt). Når threads markeres som daemons betragter Java dem som sekundære "hjælper"-threads. Hvis der kun er daemon-threads tilbage stopper afviklingen af programmet. På denne måde kan programmet stopped blot ved at lade main-threaden returnere, så der kun er daemon-threads tilbage.

I Locomotion.java benyttes Behaviour's private variable suppressed via accessor-metoden isSuppressed() til at ignorere kald fra den kaldende Behaviour-implementation baseret på om denne er suppressed eller ej. Delay-metoden i Behaviour.java benytter busy-waiting i Behaviour.java til at vente så længe det kaldende Behaviour ikke er suppressed. Så snart opførslen er suppressed returnerer delay-kaldet med det samme, således at den er klar til at genoptage arbejdet hurtigst muligt når den bliver unsuppressed.

Som beskrevet tidligere bliver den private boolean suppressed benyttet af Locomotion til at undersøge om det er relevant at ignorere kald fra et givent thread. Dette kan dog fejle i situationer hvor AvoidFront har suppressed RandomDrive, og før den er klar til at unsuppresse, tager PlaySounds over og suppresser både RandomDrive og AvoidFront. Når musikken er færdig unsuppresser den AvoidFront og RandomDrive, hvor den kun burde unsuppresse AvoidFront, da den stadig havde suppressed RandomDrive. Dette problem kunne løses ved at benytte en stak, hvor den sidste suppressor tilføjes til stakkens top, og unsuppress sker for alle i det øverste stak-element.

Tilføjelse af kør-imod-lys opførsel

Ved delvist at genbruge klasserne BraitenbergLight og BraitenbergMotorThread fra sidste uge re-implementerede vi kør-imod-lys-algoritmen som et opførselsthread. Umiddelbart blev problemerne som var nævnt i forrige afsnit yderligere forstærket på følgende områder:

  • Vores kør-imod-lys-algoritme havde et thread for hver motor - og vi oversatte dette til et opførselsthread for hver motor. Dette skabte et problem omkring supression af RandomDrive, da den ene sensor godt kunne se lys (og dermed suppresse RandomDrive) mens den anden sensor ikke ser noget lys (og dermed unsuppresser RandomDrive). Den umiddelbare effekt af dette er dog mere interessant end problematisk, idet det hjul, hvor sensoren ikke ser lys vil køre ifølge RandomDrive-opførslen mens det andet hjul (hvis sensor ser lys) vil køre efter kør-efter-lyset-algoritmen. Dette kunne undgåes hvis de to BraitenbergMotorThreads snakkede sammen om kun at suppresse RandomDrive i tilfældet, hvor begge sensorer ikke ser lys. Dette er dog lidt imod Braitenberg's idé om at motorer og sensorer skal være mere eller mindre direkte forbundne.
  • Et andet, mere alvorligt, problem er at hver enkelt opførselsthread ikke checker om det selv er suppressed før det unsuppresser andre threads. Dette betyder at AvoidFront f.eks. kan suppresse RandomDrive og BraitenbergMotorThread, men at BraitenbergMotorThread kort tid derefter unsuppresser RandomDrive - vel at mærke før AvoidFront er færdig. Løsningen var at checke for suppression.
  • Desuden havde Locomotion-klassen ikke mulighed for specifikt kun at ændre på farten på en enkelt motor. Vi tilføjede derfor 2 nye set-metoder til dette således at BraitenbergMotorThreadsne kunne køre uden at påvirke hinandens beslutninger.
  • Naturligvis skal kør-efter-lys-opførslen kun suppresse RandomDrive når der faktisk er noget lys at køre efter.
  • Vores største problem var at de gamle RCX-lyssensorer har det designproblem at de ikke opfatter lys med mindre det meget stærkt (eller rødt). Vi ville gerne have brugt de nye NXT-lyssensorer, men vi havde desværre ikke adgang til 2 af disse.

Robotten i aktion

Her er et par film og billeder, der illustrerer hvordan robotten opfører sig og hvordan de forskellige "lag" af opførsel tager over alt efter hvordan verdenen omkring robotten ændrer sig.

(Til os uden lyd: Der hvor robotten står stille, har PlaySounds opførsel overtaget styringen)

Konklusion

At betragte den samlede opførsel af en robot som en lagkage af forskellige opførsler, der hver især foretager uafhængige valg og beslutninger er en interessant måde at anskue modellering på. Denne metode åbner dog også op for nye problemstillinger, som kræver at man tager hånd om f.eks. hvordan man opnår kontrollen over systemresourcer som motorer og andre aktuatorer.
Produktet af dagens arbejde var en robot med et sofistikeret opførselsmønster, der ville have været svært at lave i en observér-planlæg-udfør-model.

Dagens arbejde involverede også opsætning af Bluetooth. Vi er blevet positivt overraskede over hvor godt Bluetooth fungerer i forhold til USB når først det er sat rigtigt op. Desværre er Bluetooth som standard sat op til at være næsten ubrugeligt i en situation hvor der er mange NXT-enheder i nærheden. Dette fandt vi dog en løsning på (som er inkluderet i starten af denne uges blogpost).

Kildekode

Kildekoden til denne uges blogpost kan findes her. Blandt disse filer er også inkluderet den modificerede nxt.bat-fil, vi har brugt til at køre Bluetooth igennem.

Ingen kommentarer: