Czy istnieje sposób, aby powstrzymać serwomechanizmy przed „drżeniem”?

20

Po prostu kontroluję serwomechanizmy (9 g Micro Servos) w oparciu o niektóre dane odczytane z innych źródeł. Wszystko działa dobrze, z wyjątkiem tego, że serwa będą się ciągle „trzęsły”. Oznacza to, że wibrują do tyłu bardzo subtelnymi ruchami (z przerywanymi ruchami około 1/2 -> 1 cm lub mniej więcej).

Próbowałem rozwiązać ten problem w oprogramowaniu, wykonując coś takiego:

  do{
    delay(DTIME);
    positionServo();
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("X position: ");
    lcd.print(xRead);
    lcd.setCursor(0,1);
    lcd.print("Y position: ");
    lcd.print(yRead);
  }while( readChange() ); //while there has been change

Tam, gdzie jest to konieczne, zainicjuj zmienne przechowujące zmapowaną wartość serwomechanizmu (używając biblioteki serwomechanizmów arduino).

Funkcja readChange () jest zdefiniowana jako:

int readChange(){
  int x_Temp, y_Temp;

  x_Temp = map(analogRead(x_axisReadPin), 0, 1023, 0, 179);
  y_Temp = map(analogRead(y_axisReadPin), 0, 1023, 0, 179);

  if( abs(x_Temp - xRead) < DEG && abs(y_Temp - yRead) < DEG ) return 0; // no change 
  else return 1; //change
}

Gdzie xRead jest wartością, która została zainicjowana (pierwsze zmapowane wyjście serwa).

Chociaż to naprawdę nie jest dobre podejście. Wymaga to, aby OBA wartości nie mogły ulec zmianie o współczynnik DEG (~ 10 stopni lub ~ 0,28 V w moim przypadku). Jeśli napiszę taką funkcję, że albo OR będzie mniejsze niż DEG, to co, jeśli zmieniam tylko jedno serwo na raz? Istnieje więc delimma ..

Czy to po prostu własność serwomechanizmów (być może tanich?), Czy może istnieje obejście?


O wiele łatwiej byłoby dołączyć link do ciasta. Oto pełny kod: http://pastie.org/8191459

Dołączyłem dwa serwa wraz ze wskaźnikiem laserowym, aby umożliwić dwa stopnie swobody (X, Y). Istnieją opcje, oparte na stanie kilku przycisków, do sterowania serwomechanizmami na różne sposoby. Pierwszym z nich jest „Motion”, w którym mam dwa fotorezystory, które w zależności od ekspozycji na światło wpływają na pozycję serwomechanizmów. Nie wdrożyłem jeszcze kodu do kontrolowania serwomechanizmów przez kontroler Xbox. A trzecia opcja to po prostu ruch losowy.

wprowadź opis zdjęcia tutaj

sherrellbc
źródło
4
Najwyraźniej masz niewielką niestabilność lub hałas w kontrolerze serwo. Wchodzisz jednak w wiele szczegółów rzeczy, które wydają się nie mieć nic wspólnego z kontrolerem serwomechanizmu, poza nieudokumentowaną linią „positionServo ();”, o której możemy jedynie zgadywać, gdzie są zakopane szczegóły. Czy kontroler serwo jest zamknięty w mikro? Zamknięty na zewnątrz? Analogowy czy cyfrowy? Jeśli cyfrowy, to przy jakiej rozdzielczości jest mierzony? Pokaż schemat całego systemu.
Olin Lathrop
Ile obciążasz serwomechanizmów?
Chris Laplante
4
@OlinLathrop - (S) Używa standardowych serwomechanizmów sterowanych radiowo, które mają zapętloną całą pętlę serwomechanizmu. sherrellbc - „Serwo” to bardzo, bardzo ogólny termin. Niestety, producenci części modelu RC wybrali najmniej opisowy termin na urządzenia, które produkują. Ponieważ mamy tutaj do czynienia z najróżniejszymi rodzajami serwomechanizmów i serwosystemów, wskazanie, że twoje „serwa” są modelami serwomechanizmów sterowanymi radiowo, jest prawdopodobnie dobrym pomysłem.
Connor Wolf,
1
Twój system jest zbyt skomplikowany, abyśmy mogli go rozwiązać. Uprość to i sprawdź, czy nadal masz problem. Jeśli masz minimalny system, który odtwarza problem, a nadal nie możesz go naprawić samodzielnie, wówczas należy poprosić o pomoc.
Phil Frost
12
Uwaga ogólna dotycząca projektowania systemów kierowania laserowego: umieść lustra na serwomechanizmach, a następnie kieruj jeden na drugi. W ten sposób nie musisz montować jednego serwa na drugim, ani lasera na serwie, a następnie możesz je mocno przykręcić.
pjc50,

Odpowiedzi:

27

Podczas korzystania z biblioteki Servo w Arduino powszechnym źródłem szumów serwomechanizmu jest to, że procedury serwonapędów sterowane przerwaniami w rzeczywistości nie dają bardzo stabilnego impulsu wyjściowego. Ponieważ AVR przyjmuje przerwania do obsługi zegara millis () i innych rzeczy w środowisku uruchomieniowym Arduino, drgania w bibliotece Servo są rzędu kilku mikrosekund, co przekłada się na duży ruch w serwomechanizmie.

Rozwiązaniem tego jest napisanie własnego pulsu. Coś takiego:

cli();
long start = micros();
digitalWrite(PIN, HIGH);
while (micros() - start < duration)
  ;
digitalWrite(PIN, LOW);
sei();

Spowoduje to wyłączenie innych przerwań i wygenerowanie znacznie czystszego impulsu PWM. Spowoduje to jednak, że zegar „millis () przegapi kilka tyknięć zegara. (Funkcja„ micros () ”może być nazwana czymś innym - dokładnie zapominam.

Ogólnie rzecz biorąc, do krytycznego kodu czasowego chcesz całkowicie pozbyć się środowiska uruchomieniowego Arduino i napisać własny przy użyciu kompilatora avr-gcc i biblioteki avr-libc, która zasila środowisko Arduino. Następnie możesz ustawić licznik czasu tak, aby tykał 4 razy na mikrosekundę, a nawet 16 razy na mikrosekundę i uzyskać znacznie lepszą rozdzielczość PWM.

Inną przyczyną szumów w serwomechanizmach są tanie serwomechanizmy z tanimi czujnikami, w których czujniki są głośne lub gdy dokładna pozycja żądana za pomocą impulsu nie może być faktycznie zakodowana przez czujnik. Serwo zobaczy „przesuń się do pozycji 1822” i spróbuje to zrobić, ale skończy z odczytem czujnika 1823. Serwo powie następnie „cofnij się trochę” i skończy z odczytem czujnika 1821. Powtórz! Rozwiązaniem tego problemu jest użycie serwomechanizmów wysokiej jakości. Idealnie nie w ogóle serwo hobbystyczne, ale prawdziwe serwomechanizmy z optycznymi lub magnetycznymi enkoderami absolutnymi.

Wreszcie, jeśli serwomechanizmy nie otrzymają wystarczającej mocy lub jeśli spróbujesz wyprowadzić ich moc z szyny 5 V w Arduino, spowoduje to wywołanie szumu indukowanego napięciem w serwomechanizmach, jak sugerowano powyżej. Możesz być w stanie naprawić to za pomocą dużych kondensatorów elektrolitycznych (które i tak są dobrym pomysłem do ogólnego filtrowania), ale bardziej prawdopodobne jest, że chcesz upewnić się, że źródło zasilania serwomechanizmu może rzeczywiście dostarczać kilka amperów prądu przy napięciu serwomechanizmu.

Jon Watte
źródło
1
Sygnały sterujące serwo R / C to PWM. Szerokość impulsu wynosi nominalnie 1-2 milisekundy, interwał powtarzania impulsu wynosi od 20 do 50 milisekund. Spodziewałbym się, że zmiana szerokości impulsu o więcej niż około 10 mikrosekund spowoduje, że serwo będzie roztrzęsione. Jitter w PRI zasadniczo nie będzie stanowić problemu, jeśli szerokość impulsu jest stabilna. (Mój prosty kontroler 555 zmieniał szerokość impulsu i PRI o tę samą wartość: serwo nie obchodziło.)
John R. Strohm
Wszystko, co mówisz, jest prawdą, z wyjątkiem drgań - serwa drgają, zanim szerokość impulsu zostanie „wyłączona” o 10 nas. A jitter przerwań dla zwykłego Arduino (zanim dodasz biblioteki) może sięgać nawet 10 nas! Wklejony kod ma na celu generowanie stabilnego impulsu skalnego w środowisku Arduino, który ogólnie nie jest tak dobry w stabilnych impulsach serwo jak dedykowany obwód 555.
Jon Watte
4
Właśnie napisałem artykuł pokazujący, jak generować precyzyjne impulsy na Arduino, jak powyższy kod, z wyjątkiem tego, że używa on sprzętu Timer - i nie trzeba wyłączać przerwań i zepsuć czas działania Arduino.
bigjosh 12.03.15
Zauważ, że Arduino obsługuje wyjście timera tylko na kilku pinach (piny PWM) i nie można użyć pinów Timer0 dla tej metody. Tak więc są tylko 4 piny, na których tak naprawdę działa w zwykłym Arduino UNO. Jeśli potrzebujesz prowadzić 4 lub mniej serwomechanizmów i nie potrzebujesz liczników czasu na coś innego, to dobra opcja.
Jon Watte
21

Nazywa się to „buzz”.

Powoduje to kilka rzeczy. Niestabilność mocy serwomechanizmu jest częstą przyczyną. Serwa R / C mogą rysować DUŻE impulsy, gdy po raz pierwszy uruchamiają silnik.

Wiele lat temu grałem z serwomechanizmem Tower Hobbies Royal Titan Standard, sterując nim z 555 i falownika z jednym tranzystorem. Prosty obwód sterowania. Dowiedziałem się, że silnik serwo pobierał 250 mA ze źródła 5 V podczas ciągłego ruchu. Brzęcząc, z łatwością rysował skoki półampera. (Może więcej: po prostu monitorowałem bieżący miernik na moim stanowisku, a nie szukałem bocznika wyczuwającego prąd).

Oswojone zajęło mi 220 uF bezpośrednio na moim serwie.

Spróbuj umieścić kondensator elektrolityczny, co najmniej 100 uF, bezpośrednio na zasilaczu serwomechanizmu, możliwie jak najbliżej serwomechanizmu, i sprawdź, czy to pomoże.

W oparciu o te eksperymenty nigdy nie rozważałbym użycia serwomechanizmów R / C do WSZYSTKIEGO bez dodania kondensatorów. Dotyczy to modeli sterowanych radiowo.

Może to być również spowodowane zabrudzeniem w naczyniu serwa wewnątrz serwomechanizmu. Najpierw wypróbuj kondensator.

John R. Strohm
źródło
6

Czy twoje brzęczenie / drżenie dzieje się tylko wtedy, gdy osiągasz lub zbliżasz się do granic serwomechanizmu (0 stopni lub 180 stopni)? Jeśli tak, może być dla Ciebie prosta poprawka. Przekonałem się, że tanie serwomechanizmy nie umieją bardzo dobrze trzymać się granic ruchów, co może powodować brzęczenie / drżenie, o których wspominasz. Jeśli jednak ograniczysz ich zasięg do 10 ~ 170 stopni, problem zostanie rozwiązany.

Jeśli to nie wystarczy, możesz zastosować bardziej złożone poprawki wspomniane w innych odpowiedziach, takie jak lepsza moc, lepsze czujniki serwo itp.

przygłup
źródło
Tak, dla mojego SG90 wartości te wynoszą od 18 do 162. W rzeczywistości 32 stopnie były nieosiągalne, może tylko połowa z nich.
Maxim Kachurovskiy
5

Rozwiązałem problem, „wyłączając serwo” po jego przeniesieniu. Przykład:

pinMode(PIN, OUTPUT);
myservo.write(degree);
//give servo time to move
delay(5000);
pinMode(PIN, INPUT);

PINjest pin PWM podłączony do twojego serwomechanizmu. przełączając go w tryb wejściowy byłem w stanie wyłączyć wibracje. To nie jest optymalne rozwiązanie i sugerowałbym najpierw wypróbowanie innych rozwiązań.

Ramast
źródło
Wypróbowałem inne rozwiązania, tylko to zadziałało, +1. Świetny pomysł, gdy wszystko inne zawiedzie!
Snappawapa,
3

Miałem ten sam problem z serwomechanizmami MG90S (drgania), moje linie sygnałowe są stosunkowo długie (60 ~ 70 cm), umieszczenie kondensatora 103 (10nF) nad sygnałem i linie uziemienia naprawiły dla mnie problem (umieściłem kondensator gdzieś w środek, w miejscu, w którym oryginalny kabel serwo łączy się z moim kablem wewnętrznym).

Ponadto nie mogłem użyć standardowej biblioteki Servo, ponieważ pierwszym zegarem, który chwyta Arduino Mega, jest Timer-5 i potrzebuję go do pomiaru częstotliwości. Ponieważ używam tylko 10 serwomechanizmów, wyodrębniłem kod klucza z biblioteki serwomechanizmów i zmieniłem go na używanie Timera-1 (każdy zegar obsługuje maksymalnie 12 serwomechanizmów w Mega).

Oddzielny kod znajduje się poniżej w celach informacyjnych, jeśli chcesz uwzględnić go we własnym projekcie, możesz użyć tylko górnej części, dolna część służy do testowania górnej części (nasłuchuje na porcie szeregowym, możesz podać sX i komendy vX, gdzie sX wybiera serwo, s0 wybiera pierwsze serwo, vX ustawia w nas pozycję serwomechanizmu, więc v1500 ustawiłby serwomechanizm 0 w pozycji środkowej, zakładając, że najpierw wydałeś komendę s0).

//----------------------------------------------------------------
// This is the actual servo code extracted from the servo library
//----------------------------------------------------------------

#include <avr/pgmspace.h>

//----converts microseconds to tick (assumes prescale of 8)
#define usToTicks(_us)    (( clockCyclesPerMicrosecond()* _us) / 8)

#define MIN_PULSE_WIDTH     544     // the shortest pulse sent to a servo  
#define MAX_PULSE_WIDTH     2400    // the longest pulse sent to a servo 
#define DEFAULT_PULSE_WIDTH 1500    // default pulse width when servo is attached
#define REFRESH_INTERVAL    20000   // minumim time to refresh servos in microseconds

#define TRIM_DURATION       2       // compensation ticks to trim adjust for digitalWrite delays // 12 August 2009

struct s_servar {
    //----counter for the servo being pulsed for each timer (or -1 if refresh interval)
    int8_t  channel;
};
static volatile struct s_servar gl_vars;

//----maximum number of servos controlled by one timer 
#define SERVOS_PER_TIMER    12
//----this can not be higher than SERVOS_PER_TIMER
#define SERVO_AMOUNT        6

struct s_servo {
    volatile unsigned int   ticks;
    unsigned char           pin;
};
struct s_servo  gl_servos[SERVO_AMOUNT] = {
    { usToTicks(DEFAULT_PULSE_WIDTH), 22 },
    { usToTicks(DEFAULT_PULSE_WIDTH), 23 },
    { usToTicks(DEFAULT_PULSE_WIDTH), 24 },
    { usToTicks(DEFAULT_PULSE_WIDTH), 25 },
    { usToTicks(DEFAULT_PULSE_WIDTH), 26 },
    { usToTicks(DEFAULT_PULSE_WIDTH), 27 },
};

ISR(TIMER1_COMPA_vect) {
    unsigned char       servooff;
    if(gl_vars.channel < 0 ) {
        //----channel set to -1 indicated that refresh interval completed so reset the timer
        TCNT1 = 0;
    }
    else{
        servooff = gl_vars.channel;
        if(servooff < SERVO_AMOUNT) {
            //----end the pulse
            digitalWrite(gl_servos[servooff].pin, LOW);
        }
    }
    //----increment to the next channel
    gl_vars.channel++;
    servooff = gl_vars.channel;
    if(servooff < SERVO_AMOUNT) {
        //----set timer interrupt for pulse length
        OCR1A = TCNT1 + gl_servos[servooff].ticks;
        //----start the pulse
        digitalWrite(gl_servos[servooff].pin, HIGH);
    }
    else {
        // finished all channels so wait for the refresh period to expire before starting over
        //----allow a few ticks to ensure the next OCR1A not missed
        if(((unsigned)TCNT1) + 4 < usToTicks(REFRESH_INTERVAL)) {
            OCR1A = (unsigned int)usToTicks(REFRESH_INTERVAL);
        }
        else {
            //----at least REFRESH_INTERVAL has elapsed
            OCR1A = TCNT1 + 4; 
        }
        //----this will get incremented at the end of the refresh period to start again at the first channel
        gl_vars.channel = -1;
    }
}

void InitServoISR() {
    unsigned char   ct;
    gl_vars.channel = -1;
    //----init timer 1
    TCCR1A = 0;             // normal counting mode
    TCCR1B = _BV(CS11);     // set prescaler of 8
    TCNT1 = 0;              // clear the timer count
    TIFR1 |= _BV(OCF1A);    // clear any pending interrupts;
    TIMSK1 |= _BV(OCIE1A);  // enable the output compare interrupt
    //----set all servo pins to output
    for(ct = 0; ct < SERVO_AMOUNT; ct++) {
        pinMode(gl_servos[ct].pin, OUTPUT); 
    }
}

void SetServoMicroSecs(unsigned char servooff, unsigned short value) {
    uint8_t oldSREG;
    if(servooff < SERVO_AMOUNT) {
        //----ensure pulse width is in range
        if(value < MIN_PULSE_WIDTH) { value = MIN_PULSE_WIDTH; }
        else {
            if(value > MAX_PULSE_WIDTH) { value = MAX_PULSE_WIDTH; }
        }
        value -= TRIM_DURATION;
        value = usToTicks(value);
        oldSREG = SREG;
        cli();
        gl_servos[servooff].ticks = value;
        SREG = oldSREG;
    }
}

//------------------------------------------------
// This is code to test the above servo functions
//------------------------------------------------

#define ERR_OK          0
#define ERR_UNKNOWN     1
#define ERR_OUTOFRANGE  2

#define SERDEBUG_CODE
#define MAX_SER_BUF     12

void setup() { 
    InitServoISR();

    #ifdef SERDEBUG_CODE
    Serial.begin(9600);
    Serial.println(F("Start"));
    #endif
}


void loop() {
    #ifdef SERDEBUG_CODE
    uint8_t         ct, chr;
    char            buf[MAX_SER_BUF];
    ct = 0;
    #endif   
    //----main while loop
    while(1) {
        #ifdef SERDEBUG_CODE
        //--------------------
        // Serial Port
        //--------------------
        while (Serial.available() > 0) {
            chr = Serial.read();
            if(chr == '\n') {
                ProcSerCmd(buf, ct);
                ct = 0;
            }
            else {
                //----if for some reason we exceed buffer size we wrap around
                if(ct >= MAX_SER_BUF) { ct = 0; } 
                buf[ct] = chr;
                ct++;
            }
        }
        #endif
    }
}

//------------------------------
// Serial Port Code
//------------------------------

#ifdef SERDEBUG_CODE
uint16_t RetrieveNumber(char *buf, uint8_t size) {
    //--------------------------------------------------------------
    // This function tries to convert a string into a 16 bit number
    // Mainly for test so no strict checking
    //--------------------------------------------------------------
    int8_t  ct;
    uint16_t    out, mult, chr;
    out = 0;
    mult = 1;
    for(ct = size - 1; ct >= 0; ct--) {
        chr = buf[ct];
        if(chr < '0' || chr > '9') { continue; }
        chr -= '0';
        chr *= mult;
        out += chr;
        mult *= 10;
    }
    return(out);
}

void ProcSerCmd(char *buf, uint8_t size) {
    //-----------------------------------------------------------
    // supported test commands
    // sX   X = 0 to SERVO_AMOUNT       Sets the servo for test
    // vX   X = MIN to MAX PULSE WIDTH  Sets the test servo to value X
    //-----------------------------------------------------------
    static unsigned char    lgl_servooff = 0;
    uint8_t                 chr, errcode;
    uint16_t                value;
    errcode = 0;
    while(1) {
        chr = buf[0];
        //----test commands (used during development)
        if(chr == 's') {
            value = RetrieveNumber(buf + 1, size - 1);
            if(value < 0 || value >= SERVO_AMOUNT) { errcode = ERR_OUTOFRANGE; break; }
            lgl_servooff = (unsigned char)value;
            break;
        }
        if(chr == 'v') {
            value = RetrieveNumber(buf + 1, size - 1);
            if(value < MIN_PULSE_WIDTH || value > MAX_PULSE_WIDTH) { errcode = ERR_OUTOFRANGE; break; }
            SetServoMicroSecs(lgl_servooff, value);
            break;
        }
        errcode = ERR_UNKNOWN;
        break;
    }
    if(errcode == 0) {
        Serial.println(F("OK"));
    }
    else {
        Serial.write('E');    
        Serial.println(errcode);
    }
}
#endif
Walter K
źródło
2

Moją najlepszą opcją w tym przypadku było dołączanie i odłączanie serwomechanizmów w każdej operacji.

servo1.attach(pinServo1);
for (pos = 0; pos <= servoMax; pos += 1) {
    servo1.write(pos);
    delay(10);
}
servo1.detach(pinServo1);

PS. to naprawdę nie jest żadna jakość, tylko obejście.

Shikartoos
źródło
1

Podczas gdy inni sugerowali różne rozwiązania tego brzęczącego serwomechanizmu, w tym wątku i na innych forach Arduino, a mianowicie:

  • Generuj własny puls
  • Podaj oddzielnie napięcie 5 V.
  • Unikaj przekraczania swoich granic (np. Użyj 10-170 zamiast 0-180)
  • Poprowadź kondensator
  • Odłącz po ruchu

W moim przypadku stwierdziłem, że brzęczenie ustało po podłączeniu zasilacza 9 V / 2 A do płyty Arduino. Ale najłatwiejszym rozwiązaniem było po prostu powolne przesuwanie serwomechanizmu:

for (pos = servo.read(); pos < 180; pos += 2) {
  servo.write(pos);
  delay(40);
}

YMMV.

użytkownik2105117
źródło
1
#include <Servo.h>             //Servo library
Servo servo_test;        //initialize a servo object for the connected servo  

int angle = 0;
int sw1 = 7;   // pushbutton connected to digital pin 7
int val=0;

void setup()
{
   servo_test.attach(2);     // attach the signal pin of servo to pin2 of arduino
   pinMode(sw1, INPUT_PULLUP);
}

void loop()
{
    val = digitalRead(sw1);
    if (val == LOW)
    {  
        servo_test.attach(2);     // attach the signal pin of servo to pin2 of arduino
        for(angle = 0; angle < 90; angle += 1)   // command to move from 0 degrees to 90 degrees 
        {                                  
            servo_test.write(angle);                 //command to rotate the servo to the specified angle
            delay(5);                     
        } 
    }
    else
    {
        servo_test.detach();// After servo motor stops we need detach commmand to stop vibration
    }
}
Musthafa Kadersha
źródło
0

Dla mnie wygląda to na błędy lub nieprawidłowe dostrojenie pętli sprzężenia zwrotnego. Najwyższej klasy serwosterowanie ma pewną wiedzę na temat charakterystyki silnika (indukcyjności, momentu obrotowego, prądu szczytowego, liczby biegunów), obciążenia (momentu bezwładności) i warunków chwilowych (pozycja, obroty na minutę, prąd wsteczny, prąd). Dzięki tym informacjom program sterujący silnikiem może przewidywać, co serwo zrobi w odpowiedzi na dane wejście ze sterownika (tj. Wejście prąd / napięcie) i na tej podstawie wygenerować optymalny sygnał wejściowy, aby osiągnąć pożądaną moc wyjściową.

Jak możesz sobie wyobrazić, jest to nieco skomplikowana sprawa, ale zaczniesz szukać w Internecie informacji zwrotnych na temat serwomechanizmu.

EBlake
źródło