Jocul de X Si 0 Si Implementarea Acestuia

CAPITOLUL III

Jocul de X și 0 și implementarea acestuia

Jocul de X și 0 este un joc cunoscut încă din vremuri străvechi, mai exact din perioada antichității, în jurul primului secol de dinaintea erei noastre. A fost denumit Terni Lapilli și în locul unui joc care se termină, fiecare jucător deținea trei pietre pe care trebuia să le ordoneze într-o linie, acestea fiind puse într-un pătrat de dimensiuni 3×3. În ciuda simplității aparente a jocului, el necesită o analiză detaliată pentru a dermina numărul de combinații posibile pentru joc.

Implementarea acestuia a fost făcută cu două module principale, unul dintre ele fiind modulul de procesare în timp real al imaginii iar cel de-al doilea este modulul de mișcare. În ajutorul acestora au fost construite alte module adiționale ce vor fi explicate în continuare, fiind apelate de către funcția main din modulul main_function.

3.1 Conceptul aplicatiei

Aplicația software a fost gândită pe modelul unui agent inteligent, prezentat în introducerea lucrării, fiind adaptată pentru situația curentă, astfel, agentul inteligent a fost împărțit în cele cinci părți componente în interiorul cărora este descris modelul comportamental. Mediul, în acest caz este compus din adversar și mediul propriu-zis de joc. Împărțirea pe module a fost făcută din rațiuni de respectare a paradigmei de programare, algoritmii de joc fiind cei de deciziei asupra acțiunilor următoare. Modulul de procesare de imagini se ocupă de procesarea stimulilor externi, împreună cu modulul de recunoaștere vocală, iar modulul de mișcare și comportament, este cel care efectuează acțiunea, conform cu figura următoare.

Figura [15] 3.1 Conceptul aplicatiei software

3.2 Modulul main

Acest modul este cel în care utilizatorul se conectează la robot scriind adresa de rețea și portul prin care se vor încarcă modulele componente. Acesta va instanția în interiorul său clasă NaoXO ce are în componența sa funcții ce vor fi explicitate în capitolul 3.3. Conectarea la robot se face printr-o conexiune de tip SSH în rețeaua locală. Folosind instrucțiunea import se aduc la dispoziția utilizatorului diferite funcții de sistem. După conectare, acesta va poziționa robotul în poziția predefinită standInit, aceasta fiind favorabilă poziționării câmpului de joc în rază vizuală a robotului. În cazul în care robotul nu reușește să înceapă jocul, pe fereastră utilitarului IDLE va apărea un mesaj “game not Initialized” împreună cu un mesaj de eroare în cazul în care acesta există. Robotul va apela metoda de curățare a variabilei, acesta fiind destructorul clasei NaoXO și va afișa eroarea. Robotul va întreba utilizatorul la finalul jocului dacă dorește o rejucare a jocului folosind modulul de vorbire deja existent pe acesta, adus la dispoziția utilizatorului folosind instructiuea ALProxy.

3.3 Modulul de procesare al imaginilor

Acest modul este responsabil cu găsirea câmpului și procesarea de imagine, fiind folosite cu precădere funcții predefinite din platforma openCV. Acesta are în componența să o multitudine de funcții de procesare a imaginilor, ce oferă un răspuns în timp real la stimuli exteriori. În interiorul acestui modul este definite clasă ImgprocessingXO() ce va fi folosită ulterior. Aceasta preia de la camera video a robotului secvențe de imagini pe care le procesează și oferă celorlalte module componente informațiile necesare pentru starea jocului și modul în care este poziționată tabla de joc.

Prima funcție utilizată în apelarea acesteia, pe langa constructorul clasei este funcția de găsire a liniilor din imagine. Funcția este sugestiv denumită preprocessLines si va fi explicitata ulterior. Funcția convertește imaginile primite de la camera într-un format de imagine alb-negru și o afiseaza utilizatorului folosind funcția imshow. Următorul pas este găsirea liniilor prin aplicarea unui algoritm de detecție denumit HoughLines ce va găsi liniile din imagine prin diferențierea culorilor si le va afisa pe imagine cu rosu. In cazul in care robotul nu găsește liniile, acesta va afisa pe ecran mesajul I have found no lines, ajutând utilizatorul sa optimizeze funcțiile utilizate.

def preprocessLines(self, image):

'''

Here we convert the image to grayscale

extract the edges,

and apply hough to obtain the lines in the field

'''

self.img_grayscale = openCV.cvtColor(image,openCV.cv.CV_BGR2GRAY)

openCV.imshow("Black_And_White", self.img_grayscale)

## show edges

edges = openCV.Canny(self.img_grayscale, self.lCanny, self.uCanny)

openCV.imshow("Edges", edges)

openCV.waitKey(1)

## imshow does not work without waitKey

## use try-catch method for error detection

try:

lines = openCV.HoughLines(edges, self.rhoRes, self.thetaRes, self.houghThreshold) [0]

image_lines = self.draw_lines(image.copy(), lines)

openCV.imshow("Lines", image_lines)

openCV.waitKey(1)

except:

print("I have found no lines")

#blank return

return []

return lines

După găsirea liniilor, robotul va găsi intersecțiile dintre acestea și va aplica un algoritm de detecție asupra intersecțiilor pentru verificarea lor cu ajutorul funcției checkIntersections. Această funcție găsește o aglomerare de puncte negre în jurul unui punct iar dacă orientarea aglomerărilor va fi de 90 de grade va semnala acest fapt printr-o variabila booleană. Următorul pas este reprezentat de găsirea punctelor de colț din imagine, presupunând că fiecare dintre șabloanele utilizate pentru tablă arată identic, fiind matrici de dimensiuni 3×3.

Principala funcție implementată în acest modul, ce reușește să coreleze celelalte funcții este reprezentată de funcția getGameState() ce primește ca parametri self (obiectul asupra căruia funcția acționează), img (imaginea provenită de la camera video și margin_lines ce reprezintă vectori cu marginile tabelei. Aceasta returnează utilizatorului doi parametri, state și board, doi vectori de dimensiune 9×1 care stochează starea actuală a jocului.

def getGameState(self, img, margin_lines):

### function used to calculate the state of the game and store it

## by analizing the image from the camera

## create vector for storing the game state

state=['-']*9

board = [[-1,-1,-1],

[-1,-1,-1],

[-1,-1,-1]]

## create board

if margin_lines:

## if there are the lines accordingly to the patern

if len(margin_lines)==4:

noughts, crosses = self.getCentroids(img)

#used getCentroids for getting the state of the game

if noughts:

## check positions for zeros and put them in the field

for nought in noughts:

(ind_x,ind_y)=self.getBoxInField(nought, margin_lines)

## save in state

state[ind_x*3+ind_y]='o'

#replace 'o' with zero

board[ind_x][ind_y] = 0

if crosses:

## get crosses and put them in the right position

for cross in crosses:

(ind_x, ind_y)=self.getBoxInField(cross, margin_lines)

## save in state

state[ind_x*3+ind_y]='x'

# replace 'x' with ones

board[ind_x][ind_y] = 1

## here we return the params in which were saved the positions

return state, board

Funcția apelează în interiorul său funcția getCentroids(img), o funcție folosită pentru găsirea formelor în interiorul imaginii. Aceasta folosește o limită pentru detecția formelor ignorând posibilele semne mici și greu de distins ce ar putea fi doar erori de scriere ale robotului. Folosind acest tip de detecție și folosind parametri liniilor de margine, robotul găsește semne de X sau de 0 și le introduce în doi vectori, o și x folosind instrucțiunea append.

Detecția semnelor de X și 0 se face folosind funcțiile getContours_X(self, imgHSV) respectiv getContoursO(self, imgHSV) ce împarte imaginea într-un spațiu de culori. Funcția face găsirea contururilor de X și 0 diferit, însă singură diferență remarcabilă este reprezentată de schimbarea parametrilor limită..

def getContours_O(self, imgHSV):

'''

Finds O’s written with green

'''

lower_green= np.array([35,74,50])

upper_green= np.array([70,255,200])

## segment the image

binImg = openCV.inRange(imgHSV, lower_green, upper_green)

openCV.imshow('zerouri', binaryImg)

## dilate and erode using morphologyEx

binImg = openCV.morphologyEx(binaryImg, openCV.MORPH_OPEN, np.ones((3,3),np.uint8))

## contour find and append

contours_o = openCV.findContours(binImg, openCV.cv.CV_RETR_TREE, openCV.cv.CV_CHAIN_APPROX_SIMPLE)[0]

## return

return contours_o

Rezultatul împărțirii este afișat utilizatorului pe ecran în două imagini separate, una pentru formele de X iar cealaltă pentru formele de 0.

Figura [16] 3.2 Împărțirea imaginii HSV în două imagini distincte

După găsirea formelor de X și de 0 robotul va returna utilizatorului cei doi vectori amintiți anterior ce vor fi introduși în imaginea Game folosind instrucțiunea openCV.imshow.Aceasta reprezintă un răspuns la stimuli exteriori în mod continuu, reușind să avertizeze utilizatorul în cazul în care detecția se face incorect prin simpla comparație dintre imaginea de pe ecran și cea din fereastra Game.

Figura [17] Figura 3.3 Fereastra GAME

3.4 Modulul de recunoaștere vocală

Modulul de recunoaștere vocală reprezintă o implementare și o adaptare a exemplului oferit de constructorul robotului ca exemplu. Este un modul aditional ce aduce robotului posibilitatea de a comunica într-o formă verbală cu utilizatorul. Scopul principal al acestuia este de a returna modului motionlib.py explicitat anterior și modulului tic_tac_toe_strategy.py dificultatea cu care robotul va juca jocul de X și 0.[18]

import time

from naoqi import ALProxy

def ask_for_difficulty (player):

ROBOT_IP = "IP address here"

# Creates a proxy on the speech-recognition module

asr = ALProxy("ALSpeechRecognition", ROBOT_IP, 9559)

memProxy = ALProxy("ALMemory",ROBOT_IP,9559)

asr.setLanguage("English")

# Adds "easy", "hard" and "please" to the vocabulary (without wordspotting)

vocabulary = ["easy", "hard"]

asr.setVocabulary(vocabulary, True)

player.motion.setStiffnesses("Body", 1.0)

player.tts.say ('Do you want hard mode or easy mode?')

time.sleep(8)

asr.subscribe("NAOXO ")

print 'Speech recognition engine started'

time.sleep(10)

word = memProxy.getData("WordRecognized")[0]

if (word == '<…> easy <…>'):

print 'easy has been undestood'

result = 'easy'

else:

print 'hard has been understood'

result = 'hard'

asr.unsubscribe("NAOXO")

return result

Acest modul utilizează patru module deja existente pe robot, ALProxy, ALMemory, ALSpeechRecognition și ALTextToSpeech pentru a comunica mesaje utilizatorului. Pe lângă returnarea rezultatului analizei vocale, robotul va afișa și în fereastra de comada cu ajutorul instrucțiunii print cuvântul înțeles. Secvența returnată va fi preluată apoi de către modulele de strategie și modulul de mișcare al robotului. Funcția primește ca parametru variabila player ce reprezintă o instanță a clasei NaoXO, se conectează la robot folosind adresa de rețea și adaugă în vocabularul robotului două cuvinte, easy și hard ce sunt echivalentele nivelului mic respectiv mare de dificultate al jocului..

3.5 Modulele de strategie

Modulele de strategie sunt împărțite în două părți, o parte pentru determinarea câștigătorului, iar cea două parte componentă este algoritmul de decizie al următoarei mișcări. Algoritmul de determinare al câștigătorului este prezentat în secvența următoare de cod pentru determinarea câștigătorului pe linii, respectiv coloane.

În secvența următoare de cod, robotul primește ca parametru o matrice de dimensiuni 3×3, în care semnele de X sunt înlocuite cu valori de 1 iar semnele de 0 sunt înlocuite cu valori de 0, spațiile goale fiind desemnate ca fiind înlocuite cu -1 din funcțiile anterioare. Dacă rezultatul analizei pe linii la numărătoarea semnelor de X său 0 este 3 acesta va returna utilizatorului o secvența pentru a fi procesată ulterior. În caz contrar, rezultatul va fi still_playing, semnificând continuarea jocului. Secvența de cod este repetată pentru numărătoarea pe coloane, respectiv pentru numărătoarea pe diagonale. Aceasta este un modul important în modulul de strategie întrucât indică robotului dacă poate efectua o mișcare ce îi poate aduce victoria.

def determining_the_winner (panel):

result="draw"

#lines

for i in range(3):

number_of_xs=0

number_of_os=0

for j in range(3):

if panel[i][j]==1:l

number_of_xs=number_of_xs+1

elif panel[i][j]==0:

number_of_os=number_of_os+1

difference=3-number_of_xs-number_of_os

if (difference>0) and (result=="draw"):

result="still playing"

elif number_of_xs==3:

result="winner x"

elif number_of_os==3:

result="winner o"

A două parte componentă a modulelor de strategie este modulul tic_tac_toe_strategy ce are în componența sa mai multe funcții. Robotul utilizează pentru a juca jocul două metode, în funcție de simbolul ce îi este alocat, presupunând tot timpul că simbolul de X este cel care începe jocul. Algoritmul de joc folosește câteva funcții adiționale pentru determinarea locurilor disponibile pentru inserarea simbolurilor. În continuare vor fi prezentate aceste funcții. Aceste două funcții returnează pozițiile libere respectiv pozițiile cu X-uri de pe tablă, cea de-a doua funcție, având corespondentul ei pentru simbolurile de 0. Acestea vor introduce în câte o matrice, folosind instrucțiunea append valorile pentru a fi returnate.

def free_places_on_board (board):

free_places=[]

for i in range (3):

for j in range (3):

if board [i][j]==-1:

free_places.append([i,j])

return free_places

def x_locations (board):

x=[]

for i in range (3):

for j in range (3):

if board [i][j]==1:

x.append ([i,j])

return x

Funcțiile strategy_x, respectiv strategy_o sunt cele două funcții vizibile în exteriorul modulului, fiind împărțite în două părți, în funcție de secvența primită de la modulul de voce. În cazul în care robotul a primit secvența ‘easy’ acesta va lua decizia de a pune aleator în spațiile libere de pe matricea de joc simbolul.

def strategy_x (board,difficulty):

if(difficulty == 'easy'):

temporary_copy=copy(board)

free_places=free_places_on_board(temporary_copy)

field = free_places[randint(0,len(free_places)-1)]

În cazul în care secvența este modul greu de joc, acesta va lua o decizie ce respectă următoarea diagrama de stări, ce îi aduce acestuia minim un joc egal cu utilizatorul. Diagrama logică din figura următoare presupune ca pozițiile în care robotul poate pune simboluri sunt calculate la fiecare trecere.

Figura [18] 3.4 Diagrama de stări a algoritmului de decizie

3.6 Modulul de mișcare și analiză a stării jocului

Modulul de mișcare și analiză a stării jocului, denumit sugestiv motionlib.py este unealta software principală prin care robotul interacționează cu programatorul. Acest modul este apelat de către funcția main prin instantierea clasei NaoXO în corpul funcției principale. Ca orice clasă, aceasta are nevoie de un constructor, denumit conform paradigmei de programare __init__ ce face conexiunea către modulele explicate anterior, modulul de comportamente, modulul de memorie, modulul video, modulul text către vorbire și modulul de procesare de imagine cu ajutorul instrucțiunii self.imgproc = img.imgProcessingXO(self.size) unde parametrul self.size reprezintă dimensiunile imaginilor procesate de către camera video.

Următoarea funcție apelată este cameraInit, funcție ce primește ca parametri 5 variabile, fx, fy, cx,cy și s ce reprezintă distanță focală pe axa x și y, centrul imaginii pe axa x și y iar s este factorul de corecție al înclinației camerei.

Cea mai importantă funcție din modul este reprezentată de funcția gameInit ce conține în interiorul său o buclă infinită de tip while True ce utilizează modulul de procesare de imagine. Metoda va căuta în primă instanță câmpul de joc, folosind modulul de procesare explicitat anterior și va încerca sa găsească diferențe între starea anterioară și starea curentă a jocului, comparând doi vectori de stare. La prima apelare, acesta va număra obiectele de pe tablă.

## if there are 2 or more objects on the playing field, robot does not know how to start

if count_x_on_the_field + count_o_on_the_field >= 2:

self.tts.say("Too many objects on the playing field, please clean the board and restart")

return False

elif count_x_on_the_field == 1 and count_o_on_the_field == 0:

self.tts.say("You started first. Ok, I will be playing with zeros, please draw them with a green pen")

self.mode = 'o'

self.difficulty = ask_for_difficulty(self)

#self.difficulty == 'hard'

return True

## clean board, robot plays first

elif count_x_on_the_field == 0 and count_o_on_the_field == 0:

self.tts.say("Ok, I will play first using crosses, please draw them with a red pen")

self.mode = 'x'

#self.difficulty == 'hard'

self.difficulty = ask_for_difficulty(self)

return True

În timpul execuției acestei secvențe de cod, robotul va avea trei posibilități. În cazul în care pe tabla vor fi mai mult de două semne de X sau 0 acesta va abandona jocul și va cere utilizatorului restartarea programului. În cazul în care numărul de X-uri este unu, acesta va ști faptul ca utilizatorul a început jocul și va juca utilizând simbolul 0, în caz contrar, tabla fiind goală, va începe jocul. Următorul pas este apelarea modulului de recunoaștere vocală, ce va face robotul să întrebe nivelul de dificultate la care se dorește a se juca jocul, stocând în memorie valoarea returnată, easy sau hard. Metoda setFieldSize este o funcție ce primește că parametru două obiecte date de utilizator: self¸ instanță a clasei asupra căreia se fac operații și dimension reprezentând dimensiunea câmpului de joc. Vor fi create în memoria robotului trei matrici de coordonate folosind centrul imaginii că origine a axelor, acestea fiind:

Colțurile pătratului central;

Centrele pătratelor;

Colțurile plăcii de joc.

O metodă importantă in dezvoltarea acestei aplicații este getImage(self) ce aduce memoria robotului imaginea de la camera video, astfel: :

def getImage(self):

img_params = self.videoProxy.getImageRemote(self.video)

openCV.cv.SetData(self.imgheader, img_params[6])

self.image = np.asarray(self.imgheader[:,:]

return self.image

Pentru a nu utiliza o singură metodă și a folosi valori fixe, robotul dispune de posibilitatea aducerii parametrilor camerei video prin metoda getImageRemote care returnează într-un vector cu șase poziții lărgimea și lățimea imaginii, numărul de spații de culoare utilizate, spațiul de culoare, ștampila de timp cu secunda capturii și cea cu milisecunda capturii. Fiecare pixel al imaginii este convertit într-un vector de valori conținând trei parametri, luminuozitate, nuanță și saturație, conform standardului HSV, fiind procesat mai departe de către algoritmii de viziune.

Alte două funcții importante implementate în cadrul acestui modul sunt funcțiile checkStates și updateState acestea reprezentând modul în care robotul prelucrează informația din mediul exterior. Prima dintre acestea două verifică dacă starea anterioară este aceeași cu starea curentă, în cazul unei inegalități, aceasta va apela funcția care face remprospatarea starii jocului prin modulul de procesare de imagine, făcând o actualizare în modulul de strategie al robotului..

Desenarea utilitarului Game State, menționat anterior, se face utilizând metoda draw_board ce va returna într-o imagine pe ecranul calculatorului conectat la robot starea curentă a jocului, fiind cel mai bun mod de verificare a corectitudinii codulului și al funcționării modulului de procesare de imagine.

def draw_board(self, state):

'''

here we draw the state of the game according to the game

'''

#white background

game_state = np.ones((300,300,3), np.uint8)

game_state[:]=(255,255,255)

#black lines

openCV.line(game_state, (0,100), (300,100), (0,0,0),3,8)

openCV.line(game_state, (0,200), (300,200), (0,0,0),3,8)

openCV.line(game_state, (100,0), (100,300), (0,0,0),3,8)

openCV.line(game_state, (200,0), (200,300), (0,0,0),3,8)

#here we create the boxes

fields = [(30,65),(130,65),(230,65),(30,165),(130,165),(230,165),(30,265), (130,265), (230,265)]

for counter_i in range(9):

if state[counter_i]=='x':

openCV.putText(game_state,state[counter_i],fields[counter_i],openCV.cv.CV_FONT_HERSHEY_COMPLEX,2,(0,0,255), 3,8)

#red x-s

if state[counter_i]=='o':

openCV.putText(game_state,state[counter_i],fields[counter_i],openCV.cv.CV_FONT_HERSHEY_COMPLEX,2,(0,255,120), 3,8)

#green zeros

return game_state

Acesta va desena pe ecran un pătrat având dimensiunea de 300 x 300 pixeli, alb, și va crea în interiorul acestuia nouă căsuțe. În acestea, folosind metoda predefinita putText, utilizatorul va observa faptul că în momentul în care robotul “scrie” un simbol pe tablă, acesta va apărea în utilitar, fie că este X, fie că este 0, acest fapt întâmplându-se și în cazul în care utilizatorul va desena un simbol pe tablă.

Un alt utilitar folosit pentru găsirea câmpului și vizionarea modului în care robotul vede câmpul de joc este funcția drawstuff.

def drawstuff(self, flag)

## open two windows for debugging

openCV.namedWindow("Initial Image")

openCV.namedWindow("Game State")

## if the flag is set to false, do nothing

if flag:

#draw valid intersections

for i in range(4):

intersection_one = (openCV.cv.Round(self.imgproc.corners[0][i]), openCV.cv.Round(self.imgproc.corners[1][i]))

intersection_two = (openCV.cv.Round(self.imgproc.corners[0][(i-1)%4]), openCV.cv.Round(self.imgproc.corners[1][(i-1)%4]))

openCV.line(self.img, intersection_one, intersection_two, (0,255,0), 3, 8)

#draw lines using red

openCV.circle(self.img, intersection_one, 5, (0,0,255), openCV.cv.CV_FILLED)

#here we put points in the places of the intersections using blue (0, 0, 255)= params rgb

for point in self.intersections:

openCV.circle(self.img, point, 5, (255,0,0), openCV.cv.CV_FILLED)

## show image

game_state = self.draw_board(self.state)

openCV.imshow("Initial Image", self.img)

openCV.imshow("Game State", game_state)

## waitKey used for being able to use imshow function, does not work without one

openCV.waitKey(1)

Pe imaginea inițială, denumită sugestiv cu traducerea în limba engleză, Initial Image, robotul va desena utilizând culoarea roșie liniile găsite în urma aplicării algoritmilor de detecție al intersecțiilor. Intersecțiile vor fi desenate cu culoarea albastră, conform standardului BGR, acestea având o valoare a culorii albastre de 255/255, iar valorile culorilor componente verde și roșu vor fi setate la valoarea 0. Aceasta va introduce în interiorul căsuței de vizualizare Game State starea jocului la momentul curent, în cazul în care valoarea variabilei flag este diferită de zero.

Funcția checkResult reprezintă modul în care robotul interacționează cu modulul de determinare a câștigătorului, folosindu-se de modulele de viziune și de comportamentele dezvoltate în programul Choreographe. În ceea ce privește rezultatele returnate de robot, acestea sunt patru numere, ce vor fi prelucrate ulterior, -1, 0, 1, 2. În cazul în care robotul a câștigat, rezultatul va fi 1, în cazul în care robotul a pierdut va fi 2, în cazul în care jocul a fost terminat la egalitate metoda va returna 0 iar în cazul în care jocul este încă în desfășurare, returnând -1, robotul va apela modululele de strategie și viziune.

CalculateGoalPos(self) este o metodă prin care robotul îi va comunică utilizatorului diferite decizii de punere a valorilor într-un anume pătrat folosind comunicarea cu modulul de strategie explicitat anterior. În cazul în care acesta ia decizia ca un simbol să fie poziționat pe poziția Y, unde Y este un număr întreg între unu și nouă, acesta va transmite utilizatorului vocal, folosind instrucțiunea tts.say unde dorește să pună simbolul pentru a ușura modalitatea prin care cei doi interacționează. Câmpurile în de X 0 numerotate de la unu la conform următoarei figuri.

Figura [19] 3.5 Numerotarea căsuțelor pe tabla de joc

Funcția play(self) este folosită de robot pentru a lega componentele centrale ale modulului de mișcare. Acesta este folosit de robot pentru a găsi câmpul și pentru a repoziționa robotul în cazul în care acesta nu găsește cele patru intersecții. Următorul pas este verificarea stării jocului, iar în cazul în care robotul a pierdut sau observa o egalitate, va activa comportamentele predefinite în utilitarul Choreographe. În cazul în care rezultatul verificării este “inca se joaca” acesta va verifica validitatea și apariția unor noi semne pe tablă, transmițând utilizatorului “este rândul tau”. În cazul în care robotul va decide că a câștigat jocul, pe baza algoritmului de decizie din modulul determining_the_winner, acesta va activa comportamentul ce va fi explicitat ulterior..

Metoda playMove este cea care animă robotul prin apelarea comportamentelor pentru indicarea pozițiilor în care robotul dorește să pună un semn de X sau 0. Acestea au fost încărcate și construite pe robot cu ajutorul funcției Timeline din utilitarul Choreographe, ce permite utilizatorului să inregitreze mișcări complexe, că mai apoi robotul să le poată reproduce integral, fără a fi nevoie de un control utilizând funcții implementate în limbajul Python. Fiecare poziție are un comportament diferit, robotul indincand poziția în funcție de mâna care este cel mai aproape de câmp. În cazul în care câmpul dorit este 1, 4 sau 7, Nao va indică poziția cu ajutorul mâinii stângi, iar în caz contrar, va folosi mâna dreaptă. Apelarea din modulul de mișcare se face cu ajutorul comenzii behavior.runBehavior, funcție definită în modulul comportamental amintit în capitolul 3.2.

if (temp_goal == 1):

self.behavior.runBehavior('tic_tac_toe_animations-62c8f2/point_towards_1')

Apelarea funcției se face prin transformarea unui parametru primit de funcție într-o variabila de tip întreg și prelucrarea ei cu ajutorul unei bucle de decizie. În figura următoare sunt prezentate trei cadre din modul în care robotul indică poziția 1, implementată pe un robot simulat.

Figura [20] 3.6 Indicarea pozitiei 1 folosind un robot simulat

După executarea acestei mișcări, robotul va reveni în poziția inițial folosind funcții predefinite din librăria de mișcare și librăria de posturi posibile, ajungând în poziția standInit executând următoarea secvența de cod ce setează tăria motoarelor din incheituri și viteza cu care robotul ajunge în poziție:

self.motion.setStiffnesses("Body", 1.0)

self.motion.setStiffnesses("LArm", 0.0)

self.motion.setStiffnesses("RArm", 0.0)

self.posture.goToPosture("StandInit", 0.5)

self.torso_default = self.motion.getPosition("Torso", 2, True)

self.motion.wbEnableEffectorControl(nameEffector, False)

Animațiile au fost construite într-un proiect Choreographe cu ajutorul utilitarului Timeline, ce reușește să înregistreze valorile actuatoarelor robotului în fișiere scrise în limbajul C++ sau Python sau ce pot fi vizualizate imediat în utilitarul ante-menționat. Motoarele, cele douăzeci și cinci de grade de libertate pe care robotul le are sunt salvate ca valori reale, putând fi modificate în orice moment de utilizator prin accesarea imaginii de pe ecran a robotului și modificarea valorilor actuatoarelor.

Figura [19] 3.7 Valorile actuatoarelor în momentul execuției unei mișcări.

În cazul în care în execuția programului erori fatale, determinate de imposibilitatea conectării, pierderea conexiunii către utilizator, imposibilitatea conectării la un modul, robotul secțiunea de cod denumita sugestiv cleanup ce primește parametri obiectul,si executa următoarea secventa de cod pentru a deconecta robotul de la modulul video a pentru închide comunicarea cu modulul openCV, astfel:

def cleanup(self):

self.videoProxy.unsubscribe(self.video)

openCV.destroyAllWindows()

CAPITOLUL IV

Concluzii

Obiectivul principal al acestei lucrări a fost dezvoltarea și implementarea unui joc de X și 0 pe robotul umanoid NAO, exemplificarea practică a acestuia făcându-se prin implementarea la nivel vocal și motric al modulelor de strategie. În esență, robotul trebuie să răspundă stimulilor proveniți de la mediul exterior și să comunice cu programatorul, în scopul unei mai bune înțelegeri a modului în care robotul operează și procesează diferiți stimuli de tip mișcare sau viziune. Pentru atingerea acestui scop au fost stabilite câteva ținte principale: înțelegerea algoritmilor de viziune, înțelegerea algoritmilor de mișcare și a programelor software utilizate și mai cu seamă, implementarea unui sistem ce dorește să fie utilizat în continuare în vederea optimizării și al implementării unor funcții mai stabile.

Primul obiectiv a constat în implementarea unor algoritmi de viziune pe robot, fiind implementat cu ajutorul librăriei predefinite openCV ce se dorește a fi o platformă comună de dezvoltare a aplicațiilor de viziune, de detectare a obiectelor și de găsire a câmpului de joc. În dezvoltarea acestei părți a aplicației software au fost luate în considerare diferiți factori de zgomot ce ar putea destabiliza sistemul, acesta având o multitudine de sisteme de asigurare a funcționării corecte în caz de eroare și de detecție a acestora.

Această teză prezintă etapele parcurse de autor în vederea obținerii unei aplicații ce este stabilă și reușește să integreze în structura să diferite module existente pe robot cu cele noi aduse în structura acestuia. Capitolul 1 prezintă o scurtă descriere a caracteristicilor importante ale robotului NAO din punct de vedere hardware cât și software, conform cu documentația oficială. O altă parte importantă a primului capitol este explicarea modului în care robotul efectuează mișcări complexe cu ajutorul microcontrolerelelor principale și ale celor secundare, prezente în articulații, cât și particularitățile de funcționare a acestora. Autorul a fost nevoit să înțeleagă modul în care robotul lucrează și acționează la comenzile ce îi vin din exterior.

Capitolul al doilea reprezintă o prezentare succintă a limbajelor de programare utilizate precum și a librăriilor predefinite ce au fost utilizate, având o caracteristică personalizată aplicației software. Au fost explicitate cele două librării principale utilizate, sistemul de operare și paradigma de programare, împreună cu limbajul de programare Python.

Capitolul 3 este capitolul în care autorul a expus contribuția personală, scrierea codului, implementarea anumitor algoritmi de viziune pe robot, mai exact crearea unor module în vederea găsirii modului optim de implementare a aplicației pe robotul umanoid. Autorul a luat în considerare limitările robotului și ale algoritmilor de viziune ce nu reușesc să funcționeze de cele mai multe ori la parametri doriți datorită multiplelor probleme ale robotului, precum blocarea firelor de execuție sau funcționarea incorectă a unor module existente pe robot. Feedback-ul primit de autor în cadrul aplicației practice se realizează cu ajutorul modulului vocal și al modulelor comportamentale, fiind creat special pentru această aplicație de inteligență artificială.

Etapele parcurse de autor în realizarea practică au avut că obiectiv crearea unui sistem de inteligență artificială ce ar putea să fie utilizat de orice programator în vederea înțelegerii mai bune a componentelor software ale robotului și a modului în care acesta face diferite feluri de procesare a imaginii.

Rezumatul contribuțiilor presonale ale autorului poate fi următorul:

Conceperea aplicației software;

Stabilirea unui mod de generare a feedback-ului în timp real;

Implementarea modulelor de strategie;

Implementarea modulelor de viziune pe robotul NAO;

Crearea unui modul de recunoaștere vocală;

Crearea unor comportamente specifice pentru aplicația de față.

Soluția sistemului propusă de autor este realizabilă practic, după cum se poate dovedi în aplicația practică, ce a fost realizată în scopul înțelegerii modului în care robotul acționează și lucrează sub acțiunea diferiților stimuli proveniți din mediul exterior. Sistemul descris în aceasta teză poate veni ca un punct de plecare în realizarea unor algoritmi mai complecși de joc pentru robotul NAO, putând fi adaptat și pentru alte tipuri de jocuri de tip “board”. Proiectele viitoare ce pot fi implementate pe robot, pornind de la aplicația de față, ar putea fi:

Implementarea unor jocuri mai complexe: GO, Connect-4 sau Șah;

Îmbunătățirea sistemelor de viziune și dezvoltarea lor pentru o stabilitate ridicată și o detecție mai bună;

Îmbunătățirea modului de mișcare în vederea eliminării codării fixe a comportamentelor și animării robotului;

Sintetizarea de voce pentru limbă română în vederea eliminării acestui neajuns și implementarea modulelor de recunoaștere și vorbire pe robot.

Similar Posts