0

Struktura Arduino koda mobilnog robota

O kodu

Navodimo jednostavan primjer toka programske logike. Odredimo sva stanja u kojima robot može biti i smjestimo ih u enumeraciju (u primjeru dolje: State). Odredimo varijablu (u primjeru: state) koja će držati trenutno stanje robota. U petlji (funkcija loop) mijenjamo vrijednost varijable state i tako izvršavamo dijelove switch naredbe:

enum State {Idle, ApproachingBall, BallInPossesion};
State state;

void setup(){}

void loop()
{
	switch (state) {
		case Idle:
			//Kod za ovo stanje
			break;
		case ApproachingBall:
			//Kod za ovo stanje
			break;
		case BallInPossesion:
			//Kod za ovo stanje
			break;
	}
}

Sigurno će se u kodu pojavljivati if naredbe koje će mijenjati vrijednost varijable state. Kad je robot u stanju Idle i uoči loptu, pripadajući if će izvršiti naredbu:

state = ApproachingBall;
i tako će robot promijeniti dio switch-a koji izvršava.

Bitno je pitanje dizajna koda koliko će dugo trajati svaki pojedini case switch-a. Problem uzrokuje potreba za održavanjem dovoljno velike frekvencije izvršavanja nekih naredbi: trebate dovoljno često čitati razne senzore, korigirati motore i drugo. U RCJ nogometu robot ne smije izaći preko bijele linije. Linija je uska, robot se kreće brzo. Bez vrlo često čitanja senzora linije, taj se zadatak ne može uspješno obaviti. Ako su svi dijelovi switch-a kratki, npr. ispod 25 msec, moguće je sve potrebne periodičke promjene postaviti iza switch-a, znači samo na jednom mjestu. Ako je bilo koji case dugačak, sve provjere moraju ići i u njega. Dupliciranje koda je jako loša programska navika. Prema tome, bolje je sve case dijelove držati brzima. Nažalost i ovo rješenje ima manu jer moramo pamtiti vrijednosti istog stanja (isti state) među prolazima kroz petlju, globalnim varijablama (lošije) ili static lokalnim varijablama (bolje).

Alternativno se mogu neke periodičke radnje obaviti putem interrupta, odnosno timer interrupta. Ipak je tu potrebna veća programska vještina i nije sve moguće riješiti ovim putem. Uzmimo primjer ultrazvučnog senzora. Rezultat karakteristično daju širinom (vremenom trajanja) digitalnog pulsa. Čitamo ga Arduino funkcijom pulseIn(). Gadan je problem što ta funkcija čeka kraj impulsa i blokira izvođenje programa. Za brzog robota je to neprihvatljivo. Rješenje može biti čitanje početnog i završnog signala impulsa putem 2 interrupta, asinkrono. Drugo je rješenje koristiti ultrazvučni senzor koji vraća rezultat npr. I2Com, kad poželimo, bez blokiranja programa.

Kolika je prihvatljiva frekvencija prolaza kroz glavnu petlju? Nema općeg pravila jer broj jako ovisi o brzini robota i potrebnoj brzini reakcije. Za male i spore robote, bez većih zahtjeva za munjevitom akcijom, frekvencija bi trebala biti veća od 30 Hz (za ljubitelje ripanja filmova može i 30 FPS - frames per second). Što kad je to teško ili nemoguće postići? Koristite više povezanih Arduino mikrokontrolera ili ARM mikrokontroler koji može izvršavati više niti (gotovo) odjednom.

Što napraviti kad nešto krene po zlu? Npr. dobijemo negativnu udaljenost. Takvi se slučajevi ne smiju ignorirati jer uzrokuju neočekivano ponašanje robota kojem je teško otkriti uzrok. Pravo je rješenje pozvati funkciju koja isključuje sve motore, ispiše poruku greške i zaustavi izvršavanje programa. Funkcija:

void error(const char * message){ //Ulazni parametar je poruka greške
	motors.go(0, 0);//Primjer zaustavljanja motora kad se koristi klasa Motors
	Serial2.print(message);//Ispišemo grešku preko Bluetootha na mobitel
	Serial.print(message);//Isto i na ekran, za slučaj da je spojen PC
	while (true)//To je bilo to: zaustavljamo program beskonačnom petljom.
		;
}
Pozivamo ju u kodu:
if (distance < 0)
	error("Negativna udaljenost!");