Difference between revisions of "Parking of robots in Remotely-operated laboratory"

From RoboWiki
Jump to: navigation, search
 
(8 intermediate revisions by the same user not shown)
Line 1: Line 1:
'''Parkovanie - vyhýbanie sa prekážkam pomocou schemas'''
+
== Detekcia uhla šípky ==
  
V Jave som začal písať program na vytváranie schémy. Keď to virtuálne otestujem, prepíšem to do C++.
+
Roboty majú na sebe farebné šípky, ktoré sú v neštandardných farbách - farby by mali byť originálne a šípky dostatočne rozmerné, aby neprišlo ku nesprávnemu určeniu prekážky, alebo časti obrázka ako šípky robota.
  
Rozpracovaný projekt na stiahnutie: http://www.st.fmph.uniba.sk/~galan2/ai/parkovanie.zip
+
=== Postup detekcie šípok ===
  
- Je možné mať rôzne rozlíšenie pracovnej matice od vonkajšieho prostredia (fotografie).
+
*1. načitanie JPG fotografie (v RGB)
 +
*2. prevod fotografie do HSV
 +
*3. odfiltrovanie farieb mimo minHSV a maxHSV hodnôt farby šípky - vzniká binárny obrázok
 +
*4. nájdenie cv contour (nerastrovo reprezentovaná krivka popisujúca jeden tvar po obvode)
 +
*5. odfiltrovanie príliš malých contours, ktoré nemôžu byť naša šípka (umožňuje mať rozmedzie minHSV-maxHSV väčšie)
 +
*6. aproximovanie do trojuholníka (a odfiltrovanie iných polygónov ako tých s troma vrcholmi)
  
- Každé políčko má vektor (uhol a silu), destinácia bude asi priťahovať každé políčko rovnakou silou, antigravitácia od priekážkok bude asi závisieť od vzdialenosti.
+
==== openCV ====
  
Prikladám test výpis uhlov časti matice, DESTIN1 je destinacia (to bude miesto, ktoré bude pred parkovacím boxom), OBSTAC1 je prekážka:
+
Táto knižnica poskytuje veľa užitočných funkcií, napr. konverziu obrázka z RGB do HSV, funkciu approxPolyDP() a pod. V ukážkach budú často funkcie z tejto knižnice.
<pre>
 
0030,00|0030,00|0028,50|0026,57|0025,23|0024,58|0024,50|0024,54|0024,27|0023,43|0022,10|0020,72|0019,81|0019,70|0020,43|
 
0030,00|0029,58|0027,20|0025,37|0024,47|0024,74|0025,84|0026,88|0027,02|0025,82|0023,44|0020,69|0018,63|0017,89|0018,44|
 
0030,00|0028,54|0026,09|0024,32|0024,05|0025,89|0028,99|0031,54|0032,28|0030,52|0026,32|0021,22|0017,43|0015,97|0016,47|
 
0030,00|0027,73|0025,11|0023,21|0023,53|0027,99|0034,28|0038,65|0040,20|0037,98|0030,68|0021,31|0015,31|0013,57|0014,48|
 
0030,00|0027,19|0024,33|0021,84|0021,55|0030,71|0041,89|0046,81|0049,30|0048,57|0036,21|0017,49|0010,80|0010,67|0012,75|
 
0030,00|0027,00|0024,00|0021,00|0018,00|DESTIN1|0048,00|0051,00|0054,00|0057,00|OBSTAC1|0003,00|0006,00|0009,00|0012,00|
 
0030,00|0027,19|0024,33|0021,84|0021,55|0030,71|0041,89|0046,81|0049,30|0048,57|0036,21|0017,49|0010,80|0010,67|0012,75|
 
0030,00|0027,73|0025,11|0023,21|0023,53|0027,99|0034,28|0038,65|0040,20|0037,98|0030,68|0021,31|0015,31|0013,57|0014,48|
 
0030,00|0028,54|0026,09|0024,32|0024,05|0025,89|0028,99|0031,54|0032,28|0030,52|0026,32|0021,22|0017,43|0015,97|0016,47|
 
0030,00|0029,58|0027,20|0025,37|0024,47|0024,74|0025,84|0026,88|0027,02|0025,82|0023,44|0020,69|0018,63|0017,89|0018,44|
 
0030,00|0030,00|0028,50|0026,57|0025,23|0024,58|0024,50|0024,54|0024,27|0023,43|0022,10|0020,72|0019,81|0019,70|0020,43|
 
  
+135,00|+141,34|+150,59|+163,08|+178,10|-165,36|-148,98|-134,26|-122,03|-112,62|-106,32|-103,36|-103,38|-105,23|-107,50|
+
==== HSV ====
+128,66|+135,32|+146,11|+160,74|+179,26|-160,09|-140,34|-123,49|-109,83|-099,16|-091,81|-088,72|-090,16|-094,54|-099,27|
 
+120,96|+127,85|+138,97|+155,55|+178,44|-155,53|-132,17|-113,84|-099,36|-087,26|-077,79|-073,46|-076,25|-083,96|-091,69|
 
+111,80|+117,74|+127,73|+144,83|+172,78|-152,66|-124,50|-105,88|-092,04|-078,74|-065,22|-057,84|-062,92|-075,64|-086,40|
 
+101,31|+104,84|+111,12|+123,87|+153,48|-152,00|-113,85|-098,51|-088,92|-076,99|-054,33|-040,59|-055,15|-074,98|-085,67|
 
+090,00|+090,00|+090,00|+090,00|+090,00|DESTIN1|-090,00|-090,00|-090,00|-090,00|OBSTAC1|-090,00|-090,00|-090,00|-090,00|
 
+078,69|+075,16|+068,88|+056,13|+026,52|-028,00|-066,15|-081,49|-091,08|-103,01|-125,67|-139,41|-124,85|-105,02|-094,33|
 
+068,20|+062,26|+052,27|+035,17|+007,22|-027,34|-055,50|-074,12|-087,96|-101,26|-114,78|-122,16|-117,08|-104,36|-093,60|
 
+059,04|+052,15|+041,03|+024,45|+001,56|-024,47|-047,83|-066,16|-080,64|-092,74|-102,21|-106,54|-103,75|-096,04|-088,31|
 
+051,34|+044,68|+033,89|+019,26|+000,74|-019,91|-039,66|-056,51|-070,17|-080,84|-088,19|-091,28|-089,84|-085,46|-080,73|
 
+045,00|+038,66|+029,41|+016,92|+001,90|-014,64|-031,02|-045,74|-057,97|-067,38|-073,68|-076,64|-076,62|-074,77|-072,50|
 
</pre>
 
  
Červená bodka je destinácia, modrá prekážka. Je možné mať veľa týchto bodov.
+
Tento farebný model (Hue, Saturation, Value(Brightness) - Tón, Sýtosť, Hodnota(Jas)) bol zvolený preto, lebo jednotlivé šípky sa medzi sebou líšia najmä tónom farby a svetelné podmienky je možné popísať sýtosťou a jasom. Pri použití modelu RGB je teoreticky možné dosiahnúť rovnako dobré výsledky (medzi RGB a HSV sa dá konvertovať oboma smermi), no testovanie by bolo omnoho pomalšie.
 +
<source lang="cpp">
 +
// orange
 +
Scalar MIN_ORANGE_HSV = getHSV(18,50,55); // 0..359, 0..100, 0..100
 +
Scalar MAX_ORANGE_HSV = getHSV(40,86,66);
  
Bude potrebné špecifikovať polomer prekážky (nejaký offset zaciatku posobenia gravitacie), keďže bod je iba bod. Alebo prekážka bude pokrytá viacerými bodmi (môže byť náročné na výpočet).
+
// purple
 +
Scalar MIN_PURPLE_HSV = getHSV(315,35,40);
 +
Scalar MAX_PURPLE_HSV = getHSV(359,70,54);
  
Pozn: obr. reprezentuje iné dáta ako vyššie
+
Mat imageRGB;
[[Image:Viz2.png]]
+
Mat imageHSV;
 +
cvtColor(imageRGB, imageHSV, CV_BGR2HSV);
 +
</source>
 +
 
 +
==== Filtrovanie farieb ====
 +
 
 +
minHSV a maxHSV sú Scalar(int, int, int) s tromi hodnotami (HSV). Dôležité je spomenúť, že openCV má formát/rozmedzia pre HSV 0..179, 0..255, 0..255. Moja funkcia getHSV() prevádza štandardnejšie rozmedzia 0..359, 0..100, 0..100 (používané aj grafickými editormi) do openCV formátu.
 +
<source lang="cpp">
 +
Mat imageHSV;
 +
Mat filtered;
 +
inRange(imageHSV, minHSV, maxHSV, filtered);
 +
</source>
 +
 
 +
==== Detekcia contour ====
 +
 
 +
<source lang="cpp">
 +
vector<vector<Point> > contours;
 +
vector<Vec4i> hierarchy;
 +
findContours(filtered.clone(), contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
 +
</source>
 +
 
 +
Viac sa je možné dočítať v oficiálnej openCV dokumentácií s tutoriálom: http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/find_contours/find_contours.html
 +
 
 +
Spomenutý tutorál využíva Canny operator, ktorý detekuje okraje objektov. V tomto projekte som tiež experimentoval s Canny operatorom, no výsledky neboli uspokojivé. Na obrázku nižšie je obrázok po aplikovaní Canny operatora a obrázok po odfiltrovaní nežiadúcich HSV farieb cez funkciu inRange().
 +
 
 +
==== Aproximácia trojuholníka ====
 +
 
 +
<source lang="cpp">
 +
vector<Point> getTriangleVertices(const vector< vector<Point> >& contours, int minTriangleArea=0)
 +
{
 +
vector<Point> approxTriangle;
 +
vector<Point> allTriangleVertices;
 +
for(size_t i = 0; i < contours.size(); i++) // contours[i] je jedna kontúra - krivka obkreslujúca nejaký tvar
 +
{
 +
if (fabs(contourArea(contours[i])) < minTriangleArea) continue; // odfiltrovanie tvarov s príliš malou plochou, náš trojuholník ma plochu cca 450-480px
 +
approxPolyDP(contours[i], approxTriangle, arcLength(Mat(contours[i]), true)*0.05, true); // viac tu: http://opencv.willowgarage.com/documentation/cpp/structural_analysis_and_shape_descriptors.html#cv-approxpolydp
 +
if(approxTriangle.size() == 3) // chceme iba trojuholníky
 +
{
 +
copy(approxTriangle.begin(), approxTriangle.end(), back_inserter(allTriangleVertices));
 +
}
 +
}
 +
return allTriangleVertices;
 +
}
 +
</source>
 +
 
 +
=== Určenie uhlu ===
 +
 
 +
<source lang="cpp">
 +
double getRobotAngle(vector<Point>& triangle);
 +
</source>
 +
Zo získaného trojuholníka zavolaním vlastnej funkcie dostaneme uhol trojuholníka/šípky v radiánoch.
 +
 
 +
Algoritmus funguje tak, že sa zistí najdlhšia hrana v trojuholníku a zistí sa jej uhol. Výsledný uhol šípky získame otočením ešte o 90°. Keďže som výpočty neurobil dosť matematicky "univerzálne", musel som si napísať pomocnú funkciu, którá zistí vzájomnú polohu tretieho bodu a priamky/hrany a opraviť tak prípadný preklopený uhol o 180°.
 +
 
 +
=== Výsledok ===
 +
 
 +
Ešte jedna funkcia, void drawShape(vector<Point> vector, Mat& img, String label); a máme to vizualizované:
 +
 
 +
[[Image:Anglerecog1.png]]
 +
 
 +
[[Image:Anglerecog2.png]]
 +
 
 +
Pri nasvietení lampou bolo však potrebné urobiť ďalšie úpravy minHSV a maxHSV:
 +
 
 +
[[Image:Anglerecog3.png]]
 +
 
 +
Potom bol výsledok lepší, aj keď vrcholy ovplyvnené neboli:
 +
 
 +
[[Image:Anglerecog3b.png]]
 +
 
 +
Výsledné uhly nie sú najpresnejšie (nepresnosť je väčšia ako 0,5°), no pri takom algoritme parkovania, ktorý priebežne zisťuje uhol robota, to nemusí byť problém.
 +
 
 +
Na pôvodný, kvalitný a širokouhlý obraz vychádzajúci z kamery je aplikovaná korekcia skreslenia, čo prispieva ku strate ostrosti hrán šípky. Riešením na zlepšenie môže byť zostrenie hrán, nastevenie väčšej citlivosti HSV filtra, alebo presná/strojová analýza farieb šipky (terajšie hodnoty minHSV a maxHSV boli odmerané ručne cez grafický editor).
 +
 
 +
== Parkovanie robota ==
 +
 
 +
Parkovanie robota nebolo celkovo otestované (výpočty uhlov boli otestované vizuálne v Delphi aplikácii) a popisuje iba jednoduchý algoritmus zaparkovania.
 +
 
 +
*1. príchod pred nabíjací box
 +
*2. doparkovanie do nabíjacieho boxu
 +
 
 +
=== Príchod pred nabíjací box ===
 +
<source lang="cpp">
 +
// pozicia pred nabijacim boxom
 +
double destX = 175;
 +
double destY = 160;
 +
 
 +
double required_distance = 5; // odchylka vzdialenosti ciela a robota
 +
double fd_step = 20; // krok dopredu na kazdu iteraciu
 +
double wait_time = 200; // cakanie pri kazdej iteracii
 +
int counter = 0;
 +
int max_counter = 50000; // aby sa navzdy nezacyklil
 +
 
 +
// dostanie sa do destX, destY
 +
 
 +
while(1){
 +
cv::Mat src_rgb = loadImage("img.jpg");
 +
 
 +
if (!src_rgb.data){
 +
  cout << "No image\n";
 +
  return 2;
 +
}
 +
 
 +
vector<Point> purple_arrow = findArrow(src_rgb, MIN_PURPLE_HSV, MAX_PURPLE_HSV, 300); // posledný parameter je min obsah šípky
 +
 
 +
if (purple_arrow.size()!=3) continue; // ak sa nedetekoval žiadny trojuholník
 +
 
 +
double robot_angle_deg = getClockAngle(radToDeg(getRobotAngle(purple_arrow))); // uhol robota v rozmedzí 0..359
 +
 
 +
Point robot_position = getCenterPoint(getLongestTriangleEdge(purple_arrow));
 +
 
 +
double direction_deg = getClockAngle(radToDeg(PI-atan2((destX-robot_position.x),(destY-robot_position.y)))); // uhol od robota ku cielu, 0..359
 +
 
 +
double difference_deg = direction_deg - robot_angle_deg; // rozdiel uhlov, kladny znamena rl, zaporny lt
  
'''Rospoznávanie pozície robotov'''
+
if (difference_deg>180) difference_deg = -(180-(difference_deg-180)); // aby sa otacal cez kratsi uhol
- Je naprogramované (rozpoznávanie kruhov)
 
  
'''Rozpoznávanie orientácie šípky'''
+
robot_rt(r, difference_deg*rotation_step);
  
Princíp: ANN, pri učení zrejme 360 dvojíc vstup(obr)-výstup(uhol v °) - (kolko podobnych obr treba na dobre natrenovanie kazdeho stupna?)
+
robot_fd(r, fd_step);
  
- asi bude potrebné odfiltrovať zbytočné dáta (farba, neostré hrany)
+
sleep(wait_time);
  
Knižnica FANN s príkladom XOR: http://leenissen.dk/fann/wp/help/getting-started/
+
counter++; if (counter>=max_counter) break; // proti zacykleniu
  
Príklad pattern recognition: http://www.doc.ic.ac.uk/~nd/surprise_96/journal/vol4/cs11/report.html#Pattern%20Recognition%20-%20an%20example
+
if (dist(destX,destY,robot_position.x,robot_position.y) <= required_distance) break; // je v cieli s required_distance presnostou
 +
}
 +
</source>
  
Predtým, ako sa pustím do programovania v C++, Vyskúšal som Java Neural Network Framework, ktorý je veľmi jednoduchý na používanie: http://neuroph.sourceforge.net/image_recognition.html
+
=== Doparkovanie robota ===
 +
Doparkovanie robota je veľmi podobné, stačí zvoliť destY = 55; (stred boxu) a opakovať jednu iteráciu cyklu spomenutého vyššie.
  
Testovanie 90° šípky pro trénovaní s viac-menenej defaultnými parametrami vyšlo takto:
+
== Iná práca ==
  
Stupne : output
+
=== Motor schema ===
  
180 : 0,0062
+
Pôvodne zámer ako zaparkovať robota do nabíjacieho boxu bol využiť motor schema move-to-goal a avoid-obstacles. Zdá sa to ako dobrý a univerzálny spôsob pohybu robotov. V Jave som vytvoril triedy (aj ako Applet), ktorý simuluje 2D svet (napr. obrázok) s objektami rôznej, kladnej alebo zápornej, gravitácie. Schéma (reprezentácia sveta/obrázka) pozná v každom svojom bode silu pôsobiacu na tento bod zo všetkých objektov s gravitáciou. Sily sa skladajú štandardne vektorovo. Je možné ľahko pridávať objekty s gravitáciou, čím sa automaticky aktualizuje celá schéma. Táto aplikácia má nastaviteľnú presnosť, v najlepšom prípade 1px obrázka = 1bod schémy (teda pre každý pixel bude počítaná gravitácia). Je využiteľná pri move-to-goal a avoid-obstacles pohyboch takým spôsobom, že na miesto ciela sa vloží objekt s kladnou gravitáciou a na miesto prekážok sa vloží objekt so zápornou gravitáciou. Podľa výslednej schémy by bolo možné robota usmerňovať.
  
135 : 0,0089
+
<source lang="java">
 +
s = new Schema(resolutionX, resolutionY, imgWidht, imgdHeight);
 +
s.addObject(new Objekt(posX1, posY1, gravity, Double.MAX_VALUE, "DESTIN1")); // pritazlivy objekt s gravitaciou gravity a s nekonecnym dosahom
 +
s.addObject(new Objekt(posX2, posY2, -gravity, gravityRadius, "OBSTAC1")); // odpudivy objekt so zapornou gravitaciou a s gravityRadius dosahom (slabne so vzdialenostou)
 +
</source>
  
0 : 0,0341
+
[[Image:Viz2.png]]
  
45 : 0,0332
+
=== Artificial neural network ===
  
225 : 0,01
+
Pôvodne sa natočenie šípok malo detekovať neurónovou sieťou, s využitím knižnice FANN. http://leenissen.dk/fann/wp/help/getting-started/
  
90 : 0,8841
+
Príklad pattern recognition: http://www.doc.ic.ac.uk/~nd/surprise_96/journal/vol4/cs11/report.html#Pattern%20Recognition%20-%20an%20example
  
315 : 0,017
+
Vyskúšal som NeurophStudio (http://neuroph.sourceforge.net/image_recognition.html) postavené na Eclipse. Dovoluje jednoducho a bez programovania trénovať a testovať neurónovú sieť.
  
270 : 0,068
+
Testovanie 90° šípky pro trénovaní s viac-menenej defaultnými parametrami vyšlo takto:  
  
 +
<pre>Stupne : output
 +
180 : 0,0062
 +
135 : 0,0089
 +
0 : 0,0341
 +
45 : 0,0332
 +
225 : 0,01
 +
90 : 0,8841
 +
315 : 0,017
 +
270 : 0,068</pre>
  
 
[[Image:JavaNeuralNetworkimg1.png]]
 
[[Image:JavaNeuralNetworkimg1.png]]
  
 
[[Image:JavaNeuralNetworkimg2.png]]
 
[[Image:JavaNeuralNetworkimg2.png]]
 +
 +
== Download ==
 +
[[Media:Proj_parkovanie_rozpoznavanie.zip|Detekcia uhla šípky]]
 +
 +
[[Media:Proj_parkovanie_parkovanie.cpp|Parkovanie robota]]
 +
 +
[[Media:Proj_parkovanie_java_schema.zip|Schema v Jave]]

Latest revision as of 15:48, 28 June 2013

Detekcia uhla šípky

Roboty majú na sebe farebné šípky, ktoré sú v neštandardných farbách - farby by mali byť originálne a šípky dostatočne rozmerné, aby neprišlo ku nesprávnemu určeniu prekážky, alebo časti obrázka ako šípky robota.

Postup detekcie šípok

  • 1. načitanie JPG fotografie (v RGB)
  • 2. prevod fotografie do HSV
  • 3. odfiltrovanie farieb mimo minHSV a maxHSV hodnôt farby šípky - vzniká binárny obrázok
  • 4. nájdenie cv contour (nerastrovo reprezentovaná krivka popisujúca jeden tvar po obvode)
  • 5. odfiltrovanie príliš malých contours, ktoré nemôžu byť naša šípka (umožňuje mať rozmedzie minHSV-maxHSV väčšie)
  • 6. aproximovanie do trojuholníka (a odfiltrovanie iných polygónov ako tých s troma vrcholmi)

openCV

Táto knižnica poskytuje veľa užitočných funkcií, napr. konverziu obrázka z RGB do HSV, funkciu approxPolyDP() a pod. V ukážkach budú často funkcie z tejto knižnice.

HSV

Tento farebný model (Hue, Saturation, Value(Brightness) - Tón, Sýtosť, Hodnota(Jas)) bol zvolený preto, lebo jednotlivé šípky sa medzi sebou líšia najmä tónom farby a svetelné podmienky je možné popísať sýtosťou a jasom. Pri použití modelu RGB je teoreticky možné dosiahnúť rovnako dobré výsledky (medzi RGB a HSV sa dá konvertovať oboma smermi), no testovanie by bolo omnoho pomalšie.

// orange
Scalar MIN_ORANGE_HSV = getHSV(18,50,55); // 0..359, 0..100, 0..100
Scalar MAX_ORANGE_HSV = getHSV(40,86,66);

// purple
Scalar MIN_PURPLE_HSV = getHSV(315,35,40);
Scalar MAX_PURPLE_HSV = getHSV(359,70,54);

Mat imageRGB;
Mat imageHSV;
cvtColor(imageRGB, imageHSV, CV_BGR2HSV);

Filtrovanie farieb

minHSV a maxHSV sú Scalar(int, int, int) s tromi hodnotami (HSV). Dôležité je spomenúť, že openCV má formát/rozmedzia pre HSV 0..179, 0..255, 0..255. Moja funkcia getHSV() prevádza štandardnejšie rozmedzia 0..359, 0..100, 0..100 (používané aj grafickými editormi) do openCV formátu.

Mat imageHSV;
Mat filtered;
inRange(imageHSV, minHSV, maxHSV, filtered);

Detekcia contour

vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(filtered.clone(), contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

Viac sa je možné dočítať v oficiálnej openCV dokumentácií s tutoriálom: http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/find_contours/find_contours.html

Spomenutý tutorál využíva Canny operator, ktorý detekuje okraje objektov. V tomto projekte som tiež experimentoval s Canny operatorom, no výsledky neboli uspokojivé. Na obrázku nižšie je obrázok po aplikovaní Canny operatora a obrázok po odfiltrovaní nežiadúcich HSV farieb cez funkciu inRange().

Aproximácia trojuholníka

vector<Point> getTriangleVertices(const vector< vector<Point> >& contours, int minTriangleArea=0)
{
	vector<Point> approxTriangle;
	vector<Point> allTriangleVertices;
	for(size_t i = 0; i < contours.size(); i++) // contours[i] je jedna kontúra - krivka obkreslujúca nejaký tvar
	{
		if (fabs(contourArea(contours[i])) < minTriangleArea) continue; // odfiltrovanie tvarov s príliš malou plochou, náš trojuholník ma plochu cca 450-480px
		approxPolyDP(contours[i], approxTriangle, arcLength(Mat(contours[i]), true)*0.05, true); // viac tu: http://opencv.willowgarage.com/documentation/cpp/structural_analysis_and_shape_descriptors.html#cv-approxpolydp
		if(approxTriangle.size() == 3) // chceme iba trojuholníky
		{
			copy(approxTriangle.begin(), approxTriangle.end(), back_inserter(allTriangleVertices));
		}
	}
	return allTriangleVertices;
}

Určenie uhlu

double getRobotAngle(vector<Point>& triangle);

Zo získaného trojuholníka zavolaním vlastnej funkcie dostaneme uhol trojuholníka/šípky v radiánoch.

Algoritmus funguje tak, že sa zistí najdlhšia hrana v trojuholníku a zistí sa jej uhol. Výsledný uhol šípky získame otočením ešte o 90°. Keďže som výpočty neurobil dosť matematicky "univerzálne", musel som si napísať pomocnú funkciu, którá zistí vzájomnú polohu tretieho bodu a priamky/hrany a opraviť tak prípadný preklopený uhol o 180°.

Výsledok

Ešte jedna funkcia, void drawShape(vector<Point> vector, Mat& img, String label); a máme to vizualizované:

Anglerecog1.png

Anglerecog2.png

Pri nasvietení lampou bolo však potrebné urobiť ďalšie úpravy minHSV a maxHSV:

Anglerecog3.png

Potom bol výsledok lepší, aj keď vrcholy ovplyvnené neboli:

Anglerecog3b.png

Výsledné uhly nie sú najpresnejšie (nepresnosť je väčšia ako 0,5°), no pri takom algoritme parkovania, ktorý priebežne zisťuje uhol robota, to nemusí byť problém.

Na pôvodný, kvalitný a širokouhlý obraz vychádzajúci z kamery je aplikovaná korekcia skreslenia, čo prispieva ku strate ostrosti hrán šípky. Riešením na zlepšenie môže byť zostrenie hrán, nastevenie väčšej citlivosti HSV filtra, alebo presná/strojová analýza farieb šipky (terajšie hodnoty minHSV a maxHSV boli odmerané ručne cez grafický editor).

Parkovanie robota

Parkovanie robota nebolo celkovo otestované (výpočty uhlov boli otestované vizuálne v Delphi aplikácii) a popisuje iba jednoduchý algoritmus zaparkovania.

  • 1. príchod pred nabíjací box
  • 2. doparkovanie do nabíjacieho boxu

Príchod pred nabíjací box

// pozicia pred nabijacim boxom
double destX = 175;
double destY = 160;

double required_distance = 5; // odchylka vzdialenosti ciela a robota
double fd_step = 20; // krok dopredu na kazdu iteraciu
double wait_time = 200; // cakanie pri kazdej iteracii
int counter = 0;
int max_counter = 50000; // aby sa navzdy nezacyklil

// dostanie sa do destX, destY

while(1){
	cv::Mat src_rgb = loadImage("img.jpg");

	if (!src_rgb.data){
	  cout << "No image\n";
	  return 2;
	}

	vector<Point> purple_arrow = findArrow(src_rgb, MIN_PURPLE_HSV, MAX_PURPLE_HSV, 300); // posledný parameter je min obsah šípky

	if (purple_arrow.size()!=3) continue; // ak sa nedetekoval žiadny trojuholník

	double robot_angle_deg = getClockAngle(radToDeg(getRobotAngle(purple_arrow))); // uhol robota v rozmedzí 0..359

	Point robot_position = getCenterPoint(getLongestTriangleEdge(purple_arrow));

	double direction_deg = getClockAngle(radToDeg(PI-atan2((destX-robot_position.x),(destY-robot_position.y)))); // uhol od robota ku cielu, 0..359

	double difference_deg = direction_deg - robot_angle_deg; // rozdiel uhlov, kladny znamena rl, zaporny lt

	if (difference_deg>180) difference_deg = -(180-(difference_deg-180)); // aby sa otacal cez kratsi uhol

	robot_rt(r, difference_deg*rotation_step);

	robot_fd(r, fd_step);

	sleep(wait_time);

	counter++; if (counter>=max_counter) break; // proti zacykleniu

	if (dist(destX,destY,robot_position.x,robot_position.y) <= required_distance) break; // je v cieli s required_distance presnostou
}

Doparkovanie robota

Doparkovanie robota je veľmi podobné, stačí zvoliť destY = 55; (stred boxu) a opakovať jednu iteráciu cyklu spomenutého vyššie.

Iná práca

Motor schema

Pôvodne zámer ako zaparkovať robota do nabíjacieho boxu bol využiť motor schema move-to-goal a avoid-obstacles. Zdá sa to ako dobrý a univerzálny spôsob pohybu robotov. V Jave som vytvoril triedy (aj ako Applet), ktorý simuluje 2D svet (napr. obrázok) s objektami rôznej, kladnej alebo zápornej, gravitácie. Schéma (reprezentácia sveta/obrázka) pozná v každom svojom bode silu pôsobiacu na tento bod zo všetkých objektov s gravitáciou. Sily sa skladajú štandardne vektorovo. Je možné ľahko pridávať objekty s gravitáciou, čím sa automaticky aktualizuje celá schéma. Táto aplikácia má nastaviteľnú presnosť, v najlepšom prípade 1px obrázka = 1bod schémy (teda pre každý pixel bude počítaná gravitácia). Je využiteľná pri move-to-goal a avoid-obstacles pohyboch takým spôsobom, že na miesto ciela sa vloží objekt s kladnou gravitáciou a na miesto prekážok sa vloží objekt so zápornou gravitáciou. Podľa výslednej schémy by bolo možné robota usmerňovať.

s = new Schema(resolutionX, resolutionY, imgWidht, imgdHeight);
s.addObject(new Objekt(posX1, posY1, gravity, Double.MAX_VALUE, "DESTIN1")); // pritazlivy objekt s gravitaciou gravity a s nekonecnym dosahom
s.addObject(new Objekt(posX2, posY2, -gravity, gravityRadius, "OBSTAC1")); // odpudivy objekt so zapornou gravitaciou a s gravityRadius dosahom (slabne so vzdialenostou)

Viz2.png

Artificial neural network

Pôvodne sa natočenie šípok malo detekovať neurónovou sieťou, s využitím knižnice FANN. http://leenissen.dk/fann/wp/help/getting-started/

Príklad pattern recognition: http://www.doc.ic.ac.uk/~nd/surprise_96/journal/vol4/cs11/report.html#Pattern%20Recognition%20-%20an%20example

Vyskúšal som NeurophStudio (http://neuroph.sourceforge.net/image_recognition.html) postavené na Eclipse. Dovoluje jednoducho a bez programovania trénovať a testovať neurónovú sieť.

Testovanie 90° šípky pro trénovaní s viac-menenej defaultnými parametrami vyšlo takto:

Stupne : output
180 : 0,0062
135 : 0,0089
0 : 0,0341
45 : 0,0332
225 : 0,01
90 : 0,8841
315 : 0,017
270 : 0,068

JavaNeuralNetworkimg1.png

JavaNeuralNetworkimg2.png

Download

Detekcia uhla šípky

Parkovanie robota

Schema v Jave