|
|
|
# Group 5
|
|
|
|
|
|
|
|
## Lab Notebook 9 - Robot Race
|
|
|
|
|
|
|
|
**Date:** 7/5 2015, 14/5 2015, 15/5 2015, 17/5 2015, 18/5 2015
|
|
|
|
|
|
|
|
**Group members participating:** Daniel Vestergaard, Christoffer Skovgaard, Mikkel Brun Jakobsen, Josephine Raun Thomsen
|
|
|
|
|
|
|
|
**Activity duration:** 3 hours, 9 hours, 4 hours, 2 hours, ?
|
|
|
|
|
|
|
|
## Goal
|
|
|
|
|
|
|
|
|
|
|
|
Formålet med denne Lab session er at konstruere og programmere en *autonom* sumobryder LEGO robot. Sumobryderrobotten skal være i stand til at kæmpe imod andre sumobryder robotter i en arena, som ses af figur 1. Kampen slutter når en robot skubbes ud over den hvide streg *eller* hvis robotten tiltes. Kampen ender uafgjort hvis ingen vinder er fundet efter 2 minutter.
|
|
|
|
|
|
|
|
*Figur 1: Bane til sumobryder-robotterne.*
|
|
|
|
|
|
|
|
![Skærmbillede 2015-05-18 kl. 14.26.59](http://gitlab.au.dk/uploads/group-5/group-5-lesson-9/d7a9648792/Sk%C3%A6rmbillede_2015-05-18_kl._14.26.59.png)
|
|
|
|
|
|
|
|
## Plan
|
|
|
|
|
|
|
|
Følge instruktionerne for Lab session 9 [1]. Derudover benytter vi instruktionerne til Express Bot [2] til at konstruere basen til vores sumobryder robot. Programmeringsmæssigt ønsker vi at gøre brug af idéerne bag behavior-based control paradigme til at give robotten dens opførsel [3]. Vi undersøger først, hvordan en behavior-based arkitektur [4] kan implementeres, herunder hvordan interfacet lejos.subsumption.Behavior og klassen lejos.subsumption.Arbitrator kan benyttes som et alternativ til den Arbitrator-implementation, som vi undersøgte i Lesson 7 [5].
|
|
|
|
|
|
|
|
## Results
|
|
|
|
|
|
|
|
### BumperCar
|
|
|
|
|
|
|
|
BumperCar programmet er et eksempel på et behavior-based control program fra leJOS NXJ. Programmet består af to slags behaviors: DriveForward og DetectWall, der begge implementere interfacet Behavior samt benytter en instans af klassen Arbitrator til at styre hvornår hvilken behaviors får kontrol.
|
|
|
|
|
|
|
|
*Billede 1: BumperCar - sumobryderbase påmonteret med bumper og ultrasonisk sensor*
|
|
|
|
|
|
|
|
![IMG_0402](http://gitlab.au.dk/uploads/group-5/group-5-lesson-9/f26dc8708d/IMG_0402.JPG)
|
|
|
|
|
|
|
|
Følgende eksperimenter bruger vi til at undersøge Arbitrator-klassens funktionalitet.
|
|
|
|
|
|
|
|
#### Holde tryksensoren inde
|
|
|
|
|
|
|
|
*Video 1: Tryk sensor holdes inde*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/PPa-NxbjCzw /0.jpg)](https://www.youtube.com/watch?v=PPa-NxbjCzw)
|
|
|
|
|
|
|
|
Video 1 [13] ovenfor viser at når tryksensoren holdes inde bakker begge robottens hjul bagud, hvorefter det ene hjul drejer til venstre, altså bakker robotten og drejer - dette fortsætter indtil tryksensoren ikke længere holdes inde. Denne opførsel er et resultat af at DetectWall-behavioren tager kontrol over bilens opførsel når tryksensoren holdes inde. I denne process bliver DriveForward-behavioren suppressed af DetectWall-behavioren fordi DetectWall-behavioren har højere prioritet og kræver kontrol.
|
|
|
|
|
|
|
|
#### Exit behavior
|
|
|
|
|
|
|
|
For at gøre robotten i stand til at stoppe midt i et program, implementerede vi en tredje behavior, Exit, som har den højeste prioritet. Målet hermed er at hvis der trykkes på ESCAPE-knappen mens en anden behavior er aktiv, tager Escape-behavioren kontrol og kalder System.Exit(0) for at stoppe programmet. Video 2 [14] demonstrerer dennem Escape-behavior.
|
|
|
|
|
|
|
|
*Video 2: Demonstration af Escape-behavior*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/lAmipiL2yUU/0.jpg)](https://www.youtube.com/watch?v=lAmipiL2yUU)
|
|
|
|
|
|
|
|
Da Escape-behavioren har højeste prioritet kan DetectWall-behavioren ikke længere have højeste prioritet. Derfor har modificeret koden i DetectWall-behavioren, så den kan undertrykkes. Java-kode 1 nedenfor viser hvordan vi har realiseret dette.
|
|
|
|
|
|
|
|
*Java-kode 1: Uddrag fra modificeret DetectWall-behavior klassen*
|
|
|
|
|
|
|
|
``` Java
|
|
|
|
public void suppress() {
|
|
|
|
_suppressed = true;
|
|
|
|
}
|
|
|
|
…
|
|
|
|
public void action() {
|
|
|
|
_suppressed = false;
|
|
|
|
BumperCar.leftMotor.forward();
|
|
|
|
BumperCar.rightMotor.forward();
|
|
|
|
while (!_suppressed) {
|
|
|
|
Thread.yield(); // don't exit till suppressed
|
|
|
|
}
|
|
|
|
BumperCar.rightMotor.stop();
|
|
|
|
BumperCar.leftMotor.stop();
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
####Metoden takeControl()
|
|
|
|
|
|
|
|
Den relevante kode fra Arbitrator klassen ses nedenfor i Java-kode 2.
|
|
|
|
|
|
|
|
*Java-kode 2: Uddrag fra Arbitrator klassen*
|
|
|
|
|
|
|
|
``` Java
|
|
|
|
_highestPriority = NONE;
|
|
|
|
for (int i = maxPriority; i >= 0; i--) {
|
|
|
|
if (_behavior[i].takeControl()) {
|
|
|
|
_highestPriority = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
Metoden takeControl() for DriveForward behavioren bliver ikke kaldt, da behavior arrayet er sorteret i stigende rækkefølge (mht. prioritet) og gennemløbet af arrayet er aftagende. Dette betyder at hvis en behavior placeret sidst i arrayet returnerer true i sit takeControl()-kald, stoppes gennemløbet og denne behavior får kontrol, hvorved behaviors placeret forrest i arrayet ignoreres.
|
|
|
|
|
|
|
|
|
|
|
|
#### Undgå delay i takeControl-metoden
|
|
|
|
|
|
|
|
DetectWall-behaviorens takeControl()-metode indeholder et kald til ultralydssensorens getDistance() metode. GetDistance() metoden er blokerende, hvilket ikke er hensigtsmæssigt i takeControl() metoden, fordi det hæmmer arbitorens evne til at vælge mellem behaviors. Vi kan overkomme dette langsomme kald, ved at implementere en lokal tråd i DetectWall-behavioren. Denne tråd kalder hvert 20. msek getDistance() metoden på ultralydssensoren, og gemmer en sensorværdien (afstanden) i variablen _is_within_wall_range. Denne værdi kan dermed hurtigt tilgås fra takeControl()-metoden. Den implementerede lokale tråd kan ses i Java-kode 3, mens den nye takeControl-metode kan ses i Java-kode 4 nedenfor.
|
|
|
|
|
|
|
|
*Java-kode 3: Lokal metode i DetectWall*
|
|
|
|
|
|
|
|
``` Java
|
|
|
|
Thread thread = new Thread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
while(true) {
|
|
|
|
sonar.ping();
|
|
|
|
_is_within_wall_range = sonar.getDistance() < 25;
|
|
|
|
Delay.msDelay(20);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
thread.setDaemon(true);
|
|
|
|
thread.start();
|
|
|
|
´´´
|
|
|
|
|
|
|
|
*Java-kode 4: takeControl-metode, der returnerer uden delay.*
|
|
|
|
|
|
|
|
``` Java
|
|
|
|
public boolean takeControl() {
|
|
|
|
return touch.isPressed() || _is_within_wall_range;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
Bemærk at der er synkroniseringsproblemer med ovenstående kode (en variabel der bliver læst og skrevet af forskellige tråde), men i praksis har det ikke den store betydning.
|
|
|
|
|
|
|
|
#### DetectWall-implementation med bakning (1 sekund)
|
|
|
|
|
|
|
|
DetectWall roterer for at afværge kollision med omgivelserne. For at sikre yderligere imod kollision implementerer vi DetectWall således at bilen bakker i ét sekund inden den begynder at rotere. Java-kode 5 viser, hvordan bilen tager højde for dette.
|
|
|
|
|
|
|
|
*Java-kode 5: Snippet fra DetectWall’s action()*
|
|
|
|
|
|
|
|
``` Java
|
|
|
|
public void action() {
|
|
|
|
_suppressed = false;
|
|
|
|
|
|
|
|
BumperCar.leftMotor.backward();
|
|
|
|
BumperCar.rightMotor.backward();
|
|
|
|
|
|
|
|
long start = System.currentTimeMillis();
|
|
|
|
while(!_suppressed && System.currentTimeMillis() - start < 1000) {
|
|
|
|
Thread.yield();
|
|
|
|
}
|
|
|
|
|
|
|
|
BumperCar.leftMotor.stop();
|
|
|
|
BumperCar.rightMotor.rotate(-1080, true);
|
|
|
|
|
|
|
|
while(!_suppressed && BumperCar.rightMotor.isMoving()) {
|
|
|
|
Thread.yield();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
#### DetectWall-implementation med afbrydelse
|
|
|
|
|
|
|
|
Hvis tryksensoren registrerer et tryk *imens* bilen er i gang med at dreje (DetectWall-behavioren er allerede aktiv), reagerer bilen ikke, medmindre der er implementeret mulighed for en interruption af DetectWall-behavioren.
|
|
|
|
|
|
|
|
Nedenstående Java-kode 6 viser vores implementation af DetectWall’s action()-metode, således at bilen kan afbrydes mens den er i gang med at dreje.
|
|
|
|
|
|
|
|
*Java-kode 6: *
|
|
|
|
|
|
|
|
``` Java
|
|
|
|
public void action() {
|
|
|
|
_suppressed = false;
|
|
|
|
|
|
|
|
BumperCar.leftMotor.backward();
|
|
|
|
BumperCar.rightMotor.backward();
|
|
|
|
|
|
|
|
long start = System.currentTimeMillis();
|
|
|
|
while(!(_suppressed || takeControl()) && System.currentTimeMillis() - start < 1000) {
|
|
|
|
Thread.yield();
|
|
|
|
}
|
|
|
|
if(System.currentTimeMillis() - start >= 1000) {
|
|
|
|
BumperCar.leftMotor.stop();
|
|
|
|
|
|
|
|
BumperCar.rightMotor.rotate(-1080, true);
|
|
|
|
}
|
|
|
|
while(!(_suppressed || takeControl()) && BumperCar.rightMotor.isMoving()) {
|
|
|
|
Thread.yield();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Den oprindelige Arbitrator klasse vil kun lade en behavior undertrykke en anden behavior, hvis den har en strengt højere prioritet. Det betyder at vores DetectWallBehavior ikke kan undertrykke sig selv. For at simulere at den kan dette alligevel, tjekker vi om der er behov for at tage kontrol, mens vi har kontrollen. Dette gør vi ved udtrykket !(_supressed) || takeControl()). Hvis dette er tilfældet stopper DetectWallBehavior før tid, så Arbitrator-klassen har mulighed for at genstarte den.
|
|
|
|
|
|
|
|
Nedenstående video 3 [15] viser en demonstration af denne opførsel.
|
|
|
|
|
|
|
|
*Video 3: Demonstration af interrupted behavior*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/7Ie5lJAXUIE/0.jpg)](https://www.youtube.com/watch?v=7Ie5lJAXUIE)
|
|
|
|
|
|
|
|
I videoen ser vi, hvordan en testperson rækker hånden ind foran afstandssensoren, imens bilen er i gang med at dreje. Bilen reagerer på dette ved at bakke yderligere 1 sekund, idet DriveForward’s action() bliver undertrykt og DetectWall’s action() bliver kaldt.
|
|
|
|
|
|
|
|
|
|
|
|
#### A Behavior-based Control Program for the Sumo Wrestler Robot
|
|
|
|
|
|
|
|
Reglerne for sumo-brydningen er som følger:
|
|
|
|
|
|
|
|
*Robottens basekonstruktion skal være den samme som Express bot består af. Se “base car with extensions” [2].
|
|
|
|
|
|
|
|
*Der må kun benyttes LEGO elementer til at konstruere robotten.
|
|
|
|
|
|
|
|
*Vægten af robotten må ikke overstige 1 kg. Samtidig må robottens størrelse ikke være mere end 30 cm * 30 cm.
|
|
|
|
|
|
|
|
*Robotten skal være autonom.
|
|
|
|
|
|
|
|
*Efter tryk på start-knappen skal robotten vente 3 sekunder inden den begynder at køre.
|
|
|
|
|
|
|
|
Efter indledende undersøgelser af arkitekturen bag behavior-based control er vores næste mål at konstruere og programmere vores egen LEGO sumobryderrobot. Vi ønsker at programmere vores robot efter behavior-based control paradigmet [3], som det beskrives af Jones et al. Vi benytter Behavior.java [6], Arbitrator.java [7] and BumperCar.java [8] som inspiration til at implementere en behavior-based arkitektur. På trods af at disse klasser understøtter “flydende prioriteter” eller “motivation factors” er alle vores prioriteter enten en høj værdi (hvis den vil have kontrol) eller 0 (hvis den ikke vil have kontrol). Nedenstående figur viser det arkitektur-netværk, som vi har til intention at konstruere og programmere vores sumobryderrobot ud fra.
|
|
|
|
|
|
|
|
|
|
|
|
*Figur 2: Arkitektur-netværk for vores sumobryder robot.*
|
|
|
|
|
|
|
|
![Skærmbillede 2015-05-18 kl. 11.25.55](http://gitlab.au.dk/uploads/group-5/group-5-lesson-9/0c6810522e/Sk%C3%A6rmbillede_2015-05-18_kl._11.25.55.png)
|
|
|
|
|
|
|
|
Som det ses af figur 2 ønsker vi derfor at implementere vores robot med 5 forskellige behaviors: AvoidEdge, AttackEnemy, FindEnemy TurnAround og Roaming (gående fra højest prioriterede mod laveste prioriterede), hvor de internt henholdsvis har værdierne 1000, 800, 600, 400 og 200. Når et Arbitrator objekt bliver instantieret får den et array af Behavior objekter. Arbitratoren skal dernæst bestemme, hvilken behavior der skal gøres aktiv. Dette gør den ved at kalde takeControl() på alle Behavior-objekter, hvorefter den Behavior med største returværdi, og dermed højeste prioritet udvælges. Hvis en anden Behavior ønsker kontrol og dens retur-værdi er større end den aktive Behavior’s returværdi, bliver den aktive Behavior undertrykt.
|
|
|
|
|
|
|
|
For yderligere at formalisere vores tanker omkring sumobryderrobottens opførsel beskrev vi de 5 forskellige behaviors vha. pseudokode. Se figur 3 nedenfor.
|
|
|
|
|
|
|
|
*Figur 3: De 5 Behaviors med tilhørende pseudokode.*
|
|
|
|
|
|
|
|
![IMG_0514](http://gitlab.au.dk/uploads/group-5/group-5-lesson-9/45b2b04487/IMG_0514.JPG)
|
|
|
|
|
|
|
|
I de følgende delmåls-afsnit beskrives den løbende konstruktion og programmering af vores sumobryderrobot beskrevet i nogenlunde samme rækkefølge som figur 3 viser.
|
|
|
|
|
|
|
|
### Delmål: Implementere AvoidEdge & Roaming-behavior
|
|
|
|
|
|
|
|
Den første behavior vi ønskede at implementere var AvoidEdge, hvorfor Roaming-behavioren (køre frem) også var nødvendig at implementere. Hensigten med AvoidEdge er at sørge for at robotten holder sig inden for banen, hvilket i sidste ende er vores højeste prioritet i en sumobryderkamp. Hvis en af robottens lyssensorer derfor registrerer den hvide kant på banen, ønsker vi at robotten bakker og drejer for at undgå at kører ud over kanten (se figur 2).
|
|
|
|
|
|
|
|
Som beskrevet i reglerne består vores sumobryder robot af Express bot basen, som ses i [2]. Derudover påsatte vi to lyssensorer. Vores hidtidige sumobryderrobot kan ses af billede 2 nedenfor.
|
|
|
|
|
|
|
|
*Billede 2: Express-bot base med to lyssensor foran og bagpå.*
|
|
|
|
|
|
|
|
![IMG_0428.JPG ](http://gitlab.au.dk/uploads/group-5/group-5-lesson-9/57614d9d2b/IMG_0428.JPG_.JPG)
|
|
|
|
|
|
|
|
AvoidEdge klassen tager kontrol hvis en af lyssensorene viser “hvid”. Som i Lesson 1 [9] har vi brugt en threshold værdi til at skelne mellem sort og hvid (se Java-kode 7):
|
|
|
|
|
|
|
|
*Java-kode 7: Black-white threshold fra Lesson 1.*
|
|
|
|
|
|
|
|
``` Java
|
|
|
|
public int takeControl() {
|
|
|
|
return (isWhite(frontLightSensor) || isWhite(backLightSensor)) ? 200 : 0;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Java-kode 8 viser, hvordan selve action() metoden i AvoidEdge klassen tjekker, hvilken af de to sensorer der rapporterede hvid og handler derefter efter hensigten (som er beskrevet i figur 3).
|
|
|
|
|
|
|
|
*Java-kode 8: AvoidEdge action()-metode*
|
|
|
|
|
|
|
|
``` Java
|
|
|
|
public void action() {
|
|
|
|
_suppressed = false;
|
|
|
|
|
|
|
|
if(isWhite(frontLightSensor)) {
|
|
|
|
BumperCar.leftMotor.controlMotor(minPower, BumperCar.MOTOR_BACKWARD);
|
|
|
|
BumperCar.rightMotor.controlMotor(minPower, BumperCar.MOTOR_BACKWARD);
|
|
|
|
} else {
|
|
|
|
BumperCar.leftMotor.controlMotor(minPower, BumperCar.MOTOR_FORWARD);
|
|
|
|
BumperCar.rightMotor.controlMotor(minPower, BumperCar.MOTOR_FORWARD);
|
|
|
|
}
|
|
|
|
|
|
|
|
long start = System.currentTimeMillis();
|
|
|
|
while(!_suppressed && System.currentTimeMillis() - start < 1750) {
|
|
|
|
Thread.yield();
|
|
|
|
}
|
|
|
|
if(System.currentTimeMillis() - start >= 1750) {
|
|
|
|
BumperCar.leftMotor.controlMotor(100, BumperCar.MOTOR_FORWARD);
|
|
|
|
BumperCar.rightMotor.controlMotor(100, BumperCar.MOTOR_BACKWARD);
|
|
|
|
}
|
|
|
|
long time = System.currentTimeMillis();
|
|
|
|
while(!_suppressed && System.currentTimeMillis() - time < 1500) {
|
|
|
|
Thread.yield();
|
|
|
|
}
|
|
|
|
BumperCar.leftMotor.controlMotor(0, BumperCar.MOTOR_STOP);
|
|
|
|
BumperCar.rightMotor.controlMotor(0, BumperCar.MOTOR_STOP);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
For at spare plads byttede vi senere i forløbet den bageste NXT lyssensoren ud med en RCX lyssensor. De eneste ændringer i ovenstående kode var at bytte LightSensor ud med RCXLightSensor og ydermere finde en ny threshold værdi.
|
|
|
|
|
|
|
|
Video 4 [16] nedenfor demonstrerer den grundlæggende AvoidEdge behavior.
|
|
|
|
|
|
|
|
*Video 4: Demonstration af Avoid Edge.*
|
|
|
|
[![image alt text](https://img.youtube.com/vi/XCGIMv6Vldc/0.jpg)](https://www.youtube.com/watch?v=XCGIMv6Vldc)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Delmål: Implementere FindEnemy.*
|
|
|
|
|
|
|
|
Den næste Behavior vi implementerede var FindEnemy. Vi ønskede som udgangspunkt at denne Behavior skulle afhænge af input fra en enkelt ultralydssensor. Dette input skulle benyttes til at detektere hvor modstanderrobotten befinder sig og, dernæst nærme sig modstanderen. Da selve bilen godt kunne ende med at bruge meget tid på at køre rundt for at finde modstander-robotten, valgte vi at placere ultralydssensoren på en motor, således at ultralydssensoren kunne drejes imens robotten var i bevægelse. Robotten ville derfor kunne afsøge flere vinkler uden at selve robotten behøvede at dreje. Nedenstående billede 3 viser hvordan vores sumobryderrobot så ud efter vi har påsat en ultralydssensor på en motor, således at ultralydssensoren kan rotere imens robotten er i bevægelse.
|
|
|
|
|
|
|
|
*Billede 3: Sumobryder robot med afstandssensor påsat på motor.*
|
|
|
|
|
|
|
|
![IMG_0434](http://gitlab.au.dk/uploads/group-5/group-5-lesson-9/e4f64c4394/IMG_0434.JPG)
|
|
|
|
|
|
|
|
*Video 5: Demonstration af roterende ultralydssensor.*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/2u-6_E8zmvs/0.jpg)](https://www.youtube.com/watch?v=2u-6_E8zmvs)
|
|
|
|
|
|
|
|
Vi måtte indse at konstruktionen med den roterende afstandssensor var sværere at implementere end først antaget. Video 5 [17] ovenfor viser hvorledes sensoren er utilregnelig og måler korte afstande på trods af der ingenting er på banen. Vi vurdere årsagen hertil er at ultralydssensoren havde en tendens til at slingre når den stoppede, hvilket i princippet kan gøre det svært for ultralydssensoren at modtage den reflekterede lyd. Selvom det formentlig ville være muligt at løse dette problem, ved at vente førend en måling foretages, vurdere vi at vi mister fordelen ved denne løsning (hastighed), hvis vi indsætter et sådan delay. Vi forsøgte os derfor i stedet med en anden type konstruktion. Denne konstruktion består af to statisk monteret ultralydssensorer, modsat den roterende konstruktion. Billede 4 nedenfor viser denne konstruktion. Bemærk: Vi har nu en motor port i overskud til f.eks. at lave noget aggressivt.
|
|
|
|
|
|
|
|
*Billede 4: Sumobryder robot med to statiske afstandssensorer.*
|
|
|
|
|
|
|
|
![IMG_0441](http://gitlab.au.dk/uploads/group-5/group-5-lesson-9/ffb41c6b9b/IMG_0441.JPG)
|
|
|
|
|
|
|
|
Måden hvorpå vi har programmeret vores FindEnemy behavior baserer sig på samme fremgangsmåde som vores forbedrede udgave af DetectWall behavioren (LeJOS BumperCar). Her benytter vi en daemon tråd der har ansvaret for at trække information ud af de to ultralydssensorer, således at ultralydssensorernes blokerende operationer ikke har betydning for de andre behaviors (se Java-kode 9)
|
|
|
|
|
|
|
|
*Java-kode 9: FindEnemy’s daemon tråd.*
|
|
|
|
|
|
|
|
``` Java
|
|
|
|
Thread thread = new Thread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
while(true) {
|
|
|
|
sensor1.ping();
|
|
|
|
sensor2.ping();
|
|
|
|
|
|
|
|
distance1 = sensor1.getDistance();
|
|
|
|
distance2 = sensor2.getDistance();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
thread.setDaemon(true);
|
|
|
|
thread.start();
|
|
|
|
```
|
|
|
|
|
|
|
|
FindEnemy klassen tager kontrol hvis en af de to afstande er tilpas små og forsøger ellers at bevæge sig i retning af den mindste værdi af de to værdier ved hjælp af følgende action() metode, som ses i Java-kode 10.
|
|
|
|
|
|
|
|
*Java-kode 10: FindEnemy action-metode()*
|
|
|
|
|
|
|
|
``` Java
|
|
|
|
public int action() {
|
|
|
|
_suppressed = false;
|
|
|
|
float difference = (float)(distance2 - distance1) / (float) MIN_DISTANCE;
|
|
|
|
|
|
|
|
int powerDifference = Math.min(Math.round(difference * 60), 20);
|
|
|
|
BumperCar.leftMotor.controlMotor(
|
|
|
|
Math.min(MIN_SPEED + powerDifference, 100),
|
|
|
|
BumperCar.MOTOR_FORWARD
|
|
|
|
);
|
|
|
|
BumperCar.rightMotor.controlMotor(
|
|
|
|
Math.min(MIN_SPEED - powerDifference, 100),
|
|
|
|
BumperCar.MOTOR_FORWARD
|
|
|
|
);
|
|
|
|
|
|
|
|
long delay = 200L + Math.round(Math.abs(difference)) * 10;
|
|
|
|
long time = System.currentTimeMillis();
|
|
|
|
|
|
|
|
while(!_suppressed && System.currentTimeMillis() - time < delay) {
|
|
|
|
Thread.yield();
|
|
|
|
}
|
|
|
|
BumperCar.leftMotor.controlMotor(0, BumperCar.MOTOR_STOP);
|
|
|
|
BumperCar.rightMotor.controlMotor(0, BumperCar.MOTOR_STOP);
|
|
|
|
}
|
|
|
|
|
|
|
|
### Delmål: TurnAround-behavior
|
|
|
|
|
|
|
|
Hvis vores robot blot kører ligefrem og forsøger at undgå kanten kan det tage lang tid for den at finde en modstander. Vi har derfor lavet en TurnAround behavior der tilfældigt en gang hver 1-5 sekund drejer i en tilfældig retning.
|
|
|
|
|
|
|
|
Vi har realiseret denne Behavior ved hjælp af følgende kode, som ses i Java-kode 11.
|
|
|
|
|
|
|
|
*Java-kode 11: Snippet fra TurnAround.*
|
|
|
|
``` Java
|
|
|
|
public int takeControl() {
|
|
|
|
return System.currentTimeMillis() >= takeControl ? 400 : 0;
|
|
|
|
}
|
|
|
|
…
|
|
|
|
private void roll() {
|
|
|
|
takeControl = System.currentTimeMillis() + MIN_DELAY + random.nextInt(MAX_DELAY - MIN_DELAY);
|
|
|
|
}
|
|
|
|
...
|
|
|
|
public void action() {
|
|
|
|
_suppressed = false;
|
|
|
|
|
|
|
|
if(random.nextBoolean()) {
|
|
|
|
BumperCar.leftMotor.controlMotor(85, BumperCar.MOTOR_FORWARD);
|
|
|
|
BumperCar.rightMotor.controlMotor(0, BumperCar.MOTOR_STOP);
|
|
|
|
} else {
|
|
|
|
BumperCar.leftMotor.controlMotor(0, BumperCar.MOTOR_STOP);
|
|
|
|
BumperCar.rightMotor.controlMotor(85, BumperCar.MOTOR_FORWARD);
|
|
|
|
}
|
|
|
|
|
|
|
|
long time = System.currentTimeMillis();
|
|
|
|
while(!_suppressed && System.currentTimeMillis() - time < 500) {
|
|
|
|
Thread.yield();
|
|
|
|
}
|
|
|
|
BumperCar.leftMotor.controlMotor(0, BumperCar.MOTOR_STOP);
|
|
|
|
BumperCar.rightMotor.controlMotor(0, BumperCar.MOTOR_STOP);
|
|
|
|
|
|
|
|
roll();
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### Delmål: AttackEnemy-behavior
|
|
|
|
|
|
|
|
Den sidste behavior vi ønskede at implementere var AttackEnemy. Med denne Behavior ønskede vi at få robotten til at angribe fjenden, lige så snart fjenden var indenfor en bestemt rækkevidde. Vi ønskede at robotten skulle angribe ved at skubbe aggressivt til modstanderen, som vi havde set en professionel sumobryderrobot benyttede i [10]. Da vi dermed skulle benytte os af en lineært direktionel kraft, lod vi os yderligere inspirere af stempel-mekanismen på et tog. Vi fandt yderligere inspiration i en lego-konstruktion, der gjorde brug af denne mekanisme [11]. Udover at være krævende at konstruere i sig selv, var det en stor udfordring for os at inkorporere denne opførsel fysisk i vores robot uden at overskride hverken de dimensionelle eller de vægtmæssige grænser. Nedenstående billede viser, hvordan det lykkedes os at konstruere vores robot, således vores sidste Behavior kunne implementeres. Billede 5 nedenfor viser dermed også, hvordan vores sumobryder robot endeligt kom til at se ud, da den nu indeholder at vores ønskede behaviors.
|
|
|
|
|
|
|
|
*Billede 5: Sumobryder robot med stempel-mekanisme*
|
|
|
|
|
|
|
|
![IMG_0505](http://gitlab.au.dk/uploads/group-5/group-5-lesson-9/2ce4c1c8b9/IMG_0505.JPG)
|
|
|
|
|
|
|
|
Vores endelige sumobryder-konstruktion består af:
|
|
|
|
|
|
|
|
* 1 NXT
|
|
|
|
* 4 motorer (bemærk RCX kablerne, så vi kunne styre to motorer med én motorport)
|
|
|
|
* 2 afstandssensorer
|
|
|
|
* 1 lyssensor (placeret foran bag skjoldet)
|
|
|
|
* 1 RCX lyssensor (placeret bagpå)
|
|
|
|
* En stempelmekanisme + skjold
|
|
|
|
* 1 ManGreen (fartøjspilot, *bemærk*: vores robot er stadig autonom)
|
|
|
|
|
|
|
|
Da vores klasse for FindEnemy-behavioren allerede benytter input fra de to ultralydssensorer, har vi implementeret AttackEnemy klassen ved at lade den have en reference til FindEnemy klassen. AttackEnemy-behavioren tager kontrollen som illustreret af følgende kodeudsnit:
|
|
|
|
|
|
|
|
*Java-kode 12: Snippet fra AttackEnemy*
|
|
|
|
|
|
|
|
``` Java
|
|
|
|
public int takeControl() {
|
|
|
|
if(System.currentTimeMillis() - lastAttackFinished >= ATTACK_DELAY_TIME &&
|
|
|
|
(findEnemy.getDistance1() < 30 || findEnemy.getDistance2() < 30)) {
|
|
|
|
return 400;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Dermed tager AttackEnemy kontrol, når ultralydssensorerne detekterer en modstander under 30 cm væk, og aktivere dens action, hvilken er at køre fremad med fuld motorkraft, samtidig med at stemplet kører på fuld kraft. Nedenstående video er en demonstration af stemplets funktionalitet, som er en del af AttackEnemy-behavioren. Derudover implementerede vi et delay som betyder at stempelmekanismen holder en pause på 3 sekunder inden stempelmekanismen begynder igen. Dermed kunne vi sikre at robotten var istand til at angribe modstanderen over flere omgange, i stedet for at “låse sig fast” i angrebsbevægelsen. Denne taktik er inspireret at den spartanske krigstaktik, hvor forsvaret gradvist rykker deres skjold frem, og dermed presser fjenden bagud gennem korte intensive bevægelser [12]. Nedenstående video 6 [18] demonstrerer AttackEnemy behavioren.
|
|
|
|
|
|
|
|
|
|
|
|
*Video 6: Demonstration af AttackEnemy behavior*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/8kMch-I9Kl8/0.jpg)](https://www.youtube.com/watch?v=8kMch-I9Kl8)
|
|
|
|
|
|
|
|
|
|
|
|
*Billede 6: Dimensioner på vores sumobryderrobot*
|
|
|
|
|
|
|
|
![Skærmbillede 2015-05-18 kl. 13.12.02](http://gitlab.au.dk/uploads/group-5/group-5-lesson-9/8590a5942e/Sk%C3%A6rmbillede_2015-05-18_kl._13.12.02.png)
|
|
|
|
|
|
|
|
Som det fremgår af billede 6 overholder vores sumobryderrobot kravet om at samtlige dimensioner på robotten skal være mindre end 30 cm. Robotten længde (a), bredde (b) og højde (c) er således mindre end et A4 ark, der har længden 29,7 cm.
|
|
|
|
|
|
|
|
*Billede 7: Vægt af sumobryderrobot*
|
|
|
|
|
|
|
|
![IMG_0513](http://gitlab.au.dk/uploads/group-5/group-5-lesson-9/e1636f3863/IMG_0513.JPG)
|
|
|
|
|
|
|
|
Slutteligt, som det ses af billede 7 overholder vores robot også kravet om at veje mindre end 1000 g. Vores robot endte med at veje hele 999 g (men altså stadig mindre end 1000 g).
|
|
|
|
|
|
|
|
#### Fights with our sumo robot
|
|
|
|
|
|
|
|
For at afprøve vores sumobryderrobots evne til at sumobryde undertog vi en række kampe med andre sumobryderrobotter.
|
|
|
|
|
|
|
|
*Video 7: Sumo battle 1, losing*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/rcu5IMD5cac/0.jpg)](https://www.youtube.com/watch?v=rcu5IMD5cac)
|
|
|
|
|
|
|
|
|
|
|
|
*Video 8: Sumo battle 2, winning*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/HVM5WeGeqOY/0.jpg)](https://www.youtube.com/watch?v=HVM5WeGeqOY)
|
|
|
|
|
|
|
|
*Video 9: Sumo battle 3, losing*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/T-GyLjsuerI/0.jpg)](https://www.youtube.com/watch?v=T-GyLjsuerI)
|
|
|
|
|
|
|
|
I videoerne ovenfor (Video 7 [19], Video 9 [21]) taber vi, umiddelbart fordi vi hele tiden befinder os i nærheden af banens kant. Vi valgte derfor efter disse kampe at ændre vores AvoidEdge-behavior således at robotten bakkede i længere tid (1 sek til 1,5 sek og maks power) og drejede mere end førend, når den fandt en kant. Denne ændring var yderst gavnlig og gjorde os i stand til at vinde *ALLE* kampe herefter. Videoerne nedenfor viser disse kampe (Video 10 [22], Video 11 [23], Video 12 [24], Video 13 [25], Video 14 [26] og Video 15 [27]).
|
|
|
|
|
|
|
|
*Video 10: Sumo battle 4, winning*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/8WttFiqLSRk/0.jpg)](https://www.youtube.com/watch?v=8WttFiqLSRk)
|
|
|
|
|
|
|
|
|
|
|
|
*Video 11: Sumo battle 5, winning*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/HkA9dSnsMR8/0.jpg)](https://www.youtube.com/watch?v=HkA9dSnsMR8)
|
|
|
|
|
|
|
|
*Video 12: Sumo battle 6, winning*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/I9aD4VxlXAc/0.jpg)](https://www.youtube.com/watch?v=I9aD4VxlXAc)
|
|
|
|
|
|
|
|
*Video 13: Sumo battle 7, winning*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/RdLUSZWj6GI
|
|
|
|
/0.jpg)](https://www.youtube.com/watch?v=RdLUSZWj6GI)
|
|
|
|
|
|
|
|
|
|
|
|
*Video 14: Sumo battle 8, winning*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/HH9u3CG-Zuk/0.jpg)](https://www.youtube.com/watch?v=HH9u3CG-Zuk)
|
|
|
|
|
|
|
|
|
|
|
|
*Video 15: Sumo battle 9, winning*
|
|
|
|
|
|
|
|
[![image alt text](https://img.youtube.com/vi/Di2TxQ91Ypo/0.jpg)](https://www.youtube.com/watch?v=Di2TxQ91Ypo)
|
|
|
|
|
|
|
|
|
|
|
|
## Conclusion
|
|
|
|
Vi har med de indledende undersøgelser fået en bedre forståelse for hvorledes behavior-based control kan implementeres og bruges til at styre en LEGO robot, samt hvilke elementer man skal tage sig i agt for. I disse undersøgelser arbejde vi bl.a. med at undgå at foretage blokerende kald i arbiteren, såsom med f.eks. ultralydssensorens getDistance(). Derudover tog vi et nærmere kig på hvorledes behaviors kan interrupted og genstartes.
|
|
|
|
|
|
|
|
Herefter konstrueret og programmerede vi vores egen sumobryderrobot til at kæmpe mod andre sumobryderroboter. Ige
|
|
|
|
|
|
|
|
## References
|
|
|
|
|
|
|
|
[1] http://legolab.cs.au.dk/DigitalControl.dir/NXT/Lesson9.dir/Lesson.html
|
|
|
|
|
|
|
|
[2] Express bot instruktioner: http://www.nxtprograms.com/9797/express-bot/pdf/ExpressBot-DrivingBase-StepByStep.pdf
|
|
|
|
|
|
|
|
[3] Jones, Flynn, and Seiger, "Mobile Robots, Inspiration to Implementation", Second Edition, 1999.
|
|
|
|
|
|
|
|
[4] The leJOS Tutorial, Behavior Programming: http://www.lejos.org/nxt/nxj/tutorial/Behaviors/BehaviorProgramming.htm
|
|
|
|
|
|
|
|
[5] Group 5 - Lesson 7: https://gitlab.au.dk/group-5/group-5-lesson-7
|
|
|
|
|
|
|
|
[6] Behavior.java - http://legolab.cs.au.dk/DigitalControl.dir/NXT/Lesson9.dir/Behavior.java
|
|
|
|
|
|
|
|
[7] Arbitrator.java - http://legolab.cs.au.dk/DigitalControl.dir/NXT/Lesson9.dir/Arbitrator.java
|
|
|
|
|
|
|
|
[8] BumperCar.java - http://legolab.cs.au.dk/DigitalControl.dir/NXT/Lesson9.dir/BumperCar.java
|
|
|
|
|
|
|
|
[9] Group 5 - Lesson 1: https://gitlab.au.dk/group-5/group-5-lesson-1/wikis/home
|
|
|
|
|
|
|
|
[10] RobotChallenge2012 - Sumo wrestler: https://youtu.be/3tguWcKTXQI?t=5m18s
|
|
|
|
|
|
|
|
[11] Inspiration til stempel-mekanisme: https://www.youtube.com/watch?v=GOQENxeL4nY
|
|
|
|
|
|
|
|
[12] Spartansk krigstaktik: https://youtu.be/HdNn5TZu6R8?t=2m51s
|
|
|
|
|
|
|
|
|
|
|
|
Videoer:
|
|
|
|
|
|
|
|
[13] Video 1: https://www.youtube.com/watch?v=PPa-NxbjCzw
|
|
|
|
|
|
|
|
[14] Video 2: https://www.youtube.com/watch?v=lAmipiL2yUU
|
|
|
|
|
|
|
|
[15] Video 3: https://www.youtube.com/watch?v=7Ie5lJAXUIE
|
|
|
|
|
|
|
|
[16] Video 4: https://www.youtube.com/watch?v=XCGIMv6Vldc
|
|
|
|
|
|
|
|
[17] Video 5: https://www.youtube.com/watch?v=2u-6_E8zmvs
|
|
|
|
|
|
|
|
[18] Video 6: https://www.youtube.com/watch?v=8kMch-I9Kl8
|
|
|
|
|
|
|
|
[19] Video 7: https://www.youtube.com/watch?v=rcu5IMD5cac
|
|
|
|
|
|
|
|
[20] Video 8: https://www.youtube.com/watch?v=HVM5WeGeqOY
|
|
|
|
|
|
|
|
[21] Video 9: https://www.youtube.com/watch?v=T-GyLjsuerI
|
|
|
|
|
|
|
|
[22] Video 10: https://www.youtube.com/watch?v=8WttFiqLSRk
|
|
|
|
|
|
|
|
[23] Video 11: https://www.youtube.com/watch?v=HkA9dSnsMR8
|
|
|
|
|
|
|
|
[24] Video 12: https://www.youtube.com/watch?v=I9aD4VxlXAc
|
|
|
|
|
|
|
|
[25] Video 13: https://www.youtube.com/watch?v=RdLUSZWj6GI
|
|
|
|
|
|
|
|
[26] Video 14: https://www.youtube.com/watch?v=HH9u3CG-Zuk
|
|
|
|
|
|
|
|
[27] Video 15: https://www.youtube.com/watch?v=Di2TxQ91Ypo
|
|
|
|
|