Stulecie Robotów w Krakowie - Reportaż, część 3

(kontynuacja tematu o Festiwalu Robotów w Krakowie) (Stulecie Robotów - Galeria) (<< poprzednia część)

Stulecie Robotów - logo

Wrzuciłem troszkę nowych zdjęć do galerii. Pojawiły się też pierwsze filmiki, ale o tym później :). W tej części reportażu opisuję osobiste zmagania z robotem Graupnera, które okazały się wielkim sukcesem ("I'm making a note here: HUGE SUCCESS") i doprowadziły do sytuacji, w której robot z zamontowanym aparatem cyfrowym rozbijał się po sklepach w Galerii Krakowskiej :).
Starcie Trzecie - Sobota, 06.06.2009
Tym razem spędziłem w Galerii praktycznie cały dzień, intensywnie pracując nad uruchomionym już robotem Graupnera. Zaczęło się od prostych programików testowych, które pozwoliły mi ocenić funkcjonowanie silników i przycisków na płytce oraz ogólną mechanikę pracy z robotem. Mając już tę podstawową wiedzę zabrałem się za kolejny etap zabawy... Kalibracja czujników odległości Przede mną znajdowały się czujniki odległości. Z instrukcji po niemiecku dowiedziałem się tylko tyle, że działają na podczerwień i mierzą odległość w granicach 4-30cm. Jego obłsuga, podobnie jak każdego innego analogowego czujnika na tej płytce sprowadzała się do wywołania funkcji, która zwracała wartość od 0 do 255. Nigdzie w dokumentacji nie podano, jak mają się te wskazania (0..255) do odległości wyrażonej w metrach (nie była podana tzw. charakterystyki czujnika). Zmierzyłem ją więc eksperymentalnie. Po stwierdzeniu, że większe wartości występują, gdy wykryty obiekt znajduje się bliżej czujnika posłużyłem się linijką i poniższym kodem:
while(1)
{
	if(board.GetMainBoard().analog(7) > FORWARD_THRESHOLD)
	{
		board.GetMainBoard().motor(0, 255);
	}
	else
	{
		board.GetMainBoard().motorsOff();
	}
}
Jak nie trudno zauważyć, regulując parametr FORWARD_THRESHOLD zmieniałem moment, w którym uruchamiały się silniczki. Po rozciągnięciu składanej kilkumetrowej linijki mierzyłem więc czujnik ręką, badając w którym momencie zaczyna on wskazywać zadaną w FORWARD_THRESHOLD wartość. W ten sposób uzyskałem 10 punktów, układających się w mniej-więcej w eksponentę ;).
Doświadczalnie zmierzona charakterystyka czujnika odległości.
Mając te próbki mogłem już wyznaczyć przybliżoną odległość dla dowolnego wskazania czujników w mierzonym zakresie oraz spodziewane wskazanie czujników dla dowolnej podanej odległości - wszystko to oczywiście za pomocą magicznej sztuczki zwanej liniową interpolacją*, o mniej więcej w ten sposób:
//(...)
//gdzies tam w konstruktorze:
//set up ir sensor data
irSensorSettings[0] = 0; 	irSensorRanges[0] = 1.3f;
irSensorSettings[1] = 12;	irSensorRanges[1] = 0.65f;
irSensorSettings[2] = 15;	irSensorRanges[2] = 0.34f;
irSensorSettings[3] = 25;	irSensorRanges[3] = 0.30f;
irSensorSettings[4] = 30;	irSensorRanges[4] = 0.24f;
irSensorSettings[5] = 40;	irSensorRanges[5] = 0.16f;
irSensorSettings[6] = 50;	irSensorRanges[6] = 0.13f;
irSensorSettings[7] = 75;	irSensorRanges[7] = 0.08f;
irSensorSettings[8] = 100;	irSensorRanges[8] = 0.06f;
irSensorSettings[9] = 150;	irSensorRanges[9] = 0.025f;
//(...)
//magiczna funkcja:
int GetIRThresholdForDistance(float distance)
{
	for(int i = 1 ; i < IR_SENSOR_SAMPLES ; ++i)
	{
		if(distance > irSensorRanges[i])
		{
			float scale = (distance - irSensorRanges[i])/(irSensorRanges[i-1] - irSensorRanges[i]);
			return static_cast<int>(irSensorSettings[i]*(scale) + irSensorSettings[i-1]*(1.0f-scale));
		}
	}
	return 0;
}
Pomiary nie były szczególnie dokładne, ale biorąc pod uwagę, że przedział od 2 centymetrów do ponad 1.5 metra mieści się w zakresie od 0 do 150 stwierdzam, że przybliżenie jest wystarczające :). Wartości powyżej 150 nie mierzyłem częściowo z braku potrzeby, a częściowo z tego, że różnice między kolejnymi wartościami wynosiłyby teoretycznie ułamki centymetra. Uzyskane pomiary błyskawicznie zweryfikowałem w praktyce i okazały się one wystarczająco dobre, by robot mógł się poruszać w terenie. Postanowiłem więc napisać... Proste AI robota Rzeczywiście, chociaż w konstrukcji jest ono stosunkowo proste, to w praktyce przerosło (pozytywnie) moje oczekiwania. Inteligencję robota zapewniała prosta maszyna stanów**. Konstrukcję robota i skończoną maszynę stanów ilustruje poniższy schemat:
Schemat konstrukcji robota oraz jego oprogramowania.
Stan Wandering Around (ang. wędrówka po okolicy) to po prostu spacer w kółko - robot najpierw porusza się dwie sekundy przed siebie, by następnie skręcić w losowym kierunku przez pół sekundy. W międzyczasie czujniki odległości odpytywane są 10 razy na sekundę i w sytuacji, w której odległość od najbliższego obiektu spadnie poniżej ustalonej wartości następuje natychmiastowe przełączenie stanu na Evade (ang. unikaj). Stan ten obsługuje omijanie obiektów, które znalazły się zbyt blisko. Zależnie od tego, na którym czujniku wykryto obiekt wybrany zostaje odpowiedni kierunek unikania. Robot następnie wykonuje manewr. Pojedyncza iteracja tego stanu trwa około 100ms - mniej więcej tak często robot sprawdza czujniki. Jeżeli potencjalna przeszkoda znalazła się wystarczająco daleko, robot wraca do stanu Wandering Around. Jeśli natomiast czujnik wykrył jakiś obiekt ekstremalnie blisko robota, włączony zostaje stan Run Like Hell - awaryjna ucieczka. W tym stanie robot z piskiem (dosłownie - beeper) ucieka w kierunku przeciwnym do tego, z którego wykryto zagrożenie. Pojedyncza iteracja stanu trwa pół sekundy - przez ten czas robot nie sprawdza okolicy, co w ekstremalnych przypadkach może skończyć się uderzeniem w inną przeszkodę przy wycofywaniu się. Gdy zagrożenie minie, robot przełącza się w stan Wandering Around.
Robot, którego programowałem.
Robot, którego programowałem
Ten prosty system okazał się niewiarygodnie skuteczny w terenie. Pierwsze testy na arenie pokazały, że ściany i ręce małych dzieci są dla robota praktycznie niegroźne (jedyny problem sprawiały duże roboty, które miały korpus powyżej linii czujników). Niezłym wyzwaniem była nawet próba złapania robota na tej arenie, by go wyłączyć :). Jednak na prawdę ciekawie zrobiło się gdy robot, uzbrojony w aparat cyfrowy, wyruszył na spacer po Galerii. Samodzielnie poruszał się między ludźmi (ku wielkiej uciesze kilkulatków), zwiedził też kilka sklepów. Poniżej dwa filmiki - jeden z areny, drugi z jazdy po Galerii:
Na koniec jeszcze uwaga o stronie implementacyjnej - całą maszynę stanów oparłem (głównie dla szybkości pisania) o wskaźnik do funkcji :) . Nie jest to może szczególnie elastyczne, ale działa świetnie :) . Zbierając do kupy różne fragmenty kodu stworzyłem w końcu pierwszą próbę rozszerzenia API dla robotów SoccerBoard. Omawiany wyżej program, wraz z TRC SoccerBoard API 1.0 można ściągnąć z poniższego linka: (źródła) Gdzieś pomiędzy tym wszystkim obsługa zaczęła mi podsuwać informacje, że ponoć te roboty mogą być zdalnie sterowane. Pytali się mnie, czy nie byłbym im w stanie tego uruchomić... Ciąg dalszy nastąpi... Przypisy: * - Przeraża mnie fakt, który zaobserwowałem, że sporo osób zetknęło się z pojęciem interpolacji w szkole lub na studiach i kojarzą ją jedynie z 'dziwnymi wzorami' Lagrange'a i Newtona, podczas gdy nie umieją w żaden sposób wyobrazić jej sobie w praktyce. Wspomnę coś na ten temat niedługo, stay tuned :). ** - Niektórzy Czytelnicy mogą nie być zaznajomieni z koncepcją maszyny stanów skończonych (ang. Finite State Machine). Żeby nie sypać skomplikowanymi definicjami z Wikipedii, powiem krótko - prostym (i typowym) przykładem FSM jest... żarówka w pokoju. Jako obiekt ma dwa stany wewnętrzne - może być zapalona lub zgaszona. Zależnie od stanu inaczej się zachowuje (zapalona - świeci i grzeje się, zgaszona - nie świeci i stygnie). Ustalone są też przejścia między stanami - używając włącznika możemy przełączyć żarówkę ze stanu świeci się na stan nie świeci się i odwrotnie. Projektując FSM rysuje się często grafy podobne do tego, który znalazł się też w tym poście.