Reaali Robootika.COM

NXT robotimaailm ja programmeerimine C-keeles

NXT ekraanile loodud multikad

Kolme põrsakese muinasjutt Lego Mindstorms NXT ekraanil, 2014 aasta NXT animatsiooni võidutöö.

Kolm põrsakest muinasjutt NXT ekraanil

 

Punamütsikese muinasjutt Lego Mindstorms NXT ekraanil, 2013 aasta NXT animatsiooni võidutöö.

Punamütsikese muinasjutt NXT ekraanil

 

Kuna ekraani pikslite arv on piiratud (64x100 pikslit) ning protsessorivõimsus samuti, on tegemist igati suurepärase saavutusega.

Mõlema puhul on kood kirjutatud programmiga BricxCC ning NXC programmeerimiskeeles (à 1000 rida koodi) ning pildid loodud programmiga nxtRICeditV2.

Koodi autor Rao ning animatsiooni pildid loodud Jaani, Timo ja Ramsese poolt.

6. 9 klass 2 õppetundi: Roboti teekonna kaardistamine

Tiigrihype_logo

See materjal on loodud Tiigrihüppe Sihtasutuse programmi ProgeTiiger raames.

Ülesande eesmärk

Ehitada ja programmeerida robot, mis suudab ekraanile kuvada oma läbitud teekonna.

Käesoleva ülesande lahendamine kestab 2 robootika tundi, kusjuures ühe tunni pikkuseks on arvestatud 2x45 min.

 

Ülesande lahendamiseks vajalik

Õpilased peavad olema hästi kursis matemaatikaga ja oskama arvutada täisnurkse kolmnurga külgede pikkuseid nii koosinuse kui ka siinuse abil.

Õpilane peab arvesse võtma ülesande lahendamisel järgmisi asjaolusid:

1)      Ratta läbimõõt

2)      Rataste teljevahe

3)      Peab oskama tuletada roboti pöördenurga, kui ülejäänud muutujad on teada

roboti pöördenurk

4)      Peab oskama arvutada kolmnurga lähiskaateti pikkust tervanurga ning hüpotenuusi abil

lähiskaatet

5)      Peab oskama arvutada vastaskaateti pikkust teravnurga ning hüpotenuusi abil

vastaskaatet

6)      Peab oskama aru saada NXT koordinaatteljestikust, mis on mõõtudega 100x64 pikselit

 

Ülesande lahendamise käik

Lahenduse koodi pole mõttekas õpilastele ette anda, tegemist on nii lihtsa koodiga, et selle mahakirjutamine võtaks aega 10 min, kuid tarkus jääks tulemata.

Ülesanne on jaotatud neljaks etapiks mille käigus õpilased jõuavad järk-järgult lahenduseni.

Etapid ja õppetundide jaotus:

1.      Tund

a.      Roboti pöördenurga leidmine rataste pöördenurga abil

b.      Kolmnurga lahendamine, lähis- ja vastaskaatetite leidmine (siinus, koosiinus)

2.      Tund

a.      Roboti pöördenurk ning kolmnurga lahendamine kokku pandud (suunamõõdik ekraanil)

b.      Lõplik ülesande lahendus
 

Roboti pöördenurga leidmine

Selle etapi eesmärk on lasta õpilastel jõuda valemini, mille abil saab teisendada roboti rattakeeramise kraadid roboti enda pööramise kraadideks. See on vajalik, kuna ülesande lahendus baseerub omadusel, et meil on teada igal hetkel rataste omavahelise pöördenurga vahe.

Roboti pööramise illustreeriv näide

Kõige lihtsam on tuua näide seisva robotiga, kus meil on teada et üks ratas seisab ja teine ratas pöörab näiteks 360 kraadi, ehk teeb ühe tiiru. Sellisel juhul saame roboti pöördenurgaks 90 kraadi, eeldusel et rataste teljevahe on 110 mm ja diameeter 56 mm.

 

Õpilased on varasemalt lahendanud ülesandeid, mille käigus nad on pidanud vastupidist ülesannet lahendama, st. et on teada mitu kraadi peab robot pöörama ning õpilased peavad välja arvutama mitu kraadi peab ratas pöörama, et saavutada roboti õige positsioon.

Õpilased on siiani kasutanud valemit:

mootori pöördenurk

 

Käesoleva ülesande käigus peavad õpilased algatuseks avaldama valemist RobotiPöördenurga.

Kui neil on valem õigesti avaldatud, kirjutavad nad programmi, mille abil saab kuvada roboti pöördenurga ekraanile numbriliselt. See annab neile kohest tagasisidet, kas nad on õigesti avaldanud RobotiPöördenurga ning seda ka õigesti rakendada oskavad.

Antud etapi lahendus on alljärgnev programmikood.

//käesolev programm kuvab ekraanil roboti pöördenurga

//roboti mootorite pöörete järgi arvutatakse välja roboti pöördenurk

//ja kuvatakse see ekraanil

//testimiseks tuleb robotit käsitsi liigutada.

 

//Roboti pööramise nurk = (Diameeter * x kraadi)/(Rataste vahe * 2)

 

task main()

{

int RattaDiameeter = 56;  //ratta diameeter

int RatasteVahe = 110;   //ratastevaheline kaugus, teljevahe

int suund;             //mootori pöörete vahe

float BMootor;         //mootori B pöörded

float CMootor;         //mootori C pöörded

int RobotiNurk;        //roboti pööramise nurk

 

while (1)

        {

        //mootorite pöörded loetakse muutujatesse

  BMootor = MotorRotationCount(OUT_B);

  CMootor = MotorRotationCount(OUT_C);

 

        //lahutatakse ühe mootori pöördenurgast teise mootori pöördenurk

        //tulemuseks on ratastevaheline erinevaus pöördenurgas

          suund = BMootor - CMootor;

         

        //siin arvutatakse välja roboti pöördenurk, võttes arvesse rataste läbimõõtu

         //rataste teljevahe ja ratastevaheline kraadide erinevus

          RobotiNurk = (RattaDiameeter * suund)/(RatasteVahe * 2); 

         

               ClearLine(LCD_LINE1);

         

          //ekraani ülemisel real kuvatakse roboti nurk

          NumOut(0, LCD_LINE1, RobotiNurk);

          TextOut(30, LCD_LINE1, "kraadi");

        }

}


 

Kolmnurga lahendamine

Selle etapi eesmärk on lasta õpilastel lahendada kolmnurk, mille kohta on teada üks teravnurk ning hüpotenuus. Leida on tarvis lähiskaateti ja vastaskaateti pikkused.

kolmnurga lahendamineLahendatud kolmnurk tuleb kuvada ekraanil ning peab olema muutumises koos nurga muutmisega. Nurga muutmine teostada noolenuppudega parem-vasak, sammuga 5 kraadi.

Antud etapp on olulise tähtsusega, kuna selle abil jõuavad õpilased ülesande lõpuks äratundmisele, kuidas aitab kolmnurga lahendamine neid robootika liikumisülesande juures teekonna kuvamisel. Neil peaks tekkima side matemaatika õppimise vajaduse ja reaalse elu vahel.

Kolmnurga lähis- ja vastaskaateti pikkuste arvutamise valemid. Vaata joonist.

lähiskaateti arvutamine

vastaskaateti arvutamine

Kolnurk

Kui nad on kolmnurga lahendamisest aru saanud, tuleb selle kohta programm kirjutada. Programm võiks välja näha selline, et hüpotenuus on ette antud (ülesande lahenduses on see samuti mittemuutuv suurus), kuid nurka peab saama muuta ja tulemusena kuvatakse ekraanil kolmnurga lähis- ja vastaskaatetite pikkused.

Kolmnurga lahendamise programmikood.

//käesolev programm lahendab kolmnurga

//ja joonistab selle ekraanile

 

//lähiskaatet = teravnurga koosinu * hüpotenuus

//vastaskaatet = teravnurga siinus * hüpotenuus

 

task main()

{

 

int RobotiNurk=45; //roboti pööramise nurk

int Lkaatet;                   //lähiskaatet

int Vkaatet;                   //vastaskaatet

int Hypotenuus=80; //hüpotenuus

 

while (1)

        {

        ClearScreen();

        TextOut(0, LCD_LINE1, "Sisesta nurk");

        NumOut(85, LCD_LINE1, RobotiNurk);

        if(ButtonPressed(BTNCENTER, FALSE))

               {

               while(ButtonPressed(BTNCENTER, FALSE));

               break;

               }

        if(ButtonPressed(BTNRIGHT, FALSE))

               RobotiNurk += 5;

        while(ButtonPressed(BTNRIGHT, FALSE));

        if(ButtonPressed(BTNLEFT, FALSE))

               RobotiNurk -= 5;

        while(ButtonPressed(BTNLEFT, FALSE));

     

          //arvutatakse välja lähiskaateti pikkus

          Lkaatet = cosd(RobotiNurk) * Hypotenuus;

          //arvutatakse välja vastaskaateti pikkus

          Vkaatet = sind(RobotiNurk) * Hypotenuus;

                 

          TextOut(0, LCD_LINE2, "Lkaat");

          TextOut(50, LCD_LINE2, "Vkaat");

               NumOut(35,LCD_LINE2, Lkaatet);

               NumOut(85,LCD_LINE2, Vkaatet);

            

               //ekraanile joonistatakse lähiskaatet

               LineOut(1, 1, Lkaatet, 1);

               //ekraanile joonistatakse vastaskaatet

               LineOut(Lkaatet, 1, Lkaatet, Vkaatet);

               //ekraanile joonistatakse hüpotenuus

               LineOut(1, 1, Lkaatet, Vkaatet);

               Wait(100);

        }

}


 

Roboti liikumise suunanäidik

Roboti suunanäidik ekraanilSelle etapi eesmärk on panna kokku esimene ja teine etapp, ehk siis ühendada omavahel roboti pöördenurga arvutamine ja kolmnurga lahendamine.

 Etapi lõpptulemusena valmib NXT ekraanile suunanäidik, mis näitab suunda kuhu poole robot sõidab. Seda omadust kasutame lõpplahenduses roboti teekonna kuvamiseks.

Roboti suunanäitamise programmi kood.

//käesolev programm kuvab ekraanile roboti liikumise suuna
 
//Roboti pööramise nurk = (Diameeter * x kraadi)/(Rataste vahe * 2)
//lähiskaatet = teravnurga koosinu * hüpotenuus
//vastaskaatet = teravnurga siinus * hüpotenuus
 
task main()
{
int RattaDiameeter = 56; //ratta diameeter
int RatasteVahe = 110;   //ratastevaheline kaugus, teljevahe
int suund;               //mootori pöörete vahe
float BMootor;           //mootori B pöörded
float CMootor;           //mootori C pöörded
int RobotiNurk;          //roboti pööramise nurk
int Lkaatet;             //lähiskaatet
int Vkaatet;             //vastaskaatet
int Hypotenuus=60;       //hüpotenuus
 
while (1)
        {
        //mootorite pöörded loetakse muutujatesse
  BMootor = MotorRotationCount(OUT_B);
  CMootor = MotorRotationCount(OUT_C);
          suund = BMootor - CMootor; 
          
          //siin arvutatakse välja roboti pöördenurk, võttes arvesse rataste läbimõõtu
          //rataste teljevahe ja ratastevaheline kraadide erinevus
          RobotiNurk = (RattaDiameeter * suund)/(RatasteVahe * 2);
          
          //arvutatakse välja lähiskaateti pikkus
          Lkaatet = cosd(RobotiNurk) * Hypotenuus;
          //arvutatakse välja vastaskaateti pikkus
          Vkaatet = sind(RobotiNurk) * Hypotenuus;
          
          ClearScreen();
          //ekraanile joonistatakse roboti suunanäidik
          LineOut(50, 25, Vkaatet+50, Lkaatet+25);
                  
          //ekraani ülemisel real kuvatakse roboti nurk ning lähis- ja vastaskaatet
          NumOut(0, LCD_LINE1, RobotiNurk);
          NumOut(50,LCD_LINE1, Lkaatet);
               NumOut(80,LCD_LINE1, Vkaatet);   
        Wait(100);
        }
}

 

Lõplik ülesande lahendus

Alljärgnevalt roboti teekonna kaardistamise programmi kommenteeritud kood.

Sisuliselt tekib ekraanile joon hästi paljude pisikeste kolmnurkade lahendamise tulemusena.

Siin on lisandunud eelkirjeldatud etappidega võrreldes kaks olulist võtet.Ülesande lahendus roboti ekraanil

1.      Kaardistamise sageduseks kasutatakse rataste pöörlemist. Kui ükskõik kumb ratas teeb 360 kraadi, ehk ühe täisringi, arvutatakse ja salvestatakse selle hetke roboti olukord ekraanil. See võte on vajalik eelkõige seetõttu, et robot ei suuda ühe rattapöörde jooksul kuigi palju viga teha ning see on piisav et saada küllaltki täpne joon ekraanil. Alati võib proovida ise selle täpsust suurendada, vähendades roboti rataste kraadide arvu mille jooksul toimub kaardistamine.

2.      Kolmnurga hüpotenuusi seadmine. See võimaldab muuta ekraanile kujutatava joone pikkust ja täpsust. Kui hüpotenuus panna liiga lühike, näiteks 2 punkti, siis on kaatetite tulemuseks ainult 1 või 0 ning joone täpsus kannatab olulisel määral. Kui aga hüpotenuus panna liiga pikk, joonistatakse ekraanile korraga väga pikk joon, mis on samuti mõttetu ekraani väikse pinna tõttu.

Ekraanipilt antud programmi tulemusest NXT ekraanil.

//käesolev programm on roboti teekonna kaardistaja

//roboti mootorite pöörete järgi arvutatakse välja roboti teekonna kaart

//ja kuvatakse see ekraanil

 

//task soida on lihtsalt roboti sõitmise ja kaardistamise testimiseks

//task sõida abil sõidab robot südame kujutise ning joonistab selle ka ekraanile

task soida()

{

while(1)

        {

        RotateMotorEx(OUT_BC, 40, 360, 0, TRUE, TRUE);

        RotateMotorEx(OUT_BC, 40, 360, 15, TRUE, TRUE);

        RotateMotorEx(OUT_BC, 40, 1300, 0, TRUE, TRUE);

        RotateMotorEx(OUT_BC, 40, 1600, -20, TRUE, TRUE);

        RotateMotorEx(OUT_BC, 40, 370, 100, TRUE, TRUE);

        RotateMotorEx(OUT_BC, 40, 1400, -25, TRUE, TRUE);

        RotateMotorEx(OUT_BC, 40, 1300, 0, TRUE, TRUE);

       

        //sõidu lõppedes oodatakse kuni nuppu vajutatakse

        while(!ButtonPressed(BTNCENTER, FALSE));

        }

}

 

//siin paar abivalemit, mille alusel arvutatakse roboti teekonna kaart

//Roboti pööramise nurk = (Diameeter * x kraadi)/(Rataste vahe * 2)

//lähiskaatet = teravnurga koosinus * hüpotenuus

//vastaskaatet = teravnurga siinus * hüpotenuus

 

task main()

{

int RattaDiameeter = 56;       //ratta diameeter

int RatasteVahe = 110;         //ratastevaheline kaugus, teljevahe

 

int suund;                     //mootori pöörete vahe

float BMootor;                 //mootori B pöörded

float CMootor;                 //mootori C pöörded

float LastBMootor;    

float LastCMootor;

int RobotiNurk;                //roboti pööramise nurk

int Lkaatet=0;                 //lähiskaatet

int Vkaatet=50;                //vastaskaatet

int xLkaatet;

int xVkaatet;

int Hypotenuus=6;              //hüpotenuus, selle numbri ja nurga põhjal arvutatakse välja Lähis- ja Vastaskaatet

 

StartTask(soida);

 

while (1)

        {

        //mootorite pöörded loetakse muutujatesse

  BMootor = MotorRotationCount(OUT_B);

  CMootor = MotorRotationCount(OUT_C);

 

 //antud if lause tingimus on võte, millega tagatakse kaardistamise sagedus

 //1 rattapööre = 1 joonistus ekraanil

 //seega antud if lause käivitub alles siis, kui B või C Mootor on teinud 360 kraadi

 

  if (BMootor > LastBMootor+360 || CMootor > LastCMootor + 360)

               { 

          LastBMootor = BMootor;

          LastCMootor = CMootor;

          suund = BMootor - CMootor;

         

          //siin arvutatakse välja roboti pöördenurk, võttes arvesse rataste läbimõõtu

          //rataste teljevahe ja ratastevaheline kraadide erinevus

          RobotiNurk = (RattaDiameeter * suund)/(RatasteVahe * 2);

         

          //eelmise tsükli käigus saadud lähis- ja vastaskaatet salvestatakse ajutisse muutujasse

          //selle alusel määratakse ekraanil joone alguspunkt

          xLkaatet = Lkaatet;

          xVkaatet = Vkaatet;

         

          //arvutatakse välja lähiskaateti pikkus

          Lkaatet = cosd(RobotiNurk) * Hypotenuus;

          //arvutatakse välja vastaskaateti pikkus

          Vkaatet = sind(RobotiNurk) * Hypotenuus;

         

          //välja arvutatud lähis- ja vastaskaatetile liidetakse juurde nende

          //eelmise tsükli tulemus, et tagada joone jätkumine samast punktist

          //kus eelmise tsükli aja lõpetati

          Vkaatet += xVkaatet;

          Lkaatet += xLkaatet;

         

          //kui kaatetid omandavad väärtuse mis on ekraani piirdeist väljas

          //siis korrigeeritakse numbreid selliselt, et joon jätkuks ekraani vastasservast

          if (Vkaatet>100)

                       {

                       Vkaatet = 1;

                       xVkaatet = 1;

                       }

          if (Vkaatet<1)

                       {

                       Vkaatet = 100;

                       xVkaatet = 100;

                       }

          if (Lkaatet>48)

                       {

                       Lkaatet = 1;

                       xLkaatet = 1;

                       }

          if (Lkaatet<1)

                       {

                       Lkaatet = 48;

                       xLkaatet = 48;

                       }

         

          //ekraanile joonistatakse roboti teekonna joon

          LineOut(xVkaatet, xLkaatet, Vkaatet, Lkaatet);

         

               ClearLine(LCD_LINE1);

         

          //ekraani ülemisel real kuvatakse roboti nurk ning lähis- ja vastaskaatet

          NumOut(0, LCD_LINE1, RobotiNurk);

          NumOut(50,LCD_LINE1, Lkaatet);

               NumOut(80,LCD_LINE1, Vkaatet);  

               }

        }

}

Robomiku võistluse roboti programm

Robomiku võistlusel saavutasime 3. koha.

Koos reavahede ja kommentaaridega tuli kokku ligi 200 rida koodi.

Kuna palusin poistel kommentaarid juurde panna siis peaks olema arusaajale inimesele täitsa loetav kood. Kuna kasutasime mitte kõige uuemat versiooni NXC-st siis seetõttu on LineLeader anduriga suhtlev library siia eraldi included. Uuemasse softi (15. märts 2011) on juba kõik need uute andurite driverid sisse kirjutatud.

/*
autor: Ramses Sepp 20/03/11
robomiku 2011 kevad karupäästmise programm
selles programmis:
* sõidab robot mööda joont LineLeader anduri abil
* leiab rohelisi ja hõbedasi teibiribasid mis on kannatanud ja annab nendest märku
* tuvastab IR abil karu ja annab temast märku
* robotil on haarad mille abil ta kannab karu majast välja
*/

#include "LL-Lib.nxc"
#define IR_TOUCH S1
#define I2C_SENSOR S2
#define COLOR S3
#define LIGHT S4
#define LL_ADDR 0x04

long endTime;
bool haaradLahti = FALSE;
bool mulOnKaru = FALSE;
task DriveOnTheLine();
task KeeraRobotit180();
task KeeraRobotit();

//funktsioon ArvutaKiirus tagastab väärtuse, mis jääb -100 ... 100 vahele
int ArvutaKiirus(int x, int min, int max)
{
if (x < min)
   return min;
else if (x > max)
   return max;
else
   return x;
}

//alamprogramm HaaradLahti avab haarad ning seejärel robot sõidab edasi
task HaaradLahti()
{
RotateMotorEx(OUT_A, -100, 6840, 0, FALSE, TRUE);
haaradLahti = TRUE;
//endTime muutujat kasutatakse selleks 
//et robot sõidaks peale haarade lahti tegemist 2 sekundit edasi
endTime = CurrentTick() + 1000;
ExitTo(DriveOnTheLine);
}

//alamprogramm HaaradKinni sulgeb haarad ning siis käivitub roboti pööramine
task HaaradKinni()
{
RotateMotorEx(OUT_A, 100, 6840, 0, FALSE, TRUE);
mulOnKaru = TRUE;
haaradLahti = FALSE;
ExitTo(KeeraRobotit180);
}

//alamprogramm KeeraRobotit180 keerab robotit 180 kraadi 
//kuni ta on joone keskel
task KeeraRobotit180()
{
int onJoonel;
//hakkame mootoreid keerama, et robot liiguks joonelt ära
RotateMotor(OUT_BC, 40, 360);
OnFwdReg(OUT_B, 40, OUT_REGMODE_SPEED);
OnFwdReg(OUT_C, -40, OUT_REGMODE_SPEED);
Wait(1000);
while (TRUE)
   {
   OnFwdReg(OUT_B, 40, OUT_REGMODE_SPEED);
   OnFwdReg(OUT_C, -40, OUT_REGMODE_SPEED);
   //loeme LL andurist kas robot on joone keskel
   onJoonel = LL_Read (I2C_SENSOR, LL_ADDR, LL_READ_AVERAGE);
   //kui robot on joone keskel siis käivitub uuesti põhiprogramm
   if (onJoonel < 50 && onJoonel > 40)
      {
      Off(OUT_BC);
      ExitTo(DriveOnTheLine);
      }
   }
}

//alamprogramm KeeraRobotit keerab robotit 180 kraadi
task KeeraRobotit()
{
RotateMotor(OUT_BC, 50, 360);
RotateMotorEx(OUT_BC, 50, 1100, 100, TRUE, TRUE);
Off(OUT_BC);
ExitTo(DriveOnTheLine);
}

//alamprogramm DriveOnTheLine on joone jälgimise, kannatanute tuvastamise
//ja karu kandmise programm
task DriveOnTheLine()
{
int steering;
int b_speed;
int c_speed;
int rohelisedMehed;
int hobedasedMehed;
bool naebKaru;
int karuKaugus;
bool onSoitnud2Sek = FALSE;
int baasKiirus;

//lõpmatu tsükkel mille sees toimub enamus programmist
while (TRUE)
   {
   rohelisedMehed = Sensor(COLOR);
   hobedasedMehed = Sensor(LIGHT);
   naebKaru = !Sensor(IR_TOUCH);
   karuKaugus = SensorUS(I2C_SENSOR);
   
   //kui värviandur näeb rohelist või hõbedast meest põrandal
   //annab heliga märku
   if (rohelisedMehed == 3 || hobedasedMehed > 70)
      {
      PlaySound(SOUND_DOUBLE_BEEP);
      }
      
   //kui IR_sensor näeb karu, haarad on kinni ja mul ei ole karu 
   //siis annab sellest heliga märku:P
   if (naebKaru && !haaradLahti && !mulOnKaru)
      {
      Off(OUT_BC);
      PlaySound(SOUND_UP);
      ExitTo(HaaradLahti);
      }
   
   //kui haarad on lahti, ei näe karu ja on sõitnud 2 sekundit 
   //siis paneb haarad kinni
   if (haaradLahti && !naebKaru && onSoitnud2Sek)
      {
      Off(OUT_BC);
      ExitTo(HaaradKinni);
      }
   
   //kui robot on haarad lahti teinud siis ta sõidab 2 sekundit joonel edasi
   //enne kui hakkab kontrollima kas on tarvis haarasid kinni panna
   if (haaradLahti)
      {
      if (endTime < CurrentTick())
         {
         onSoitnud2Sek = TRUE;
         }
      }
   
   //steering muutujasse kirjutatakse LineLeaderist steering väärtus
   //mida kasutatakse mootorite juhtimisel
   steering = LL_ReadSteering(I2C_SENSOR, LL_ADDR);
   
   //see If lause on vajalik, et steering oleks vahemikus -127 ... 127
   if(steering > 127)
      {
      steering = steering - 256;
      }

   // kui robot sõidab üles siis on baasKiirus 40, allaminekul 30
   if (mulOnKaru)
      {
      baasKiirus = 30;
      steering = steering / 3;
      }
   else
      {
      baasKiirus = 40;
      }
   /*
   mootoritele õige kiiruse andmine
   igal juhul ei saa olla mootori kiirus suurem kui 100
   igal juhul ei saa olla mootori kiirus väiksem kui -100
   */
   b_speed = ArvutaKiirus(baasKiirus + steering, -100, 100);
   c_speed = ArvutaKiirus(baasKiirus - steering, -100, 100);
   
   //kuna mootorid on tagurpidi siis peame muutma väärtused vastasmärgiliseks
   b_speed = 0 - b_speed;
   c_speed = 0 - c_speed;
   
   OnFwdReg(OUT_B, b_speed, OUT_REGMODE_SPEED);
   OnFwdReg(OUT_C, c_speed, OUT_REGMODE_SPEED);
   }
}

task main()
{
SetSensorLight(LIGHT);
SetSensorColorFull(COLOR);
SetSensorLowspeed(I2C_SENSOR);
SetSensorTouch(IR_TOUCH);
Wait(SEC_5);

LL_Write_KP(I2C_SENSOR, LL_ADDR, 40, 32); //katsetada 0...100 //alla:40
LL_Write_KI(I2C_SENSOR, LL_ADDR, 0, 32);  //jääb nulliks
LL_Write_KD(I2C_SENSOR, LL_ADDR, 5, 32);  //katsetada 0..20 //alla:5

StartTask(DriveOnTheLine);
}

8. tund: mäng NXT ekraanile 1. osa

Esimene pool tunnist kulus Robomiku kevadise võistluse tutvustamiseks mis toimub Tartus 22. märtsil.

Selgus, et nii Ramsesel kui ka Jaanil oli juba ette valmistatud oma nägemus Robomiku võistlusrobotist.

Teine pool tunnist aga kulutasime selle peale, et alustada ühe mängu programmeerimisega NXT ekraanile. Ma viibisin nädal aega Redmondis ning seal oli aega NXT-le üks mäng ajaviiteks välja mõelda.

Mäng

Ekraanil põrkab pall üles-alla ja paremale-vasakule ning NXT ekraani alumises servas on väravavaht mida saab nooltega paremale-vasakule liigutada.

Väravavaht peab alati palli põrke all olema, et pall üles tagasi põrkaks. Kui pall kukub ekraani alt välja on mäng läbi. Lisaks saab valida 3-e erineva raskusastme vahel ning need mõjutavad palli liikumise kiirust ja punktiarvestust. Raskema mängu korral saab rohkem punkte.


Jagasime ülesande etappideks ning selle tunni aeg kulus õppimisele, kuidas liigutada väravat nooleklahvide abil paremale vasakule.

/*
Leivo Sepp, 23.02.2011
See on mäng, kus tuleb nooltega liigutada väravat paremale/vasakule nii,
et ekraanil põrkav pall ei kukuks mängualast välja vaid põrkaks vastu väravat.
*/

//värava muutuja koos palli esialgse asukohaga
int Joon = 50;

//joone liigutamise alamprogramm liigutab parema-vasaku klahvi abil 
//väravat ekraani allservas edasi-tagasi
task JooneLiigutamine()
{
while (TRUE)
   {
   if (ButtonPressed(BTNRIGHT, TRUE))
      {
      Joon += 4;
      }
   else if (ButtonPressed(BTNLEFT, TRUE))
      {
      Joon -= 4;
      }
   ClearLine(LCD_LINE8);
   TextOut(Joon, LCD_LINE8, "=");
   
   //wait on vajalik, et värav liiga kiiresti ei liiguks
   Wait(150);
   }
}

task main()
{
//siit kutsutakse välja ja käivitatakse alamprogramm JooneLiigutamine
Precedes(JooneLiigutamine);
}

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

Juhend: HiTechnic prototüüpimislaud

HiTechnic NXT andurite tootjana on teinud ka ühe väga laheda asja – nimelt prototüüpimislaud.

http://www.hitechnic.com/cgi-bin/commerce.cgi?preadd=action&key=NPK1056 

Sellel on 5 analoogsisendit (1024 astet pingega 0..3,3 V) ning 6 digitaalset sisendit/väljundit, mille abil saab kõikvõimalikke keerukaid asju teha. Alljärgnevalt kaks lihtsat näidisprogrammi: a) jooksvad tuled ja b) KITT tuled.

Jooksvad tuled ja KITT tuled HiTechnic Prototype Board abil

Jooksvad tuled

Käesoleva juhendi käigus vaatame ainult prototüüpimislaua digitaalsete väljunditesse kirjutamise osa.

Prototüüpimislaual on 6 digitaalset väljundit B0…B5. Neid saab kasutada nii sisendi kui ka väljundi režiimis. Register “Digital Control” määrab ära millised pordid on sisendid ja millised väljundid. Näiteks “Digital Control” registris number 63 (kahendsüsteemis 111111) muudab kõik väljunditeks.

Digital Out registrisse antud number määrab ära milline väljundportidest on aktiveeritud 1-ga. 1 tähendab siis 3,3V ja 12mA. Näiteks Digital Out numbriga 63 (ehk 0x3F) tähendab, et kõik valgusdioodid põlevad.

Alljärgnev programm suunab DigitalOut registrisse järjest numbrid 1,2, 4, 8, 16, 32 (0x01, 0x02, 0x04, 0x08, 0x10, 0x20).

Alljärgnevalt näidisprogramm kuidas teha prototüüpimislaua abil jooksvad tuled NXC programmeerimiskeeles.

/*
Leivo Sepp 12.02.2011
See programm kasutab HiTechnicu prototüübi plaati
Paneb plaadi taha ühendatud valgusdioodid järjekorras põlema
Ehk teisisõnu "jooksvad tuled"
*/


#define PROTO_PORT S1

int count;
int light = 0x01;
byte cmndbuf[];
byte respbuf[];                 

/* protoboard I/O map
   42,43 - A0 input
   44,45 - A1 input
   46,47 - A2 input
   48,49 - A3 input
   4A,4B - A4 input
   4C    - B inputs
   4D    - B outputs
   4E    - B controls
*/

//see alamprogramm teostab reaalse kirjutamise protolaua registrisse
//aktiveerides kindla pordi (B0..B5), mille taga peab süttima valgusdiood
void writedata(int outputdata)
  {
  //loo 3 väärtusega massiiv
  ArrayInit(cmndbuf, 0, 3);
  //protolaua aadress
  cmndbuf[0] = 0x02;
  //register kuhu kirjutatakse 4D on output
  cmndbuf[1] = 0x4D;
  //output registrisse kirjutatakse number mis aktiveerib kindla väljundi
  cmndbuf[2] = outputdata;
  count=0;
	//Käivitada eelnevate andmete põhjal protolauda kirjutamine
  I2CBytes(PROTO_PORT, cmndbuf, count, respbuf);  
  }

task main()
  {
  SetSensorLowspeed(PROTO_PORT); 
  Wait(100);

  //massiivi initsialiseerimine, 3 väärtust
  ArrayInit(cmndbuf, 0, 3);
  //esimene väärtus massiivis, prototüüpimislaua aadress
  cmndbuf[0] = 0x02;
  //teine väärtus massiivis, registri Control valimine (sisendid/väljundid)
  cmndbuf[1] = 0x4E;
  //kirjutada registrisse Control number 63 ehk 111111
  //st. et kõik B0..B5 on väljundid
  cmndbuf[2] = 0x3F;
  //mitu baiti peab I2C protolaud tagastama, meie puhul 0
  count=0;
  //Käivitada eelnevate andmete põhjal protolauda kirjutamine
  I2CBytes(PROTO_PORT, cmndbuf, count, respbuf);
  Wait(100);

  while (TRUE)
    {
    //kutsutakse välja alamprogramm, kuhu kirjutatakse number
    //numbrid on 1, 2, 4, 8, 16, 32
    writedata(light);
     light = light * 0x02;
     if (light == 0x40)
        light = 0x01;
    Wait(100);
    }
}

KITT tuled.

Ja teine programm, mis paneb tuled edasi-tagasi liikuma, ehk nagu KITT
/*
Leivo Sepp 12.02.2011
See programm kasutab HiTechnicu prototüübi plaati
Paneb plaadi taha ühendatud valgusdioodid edasi/tagasi põlema
Ehk teisisõnu "KITT tuled"
*/


#define PROTO_PORT S1

int count;
int light = 0x01;
byte cmndbuf[];
byte respbuf[]; 
bool kitt=TRUE;                

/* protoboard I/O map
   42,43 - A0 input
   44,45 - A1 input
   46,47 - A2 input
   48,49 - A3 input
   4A,4B - A4 input
   4C    - B inputs
   4D    - B outputs
   4E    - B controls
*/

//see alamprogramm teostab reaalse kirjutamise protolaua registrisse
//aktiveerides kindla pordi (B0..B5), mille taga peab süttima valgusdiood
void writedata(int outputdata)
  {
  //loo 3 väärtusega massiiv
  ArrayInit(cmndbuf, 0, 3);
  //protolaua aadress
  cmndbuf[0] = 0x02;
  //register kuhu kirjutatakse 4D on output
  cmndbuf[1] = 0x4D;
  //output registrisse kirjutatakse number mis aktiveerib kindla väljundi
  cmndbuf[2] = outputdata;
  count=0;
	//Käivitada eelnevate andmete põhjal protolauda kirjutamine
  I2CBytes(PROTO_PORT, cmndbuf, count, respbuf);  
  }

task main()
  {
  SetSensorLowspeed(PROTO_PORT); 
  Wait(100);

	//massiivi initsialiseerimine, 3 väärtust
  ArrayInit(cmndbuf, 0, 3);
  //esimene väärtus massiivis, prototüüpimislaua aadress
  cmndbuf[0] = 0x02;
  //teine väärtus massiivis, registri Control valimine (sisendid/väljundid)
  cmndbuf[1] = 0x4E;
  //kirjutada registrisse Control number 63 ehk 111111
  //st. et kõik B0..B5 on väljundid
  cmndbuf[2] = 0x3F;
  //mitu baiti peab I2C protolaud tagastama, meie puhul 0
  count=0;
  //Käivitada eelnevate andmete põhjal protolauda kirjutamine
  I2CBytes(PROTO_PORT, cmndbuf, count, respbuf);
  Wait(100);

    while (TRUE)
    {
    //kutsutakse välja alamprogramm, kuhu kirjutatakse number
    //numbrid on 1, 2, 4, 8, 16, 32
    writedata(light);
    if (kitt){
    	 //numbrid jooksevad väiksemast suuremaks
       light = light * 0x02;
       if (light == 0x20){
          kitt = FALSE;
          }
       }
    else {
    	  //numbrid jooksevad suuremast väiksemaks
        light = light / 0x02;
        if (light == 0x01)
           kitt = TRUE;
        }
    Wait(200);
    }
}

Kahe seina vahel roniv robot

Ma tegin ühe roboti mis ronib kahe seina vahel üles-alla. Seda ehitades mul polnud mitte mingit probleemi. Kuid kui ma jõudsin programmeerimise juurde siis tekkis mul küll mõningaid probleeme. Kuid enne räägin milline oli mu roboti programmeerimise järjekord. Kõigepealt kui robot oli valmis siis polnud mingit muret esimese kolmandikuga alustada. Esimeses kolmandikus ma panin ta lihtsalt üles sõitma seni kuni ta näeb lähedamal kui 10 cm lage. Sellega erilisi probleeme ei tekkinud kuna see oli üpriski lihtne programm. Teise kolmandiku programmist ma panin roboti sõitma küll pikema programmiga kuid mõne algaja jaoks ka kergem ning seekord sõitis robot lõpmatuseni üles-alla. Minu viimane kolmandik programmist sõitis robot ka muidugi lõpmatusein üles alla kuid nüüd on see lühem ja kergem kui eelmine kord.

Mu isa tegi sellest ka video:

Kahe seina vahel ronija robot

Programm näeb välja selline:

/*
Autor:Ramses Sepp 09.02.2011
See programm võimaldab robotil sõita kahe seina vahelt üles
*/

//See alamprogramm juhib roboti mootoreid üles-alla sõidul
void Liikumine(bool Yles, int KiirusSeinal){
	//Järgnev if lause peatab ühe või teise mootori kui robot on balansist väljas
	//Siin if-i lauses korrgeerib balanssi robot siis kui ta näeb oranþi
	if (Sensor(S2) > 50){
		//Need kaks järgmist rida suunavad kumb mootor peatub 
		//siis kui läheb üles-alla ja näeb oranþi
		if (Yles) Off(OUT_B);
		else Off(OUT_C);
		until(Sensor(S2) < 50);
		}
	//Siin else if-i lauses korrigeerib balanssi robot siis kui ta näeb musta
	else if(Sensor(S2) < 40){
		//Need kaks järgmist rida suunavad kumb mootor peatub 
		//siis kui läheb üles-alla ja näeb musta
		if (Yles) Off(OUT_C);
		else Off(OUT_B);
		until(Sensor(S2) > 40);
		}
	else{
		//Muidu sõidab robot lihtsalt kahe seina vahelt alla
		OnRevReg(OUT_BC, KiirusSeinal, OUT_REGMODE_SPEED);
		OnRev(OUT_A, 20);
		}
}
task main(){
SetSensorLowspeed(S4);
SetSensorLight(S2);
SetSensorLowspeed(S3);
bool TOP;
//Järgnev mootori programmi osa laiendab mõlemad mootorid seinte vastu
OnFwd(OUT_A, -75);
Wait(1000);
while (TRUE){
	//Siin vaatab kas lagi on lähedamal kui 10 cm
	if(SensorUS(S4) < 10){
		Off(OUT_BC);
		TOP = TRUE;
		}
	//Siin vaatab kas põrand on lähedamal kui 10 cm
	if(SensorUS(S3) < 10){
		Off(OUT_BC);
		TOP = FALSE;
		}
	//See pool paneb roboti alla sõitma
	if(TOP){
		Liikumine(FALSE, -15);
	}
	//See pool paneb roboti üles sõitma
	else{
		Liikumine(TRUE, 30);
	}
}
}

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);
   }
}

Bricx Command Center häälestamine

BricxCC on NXT programmeerimise vahend, mille abil meie oma põhilise osa koodist kirjutamine.

Tegemist on tasuta tarkvaraga, mille saab alla laadida siit: http://bricxcc.sourceforge.net/

Järgnevalt leidsin hulga asju mida annab selles keskkonnas vaikimisi häälestuse suhtes oluliselt kasutajasõbralikumaks muuta.

Alljärgnevate ekraanipiltide järgi häälestades saad enda BricxCC keskkonna mugavamaks.

Edit – Preferences – Editor vaheleht

Bricx Preferences Editor seaded

Paremal pool ülalt alla:

Automatically indent lines (ON) – kui kirjutad koodirida mis algas mitte rea algusest vaid eendiga, siis enteriga järgmisele reale minnes satud täpselt samale tasemele eelneva reaga. Väga kasutajasõbralik ning normaalne käitumine.

Alt sets column selection (OFF) – Selle sisselülitamisel saab Alt klahvi all vajutades teha hiirega selectioni üle mitme rea nii et veeru osa jääb paigale. Üsna segane ja mõttetu asi.

Move cursor on right click (ON) – kui teed koodis mõnes kohas hiirega paremklõpsu siis viiakse automaatselt ka hiirekursor sellesse kohta. Põhimõtteliselt on see normaalne asjade toimimise viis arvutis.

Scroll Past EOL (OFF) – sisselülitamisel liigub kursor lõputult paremale kui vajutad noole paremale. Täiesti mõttetu funktsionaalsus – lülitasin välja. Välja lülitaud asendis viib nool paremale kursori rea lõppu jõudes järgmise rea algusesse. See on normaalne ja ootuspärane käitumine.

HalfPage Scroll (OFF) – Kui see on sisse lülitatud siis PgUp või PgDn nuppe vajutades liigub ekraan üles-alla ainult poole kaupa. Ebamugav funktsionaalsus, lülitasin välja, mulle meeldib et kogu leht liigub korraga üles-alla.

Drag and drop editing (ON) – see on ju elementaarne, et saan koodijupi hiirega kinni võtta ja teise kohta lohistada, muidugi lülita sisse.

Quick tab (OFF) – jälle üks mõttetu asi, lülita välja. Sisselülitamise korral liigub kursor TAB klahvi vajutades eelmise rea esimese tühikuni jne. Täiesti ebakasutajasõbralik funktsioon.

Keep trailing blanks (ON) – vajalik sisse lülitada, et TAB klahv korralikult töötaks.

Enhanced home key (ON) – sisselülitamise korral viiakse kursor Home nupule vajutuse korral esimese täheni antud real. Lülitasin välja, kuna ma olen harjunud et kursor viiakse konkreetselt rea algusesse kui vajutan „home“ nuppu.

With grouped undo (OFF) – ei saanudki aru mis undo’d see grupeerib. Lülitasin välja et ei hakkaks mingit segadust kogemata tekitama.

Use Tab to indent (ON) –See on väga oluline asi sisse lülitada. Selle abil saad tervet koodi blokki korraga paremale nihutada või siis Shift-Tab abil vasakule nihutada. Väga vajalik asi sisse lülitada.

Show special characters (OFF) – kuvad koodikirjutamise aknas ka reavahetuse, tühiku jms sümbolid. Pole kunagi vaja läinud.

Convert tabs to spaces (OFF) – pole vaja sisse lülitada. Las tabid jäävad tabideks ja mitte ei muutu tühikuks.

Highlight current line (ON/OFF) – mul on see sisse lülitatud, kuna poistele suurelt ekraanilt näidates on sedasi parem koodirea peal fookust hoida – sellisel juhul tuleb ka Color vahelehelt määrata mis on Highlight rea värvus, mina valisin kollase. Kui kirjutan lihtsalt koodi, siis selleks ajaks võtan välja.

Keep caret x position (ON) – sisselülitamisel hoiab kursor oma positsiooni kui üles alla liikuda. Hea mugav asi, kogu aeg vaja.

Autosize max left character (OFF) – mingi veidi mõttetu asi taas.

Edit – Preferences – General vaheleht

image

Multiformat clipboard copy (ON) – Kopeerib koodi vaikimisi RTF-vormingusse.

Maximize editor Windows (ON) – Käivitamisel ja uute loomisel on aknad kohe full screen.

Use MDI mode (OFF) – võta linnuke ära, siis on programmid TAB-dena

Edit – Preferences – Options vaheleht

image

Lülita sisse ridade nummerdamine. Line numbers.

Edit – Preferences – StartUp vaheleht

image

Määra, et vaikimisi Brick-type oleks NXT.

Juhend: Do–While ja For tsüklid

Alljärgnevalt veel kaks eriliste omadustega tsüklit, mille abil saab panna programmi täitma mingit protseduuri teatud arv kordi.

 

do { “body” } while ("tingimus")

do
 {
    x = x + 1;
    y = y * 2;
 } while (x < 10);

 

Erinevus tavalisest while-tsüklist seisneb selles, et tsükkel täidetakse alati vähemalt ühe korra ja alles seejärel kontrollitakse tingimuse vastavust. Kui on tõene, täidetakse tsükkel teist korda.

Seega, kõigepealt läheb tsükkel käima, täida enda sees olevad käsud ja siis jõuab viimase reani while (..), mille sees on tingimus. Kui see tingimus on tõene minnakse do-tsükli algusesse tagasi ja täidetakse uuesti kõik käsud. Kui aga while (..) tingimus on muutunud vahepeal FALSE-ks, väljutakse tsüklist ja minnakse ülejäänud programmiga edasi.


for("init", “condition”, “increment”) { “body” }

for (int i = 0; i < 8; i++)
 {
    NumOut(0, LCD_LINE1-i*8, i);
 }


Init-ga algväärtustatakse muutuja i, tsükkel täidetakse kuni tingimus on tõene ja iga tsükli alguses täidetakse increment, mis liidab muutujale i juurde ühe numbri.

Seega, For-tsükli eripära seisneb selles, et sellele on sisse ehitatud loendur, mida saab kohe for-tsükli parameetrites häälestada. Pole tarvis ise tsükli sisse kirjutada liitmistehet ja suurem/väiksem kontrolli.

5. tund: Värvilise rooliga auto

Käesoleva tunni eesmärk on õppida kasutama C-keeles switch-funktsiooni.

Õppematerjal: Juhend- IF ELSE ja SWITCH kasutamine

Ülesanne: Värvilise rooliga auto

Teha BOT mis on ühendatud juhtme abil värvianduriga, mis omakorda on BOT-i juhtimispult.

Juhtimispult ehitada selliselt, et värvianduri ees oleks pöörlev komponent (hammasratas, rool), mille peale on kinnitatud värviline ketas. Rooli otse hoides sõidab robot otse kuid keerates muutub värv ning robot hakkab sellele vastavalt pöörama.

Musta värvi paber kinnita teise klotsi külge, mis liigub värvilise ketta ning värvianduri ees edasi tagasi. Must värv on nagu autol gaas, kui must on ees, siis robot seisab, vastasel juhul liigub vastavalt ketta värvile. Sellesama pildi saad siit suuremana avada ja siis välja printida.

Värviline roolVärvide tähendused:

  • Punane: sõidab otse
  • Sinine-roheline: keerab veidi paremale-vasakule
  • Kollane-oranz: keerab keskmisel paremale-vasakule
  • Helesinine-heleroheline: keerab palju paremale-vasakule
  • Valge: keerab kohapeal ringi

Lahendus.

Nagu arvata oligi siis tegemist on lihtsa programmiga, kuid õpetab hästi switch-funktsiooni olemust.

Rooli ehitamise peamine raskus seisnes selles, et värviandur ei tundnud korralikult värve ära. Esialgu arvasime, et liiga ligidalt mõõdab ning jätsime vahe ca 3 cm. Kuid nüüd oli valge ja musta tulemus sama, mis oli eriti veider.

Lõpuks jõudsime lahenduseni, värviandur tuli paigutada väikese nurga all, muidu see mõõtis enda poolt välja kiirguva valguse peegeldust. Seega HiTechnicu värviandur poolviltu asetades oli tulemus perfektne.

/*
Autor: Ramses Sepp 02.02.2011
Sellel robotil on värviline rool ja must käigukang. 
Rooliga ta sõidab erinevalt ja käigukang peatab roboti. */ task main(){ SetSensorLowspeed(S2); while (TRUE){ switch (SensorHTColorNum(S2)) { //must - mootorid seisavad case 0: Off(OUT_BC); ClearScreen(); TextOut(5, LCD_LINE3, "must"); break; //sinine - robot keerab kergelt paremale case 2: OnFwdSync(OUT_BC, -50, 5); ClearScreen(); TextOut(5, LCD_LINE3, "sinine"); break; //helesinine - robot keerab palju paremale case 3: OnFwdSync(OUT_BC, -50, 35); ClearScreen(); TextOut(5, LCD_LINE3, "helesinine"); break; //roheline - robot keerab kergelt vasakule case 4: OnFwdSync(OUT_BC, -50, -5); ClearScreen(); TextOut(5, LCD_LINE3, "roheline"); break; //kollane - keerab keskmiselt paremale case 6: OnFwdSync(OUT_BC, -50, 15); ClearScreen(); TextOut(5, LCD_LINE3, "kollane"); break; //oranþ keerab keskmiselt vasakule case 8: OnFwdSync(OUT_BC, -50, -15); ClearScreen(); TextOut(5, LCD_LINE3, "oranþ"); break; //punane - sõidab otse case 9: OnFwdSync(OUT_BC, -50, 0); ClearScreen(); TextOut(5, LCD_LINE3, "punane"); break; //heleroheline - keerab palju vasakule case 13: OnFwdSync(OUT_BC, -50, -35); ClearScreen(); TextOut(5, LCD_LINE3, "heleroheline"); break; //valge - keerab kohapeal ringi case 17: OnFwdSync(OUT_BC, -50, 100); ClearScreen(); TextOut(5, LCD_LINE3, "valge"); break; } } }

Juhend: NXT ekraani kasutamine

Ekraani saab kasutada selleks, et saada aktiivselt tagasisidet roboti töö kohta. Alljärgnevalt mõned peamised käsud NXC-s, mille abil juhtida ekraani. Siin on toodud välja ka NXT-l asuvad nupud ja heli tegemine.

ClearScreen();

 

Puhastab ekraani.

ClearLine(“Line”);

ClearLine(LCD_LINE2);

Puhastab ekraanil rea nr 2.

TextOut(“x”, “line”, “txt”, “options”);

TextOut(50, LCD_LINE3, "Hello World");

 

Kirjutab  ekraanil reale nr 3 sõnad „Hello World“.

NumOut(“x”, “line”, “val”, “options”);

TextOut(50, LCD_LINE3, 38);

 

Kirjutab  ekraanil reale nr 3 numbri 38.

ButtonPressed(“btn”,“reset”);

ButtonPressed(BTNRIGHT, FALSE);

Kontrollitakse nupuvajutust.

BTNRIGHT

BTNLEFT

BTNCENTER

BTNEXIT

Reset on TRUE/FALSE ning see nullib nupuvajutuste arvu.

PlayTone(“frequency”, “duration”);

PlayTone(440, SEC_2);

Mängib 2 sekundit tooni sagedusel 440 Hz.

Juhend: Matemaatika C-keeles

Programmide kirjutamisel on tarvis liita-lahutada-korrutada-jagada. Oma tavapärasest matemaatikas oleme harjunud kirjutama, et a + b = c.

C-keeles saab aga asju lühemalt väljendada, kõik ikka selleks et programm oleks lihtsam.

Tüüpiliselt on programmis muutuja (see on mingi arv näiteks), millele on vaja juurde liita mingi teine number.

Matemaatiliselt tavaline liitmistehe on võimalik C-keeles järgmiselt teha x += y. Hea lühike.

Tehe C-keeles Kirjeldus

liitmine

x += 2

Liidab muutujale juurde teise arvu. 

  • Olgu x alguses 1
  • Seejärel liidame x-le juurde 2 (x + 2 = 3)
  • Tulemuse kirjutame iseendasse, st. x-le omistatakse väärtus 3 ja seega x = 3

See tehe on pikemalt välja kirjutades x = x + 2

Näide 2. x += y

  • olgu x alguses 3 ja y väärtus 2
  • ülaltoodud tehe on x + y ehk siis 3 + 2 = 5
  • antud arv 5 omistatakse x-le, ja seega x = 5

lahutamine

x –= 2

Lahutab muutujast teise arvu. 

  • Olgu x alguses 5
  • Seejärel lahutame x-st arvu 2 (x – 2 = 3)
  • Tulemuse kirjutame iseendasse, st. x-le omistatakse väärtus 3 ja seega x = 3

See tehe on pikemalt välja kirjutades x = x - 2

korrutamine

x *= 2

Korrutab muutujat teise arvuga. 

  • Olgu x alguses 3
  • Seejärel korrutame x-i arvuga 2 (x * 2 = 6)
  • Tulemuse kirjutame iseendasse, st. x-le omistatakse väärtus 6 ja seega x = 6

See tehe on pikemalt välja kirjutades x = x * 2

jagamine

x /= 2

Jagab muutujat teise arvuga. 

  • Olgu x alguses 6
  • Seejärel jagame x-i arvuga 2 (x / 2 = 3)
  • Tulemuse kirjutame iseendasse, st. x-le omistatakse väärtus 3 ja seega x = 3

See tehe on pikemalt välja kirjutades x = x / 2

liitmine
ühe ühiku võrra

i++

Kahekordne pluss tähistab seda, et muutujale i liidetakse juurde üks ühik.

  • Olgu i alguses 1
  • peale selle käsu käivitamist on tehtud tehe i + 1
  • seega tulemus, i = 2

lahutamine
ühe ühiku võrra

i--

Kahekordne miinus tähistab seda, et muutujast i lahutatakse üks ühik.

  • Olgu i alguses 10
  • peale selle käsu käivitamist on tehtud tehe i - 1
  • seega tulemus, i = 9

Juhend: IF ELSE ja SWITCH kasutamine

Programmeerimise enimkasutatav funktsioon on ilmselgelt IF ja IF ELSE laused. Tegemist siis tingimuste kontrolliga ning vastavalt sellele tegevuse ette võtmine.

Teise funktsioonina olen alljärgnevalt välja toonud ka SWITCH funktsiooni, kuna selle näol on tegemist nö. pika if-elseif-elseif-else lausendi jadaga.


if ("condition”) {“body” } else {“body” }

if (x == 1) {
   y = 1;
   z = 2;
 }
 else {
   y = 3;
   z = 5;
 }

Kui tingimus on tõene, täidetakse esimene käsk kui tingimus on väär täidetakse alternatiivne käsk.

IF lause sees võib ELSE IF lauseid palju olla, vaata alljärgnevat.

if (x == 1) {
   y = 1;
   z = 2;
 }
 else if (x == 2) {
   y = 3;
   z = 5;
 }
else {
  y = 6;
  z = 7;
 }

 

Sellisel juhul kontrollitakse kõigepealt IF tingimust kui see on väär minnakse edasi ja kontrollitakse ELSE IF tingimust, kui ka see on väär minnakse edasi järgmise kontrolli juurde.

Ehk siis ülaltoodud IF lauset tuleks lugeda järgmiselt: Kui x on võrdne 1-ga, pane y väärtuseks 1 ja z väärtuseks 2, kui aga x on võrdne 2-ga, pane y väärtuseks 3 ja z väärtuseks 5, või muidu pane y väärtuseks 6 ja z väärtuseks 7.


switch ("expression”) {“body” }

switch(x)
 {
        case 1:
         // tee midagi kui x on 1
         break;
        case 2:
        case 3:
         // tee midagi kui x on 2 või 3
         break;
        default:
         // tee midagi kui x pole 1, 2 või 3
         break;
 }

 

Switch kontrollib x väärtust ja vastavalt selle väärtusele teostab operatsiooni. See on sama tulemus kui ELSE IF funktsiooni jada kasutades. Kuid switch konstruktsiooni on oluliselt lihtsam kasutada võrreldes ELSE IF lausete jadaga.

Näiteks värviandurist värvi-info saamise korral, musta värvi korral tehakse ühtemoodi, punase korral teisiti jne.

Või infrapuna seekerist saabuvate numbrite 1..9 korral saan kirjeldada, mida ühel või teisel juhul tehakse.

Juhend: while, until tsüklite kasutamine

Programmeerimise peamised konstruktsioonid seisnevad tsüklites ning tingimuslausetes. Tsükkel on  tegevus mida korratakse seni, kuni etteantud tingimus vastab tsükli konstruktsioonile. Alljärgnevalt vaatame tsükleid while ning until ja seda mille poolest need erinevad.

Väga levinud on robootikas kasutada lõpmatult täidetavat tsüklit, seda läheb praktiliselt tarvis iga lõputult toimiva programmi korral.


while("tingimus")

while(x < 10)
 {
        x += 1;
        y *= 2;
 }

Kontrollitakse tingimust x<10 kui see on tõene, täidetakse while tsükli sisu ning siis kontrollitakse taas tingimust. Toimub seni kuni tingimus on väär - siis väljutakse tsüklist. Seega antud näite korral läbitakse tsüklit 9 korda, sest 10-dal korral on x = 10, seega tingimus pole tõene ja väljutakse tsüklist ehk minnakse programmiga edasi. 

while(TRUE) see on lõpmatult kestev tsükkel. Kuna TRUE tähendab seda, et avaldise väärtus on alati tõene siis selline tsükkel täidab oma sisu lõpmatu arv kordi.

Alljärgnev on näide nupu “bumped” funktsiooni teostamine:

while(!Sensor(S1));        // ootab nupule vajutamist
while(Sensor(S1));         // ootab nupu lahti laskmist


Ülalkirjeldatud bumped-funktsionaalsus töötab järgmiselt. Kui nupp on lahti siis see tähendab, et esimene tsükkel on tõene. Kui nupp vajutatakse alla, muutub esimese tsükli tingimus vääraks ning tõeseks muutub teise tsükli tingimus. Kui nuppu hoitakse all ei lähe programm edasi, kuna täidetakse teist tsüklit. Nupu lahti laskmise korral muutub tingimus vääraks ning programm läheb edasi.

 


until(EVENT_OCCURS);

until(SensorUS(S1) > 50);

until käitub vastupidiselt while tsüklile, st. see funktsioon ootab kuni tema tingimus saab tõeseks. Seda saab kasutada edukalt programmi seiskamiseks ning andurist kindla signaali ootamiseks.

Alljärgnev on näide nupu “bumped” funktsiooni teostamine, võrdle while tsükliga:

until(Sensor(S1));         // ootab nupule vajutamist
until(!Sensor(S1));        // ootab nupu lahti laskmist

Kui nupp on avatud siis esimese until funktsiooni tingimus on FALSE ja seega programm ootab selle taga. Nupu allavajutamisel muutub tingimus TRUE-ks ning programm läheb edasi, jäädes ootama teise until funktsiooni taha. Kui nüüd nupp lahti lasta ehk nupu väljund on FALSE, muutub teise until funktsiooni tingimus TRUE-ks ning programm liigub edasi