Servoların “titremesini” engellemenin bir yolu var mı?


20

Çok basit bir şekilde, başka bir yerden okunan bazı verilere dayanarak servoları (9g Mikro Servolar) kontrol ediyorum. Servolar sürekli "sallanacak" dışında her şey iyi çalışıyor. Yani, çok ince hareketlerle (1/2 -> 1 cm kadar aralıklı hareketlerle) geri titreşirler.

Aşağıdaki gibi bir şey yaparak yazılımda bu sorunu düzeltmeye çalıştım:

  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

Do-while gerektiğinde, eşlenen servo değerini saklayan değişkenleri başlatın (arduino servo kütüphanesini kullanarak).

ReadChange () işlevi şu şekilde tanımlanır:

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
}

Burada xRead, başlatılan değerdir (ilk, eşlenen servo çıkışı).

Bununla birlikte, bu gerçekten iyi bir yaklaşım değildir. İKİ değerlerin bir DEG faktörü tarafından değiştirilmemesi gerekir (benim durumumda ~ 10 derece veya ~ 0.28V). Eğer işlevi OR veya DEG'den daha az olacak şekilde yazarsam, bir seferde sadece bir servo değiştiriyor olsaydım ne olurdu? Demek bir sınır var ..

Bu sadece servoların (belki de ucuz olanların) bir özelliği mi yoksa bir çözüm var mı?


Bir pastie bağlantısı eklemek çok daha kolay olurdu. İşte tam kod: http://pastie.org/8191459

İki serbestlik derecesine (X, Y) izin vermek için lazer işaretçisiyle birlikte iki servo bağladım. Servoları çeşitli şekillerde kontrol etmek için birkaç düğmenin durumuna bağlı seçenekler vardır. Birincisi, ışığa maruz kalma miktarına bağlı olarak, servoların konumunu etkileyen iki fotodirençim olan "Hareket" tir. Henüz bir Xbox denetleyicisi tarafından servoları kontrol etmek için kod uygulamadı. Ve üçüncü seçenek sadece rastgele harekettir.

resim açıklamasını buraya girin


4
Görünüşe göre servo kontrol cihazınızda biraz kararsızlık veya gürültü var. Bununla birlikte, sadece tahmin edebileceğimiz, belgelenmemiş "positionServo ();" hattı dışında, servo denetleyiciyle hiçbir ilgisi yok gibi görünen birçok ayrıntıya girersiniz. Servo kontrol cihazı mikroda kapalı mı? Harici olarak kapalı mı? Analog mu dijital mi? Dijitalse, hangi çözünürlükte ölçülüyor? Tüm sistemin bir diyagramını gösterin.
Olin Lathrop

Servolara ne kadar yük atıyorsunuz?
Chris Laplante

4
@OlinLathrop - (S) Tüm servo döngüsünü cihaza pişirmiş standart radyo kontrollü model servolar kullanıyor. sherrellbc - "Servo" çok ama çok genel bir terimdir. Ne yazık ki, RC model bileşen üreticileri, üretilen cihazlar için en az tanımlayıcı terimi seçti. Burada birçok farklı servo ve servo sistemle uğraştığımızdan, "servo" larınızın radyo kontrollü model servo olduğunu belirtmek muhtemelen iyi bir fikirdir.
Connor Wolf

1
Sisteminiz sizin için sorunu gideremeyeceğimiz kadar karmaşık. Basitleştirin ve hala sorunun olup olmadığını görün. Bir olduğunda minimum problemi çoğalır sistemi ve hala bunu kendini düzeltemez, o zaman yardım istemek uygun hale gelir.
Phil Frost

12
Lazer yönlendirme sistemlerinin tasarımı için genel not: aynaları servolara koyun, sonra birini diğerine yönlendirin. Bu şekilde bir servoyu diğerine veya lazeri servolara monte etmek zorunda kalmazsınız ve daha sonra hepsini sıkıca vidalayabilirsiniz.
pjc50

Yanıtlar:


27

Bir Arduino'da Servo kütüphanesini kullanırken, yaygın bir servo vızıltı kaynağı, kesintiye bağlı servo rutinlerinin aslında çok kararlı bir çıkış darbesi vermemesidir. AVR, milis () saatine ve Arduino çalışma zamanında diğer şeylere hizmet vermek için kesintiler aldığından, Servo kütüphanesindeki titreşim, servoda çok fazla harekete dönüşen birkaç mikrosaniye civarındadır.

Bunun düzeltmesi kendi nabzınızı yazmaktır. Bunun gibi bir şey:

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

Bu, diğer kesintileri kapatır ve daha temiz bir PWM darbesi oluşturur. Ancak, "millis () zamanlayıcı bazı saat keneleri özledim yapacak. (" Micros () "fonksiyonu başka bir şey denilebilir - Ben tam olarak ne unutuyorum.)

Genel olarak, kritik kod zamanlaması için, Arduino çalışma zamanından tamamen kurtulmak ve Arduino ortamına güç veren avr-gcc derleyicisini ve avr-libc kütüphanesini kullanarak kendiniz yazmak istersiniz. Ardından mikrosaniye başına 4 kez, hatta mikrosaniye başına 16 kez işaretlemek ve PWM'nizde çok daha iyi bir çözünürlük elde etmek için bir zamanlayıcı ayarlayabilirsiniz.

Servolardaki vızıltıların bir başka nedeni, sensörlerin gürültülü olduğu veya nabızla istenen tam konum gerçekten sensör tarafından kodlanamadığı zaman ucuz sensörlü ucuz servolardır. Servo "1822 pozisyonuna hareket et" i görecek ve bunu yapmaya çalışacaktır, fakat sensör 1823 okumasıyla sona erecektir. Servo daha sonra "biraz geriye hareket et" diyecektir ve sensör 1821 okumasıyla sona erecektir. Tekrarlayın! Bunun çözümü yüksek kaliteli servolar kullanmaktır. İdeal olarak, hiç hobi servo değil, optik veya manyetik mutlak enkoderli gerçek servolar.

Son olarak, eğer servolar yeterli güç almazsa veya güçlerini Arduino'daki 5V raydan çekmeye çalışırsanız, bu yukarıda belirtildiği gibi servolarda gerilim düşmesine bağlı vızıltı üretir. Büyük elektrolitik kapasitörlerle (yine de genel filtreleme için iyi bir fikirdir) düzeltebilirsiniz, ancak servo güç kaynağınızın gerçekten servo voltajında ​​birkaç amper akım sağlayabildiğinden emin olmak istersiniz.


1
Uzaktan kumanda servo kontrol sinyalleri PWM'dir. Darbe genişliği nominal olarak 1-2 milisaniyedir, darbe tekrarlama aralığı 20 ila 50 milisaniyedir. Servonun gerginleşmesine neden olmak için darbe genişliğinde yaklaşık 10 mikrosaniyeden fazla varyasyon bekleyebilirim. Darbe genişliği sabitse, PRI'daki titreşim genellikle bir sorun olmayacaktır. (Kir basit 555 kontrol
cihazım

Söylediğiniz her şey doğrudur, jitter - servolar, darbe genişliği 10 bize "kapalı" olmadan önce titreyecektir. Ve düz Arduino için kesme kütüphanesi (kütüphaneleri eklemeden önce) 10 bize kadar çıkabilir! Yapıştırdığım kod Arduino ortamında, genellikle 555 devresi kadar kaya kararlı servo darbelerinde iyi olmayan kaya kararlı bir darbe üretmeyi amaçlamaktadır.
Jon Watte

4
Sadece Timer donanımını kullanması ve Arduino çalışma zamanını bozmasına gerek kalmaması dışında yukarıdaki kod gibi Arduino'da nasıl hassas darbeler üretileceğini gösteren bir makale yazdım .
bigjosh

Arduino'nun sadece birkaç pimdeki (PWM pimleri) zamanlayıcı çıkışını desteklediğini ve bu yöntem için Timer0 pimlerini kullanamayacağınızı unutmayın. Bu nedenle, normal bir Arduino UNO'da gerçekten işe yarayan sadece 4 pim var. 4 veya daha az servo sürmeniz gerekiyorsa ve başka bir şey için zamanlayıcılara ihtiyacınız yoksa, bu iyi bir seçenektir.
Jon Watte

21

Buna "vızıltı" denir.

Buna neden olacak birkaç şey var. Servoya giden güçteki dengesizlik yaygın bir nedendir. R / C servoları, motoru ilk kez çalıştırdıklarında bazı BÜYÜK sivri uçlar çizebilir.

Yıllar önce, 555 ve tek transistörlü bir invertörden kontrol eden bir Tower Hobbies Royal Titan Standard servo ile oynadım. Ölü basit kontrol devresi. Servo motorun sürekli hareket halindeyken 5V beslemeden 250 mA çektiğini öğrendim. Uğultu, kolayca yarım amp sivri çekti. (Belki daha fazlası: Sadece tezgah beslememdeki akım ölçeri izliyordum, akım algılayan bir şantın kapsamını değil.)

Onu evcilleştirmek için doğrudan servo genelinde 220 uF aldı.

En az 100 uF elektrolitik kondansatörü doğrudan servo güç kaynağına, servoya mümkün olduğunca elektriksel olarak yakın bir yere koymayı deneyin ve bunun yardımcı olup olmadığını görün.

Bu deneylere dayanarak, HİÇBİR ZAMAN servoları kapasitör eklemeden kullanmayı asla düşünmem. Buna radyo kontrollü modeller dahildir.

Bu, servo içindeki servo kabındaki kirden de kaynaklanabilir. Önce kapasitörü deneyin.


6

Vızıltı / titreme sadece servo sınırlarında veya yakınında (0 derece veya 180 derece) oluyor mu? Eğer öyleyse, sizin için basit bir düzeltme olabilir. Ucuz servoların bahsettiğiniz vızıltıya / titremeye neden olabilecek hareketlerinin sınırlarında nasıl kaldığını bilmediğini gördüm. Ancak, aralıklarını sadece 10 ~ 170 derece ile sınırlarsanız, sorun düzeltilecektir.

Bu sizin için yeterince iyi değilse, daha iyi güç, daha iyi servo sensörler vb.Gibi diğer cevaplarda belirtilen daha karmaşık düzeltmeleri takip edebilirsiniz.


Evet, SG90'ım için bu değerler 18 ila 162'dir. Aslında 32 dereceyi ulaşılamaz hale getirmedi, belki sadece yarısı.
Maxim Kachurovskiy

5

Taşıdıktan sonra "servoyu kapatarak" sorunumu çözdüm. Örnek:

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

PINservo'nuza bağlı PWM pinidir. Giriş moduna geçirerek titreşimi kapatabiliyordum. Bu en uygun çözüm değildir ve önce diğer çözümleri denemenizi öneririm.


Diğer çözümleri denedim, çalışan tek çözüm buydu, +1. Her şey başarısız olduğunda harika bir fikir!
Snappawapa

3

MG90S servoları ile aynı problemi yaşadım (titreme), sinyal hatlarım nispeten uzun (60 ~ 70 cm), 103 (10nF) kapasitörün sinyal üzerine yerleştirilmesi ve toprak hatları sorunu benim için düzeltti (kapasitörü ortada, orijinal servo kablosunun dahili kabloma bağlandığı noktada).

Ayrıca standart Servo kütüphanesini kullanamadım çünkü Arduino Mega'da aldığı ilk zamanlayıcı Timer-5 ve frekans ölçümü için buna ihtiyacım var. Yalnızca 10 servo kullandığımda, anahtar kodunu Servo kütüphanesinden çıkardım ve Timer-1 kullanarak değiştirdim (her bir zamanlayıcı Mega'da maksimum 12 servoyu destekliyor).

Tek başına kod referans için aşağıdadır, eğer kendi projenize dahil etmek istiyorsanız, sadece üst kısmı kullanabilirsiniz, alt kısım üst kısmı test etmektir (seri portta dinler, sX verebilirsiniz ve vX komutları, burada sX bir servo seçer, s0 ilk servoyu seçer, vX içimizde servo pozisyonunu ayarlar, böylece v1500 önce bir s0 komutu verdiğinizi varsayarsak servo0'ı orta konuma ayarlar).

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

Bu durumda en iyi seçeneğim, her işlemde Servo'ları takmak ve çıkarmaktı.

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

PS. bu gerçekten hiçbir kalite, sadece bir çözüm.


1

Diğerleri bu servo uğultu sorununa çeşitli çözümler önermiş olsa da, bu konu ve diğer Arduino forumlarında:

  • Kendi nabzını üret
  • Ayrı olarak 5V güç sağlayın
  • Sınırlarını zorlamaktan kaçının (örn. 0-180 yerine 10-170 kullanın)
  • Karşıdan bir kondansatör çalıştırın
  • Hareket ettikten sonra ayır

Benim durumumda, Arduino kartına bir 9V / 2A güç kaynağı takıldığında uğultunun durduğunu buldum. Ancak en kolay nihai çözüm, servoyu yavaşça hareket ettirmekti:

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

Bana göre bu, geri bildirim döngüsünün hataları veya yanlış ayarlanması gibi görünüyor. Üst düzey servo kontrol sistemleri motor özellikleri (endüktans, tork, tepe akımı, kutup sayısı), yük (atalet momenti) ve anlık koşullar (pozisyon, rpm, arka emf, akım) hakkında bilgi sahibidir. Bu bilgi ile motor kontrol programı, kontrol cihazından verilen bir girişe (yani akım / gerilim girişi) karşılık olarak servonun ne yapacağına ilişkin tahminlerde bulunabilir ve bu temelde istenen çıkışı elde etmek için optimum girişi üretebilir.

Tahmin edebileceğiniz gibi, bu biraz karmaşık şeyler, ancak servo geribildirimi ile ilgili bir internet araması başlamanıza yardımcı olacak.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.