Skocz do zawartości

HC-SR04 - Wielofunkcyjność programowa na podstawie czujnika odległości. Modyfikacja programu


Forseti

Pomocna odpowiedź

To ja mam pytanie: nauczyłeś się o maszynie stanów? Bo to co chcesz zrobić to się tak właśnie nazywa, tylko nie wiadomo po co chcesz ją wynaleźć od nowa.

Co rozumiesz przez "przeskakiwanie między casami"? Case to po prostu miejsce w programie a nie instrukcja - pisałem o tym ostatnio:

Zapoznaj się może z tym, bo bez sensu by było pisanie tego samego tylko w innym wątku. A o maszynie stanów ktoś tu kiedyś pisał tylko nie mogę teraz znaleźć - ale znajdź sobie w googlach "maszyna stanów" albo "automaty skończony".

Link do komentarza
Share on other sites

(edytowany)

@ethanak Tak czytałem. Po prostu jeszcze tego nie rozumiem i mi się to wszystko pierniczy. To moje pierwsze kroki w świecie programowania.... ostatni raz miałem do czynienia z Pascalem w podstawówce. 

Ok. Biorę się dogłębniej za maszynę stanów. To wygląda ciekawie.

 

Edytowano przez Forseti
Link do komentarza
Share on other sites

(edytowany)

Smutna konstatacja.

Nie wygląda na to abym opanował to co chcę w relatywnie zadowalającym czasie (którego niestety nie mam 😡 ). Nauczyłem się na tyle poruszać w środowisku absolutnie mi obcym aby raczej poradzić sobie z już gotowymi projektami (kodami) i ich obróbką do (z grubsza) moich potrzeb. Zrobienie robota jednofunkcyjnego jest w moim zasięgu. Zajmę się mechaniką i elektroniką oraz zbuduję platformę, do której będę po prostu wgrywał Program_1 lub Program_2 w zależności od potrzeby zabawy robotem z dzieciakiem.  Oczywiście zrobię to tak, że będzie można w przyszłości napisać mój kod, który przedstawiłem na diagramie. Czas, który poświęcam na naukę i zasadniczo żenujący brak postępów skłania mnie do zmiany podejścia. Najpierw zbuduję robota ze wszystkimi niezbędnymi elementami, a potem będę próbował połączyć dwa kody w jedno. Nie chcę przez rok stać z projektem tylko dlatego, że nie ogarniam logiki. To bez sensu bo ostatecznie cały temat pójdzie do kosza.  

Nie piszę tego aby narzekać ale po to aby podziękować osobom, które przez ostatni miesiąc bardzo mi pomogły zrozumieć zarys pracy kodów w Arduino. 

Dziękuję w szczególności @farmaceuta za tonę cierpliwości na PW (niech Twe imię sławią w przedszkolach 🙂 ) oraz pozostałym Wam za wskazówki.

Edytowano przez Forseti
Link do komentarza
Share on other sites

Dnia 11.05.2021 o 21:44, Forseti napisał:

 będę po prostu wgrywał Program_1 lub Program_2 w zależności od potrzeby zabawy robotem z dzieciakiem.

Nie bedziesz wgrywam ani programu1 ani programu2...wgrasz i bedziesz sobie guziczkiem zmienial..😅 Ja Ci cos tam pomoge tylko teraz mam urwanie lba...i naprawde ciezko mi znalezdz takie "luzne" pol godziny...mysle ze kolo soboty/niedzieli do Ciebie zapukam to cos ruszymy..😉

  • Lubię! 1
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

Oj nie pękaj 😉 daj sobie trochę czasu na zrozumienie, nikt z nas nie urodził się z całą wiedzą ino zdobywał powoli i sukcesywnie. Próbuj na prostszych programach i je rozwijaj, no i moim zdaniem warto wydać trochę grosza na dobrą książkę. Pamiętaj też że w każdej dziedzinie nauki występuje coś takiego jak krzywa uczenia no i niestety C C++ jest dosyć płaska i długa na początku. Jak to mówi młodzież: Pozdro i z fartem!

  • Lubię! 1
Link do komentarza
Share on other sites

(edytowany)

@_LM_ @farmaceuta

Nie poddałem się jeszcze ale zmiana podejścia była słuszna. Popracowałem nad kodem dla jednego Programu i w trakcie uzupełniłem go o maszynę stanów. Bazując na zdobytej "wiedzy" oraz gotowych rozwiązań w necie zmęczyłem kod dla robota - Program_1 polegający na omijaniu przeszkód w oparciu o czujnik odległości - działa fajnie i potrafię go modyfikować do swoich potrzeb. Uzyskałem z grubsza zakładaną funkcjonalność przełączania trybów - Program_1 - Program_2 - OFF ale mam problem (wg. mojej oceny) z delayami- przycisk nie reaguje tak jak powinien - posiada opóźnienia gdy program wykonuje odczyt i zwrot (blokują delaye). Czasami przycisk "nie łapie".

Poniżej kod:

/*Robot omijający przszkody - oparty o L293D Motor Driver Shield
   oraz czujnik odległości HC-SR04
   Napęd - 2x silniki DC 3-6V
   Serwomechanizm SG-90
   Włączany microswitchem
   dioda LED sygnalizacyjna stan pracy - Wolne miganie Program_1, Szybkie Program_2, Ciągłe STOP
   Opcja rozbudowy o sterowanie BT i modułem HC-05
*/

//Biblioteki
#include <AFMotor.h> //Wczytanie Biblioteki AFMotor dla Arduino Shield
#include <NewPing.h> //Wczytanie Biblioteki NewPing dla HC-SR04
#include <Servo.h>  //Wczytanie Biblioteki Servo dla SG-90


//Definicje
#define LED_1 A4 //LED 1 
#define Przycisk_1 A5 //Microswitch_1

//Sensor HC-SR04
#define TRIG_PIN A0 //Wejście wyzwalające Trigger - pomiar odległości
#define ECHO_PIN A1 //Wyjście ECHO - odczyt odległości
#define MAX_DISTANCE 200 //Maksymalny dystans dla czujnika HC-SR04 - od tej wartości reaguje robot

//Silniki DC
#define MAX_SPEED 190 //Ustawienie prędkości silników DC - 0-255
#define MAX_SPEED_OFFSET 20 //?? Interwał przyśpieszania ??

//Ustawienia i przypisania

NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE); //Przypisanie zmiennych z define

//Wyjście silników DC oraz serwomechanizm
AF_DCMotor motor1(3); //Przypisanie silnika 1 na wyjście M3 Shielda
AF_DCMotor motor2(4); //Przypisanie silnika 2 na wyjście M4 Shielda

Servo moje_servo;   //Obiekt dający możliwość odwołania się do serwa o nazwie moje_servo

boolean jazdaPRZOD = false; /*Przypisanie zmiennej jedź naprzód
  - zmienna do przechowania wartości prawda/fałsz
  Można użyć zamiennie bool. W oryginale boolean goesForward=false;*/
int dystans = 100; //Ustawienie dystansu dla moje_servo - int distance = 100;
int Ustaw_predkosc = 0; //Ustawienie prędkości startowej silnika - int speedSet = 0;
int stanLED1 = LOW; //Przypisanie stanu LED 1 do zmiennej int - Zapamiętanie stanu LED

uint8_t tryb_program = 0; //Zmienna trybu programu


//Ustawienia do millis()
unsigned long aktualnyCzas = 0 ; //Czas, który jest zliczany - ciągle rośnie - Wartość od której odejmujemy zapamiętany czas!!!
unsigned long zapamietanyCzas_LED1 = 0; //Czas, który był zapamiętany w ostatnim obiegu pętli dla LED1
unsigned long zapamietanyCzas_DC1 = 0; //Czas, który był zapamiętany w ostatnim obiegu pętli dla silnika DC1


unsigned long miganie_LED1 = 500; //Interwał migania LED1 - Program_1
unsigned long miganie_LED2 = 100; //Interwał migania LED1 - Program_2

//▼▼▼▼▼▼▼▼▼▼▼▼▼PRZYPISANIE RUCHÓW ROOTA▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼

int patrz_PRAWO() //Ustawienie serwa - do patrzenia w prawo - int lookRight()
{
  moje_servo.write(50); //Ustawienie kąt 50°
  delay(500);
  int dystans = readPing();
  delay(100);
  moje_servo.write(115); //Ustawienie kąt 115°
  return dystans;
}

int patrz_LEWO() //Ustawienie serwa - do patrzenia w lewo - org int lookLeft()
{
  moje_servo.write(170); //Ustawienie kąt 170°
  delay(500);
  int dystans = readPing();
  delay(100);
  moje_servo.write(115); //Ustawienie kąt 115°
  return dystans;
  delay(100);
}

int readPing() //Ustawienie odległości w cm dla czujnika HC-SR04
{
  delay(70);
  int cm = sonar.ping_cm(); //zwraca dystans w cm
  if (cm == 0)
  {
    cm = 250; //do sprawdzenia odległość- czemu aż 250cm ?
  }
  return cm;
}

void DC_STOP() //Zatrzymanie silników!
{
  motor1.run(RELEASE);
  motor2.run(RELEASE);
}

void DC_PRZOD() //Silnik jazda przód - ten element przerobić na millis()
{
  if (!jazdaPRZOD)
  {
    jazdaPRZOD = true;
    motor1.run(FORWARD);
    motor2.run(FORWARD);

    for (Ustaw_predkosc = 0; Ustaw_predkosc < MAX_SPEED; Ustaw_predkosc += 2) //Przyśpiesz do max speed w przód.
    {
      motor1.setSpeed(Ustaw_predkosc);
      motor2.setSpeed(Ustaw_predkosc);

      delay(5);
    }
  }
}

void DC_TYL() //Silnik jazda tył - ten element przerobić na millis()
{
  jazdaPRZOD = false;
  motor1.run(BACKWARD);
  motor2.run(BACKWARD);

  for (Ustaw_predkosc = 0; Ustaw_predkosc < MAX_SPEED; Ustaw_predkosc += 2) //Przyśpiesz do max speed w tył.
  {
    motor1.setSpeed(Ustaw_predkosc);
    motor2.setSpeed(Ustaw_predkosc);

    delay(5);
  }
}

void DC_PRAWO() //Silniki skręc w prawo
{
  motor1.run(FORWARD);
  motor2.run(BACKWARD);
  delay(500);
  motor1.run(FORWARD);
  motor2.run(FORWARD);
}

void DC_LEWO() //Silniki skręc w lewo
{
  motor1.run(BACKWARD);
  motor2.run(FORWARD);
  delay(500);
  motor1.run(FORWARD);
  motor2.run(FORWARD);
}

//▼▼▼▼▼▼▼▼▼▼▼▼▼POCZĄTEK PROGRAMU▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼

void setup() {

  Serial.begin(9600);

  //Startowe ustawienie serva

  moje_servo.attach(10); /*Serwomechanizm podłączony do pinu 10 - obsługiwane PINY 9 i 10.
  Dla Sheilda L293D kolejno 9=Servo_1, 10=Servo 2 -sygnały sterujące*/
  moje_servo.write(115); //Ustawienie pozycji serva na 115°

  delay(2000);
  dystans = readPing();
  delay(100);
  dystans = readPing();
  delay(100);
  dystans = readPing();
  delay(100);
  dystans = readPing();
  delay(100);

  //Wyprowadzenie przycisków
  pinMode(Przycisk_1, INPUT_PULLUP); //Wejście przycisku 1 w trybie PULLUP


  //Wyprowadzenia LED
  pinMode(LED_1, OUTPUT);


  //Ustawienie wyjściowego stanu LED
  digitalWrite(LED_1, LOW); //Stan diody LED1 - wyłączona

  //Ustawienie początkowej wartości silnika DC oraz STOP
  motor1.setSpeed(0);
  motor1.run(RELEASE);

  //Ustawienie wyjściowe dla millis() - zerowanie liczenia
  aktualnyCzas = millis();

}

void loop() {

  int dystansR = 0; //Ustawienie dystansu w Prawo
  int dystansL =  0; //Ustawienie dystansu w Lewo
  delay(40);
  aktualnyCzas = millis(); //Ustawienie wyjściowe dla millis()

  if (!digitalRead(Przycisk_1)) tryb_program++; //Jeśli ostatni stan był wyłączony i aktualny stan jest włączony to:

  switch (tryb_program % 4)  {

    //Modulo - reszta z dzielenia przez 4 - bo 3 programy

    case 1:

      //Miganie LED_1 Wolne miganie Program_1
      if (aktualnyCzas - zapamietanyCzas_LED1 >= miganie_LED1) {
        stanLED1 = !stanLED1;
        digitalWrite(LED_1, stanLED1);
        zapamietanyCzas_LED1 = aktualnyCzas;  //zapamiętaj aktualny czas
      }
      if (dystans <= 15) //jeśli dystans mniejszy od 15cm STOP->TYŁ->Patrz_Prawo->Patrz_lewo
      {
        DC_STOP();
        delay(100);
        DC_TYL();
        delay(300);
        DC_STOP();
        delay(200);
        dystansR = patrz_PRAWO();
        delay(200);
        dystansL = patrz_LEWO();
        delay(200);

        /*Wersja 2 powyższego kodu bez delay
              if ((aktualnyCzas - zapamietanyCzas_DC1 >= 100) && (dystans <= 15)) {
             DC_STOP();

             } if ((aktualnyCzas - zapamietanyCzas_DC1 >= 300) && (dystans <= 15)) {
             DC_TYL();

             } if ((aktualnyCzas - zapamietanyCzas_DC1 >= 200) && (dystans <= 15)) {
             DC_STOP();

             } if ((aktualnyCzas - zapamietanyCzas_DC1 >= 200) && (dystans <= 15)) {
             dystansR = patrz_PRAWO();

             } if ((aktualnyCzas - zapamietanyCzas_DC1 >= 200) && (dystans <= 15)) {
             dystansR = patrz_LEWO();
             zapamietanyCzas_DC1 = aktualnyCzas;
        */
        if (dystansR >= dystansL)
        {
          DC_PRAWO();
          DC_STOP();
        } else
        {
          DC_LEWO();
          DC_STOP();
        }
      } else
      {
        DC_PRZOD();
      }
      dystans = readPing();

      break;
    case 2:

      //Miganie LED_1 Szybkie miganie Program_2
      if (aktualnyCzas - zapamietanyCzas_LED1 >= miganie_LED2) {
        stanLED1 = !stanLED1;
        digitalWrite(LED_1, stanLED1);
        zapamietanyCzas_LED1 = aktualnyCzas;  //zapamiętaj aktualny czas
      }

      break;
    case 3:

      //Tutaj będzie stan OFF

      break;
  }

  while (!digitalRead(Przycisk_1));
  delay(25);
}

Wydaje mi się, że winowajcą jest to gdzie mam 1,2 sekundy opóźnienia :

int patrz_PRAWO() //Ustawienie serwa - do patrzenia w prawo - int lookRight()
{
  moje_servo.write(50); //Ustawienie kąt 50°
  delay(500);
  int dystans = readPing();
  delay(100);
  moje_servo.write(115); //Ustawienie kąt 115°
  return dystans;
}

int patrz_LEWO() //Ustawienie serwa - do patrzenia w lewo - org int lookLeft()
{
  moje_servo.write(170); //Ustawienie kąt 170°
  delay(500);
  int dystans = readPing();
  delay(100);
  moje_servo.write(115); //Ustawienie kąt 115°
  return dystans;
  delay(100);
}

oraz obrót robota gdzie tracę 1 sekundę:

void DC_PRAWO() //Silniki skręc w prawo
{
  motor1.run(FORWARD);
  motor2.run(BACKWARD);
  delay(500);
  motor1.run(FORWARD);
  motor2.run(FORWARD);
}

void DC_LEWO() //Silniki skręc w lewo
{
  motor1.run(BACKWARD);
  motor2.run(FORWARD);
  delay(500);
  motor1.run(FORWARD);
  motor2.run(FORWARD);
}

 

W sumie jak będę chciał zmienić tryb i tak muszę złapać robota więc nie będzie on wykonywał w tym czasie poleceń z delayem (LEWO,PRAWO,OBRÓT) zatem z grubsza jest ok. Zasadniczo program działa i da się przełączać tryby microswitchem - pytanie jak to poprawić aby działało hmmm... bardziej gładko? Da się to zrobić bez zagłębiania się w arkana wiedzy tajemnej o Arduino ? 🙂 🙂  Dzięki za sugestie. 

Edytowano przez Forseti
Link do komentarza
Share on other sites

Myślę że powinieneś jak najszybciej pozbyć się tych delay, wyrabiasz u siebie złe nawyki. Skoro kod działa (jako tako 😉 ) to najwyższa pora abyś zaczął używać funkcji millis(). Bez tego postępu raczej nie będzie. Tyle że ten twój kod będzie wymagał gruntownych zmian, żeby nie powiedzieć - napisania od początku. 

Link do komentarza
Share on other sites

(edytowany)

@_LM_ Właśnie walczę z wywaleniem tych delay(). Chociaż nie jest ich dużo to jednak wg. mnie one mi przywieszają przełączanie pomiędzy Programami. "Jako tako" miałem na myśli, że moment przywieszenia (Sensor i obrót robota) nie jest upierdliwy w przełączaniu bo działa tylko gdy jest blisko przeszkody. W innym przypadku ładnie się zmienia case. 

9 minut temu, _LM_ napisał:

Tyle że ten twój kod będzie wymagał gruntownych zmian, żeby nie powiedzieć - napisania od początku. 

I tego się właśnie obawiałem najbardziej ponieważ nad tym kodem siedziałem sporo czasu .....😞

Edytowano przez Forseti
Link do komentarza
Share on other sites

Patrz w jaki sposób można podzielić zadania dzięki operatorowi modulo. 

If(0== millis() %100){}// zadanie co 100ms
If(0 == millis() % 500){}// zadanie co 500ms
....

Fakt wymaga to nieco innego podejścia do pisania programów ale ta "inwestycja" szybko się zwróci. Warto próbować bo programiwaniem liniowym zbyt wiele nie osiągniesz. O czym z resztą już się przekonałeś 

Edytowano przez _LM_
Link do komentarza
Share on other sites

No i jest duża szansa że drugie zadanie się nie wykona.

Fakt wymaga to nieco innego podejścia do pisania programów ale ta "inwestycja" szybko się zwróci. Warto próbować, bo zaufaniem do tego że wstrzelisz się akurat dokładnie w tą milisekundę wiele nie osiągniesz...

 

Link do komentarza
Share on other sites

@ethanak żebyś się nie gorączkował 😉

If (licznik == 0){
  licznik = 10 
    Zadanie 1
  }

If (licznik2 == 0){
  Licznik2 = 50
  Zadanie 2
  }

Tyle że liczniki musiałyby być dekrementowane w przerwaniu. 

Link do komentarza
Share on other sites

59 minut temu, ethanak napisał:

Nie, nie zadziała.

Zadziała przy odpowiednio napisanym warunku. Tu oczywiście muszę przyznać rację ze pierwsza wersja nie była poprawna 

//z millis
if(millis() >= licznik){
funkcja();
licznik = millis() + 100; // 100mS
}

// Zaś gdy używasz przerwań w sposób tradycyjny


volatile uint8_t x;

ISR (TIMER2_OVF_vect) {
	if(x)x--;
}

void loop(){
 if(x == 0){
   funkcja();
  x = 100;
  }
}  

Tak patrzę teraz na typ który zwraca millis() i jest to ogromne marnowanie pamięci zwłaszcza w małych mikrokontrolerach. Jak dla mnie kolejny argument przeciw arduino.

 

Edytowano przez _LM_
literówka
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.