Skocz do zawartości

Forbotowy robot - zmiana delay() na millis()


Danyeru

Pomocna odpowiedź

Hej! 
Jeszcze jestem zielony, ale walczę. 😉
Moja wiedza to na razie kurs Arduino I i coś tam echem o millis.

Jestem w trakcie modyfikacji robota z forbotowego kursu, oprócz zmian w budowie, wprowadzam też zmiany w programie.

Jednym z założeń jest eliminacja wstrzymujących program delayów.

 

Aktualnie pracuję nad sekcją jazdy autonomicznej. Popełniłem działający program z millisami, ale kolega @ethanak wskazał mi, że no nie do końca. 😉

Zatem, na początku kody z kursu (żeby w jednym miejscu było):
Kontaktowe wykrywania/omijanie przeszkód:

if (digitalRead(L_SIDE_SENSOR) == LOW) {
    //Jesli przeszkoda po lewej stronie
    //Jedz wstecz i wydawaj dzwiek
    leftMotor(-40);
    rightMotor(-40);
    digitalWrite(BUZZER, 1);
    delay(800);
    //Obrot w miejscu w prawo
    leftMotor(40);
    rightMotor(-40);
    digitalWrite(BUZZER, 0);
    delay(140);
    //Koniec warunku wracamy do jazdy prosto
  }
 
  
  if (digitalRead(R_SIDE_SENSOR) == LOW) {
    //Jesli przeszkoda po prawej stronie
    //Jedz wstecz i wydawaj dzwiek
    leftMotor(-40);
    rightMotor(-40);
    digitalWrite(BUZZER, 1);
    delay(800);
    //Obrot w miejscu w lewo
    leftMotor(-40);
    rightMotor(40);
    digitalWrite(BUZZER, 0);
    delay(140);
    //Koniec warunku wracamy do jazdy prosto
  }

 

Wykrywanie przeszkód czujnikiem ultradźwiękowym:

void loop() {
  //Czy wykryto przeszkode w zakresie 0-40 cm
  if (zmierzOdleglosc() > 40) {
    leftMotor(40); //Jesli nie, to jedz prosto
    rightMotor(40);
  } else {
     //Jesli przeszkoda
     stopMotors(); //Zatrzymaj robota
     serwo.write(20); //Skrec czujnikiem w prawo
     delay(800); //Poczekaj 800ms dla ustabilizowania konstrukcji
 
    //Sprawdz, czy po prawej stronie jest przeszkoda
    if (zmierzOdleglosc() > 40) {
       //Jesli jest pusto
      leftMotor(40);
      rightMotor(-40);
      delay(400); //Obracaj w prawo przez 400 ms
    } else {
      //Jeśli po prawej jest przeszkoda
      serwo.write(160); //Obroc czujnik w lewo
      delay(800); //Poczekaj 800ms dla ustabilizowania konstrukcji
 
      //Sprawdz, czy po lowej stronie jest przeszkoda
      if (zmierzOdleglosc() > 40) {
         //Jesli jest pusto
        leftMotor(-40);
        rightMotor(40);
        delay(400); //Obracaj w lewo przez 400 ms
      } else {
        //Jesli z przodu, z lewej i prawej jest przeszkoda
        digitalWrite(BUZZER, 1);
        delay(500);
        digitalWrite(BUZZER, 0);
        //Daj sygnal buzzerem
      }
    }
    //Po sprawdzeniu przeszkod po bokach
    //Ustaw czujnik prosto    
    serwo.write(90);
  }
 
  //Opoznienie 100ms, ponieważ nie ma potrzeby sprawdzać przeszkod czesciej
  delay(100); 
}

 

 

 

Aktualnie popełniłem taką funkcję, łączącą wykrywanie przeszkód przez krańcówki i sonar:

void jazdaAuto() { //Funkcja jazdy autonomicznej
  ekspander.digitalWrite(LEDjazda, 1);
  unsigned long kontakt = 0; //Zmienne przechowująca czas startu
  unsigned long przeszkoda = 0;

  if (digitalRead(lewySensor) == LOW) { //Jeżeli przeszkoda po lewej
    zatrzymajSilniki();
    kontakt = millis(); //Aktualizuj czas startu
    while (millis() - kontakt < 100) {} //Poczekaj 100 ms
    lewySilnik(-80); //Cofnij kawałek
    prawySilnik(-80);
    kontakt = millis(); //Aktualizuj czas startu
    while (millis() - kontakt < 800) {} //Cofaj przez 800 ms
    lewySilnik(80); //Skręć w prawo
    prawySilnik(-80);
    kontakt = millis(); //Aktualizuj czas startu
    while (millis() - kontakt < 800) {} //Skręcaj przez 800 ms
    zatrzymajSilniki();
    kontakt = millis(); //Aktualizuj czas startu
    while (millis() - kontakt < 100) {} //Poczekaj 100 ms
  }
  
  if (digitalRead(prawySensor) == LOW) { //Jeżeli przeszkoda po prawej
    zatrzymajSilniki();
    kontakt = millis(); //Aktualizuj czas startu
    while (millis() - kontakt < 100) {} //Poczekaj 100 ms
    lewySilnik(-80); //Cofnij kawałek
    prawySilnik(-80);
    kontakt = millis(); //Aktualizuj czas startu
    while (millis() - kontakt < 800) {} //Cofaj przez 800 ms
    lewySilnik(-80); //Skręć w lewo
    prawySilnik(80);
    kontakt = millis(); //Aktualizuj czas startu
    while (millis() - kontakt < 800) {} //Skręć w lewo przez 800 ms
    zatrzymajSilniki();
    kontakt = millis(); //Aktualizuj czas startu
    while (millis() - kontakt < 100) {} //Poczekaj 100 milisekund
  }
  
  if (sonarDystans() > 30) { // Jeżeli w odległości 30 cm nie ma przeszkody
    lewySilnik(90); // To jedź prosto
    prawySilnik(90);
  } else { // Jeżeli wykryłeś przeszkodę
    serwo.attach(SERWO_PIN); // Podłącz serwo
    zatrzymajSilniki();    
    serwo.write(700); // Skręć czujnikiem w prawo
    przeszkoda = millis();
    while(millis() - przeszkoda < 800) {}; //Poczekaj 800 ms
    if (sonarDystans() > 30) { // Jeżeli w odległości 30 cm nie ma przeszkody
      lewySilnik(70); // Skręcaj w prawo
      prawySilnik(-70);
      serwo.write(1400); // Ustaw serwo do pozycji domyślnej
      przeszkoda = millis();
      while(millis() - przeszkoda < 800) {}; // Skręcaj przez 800 ms
      serwo.detach(); // Odłącz serwo
    } else { // Jeżeli wykryłeś przeszkodę
      serwo.write(2100); // Skręć czujnikiem w lewo
      przeszkoda = millis();
      while(millis() - przeszkoda < 800) {}; //Poczekaj 800 ms
      if (sonarDystans() > 30) { // Jeżeli w odległości 30 cm nie ma przeszkody
        lewySilnik(-70); // Skręcaj w lewo
        prawySilnik(70);
        serwo.write(1400); // Ustaw serwo domyślnie
        przeszkoda = millis();
        while(millis() - przeszkoda < 800) {}; // Skręcaj przez 800 ms
        serwo.detach(); // Odłącz serwo            
      } else { // Jeżeli z każdej strony jest przeszkoda
        serwo.write(1400); // Ustaw serwo domyślnie
        digitalWrite(BUZZER, 1); // Daj sygnał buzzerem
        przeszkoda = millis();
        while(millis() - przeszkoda < 500) {}; //Buzzer włączony przez 500 ms
        digitalWrite(BUZZER, 0); //Wyłącz buzzer
        serwo.detach(); //Odłącz serwo           
      }
    }
  }
  przeszkoda = millis();
  while (millis()-przeszkoda < 100) {} //Opóźnienie 100 ms dla wykrywania przeszkód
}

 

Edytowano przez Danyeru
Link do komentarza
Share on other sites

W międzyczasie popełniłem takiego potworka, ale po wykryciu przeszkody z przodu, obracał czujnik od razu w lewo i jak nie wykrył przeszkody, to ruszał do przodu nie odwracając czujnika.
Wiem, że gdzieś dzwoni, ale nie do końca... 😅

void jazdaAuto(){
  ekspander.digitalWrite(LEDjazda, 1);
  czasSonar = millis();
  roznicaSonar = czasSonar - odmierzSonar;
  if (roznicaSonar >= 100UL) {
    odmierzSonar = czasSonar;
    if (sonar() > 30) { //Jeżeli w odległości 30 cm brak przeszkody
    lewySilnik(95); //Jedź prosto
    prawySilnik(95);
    }
      else {
        serwo.attach(SERWO_PIN); //Podłącz serwo
        zatrzymajSilniki();
        serwo.write(25); //Spójrz w prawo
        czasPrawo = millis();
        roznicaPrawo = czasPrawo - odmierzPrawo;
        if (roznicaPrawo >= 500UL) {
          odmierzPrawo = czasPrawo;
          if (sonar() > 30) { //Jeżeli w odległości 30 cm brak przeszkoday
          lewySilnik(70); //Skręć w prawo
          prawySilnik(-70);
          czasSkretP = millis();
          roznicaSkretP = czasSkretP - odmierzSkretP;
            if (roznicaSkretP >= 1000UL){
              odmierzSkretP = czasSkretP;
              zatrzymajSilniki();
              serwo.write(85); //Ustaw serwo na wprost
              serwo.detach(); //Odłącz serwo
            }
          }
            else {
              serwo.write(155); //Spójrz w lewo
              czasLewo = millis();
              roznicaLewo = czasLewo - odmierzLewo;
                if (roznicaLewo >= 500) {
                  odmierzLewo = czasLewo;
                  if (sonar() > 30){ //Jeżeli w odległości 30 cm brak przeszkody
                    lewySilnik(-70); //Skręć w lewo
                    prawySilnik(70);
                    czasSkretL = millis();
                    roznicaSkretL = czasSkretL - odmierzSkretL;
                      if (roznicaSkretL >= 1000UL){
                        odmierzSkretL = czasSkretL;
                        zatrzymajSilniki();
                        serwo.write(85); //Ustaw serwo na wprost
                        serwo.detach(); //Odłącz serwo
                      }
                  }
                    else {
                      serwo.write(85); //Ustaw serwo na wprost
                      serwo.detach(); //Odłącz serwo
                      digitalWrite(BUZZER,1);
                      czasBuzzer = millis();
                      roznicaBuzzer = czasBuzzer - odmierzBuzzer;
                        if (roznicaBuzzer >= 500UL){
                          odmierzBuzzer = czasBuzzer;
                          digitalWrite(BUZZER,0);
                        }                      
                    }
                }
 
            }
        }
    }
  }
 
  if (digitalRead(lewySensor) == LOW) { //Jeżeli dotkniesz przeszkody po lewej
    zatrzymajSilniki();
    czasLewyDotyk = millis();
    roznicaLewyDotyk = czasLewyDotyk - odmierzLewyDotyk;
      if (roznicaLewyDotyk >= 100UL){ //Zatrzymaj się na 100 ms
        odmierzLewyDotyk = czasLewyDotyk;
        lewySilnik(-70); //Cofnij się kawałek
        prawySilnik(-70);
        czasLeweCofanie = millis();
        roznicaLeweCofanie = czasLeweCofanie - odmierzLeweCofanie;
          if (roznicaLeweCofanie >= 500UL) { //Cofaj przez 500 ms
            odmierzLeweCofanie = czasLeweCofanie;
            lewySilnik(70); //Obracaj w prawo
            prawySilnik(-70);
            czasLewyObrot = millis();
            roznicaLewyObrot = czasLewyObrot - odmierzLewyObrot;
              if (roznicaLewyObrot >= 1000UL){ //Obracaj przez sekundę
                odmierzLewyObrot = czasLewyObrot;
                zatrzymajSilniki();
              }
          }
      }
  }
 
  if (digitalRead(prawySensor) == LOW) { //Jeżeli dotkniesz przeszkody po prawej
    zatrzymajSilniki();
    czasPrawyDotyk = millis();
    roznicaPrawyDotyk = czasPrawyDotyk - odmierzPrawyDotyk;
      if (roznicaPrawyDotyk >= 100UL){ //Zatrzymaj się na 100 ms
        odmierzPrawyDotyk = czasLewyDotyk;
        lewySilnik(-70); //Cofnij się kawałek
        prawySilnik(-70);
        czasPraweCofanie = millis();
        roznicaPraweCofanie = czasPraweCofanie - odmierzPraweCofanie;
          if (roznicaPraweCofanie >= 500UL) { //Cofaj przez 500 ms
            odmierzPraweCofanie = czasPraweCofanie;
            lewySilnik(-70); //Obracaj w lewo
            prawySilnik(70);
            czasPrawyObrot = millis();
            roznicaPrawyObrot = czasPrawyObrot - odmierzPrawyObrot;
              if (roznicaPrawyObrot >= 1000UL){ //Obracaj przez sekundę
                odmierzPrawyObrot = czasPrawyObrot;
                zatrzymajSilniki();
              }
          }
      }
  }      
}


DHT22 pracuje na millis i jest ok, ale nie potrafię się odnaleźć w nieco bardziej złożonym przypadku.

Link do komentarza
Share on other sites

Ja bym to trochę zmienił i rozpisał maszynę stanów...millis() używasz tak często że być może lepsza opcja było by użycie funkcji...

fun(last_time, 1000); 

bool fun(uint32_t & last, uint32_t interval) { 
  if(millis() - last > interwal) { 
    last = millis(); 
    return true;
   } else { 
    return false;
 }

 

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

@farmaceuta bardzo fajny przykład (tylko zmień typ drugiego argumentu na uint32_t i popraw literówkę)

@Danyeru problem w tym, że traktujesz "delay" jako jakiś strasznie fujasty wynalazek diabła i robisz własną konstrukcję... która w rzeczywistości robi dokładnie to samo tylko z mniejszą dokładnością (w folderze instalacyjnym Arduino masz gdzieś plik wiring.c i możesz zobaczyć jak działa ten "systemowy" delay. Uprzedzam: funkcja yield() nie robi nic) i jest dokładnie tak samo fujasta 🙂

W swoim kodzie masz coś takiego:

void loop() {
  zrób_coś();
  zrób_coś_jeszcze();
  poczekaj_chwilkę();
}
  

Jeśli chcesz pozbyć się czekania, to raczej powinno być coś w stylu:

void loop()
{
  if (przyszedł_czas_na_zrobienie_czegoś()) zrób_coś();
  if (przyszedł_czas_na_zrobienie_czegoś_jeszcze()) zrób_coś_jeszcze();
}
  

Jeśli teraz owe "zróbcosie" potraktujesz tak samo, okaże się że nie masz żadnych opóźnień, ale za to wielce krzaczasty gąszcz ifów... ale właśnie dlatego wymyślono coś co się nazywa "maszyna stanów" i bardzo skutecznie eliminuje owe krzaki.

Tak przy okazji: "automat skończony" i "maszyna stanów" to takie bardzo mądre nazwy, ale w rzeczywistości jest to całkiem proste i nie ma się czego obawiać.

Na tym polega owo "przestawienie myślenia" o którym pisałem na czacie.

Ale... jeśli nie skończyłeś całego kursu Arduino (a zdaje się że tylko pierwszą część) to lepiej skończ cały, bo takie przeskakiwanie do produ może być nieskuteczne.

 

  • 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

To nie jest tak, że skończyłem pierwszą część kursu i pozjadałem wszystkie rozumy, oczywiście będę kontynuował. 😉

 

Tutaj zrobiłem jeden skok do przodu przy okazji czujnika DHT.

A, że przeglądając forum pod tematami pierwszej części kursu często widziałem w postach wypowiedzi typu "lepiej użyj millis, zainteresuj się millis", no to się zainteresowałem nieco ponad kursem, może nieco na wyrost. 😅🤪

Robot oprócz jeżdżenia wysyła dane z czujnika i ma mieć konkretne sekwencje grup LED, zatem czy ten aktualny delay w manewrach nie wpłynie na te funkcje?

 

Zatem dziękuję za wskazówki, zainteresuję się tą maszyną stanów i tym rozwiązaniem od Farmaceuty. 🙃

Link do komentarza
Share on other sites

(edytowany)

Dobra, idę uczyć się dalej, a póki co, ogarnąłem w ten sposób, dla sekcji z czujnikiem odległości:

 

enum States {
  PROSTO,
  PATRZ_PRAWO,
  OBROT_PRAWO,
  PATRZ_LEWO,
  OBROT_LEWO,
  ALARM,
  ZATRZYMAJ,
};

void jazdaAuto() { //Funkcja jazdy autonomicznej
  ekspander.digitalWrite(LEDjazda, 1); //Włącz LED informujący o jeździe autonomicznej
  static States obecnyStan = PROSTO; //Domyślny stan
  static unsigned long czasStanu = 0; //Zmienna dla timera
  const int doPrzeszkody = 20; //Odległość wykrywania przeszkody w cm

  switch (obecnyStan) {
    case PROSTO:
      if (sonarDystans() <= doPrzeszkody) { //Jeżeli w zadanej odległości jest przeszkoda
        obecnyStan = ZATRZYMAJ; //Przejdź do zatrzymania
        czasStanu = millis(); //Ustaw czas dla timera
        serwo.attach(SERWO_PIN); //Podłącz serwo
      } else { //Jedź prosto
        lewySilnik(90);
        prawySilnik(90);
      }
      break;

    case ZATRZYMAJ:
      zatrzymajSilniki();
      if (millis() - czasStanu >= 500) {
        obecnyStan = PATRZ_PRAWO; //Przejdź do wykrywania przeszkody po prawej
        czasStanu = millis();
      }
      break;

    
    case PATRZ_PRAWO: //Wykrywanie przeszkód po prawej
      serwo.write(25); //Ustw serwo w prawo
      if (millis() - czasStanu >= 200) { //Patrz przez 200 ms dla ustabilizowania serwa
        if (sonarDystans() > doPrzeszkody) { //Jeżeli nie ma przeszkód bliżej niż zadana odległość
          obecnyStan = OBROT_PRAWO; //Skręć w prawo
          czasStanu = millis(); //Ustaw czas dla timera
        } else { //Jeżeli jest przeszkoda
          obecnyStan = PATRZ_LEWO; //Przejdź do wykrywania przeszkody po lewej
          czasStanu = millis(); //Ustaw czas dla timera
        }
      }
      break;
      
    case OBROT_PRAWO: //Skręt w prawo
      lewySilnik(70);
      prawySilnik(-70);
      serwo.write(85); //Ustaw serwo na wprost      
      if (millis() - czasStanu >= 800) { //Skręcaj przez 800 ms
        serwo.detach();
        obecnyStan = PROSTO; //Przejdź do jazdy na prosto
        czasStanu = millis(); //Uataw czas dla timera
      }
      break;

    case PATRZ_LEWO: //Wykrywanie przeszkód po lewej
      serwo.write(155); //Obróć serwo w lewo
      if (millis() - czasStanu >= 250) { //Patrz przez 200 ms dla ustabilizowania serwa
        if (sonarDystans() > doPrzeszkody) { //Jeżeli nie ma przeszkody w zadanej odległości
          obecnyStan = OBROT_LEWO; //Skręć w lewo
          czasStanu = millis(); //Ustaw czas dla timera
        } else { //Jeżeli jest przeszkoda
          obecnyStan = ALARM; //Włącz alarm
          czasStanu = millis(); //Ustaw czas dla timera
        }
      }
      break;

    case OBROT_LEWO: //Skręt w lewo
      lewySilnik(-70);
      prawySilnik(70);
      serwo.write(85); //Ustaw serwo na wprost
      if (millis() - czasStanu >= 800) { //Skręcaj przez 800 ms
        serwo.detach();
        obecnyStan = PROSTO; //Przejdź do jazdy prosto
        czasStanu = millis(); //Ustaw czas dla timera
      }
      break;

    case ALARM:
      serwo.write(85); //Ustaw serwo na wprost
      digitalWrite(BUZZER, 1); //Włącz buzzer
      if (millis() - czasStanu >= 500) { //Po upływie 500 ms
        digitalWrite(BUZZER, 0); //Wyłącz buzzer
        serwo.detach();
        obecnyStan = PROSTO; //Przejdź do jazdy prosto
        czasStanu = millis(); //Ustaw czas dla timera
      }
      break;
}

 

Edytowano przez Danyeru
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.