Hrvatsko društvo za robotiku - Uvod u robotiku - vježbe

Struktura programa

Cilj

Kako osmisliti program za izvršavanje više grupa zadataka? Naučit ćemo što su "stanja". Osnova su za bilo koji složeniji program.

Stanja

Stanja okoline je često potrebno razdvojiti i nazvati posebnim imenima. Naše ponašanje može ovisiti o tome kad "smo u autu", "gledamo TV", "smo na radnom mjestu". Za svako od ovih "stanja" imamo poseban skup naučenih ponašanja i već znamo da odmah trebamo prilagoditi sve akcije tom stanju.

Kako je program slika ponašanja u okolini, tako se i u programu moraju posebno opisati takva stanja. Pogledajmo 2 stanja u takmičenju "Robocup Junior Line". Jedno je "praćenje linije", drugo je "prostor evakuacije".

U "praćenju linije" robot, pogađate, prati liniju. U "prostoru evakuacije" prati zid. Oprostite što nismo našli bolju sliku, ali zid je ipak neka veza. Bilo je i boljih, ali su se plaćale pa...

Ovo je bio najkraći primjer. U praćenju linije se pojavljuje "obilazak prepreke", u "prostoru evakuacije" niz drugih stanja. Počnimo s ovim jednostavnim primjerom i pogledajmo kako bismo napisali program.

Početna funkcija

void RobotLine::rcjLine() {
	//...
}

Prvo odlučimo gdje počinje izvršavanje našeg programa. Možemo izabrati jednu od niza predviđenih funkcija: loop(), loop1(), loop2(),... Rješenje je potpuno u redu, ali ipak ostavimo ove funkcije za 10 testnih programića koje možemo pokrenuti na brzinu.

Možemo napisati novu funkciju - ali sad nećemo jer to još ne znate.

Prihvatit ćemo 3. rješenje: koristiti jednu od postojećih funkcija, koja je ostavljena u kodu baš s namjerom da se koristi za početak rješavanja Rescue Line arene: "rcjLine()". Pokrenut ćemo ju kasnije isto kao i loop(): naredbom iz menije ili putem tipkala. Ako Vam je draži loop() - možete ostati na njemu.

Kao što vidite, "rcjLine()" je vrlo sličan "loop()" funkciji. Različito je jedino ime i možda kod između početne i završne vitičaste zagrade, koji možete u potpunosti obrisati.

Najgore rješenje

void RobotLine::rcjLine() {
	if (lineAny()){
		// Tu biste upisali niz naredbi za praćenje linije.
	} else{
		// Ovdje idu naredbe za kretanje u zoni evakuacije.
	}
}

Prvo, najgore rješenje. Recimo da iz jednog stanja u drugo prebacujemo u skladu s tim vidi li robot liniju.

Zamislimo da ste pokrenuli rcjLine() iz menija i upisali umjesto komentara naredbe za praćenje linije i kretanje u zoni evakuacije. Zapamtite, kao i loop, ovaj se program izvršava tisuće puta, ne samo jednom. U svakom bi prolazu pogledao je li na liniji. Ako jest, pratio bi liniju. Ako nije, kretao bi se u zoni evakuacije.

Vjerojatno ste naslutili što radi nova funkcija "lineAny()": ako ijedan senzor robota vidi liniju, vraća istinu. Ako nijedan, laž.

Robot bi stvarno izvršavao željeno. Cilj je postignut, ali, uz sad još nevidljivu, žrtvu: "špageti kod" - komplicirano i teško razmrsivo klupko. Kod za sva stanja bi bio utrpan u jednu funkciju. Na ovaj način je moguće programirati, no ne bismo Vam savjetovali. Svako stanje bi trebalo imati svoj zasebni dio koda. Pogledajmo kako to ostvariti.

Funkcije stanja

void RobotLine::lineFollow() {
	//...
}

...

void RobotLine::evacuationZone(){
	//...
}
Ovo nije doslovan kod iz programa, ali pretraživanjem istog (npr. Ctrl-F) po tekstu "::evacuationZone" i "::lineFollow" ćete ih lako pronaći. Ako imaju naredbe u "tijelu funkcije" (prostoru između početne vitičaste zagrade i završne), slobodno ga obrišite.

Ponovimo, funkcija je niz naredbi, koji čini logičku cjelinu. Funkcija se može "pozvati" u kodu - u tom času će ona izvršiti sve svoje naredbe, počevši s prvom.

Naš je cilj kako izvršavati ove funkcije ("pozvati ih") baš u času kad to bude potrebno. Npr., kad senzor linije vidi da je ispod njega linija, program bi se trebao prebaciti u stanje "praćenje linije" i početi izvršavati za to danu funkciju: "lineFollow()".

Bolje rješenje

void RobotLine::rcjLine() {
	static uint32_t lastMs = millis();
	if (lineAny())
		lastMs = millis();

	if (millis() - lastMs > 2000)
		evacuationZone();
	else
		lineFollow();
}

Idemo mi, u skladu s naučenim, na bolje rješenje. Recimo da je robot u stanju "praćenje linije" kad vidi liniju ili ju je vidio prije manje od 2 sekunde. Na taj način mu se daje prilika da pređe prekinutu liniju i nastavi praćenje.

U kodu je to ostvareno na način da je uvedena nova varijabla lastMs. Vidimo da je tipa jako velikog cijelog broja i da ima prefiks "static", što znači da u nju možemo pohraniti broj milisekundi i da će se to dogoditi samo kad prvi put program izvrši naredbu.

Zamislimo sad da se program izvršava, znači "rcjLine()" se opetovano izvršava tisuće puta. Kad se prvi put izvrši, "lastMs" će poprimiti trenutni broj milisekundi. Nakon toga slijedi "if". Ako robot jest na liniji, "lastMs" će poprimiti trenutni broj milisekundi - što ovdje nije bitno jer je to već spremljeno u prethodnoj liniji. Međutim, kako će vrijeme odmicati, dok god je robot na liniji, "lastMs" će i dalje pokazivati trenutno vrijeme.

Pogledajmo što sljedeći "if" radi u tom slučaju. Razlika "millis() - lastMs" će biti 0 (ponekad 1), u svakom slučaju nije veće od 2000. Znači, biti će izvršen "else" dio - bit će "pozvana" funkcija "lineFollow()". "Poziva" se tako da se njeno ime jednostavno upiše u kod. Kad program dođe do te linije, izvrši njeno tijelo.

Zamislite sad da je robot sišao s linije. "rcjLine()" se i dalje izvršava, ali prvi "if" više ne ažurira varijablu "lastMs". Razlika "millis() - lastMs" će postajati sve veće. Sve dok, nakon 2 sekunde, ne postane veća od 2000. U tom času će robot izvršiti funkciju "evacuationZone()" u 2. "if-u".

Ovo je bio idejni kod. Npr. robot neće pratiti liniju jer nismo još ništa upisali u "lineFollow()".

Najbolje rješenje

Nažalost, komplicirano je. Ostanimo zasad na prethodnom rješenju.

Eksperti,

Zadatak: proučite nogometaša.

Proučite program za jednog robota, npr. za nogometaša. Pogledajte koje se akcije tamo koriste stanja i kako se prelazi u sljedeće.

Primjedbe



Projekt "Uvod u robotiku" sufinanciran je iz Europskog socijalnog fonda, poziv "Jačanje kapaciteta organizacija civilnoga društva za popularizaciju STEM-a". Relevantne stranice: Sadržaj vježbe za virtualne radionice isključiva je odgovornost Hrvatskog društva za robotiku.