Чи є спосіб зупинити сервоприводи від «трясіння»?


20

Дуже просто, я керую сервоприводами (9 г Micro Servos) на основі деяких даних, прочитаних з інших місць. Все працює добре, за винятком того, що сервоприводи постійно «трясуться». Тобто вони вібрують назад дуже тонкими рухами (з переривчастими рухами 1/2 -> 1см і так).

Я спробував виправити цю проблему в програмному забезпеченні, зробивши щось на кшталт:

  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

Там, де потрібен час виконання, ініціалізуйте змінні, які зберігають відображене значення сервоприводу (використовуючи сервісну бібліотеку arduino).

Функція readChange () визначається як:

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
}

Де xRead - це значення, яке було ініціалізовано (перший, зіставлений вихід сервоприводу).

Хоча, це насправді не дуже вдалий підхід. Це вимагає, щоб значення BOTH не мали змінюватися в коефіцієнті DEG (~ 10 градусів, або ~ 0,28 В у моєму випадку). Якщо я записую функцію таким чином, що або АБО менше, ніж DEG, що робити, якщо я міняв лише один сервоприймач за один раз? Отже, існує деліма ..

Це просто властивість сервоприводів (можливо, дешевих?) Чи існує обхід?


Набагато простіше було б включити посилання на пасти. Ось повний код: http://pastie.org/8191459

Я приєднав два сервоприводи разом з лазерним вказівником, щоб забезпечити два ступені свободи (X, Y). Існують варіанти, засновані на стані декількох кнопок, керувати сервоприводами різними способами. Перший - "Рух", де у мене є два фоторезистори, які, виходячи з кількості випромінювання світла, впливають на положення сервоприводів. Я ще не реалізував код для управління сервоприводами контролером Xbox. І третій варіант - просто рандомізований рух.

введіть тут опис зображення


4
У вас, мабуть, є невелика нестабільність або шум у вашому сервоконтролері. Однак ви вникаєте в багато деталей речей, які, здається, не мають нічого спільного з контролером сервоприводу, окрім недокументованого рядка "positionServo ();", про який ми можемо лише здогадуватися, - де заховані деталі. Чи закритий сервісний контролер в мікрофоні? Зовнішні закриті? Аналоговий чи цифровий? Якщо цифрова, яка роздільна здатність вимірюється? Покажіть схему всієї системи.
Олін Латроп

Скільки навантаження ви накладаєте на сервоприводи?
Кріс Лаплант

4
@OlinLathrop - (S) Він використовує стандартні радіокеровані моделі сервоприводів, у яких весь цикл сервоприводу запечений у пристрої. sherrellbc - "Серво" - це дуже-дуже загальний термін. На жаль, виробники компонентів моделей RC обрали приблизно найменш описовий термін для пристроїв, які випускають. Оскільки ми маємо справу з найбільш різними видами сервоприводів і сервосистемами, то уточнення того, що ваші "сервоприводи" є радіокерованими моделями сервоприводів - це, мабуть, хороша ідея.
Коннор Вольф

1
Ваша система занадто складна для нас, щоб ми могли її усунути. Спростіть його і подивіться, чи все ще виникають проблеми. Коли у вас є мінімальна система, яка відтворює проблему, і ви все ще не можете вирішити свою проблему, то стає доцільним звернутися за допомогою.
Філ Мороз

12
Загальна примітка для проектування лазерно-направляючих систем: поставте дзеркала на сервоприводи, потім направляйте одне на інше. Таким чином, вам не потрібно мати один сервопривід на іншому, ні лазер, встановлений на сервоприводах, і ви можете потім їх міцно закрутити.
pjc50

Відповіді:


27

Під час використання бібліотеки сервоприладів на Arduino загальним джерелом сервогуму є те, що сервопрограми, керовані перериванням, насправді не дають дуже стабільного вихідного імпульсу. Оскільки AVR приймає перерви для обслуговування годинника millis () та інших речей під час виконання Arduino, тремтіння в бібліотеці Servo має порядок декількох мікросекунд, що означає багато руху в сервоприводі.

Виправленням цього є написання власного імпульсу. Щось на зразок цього:

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

Це вимкне інші переривання та генерує набагато чистіший ШІМ імпульс. Однак це змусить "таймер millis () пропустити декілька годинних галочок. (Функцію" micros () "можна назвати чимось іншим - я точно забуду.)

Загалом, для тимчасового критичного коду ви хочете повністю позбутися часу виконання Arduino і написати своє власне, використовуючи компілятор avr-gcc та бібліотеку avr-libc, що керує середовищем Arduino. Тоді ви можете встановити таймер, щоб відзначити 4 рази на мікросекунду, або навіть 16 разів на мікросекунду, і отримати набагато кращу роздільну здатність у вашому ШІМ.

Ще однією причиною шуму в сервоприводах є дешеві сервоприводи з дешевими датчиками, де датчики шумно, або коли точне положення, яке вимагається за допомогою імпульсу, насправді не може бути закодовано датчиком. Сервоприймач побачить "переміщення в позицію 1822" і спробує це зробити, але закінчиться зчитуванням датчика 1823. Потім сервоприймач скаже "трохи рухайся назад", і закінчиться зчитуванням датчика 1821. Повторіть! Виправленням цього є використання високоякісних сервоприводів. В ідеалі - це зовсім не хобі-сервоприводи, а справжні сервоприводи з оптичними або магнітними абсолютними кодерами.

Нарешті, якщо сервоприводи не отримують достатньої потужності, або якщо ви намагаєтеся приводити їх потужність з 5V рейки на Arduino, це призведе до пониження напруги в сервоприводах, як було запропоновано вище. Можливо, ви зможете це виправити великими електролітичними конденсаторами (які все-таки є хорошою ідеєю для загальної фільтрації), але ви, швидше за все, хочете переконатися, що джерело живлення вашого сервоприводу справді може подати кілька амперів струму при серво напрузі.


1
Сигнали сервоуправління R / C - ШІМ. Ширина імпульсу номінально становить 1-2 мілісекунди, інтервал повторення імпульсу становить десь від 20 до 50 мілісекунд. Я б очікував, що більше, ніж приблизно 10 мікросекунд зміни ширини імпульсу призведуть до того, що сервопривід отримає тремтіння. Джиттер в PRI, як правило, не буде проблемою, якщо ширина імпульсу стабільна. (Мій простий брудний контролер 555 варіював ширину імпульсів і PRI на стільки ж: сервоприймач не
хвилювався

Все, що ви говорите, правдиве, за винятком тремтіння - сервоприводи будуть тремтіти до того, як ширина імпульсу буде «вимкнена» на 10 нас. А переривчастий тремтіння для простого Arduino (перед тим, як додати бібліотеки) може досягати 10 нас! Код, який я вставив, призначений для генерування стійкого до породи імпульсу в середовищі Arduino, який, як правило, не такий хороший для скелестійких сервоімпульсів, як виділений ланцюг 555.
Джон Ватт

4
Я щойно написав статтю, в якій показав, як генерувати точні імпульси на Arduino, як у наведеному вище коді, за винятком того, що він використовує апаратне забезпечення Таймера - і немає необхідності вимикати перебої та псувати час виконання Arduino.
bigjosh

Зверніть увагу, що Arduino підтримує лише вихід таймера на декількох штирях (ШІМ-шпильки,), і ви не можете використовувати штифти Timer0 для цього методу. Таким чином, є лише 4 штифти, для чого це справді працює у звичайній Arduino UNO. Якщо вам потрібно керувати 4-х сервоприводами і не потрібні таймери для чогось іншого, це хороший варіант.
Джон Ватт

21

Це називається "гудіння".

Є кілька речей, які це спричинить. Нестабільність живлення в сервоприводі є загальною причиною. R / C сервоприводи можуть намалювати великі колоски під час першого введення двигуна в рух.

Багато років тому я грав із сервоприводом Tower Hobbies Royal Titan Standard, керуючи ним із 555 та одно транзисторним інвертором. Безпроблемна схема управління. Я дізнався, що сервомотор витягував 250 мА з живлення 5В під час безперервного руху. Гуде, він легко намалював шипи напівсилі. (Можливо, більше: я просто контролював поточний лічильник на моєму стенді, не проводив обстеження шумового датчика.)

Щоб приручити його, знадобилося 220 мкФ прямо через мій сервопривід.

Спробуйте поставити електролітичний конденсатор, щонайменше, 100 мкФ, безпосередньо через джерело живлення сервоприводу, як можна ближче до електроприводу, і побачите, чи це допомагає.

Виходячи з цих експериментів, я ніколи б не подумав використовувати R / C сервоприводи для чого-небудь без додавання конденсаторів. Це включає в себе радіокеровані моделі.

Це також може бути викликано брудом у сервоприводі всередині сервоприводу. Спробуйте спочатку конденсатор.


6

Чи твій дзижчання / тремтіння відбувається лише тоді, коли на межі сервоприводу або близько до нього (0 градусів або 180 градусів)? Якщо так, то для вас може бути просте виправлення. Я виявив, що дешеві сервоприлади не вміють дуже добре триматися на межі їх руху, що може викликати гудіння / тремтіння, про яке ви згадуєте. Однак якщо ви просто обмежите їх діапазон на 10 ~ 170 градусів, проблема буде виправлена.

Якщо це недостатньо добре для вас, ви можете дотримуватися складніших виправлень, згаданих в інших відповідях, як-от краща потужність, кращі серво-датчики тощо.


Так, для мого SG90 ці значення від 18 до 162. Це насправді не робило 32 градуси недоступними, можливо, лише половину.
Максим Качуровський

5

Я вирішив свою проблему, "вимкнувши сервопривід" після його переміщення. Приклад:

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

PIN- ШІМ-штифт, підключений до вашого сервоприводу. переключивши його в режим введення, я зміг вимкнути вібрацію. Це не оптимальне рішення, і я б запропонував спробувати інші рішення спочатку.


Я спробував інші рішення, це було єдине, що працювало, +1. Чудова ідея, коли все інше не вдається!
Snappawapa

3

У мене була така ж проблема з сервоприводами MG90S (тремтіння), мої сигнальні лінії відносно довгі (60 ~ 70 см), розміщення 103 (10nF) конденсатора над сигнальними і наземними лініями вирішило проблему для мене (я розмістив конденсатор десь у посередині, в точці, де оригінальний сервокабель підключається до мого внутрішнього кабелю).

Крім того, я не міг використовувати стандартну бібліотеку сервоприладів, тому що перший таймер, який він захоплює на Arduino Mega, є Timer-5, і мені це потрібно для вимірювання частоти. Оскільки я використовую лише 10 сервоприводів, я вилучив код ключа з бібліотеки сервоприладів і змінив його на використання Таймера-1 (кожен таймер підтримує максимум 12 сервоприводів на Мега).

Окремий код наведено нижче для довідки, якщо ви хочете включити його у свій власний проект, тоді ви можете використовувати лише верхню частину, нижню частину - тестувати верхню частину (вона слухається на послідовному порту, ви можете дати sX і команди vX, де sX вибирає сервопривід, s0 обирає перший сервоприймач, vX встановлює сервове положення в нас, тому v1500 встановлює серво0 в середнє положення, припускаючи, що ви дали команду 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

2

Моїм найкращим варіантом у цьому випадку було приєднання та від'єднання сервоприводів у кожній операції.

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

PS. це насправді взагалі немає якості, просто обхід.


1

У той час як інші пропонують різні варіанти вирішення проблеми сервоприводу, у цій темі та інших форумах Arduino, а саме:

  • Генерувати власний пульс
  • Живлення 5В окремо
  • Уникайте натискання на свої межі (наприклад, використовуйте 10-170 замість 0-180)
  • Запустіть конденсатор поперек
  • Відхиліться після руху

У моєму випадку я виявив, що гудіння припинилося, коли в плату Arduino підключено джерело живлення 9 В / 2А. Але найпростішим кінцевим рішенням було просто повільно переміщати сервопривід:

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

YMMV.


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
    }
}

0

Для мене це виглядає як помилки або неправильне налаштування циклу зворотного зв'язку. Системи сервоуправління високого класу мають певні знання про моторні характеристики (індуктивність, крутний момент, піковий струм, кількість полюсів), навантаження (момент інерції) та миттєві умови (положення, обороти в хвилину, зворотний струм, струм). За допомогою цієї інформації програма управління двигуном може передбачати, що буде робити сервоприймач у відповідь на заданий вхід контролера (тобто вхід струму / напруги) і на цій основі генерувати оптимальний вхід для досягнення бажаного виходу.

Як ви можете собі уявити, це дещо складні речі, але пошук в Інтернеті щодо зворотного зв'язку сервоприводу почне вам розпочати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.