Opis

Take the PlNXT API. Build tutorial and demo cases.

Łukasz Bandała - feanaro@student.agh.edu.pl

Spotkania

20090XYZ

Projekt

Założenia

Celem projektu było przygotowanie demonstracji zastosowania PlNXT API. Zastanawiając się nad licznymi możliwościami prezentacji potencjału połączenia języka prolog oraz klocków NXT, ostatecznie zdecydowałem się na budowę i oprogramowanie robota umożliwiającego układanie kostki rubika. Język prolog doskonale nadaje się do rozwiązywania tego typu problemów.

Konstrukcja

Podstawowym problemem była ograniczona ilość silników i elementów konstrukcyjnych. W pierwszej kolejności należało sprecyzować zadania robota:

  1. Zmiana orientacji kostki w przestrzenii
  2. Ruch kostki
  3. Badanie rozmieszczenia kolorów na kostce

Posiadając tylko 3 silniki można się spodziewać sporych problemów z wykonaniem wszystkich tych czynności. Okazuje się jednak, że problemem tym zajął się ktoś już kiedyś.

Konstrukcja Tilted Twister jest dokładnie tym czego poszukiwałem. Wykorzystująca wszelkie elementy w podstawowym zestawie NXT w sposób wysoce optymalny, umożliwia wykonywanie wszystkich wymienionych powyżej czynności. Można to zobaczyć tutaj.

Zbudowany Titled Twister

Kompletną instrukcję budowy można pobrać ze strony projektu. Ponadto znajdują się tam również kody źródłowe dla C++.

Oprogramowanie

Wstęp

Posiadając już zbudowaną konkretną konstrukcję, nie pozostało mi nic innego jak przystąpić do próby napisania oprogramowania w języku Prolog z wykorzystaniem biblioteki PlNXT.

Biblioteka składa się z kilku modułów. Najbardziej zewnętrznymi (z punktu widzenia użytkownika) z nich są 3 następujące: nxt_movment, threads oraz nxt_sensomoto. Pierwszy jest skierowany do podstawowych robotów jeżdżących. Stąd jego mała przydatność w tym projekcie. Druga dotyczy wywoływaczy (triggers) oraz liczników. Jak okazało się podczas prac, również ten moduł jest mało użyteczny przy oprogramowywaniu twistera. Trzecia natomiast posiada podstawowe czynności związane z komponentami NXT, co okazało się zdecydowanie nabardziej przydatne.

Ruchy i obroty

W celu ujednoznacznienia wykorzystywanych zwrotów, wprowadziłem drobny podział w nazewnictwie:

  • Kostka zbudowana jest z klocków ( 8 trójściennych, 6 jednościennych oraz 12 dwuściennych). To daje w sumie 54 kolorowe kwadraty na kostce.
  • Ruchem nazywać będę zmianę rozmieszczenia kolorowych kwadratów na kostce. Tym samym ruch zmienia rozmieszczenie klocków w kostce. Odpowiednia sekwencja ruchów powodować może rozwiązanie kostki.
  • Obrotem nazywać będę zmianę jedynie orientacji kostki w przestrzenii. Tym samym nie umożliwiają one rozwiązania kostki.

Uwaga! Początkowa próba wykorzystania wbudowanego w biblotece predykatu nxt_motor(Motor,Speed,angle(Angle)) nie przyniosła oczekiwanego rezultatu. Obroty silników o ustalony kąt były zdecydowanie nieprecyzyjne i pozostawiały bardzo dużo do życzenia jeżeli chodzi o jakikolwiek determinizm. Ratunkiem w ów sytuacji okazał się predykat nxt_motor_get_rotations(Motor,Rot_count). Umożliwił on zaimplementowanie prostego regulatora P. Jak wiadomo, zdecydowanie najlepiej budować serwomechanizmy w oparciu o regulator typu PD. Jednakże w tej sytuacji lekko zmodyfikowane P okazało się wystarczające. Pobierając dane o pozycji silnika, wypracowywane jest odpowiednie sterowanie.

Pojawił się niestety jeszcze jeden kłopot. Otóż ze względu na zbliżanie się silnika do zadanej wartości, stopniowo jest mu zadawana coraz mniejsza prędkość (w założeniu jest ona z przedziału 900 do 0). Niestety przy bardzo niskich wartościach i jakimkolwiek obciążeniu silnika, nie jest w stanie się on poruszyć. Konieczne zatem było dodanie pewnej tolerancji błędu pozycji oraz przełączenie na regulację dwupołożeniową w bliskim otoczeniu wartości zadanej. Wprowadzenie tej tolerancji wymogło również stosowanie pomiaru pozycji absolutnej (od włączenia układu) zamiast o wiele łatwiejszego w implementacji, pomiaru pozycji względnej ( w stosunku do poprzedniej pozycji - powoduje to stopniową kumulację błędu). Skomplikowało to zdecydowanie predykaty odpowiedzialne za wszelki ruch.

Obrót względem osi pionowej - silnik A

Jest to podstawowy obrót. Może odbywać się zgodnie z ruchem wskazówek zegara lub odwrotnie. Zakładając prawidłową pozycję obrotowej platformy, obrót ten odbywa się głównie o 90 stopni. Wyjątkiem jest sytuacja analizy rozmieszczenia kolorowych kwadratów na kostce. W tym przypadku wykorzystywany jest również obrót o 45 stopni. Z tego powodu zaimplementowane są w zasadzie 4 predykaty obrotu platformy. 2 z nich odpowiadają za obroty o 90 stopni (jeden z pozycji początkowej plus wielokrotność kąta 90 stopni, drugi z pozycji początkowej przesuniętej o 45 stopni plus wielokrotność kąta 90 stopni). Kolejne 2 natomiast odpowiadają za ruch o 45 stopni (przejście i powrót z pozycji początkowej do pozycji przesuniętej).

W sposób empiryczny sprawdziłem ilość obrotów silnika odpowiadających za 90 stopniowy obrót (można to również próbować przeliczyć na podstawie stosunku ilości zębów zębatek - patrz: przekładnia ).

Na obrót o kąt 90 stopni platformy przypada 315 kroków.

Poniżej prezentuję kody źródłowe poszczególnych obrotów:

  • Obrót o 90 stopni z pozycji początkowej - niewiadoma 'Z' jest mnożnikiem opowiedzialnym za kierunek (wartości to +1 lub -1). Niewiadoma 'M' odpowiada jest współczynnikiem P regulatora. Obrót ten wykorzystywany jest również przy ruchach kostką, stąd większe obciążenie i konieczność zwiększenia współczynnika.
% rotacje calej kostki
rr :- wait_till(wolny), retract(wolny), ruch_A(-1,1.7), sleep(0.3), assert(wolny).
rl :- wait_till(wolny), retract(wolny), ruch_A(+1,1.7), sleep(0.3), assert(wolny).
 
% wzmocnione rotacje calej kostki
rrs :- wait_till(wolny), retract(wolny), ruch_A(-1,2.3), sleep(0.3), assert(wolny).
rls :- wait_till(wolny), retract(wolny), ruch_A(+1,2.3), sleep(0.3), assert(wolny).
 
ruch_A(Z,M) :-
  nxt_motor_get_rotations('A',V1),	% badanie polozenia w odniesienniu do absolutnej poczatkowej pozycji
  V2 is V1+157 ,V3 is V2-(V2 mod 315),
  repeat,
  nxt_motor_get_rotations('A',R),
  X is (R-V3), 
  P is (Z*315-X)*M,
  dwupolozeniowy(P,P2,25), 		% zmiana regulatora przy odpowiednim progu
  nxt_motor('A',P2),
  A is abs(X),				% tolerancja roznicy polozenia
  A =< 317,
  A >= 315,
  nxt_motor('A',0).
  • Obrót o 90 stopni z pozycji przesuniętej - niewiadoma 'Z' podobnie jak wyżej.
% rotacje przy pozycji przesuniętej o 45 stopni
ar :- wait_till(wolny), retract(wolny), ruch_A_45(-1), sleep(0.3), assert(wolny).
al :- wait_till(wolny), retract(wolny), ruch_A_45(+1), sleep(0.3), assert(wolny).
 
ruch_A_45(Z) :-
  nxt_motor_get_rotations('A',V1),	% badanie polozenia w odniesienniu do absolutnej poczatkowej pozycji
  V2 is V1+315 ,V3 is V2-(V2 mod 315),
  repeat,
  nxt_motor_get_rotations('A',R0), R is R0+157,
  X is (R-V3), 
  P is (Z*315-X)*1.7,
  dwupolozeniowy(P,P2,25), 		% zmiana regulatora przy odpowiednim progu
  nxt_motor('A',P2),
  A is abs(X),				% tolerancja roznicy polozenia
  A =< 316,
  A >= 314,
  nxt_motor('A',0).
  • Obroty dopełniające do pozycji początkowej i pozycji przesuniętej.
dopelnienie_do_90 :-
  nxt_motor_get_rotations('A',V),
  G is V - (V mod 315),
  repeat,
  nxt_motor_get_rotations('A',R),
  P is (G-R)*1.5,
  dwupolozeniowy(P,P2,25), 		% zmiana regulatora przy odpowiednim progu
  nxt_motor('A',P2),
  A is abs(R),				% tolerancja roznicy polozenia
  B is abs(G),				
  A =< B+1,
  A >= B-1,
  nxt_motor('A',0),sleep(0.1).
 
dopelnienie_do_45 :-
  nxt_motor_get_rotations('A',V),
  G is V - ((V+157) mod 315),
  repeat,
  nxt_motor_get_rotations('A',R),
  P is (G-R)*1.5,
  dwupolozeniowy(P,P2,25), 		% zmiana regulatora przy odpowiednim progu
  nxt_motor('A',P2),
  A is abs(R),				% tolerancja roznicy polozenia
  B is abs(G),				
  A =< B+1,
  A >= B-1,
  nxt_motor('A',0),sleep(0.1).

Obrót względem osi poziomej

Obrót ten zmienia orientację w poziomie czyli przerzuca kostkę na inną stronę. Ramię jest wyrzucane i cofane. Uderzenie w kostkę powoduje przeniesienie punktu równowagi. Grawitacja dokonuje reszty przywracając kostce prawidłową pozycję na platformie. Konstrukcja twistera do tej złożonej czynności wykorzystuje jedynie silnik 'C'. Jest to zarazem jej największa zaleta (pozostałe silniki mogą być wykorzystane przy innych czynnościach) i wada (obrót ten nie zawsze kończy się sukcesem). Kod odpowiedzialny za ten obrót również zawiera regulator:

% przerzucenie kostki
f :-
    wait_till(wolny), 
    retract(wolny),
    nxt_stop,
    nxt_motor('C',500), 	% wyrzut ramienia
    sleep(0.5), 	
    cofnij,			% cofnięcie ramienia
    sleep(0.4),
    assert(wolny).
 
cofnij :-
  repeat,
  nxt_motor_get_rotations('C',R),
  P is (-R)*9,
  dwupolozeniowy(P,P2,100), 	% zmiana regulatora przy odpowiednim progu
  nxt_motor('C',P2),
  A is abs(R),			% tolerancja roznicy polozenia
  A =< 5,
  nxt_motor('C',0).

Obrót ten jest prawdopodobnie najbardziej podatny na zmianę kostki, niedokładny obrót platformy i niedopasowanie klocków (za mocno lub za słabo dociśnięte klocki w różnych miejscach).

Ruchy kostki

Właściwa i główna czynność dotycząca kostki Rubika. Łączy w sobie częściowo wszystkie powyższe czynności. Ramię jest lekko wysuwane i tym samym utrzymuje 2 górne linie kostki, nie dopuszczając do ich obrotu. Następnie wykonywany jest obrót platformy, który powoduje ruch dolnej warstwy kostki.

% ruchy kostki
mr :- ruch_C_gora(38),wait_till(nxt_is_stopped),rrs,wait_till(nxt_is_stopped),ruch_C_dol,sleep(0.4).
ml :- ruch_C_gora(38),wait_till(nxt_is_stopped),rls,wait_till(nxt_is_stopped),ruch_C_dol,sleep(0.4).
 
ruch_C_gora(G) :-
  retract(wolny),
  repeat,
  nxt_motor_get_rotations('C',R), 
  P is (G-R)*5,
  dwupolozeniowy(P,P2,25), 	% zmiana regulatora przy odpowiednim progu
  nxt_motor('C',P2),
  A is abs(R),			% tolerancja roznicy polozenia
  A =< G+1,
  A >= G,
  nxt_motor('C',0),
  assert(wolny).
 
ruch_C_dol :-
  retract(wolny),
  repeat,
  nxt_motor_get_rotations('C',R), 
  P is (-R)*5,
  dwupolozeniowy(P,P2,25), 	% zmiana regulatora przy odpowiednim progu
  nxt_motor('C',P2),
  A is abs(R),			% tolerancja roznicy polozenia
  A =< 5,
  nxt_motor('C',0),
  assert(wolny).

Uzupełnienie i uwagi

  • Wykorzystywany kod regulatora dwupołożeniowego:
% regulator dwupołożeniowy (w zasadzie trójpołożeniowy)
dwupolozeniowy(P,P2,_) :- P == 0, P2 is 0.
dwupolozeniowy(P,P2,T) :- P <0, P > -T , P2 is -T.
dwupolozeniowy(P,P2,T) :- P >0, P < T , P2 is T.
dwupolozeniowy(P,P2,T) :- (P > T ; P < -T), P2 is P.
  • Jak widać w powyższych kodach, jest tam wiele wartości przyjętych na sztywno. Wynikają one z moich własnych prób możliwie najdokładniejszego dopasowania ruchów i obrotów dla mojej konstrukcji i mojej kostki. Każda próba zwiększenia dokładności wiąże się równocześnie z drastycznym spadkem prędkości wykonywanych czynności. Działanie natomiast w drugą stronę, obfituje w liczne losowe błędy, które w ujęciu wielu ruchów kończą się wypadnięciem kostki z platformy lub innym nieporządanym zachowaniem układu.
  • Ponadto pamiętać należy, że są to najbardziej podstawowe czynności. Możliwe jest dodanie również obrotów o 180 stopni, które wielokrotnie w niektórych miejscach wykonywania algorytmu, mogłyby przyśpieszyć działanie.

Czujniki

Konstrukcja robota wykorzystuje 3 czujniki: świetlny, ultrasoniczny i dotykowy. W oprogramowaniu wykorzystywane są jedynie 2 z nich. Przeznaczeniem obu jest odczytywanie rozmieszczenia kolorowych kwadratów na kostce. Wykorzystywane są do tego predykaty odpowiadające za obroty. Oto kody odpowiadające za poszczególne czynności:

  • Pozycjonowanie - jest to podstawowy obrót ramienia mający na celu dokładną kalibrację. Wykonywany jest tylko raz.
pozycjonuj :- 
  repeat,
  nxt_motor('B',-150),
  nxt_touch(V,force),
  V==1,  
  nxt_motor('B',0),
  nxt_motor_reset_position('B',absolute),
  sleep(0.1).
  • Obrót ramienia - jest to predykat odpowiadający za ruch ramienia z czujnikiem światła. Podobnie jak w przypadku pozostałych silników również tutaj potrzebna jest regulacja. Predykat ten przyjmuje niewiadomą 'Z', która jest wartością zadaną oraz 2 (lub tylko 1) niewiadome 'T', które są dolną i górną granicą tolerancji położenia.
obrot_ramienia(Z,T) :- obrot_ramienia(Z,T,T). 
obrot_ramienia(Z,T1,T2) :-
  retract(wolny),
  repeat,
  nxt_motor_get_rotations('B',R), 
  P is (Z-R)*2,
  dwupolozeniowy(P,P2,5),
  nxt_motor('B',P2),
  R =< Z+T1,			% tolerancja roznicy polozenia
  R >= Z-T2,
  nxt_motor('B',0),
  assert(wolny),
  sleep(0.2).
  • Odczyt ściany - poniższe predykaty odpowiadają za odczytanie jednej ściany oraz zamianę odczytanych wartości na kolory. Granice kolorów są niestety bardzo zmienne w zależności od oświetlenia.
czytaj_sciane(sciana(V1b,V2b,V3b,V4b,V5b,V6b,V7b,V8b,V9b)) :- 
  wait_till(nxt_is_stopped),
  obrot_ramienia(155,0),
  nxt_light(V1a), kolor(V1a,V1b),
  obrot_ramienia(187,0), 
  nxt_light(V2a), kolor(V2a,V2b), 
  rl, wait_till(nxt_is_stopped),
  nxt_light(V3a), kolor(V3a,V3b), 
  rl, wait_till(nxt_is_stopped),
  nxt_light(V4a), kolor(V4a,V4b), 
  rl, wait_till(nxt_is_stopped),
  nxt_light(V5a), kolor(V5a,V5b), 
  rl,
  obrot_ramienia(177,0), wait_till(nxt_is_stopped),
  dopelnienie_do_45,wait_till(nxt_is_stopped),
  nxt_light(V6a), kolor(V6a,V6b), 
  al,wait_till(nxt_is_stopped),
  nxt_light(V7a), kolor(V7a,V7b), 
  al,wait_till(nxt_is_stopped),
  nxt_light(V8a), kolor(V8a,V8b), 
  al,wait_till(nxt_is_stopped),
  nxt_light(V9a), kolor(V9a,V9b), 
  dopelnienie_do_90,rl,rl,
  obrot_ramienia(54,4).
 
kolor(X,Y) :- X =< 20, 		Y=w. % biały
kolor(X,Y) :- X >= 21, X =< 30, Y=y. % żółty
kolor(X,Y) :- X >= 31, X =< 37, Y=g. % zielony
kolor(X,Y) :- X >= 38, X =< 44, Y=r. % czerwony
kolor(X,Y) :- X >= 45, X =< 49, Y=o. % pomarańczowy
kolor(X,Y) :- X >= 50, 		Y=b. % niebieski
  • Ostani predykat łączy wszystkie pozostałe i odpowiada za całościowe zbadanie kostki.
badaj(cube(V1, V2, V3, V4, V5, V6,
  V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, 
  V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, 
  V29, V30, V31, V32, V33, V34, V35, V36, V37, V38, V39, V40, 
  V41, V42, V43, V44, V45, V46, V47, V48, V49, 
  V50, V51, V52, V53, V54)) :- 
  pozycjonuj,
  obrot_ramienia(54,4),
  nxt_light_LED(activate),
  % czytanie ścian normalne
  odczyt(V1,  V2,  V3,  V4,  V6,  V7,  V8,  V9,  V10, 
    V11, V12, V13, V15, V16, V18, V19, V20, V21, 
    V22, V23, V24, V25, V27, V28, V30, V31, V32, 
    V33, V34, V36, V38, V39, V40, V41, V42, V43, 
    V44, V45, V46, V47, V48, V49, V51, V52, V53),
  % ściana czytana do góry nogami
  czytaj_sciane(sciana(V5,V29,V17,V14,V26,V37,V54,V35,V50)),
  rl,f,rl. 
 
odczyt(V1,  V2,  V3,  V4,  V6,  V7,  V8,  V9,  V10, 
  V11, V12, V13, V15, V16, V18, V19, V20, V21, 
  V22, V23, V24, V25, V27, V28, V30, V31, V32, 
  V33, V34, V36, V38, V39, V40, V41, V42, V43, 
  V44, V45, V46, V47, V48, V49, V51, V52, V53) :-
  czytaj_sciane(sciana(V3,V9,V21,V27,V15,V40,V32,V44,V36)),
  f,
  czytaj_sciane(sciana(V1,V10,V7,V13,V16,V41,V47,V39,V49)),
  f,
  czytaj_sciane(sciana(V6,V24,V12,V18,V30,V46,V34,V42,V38)),
  f,
  czytaj_sciane(sciana(V4,V19,V22,V28,V25,V43,V51,V45,V53)),
  rr,f,
  czytaj_sciane(sciana(V2,V11,V23,V20,V8,V48,V33,V52,V31)),
  f,f.

Uzupełnienie i uwagi

  • Niestety odczytanie kolorów z użyciem tego predykatu jest bardzo trudne. Wynika to z problemów z wciąż ograniczoną precyzją ruchów ramienia i przede wszystkim słabym odczytem kolorów, które nie różnią się znacząco pod względem luminancji (a to ona jest badana). Potencjalnie są 2 możliwości rozwiązania tego problemu. Pierwszym jest zastosowanie czujnika HiTechnic. Drugim jest zmiana kolorów na kostce rubika i poszukanie odpowiednich, rozróżnialnych barw.
  • Podczas pisania predykatów natknąłem się na osobliwy problem związany z czymś innym niż mój program. Otóż początkowo predykat 'badaj' zawierał w sobie całość ciała predykatu 'odczyt'. Co zresztą wydaje się czytelniejsze. Niestety podczas próby odczytu 50 koloru, program bez przyczyny się zawieszał. Rozbicie tego samego na 2 predylaty rozwiązało sprawę.

Algorytm

Posiadając już konstrukcję oraz komplet czynności, które powinien wykonywać robot, nie pozostało nic innego jak zajęcie się algorytmem. Jak wiadomo, ułożenie kostki Rubika, nie jest problemem trywialnym. W związku z tym, należy się również liczyć ze sporą złożonością tego typu programu. Wykorzystałem zatem istniejący program Cube Solver II, który rozwiązuje problem kostki Rubika (Cube Solver II).

Zarys wykorzystywanych metod oraz samego algorytmu poszukiwań można znaleźć na tej stronie: "Solving Rubik's Cube. Prolog in action".

Z wiadomych powodów, by skorzystać z potencjału tego programu musiałem go znacząco przerobić. Dodałem możliwość analizy kostki, podanie jej kolorów ręcznie oraz uruchomienie wykonywania algorytmu.

NXT_CUBE_SOLVER

Niestety i tutaj pojawiły się spore problemy. Sam program jest bardziej ideowy, niż praktyczny. Stąd trafiają się nierozwiązywalne przez niego konfiguracje kostki. Ponadto znajdywane przez niego rozwiązania zazwyczaj wymagają około 500 ruchów (sic!), a co przy ograniczonej prędkości i precyzji, okazuje się bardzo problematyczne. Według tego co pisze wikipedia na temat optymalności rozwiązania (Optimal solutions for Rubik's Cube), możliwe jest zaimplementowanie algorytmu, który będzie wymagać nie więcej niż 22 standardowych ruchów (czyli szacunkowo jakichś 60 czynności dla twistera).

Obsługa i uruchomienie

Po poskładaniu robota, podstawową czynnością jest przygotowanie komunikacji. Należy włączyć brick, Bluetooth w bricku i na komputerze, a w dalszej kolejności przejść opis konfiguracji. Kolejnym krokiem jest przygotowanie i umieszczenie w tym samym katalogu co plik nxt_rubik.pl, plików z biblioteki PlNXT. Następnie należy włączyć interpreter prologa i skonsultować plik nxt_rubik.pl.

Sam program uruchamia się predykatem 'go.'. Zostaną wyświetlone 4 opcje: auto, ręcznie, ułóż oraz wyjdź. Ostatnia nie wymaga chyba wyjaśnień.

  1. Auto - powoduje próbę automatycznego odczytania pól kostki.
  2. Recznie - powoduje oczekiwanie na ręczne podanie rozkładu pól kostki. Podaje się kolejno rzędami od góry (według zaprezentowanego w programie przykładu). Poszczególne pola mogą mieć dowolną nazwę (ale konsekwentną) i oddziela się je dowolnymi białymi znakami (enter, spacja).
  3. Uloz - jak sama nazwa wskazuje. Wykonywany jest algorytm, a następnie wykonywane są ruchy na kostce. Aktualizowany jest procent wykonanych ruchów.

Ponadto można wykonywać pojedyńcze ruchy wpisując f,rr,rl,mr lub ml.

Uwaga! Należy pamiętać by platforma robota, na której kostka się znajduje, była ustawiona w możliwie najbardziej równoległej do chwytaka pozycji pozycji.

Podsumowanie

  • Problemy - Niestety większość problemów, które wyniknęły podczas realizacji tego projektu, jest spowodowanych niedoskonałością biblioteki. Oryginalny program do obsługi Titled Twistera są zdecydowanie szybsze i precyzyjniejsze, czego nie da się do końca tłumaczyć wadą kodu w prologu. Dodatkowo mechanizmy, które w założeniu powinny ułatwiać pracę z biblioteką, okazały się mało (a w zasadzie w ogóle!) nieprecyzyjne. Wymieniam tutaj najbardziej rażące problemy z biblioteką:
  1. Predykat nxt_motor(Motor,Speed,angle(Angle)) - nie sprawdza przy jakimkolwiek obciążeniu. Zachowana jest jedynie prędkość. Natomiast kąt obrotu ZNACZĄCO odbiega od tego zadanego.
  2. Predykat nxt_motor(Motor,Speed,time(Time)) - sens tego predykatu sam w sobie jest dziwny. Kąt obrotu całkowicie zależy od obciążenia.
  3. Predykat nxt_ultrasonic(Value) - w ogóle nie działa. Z niewiadomego powodu wyświetlany jest błąd bez wskazówek co może być przyczyną.
  4. System triggerów - generalnie funkcjonuje, ale kompletnie nie nadaje się do niczego co wymaga chociażby odrobiny prędkości, a co za tym idzie - precyzji. Początkowa próba sprzężenia pomiaru pozycji z prędkością silnika, nie dała sensownych rezultatów. Niską prędkość w stosunku do zwykłego „sekwencyjnego” rozwiązania widać bardzo na podstawie chociażby samego wyświetlania.
  5. Ogólna prędkość działania - PlNXT wydaje się strasznie utykać pod względem szybkości komunikacji z silnikami. Wystarczy chociażby spojrzeć na oryginalny program Titled Twistera i to jak szybko jest wykonywany.
  • To do:
  1. zastosowanie czujnika kolorów HiTech
  2. napisanie DOBREGO algorytmu

Materiały i linki

pl/miw/2009/nxt_api_demo.txt · ostatnio zmienione: 2019/06/27 15:50 (edycja zewnętrzna)
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0