Есть ли способ остановить сервопривод от «тряски»?


20

Очень просто, я управляю сервоприводами (9g 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 - это значение, которое было инициализировано (первый, отображенный серво выход).

Хотя, это действительно не очень хороший подход. Это требует, чтобы ОБА значения не должны были изменяться с коэффициентом 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. Повторите! Исправление для этого заключается в использовании высококачественных сервоприводов. В идеале это не хобби-серво, а настоящие сервоприводы с оптическими или магнитными абсолютными энкодерами.

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


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

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

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

Обратите внимание, что Arduino поддерживает выход таймера только на нескольких выводах (выводы PWM), и вы не можете использовать выводы Timer0 для этого метода. Таким образом, на обычном Arduino UNO есть только 4 вывода. Если вам нужно управлять 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. Отличная идея, когда все остальное терпит неудачу!
Снаппавапа

3

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

Кроме того, я не мог использовать стандартную библиотеку Servo, потому что первый таймер, который она захватывает на Arduino Mega, это Timer-5, и он мне нужен для измерения частоты. Поскольку я использую только 10 сервоприводов, я извлек код ключа из библиотеки сервоприводов и изменил его на использование Timer-1 (каждый таймер поддерживает максимум 12 сервоприводов на Mega).

Ниже приведен автономный код для справки: если вы хотите включить его в свой собственный проект, то вы можете использовать только верхнюю часть, нижняя часть - для тестирования верхней части (она прослушивает последовательный порт, вы можете дать 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.