tirsdag den 18. januar 2011

NXT Programming, lesson 19

Dato: 11-1-2011
Varighed: 7½ timer
Deltagere: JC, Allan, Nick



Formål:


Vi skal i dag lave NXT'ens side af bluetoothforbindelsen, så den løbende kan modtage data fra vores mobiltelefon. Vi skal samtidig lave en sensorklasse der repræsenterer kameraet, så vi kan kalde denne klasse, når vi skal bruge sensorinput fra mobilen.


Plan:


-Lave en klasse, der kommer til at hedde BTReceiver, der skal køre i sin egen tråd og hele tiden modtage data fra mobilen.
-Lave en klasse, der kommer til at hedde CameraSensor, der skal virke som de andre sensorer, hvor vi løbende kan bede om ny data.




BTReceiver:


Koden til denne klasse:



package Robot;


import java.io.DataInputStream;
import java.io.IOException;
import lejos.nxt.LCD;
import lejos.nxt.comm.BTConnection;
import lejos.nxt.comm.Bluetooth;
import lejos.nxt.comm.NXTConnection;


public class BTReceiver implements Runnable{

String data;
private long timeNow = System.currentTimeMillis();
long time;
CameraSensor cs;

public BTReceiver(CameraSensor cs){
this.cs = cs;
}


@Override
public void run() {
LCD.drawString("Awaiting Connection", 0, 0);
BTConnection btc = Bluetooth.waitForConnection(0, NXTConnection.RAW);

while(true){
LCD.drawString("Starting stream", 0, 0);
DataInputStream dis = btc.openDataInputStream();

byte[] b = new byte[254];
try {
LCD.drawString("Trying to read", 0, 0);
dis.read(b);
} catch (IOException e) {
}
LCD.drawString("Refreshing data", 0, 0);
cs.setData(new String(b));
long time = System.currentTimeMillis() - timeNow;
timeNow = System.currentTimeMillis();
}
}
}

BTReceiver er en implementation af interfacet Runnable, så denne klasse kan køre i en separat tråd, sideløbende med vores hovedtråd, der styrer robotten.

Klassen har et instans af CameraSensor, og den kan via metoden setData(String s) sætte en sensormåling på CameraSensor'eren og derved opdatere dataen, der er målt.

I klassens run-metode bliver der oprettet en BTConnection, der venter på forbindelse fra en enhed den i forvejen er parret. I vores tilfælde havde vi parret den med mobiltelefonen på forhånd. Nu kan BTReceiver modtage data i størrelsen 254 bytes og opdaterer dataen ved hvert gennemløb.

I starten ville vi sende billedet, ved at vælge en enkelt horisontal pixellinie der ville ramme den højde hvor alle klodserne var, ligegyldigt med hvilken afstand. Pixellinien skulle sendes i formatet {RØD},{GRØN}#... hvor det er værdierne for de to farver i den givne pixel (vi valgte at se bort fra den blå værdi i en pixel, da vi kun skal finde røde eller grønne klodser). Dette ville dog betyde med et billede med en vidde på 176 pixels, og en char-størrelse i Java på 2 bytes, at vi skulle sende 2*8*176 = 2816 bytes per billede.

Vores første forsøg på en løsning til dette problem var at sende dataen i mindre pakker og kun opdatere dataen når vi havde en fuld pixellinie, men dette nedsatte hastigheden på vores opdateringer så kraftigt, at denne ide ikke var brugbar.

Vores anden løsning var at kun tage en del af de pixels der er i denne linie. Men dette ville betyde at vi kun kunne tage hver 12. pixel og dette var heller ikke acceptabelt.

Vi har derfor valgt at ændre på datastrukturen, så telefonen laver en del af analysearbejdet, og kun sender et kort svar om resultatet, som robotten kan forholde sig til. Dette vil vi arbejde videre med i morgen.

CameraSensor:

Koden til denne klasse:

package Robot;

import java.util.ArrayList;
import lejos.nxt.LCD;
import lejos.nxt.Sound;

public class CameraSensor {
String data = "";
BTReceiver btr;
ArrayList<Integer> tdata = new ArrayList<Integer>();
int brickColor = 0;

public CameraSensor(){
btr = new BTReceiver(this);
Thread t = new Thread(btr);
t.start();
}

public int getBrickColor() {
if(Claw.isOpen())
brickColor = tdata.get(1);
return brickColor;
}
public int getHits() {
return tdata.get(0);
}

public void setData(String data) {
this.data = data;
if(data.equals("")) return;
tdata = splitData();
}

private ArrayList<Integer> splitData(){
ArrayList<Integer> res = new ArrayList<Integer>();
try {
int splitter = data.indexOf(';');
res.add(Integer.parseInt(data.substring(0,splitter)));
res.add(Integer.parseInt(data.substring(splitter+1, splitter+2)));
} catch(Exception e) {
}
return res;
}
}

Denne klasse startede ud som værende meget stor, da denne skulle stå for at tage den store mængde data fra mobiltelefonen, og omdanne denne data til en liste af tal, der hver især repræsenterede en pixels farve. Hvis der i denne pixel var hverken rød eller grøn, ville der stå 0, hvis der var mest rød 1, og hvis det var grøn 2. Derved ville robotten kunne bruge denne data til at se hvilken retning den skulle, hvis der var store mængder rød eller grøn. Da vi blev nødt til at ændre på datastrukturen for dataen der blev sendt, blev denne klasse også ændret, og det meste af billedeanalysen ligger nu på telefonen.

Koden ovenfor er hvad vi endte med efter ændringen. CameraSensor'en starter en BTReceiver-tråd og hver gang denne opdaterer dataen, bliver den delt op i to, det ene er hits og det andet er brickColor. Dette vil blive uddybet i næste blogindlæg.

Vi ville gerne have brugt metoden split(), som strings normalt har i Java, men denne fandtes ikke i LeJOS, og vi var derfor nødt til at lave vores egen version. Dog har LeJOS metoden substring() og indexOf(), så det var let.

Resultat:

Vi er kommet frem til at vi er nødt til at lade telefonen lave flere beregninger for at kunne minimere mængden af data der skal sendes mellem de to enheder. Dette burde der være kraft nok til, da det er en 1 GHz processor der sidder i telefonen. Derfor er meget af beregningsbyrden flyttet fra NXT'en over til telefonen.

Pga. denne ændring i datastrukturen er vores CameraSensor-klasse også blevet betydelig mindre og skal derfor også kun står for at tage mod dataen og putte den ind i en liste.

Om NXT'en kan bruge denne lille mængde data til at navigere med kigger vi på næste gang.


Ingen kommentarer:

Send en kommentar