• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Odtwarzanie trasy przejazdu robota - poprawność programu
#3
Dziękuję bardzo za te wskazówki.
Zastosowałem się do nich i aktualnie kod wygląda następująco:

Kod:
#define B_ENC 7 //enkoder silnika A
#define A_ENC 3 //enkoder silnika B
#define STDBY 4 //uśpienie sterownika silników
#define ALR 5 //sygnał alarmu
#define A_IN1 2 //zmiana kierunku obrotów silnika prawego
#define A_IN2 6 //
#define A_PWM 9 //wyjście PWM na silnik prawy
#define B_IN1 8 //zmiana kierunku obrotów silnika prawego
#define B_IN2 11 //
#define B_PWM 10 //wyjście PWM na silnik lewy
volatile int A_enc = 0; // impulsy enkodera silnika A
volatile int B_enc = 0; // impulsy enkodera silnika B
int line = 0; //numer wiersza macierzy (krok wykonania ruchu powrotnego)
unsigned int macierz [40][4] = {}; //tablica danych trasy przejazdu
int Speed1 = 120; //prędkość obrotów silników w trybie normalnym
int Speed2 = 65; //prędkość obrotów silników w trybie nauki
int Speed3 = 65; //prędkość obrotów silników w trybie powrotu
const int w_zad = 0; //wartość zadana
const int k_p = 3; //współczynnik wzmocnienia członu P regulatora
const int k_d = 1; //stała różniczkowania regulatora
volatile int p; //wartość wyjściowa członu typu P
volatile int d; //wartość wyjściowa członu typu D
volatile int u; //aktualny uchyb regulacji
volatile int u_p = 0; //poprzedni uchyb regulacji
volatile int out; // sygnał wyjściowy regulatora
volatile int diff = 0; //różnica wskazań enkoderów
volatile int x; // regulowana wartość PWM A
volatile int y; // regulowana wartość PWM B
boolean Forward = 0; //znacznik jazdy do przodu
boolean Backward = 0; //znacznik jazdy do tyłu
boolean Left = 0; //znacznik jazdy w lewo
boolean Right = 0; //znacznik jazdy w prawo
char state = 0; //odczyt z bluetooth(SerialPort1)
int meas = 0; // wartość odczytana z ADC
float volt; // obliczona wartość napięcia

void setup() {

  pinMode(A_ENC, INPUT_PULLUP); //kofiguracja wejść i wyjść
  pinMode(B_ENC, INPUT_PULLUP);
  pinMode(ALR, OUTPUT);
  pinMode(STDBY, OUTPUT);
  pinMode(A_IN1, OUTPUT);
  pinMode(A_IN2, OUTPUT);
  pinMode(A_PWM, OUTPUT);
  pinMode(B_IN1, OUTPUT);
  pinMode(B_IN2, OUTPUT);
  pinMode(B_PWM, OUTPUT);
  Serial1.begin(9600); //włączenie transmisji szeregowej i ustawienie prędkości
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(A_ENC), COUNT_A, CHANGE); // włączenie przerwań zewnętrznych
  attachInterrupt(digitalPinToInterrupt(B_ENC), COUNT_B, CHANGE);
}
void loop() {
  
  meas = analogRead(A0);   // pomiar napięcia baterii
  volt = meas * (0.0097752);  // przeliczenie na wartość dziesiętną
  Serial1.print(" "); // wysłanie wartości przez bluetooth
  Serial1.print(volt); //
  
  while (volt < 5.0){ //kontrola napięcia
    digitalWrite(ALR, !digitalRead(ALR)); // napięcie poniżej 5V włącza alarm
    Stop();
    delay(500);
  }
  digitalWrite(ALR, LOW); // wyłączenie alarmu
  
  if (state == '0') { // czyszczenie odczytów z enkoderów
    clearenc();
  }
  if (Serial1.available()) { // odczyt danych z bluetooth
    state = Serial1.read(); // przypisanie odebranej danej do zmiennej
  }
  switch (state) {
    case '0': //stop
      Stop();
      clearenc();
      break;
    case '1': //
      forward(Speed1); //do przodu
      break;
    case '2':
      backward(Speed1); //do tyłu
      break;
    case '3':
      left(Speed1); //w lewo
      break;
    case '4':
      right(Speed1); //w prawo
      break;
    case '5': // nauka
      clearenc();
      break;
    case '6':                          // powrót
      for (char  i = 39; i >= 0; i--) {
        clearenc();
        if (macierz[i][0] > 0) {
          while (A_enc < macierz[i][0]) { //do tyłu
            backward(Speed3);
          }
          Stop();
          macierz[i][0] = 0;
        }
        else if (macierz[i][1] > 0) { // do przodu
          while (A_enc < macierz[i][1]) {
            forward(Speed3);
          }
          Stop();
          macierz[i][1] = 0;
        }
        else if (macierz[i][2] > 0) { // w prawo
          while (A_enc < macierz[i][2]) {
            right(Speed3);
          }
          Stop();
          macierz[i][2] = 0;
        }
        else if (macierz[i][3] > 0) { // w lewo
          while (A_enc < macierz[i][3]) {
            left(Speed3);
          }
          Stop();
          macierz[i][3] = 0;
        }
      }
      line = 0;
      break;
    case '7': //do przodu w trybie nauki
      forward(Speed2);
      Forward = 1;
      break;
    case '8': //do tyłu w trybie nauki
      backward(Speed2);
      Backward = 1;
      break;
    case '9': //w lewo w trybie nauki
      left(Speed2);
      Left = 1;
      break;
    case 'A': //w prawo w trybie nauki
      right(Speed2);
      Right = 1;
      break;
    case 'B':
      Stop(); // zatrzymanie w trybie nauki + zapamiętanie przejechanej trasy
      if (Forward == 1) { //zapisanie drogi do przodu
        macierz [line] [0] = A_enc;
      }
      else if (Backward == 1) { //zapisanie drogi do tyłu
        macierz [line] [1] = A_enc;
      }
      else if (Left == 1) { //zapisanie obrotu w lewo
        macierz [line] [2] = A_enc;
      }
      else if (Right == 1) { //zapisanie obrotu w prawo
        macierz [line] [3] = A_enc;
      }
      line ++;
      Forward = 0;
      Backward = 0;
      Left = 0;
      Right = 0;
      clearenc();
      break;
  }
}
void COUNT_A() { //zliczanie impulsów ennkodera silnika A
  A_enc++;  
}  
void COUNT_B() { //zliczanie impulsów ennkodera silnika B
  B_enc++;
}
void forward(int Speed) { //funkcja jazdy do przodu + synchronizacja prędkości obrotowej silników
  diff = A_enc - B_enc;
  u = w_zad - diff;
  p = k_p * u;
  d = k_d * (u_p - u);
  out = p + d;
  u_p = u;
  x = Speed - out;
  y = Speed + out;
  if(x > 255){
    x = 255;
  }else if(x < 35){
    x = 0;
  }
  if(y > 255){
    y = 255;
  }else if(y < 35){
    y = 0;
  }
  digitalWrite(A_IN1, HIGH);
  digitalWrite(A_IN2, LOW);
  digitalWrite(B_IN1, LOW);
  digitalWrite(B_IN2, HIGH);
  analogWrite(B_PWM, x);
  analogWrite(A_PWM, y);
  digitalWrite(STDBY, HIGH);
}
void backward(int Speed) { //funkcja jazdy do tyłu + synchronizacja prędkości obrotowej silników
  diff = A_enc - B_enc;
  u = w_zad - diff;
  p = k_p * u;
  d = k_d * (u_p - u);
  out = p + d;
  u_p = u;
  x = Speed - out;
  y = Speed + out;
  if(x > 255){
    x = 255;
  }else if(x < 35){
    x = 0;
  }
  if(y > 255){
    y = 255;
  }else if(y < 35){
    y = 0;
  }
  digitalWrite(A_IN1, LOW);
  digitalWrite(A_IN2, HIGH);
  digitalWrite(B_IN1, HIGH);
  digitalWrite(B_IN2, LOW);
  analogWrite(B_PWM, x);
  analogWrite(A_PWM, y);
  digitalWrite(STDBY, HIGH);
}
void left(int Speed) { //funkcja jazdy w lewo  + synchronizacja prędkości obrotowej silników
  diff = A_enc - B_enc;
  u = w_zad - diff;
  p = k_p * u;
  d = k_d * (u_p - u);
  out = p + d;
  u_p = u;
  x = Speed - out;
  y = Speed + out;
  if(x > 255){
    x = 255;
  }else if(x < 35){
    x = 0;
  }
  if(y > 255){
    y = 255;
  }else if(y < 35){
    y = 0;
  }
  digitalWrite(A_IN1, HIGH);
  digitalWrite(A_IN2, LOW);
  digitalWrite(B_IN1, HIGH);
  digitalWrite(B_IN2, LOW);
  analogWrite(B_PWM, x);
  analogWrite(A_PWM, y);
  digitalWrite(STDBY, HIGH);
  
}
void right(int Speed) { //funkcja jazdy w prawo  + synchronizacja prędkości obrotowej silników
  diff = A_enc - B_enc;
  u = w_zad - diff;
  p = k_p * u;
  d = k_d * (u_p - u);
  out = p + d;
  u_p = u;
  x = Speed - out;
  y = Speed + out;
  if(x > 255){
    x = 255;
  }else if(x < 35){
    x = 0;
  }
  if(y > 255){
    y = 255;
  }else if(y < 35){
    y = 0;
  }
  digitalWrite(A_IN1, LOW);
  digitalWrite(A_IN2, HIGH);
  digitalWrite(B_IN1, LOW);
  digitalWrite(B_IN2, HIGH);
  analogWrite(B_PWM, x);
  analogWrite(A_PWM, y);
  digitalWrite(STDBY, HIGH);
}
void Stop() {
  digitalWrite(A_IN1, HIGH); // stop
  digitalWrite(A_IN2, HIGH);
  digitalWrite(B_IN1, HIGH);
  digitalWrite(B_IN2, HIGH);
  analogWrite(A_PWM, 0);
  analogWrite(B_PWM, 0);
  delay(100);
  digitalWrite(STDBY, LOW);
}
void clearenc() { //funkcja czyszczenia odczytów z enkoderów
  A_enc = 0;
  B_enc = 0;
}

Udało się zlikwidować problem wyjścia z pętli while. Powodem było operowanie przez kompilator zawartością rejestru pamięci, zamiast, zamiast odwoływać się do zawartości komórki pamięci (optymalizacja). Zastosowanie specyfikatora volatile do zmiennych A i B rozwiązało problem. Dodatkowo wydłużyłem ich nazwy do A_enc i B_enc, dzięki czemu teraz można je łatwiej wyszczególnić poprzez ctrl+f.
Jeśli chodzi o oszczędzanie pamięci RAM, to zwróciłem na to uwagę, jednak tablicy "macierz" specjalnie przypisałem typ 16-bit int, ponieważ zapisywane są w niej wartości zliczonych impulsów enkoderów, a więc są to duże wartości przy rozdzielczości 1440 imp/obr. Dodałem nawet znacznik unsigned, aby wydłużyć zakres do 65535, ponieważ wartości te zawsze przyjmują wartości dodatnie. Może okazać się nawet, że przy dłuższych przejechanych odcinkach wartość do zapisania będzie większa niż zakres uint16_t, więc będzie trzeba zastosować inny typ. Oczywiście wszystko w miarę rozsądku, aby zmieścić się w ilości dostępnej pamięci.
Aktualnie sprawa z pamięcią wygląda tak:
"Szkic używa 9852 bajtów (34%) pamięci programu. Maksimum to 28672 bajtów.
Zmienne globalne używają 699 bajtów (27%) pamięci dynamicznej, pozostawiając 1861 bajtów dla zmiennych lokalnych. Maksimum to 2560 bajtów."
Wracając do problemu błędnego odtwarzania drogi powrotu, to podejrzewam jedną przyczynę:
Impulsy z enkoderów są zliczane w podprogramach przerwać zewnętrznych. W danym momencie może wykonywać się tylko jeden podprogram od jednego przerwania, ponieważ na czas procedury obsługi przerwania procesor wyłącza możliwość wystąpienia innych przerwań. Być może poszczególne zbocza z dwóch enkoderów nakładają się w czasie lub są oddalone od siebie o czas krótszy niż czas wykonania procedury przerwania i wtedy zliczenie impulsu z drugiego enkodera zostaje pominięte? Tylko jak taką sytuację wyeliminować?

P.S. Uśrednianie pomiaru napięcia wykonam w późniejszym czasie, ponieważ nie jest to dla mnie aktualnie priorytetem. Jednak na pewno to poprawię w swoim czasie Smile
 
Odpowiedź
  


Wiadomości w tym wątku
RE: Odtwarzanie trasy przejazdu robota - poprawność programu - przez BourbonKid - 06-01-2019, 15:33

Skocz do:


Przeglądający: 1 gości