Reaali Robootika.COM

NXT robotimaailm ja programmeerimine C-keeles

Tunnitöö: Explorer 3 – maadeavastaja robot

image

Ülesanne 3 (Exploreri sarjast)

Ehita BOT, millel on peal mootoriga Ultrasonic andur.

Kirjuta programm, mille käigus robot sõidab otse kuni näeb ees seina 20 cm kaugusel. Seina nägemisel robot peatub, vaatab Ultrasonic anduriga paremale / vasakule ning see kummal pool on rohkem ruumi (ehk US andur näeb kaugemale), sinna sõidab.

 

Lahendus

Ultrasonic anduriga paremalt-vasakult kaugust mõõtes peab selle salvestama muutujatesse ning siis neid omavahel võrdlema. Selle põhjal otsustab robot kuhu keerata.

 

task main(){
SetSensorLowspeed(S1);

int Kaugus;
int ParemPool;
int VasakPool;
Kaugus = 20;

while(TRUE){
	if(SensorUS(S1) < Kaugus){
		Off(OUT_BC);
		RotateMotor(OUT_A, 25, 90);
		ParemPool = SensorUS(S1); //mõõdab paremal kaugust
		RotateMotor(OUT_A, 25, -180);
		VasakPool = SensorUS(S1); //mõõdab vasakult poolt kaugust
		RotateMotor(OUT_A, 25, 90);
		if(ParemPool > VasakPool){
			RotateMotorEx(OUT_BC, 50, 217, 100, TRUE, TRUE); //keerab paremale
			}
		else{
			RotateMotorEx(OUT_BC, 50, 217, -100, TRUE, TRUE); //keerab vasakule
			}
		}
	
	else{
		OnFwdSync(OUT_BC, 100, 0);
		}
	}
}

Tunnitöö: Explorer 1 ja 2 – maadeavastaja robot

Explorer

Ülesanne 1

Ehita BOT, millel on ees Ultrasonic kauguseandur.

Kirjuta programm, mille käigus robot sõidab otse kuni näeb ees seina 20 cm kaugusel. Seina nägemisel robot peatub, tagurdab ja keerab kas paremale või vasakule (valitakse juhuslikkuse meetodil, funktsiooniga Random()).

 

Ülesanne 2

Ehita BOT, millel on ees lülitiandur.

Kirjuta programm, mille käigus robot sõidab otse kuni lüliti vajutamiseni ehk seina vms takistuseni. Takistuse avastades robot peatub, tagurdab ja keerab kas paremale või vasakule (valitakse juhuslikkuse meetodil, funktsiooniga Random()).

 

Lahendus ülesannetele 1 ja 2

Mõlemad ülesanded on sarnased, erinedes ainult selle poolest, et ühel puhul keerab robot tagasi kauguseandurist saabuva signaali tõttu ning teisel juhul lülitist saabuva signaali tõttu.

Alljärgnevas näites on kasutatud switch-elementi, kuid selle võib asendada ka if-else lausega.

task main(){
/*
KAUGUS muutuja abil määrab robot kas peab tagasi sõitma
RAND muutuja abil määrab robot kas ta sõidab paremale või vasakule
*/
SetSensorLowspeed(S4);
int KAUGUS = 20;
int RAND;
while (TRUE){
      if (SensorUS(S4) < KAUGUS){          //Kui robot näeb seina lähedamal kui 20 cm siis ta sõidab tagasi
        RAND = Random(2);
        switch (RAND)
        {
          case 0:                          //Robot sõidab paremale
          RotateMotorEx(OUT_BC, 100, 335, -50, TRUE, TRUE);
          break;
          case 1:                          //Robot sõidab vasakule
          RotateMotorEx(OUT_BC, 100, 335, 50, TRUE, TRUE);
          break;
        }
      }
      else {
           OnRevSync(OUT_BC, 100, 0);      //Muidu sõidab robot edasi
      }
}
}

7. tund: NXT kiiruse testimine

Tunni sisu on testida NXT aju protsessori kiirust kahe erineva programmeerimiskeele NXC ja NXT-G ja erinevate NXT Firmwarede korral.

Õppematerjal: Juhend- Do–While ja For tsüklid

Antud ülesande lahendame C-keeles FOR tsükliga.

Ülesanne. NXT kiiruse testimine.

Programm peab välja nägema järgmine.

Programmi käivitudes alustab tööd tsükkel, mis teeb 10 000/100 000 tavalist tsüklit. Lisaks on vaja kirjutada juurde stopper, mis mõõdab nende tsüklite läbimiseks kulunud aega ning kuvab selle ekraanil peale tsükli lõpetamist.

Kirjeldatud programm tuleb luua nii NXT-G kui ka NXC-s. Mõõta ja panna kirja tulemused.

Lisaks tuleb katsetada seda erineva NXT Firmware-ga. 

  • NXC: 1.28
  • Mindsensor: 1.29
  • Lego: 1.29
  • Lego: 1.31

Mõõta ja katsetada kas on erinevate Firmwarede ja programmeerimiskeeltega kiirustes erinevusi. Alljärgnevas tabelis on erinevate mõõtmiste kokkuvõte, tulemused on sekundites.

  Firmware 1.28
NXC
Firmware 1.29
Lego
Firmware 1.31
Lego
Firmware 1.29
Mindsensor
tsükleid NXT-G NXC NXT-G NXC NXT-G NXC NXT-G NXC
10 000 2,197 s 0,806 s 2,179 s 0,774 s 2,176 s 0,773 s 2,059 s 0,736 s
100 000 21,903 s 8,061 s 21,715 s 7,742 s 21,687 s 7,731 s 20,516 s 7,359 s
                 

Erakordselt suur vahe on programmeerimiskeele valikul. NXC on peaaegu 3 korda kiirem kui NXT-G.

Teine, kuid võrdlemisi tagasihoidlik vahe on Firmware’s.

Kuna me kirjutame programme C-keeles, on meil peamiselt laetud NXC firmware 1.28. Olen avastanud sellel firmware’l ühe puuduse, nimelt Mindsensori I2C Digital Port Splitter taga olevad andurid ei tööta selle firmware korral.

Kui laadida aga Mindsensori firmware (et töötaks I2C Digital Port Splitter), ei tööta jällegi mõned NXC spetsiifilised funktsioonid, nagu näiteks HiTechnic Prototype Boardi juhtimine.


Lahendus: NXC programm

/*
autor:Ramses Sepp 16.02.2011   19:28
See programm mõõdab aega mis kulub NXT-l ühe miljoni tsükli läbimiseks
Muudetud: Leivo Sepp 18.02.2011
*/

long LIIDA = 0;
long START;

long Stopper(long tsykkel)
{
	START = CurrentTick();
	//siin defineerin muutuja "i" mis võrdub nulliga 
	//ning paneb iga kord ühe i-le juurde
	//seni kaua kuni on üks miljon täis
	for(long i = 0; i < tsykkel; i++){
		//et NXT-l oleks ka mingi lihtne tehe arvutada
		LIIDA += 4;	
		}
	//funktsioon Stopper tagastab stopperi lõpuaja
	return CurrentTick()-START;
}

task main(){

long k = 10000;
string msg, stpr;

for (long j = 1; j < 3; j ++ ){
	//Kuvame ekraanil, et programm on alles käimas
	TextOut(3, LCD_LINE1, "Running ... ");
	//moodustame stringi ekraanile, mis kuvab tsükli arvu ja kestuse
	msg = StrCat(NumToStr(k), ":              ");
	stpr = NumToStr(Stopper(k));
	msg = StrReplace(msg, 8, stpr);
	TextOut(5, LCD_LINE2-j*8, msg);
	//korrutame tsükliarvu 10-ga
	k *= 10;
	}
ClearLine(LCD_LINE1);
TextOut(3, LCD_LINE1, "Valmis");
//programm jääb nupulevajutust ootama
until(ButtonPressed(BTNCENTER, FALSE));
}

Lahendus 2: NXT-G programm

NXT-G programm on selles mõttes lihtsam tehtud, et iga mõõtmise korral tuleb eraldi loop-le counter määrata: 10 000/100 000. NCX programmis toimus see automaatselt.

image

6. tund: Andurite info kuvamine NXT ekraanil

Selle tunni käigus õpitakse kuvama andurite infot ekraanil. See on vajalik et aru saada mis infot andurid parasjagu väljastavad.

Õppematerjal:

Ülesanne.

Kuva ühendatud anduri info ekraanil. Vaja läheb NXT-d ning erinevaid andureid.


Variant a)

UltraSonic kauguseandur

Lahendus: Iseenesest väga lihtne programm, vaid ühe while() tsükliga.

/*
See programm kuvab ekraanile sõna Ultrasensor 
ja selle alla kui kaugel on ees olev asi.
*/

task main () {

SetSensorLowspeed (S2);

while (TRUE) {
   ClearScreen();
   TextOut(1, LCD_LINE1, "Ultrasonic");
   NumOut(1, LCD_LINE2, SensorUS (S2));
   Wait(200);
   }
}

Variant b)

Light sensor andur

/*
See programm kuvab ekraanile sõna LightSensor 
ja selle alla kui hele on ees olev asi.
*/
task main () {

SetSensorLight (S2);

while (TRUE) {
   ClearScreen();
   TextOut(1, LCD_LINE1, "LightSensor");
   NumOut(1, LCD_LINE2, Sensor(S2));
   Wait(200);
   }
}

Variant c)

Color sensor

Lahendus. Selle anduri juures annab numbri kuvamine liiga vähe olulist informatsiooni, seega palusin lahendada ülesande sellisel moel, et vastavalt värvile kuvataks ekraanil värvi nimetus. Ja need värvid mille nimetus pole kirjutatud, nende korral kuvatakse number. NB! Edaspidi õpime massiive ning saab sama programmi uuesti teha massiivi abil. Ehk tunduvalt lühema ja lihtsama.

/*
See programm kuvab ekraanile sõna ColorSensor 
ja selle alla värvi nimetuse
*/
task main () {

SetSensorLowspeed (S2);

while (TRUE) {
   ClearScreen();
   TextOut(1, LCD_LINE1, "ColorSensor");
   switch(SensorHTColorNum(S2))
   {
     case 0:
            TextOut (1, LCD_LINE2, "Must");
            break;
     case 6:
            TextOut (1, LCD_LINE2, "Kollane");
            break;     
     case 8:
            TextOut (1, LCD_LINE2, "Punane");
            break;     
		 case 14:
            TextOut (1, LCD_LINE2, "Beez");
            break;
     case 17:
            TextOut (1, LCD_LINE2, "Valge");
            break;

     default:
            NumOut(1, LCD_LINE2, SensorHTColorNum(S2));
      }
      Wait (200);
   }
}

4 tund: Kahe lülitiga roboti juhtimine

Õppematerjal:
3 tund- Lülititega roboti juhtimine
Juhend- while, until tsüklite kasutamine
Juhend- IF ELSE ja SWITCH kasutamine
Juhend- Andurite defineerimine ja lugemine

Sisuliselt on selleks tunniks 2 ülesannet.

Alljärgnevate ülesannete juurde on lisatud ka nende olekudiagrammid, mille abil on programmiosade töö selgitamine lihtsam. Samuti annavad programmist tervikliku ülevaate.

Ülesanne 2. Robotil on küljes kaks lülitiandurit.

roboti programmi olekudiagrammVariant a) Robot sõidab edasi, kuid vajutades ühte lülitit keerab sujuvalt paremale ning teise lüliti korral vasakule.

Lahendus. Iseenesest lihtne IF ELSE lausete jada. Esiteks täidetakse lõpmatult tsüklit, mille sees kontrollitakse iga tsükli käigus kas üks või teine lüliti on vajutatud.  Siin on kasutatud edasiliikumiseks OnRevSync funktsiooni, kuna antud BOT-l olid mootorid teisipidi peal.

task main(){
SetSensorTouch(S2);
SetSensorTouch(S1);
while(TRUE){
  if (Sensor(S2)){
    OnRevSync(OUT_BC, 50, -10);
  }
  else if(Sensor(S1)){
    OnRevSync(OUT_BC, 50, 10);
  }
  else{
    OnRev(OUT_BC, 50);
  }
}
}

roboti programmi olekudiagrammVariant b) Robot hakkab edasi sõitma siis kui mõlemad lülitid on korraga alla vajutatud ja lahti lastud. Üksikult kasutades toimivad lülitid paremale-vasakule keeramisena. Teistkordsel üheaegsel vajutamisel jääb robot seisma.

Lahendus.

Alguses robot seisab ja ootab kuni mõlemad lülitid on bumped (ehk vajutatud-lahti lastud).

Peale lülitite vajutamist käivitub funktsioon while, mille tingimuseks on määratud Bumped2Buttons muutuja. Kuna see muutuja on algväärtustatud eelnevalt FALSE-ks siis läheb antud tsükkel käima (kuna tingimuslauses on muutuja ees ! hüüumärk).

Seejärel toimib programm täpselt nii nagu ee on kirjeldatud eelmise variandi korral – ehk keerab paremale-vasakule-sõidab otse.

Kuid lisaks on toodud üks IF lause, mille tingimuseks on kontroll, kas mõlemad lülitid on vajutatud. Kui on alla vajutatud, täidetakse IF lause ning until-funktsioon jääb ootama, kuni lülitid on lahti lastud.

Lülitite lahti laskmisel muudetakse Bumped2Buttons väärtus TRUE-ks ning edasi läheb programm uuele tsüklile ja kontrollib tingimust. Kuna tingimus ei ole tema jaoks tõene, väljutakse while tsüklist lülitatakse mootorid välja ja jäädakse ootama taas nuppude bumped-vajutust roboti käivitamiseks.

Selle programmi loomisel tuli juba päris mitu viga ning algne versioon oli ka sutsu keerukam kui alljärgnev lõplik lahendus.

task main(){
bool Bumped2Buttons;
SetSensorTouch(S1);
SetSensorTouch(S2);
while(true){
  Bumped2Buttons = FALSE;
  until(Sensor(S1) && Sensor(S2));
  until(!Sensor(S1) && !Sensor(S2));
  while(!Bumped2Buttons){
    if (Sensor(S1)== 1){
       OnRevSync(OUT_BC, 50, -10);
    }
    else if(Sensor(S2)== 1){
       OnRevSync(OUT_BC, 50, 10);
    }
    else{
       OnRev(OUT_BC, 50);
    }
    if(Sensor(S1) && Sensor(S2)){
      until(!Sensor(S1) && !Sensor(S2));
      Bumped2Buttons = TRUE;
    }
  }
  Off(OUT_BC);
}
}

Teine variant sama ülesande lahendamiseks on alljärgnev.

2 lülitiga pöörav robot

Siin variandis ei ole kasutatud muutujat, mille alusel väljutaks teisest while-tsüklist. Siin on kasutatud ära matemaatilist loogika ning tsüklist väljumise tingimus on sisse kirjutatud while-tsükli enda tingimuseks. Tulemuseks on mõnevõrra lihtsam kood.

 

task main()
{
 SetSensorTouch(S1);
 SetSensorTouch(S2);
 while(TRUE)
 {
	until(Sensor(S1) && Sensor(S2)); //ootab nupu vajutust
	until(!Sensor(S1) && !Sensor(S2)); //ootab nupu lahti laskmist
	//While tsükli stoppimiseks, ootab kuni mõlemad nupud on alla vajutatud 
	while(!Sensor(S1) || !Sensor(S2)) 	{
		if(Sensor(S1))
		{
			OnFwdSync(OUT_BC, 75, 10);
		}
		else if(Sensor(S2))
		{
			OnFwdSync(OUT_BC, 75, -10);
		}
		else
		{
		OnFwdSync(OUT_BC, 75, 0);
		}
	}
	Off(OUT_BC);
	until(!Sensor(S1) && !Sensor(S2)); //ootab nupu lahti laskmist
 }
}

3 tund: Lülititega roboti juhtimine

Tunniteemaks ülesanded, mille käigus tuleb roboti tööd juhtida lülitite abil. tegemist suhteliselt lihtsate ülesannetega, kuid C-keele õppimiseks täiesti piisava keerukusega.

Õppematerjal:
Juhend- Andurite defineerimine ja lugemine
Juhend- while, until tsüklite kasutamine
Juhend- IF ELSE ja SWITCH kasutamine

Tunni sisu:

Igaühel on oma BOT, millele on vastavalt esimeste ülesannete käigus ühendatud 1 lüliti ning teiste käigus kaks lülitit. Õpime kasutama funktsioone Until, While ja IF ELSE laused.

Tegelikult ei jõudnud keegi neid ülesandeid 2 tunni jooksul ära teha, mistõttu ülesanne nr 2 jääb järgmise tunni teemaks.


Ülesanne 1. Robotil on küljes üks lülitiandur pika juhtmega.

Variant a) Robot sõidab seni kuni lüliti on vajutatud.

Lahendus. Esialgu muutujaid ei kasutanud, jõuame nendeni edaspidi. Õppisime kasutama IF ELSE lauset.

task main(){
SetSensorTouch(S1);                          
while (TRUE)
{ if (Sensor(S1))
{ OnFwd(OUT_BC, 100); } else
{ Off(OUT_BC); } } }

Variant b) Robot seisab kui lüliti on vajutatud.

Lahendus. Selle ülesande lahendamisel lähtus enamik sellest, et vahetati IF ja ELSE laused omavahel ära ning suur oli poiste üllatus kui selgus, et seda on võimalik lahendada vaid ühe hüüumärgi lisamisega. Kui näiteks IF ja ELSE lausete sisu on väga mahukas, on nende vahetamine igal juhul ajamahukam ja mõningate ohtudega seotud vs. hüüumärgi lisamine.

task main(){
SetSensorTouch(S1);                          
while (TRUE)
{ if (!Sensor(S1))
{ OnFwd(OUT_BC, 100); } else
{ Off(OUT_BC); } } }

Variant c) Robot hakkab sõitma kui lülitit on korra vajutatud ja lahti lastud ning peatub kui teist korda vajutatakse/lahti lastakse (bumped).

Lahendus. Õppisime kasutama until funktsiooni, mille abil on lihtne teha lülitile bumped-tüüpi vajutuse kontroll. Esimene until ootab kuni lüliti on vajutatud ning järgmine until ootab kuni lüliti on lahti lastud.

task main(){
SetSensorTouch(S1);
while(TRUE){
  until(Sensor(S1));
  until(!Sensor(S1));
  OnRev(OUT_BC, 100);
  until(Sensor(S1));
  until(!Sensor(S1));
  Off(OUT_BC);
}
}

Ülesanne 2. Robotil on küljes kaks lülitiandurit.

Variant a) Robot sõidab edasi, kuid vajutades ühte lülitit keerab sujuvalt paremale ning teise lüliti korral vasakule.
Variant b) Robot hakkab edasi sõitma siis kui mõlemad lülitid on korraga alla vajutatud ja lahti lastud. Üksikult kasutades toimivad lülitid paremale-vasakule keeramisena. Teistkordsel üheaegsel vajutamisel jääb robot seisma.

2 tund: Ratta läbimõõt ja mootori pöörete arv

Eelmisel tunnil õppisime arvutama roboti pööramiseks vajalikku mootori pöördenurka. Täna tuleb seda kasutada praktikas.

Kasutatavad materjalid:
Juhend- Lego rattad ja matemaatika
Juhend- Mootorite peamised käsud NXC-s

Tunni sisu:

Igaühel oli vaja endale BOT teha. Mõni oli juba valmis ja puuduolevad valmisid kiirelt.

Edasi oli vaja mõõta roboti andmed, ratta diameeter (lasin kõigil BOT-del erinevate suurustega rattad panna) ning ratastevaheline kaugus.

Enne programmeerimise juurde asumist tuli välja arvutada ülesandes vajaminevad numbrid.

Kõige rohkem tekitas probleeme viimase ülesande nurga arvutus. Antud on sisenurk, kuid robot pidi tegelikkuses keerama 180’ – 36’ = 144’

Lisaks tekitas probleeme funktsioon RotateMotorEx, mille keeramise järele tuli kirjutada Wait(10), kuid sellest on täpsemalt juttu pööramise arvutamise juhendis. Sisuliselt oli nende kolme ülesande lahendamisega poistel tegemist terveks tunniks (2h).


Arvuta välja ning kirjuta programm roboti liigutamiseks.

Roboti andmed.

·         Roboti ratta diameeter DR = ….   cm

·         Rataste vahe LR = ….  cm

Kirjuta alati välja valem arvutuskäigu kohta ja arvuta roboti liikumine.

Ülesanne 1.

Robot sõidab mööda joont edasi tagasi. Joone pikkus on 1m.

image

Esimese ülesande lahendus, numbrid sõltuvad konkreetsest BOT-st:

task main(){
repeat(2){                                     
RotateMotorEx(OUT_BC, 50, -1400, 0, TRUE, TRUE);
RotateMotorEx(OUT_BC, 17, 333, 100, TRUE, TRUE);
Wait(10);
} }

imageÜlesanne 2.

Robot sõidab ruudu, mille küljepikkus on 30cm. Nurk = 90’

 

Teise ülesande lahendus, kolmanda oma on analoogiline.

task main(){
repeat(4){
RotateMotorEx(OUT_BC, 50, -420, 0, TRUE, TRUE);
RotateMotorEx(OUT_BC, 17, 166, 100, TRUE, TRUE);
Wait(10);
}
}

image

Ülesanne 3.

Robot sõidab viisnurga, mille küljepikkus on 50cm. Nurk = 36’

 

 

 

.

1 tund: Roboti pööramine ja nurga arvutus

Tunni eesmärk on anda teadmine, kuidas on võimalik puhtalt matemaatika abil välja arvutada roboti edasiliikumiseks või keeramiseks vajalik nurk, mille peavad täitma mootorid.

Praktiliselt robotite katsetamiseni ei jõudnudki, terve tund kulus näidetele ja arusaamisele.

Sisuliselt said poisid teadmise kuidas välja arvutada kolme erinevat liikumist:

1. Roboti otseliikumine

2. Roboti pööramine nii, et üks ratas seisab

3. Roboti pööramine nii, et rattad pöörlevad erinevates suundades.

Kõik said praktilist tööd, mõõdeti joonlauaga üle rataste ümbermõõdud ning arvutasime nende järgi keskmise tõenäolise ümbermäädu ja diameetri.

Kogu tunni sisu sai kirja siia juhendisse: Juhend- Lego rattad ja matemaatika