Apakah ada cara untuk menghentikan servos dari "gemetar"?


20

Sangat sederhana, saya mengendalikan servos (9g Micro Servos) berdasarkan pada beberapa data yang dibaca dari tempat lain. Semuanya berfungsi dengan baik kecuali bahwa servos akan terus "gemetar." Artinya, mereka bergetar kembali dengan gerakan yang sangat halus (dengan gerakan terputus-putus 1/2 -> 1cm atau lebih).

Saya mencoba memperbaiki masalah ini dalam perangkat lunak dengan melakukan sesuatu seperti:

  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

Di mana do-while diperlukan menginisialisasi variabel yang menyimpan nilai servo yang dipetakan (menggunakan perpustakaan arduino servo).

Fungsi readChange () didefinisikan sebagai:

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
}

Di mana xRead adalah nilai yang diinisialisasi (yang pertama, keluaran servo yang dipetakan).

Meskipun, ini sebenarnya bukan pendekatan yang baik. Ini mensyaratkan bahwa KEDUA nilai tidak boleh diubah oleh faktor DEG (~ 10 derajat, atau ~ 0.28V dalam kasus saya). Jika saya menulis fungsi sedemikian sehingga ATAU kurang dari DEG, lalu bagaimana jika saya hanya mengubah satu servo sekaligus? Jadi ada delimma ..

Apakah ini hanya properti servos (mungkin yang murah?) Atau apakah ada solusi?


Akan lebih mudah untuk menyertakan tautan pastie. Berikut adalah kode lengkapnya: http://pastie.org/8191459

Saya telah melampirkan dua servos bersama dengan laser pointer untuk memungkinkan dua derajat kebebasan (X, Y). Ada beberapa opsi, berdasarkan keadaan beberapa tombol, untuk mengontrol servos dengan berbagai cara. Yang pertama adalah "Gerak" di mana saya memiliki dua fotoresistor yang, berdasarkan jumlah paparan cahaya, mempengaruhi posisi servos. Saya belum menerapkan kode untuk mengontrol servos oleh pengontrol Xbox. Dan opsi ketiga hanyalah gerakan acak.

masukkan deskripsi gambar di sini


4
Anda tampaknya memiliki sedikit ketidakstabilan atau kebisingan di pengontrol servo Anda. Namun, Anda masuk ke banyak detail hal-hal yang tampaknya tidak ada hubungannya dengan pengontrol servo, selain garis tidak terdokumentasi "positionServo ();", yang hanya bisa kita tebak di mana detailnya dikubur. Apakah pengontrol servo ditutup dalam mikro? Ditutup secara eksternal? Analog atau digital? Jika digital, berapa resolusi yang diukur? Tampilkan diagram seluruh sistem.
Olin Lathrop

Berapa banyak beban yang kamu berikan pada servos?
Chris Laplante

4
@OlinLathrop - (S) Dia menggunakan servos model radio-dikendalikan standar, yang memiliki seluruh loop servo dimasukkan ke dalam perangkat. sherrellbc - "Servo" adalah istilah yang sangat, sangat umum. Sayangnya, produsen komponen model RC memilih istilah yang paling tidak deskriptif untuk perangkat yang diproduksi. Karena kita berurusan dengan berbagai jenis servos dan sistem-servo yang berbeda di sini, menetapkan bahwa "servos" Anda adalah model servos yang dikendalikan oleh radio mungkin merupakan ide yang bagus.
Connor Wolf

1
Sistem Anda terlalu rumit bagi kami untuk dapat memecahkan masalah untuk Anda. Sederhanakan, dan lihat apakah Anda masih memiliki masalah. Ketika Anda memiliki sistem minimal yang mereproduksi masalah, dan Anda masih tidak dapat memperbaikinya sendiri, maka menjadi tepat untuk meminta bantuan.
Phil Frost

12
Catatan umum untuk merancang sistem pengarah laser: letakkan cermin pada servos, lalu arahkan satu ke yang lain. Dengan begitu Anda tidak harus memiliki satu servo yang terpasang di yang lain, atau laser yang dipasang pada servos, dan Anda kemudian dapat membaut semuanya dengan kuat.
pjc50

Jawaban:


27

Saat menggunakan pustaka Servo pada Arduino, sumber umum servo buzz adalah bahwa rutinitas servo yang digerakkan oleh interupsi tidak benar-benar memberikan pulsa keluaran yang sangat stabil. Karena AVR mengambil interupsi untuk melayani jam millis () dan hal-hal lain dalam runtime Arduino, jitter di perpustakaan Servo ada di urutan beberapa mikrodetik, yang diterjemahkan ke banyak gerakan di servo.

Cara mengatasinya adalah menulis pulsa Anda sendiri. Sesuatu seperti ini:

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

Ini akan mematikan interupsi lainnya, dan menghasilkan pulsa PWM yang jauh lebih bersih. Namun, itu akan membuat timer "millis () melewatkan beberapa tick ticks. (Fungsi" micros () "dapat disebut sesuatu yang lain - saya lupa persis apa.)

Secara umum, untuk menentukan waktu kode kritis, Anda ingin menyingkirkan runtime Arduino sepenuhnya, dan menulis sendiri menggunakan kompiler avr-gcc dan perpustakaan avr-libc yang mendukung lingkungan Arduino. Kemudian Anda dapat mengatur timer untuk mencentang 4 kali per mikrodetik, atau bahkan 16 kali per mikrodetik, dan mendapatkan resolusi yang jauh lebih baik di PWM Anda.

Penyebab lain buzz di servos adalah servos murah dengan sensor murah, di mana sensornya berisik, atau ketika posisi tepat yang diminta dengan pulsa tidak bisa dikodekan oleh sensor. Servo akan melihat "pindah ke posisi 1822" dan mencoba melakukannya, tetapi berakhir dengan pembacaan sensor 1823. Servo kemudian akan mengatakan "mundur sedikit" dan berakhir dengan pembacaan sensor 1821. Ulangi! Cara mengatasinya adalah dengan menggunakan servos berkualitas tinggi. Idealnya, bukan servos hobi sama sekali, tetapi servos asli dengan enkoder absolut optik atau magnetik.

Akhirnya, jika servos tidak mendapatkan daya yang cukup, atau jika Anda mencoba menggerakkan tenaganya dari rel 5V pada Arduino, ini akan menghasilkan buzz yang disebabkan oleh penurunan tegangan pada servos, seperti yang disarankan di atas. Anda mungkin dapat memperbaikinya dengan kapasitor elektrolitik besar (yang merupakan ide bagus untuk penyaringan umum) tetapi Anda lebih mungkin ingin memastikan sumber daya servo Anda benar-benar dapat memberikan beberapa amp arus pada tegangan servo.


1
Sinyal kontrol servo R / C adalah PWM. Lebar pulsa secara nominal 1-2 milidetik, interval pengulangan pulsa berkisar antara 20 hingga 50 milidetik. Saya berharap lebih dari sekitar 10 mikrodetik variasi dalam lebar pulsa menyebabkan servo menjadi gelisah. Jitter dalam PRI umumnya tidak akan menjadi masalah jika lebar pulsa stabil. (Kontroler 555 tanah-sederhana saya memvariasikan lebar pulsa dan PRI dengan jumlah yang sama: servo tidak peduli.)
John R. Strohm

Semua yang Anda katakan adalah benar, kecuali jitter - servos akan jitter sebelum lebar pulsa "mati" oleh 10 kita. Dan jitter interupsi untuk Arduino biasa (sebelum Anda menambahkan perpustakaan) dapat mencapai 10 us! Kode yang saya tempel dimaksudkan untuk menghasilkan pulsa stabil rock di lingkungan Arduino, yang umumnya tidak sebagus pulsa servo stabil rock sebagai sirkuit 555 khusus.
Jon Watte

4
Saya baru saja menulis sebuah artikel yang menunjukkan cara menghasilkan pulsa tepat pada Arduino seperti kode di atas, kecuali ia menggunakan perangkat keras Timer -dan tidak perlu mematikan interupsi dan mengacaukan run-time Arduino.
bigjosh

Perhatikan bahwa Arduino hanya mendukung output timer pada beberapa pin (pin PWM,) dan Anda tidak dapat menggunakan pin Timer0 untuk metode ini. Jadi, hanya ada 4 pin yang benar-benar berfungsi untuk Arduino UNO biasa. Jika Anda perlu mengendarai 4 atau kurang servos, dan tidak memerlukan timer untuk hal lain, itu adalah pilihan yang baik.
Jon Watte

21

Ini disebut "buzz".

Ada beberapa hal yang akan menyebabkannya. Ketidakstabilan dalam kekuatan ke servo adalah penyebab umum. R / C servos dapat menggambar beberapa paku BESAR ketika pertama kali menempatkan motor dalam gerakan.

Bertahun-tahun yang lalu, saya bermain dengan servo Tower Hobbies Royal Titan Standard, mengendalikannya dari 555 dan inverter satu-transistor. Sirkuit kontrol mati-sederhana. Saya belajar bahwa motor servo menarik 250 mA dari suplai 5V saat bergerak terus menerus. Berdengung, itu dengan mudah menarik paku setengah amp. (Mungkin lebih: saya hanya memantau meteran saat ini pada persediaan bangku saya, bukan membatasi shunt penginderaan saat ini.)

Butuh 220 uF langsung melintasi servo saya untuk menjinakkannya.

Coba letakkan kapasitor elektrolitik, setidaknya 100 uF, langsung di seberang catu daya ke servo, sedekat mungkin dengan servo, dan lihat apakah itu membantu.

Berdasarkan percobaan tersebut, saya tidak akan pernah mempertimbangkan menggunakan R / C servos untuk APA SAJA tanpa menambahkan kapasitor. Itu termasuk model yang dikendalikan radio.

Ini juga dapat disebabkan oleh kotoran di panci servo di dalam servo. Coba kapasitor terlebih dahulu.


6

Apakah dengungan / goncangan Anda terjadi hanya ketika berada pada atau dekat dengan batas servo (0 derajat atau 180 derajat)? Jika demikian, mungkin ada perbaikan sederhana untuk Anda. Saya telah menemukan bahwa servos murah tidak tahu bagaimana untuk tetap pada batas gerakan mereka dengan sangat baik, yang dapat menyebabkan dengungan / goncangan yang Anda sebutkan. Namun, jika Anda hanya membatasi jangkauannya hingga 10 ~ 170 derajat, masalah ini akan diperbaiki.

Jika itu tidak cukup baik untuk Anda, Anda dapat mengikuti perbaikan yang lebih kompleks yang disebutkan dalam jawaban lain, seperti daya yang lebih baik, sensor servo yang lebih baik, dll.


Ya, untuk SG90 saya, nilai-nilai ini adalah 18 hingga 162. Tidak benar-benar membuat 32 derajat tidak dapat dijangkau, mungkin hanya setengahnya.
Maxim Kachurovskiy

5

Saya telah memperbaiki masalah saya dengan "mematikan servo" setelah saya memindahkannya. Contoh:

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

PINadalah pin PWM yang terhubung ke servo Anda. dengan mengalihkannya ke mode Input saya dapat mematikan getaran. Ini bukan solusi optimal dan saya sarankan mencoba solusi lain terlebih dahulu.


Saya mencoba solusi lain, ini satu-satunya yang berfungsi, +1. Ide bagus ketika semuanya gagal!
Snappawapa

3

Saya memiliki masalah yang sama dengan MG90S servos (jittering), jalur sinyal saya relatif panjang (60 ~ 70cm), menempatkan kapasitor 103 (10nF) di atas sinyal dan jalur tanah memperbaiki masalah bagi saya (saya menempatkan kapasitor di suatu tempat di tengah, pada titik di mana kabel servo asli terhubung ke kabel internal saya).

Selain itu saya tidak bisa menggunakan pustaka Servo standar karena timer pertama yang diambil pada Arduino Mega adalah Timer-5 dan saya memerlukannya untuk pengukuran frekuensi. Karena saya hanya menggunakan 10 servos, saya mengekstrak kode kunci dari perpustakaan Servo dan mengubahnya menjadi menggunakan Timer-1 (setiap timer mendukung maksimum 12 servo pada Mega).

Kode yang berdiri sendiri di bawah ini untuk referensi, jika Anda ingin memasukkannya dalam proyek Anda sendiri maka Anda dapat menggunakan bagian atas saja, bagian bawah adalah untuk menguji bagian atas (itu mendengarkan pada port serial, Anda dapat memberikan sX dan perintah vX, di mana sX memilih servo, s0 akan memilih servo pertama, vX menetapkan posisi servo di kami, jadi v1500 akan mengatur servo0 ke posisi tengah, dengan asumsi Anda memberikan perintah s0 pertama).

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

Opsi terbaik saya dalam hal ini adalah melampirkan dan melepaskan Servo di setiap operasi.

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

PS. ini benar-benar tidak berkualitas sama sekali, hanya solusi.


1

Sementara yang lain telah menyarankan berbagai solusi untuk masalah buzz servo ini, di utas ini dan forum Arduino lainnya, yaitu:

  • Hasilkan pulsa sendiri
  • Suplai daya 5V secara terpisah
  • Hindari mendorong sampai batasnya (mis. Gunakan 10-170 bukannya 0-180)
  • Jalankan kapasitor
  • Lepaskan setelah bergerak

Dalam kasus saya, saya menemukan bahwa dengungan berhenti ketika catu daya 9V / 2A dicolokkan ke papan Arduino. Tapi solusi pamungkas termudah adalah dengan menggerakkan servo perlahan:

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

Bagi saya, ini terlihat seperti kesalahan atau salah pengaturan dari loop umpan balik. Sistem kontrol servo kelas atas memiliki pengetahuan tentang karakteristik motor (induktansi, torsi, arus puncak, jumlah kutub), beban (momen inersia), dan kondisi seketika (posisi, rpm, back-emf, arus). Dengan informasi ini, program kontrol motor dapat membuat prediksi tentang apa yang akan dilakukan servo sebagai respons terhadap input yang diberikan dari pengontrol (yaitu input arus / tegangan) dan atas dasar itu menghasilkan input optimal untuk mencapai output yang diinginkan.

Seperti yang dapat Anda bayangkan, ini adalah hal yang agak rumit, tetapi pencarian internet tentang umpan balik servo akan membantu Anda memulai.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.