Czyli pomoc w zrozumieniu symulatora mobilnego RobSym napisanego przez
doktora inżyniera Macieja Horczyczaka
1. Wstęp
Poradnik przeznaczony dla osób mających po raz pierwszy styczność z symulatorem RobSym.
Po pierwsze: zszywka (robsym.pdf) jest podstawowym źródłem informacji. W ramach poradnika postaram się wyjaśnić krzatające się w niej treści w sposób zrozumiały dla nas, studentów. Wybiorę z niej rzeczy warte objaśnienia, a nie wszystkie.
Poradnik zawiera przykłady działających części programów, co w obliczu braku dostepu do RobSym poza salą laboratoryjną powinno być cenną pomocą. Informacje zweryfikowane empirycznie.
Po drugie: przed przystąpieniem do pisania programu warto zaplanować wstępnie jak podejść do problemu i przemyśleć, co potrafi nasza kosiarka i jakie ograniczenia mają czujniki i dane intrukcje. (Źródła wiedzy to przykładowo: ograniczenia pętli while opisane są w tym poradniku, ograniczenie czujnika ultradźwiękowego można wyczytać ze zszywki)
Rada ogólna: polecam pisanie programu w windowsowym notatniku, po czym wczytywanie go do symulatora.
Znacznie ułatwia to edycję programu i stosunkowo płynne poruszanie się w chmarze literek itp.
2. Rady i przykłady
a) Zadanie następujące: Robot stoi w losowym miejscu, chcemy obórcić go w miejscu tak, by „patrzył się”
prosto na marker. Niech będzie to marker 2. (położenie markera można ustawić w pliku konfiguracyjnym środowiska, chyba, że prowadzący sam ustawi marker)
Przykładowy program:
let kat 0
Deklaruję zmienną “kat” przez podstawienie do niej dowolnej wartości, dla
wygody 0. Deklaracja jest niezbędna przed użyciem zmiennej do innych
celów.
get 6 kat
Po pierwsze pobieram wartość z portu 6. Port ten mówi mi, pod jakim
kątem przód robota jest obrócony względem markera 2. Wartość podaje w
stopniach podzielonych przez 3, czyli od 0 do 119. Kąt rośnie przeciwnie do
ruchu wskazówek zegara. Nidgy nie wystąpi liczba ujemna ani większa od
119, zawsze będzie całkowita. Następnie wartość kąta wpisuję w zmienną
„kat”.
while kat :m2
While rozpoczyna pętlę, która wykonuje się, dopóki zmienna (w tym
przypadku „kat”) osiągnie wartość 0. „:m2” to nazwa miejsca, w którym
pętla się kończy i z którego powróci na początek lub wyjdzie w razie
spełnienia warunku zmienna=0. Po dwukropku może się znajdować
dowolna nazwa. Nie wolno stosować tego samego ciągu znaków po
dwukropku do więcej niż do jednej pętli w programie. Oczywistym jest, że
w pętli należy uaktualniać zmienną tak, aby kiedyś uzyskała wartość 0,
inaczej nasz program nigdy z pętli nie wyjdzie.
set 1 34
Wysyłam wartość 34 na port nr 1. Port ten odpowiada za uruchamianie
napędów kół. Wzór na obliczenie wartości podany jest w zszywce. W tym przypadku na koło lewe podaję wartość 1, na koło prawe -1 co daje
równanie (1+3)*8+(-1+3) = 34, skutkiem jest obrót robota wokół swojego
środka w prawo. Podanie na któreś koło wartości 0 skutkowałoby obrotem
wokół tego koła co zazwyczaj nie jest pożądane. Zakładam, że w zadaniu
nie ma różnicy, czy obracam się w lewo czy w prawo.
get 6 kat
Robot przed chwilą obrócił się nieco, muszę więc sprawdzić, czy może już
osiągnął kąt równy 0.
write "Kat na M2 to: "
Komenda wyświetli na ekranie zdanie podane między znakami ””. Spacje
też się liczą.
write kat
Wyświetli na ekranie liczbę kryjącą się pod zmienną kąt.
write „stopni kosiarkowych.”
Jak wcześniej. Write jest przydatny do oceny na bieżąco, czy program działa
dobrze i co w rzeczywistości widzą czujniki kosiarki. Kilka write pod rząd
wyświetli się ciągiem w jednej linii, tutaj jako: „Kat na M2 to: 12 stopni
kosiarkowych.” Liczba 12 jest przypadkowa.
newl
Związane z write, wymusza przejście do kolejnego wiersza (lub wciśnięcie
„entera” jak kto woli). Bez tego czytelność byłaby znikoma.
:m2
Oznacza koniec pętli, po napotkaniu tej komendy program wróci na
początek odpowiedniej pętli while, sprawdzi warunek, po czym kontynuuje
działanie pętli lub w razie spełnienia warunku wyjdzie z niej i wykona
instrukcję stojącą za tym znakiem.
set 1 27
W tym momencie nasz robot jest już obrócony na marker 2, lecz na silniki
nadal jest podawana wartość 34 czyli obrót. Tą komendą zatrzymamy
obrót kół, a robot stanei w miejscu.
stop
Komenda kończąca cały program. Koniecznie musi występować na końcu
programu.
b)
W dowolnym miejscu programu można wstawić pusty wiersz (lub wiele takich pod rząd), zostanie on ominięty przez program. Innym wyborem jest komentarz, zaczyna się on od symbolu # , po nim może występować dowolny ciąg znaków i zostanie zignorowany przez program. Nie można stawiać komentarzy w tej samej linii co inna komenda, przykładowo:
set 1 34 #obracam sie
set 1 34
get 6 kat #pobieram kat
#obracam sie
write "Kat na M2 to: " #wyswietlam napis
get 6 kat
#pobieram kat
write "Kat na M2 to: "
#wyswietlam napis
Błędnie dodane komentarze
Poprawnie dodane komentarze
(specjalnie ułożyłem je w mało czytelny sposób)
c) Za zadanie postawmy sobie dojazd do markera 2. Zacząć należy programem z podpunktu a) (przesuwając instrukcję stop na faktyczny koniec programu), dzięki temu robot patrzy się prosto na marker. Zdawać by się mogło, że wystarczy zastosować pętlę while, w której jako zmienną zastosujemy odległość od markera (gdy dojedziemy do markera zmienna powinna osiągnąć wartość 0 i zakończyć jazdę) a w środku pętli umieścimy instrukcję set 1 xx, gdzie xx oznacza jazdę do przodu (z równą prędkością na oba koła). Niestety, robot po zastosowaniu programu a) nie jest obrócony idealnie prosto na marker, może się mylić w ustawieniu nawet o 3 standardowe stopnie. W praktyce oznacza to, że nasz robot prawie na pewno ominie marker. Przejechanie nawet o 1 jednostkę za daleko od markera oznacza niemożność wyjścia z pętli a tym samym dalszą jazdę robota do przodu aż do kresu
planszy. W poniższym przykładzie pokażę, jak można zaprogramować korekcję kąta w trakcie jazdy z jednoczesnym badaniem odległości. Dzieki temu robot nie przejedzie obok markera, tylko wjedzie prosto na niego.
#dojazd do wierzcholka m2
komentarz
let odl 0
Deklaruję zmienną „odl”, jak wspomniałem jest to niezbędne.
get 9 odl
Wartość z portu 9 wpisuję do zmiennej odl. Port 9 podaje odległość
robota od markera 2. Odległość ta jest liczbą całkowitą nieujemną. Jej
mianem niech będą jednostki [j], trudno móić tu o metrach i tym
podobnych.
while odl :stoje_w_m2
Początek pętli. Kończy się w linijce „:stoje_w_m2” w momencie, gdy
nasz czujnik odległości wykryje wartość 0, czyli gdy robot będzie stał
na/przed markerem m2.
set 1 45
Jazda do przodu, na obu kołach wartość 2. Niezalecane jest
poruszanie się szybsze niż 2, w takim wypadku robot mógłby pominąć
jakieś wskazanie czujnika kąta i za mocno się w jednej chwili obrócić.
Niby nic poważnego, ale słyszałem o problemach mogących z tego
wyniknąć.
get 9 odl
Robot przed chwilą pojechał nieco do przodu, należy więc pobrać
get 6 kat
aktualne wartości odległości i kąta względem markera 2, by za chwilę
móc dokonać ewentualnych korekcji lub skończyć program w razie
dojechania do celu.
write "Do markera nr 2 pozostalo: "
Wyświetla pozostałą drogę.
write odl
write "jednostek."
newl
Przerwa dla czytelności ☺
goifzero :jade_prosto kat
Poniższe 3 linijki napisane zostały po to, aby ocenić czy robot nie
odchylił się za bardzo od markera. Są miejscem decyzji co robić w
przypadku braku odchylenia, odchylenia w lewo lub odchylenia w
prawo.
Ta linia (goifzero :jade_prosto kat) wysyła nas na koniec głównej pętli
jeśli robot nie odchylił się od zadanego położenia a jazda przebiega
poprawnie (kat=0).
goifminus :lewo kat - 6
Jeśli program wszedł do tej instrukcji to oznacza, że robot odczytał
niewłaściwy kąt na marker 2.
W tym przypadku instrukcja wyłapuje kąty w zakresie <1, 5>.
Pojawienie się takich kątów oznacza, że robot patrzy się nieco w
prawo od markera, musi więc wejść w pętlę, która pozwoli mu
obrócić się w lewo aż do skorygowania kąta.
goifplus :prawo kat
Wysyła program do pętli korygującej robota w prawo. Po przejściu
przez dwie poprzednie instrukcje możliwe kąty to 119 i ewentualnie
od niego troszkę mniejsze. Na pewno są dodatnie, więc instrukcja
zadziała. Mimo to wystarczyłaby instrukcja goto :prawo, która
bezwzględnie wysłałaby program w żądane miejsce.
Przerwa dla czytelności ☺
#korekcja w prawo
komentarz
:prawo
Początek korekcji w prawo. W ninejszym przykładzie można
wykasować tą linię i cały warunek „goifplus :prawo kat” bez szkody
dla poprawności działania. Instrukcje pozostawiam w razie, gdyby
potrzeba było dopisać jakieś instrukcje pomiędzy goifplus i :prawo (
daje to możliwość ominięcia tych instrukcji).
while kat :obrprawo
Poczatek pętli
set 1 34
Skręt w prawo wokół własnej osi
get 6 kat
Pobranie aktualnego kąta po minimalnym obrocie
write " Korekcja w prawo, kat na M2: "
Wyświetla na ekranie podstawowe info
write kat
newl
:obrprawo
Koniec pętli
Przerwa dla czytelności ☺
goto :2
Bezwarunkowo skacze do instrukcji „:2” Zastosowane, by ominąć
korekcję w lewo po zakończeniu korekcji w prawo.
#korekcja w lewo
komentarz
:lewo
Początek korekcji w lewo
while kat :obrlewo
Początek pętli
set 1 20
Obrót w lewo
get 6 kat
Pobranie aktualnego kąta
write " Korekcja w lewo, kat na M2: "
Wyświetlanie przydatnych info
write kat
newl
:obrlewo
Koniec pętli
Przerwa dla czytelności ☺
:jade_prosto
Punkt po ominięciu obu korekcji
:2
Punkt po ominięciu jednej z korekcji
Przerwa dla czytelności ☺
set 1 36
Jazda prosto
:stoje_w_m2
Koniec głównej pętli
Przerwa dla czytelności ☺
#zatrzymanie
komentarz
set 1 27
Zatrzymanie robota
#stoimy w M2
komentarz
d) W tym przykładzie chcę pokazać prosty sposó rozwiązania pewnego problemu. Problem jest następujący: w linijce zaczynającej pętlę while nie możemy umieścić operacji arytmetycznej.
Akceptowalna jest jedynie zmienna.
Zapis nieprawidłowy:
While b – 10 :koniec
:koniec
Zapis prawidłowy:
Let c b -10
While c :koniec
:koniec
Aby pokazać wykorzystanie powyższego w praktyce, zamieszczam przykład zadania polegającego na obrocie robota jak w a) z tą róznicą, że pętlę while uzależniamy od różnicy kątów (a nie od wartości pojedynczego kąta).
let kat_aktualny 0
Deklaracja zmiennej
get 4 kat_aktualny
Z portu 4 pobieram wartość kąta będącego odchyleniem osi robota
od osi X planszy, wartość tą zapisuję jako kat_aktualny.
let mleko 0
Deklaracja zmiennej
let mleko kat_staly – kat_aktualny
Zmienna „mleko” posłuży nam jako zastąpienie operacji
arytmetycznej tak, aby kompilator się nie czepiał.
„kat_aktualny” został wyjaśniony wyżej.
„kat_staly” to zmienna, którą na przykład pobraliśmy z portu 4
wcześniej w programie, gdy robot był obrócony o inny kąt
względem osi X (nie ujęte w przykładzie). Teraz chcemy robota
obrócić z powrotem na ten kąt określony nie przez marker, a przez
oś X. Z tego powodu badamy różnicę między tymi kątami i kończymy
pętlę, gdy wyniesie ona zero.
while mleko :czekolada
Początek pętli
set 1 34
Obrót (kierunek dowolny, tutaj: w prawo)
get 4 kat_aktualny
Tak jak we wsześniejszych przykładach, pobieram aktualny kąt
zmieniony nieco po niedawnym obrocie w prawo.
let mleko kat_staly – kat_aktualny
W środku pętli musimy zaktualizować zmienną mleko. Zmiana
aktualnego kąta nie aktualizuje nam automatycznie zmiennej
„mleko”, która warunkuje nam wykonywanie pętli.
write "Kat: "
Wyświetlanie info.
write mleko
newl
:czekolada
Koniec pętli
#zatrzymanie
set 1 27
Zatrzymanie silników
3. Powyżej omówione programy zamieszczam ponownie bez tabelki i moich komentarzy: a)
c)
let kat 0
#dojazd do wierzcholka m2
get 6 kat
let odl 0
while kat :m2
get 9 odl
set 1 34
while odl :stoje_w_m2
get 6 kat
set 1 45
write "Kat na M2 to: "
get 9 odl
write kat
get 6 kat
write „stopni kosiarkowych.”
write "Do markera nr 2 pozostalo: "
newl
write odl
:m2
write "jednostek."
set 1 27
newl
stop
goifzero :jade_prosto kat
goifminus :lewo kat - 6
goifplus :prawo kat
#korekcja w prawo
:prawo
while kat :obrprawo
set 1 34
get 6 kat
write " Korekcja w prawo, kat na M2: "
write kat
newl
:obrprawo
goto :2
#korekcja w lewo
:lewo
while kat :obrlewo
set 1 20
get 6 kat
write " Korekcja w lewo, kat na M2: "
write kat
newl
:obrlewo
:jade_prosto
:2
set 1 36
:stoje_w_m2
#zatrzymanie
set 1 27
#stoimy w M2
stop
d)
let kat_aktualny 0
get 4 kat_aktualny
let mleko 0
let mleko kat_staly – kat_aktualny
while mleko :czekolada
set 1 34
get 4 kat_aktualny
let mleko kat_staly – kat_aktualny
write "Kat: "
write mleko
newl
:czekolada
#zatrzymanie
set 1 27
4. Zakończenie
Za literówki i ewentualne nieścisłości przepraszam. Poradnik modyfikujcie i rozprzestrzeniajcie wedle uznania.
W razie chęci kontaktu ze mną – autorem, proszę wysyłać email’e na adres: aningan@poczta.onet.pl (doceniam sugestie i komentarze!)
Aningan, listopad 2012r.