Skocz do zawartości

Enkoder Arduino Programowanie


PacTrac

Pomocna odpowiedź

Witam,

Po zakupie enkoderu postanowiłem dziś podłączyć go do Arduino i trochę się nim pobawić. Po podłączeniu i przepisaniu kodu ze strony Arduino moduł sczytuje impulsy, jednakże ma tendencję do gubienia ich. Co może być przyczyną takiego zachowania ?

Enkoder wykorzystany do pracy: http://sklep.cncprofi.com/enkoder-profi-360-06-a-a-b-b-z-z.html

Podłączenie : Wprost do Arduino Vcc-5V, Gnd-Gnd, Pin A do Pinu 2 i Pin B do Pinu 3 ( z rezystorami 300 Ohm).

Kod: ze stronki arduino playground

int val; 
int encoder0PinA = 3;
int encoder0PinB = 4;
int encoder0Pos = 0;
int encoder0PinALast = LOW;
int n = LOW;

void setup() { 
pinMode (encoder0PinA,INPUT);
pinMode (encoder0PinB,INPUT);
Serial.begin (9600);
} 

void loop() { 
n = digitalRead(encoder0PinA);
if ((encoder0PinALast == LOW) && (n == HIGH)) {
if (digitalRead(encoder0PinB) == LOW) {
encoder0Pos--;
} else {
encoder0Pos++;
}
Serial.print (encoder0Pos);
Serial.print ("/");
} 
encoder0PinALast = n;
} 

Pozdrawiam,
TraCerT

Link do komentarza
Share on other sites

Zacznij od wywalenia z pętli wszystkich print'ów. Twój program jest trywialny do bólu i dlatego musi dostać całą moc procesora żeby podglądać zmiany stanów wejść. Jeśli coś go zatrzyma (a port szeregowy, szczególnie używany w taki sposób - gdy ilość danych jest nieograniczona - może związać CPU na długo), przestajesz śledzić wydarzenia i tyle.

Spróbuj wypisywać po zakończeniu eksperymentu z obracaniem, np. na sygnał (z przycisku) na jakimś innym wejściu lub w odpowiedzi na konkretny znak przysłany z komputera. Sprawdzanie czy coś przyszło

if (Serial.available())

oraz odczyt już siedzącego w buforze znaku i jego porównanie z wzorcem praktycznie nie kosztuje nic czasu.

Link do komentarza
Share on other sites

witaj ponownie,

Wywaliłem z programu wszystkie printy i ogólnie tak naprawdę nie muszę nimi się posługiwać, gdyż orientacyjny kąt mogę odczytać przy pomocy wahadła odwrotnego. Ogólnie stabilizacja działa na okres 2-3 sekund, po czym widać, iż enkoder ponownie zaczyna gubić kroki ( objawia się tym, że stabilizacja wahadła nie jest idealnie w pionie, a ok. 175 stopni ). Niestety jestem na tym etapie kompletnie pozbawiony jakichkolwiek myśli, co może być przyczyną takiego a nie innego zachowania. Czy problem istnieje w programie, w ustawieniach arduino czy może tak ma być po prostu - histereza ?

Przedstawiam tutaj kod używany do stabilizacji wahadła:

#define fazaA         2      
#define fazaB         4      
#define rozdzielczosc         360    

int bladcw = 0; // obliczony blad dla ruchu zgodnie ze wskazowkami zegara
int bladccw = 0; // obliczony blad dla ruchu przeciwnie do ruchu wskazowek zegara
int korekcja = 0; // korekcja obrotow silnika w postaci sygnalu PWM
double kp = 12; // wspolczynnik kp
double ki = 1; // wspolczynnik ki
double kd = 0; // wspolczynnik kd
int regulator = 0; // regulator 
int blad = 0; // blad
int ostatniblad = 0; //ostatni blad
int kat = 0; // kat obecny
int zadany = 180; // zadany kat

volatile long pozycja0enkodera = 0;

long poprzedniapozycja = 0;

void setup() {
pinMode(8, OUTPUT);
pinMode(7, OUTPUT);
pinMode(6, OUTPUT);

 pinMode(fazaA, INPUT);
 pinMode(fazaB, INPUT);
 attachInterrupt(digitalPinToInterrupt(2), isr, RISING);

}

int liczPID(int blad)
{
regulator = (kp*(blad)) + (ki*(blad + ostatniblad)) + (kd*(blad - ostatniblad));
ostatniblad = blad;

return constrain(regulator, 50, 60);
}

void loop() { 
kat=abs(pozycja0enkodera);
 if(kat > 160 && kat < 200)
 {
   if(kat > 160 && kat < 178)
   {
     bladcw = (zadany-kat);
     korekcja=liczPID(bladcw);
     analogWrite(6,korekcja);
     digitalWrite(7,HIGH);
     digitalWrite(8,LOW);
   }
   if ( kat < 200 && kat > 182 )
 {
bladccw= (kat-zadany);
korekcja=liczPID(bladccw);
analogWrite(6,korekcja);
digitalWrite(7,LOW);
digitalWrite(8,HIGH);
 }
 }
else
{
 analogWrite(6,0);
 digitalWrite(7,LOW);
 digitalWrite(8,LOW);
}



return;
}

void isr()
{

 int a = digitalRead(fazaA);
 int b = digitalRead(fazaB);

 if (a == b)
{ 
  pozycja0enkodera--;
}
else
{
  pozycja0enkodera++;
}
 pozycja0enkodera = pozycja0enkodera % rozdzielczosc;
}

Pozdrawiam,
TraCerT

Link do komentarza
Share on other sites

Ale to jest inny program. Tu masz enkoder na przerwaniu i tutaj printy będą działały.

Może zróbmy tak: narysuj oba przebiegi z enkodera, zaznacz na wykresie kiedy przychodzi przerwanie i kiedy co robi tj. kiedy inkrementuje a kiedy dekrementuje pozycję. A potem zastanów się co się dzieje podczas zmian kierunku obracania osi. Te chwile są krytyczne, po prostu narysuj sobie kilka przypadków nawrotów w różnych stanach enkodera. Jeśli tam gubisz krok na skutek błędnego algorytmu, to masz przyczynę i bezwzględne położenie zacznie się przesuwać. Kiedyś się na to nadziałem robiąc dekoder kwadraturowy.

Swoją drogą w takich mechanizmach dużo lepiej sprawdzają się enkodery bezwzględne - cyfrowe w kodzie Gray'a lub nawet zwykły potencjometr, bo jest to właśnie oś której bezwzględne położenie chcesz znać. Taki system wstaje od razu po włączeniu zasilania bez jakiejś ręcznej kalibracji na pion, bo zawsze wie gdzie jest ramię.

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

O rany. Jak to jest nieczytelnie napisane.

Wcięcia. Wcięcia pomagają odczytać co miałeś na myśli, często pomagają na szybko znaleźć błędy logiczne - jeśli wcięcia były robione z jakąś intencją, a klamerki się z nimi nie zgadzają.

Zmienne globalne. Jak zmienne są deklarowane dla całego programu, to sugerują, że są używane w całym programie. Jeśli by były deklarowane w ramach bloku, w którym są używane, to od razu widać, że nie występują nigdzie indziej i nie trzeba ich szukać po całym kodzie "gdzie jeszcze występują i na co mają wpływ".

Polskawe identyfikatory: polskie nazwy bez ogonków to jest kaleczenie języka. "kat" to jest kat czy kąt? A "zadany" to jest zadany czy żądany? Takie dylematy nie poprawiają czytelności, a rozpraszają uwagę. "Żądany kat" z komentarza czy "Zadany kąt" 🙂

bladcw, bladccw - cw (clockwise), ccw (counterclockwise) - w jednym wyrazie angielski i polski ? trzeba chwilę wysiłku umysłowego "co autor miał na myśli?"

#define (na przykład #define rozdzielczosc 360) - już się nie używa #define, tylko const. Mniej więcej od 14 lat.

Stałe (const) - jak coś jest stałe, to niech będzie to wskazane:

const double kp = 12; 
const double ki = 1; 
const double kd = 0; 
const int zadany = 180; // Żądany kat

Komentarze bez znaczenia. No całe szczęście, że nie ma sprzeczności między komentarzem a bytem komentowanym.

double kp = 12; // wspolczynnik kp 
int regulator = 0; // regulator 

Globalna zmienna "blad" (nigdzie nie używana) i taki sam parametr funkcji. Ooops.

Te wewnętrzne ify już nie muszą mieć "kat > 160" ani "kat < 200" - procesor się mniej męczy, a człowiekowi łatwiej czytać.

Przypuszczam, że magiczne liczby: 160, 200, 178, 182 mają coś wspólnego ze stałą "zadany=180" określoną na początku. No to jakbyś zmienił kiedyś zadany na 185, to ten zestaw ifów chyba nie spełni Twoich oczekiwań. Nie bój się użyć "zadany-2" - kompilator policzy to za Ciebie, Arduino już tego nie będzie liczyć.

if (kat > 160 && kat < 200)  
   if ( kat > 160 && kat < 178) ...
   if ( kat < 200 && kat > 182 ) ...

Używanie longa do przechowywania liczby z zakresu: -360..360 to trochę rozpusta, szczególnie w przerwaniu.

volatile long pozycja0enkodera = 0;

Po refaktoryzacji (to jest nadal Twój kod):

#define fazaA         2     
#define fazaB         4     

const double kp = 12; 
const double ki = 1; 
const double kd = 0; 

const int zadany = 180; 

volatile int pozycja0enkodera = 0;

long poprzedniapozycja = 0;

void setup() {
   pinMode( 8, OUTPUT );
   pinMode( 7, OUTPUT );
   pinMode( 6, OUTPUT );

   pinMode( fazaA, INPUT );
   pinMode( fazaB, INPUT );
   attachInterrupt( digitalPinToInterrupt( 2 ), isr, RISING );
}

int ostatniblad = 0;

int liczPID( int blad ) {
   int regulator = kp * (blad) + ki * ( blad + ostatniblad ) + kd * ( blad - ostatniblad );
   ostatniblad = blad;
   return constrain( regulator, 50, 60 );
}

void loop() {
   int kat = abs( pozycja0enkodera ); 

   if ( abs( zadany - kat ) < 20 ) {
       if ( kat < zadany - 2 ) {
           int bladcw = zadany - kat; // obliczony blad dla ruchu zgodnie ze wskazowkami zegara            
           int korekcja = liczPID( bladcw ); // korekcja obrotow silnika w postaci sygnalu PWM
           analogWrite( 6, korekcja );
           digitalWrite( 7, HIGH );
           digitalWrite( 8, LOW );
       }
       if ( kat > zadany + 2 ) {
           int bladccw = ( kat - zadany ); // obliczony blad dla ruchu przeciwnie do ruchu wskazowek zegara
           int korekcja = liczPID( bladccw ); // korekcja obrotow silnika w postaci sygnalu PWM
           analogWrite( 6, korekcja );
           digitalWrite( 7, LOW );
           digitalWrite( 8, HIGH );
       }
   } else {
       analogWrite( 6, 0 );
       digitalWrite( 7, LOW );
       digitalWrite( 8, LOW );
   }
   return;
}

void isr() {
   int a = digitalRead( fazaA );
   int b = digitalRead( fazaB );
   if ( a == b ) {
       pozycja0enkodera--;
   } else {
       pozycja0enkodera++;
   }
   pozycja0enkodera = pozycja0enkodera % 360;
}

====

Teraz o działaniu:

Chyba się numery pinów zmieniły w porównaniu z Twoim pierwszym programem?

Start systemu:

czy aby pozycja enkodera na dzień dobry nie powinna być 180?

volatile int pozycja0enkodera = 180;

Po pierwsze: funkcja liczPID().

Dostaje argumenty typu int (oraz ostatniblad też int), o wartości co najmniej 3, ten constrain - nie za agresywny? Prawie zawsze zwróci 50 albo 60, mało jest sytuacji, kiedy coś pośrodku, jak jesteś blisko równowagi to na pewno chcesz wyłączyć sterowanie? Czyli utrzymać istniejący stan rzeczy: regulator = 50?

Pętla główna:

Jak się domyślam, to piny 7 i 8 maja sterować polaryzacją.

Histereza wynika z logiki: jeśli mniej, niż 180, to błąd jest 180-kat, jeśli więcej niż 180, to błąd jest kat-180, zawsze blad jest dodatni, zawsze ostatniblad jest dodatni, regulator dostaje w zasadzie te same dane przy przejściu z 177 na 176, jak i ze 177 na 184. Wokół 180 nie będzie chciał oscylować, będzie oscylować wokół czegoś mniejszego niż 178 albo czegoś większego niż 182.

To może tak?

int liczPID( int blad ) {
   int regulator = kp * (blad) +ki * ( blad + ostatniblad ) + kd * ( blad - ostatniblad );
   ostatniblad = blad;
   if ( regulator > 0 )
       return constrain( regulator, 50, 60 );
   else
       return constrain( regulator, -60, -50 );
}

void loop() {
   int kat = abs( pozycja0enkodera );
   if ( abs( zadany - kat ) < 20 ) {
       int err = zadany - kat;
       int korekcja = liczPID( err ); // korekcja obrotow silnika w postaci sygnalu PWM
       analogWrite( 6, abs( korekcja ) );
       if ( korekcja > 0 ) {         // albo odwrotnie - nie widzę schematu
           digitalWrite( 7, HIGH );
           digitalWrite( 8, LOW );
       } else {
           digitalWrite( 7, LOW );
           digitalWrite( 8, HIGH );
       }
   } else {
       analogWrite( 6, 0 );
       digitalWrite( 7, LOW );
       digitalWrite( 8, LOW );
   }
   return;
}

Albo nawet tak:

int liczPID( int blad ) {
   int regulator = kp * (blad) +ki * ( blad + ostatniblad ) + kd * ( blad - ostatniblad );
   ostatniblad = blad;
   return constrain( regulator, -60, 60 );
}

Best regards.

  • Pomogłeś! 1
Link do komentarza
Share on other sites

Funkcja liczPID jest zła bo:

regulator = kp * (blad) +ki * ( tu powinno być całkowanie, a nie suma 2 ostatnich błędów) + kd * ( tu powinno być różniczkowanie );

Jeśli chodzi o impulsator bez obsługi przerwań to proponuje funkcję:

int impulser(byte pinS, byte pinL, byte pinR){
  int w=0,a=digitalRead(pinL);
   if(a != digitalRead(pinR)){
    while(digitalRead(pinL) != digitalRead(pinR));
    if(a == digitalRead(pinR)) w = -1; //left
    else w = 1; // right
   }
   else if(!digitalRead(pinS)) w = 2; //switch
  return w;
  }

Piny: pinS -przycisk;  pinL, pinR - ustawiamy wcześniej na tryb INPUT_PULLUP

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • 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.