Oryginalna strona colobot.cba.pl umarła, gdy cba.pl przestało oferować darmowy hosting. To jest statyczny mirror, pobrany w 2018. ~krzys_h
 Polski Portal COLOBOTa - COLOBOT Polish Portal
Forum - Polski Portal COLOBOTa
Strona głównaStrona główna UżytkownicyUżytkownicy GrupyGrupy StatystykiStatystyki


Poprzedni temat «» Następny temat
Sterowanie silnikami [Poradnik?]
Autor Wiadomość
FE4R 


Twoja ulubiona misja: Saari - Inwazja
Pomógł: 4 razy
Wiek: 24
Dołączył: 21 Paź 2009
Posty: 144
  Wysłany: 28-12-2009, 01:26   Sterowanie silnikami [Poradnik?]

Hej hej.

Chciałem stworzyć coś na rodzaj poradnika, który będzie opisywał najpopularniejsze sposoby sterowania silnikami. Część z nich jest powszechnie znana z Ćwiczeń. Chciałem opisać ich działanie i zastosowanie. Inne to mój pomysł.
'Poradnik' obejmuje oba typy silników.


1. Wstęp

a) Zwykły silnik
Zwykły silnik jest zamontowany w podstawie robota. Został on podzielony na dwa - lewy oraz prawy. Pozwala on poruszać się robotowi w płaszczyźnie xy. Sterując mocą obu części silnika, jesteśmy w stanie ustawić prędkość oraz kierunek robota.
Funkcja motor(); steruje mocą zwykłego silnika.

b) Silnik odrzutowy
Odrzutowy silnik jest zamontowany jedynie w robotach latających, obok zwykłego. Pozwala on poruszać się robotowi w górę oraz w dół, zmieniając jego pozycję z oraz altitude. Silnikowi odrzutowemu towarzyszy również przegrzanie, kryjące się pod zmienną temperature. Odrzutem silnika można sterować tylko jedną liczbą w funkcji jet();.

2. Podstawy funkcji motor(); oraz jet();

a) motor();
Według definicji z SatCom, funkcja motor(); musi być wywołana z dwiema liczbami. Pierwsza z nich to moc lewego silnika, ustawiana dzięki liczbie w zakresie od -1 do 1. Pełna moc to '1', a brak ruchu to 0. Ujemna liczba oznacza ruch do tyłu. Druga liczba, po przecinku, to moc prawego silnika. Analogicznie, steruje się nim tak samo jak lewym. Odpowiednio ustawiając moc obu silników możemy uzyskać różne efekty. Przejdzmy jednak do prostych przykładów:

Kod:
motor(0,0);

oznacza, iż silniki robota się nie poruszają. Nie będzie on wykonywał żadnego ruchu, jeśli nie działa na niego żadna inna siła.
Kod:
motor(1, 1);

Tutaj robot ustawia moc silników na 'max' dla obu stron silnika. Dzięki temu porusza się on ze swoją maksymalną prędkością do przodu(każdy obiekt ma inną prędkość). Każda próba ustawienia wartości większej niż 1 nie zwiększy prędkości. Dodam tylko, że funkcje move();, goto(); oraz ręczne sterowanie robotem zawsze ustawi prędkość któregoś z silników na 1.
Kod:
motor(0.5, 0.5);

Oznacza, że robot będzie poruszał się z połową swojej maksymalnej prędkości. Możliwe jest ustawienie ułamkowej prędkości, dzięki wstawieniu liczby typu float, czyli z dwoma / jednym miejscem po przecinku. Zastosowanie tego opiszę w następnych działach.
Kod:
motor(0, 1);

Będzie oznaczać, iż lewy silnik nie wykonuje ruchu, natomiast prawy jest ustawiony na max. Ponieważ sam prawy silnik nie jest w stanie poruszyć robota do przodu, będzie się on kręcił w kółko wokół środka okręgu, którym jest lewy bok robota. Aby sprawić, żeby robot obracał się wokół własnej osi, trzeba ustawić taką samą wartość w przeciwnym silniku, tyle że... ujemną. W ten sposób oba silniki będą się uzupełniać.
To tyle z podstaw. Bardziej zaawansowane sterowanie tym silnikiem opiszę w następnym dziale.

b) jet();
Sterowanie odrzutem silnika w robotach latających jest wywoływane przez jedną liczbę w funkcji jet();, również dzięki liczbie w zakresie -1, 1.
Dzięki temu robot może się wznosić oraz opadać. Funkcja goto();, oprócz motor(1,1); używa również jet(1);. Maksymalna wysokość lotu jest uzależniona od misji i wysokości bezwzględnej. Oto kilka przykładów podstawowego wykorzystania funkcji jet();:
Kod:
jet(1);

To najszybsze wznoszenie się.
Kod:
jet(0);

Oznacza, że silnik odrzutowy wstrzyma poprzednią akcję wznoszenia / opadania. Dzięki temu robot może zawisnąć w powietrzu na określonej wysokości.
Kod:
jet(-1);

Analogowo - najszybsze opadanie.

c) Ograniczenia
Oczywiście każda funkcja ma ograniczenia. Nie jest możliwe sterowanie jakimkolwiek silnikiem bez użycia pętli sterującej użyciem obu w/w funkcji. Nie da się wpisać:
Kod:
extern void object::Funkcja()
{
motor(1,1);
}

I liczyć na efekty. Jeśli robot ma się poruszać do przodu, motor(); musi zostać umieszczone w pętli, np. while(true). To samo tyczy się funkcji jet();.

3. Zaawansowane wykorzystanie funkcji motor(); oraz jet();

Ten dział chciałem poprowadzić trochę inaczej niż poprzedni, pokazując rozwiązania z Ćwiczeń Colobota i ich zastosowania jak również pomóc zrozumieć ich działanie.

a) Spowalniacz
Spowalniacz to misja, gdzie robot ma przebyć 25 metrów, a następnie zacząć zwalniać, aby nie wpaść na miny zaraz za końcową platformą. Aby to zrobić, należy policzyć kilka odległości i dostosować funkcję motor(); do odległości od platformy. Wystarczy wykorzystać proste uśrednienie:
Kod:
point start = position;

while(true)
{
object cel = radar(GoalArea);
float len1 = distance(cel.position, start);
float len2 = distance(position, start);

motor( (len1 - len2) / 2, (len1 - len2) / 2);
}

Wydaje się skomplikowane, ale takie nie jest. Na początku zapisujemy naszą pozycję startową. len1 to niezmienna liczba, w tej misji wynosząca 25 metrów. len2 na początku wynosi 0, dlatego działanie (25 - 0) / 2 będzie wynosić 12.5. Tak jak napisałem wcześniej - wartosć większa niż 1 nie zwiększy prędkości, czyli robot będzie się poruszał z maksymalną prędkością. Dopiero przy końcu, na 23 metrze, działanie (25 - 23) / 2; będzie wynosić dokładnie 1. Każda odległość mniejsza od 23 metrów spowoduje, że moc silników będzie maleć. Ponieważ roboty naziemne mają bardzo niski dystans hamowania, robot nie wpadnie na miny zaraz za podestem. Ba! Zatrzyma się dokładnie na pozycji platformy.
Wyrażenie (len1 - len2) można dostosować. W tym programie jest one dzielone przez 2, jednak można je podzielić przez większą liczbę, aby otrzymać spowalnianie już na wcześniejszym odcinku.
Program jest bardzo skuteczny dla osłaniacza oraz by się dostać dokładnie na jakąś pozycję, gdyż motor(1,1); może sprawić, że robot nie będzie miał czasu, aby pomierzyć dystans do celu i po prostu go minie. Natomiast motor( (len1 - len2) / 2, (len1 - len2) / 2); zapewni odpowiednią siłę silników.

Roboty naziemne będą się świetnie spisywały z tym programem, gdyż tarcie szybko je zatrzyma. Roboty latające mają większą bezwładność, a ich dystans zwalniania do całkowitego zatrzymania jest znacznie większy (około 15 metrów), więc trzeba zastosować podzielenie len1 - len2 na np. 10.

b) Wykorzystanie 'spowalniacza' w silniku odrzutowym
Jest drugie zastosowanie metody tutaj opisanej dla silnika odrzutowego.
Można użyć powyższego kodu (lekko zmodyfikowanego), aby ustalić daną wysokość lotu robota. Może to wyglądać mniej więcej tak:
Kod:
 extern void object::Jet()
{
    while(true)
    {
        float alt1 = 20;
        float alt2 = position.z - topo(position);
       
        jet( (alt1 - alt2) / 2 );
    }
}

Zmienną alt1 oznaczamy żądaną wysokość lotu, którą robot ma utrzymywać. Zmienna alt2 to po prostu wysokość lotu. Nie chcę tłumaczyć tego po raz kolejny, lecz działa to na podobnej zasadzie jak program Spowalniacz. Tak jak powiedziałem roboty latające mają większą bezwładność, więc robot za pierwszym razem wzniesie się trochę wyżej, potem się obniży, aż do poziomu równo 20 metrów nad poziomem gruntu.
Zastosowanie to skrócenie programu do dział latających. Zamiast męczyć się z if(position.z - topo(position) < 12) jet(1); to wystarczy ustawić daną wysokość w programie i można się cieszyć skutkiem. W przypadku zwykłych lotów polecam dzielić wartość (alt1 - alt2) na 10, by szybko wyrównać poziom. Dla zestrzeliwania os latającym działem, polecam zostać przy dzieleniu przez 2, aby obniżanie i wznoszenie było bardziej dynamiczne. Oto przykład programu dostosowanego do podążania za osą:
Kod:
object cel = radar(AlienWasp);
float alt1 = cel.altitude;
float alt2 = position.z - topo(position);

jet((alt1 - alt2) / 2);

W tradycyjnym programie trzeba było ustawić warunek if(cel.altitude > altitude) jet(1);. Tutaj wystarczy jedna funkcja kontrolująca wysokość lotu.

c) Szalony robot!
To Ćwiczenie jest dość niepozorne, jednak jest niesamowicie ważne w aspekcie poznania możliwości funkcji motor();. Tutaj uczymy się, jak nakierować robot na daną pozycję, dzięki wykorzystaniu mocy silników w zależności od kąta obrotu w stronę obiektu. To również jedno z moich ulubionych zagadnień.
Zacznijmy jednak nie od praktyki, a od teorii. Być może pomocny będzie ten obrazek.


Kąty w Colobocie podzielone są na dwie części. Jeśli kąt oznacza obrót w lewo - jest to kąt o wartości dodatniej. Jeśli jednak jest to kąt obracający w prawo, jest on ujemny. Dlatego nie ma kąta powyżej 180 stopni. Zakres kątów to -180(prawo) do 180(lewo).
Z obrazka możemy wywnioskować, jaki jest kąt potrzebny do obrotu robota, aby był skierowany na wprost celu. W tradycyjnych programach wykorzystywane jest po prostu turn( direction ( cel1.position));.
Są ludzie, którzy twierdzą, iż jest to idealna forma zwrotu robota w żądaną stronę. Nie jestem jednym z nich. Za pomocą pewnego kodu można obrócić robota funkcją motor();.
Zacznijmy od prostej matematyki:
Cytat:
-125 / 90 = -1.39
-35 / 90 = -0.38
40 / 90 = 0.44
120 / 90 = 1.33
170 / 90 = 1.88
dla ostatniego przykładu 180 / 90 = 2


Podzieliłem kąty potrzebne do obrotu na 90, co daje nam głównie liczby po przecinku. I o takie nam chodzi, gdy chcemy się obrócić w danym kierunku. Teraz nasze wyniki trzeba jakoś 'wkleić' do funkcji motor();. Zróbmy to tak:
Kod:
motor( 1 - dir / 90, 1 + dir / 90 );


Zmienna typu float dir oznacza po prostu kąt potrzebny do obrotu. W/w to przykłady. W tym kodzie chodzi o to, iż im większy jest kąt do obrotu, tym szybciej robot się obróci. Podstawmy liczby do kodu. Ostatni przykład; robot musi się obrócić do tyłu:
motor( 1 - 180 / 90, 1 + 180 / 90); daje motor( 1 - 2, 1 + 2);, czyli motor(-1, 1);.

Weźmy teraz przykład, gdzie robot obraca się w prawo, a kąt jest ujemny:
motor( 1 - (-35) / 90, 1 + (-35) / 90 );. Po podzieleniu daje to: motor(1 - (-0.38), 1 + (-0.38);. Teraz szkoła podstawowa, czyli odejmowanie liczb ujemnych. Da to motor(1.38, 0.62);. Robot skręci lekko w prawo. Kąt wynosi tylko -35 stopni, więc nie trzeba się obracać agresywnie, tak jak w przypadku kąta 180 stopni.

Jednak tutaj jest jeden szczegół. Ten program ma zastosowanie w Ćwiczeniach typu Szalony Robot!, atakowaniu obcych oraz jako program dla osłaniacza. Dlaczego? Ponieważ dir/90 odejmujemy od 1. To oznacza, że robot będzie jechał do przodu, ustawiając się idealnie przodem do celu. Gdy kąt wyniesie 0, dir/90 wyniesie 0, przez co robot zostanie jedynie z motor(1,1);. Kod można użyć w programach ofensywnych dla dział każdego rodzaju.

Funkcja turn(dir); obraca robota w miejscu, podczas gdy motor(1-dir/90, 1+dir/90); po prostu jedzie na wroga. Aby obracać robota w miejscu, trzeba zamienić jedynki na zera, czyli motor(0-dir/90, 0+dir/90);. Wtedy robot będzie podążał za celem bez ruszania się z miejsca.

Jest jeszcze inny trick związany z dzieleniem kątów. Na początku podzieliłem przykładowe kąty na 90. A gdyby podzielić je przez mniejszą liczbę? Popatrzmy:
Cytat:
-125 / 40 = -3.12
-35 / 40 = -0.87
40 / 40 = 1
120 / 40 = 3
170 / 40 = 4.25


Wyszły trochę dziwne wartości, niektóre powyżej 2. Czemu ma to służyć? Dzieląc kąt przez mniejszą liczbę zwiększamy 'ostrość' skrętu. Biorąc poprzedni przykład, kąt -35 dał: motor(1.38, 0.62);. Teraz dałby motor(1.87, 0.13);. Daje to ostrzejszy skręt dla tego samego kąta. Można to zastosować, gdy chcemy szybko skręcać, np. goniąc szybki obiekt - osę. Nie należy jednak przesadzać, gdyż dzielenie kąta przez zbyt małą wartość spowoduje, że skręt będzie tak ostry, że robota będzie rzucać w obie strony zanim wycentruje swój kierunek lotu.

Powyższa metoda skrętu zapewnia kilka udogodnień na niekorzyść turn();. Przede wszystkim turn(); może skręcać jedynie w miejscu - potrzebna jest druga funkcja do poruszania; nie można sterować 'ostrością' skrętu. Co prawda turn(); sprawi, że robota nie będzie wyrywać na boki przy skręcie, jednak z drugiej strony podczas używania funkcji motor(); można używać innych funkcji, np. fire();.

d) Połączenie 'spowalniacza' oraz 'Szalonego Robota!'
Wszystko, co zostało powiedziane powyżej można połączyć. Nie chcę tutaj długo opisywać sposobów, jak to wszystko działa i jakie są zastosowania. Połączenie daje korzyści obu kodów.
A wygląda to tak:
Kod:
motor( (len1 - len2) / 2 - dir / 90, (len1 - len2) / 2 + dir / 90 );

Tak jak wcześniej. Wszelkie modyfikacje zwiększą skuteczność programu, w zależności do czego chcecie tej funkcji użyć.

4. Sugestie

Gdzieś miałem kod, który łączył zarówno 'Spowalniacz' dla odrzutu, jak i dla silnika wraz ze skręcaniem, jako część dynamicznego programu osy, jednak gdzieś się zagubił i muszę go opracować na nowo.

Gdyby ktoś miał jakiekolwiek sugestie, co można dodać do 'Poradnika' lub zna jakieś inne ciekawe wykorzystanie sterowania silnikami za pomocą tych dwóch funkcji, prosiłbym zamieścić na dole. ;)




Trochę mnie nie będzie, gdyż wyjeżdżam do Polski na tydzień, ale nie po to, by spędzić czas przed kompem, więc może mnie trochę nie być. Pozdrawiam,
FE4R
 
     
sajmon313 
Jedi Master


Wiek: 28
Dołączył: 16 Gru 2009
Posty: 42
Skąd: /dev/uarndom
Wysłany: 28-12-2009, 10:14   Re: Sterowanie silnikami [Poradnik?]

FE4R napisał/a:

Analogowo - najszybsze opadanie.

Analogowo? A nie Analogicznie? :P

A ogólnie jeszcze możnaby wyliczyć zależnośći dla każdego typu napędu, takie że:
przy danym pitch ustawić motor() tak żeby robot stał w miejscu (nie zsuwał się)
_________________
Validator CBot - Prace Trwają
Ostatnia Aktualizacja: 02.01.10
 
 
     
Berserker 
Dark Ness


Twoja ulubiona misja: Ofrenia
Pomógł: 16 razy
Wiek: 24
Dołączył: 24 Mar 2009
Posty: 496
Skąd: Bigos
Wysłany: 28-12-2009, 13:25   

Cytat:
I liczyć na efekty. Jeśli robot ma się poruszać do przodu, motor(); musi zostać umieszczone w pętli, np. while(true). To samo tyczy się funkcji jet();.

Troche niedopowiedziane. Instrukcje motor() i jet() to instrukcje wykonywane w tle. Program wykonuje je rownolegle z nastepnymi instrukcjami dopoki nie napotka nastepnej instrukcji dotyczacej danego silnika. Nie znaczy to dokladnie, ze trzeba umieszczac to w petli, ale, ze program musi chodzic wystarczajaco dlugo by mozna bylo zaobserwowac efekt :)

A tu ciekawostka. Kto chce, niech uzyje tego w np szalonym robocie.
Kod:
motor(1-(dir*(1+dir/10))/90,1+(dir*(1-dir/10))/90);

I kolejna, ktora jest w miare zwrotna:
Kod:

dir = direction(p.position);
len = distance2d(position, p.position);
len = 2-len/5;
if(len < 0) len = 0;
rangle = 1-abs(dir)/10;
if(rangle > 1) rangle = 1;
motor(1+rangle*len-dir/90, 1+rangle*len+dir/90);
 
 
     
Wyświetl posty z ostatnich:   

Wersja do druku

Skocz do:  

Powered by phpBB modified by Przemo © 2003 phpBB Group
Polski Portal COLOBOTa © 2008 - 2012