Skocz do zawartości

Jak uzyskać powolną pracę serva bez delay.


OLi_m

Pomocna odpowiedź

Kombinuję jak koń pod górkę z napisaniem sterowania do serva. Będzie b. mocne (80kg/cm) i wykonujące część ruchu po maleńku - w przybliżeniu  20st. w ciągu 1-2 minut, resztę szybciej, ale i tak ze 3 razy wolniej od oryginalnej prędkości.

W związku z tym zastosowanie delay między krokami odpada, bo niczego innego w tym czasie procesor nie jest mi w stanie obsłużyć.

Przeszukując internet i literaturę natknąłem się na opracowanie:

https://botland.com.pl/content/226-wielozadaniowosc-arduino-czesc-druga

jest tam przykład sterowania servo w mikrokrokach właśnie bez delay:

#include <Servo.h>
class Flasher {
  // Zmienne składowe klasy
  // Są inicjowane podczas uruchamiania:
  int ledPin;                    // the number of the LED pin
  long OnTime;                   // milliseconds of on-time
  long OffTime;                  // milliseconds of off-time
                                 // Utrzymują one obecny stan
  int ledState;                  // ledState używany do ustawiania diody LED
  unsigned long previousMillis;  // zapisze ostatnią aktualizację diody LED
                                 // Konstruktor - tworzy Flashera
  // i inicjuje zmienne składowe i stan
public:
  Flasher(int pin, long on, long off) {
    ledPin = pin;
    pinMode(ledPin, OUTPUT);
    OnTime = on;
    OffTime = off;
    ledState = LOW;
    previousMillis = 0;
  }
  void Update() {
    // sprawdź, czy nadszedł czas, aby zmienić stan diody LED
    unsigned long currentMillis = millis();

    if ((ledState == HIGH) && (currentMillis - previousMillis >= OnTime)) {
      ledState = LOW;                  // Turn it off
      previousMillis = currentMillis;  // zapamiętuje czas
      digitalWrite(ledPin, ledState);  // Update the actual LED
    } else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime)) {
      ledState = HIGH;                 // turn it on
      previousMillis = currentMillis;  // zapamiętuje czas
      digitalWrite(ledPin, ledState);  // aktualizuje aktualną diodę LED
    }
  }
};
class Sweeper {   //    do serva
  Servo servo;               // the servo
  int pos;                   // aktualna pozycja serwa
  int increment;             // krok do przesunięcia dla każdego interwału
  int updateInterval;        // odstęp między aktualizacjami
  unsigned long lastUpdate;  // ostatnia aktualizacja pozycji
public:
  Sweeper(int interval) {
    updateInterval = interval;
    increment = 1;
  }
  void Attach(int pin) {
    servo.attach(pin);
  }
  void Detach() {
    servo.detach();
  }
  void Update() {
    if ((millis() - lastUpdate) > updateInterval)  // czas na aktualizację
    {
      lastUpdate = millis();
      pos += increment;
      servo.write(pos);
      Serial.println(pos);
      if ((pos >= 180) || (pos <= 0))  // koniec sweep
      {
        // odwrotny kierunek
        increment = -increment;
      }
    }
  }
};


Flasher led1(11, 123, 400);  // nr pin ,  czas on , czas off
Flasher led2(12, 350, 350);
Flasher led3(13, 50, 500);
Sweeper sweeper1(15);        //   odstęp pomiędzy kolejnymi krokami w ms, serva nr 1
Sweeper sweeper2(25);        //   odstęp pomiędzy kolejnymi krokami w ms, serva nr 2

void setup() {
  Serial.begin(9600);
  sweeper1.Attach(9);         // pin serva 1
  sweeper2.Attach(10);
}

void loop() {
  sweeper2.Update();

  if (digitalRead(2) == HIGH) {
    sweeper1.Update();
    led1.Update();
  }
  led2.Update();
  led3.Update();
}

wszystko ładnie mruga, chodzi w tę i z powrotem, problem jest jak chcę to przerobić tak,

żeby mieć pojedyncze ruchy serva w takich mikrokrokach, np. jak na przykładzie poniżej -  0st. i 170st. Obraca się, ale na pełnej prędkości, nie w krokach.

#include <Servo.h>
int buttonPin = 2;    //  pin, na którym znajduje się przycisk
volatile int pos;

class Sweeper {              
  Servo servo;               
//  int pos;                   
  int increment;             
  int updateInterval;        
  unsigned long lastUpdate;  
public:
  Sweeper(int interval, int inc) {
    updateInterval = interval;
    increment = inc;  
  }
  void Attach(int pin) {
    servo.attach(pin);
  }
  void Detach() {
    servo.detach();
  }
  void Update() {

    if ((millis() - lastUpdate) > updateInterval) {
      lastUpdate = millis();
      pos += increment;  //  dodanie increment do aktualnej pozycji
      servo.write(pos);
    }
  }
};  

Sweeper sweeper1(15,1);  //   odstęp pomiędzy kolejnymi krokami w ms,  krok przesunięcia serwa



void setup() {
    pinMode(buttonPin, INPUT);   //  ustawienie pinu jako wejście
  sweeper1.Attach(9);
 
} 

void loop() {

  int buttonState = digitalRead(buttonPin);   //  odczyt stanu przycisku

  if (buttonState == LOW) {   //  jeśli przycisk jest wciśnięty
    pos = 0;                        //  wpisanie jaka ma być docelowa pozycja 
    sweeper1.Update();
  }
  else if (buttonState == HIGH) {   //  jeśli przycisk jest nie wciśnięty
    pos = 170;                      //  wpisanie jaka ma być docelowa pozycja 
    sweeper1.Update();
  }
}

drążąc dalej otrzymałem taką wskazówkę:

Cytat

Wygląda na to, że problem polega na tym, że zmienna 'pos' jest zmienną globalną, a nie zmienną klasy, więc zmienna ta jest modyfikowana przez 'loop' bezpośrednio, a nie przez 'Update' klasy 'Sweeper'. Aby rozwiązać ten problem, należy przekształcić 'pos' w zmienną klasową i dostęp do niej uzyskać przez metodę klasy, np. 'setPos()', a następnie w miejscu gdzie jest ustawiana pozycja serwa należy użyć metody setPos.

Co dało poniższy szkic ( sam bym go nie stworzył , tyle że kapuję o co w nim biega )  i niestety żadnej poprawy w działaniu:

#include <Servo.h>
int buttonPin = 2;    //  pin, na którym znajduje się przycisk

class Sweeper {              
  Servo servo;               
  int pos;                   
  int increment;             
  int updateInterval;        
  unsigned long lastUpdate;  
public:
  Sweeper(int interval, int inc) {
    updateInterval = interval;
    increment = inc;  
  }
  void Attach(int pin) {
    servo.attach(pin);
  }
  void Detach() {
    servo.detach();
  }
  void setPos(int newPos){
    pos = newPos;
  }
  void Update() {

    if ((millis() - lastUpdate) > updateInterval) {
      lastUpdate = millis();
      pos += increment;  
      servo.write(pos);
    }
  }
};  

Sweeper sweeper1(15,1);  //   odstęp pomiędzy kolejnymi krokami w ms, krok przesunięcia serwa


void setup() {
    pinMode(buttonPin, INPUT);   //  ustawienie pinu jako wejście
  sweeper1.Attach(9);
 
} 

void loop() {
  int buttonState = digitalRead(buttonPin);   //  odczyt stanu przycisku

if (buttonState == LOW) {   //  jeśli przycisk jest wciśnięty
    sweeper1.setPos(0);    
    sweeper1.Update();
  }
  else if (buttonState == HIGH) {   //  jeśli przycisk jest nie wciśnięty
    sweeper1.setPos(170);
    sweeper1.Update();
  }

}

Jak to zrobić, żeby działało prawidłowo ( na razie bez zmiennej prędkości - tylko jednostajnie , ale powoli )  ?

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

Zasilanie oddzielnie , dziwna praca - tak jakby nie kasował całości poprzedniego kodu , mrugał na pół gwizdka w rytm poprzedniego szkicu , ustawiał servo na pozycji z poprzedniego szkicu , ale mega wolno. Nie przejmuję się, w końcu nauka coś tam kosztuje oprócz czasu 🙂

 

Link do komentarza
Share on other sites

(edytowany)

Może przyda się komuś do zabawy:

/*
---------------------------------  LCD Keypad Shield ----------------------------------
*/
#include <Servo.h>
Servo myservo;

#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);;

int keypad_pin = A0;
int keypad_value = 0;
int keypad_value_old = 0;

char btn_push;

byte mainMenuPage = 1;
byte mainMenuPageOld = 1;
byte mainMenuTotal = 6;

int a = 90;                      // ustawienie zmiennej do menu E i F


unsigned long previousMillis = 0;
const long interval = 200;          // czas kroku w ms


void setup()
{
  lcd.begin(16,2);  //Initialize a 2x16 type LCD
  lcd.setCursor(0,0); // Ustawienie kursora w pozycji 0,0 

    Serial.begin(9600);
    MainMenuDisplay();


    myservo.attach(3);
    myservo.write(0);              //   początkowe ustawienie na 0 st.



}
void loop()
{
    btn_push = ReadKeypad();

    MainMenuBtn();

    if(btn_push == 'S')//enter selected menu
    {
        WaitBtnRelease();
        switch (mainMenuPage)
        {
            case 1:
              MenuA();
              break;
            case 2:
              MenuB();
              break;
            case 3:
              MenuC();
              break;
            case 4:
              MenuD();
              break;
            case 5:
              MenuE();
              break;
            case 6:
              MenuF();
              break;                            
        }

          MainMenuDisplay();
          WaitBtnRelease();
    }



    delay(10);

}//--------------- End of loop() loop ---------------------

void MainMenuDisplay()
{
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Wybor menu:");
    lcd.setCursor(1,1);
    switch (mainMenuPage)
    {
        case 1:
          lcd.print("1. Menu A");
          break;
        case 2:
          lcd.print("2. Menu B");
          break;
        case 3:
          lcd.print("3. Menu C");
          break;
        case 4:
          lcd.print("4. Menu D");
          break;
        case 5:
          lcd.print("5. Menu E");
          break;
        case 6:
          lcd.print("6. Menu F");
          break;                    
    }
}

void MenuA()   
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Menu A");
    while(ReadKeypad()!= 'L')  {
       btn_push = ReadKeypad(); 

    }    
}

void MenuB() 
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Menu B");
    while(ReadKeypad()!= 'L')  {
       btn_push = ReadKeypad(); 

    }    
}

void MenuC()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Menu C");
    while(ReadKeypad()!= 'L')  {
       btn_push = ReadKeypad(); 

    }    
}

void MenuD()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Menu D");
    while(ReadKeypad()!= 'L')  {
       btn_push = ReadKeypad(); 
       
    }    
}

void MenuE()   // poruszanie servem góra-dół w mikrokrokach z( interval = 200;) + licznik na lcd
{
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Menu E");
      lcd.setCursor(9,1);
      lcd.print("a = ");
      lcd.setCursor(13,1);
      lcd.println(a); 

    while(ReadKeypad()!= 'L')
    {
      btn_push = ReadKeypad();
      myservo.attach(3);
      myservo.write(a);  // servo ustawia się na wartość zmiennej a 
            lcd.setCursor(13,1);
      lcd.println(a); 

if ( btn_push == 'U') {
    digitalWrite(12, HIGH);  
    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis >= interval) {
        a = a+1;
        previousMillis = currentMillis;
    }
  }
      
if ( btn_push == 'D') {
    digitalWrite(12, LOW);  
    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis >= interval) {
        a = a-1;
        previousMillis = currentMillis;
    }
  }
 }
}

void MenuF()        // poruszanie servem góra-dół w mikrokrokach z( interval = 200;) + licznik na lcd
                    // w granicach / do pozycji wyznaczonych w if-ach
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Menu F");
      lcd.setCursor(9,1);
      lcd.print("a = ");


    while(ReadKeypad()!= 'L')
    {
      btn_push = ReadKeypad();

      myservo.attach(3);

      myservo.write(a);  // servo ustawia się na wartoś zmiennej a 
      
      lcd.setCursor(13,1);
      lcd.println(a); 

if ( btn_push == 'U') {
    digitalWrite(12, HIGH);  
    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis >= interval) {
        
        if (a < 110) {                                      // kąt zatrzymania w st.
          a = a+1;
        }
        previousMillis = currentMillis;
    }
  }
      
if ( btn_push == 'D') {
    digitalWrite(12, LOW);  
    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis >= interval) {
        if (a > 70) {                                     // kąt zatrzymania w st.
          a = a-1;
        }
        previousMillis = currentMillis;
    }
  }
 }
}

//------------------------------------koniec------------------------------------------
void MainMenuBtn()
{
    WaitBtnRelease();
    if(btn_push == 'U')
    {
        mainMenuPage++;
        if(mainMenuPage > mainMenuTotal)
          mainMenuPage = 1;
    }
    else if(btn_push == 'D')
    {
        mainMenuPage--;
        if(mainMenuPage == 0)
          mainMenuPage = mainMenuTotal;    
    }

    if(mainMenuPage != mainMenuPageOld) //aktualizuje wyświetlanie tylko po zmianie strony
    {
        MainMenuDisplay();
        mainMenuPageOld = mainMenuPage;
    }
}

char ReadKeypad()
{
  /* Nic nie wciśnięte - 1023
  select  741
  left    503
  up      326
  down    142
  right   0
  */
  keypad_value = analogRead(keypad_pin);

  if(keypad_value < 100)
    return 'R';
  else if(keypad_value < 200)
    return 'D';
  else if(keypad_value < 400)
    return 'U';
  else if(keypad_value < 600)
    return 'L';
  else if(keypad_value < 800)
    return 'S';
  else
    return 'N';

}

void WaitBtnRelease()
{
    while( analogRead(keypad_pin) < 800){}
}

i  jeszcze coś ze zmiennymi prędkościami:  🙂

if ( btn_push == 'U') {
    digitalWrite(12, HIGH);  
    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis >= interval) {

      if (a >= 0 && a <= 120) {
      interval = 20;
        }
      if (a >= 121 && a <= 150) {
      interval = 200;
        }

      if (a >= 0 && a <= 120) {
      a = a + 1;
        }
      if (a >= 121 && a <= 150) {
      a = a + 1;
        }
      if (a < 150) {                                      // kąt zatrzymania w st.
          a = a;
        }
        previousMillis = currentMillis;
    }
  }
      
if ( btn_push == 'D') {
    digitalWrite(12, LOW);  
    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis >= interval) {
        if (a > 0) {                                     // kąt zatrzymania w st.
          a = a-1;
        }
    if (a >= 100 && a <= 150) {
      interval = 100; 
      }
    if (a >= 0 && a <= 100) {
      interval = 15; 
      }
          previousMillis = currentMillis;
    }
   }
  }

 

Edytowano przez OLi_m
uzupełnienie
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.