• 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
problem ze zdalnym sterowaniem IR
#1
Cześć wszystkim!

Od dłuższego czasu głowię się nad rozwiązaniem swojego problemu, związanego ze zdalnym sterowaniem i w końcu postanowiłem poprosić o pomoc na tym forum. 
Próbuję zbudować własnego robota na bazie Arduino UNO. Większość mojej wiedzy i kodu pochodzi ze strony forbot.pl. 
Robot zbudowany jest z dwóch silników - każdy napędza jedno koło oraz wyposażony jest w czujnik odległości imitujący czujnik parkowania.
Problem polega na tym, że wywołując razem dwie funkcje Pilot i Buzzer w pętli, mój robot przestaje reagować na polecenia z pilota natomiast działa sam brzęczyk (imitacja czujnika parkowania). Natomiast kiedy wywołam samą funkcję Pilot, robot reaguje na polecenia jazdy zgodnie z założeniami. Po wielu testach doszedłem w końcu do tego, że zawadza mi instrukcja PulseIn pośrednio biorąca udział w funkcji Buzzer (w funkcji Buzzer zamieszczona jest funkcja DistanceSensor, która korzysta z PulseIn). Czy jest to możliwe, że biblioteka RC5 i instrukcja PulseIn jakoś się zakłócają? Jak można sobie z tym poradzić?

SERDECZNIE PROSZĘ O POMOC

Kod:
Kod:


#define L_DIR 4                                            // pin 4 - kierunek obrotów silnik lewy (0 do przodu; 1 do tyłu)

#define L_SPD 5                                            // pin 5 - prędkość obrotów silnik lewy

#define R_DIR 9                                            // pin 9 - kierunek obrotów silnik prawy (0 do przodu; 1 do tyłu)
#define R_SPD 6                                            // pin 6 - prędkość obrotów silnik prawy

#define SPD_MAX 150                                        // maksymalna prędkość robota, która nie uszkodzi silników

#define BZR 10                                              // pin 10 - buzzer
#define TRIG 7                                              // pin 7 - wyzwolenie czujnika odległości (YELLOW)
#define ECHO 8                                              // pin 8 - pomiar odległości (GREEN)
#define IR 3                                                // pin 3 - odbiornik podczerwieni

#include <RC5.h>                                            // biblioteka odpowiedzialna za sterownie pilotem
RC5 RC5(IR);

//zmienne do komunikacji IR
byte adress_device;
byte command_for_device;
byte toggle;

//zmienne do obsługi czujnika odległości
long measured_time;
long measured_distance;


void setup()
{
  //uruchomienie komunikacji UART
  Serial.begin(9600);

  // ustawienie zdefiniowanych pinów mostka H jako wyjścia (sterowanie silnikami)
  pinMode (L_DIR, OUTPUT);                                
  pinMode (L_SPD, OUTPUT);
  pinMode (R_DIR, OUTPUT);
  pinMode (R_SPD, OUTPUT);

  // ustawienie pozostałych pinów
  pinMode (BZR, OUTPUT);
  pinMode (TRIG, OUTPUT);
  pinMode (ECHO, INPUT);
}


// funkcja do sterownia lewym silnikiem - za pomocą parametru SPEED określamy prędkość i kierunek obrotu lewego silnika
void LeftMotor (int SPEED)
{                              
if (SPEED > 100 || SPEED < -100 ) {goto speedError;}        // zabezpieczenie prędkości maksymalnej
                            else {goto speedControl;}    
  
        speedError:                                        // jeśli prędkość silnika będzie z innego zakresu niż (-100; 100) to ERROR
  digitalWrite (BZR, HIGH);
  delay (500);
  digitalWrite (BZR, LOW);
  Serial.println(" SpeedERROR - niebezpieczna prędkość ");
  delay (10000);
  goto speedError;

        speedControl:
    if (SPEED > 0) {                                        // jeśli zmienna SPEED jest dodatnia to lewy silnik obraca się "do przodu"
    map (SPEED, 0, 100, 0, SPD_MAX);                        // skalowanie wartości SPEED
    digitalWrite (L_DIR, 0);                                // ustawienie kierunku obrotów silnika (0 - do przodu)
    analogWrite (L_SPD, SPEED);                            // ustawienie prędkości silnika lewego
    }
    else {                                                  // jeśli zmienna SPEED jest ujemna to lewy silnik obraca się "do tyłu"
      SPEED = abs(SPEED);                                  // zwrócenie wartości absolutnej (bez znaku)
      map (SPEED, 0, 100, 0, SPD_MAX);                      // skalowanie wartości SPEED
      digitalWrite (L_DIR, 1);                              // ustawienie kierunku obrotów silnika (1 - do tyłu)
      analogWrite (L_SPD, SPEED);                          // ustawienie prędkości silnika lewego
    }
}

// funkcja do sterownia prawym silnikiem - za pomocą parametru SPEED określamy prędkość i kierunek obrotu prawego silnika
void RightMotor (int SPEED)
{                              
if (SPEED > 100 || SPEED < -100 ) {goto speedError;}        // zabezpieczenie prędkości maksymalnej
                            else {goto speedControl;}  

        speedError:                                        // jeśli prędkość silnika będzie z innego zakresu niż (-100; 100) to ERROR
  digitalWrite (BZR, HIGH);
  delay (500);
  digitalWrite (BZR, LOW);
  Serial.println(" SpeedERROR - niebezpieczna prędkość ");
  delay (10000);
  goto speedError;
    
      speedControl:
    if (SPEED > 0) {                                    // jeśli zmienna SPEED jest dodatnia to prawy silnik obraca się "do przodu"
    map (SPEED, 0, 100, 0, SPD_MAX);                        // skalowanie wartości SPEED
    digitalWrite (R_DIR, 0);                                // ustawienie kierunku obrotów silnika (0 - do przodu)
    analogWrite (R_SPD, SPEED);                            // ustawienie prędkości silnika lewego
    }
    else {                                                  // jeśli zmienna SPEED jest ujemna to prawy silnik obraca się "do tyłu"
      SPEED = abs(SPEED);                                  // zwrócenie wartości absolutnej (bez znaku)
      map (SPEED, 0, 100, 0, SPD_MAX);                      // skalowanie wartości SPEED
      digitalWrite (R_DIR, 1);                              // ustawienie kierunku obrotów silnika (1 - do tyłu)
      analogWrite (R_SPD, SPEED);                          // ustawienie prędkości silnika lewego
    }
}

// funkcja do zatrzymania silnika prawego
void StopRightMotor()
{
  analogWrite (R_SPD, 0);
}

// funkcja do zatrzymania silnika lewego
void StopLeftMotor ()
{
  analogWrite (L_SPD, 0);
}

// funkcja do obsługi czujnika odległości zwracająca odległość do przeszkody
int DistanceSensor ()
{

digitalWrite (TRIG, LOW);                                // zmiana stanu sygnału z wysokiego na niski na wyjściu TRIG powoduje wyzowlenie pomiaru
delayMicroseconds (5);
digitalWrite (TRIG, HIGH);
delayMicroseconds (15);
digitalWrite (TRIG, LOW);

measured_time = pulseIn(ECHO, HIGH);                    // dokonanie pomiaru czasu na wyjściu ECHO
measured_distance = measured_time / 58 ;                // zmierzony czas podzielony przez 58 daje odległość w cm

return measured_distance;

}

// funkcja do wyzwolenia alarmu w zależności od odległości od przeszkody
void Buzzer()
{
    if (DistanceSensor() < 15)
    {
    digitalWrite (BZR, HIGH);
    }
  if ((DistanceSensor() > 15) && (DistanceSensor() < 35))
  {
  digitalWrite (BZR, HIGH);
  delay (200);
  digitalWrite (BZR, LOW);
  }
    if (DistanceSensor() > 35)
    {
    digitalWrite (BZR, LOW);
    }
}

void Pilot ()
{

// dokonuje odcztu z pilota po wciśnięciu przycisku
  RC5.read(&toggle, &adress_device, &command_for_device);
  {
    switch (command_for_device)
    {
  // jazda do prozdu po wciśnięciu przycisku "2"  
      case 2:
      LeftMotor (90);
      RightMotor (90);
      break;
    
  // jazda do tyłu po wciśnięciu przycisku "8"
      case 8:
      LeftMotor (-90);
      RightMotor (-90);
      break;

  // jazda w prawo po wciśnięciu przycisku "6"
      case 6:
      LeftMotor (90);
      RightMotor (-90);
      break;

  // jazda w lewo po wciśnięciu przycisku "4"
      case 4:
      LeftMotor (-90);
      RightMotor (90);
      break;
    
  // zatrzymanie silników po wciśnięciu przycisku "5"
      case 5:
      StopLeftMotor();
      StopRightMotor();
      break;
    }
  }
}



void loop()
{

Pilot ();

Buzzer ();

}

Z góry bardzo dziękuję za pomoc i wskazówki Smile
 
Odpowiedź
#2
Te biblioteki się nie gryzą.
Funkcja Pilot() czeka na zakończenie funkcji Buzzer(), a w funkcji Buzzer() jest while, która czeka na echo licząc w międzyczasie micros().
Tak sobie myślę, że nadajnik podczerwieni, to nie nadaje tych impulsów jeden od razu za drugim, tylko robi pauzy i impulsów jest dwa, może cztery, na sekundę. W momencie nadawania z pilota program musi wejść w funkcję Pilot(), co raczej się nie uda, skoro większość czasu jesteśmy w funkcji Buzzer().
Szkoda że Arduno nie ma debugera, to byś to sobie zobaczył naocznie.
W funkcji Buzzer() wywołujesz funkcję DistanceSensor() aż cztery razy.

Ja bym zmienił to:

Kod:
void Buzzer()
{
    if (DistanceSensor() < 15)
    {
    digitalWrite (BZR, HIGH);
    }
  if ((DistanceSensor() > 15) && (DistanceSensor() < 35))
  {
  digitalWrite (BZR, HIGH);
  delay (200);
  digitalWrite (BZR, LOW);
  }
    if (DistanceSensor() > 35)
    {
    digitalWrite (BZR, LOW);
    }
}

Na to:

Kod:
void Buzzer()
{
    int dystans = DistanceSensor();
    if (dystans < 15)
    {
    digitalWrite (BZR, HIGH);
    }
  if (dystans > 15) && (dystans < 35))
  {
  digitalWrite (BZR, HIGH);
  delay (200);
  digitalWrite (BZR, LOW);
  }
    if (dystans > 35)
    {
    digitalWrite (BZR, LOW);
    }
}

I funkcja DistanceSensor() będzie wywoływana cztery razy rzadziej.
Po drugie powinieneś określić parametr timeout dla funkcji pulseIn(). Po co czekać na echo od ściany oddalonej o 10 metrów skoro największa wartość jaką używasz w kodzie to 35 centymetrów?
Jeden metr to pewnie jakieś 3000 mcros(), wię ustaw timeout na 1500.

Takie są moje doraźne rady, ale ja bym całkowicie wywalił tę funkcję pulseIn() i przechwytywał czas w przerwaniu od Timera w trybie ICP.
Jeśli masz problem z kodem lub sprzętem, zadaj pytanie na forum. Nie odpowiadam na PW, jeśli nie dotyczą one spraw forum lub innych tematów prywatnych.

[Obrazek: SsIndaG.jpg]
 
Odpowiedź
#3
Kurcze, czapka z głowy, pięknie dziękuję za pomoc, wskazówki i wyjaśnienia!
Jak wrócę do domu to na pewno potestuję zaproponowane rozwiązania.

Później dam znać czy się udało, jeszcze raz dzięki Wink
 
Odpowiedź
#4
Wprowadziłem zmiany, o których wspomniałeś (wyliczyłem odpowiedni timeout + ograniczyłem ilość wywołań funkcji DistanceSensor()). Niestety robot dalej nie reaguje na polecenia z pilota, kiedy wywoływana jest funkcja Buzzer().

"Takie są moje doraźne rady, ale ja bym całkowicie wywalił tę funkcję pulseIn() i przechwytywał czas w przerwaniu od Timera w trybie ICP."

Czy mógłbyś mi nieco wytłumaczyć co masz na myśli pisząc zdanie, które zacytowałem?
 
Odpowiedź
#5
Nie mam czasu, więc będzie krótko. 
W Atmega 328 są trzy zegary/liczniki. TC0 i TC2 są 8bit, a TC1 jest 16bit.
Timer1 daje nam możliwość przechwytywania wejścia PB0, czyli dla Arduino będzie to pin 8 (można jeszcze z wyjścia komparatora analogowego, ale ja nie o tym).
Może napiszę bardziej profesjonalnie.
Mamy Jednostkę Przechwytującą Sygnały Wejściowe, która potrafi przechwytywać zdarzenia zewnętrzne i nadawać im znacznik czasowy.
Jeśli na pinie 8 nastąpi zmiana poziomu i będzie ona zgodna z ustawieniem wykrywania zbocza, to nastąpi zatrzask.
Tzn. dane zostaną zatrzaśnięte.
Wartość licznika TCNT1 zapisana zostaje do rejestru ICR1. Wskaźnik stanu ICF1 jest ustawiany na 1, co generuje przerwanie ISR(TIMER1_CAPT_vect).
I tyle w temacie.

Teraz, jak to ugryźć?

Ja bym ustawił Timer1 na liczenie z preskalerem na 1, oraz wykrywaniem zbocza rosnącego:
Kod:
    TCCR1B = (1 << ICES1) | (1 << CS10);

Następnie włączył przerwania od ICP1, oraz od przepełnienia licznika.

Kod:
    TIMSK1 = (1 << ICIE1) | (1 << TOIE1);

Ogólnie na początku proponuję wyłączyć zegar:

Kod:
TCCR1B = (0 << ICES1)

Włączymy go w funkcji inicjującej pomiar.

Po co przerwanie od przepełnienia ISR(TIMER1_OVF_vect) ?
Ano dla tego, że jak napiszemy sobie jakąś funkcję, która wygeneruje nam impuls z SR04, np:

Kod:
void pomiarStart()
{
  digitalWrite(trigPIN, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPIN, HIGH);

  TCCR1B = 1 << CS10;   /* Start zegara */

  delayMicroseconds(10);
  digitalWrite(trigPIN, LOW);
}


To nasz licznik będzie się zwiększał co jeden takt zegara, aż osiągnie wartość 65535. Następnie wygeneruje przerwanie od przepełnienia, wyzeruje się i zacznie liczyć od nowa. Cała ta procedura trwa dosyć krótko, więc w oczekiwaniu na echo parę razy się przepełni zanim SR04 wygeneruje nam przerwanie od ICP1, więc powinniśmy zadeklarować zmienną globalną np:

Kod:
volatile unsigned int licznik;

i zwiększać go w przerwaniu od przepełnienia:

Kod:
ISR(TIMER1_OVF_vect)
{
  licznik++;
}

W przerwaniu od ICP odczytujemy znacznik czasowy, ilość przepełnień timera i liczymy ile czasu upłynęło od jego startu.
Trzeba pamiętać, że zmienna przechowująca czas powinna być globalna typu volatile int, czy volatile unsigned long
Np: volatile unsigned long czas;
Czyli w przerwaniu od ICP mogło by być na przykład to:

Kod:
ISR(TIMER1_CAPT_vect)
{
  czas = (ICR1 + licznik*65536)/16000000;
  TCCR1B = 0 << CS10; /* Zegar stop */
  licznik = 0;
}

Nie wiem czy to dobrze napisałem, bo dawno nie bawiłem się ośmiobitowcami, ale ogólna zasada została miej więcej wyjaśniona.
Ważne, że całą robotę wykonują układy wewnętrzne, procesor się leni, więc może w tym czasie zająć się odbiorem IR, czy wyświetlaniem czegoś na LCD.
Jeśli masz problem z kodem lub sprzętem, zadaj pytanie na forum. Nie odpowiadam na PW, jeśli nie dotyczą one spraw forum lub innych tematów prywatnych.

[Obrazek: SsIndaG.jpg]
 
Odpowiedź
#6
Stokrotne dzięki za pomoc i poświęcony czas! 

Chyba jeszcze nie wszystko jest dla mnie jasne, ale bardzo mi pomogłeś. Niestety w najbliższym czasie, nie będę mógł się zająć studiowaniem tego, ale będę informował o postępach.
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości