Skocz do zawartości

Line follower - problem z członem P


sofnir

Pomocna odpowiedź

Hej, piszę kod do line followera i mam spory problem. Kod wygląda w ten posób:

 readSensors();
 readCurrentPosition();

 error = wantedPosition - currentPosition;
 change = kp * error;

 leftEngine = maxSpeed - change;
 rightEngine = maxSpeed + change;

 analogWrite(leftMotor, leftEngine);
 analogWrite(rightMotor, rightEngine);

Przedstawię teoretyczny model:

Mam 4 czujniki. Zczytuję z nich wartości. Obliczam aktualną pozycję:

currentPosition = -2 * sensors[0] - sensors[1] + sensors[2] + 2 * sensors[3];

Jeśli currentPosition z obliczeń da mi 0 to jestem na środku, -1400 jestem skrajnie na lewo, +1400 jestem skrajnie na prawo.

Obliczm błąd. Error = 0 - pozcyja. (mogę otrzymać wyniki -1400 do 1400, gdzie 0 to prawidłowa pozycja).

Kp obliczyłem w taki sposób: maxSpeed/14000 (uznałem sobie, że maxSpeed = 200 (może to być wartość od 0 - 255, bo to PWM)). Czyli change przyjmuje wartości od 0 do 200.

Teraz obliczam wartości z jakimi mają obracać się silniki.

Tutaj pojawia się pierwszy problem. Jeśli pozycja = 0 to change = 0 to obroty lewego i prawego silnika = maxSpeed (czyli 200, które ustaliłem). Ale, jeśli jestem na skrajnej pozycji, to obroty wyniosą 0 i 400, więc zastanawiam się jak to poprawić. Może wtedy jeden silnik ma mieć to 200, które zadałem jako maksymalne, a ten drugi po prostu ma tylko zwalniać? Ale wtedy jak to opisać, może ifem? Jeśli jest > 200 to przyjmuje 200? I po przekroczeniu 200 zawsze będę miał 200 (maxSpeed).

Ale to tylko model teoretyczny, o którego bardzo proszę weryfikację i czy mój pomysł jest dobry. W rzeczywistości mam jednak problem z czujnikami. Podpięte są pod wartości analogwe ;/ Pokazują różne wartości w różnych miejscach. Moja konstrukcja jest prowizoryczna i czujniki nie są idealnie ułożone w poziomie, a poza tym ich wartości i tak się zmieniają w dziwny sposób. Czasem pokazują np. 123 | 123 | 280 | 340 . Czasem np. 170 | 140 | 150 | 280. Itd.

Jeśli aktualną pozycje obliczam o tak:

currentPosition = -2 * sensors[0] - sensors[1] + sensors[2] + 2 * sensors[3];

To to działa tylko, jeśli czujniki ZAWSZE działały by jednakowo (podawały te same wartości) tzn. np. 123|123|123|123. Ja się staram odejmując/dodając pewne wartości w programie ustalić te wartości, żeby były jednakowe, ale co z tego jak robota przesunę gdzieś indziej, albo minie jakiś czas i te czujniki i tak te wartości zmieniają swoje ;/ Nawet jeśli zrobiłbym stabilniejszą konstrukcję to te wartości i tak będą oscylowały w pewnej granicy no nie wiem np. +- 100/200 do tego dochodzą jakieś nierówności kartki, zabrudzenia itp. Wcześniej robiłem robota na digitalach i ifach i jeździł nie najgorzej, ale przy większych prędkościach wypadał z zakrętów i takim owalnym ruchem wracał dopiero na trasę (po dłuższym czasie to wyglądało jakby był pijany). Chciałem tego pid zastosować i wartości analogowe, na razie to co zrobiłem to tylko człon P, ale robot nie jeździ prawie w ogóle i wcale mu się nie dziwie, bo takie dostaje komendy ;/ Tylko nie wiem jak to za bardzo zmienić. Może jakoś te wartości zaokrąglać? Albo kupić jakieś inne czujniki? No nie mam pojęcia.

Bardzo Was proszę o pomoc. Jeśli coś z mojego opisu jest niejasne, to pytajcie, postaram się to jeszcze bardziej sprecyzować, bo ciężko wyrażać swoje myśli 🙂

Link do komentarza
Share on other sites

Jeśli na silniki możesz "wystawić" PWM o wartości 0÷255, to czego byś nie robił, powinieneś dać na wyjściu sygnału tzw. saturator, czyli warunek sprawdzania poprawności sygnału, który wychodzi z Twojego regulatora - "jeśli wyjscie < 0, to PWM=0, jeśli wyjscie > 255, to PWM=255". W ten sposób zabezpieczysz się przed przekręceniem zmiennej, która jest 8-bitowa. To powinno pomóc w Twojej wizji algorytmu... W skrajnym przypadku jeden silnik się zatrzyma, drugi będzie się kręcić z PWM=255.

Musisz jednak wiedzieć, że zmienna "change" służy tylko do korekcji, czyli jeśli error=0, to change także powinno być równe 0. Jeśli chcesz użyć klasycznego algorytmu w obecnej postaci, to załóż swoje maxSpeed = 100, a nie 200. Wtedy w przypadku maksymalnego błędu jeden silnik się zatrzyma, a drugi będzie działać z maxSpeed*2, żeby błąd skompensować, a przy błędzie 0 oba silniki będą się kręcić z maxSpeed.

Tylko nie spodziewaj się cudów po samym członie proporcjonalnym, efekt może być nawet gorszy od prostej pętli warunkowej (if...else if...). Cała sztuka to dobrze dobrać współczynnik Kp - w przypadku Linefollowera, to chyba tylko doświadczalnie. Potem trzeba zbytnie "pijaństwo" poprawić członem różniczkującym, inaczej robot będzie albo wolno reagować (małe Kp), albo będzie go ciągle "zarzucać" nawet na prostej (duże Kp). No chyba, że nie zależy Ci na prędkości.

Link do komentarza
Share on other sites

Jeśli na silniki możesz "wystawić" PWM o wartości 0÷255, to czego byś nie robił, powinieneś dać na wyjściu sygnału tzw. saturator, czyli warunek sprawdzania poprawności sygnału, który wychodzi z Twojego regulatora - "jeśli wyjscie < 0, to PWM=0, jeśli wyjscie > 255, to PWM=255". W ten sposób zabezpieczysz się przed przekręceniem zmiennej, która jest 8-bitowa. To powinno pomóc w Twojej wizji algorytmu... W skrajnym przypadku jeden silnik się zatrzyma, drugi będzie się kręcić z PWM=255.

Musisz jednak wiedzieć, że zmienna "change" służy tylko do korekcji, czyli jeśli error=0, to change także powinno być równe 0. Jeśli chcesz użyć klasycznego algorytmu w obecnej postaci, to załóż swoje maxSpeed = 100, a nie 200. Wtedy w przypadku maksymalnego błędu jeden silnik się zatrzyma, a drugi będzie działać z maxSpeed*2, żeby błąd skompensować, a przy błędzie 0 oba silniki będą się kręcić z maxSpeed.

Tylko nie spodziewaj się cudów po samym członie proporcjonalnym, efekt może być nawet gorszy od prostej pętli warunkowej (if...else if...). Cała sztuka to dobrze dobrać współczynnik Kp - w przypadku Linefollowera, to chyba tylko doświadczalnie. Potem trzeba zbytnie "pijaństwo" poprawić członem różniczkującym, inaczej robot będzie albo wolno reagować (małe Kp), albo będzie go ciągle "zarzucać" nawet na prostej (duże Kp). No chyba, że nie zależy Ci na prędkości.

Bardzo dziękuję Ci za odpowiedź na moje pytanie. Nurtuje mnie jeszcze, w jaki sposób obliczać aktualną pozycję? Jako, że czujniki nie pokazują idealnie tych samych wartości, myślę nad czymś takim, aby wstawić wartość graniczną:

void readCurrentPosition()
{
 int lanePosition[4];
 for(int i=0; i < 4; i++)
 {
   if(sensors[i] < 500) lanePosition[i] = 0; // sensors to wartosci analogowe z czujnikow
   else lanePosition[i] = 1;
 }
 int sensorsNumber = 0;
 for(int i = 0; i < 4; i++)
   if(lanePosition[i] == 1) sensorsNumber++;

 if(sensorsNumber == 0) sensorsNumber = 1;

 currentPosition = (-2 * lanePosition[0] - lanePosition[1] + lanePosition[2] + 2 * lanePosition[3]) / sensorsNumber;
}

Pewne ograniczenie, że jeśli odczyt analogowy < 500 to wtedy na pewno jest biała linia, a jeśli > to jest czarna, tylko czy to się wtedy różni w jakiś sposób od tego, jakbym działał na digitalach i czy ma sens? Ale wówczas będę miał kilka możliwych ustawień i error wyniesie 0/1/1.5/2 (maksymalnie 2), więc change = Kp * error. Więc kp = change/2. Wybieram sobie jaką maksymalną korekcję przewiduję i mam kp. Tyle w teorii, jutro to przetestuję.

Czy mój tok rozumowania jest dobry?

PS: W sumie szkoda, że nie mam na środku czujnika, bo bym miał pozycję 0.5 jeszcze 🙂

Link do komentarza
Share on other sites

Pewne ograniczenie, że jeśli odczyt analogowy < 500 to wtedy na pewno jest biała linia, a jeśli > to jest czarna, tylko czy to się wtedy różni w jakiś sposób od tego, jakbym działał na digitalach i czy ma sens?

Dokładnie tak jest w większości linefollowerów. Od działania na digitalach różni się to tym, że nie musisz bawić się w dopasowywanie wartości rezystorów przy fototranzystorach, a tylko w dobranie progu w programie.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

A ja myślę, że pomieszałeś dwie rzeczy. Jedna to - nazwijmy to - system czujników, a druga to regulator. W bloku obsługi czujników powinieneś zrealizować coś takiego:

- wczytywać sygnały analogowe ze wszystkich kanałów,
- analizować wczytane wielkości pod kątem obecności linii, tj. porównywać każdy sygnał analogowy z wzorcem (poziomem) innym dla każdego kanału ustalonym podczas kalibracji (być może nadążnej, dynamicznie reagującej na średni poziom oświetlenia itp),
- zestaw informacji zero-jedynkowych powstałych w poprzednim kroku filtrować pod kątem sytuacji nienormalnych, tj. np. dla 5 czujników stany 00110 lub 01000 są OK, ale już 10011 - raczej nie.

- na tym etapie możesz dodać algorytm analizy behawioralnej czyli algorytm śledzący zachowanie się linii, rozpoznający trendy i sytuacje "dziwne": linia przesuwa się w prawo, jeszcze bardziej w prawo, zniknęła a więc jest gdzieś po prawej od najbardziej prawego czujnika. Możesz tu np. dodać dwa "wirtualne" czujniki będące przewidywanym (na podstawie ostatniego zachowania) położeniem linii poza linią prawdziwych czujników IR, np. kolejne odczyty 00110, 00011, 00001, 0000, 0000... powinny skutkować (przy dodanych 2 bitach skrajnych) czymś takim: 0-00110-0, 0-00011-0, 0-00001-0, 0-0000-1, 0-0000-1... Wcześniej możesz też dodać filtr szerokości ograniczający szerokość wykrytej linii do maksymalnie np. 2 czujników a także zwiększyć liczbę bitów wyjściowych właśnie po to by precyzyjniej określać położenie, np. 5-bitowy stan wejściowy 00110 możesz zamienić na 9-bitowy stan 000001000 poprzez dodanie nowych "czujników" pomiędzy te rzeczywiste, ustawianych gdy dwa obok siebie są aktywne.

- dopiero tak odfiltrowany stan bitów (zawsze tylko z jednym bitem ustawionym) mnożysz przez założone/wymyślone wagi (które też musisz rozsądnie dobrać) i dostajesz analogowe wejście dla regulatora.

W obecnej sytuacji dostajesz jakieś nieskalibrowane wielkości analogowe z czujników które nie mają wiele wspólnego z rzeczywistym położeniem linii i mnożysz je przez wagi uzyskując jakąś absurdalną sumę, którą wpuszczasz do regulatora. W dodatku jesteś bezradny jak dziecko w przypadku błędów odczytu.

Link do komentarza
Share on other sites

A ja myślę, że pomieszałeś dwie rzeczy. Jedna to - nazwijmy to - system czujników, a druga to regulator. W bloku obsługi czujników powinieneś zrealizować coś takiego:

- wczytywać sygnały analogowe ze wszystkich kanałów,
- analizować wczytane wielkości pod kątem obecności linii, tj. porównywać każdy sygnał analogowy z wzorcem (poziomem) innym dla każdego kanału ustalonym podczas kalibracji (być może nadążnej, dynamicznie reagującej na średni poziom oświetlenia itp),
- zestaw informacji zero-jedynkowych powstałych w poprzednim kroku filtrować pod kątem sytuacji nienormalnych, tj. np. dla 5 czujników stany 00110 lub 01000 są OK, ale już 10011 - raczej nie.

- na tym etapie możesz dodać algorytm analizy behawioralnej czyli algorytm śledzący zachowanie się linii, rozpoznający trendy i sytuacje "dziwne": linia przesuwa się w prawo, jeszcze bardziej w prawo, zniknęła a więc jest gdzieś po prawej od najbardziej prawego czujnika. Możesz tu np. dodać dwa "wirtualne" czujniki będące przewidywanym (na podstawie ostatniego zachowania) położeniem linii poza linią prawdziwych czujników IR, np. kolejne odczyty 00110, 00011, 00001, 0000, 0000... powinny skutkować (przy dodanych 2 bitach skrajnych) czymś takim: 0-00110-0, 0-00011-0, 0-00001-0, 0-0000-1, 0-0000-1... Wcześniej możesz też dodać filtr szerokości ograniczający szerokość wykrytej linii do maksymalnie np. 2 czujników a także zwiększyć liczbę bitów wyjściowych właśnie po to by precyzyjniej określać położenie, np. 5-bitowy stan wejściowy 00110 możesz zamienić na 9-bitowy stan 000001000 poprzez dodanie nowych "czujników" pomiędzy te rzeczywiste, ustawianych gdy dwa obok siebie są aktywne.

- dopiero tak odfiltrowany stan bitów (zawsze tylko z jednym bitem ustawionym) mnożysz przez założone/wymyślone wagi (które też musisz rozsądnie dobrać) i dostajesz analogowe wejście dla regulatora.

W obecnej sytuacji dostajesz jakieś nieskalibrowane wielkości analogowe z czujników które nie mają wiele wspólnego z rzeczywistym położeniem linii i mnożysz je przez wagi uzyskując jakąś absurdalną sumę, którą wpuszczasz do regulatora. W dodatku jesteś bezradny jak dziecko w przypadku błędów odczytu.

Hej, bardzo dziękuję Ci, za tak szczegółową odpowiedź. Właśnie poprawiłem to o czym pisałeś tzn. mam już zestaw informacji zero - jedynkowy. Teraz muszę rozwiązać ten problem, który tak dobrze przewidziałeś. Jak zakręty są łagodne to wszystko działa, natomiast jak jest ostry zakręt (prawie 90 stopni) to mam taką sytuację, że robot zamiast nadal skręcać napotyka w pewnym momencie na tą dziwną sytuację tzn. 0010 -> 0001 -> 0000 i zaczyna jechać prosto, a powinien nadal skręcać. Przynajmniej tak mi się wydaję, że tak się dzieje, czy to może być właściwa diagnoza? Ładuję właśnie baterię, a algorytm, który odpowiednio zareaguje w takich sytuacjach napiszę i jak bateria się naładuje to przejdę do dalszych testów.

Ja mam niestety tylko 4 czujniki:

http://i01.i.aliimg.com/wsphoto/v0/1694754433/One-way-Tracking-sensor-module-TCRT5000-tracker-sensor-infrared-reflective-sensor-for-Arduino-Robot-Smart-Car.jpg

Arduino, z którego korzystam ma tylko 6 wyjść analogowych. Lepiej zatem dokupić jeszcze 2 takie czujniki, czy 1? Bo czytałem gdzieś, że lepsza jest nieparzysta ilość, zatem 5, czy 6?

PS: Ale ogólnie z łagodną trasą robot już sobie radzi 😉

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.