서보가 "흔들리는"것을 막을 수있는 방법이 있습니까?


20

간단히 말해서 다른 곳에서 읽은 데이터를 기반으로 서보 (9g 마이크로 서보)를 제어하고 있습니다. 서보가 지속적으로 "흔들리는"것을 제외하고는 모든 것이 잘 작동합니다. 즉, 그들은 매우 미묘한 움직임 (1/2-> 1cm 정도의 간헐적 인 움직임)으로 진동합니다.

소프트웨어에서 다음과 같은 작업을 수행 하여이 문제를 해결하려고했습니다.

  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이 필요한 경우 매핑 된 서보 값을 저장하는 변수를 초기화합니다 (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.28V)만큼 변경되어서는 안됩니다. OR이 DEG보다 작도록 함수를 작성하면 한 번에 하나의 서보 만 변경하면 어떻게됩니까? 그래서 딜림 마가 있습니다 ..

이것은 단순히 서보의 속성입니까 (아마도 저렴한 것입니까?) 해결 방법이 있습니까?


Pastie 링크를 포함시키는 것이 훨씬 간단합니다. 전체 코드는 다음과 같습니다. http://pastie.org/8191459

두 개의 자유도 (X, Y)를 허용하기 위해 레이저 포인터와 함께 두 개의 서보를 연결했습니다. 여러 버튼의 상태를 기반으로 다양한 방법으로 서보를 제어하는 ​​옵션이 있습니다. 첫 번째는 "모션 (Motion)"인데, 여기서는 노광량에 따라 서보 위치에 영향을주는 두 개의 포토 레지스트가 있습니다. Xbox 컨트롤러에서 서보를 제어하는 ​​코드를 아직 구현하지 않았습니다. 그리고 세 번째 옵션은 무작위 움직임입니다.

여기에 이미지 설명을 입력하십시오


4
서보 컨트롤러에 약간의 불안정성 또는 노이즈가있는 것 같습니다. 그러나 문서화되지 않은 행 "positionServo ();"외에는 서보 컨트롤러와 관련이없는 것 같은 많은 세부 사항을 살펴볼 수 있습니다. 여기서는 세부 정보가 어디에 저장되어 있는지 추측 할 수 있습니다. 서보 컨트롤러가 마이크로로 닫혀 있습니까? 외부 휴무? 아날로그 또는 디지털? 디지털 인 경우 어떤 해상도로 측정됩니까? 전체 시스템의 다이어그램을 보여줍니다.
Olin Lathrop

서보에 얼마나 많은 부하를 가하고 있습니까?
크리스 라플란 트

4
@OlinLathrop-(S) 전체 무선 루프를 장치에 구운 표준 무선 제어 모델 서보를 사용하고 있습니다. sherrellbc- "서보"는 매우 일반적인 용어입니다. 불행히도 RC 모델 구성 요소 제조업체는 생산 장치에 대한 설명이 가장 적은 용어를 선택했습니다. 우리는 여기서 가장 다양한 종류의 서보 및 서보 시스템을 다루기 때문에 "서보"가 무선 제어 모델 서보임을 지정하는 것이 좋습니다.
코너 울프

1
귀하의 시스템이 너무 복잡하여 문제를 해결할 수 없습니다. 그것을 단순화하고 여전히 문제가 있는지 확인하십시오. 문제를 재현 하는 최소한의 시스템이 있어도 스스로 해결할 수 없으면 도움을 요청하는 것이 적절 해집니다.
Phil Frost

12
레이저 지향 시스템 설계에 대한 일반적인 참고 사항 : 서보에 미러를 놓은 다음 서로를 지시하십시오. 이렇게하면 하나의 서보를 다른 서보에 장착하거나 레이저를 서보에 장착 할 필요가 없으며 모든 볼트를 단단히 고정시킬 수 있습니다.
pjc50

답변:


27

Arduino에서 서보 라이브러리를 사용할 때 일반적인 서보 버즈 소스는 인터럽트 구동 서보 루틴이 실제로 매우 안정적인 출력 펄스를 제공하지 않는다는 것입니다. AVR은 Arduino 런타임에서 millis () 클럭 및 기타 서비스를 서비스하기 위해 인터럽트를 수행하기 때문에 서보 라이브러리의 지터는 몇 마이크로 초 정도이며, 이는 서보의 많은 움직임을 의미합니다.

이것에 대한 해결책은 자신의 펄스를 작성하는 것입니다. 이 같은:

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

이것은 다른 인터럽트를 끄고 훨씬 더 깨끗한 PWM 펄스를 생성합니다. 그러나 "millis () 타이머가 일부 시계 틱을 놓칠 수 있습니다."micros () "함수는 다른 것으로 호출 될 수 있습니다.

일반적으로 중요한 코드 타이밍의 경우 Arduino 런타임을 완전히 제거하고 Arduino 환경을 지원하는 avr-gcc 컴파일러 및 avr-libc 라이브러리를 사용하여 직접 작성하십시오. 그런 다음 타이머를 설정하여 마이크로 초당 4 회 또는 심지어 마이크로 초당 16 회까지 틱하고 PWM에서 훨씬 더 나은 해상도를 얻을 수 있습니다.

서보에서 버즈의 또 다른 원인은 센서가 노이즈가 있거나 펄스로 요청 된 정확한 위치가 실제로 센서에 의해 인코딩 될 수없는 값싼 센서가있는 저렴한 서보입니다. 서보는 "1822 위치로 이동"을보고 시도하지만 센서 판독 값 1823으로 끝납니다. 서보는 "조금 뒤로 이동합니다"라고 말하고 센서 판독 값 1821로 끝납니다. 반복합니다! 이를위한 해결책은 고품질 서보를 사용하는 것입니다. 이상적으로는 취미 서보가 아니라 광학 또는 자기 절대 인코더가 장착 된 실제 서보가 이상적입니다.

마지막으로, 서보에 충분한 전원이 공급되지 않거나 Arduino의 5V 레일에서 전원을 공급하려고하면 위에서 제안한 것처럼 서보에서 전압 새 그로 인한 버즈가 발생합니다. 대형 전해 커패시터 (어쨌든 일반적인 필터링에 좋은 아이디어)로 문제를 해결할 수는 있지만 서보 전원이 실제로 서보 전압에서 몇 암페어의 전류를 공급할 수 있는지 확인하고 싶을 것입니다.


1
R / C 서보 제어 신호는 PWM입니다. 펄스 폭은 명목상 1-2 밀리 초이고 펄스 반복 간격은 20 ~ 50 밀리 초입니다. 펄스 폭이 약 10 마이크로 초 이상 변해 서보가 불안정 해지기를 기대합니다. 펄스 폭이 안정적인 경우 PRI의 지터는 일반적으로 문제가되지 않습니다. (더러움이 적은 간단한 555 컨트롤러는 펄스 폭과 PRI를 같은 양으로 변경했습니다. 서보는 신경 쓰지 않았습니다.)
John R. Strohm

지터를 제외하고 말한 모든 것이 사실입니다. 펄스 폭이 10 us만큼 "오프"되기 전에 서보가 지 터링합니다. 그리고 일반 Arduino의 인터럽트 지터 (라이브러리를 추가하기 전에)는 10 us까지 올라갈 수 있습니다! 내가 붙여 넣은 코드는 Arduino 환경에서 록 안정 펄스를 생성하기위한 것인데, 이는 일반적으로 전용 555 회로만큼 록 안정 서보 펄스에 좋지 않습니다.
Jon Watte

4
방금 타이머 하드웨어를 사용한다는 점을 제외하고는 위의 코드와 같이 Arduino에서 정확한 펄스생성 하는 방법을 보여주는 기사를 작성했습니다. 인터럽트를 끄고 Arduino 런타임을 망칠 필요가 없습니다.
bigjosh

Arduino는 몇 개의 핀 (PWM 핀)에서만 타이머 출력을 지원하며이 방법에는 Timer0 핀을 사용할 수 없습니다. 따라서 일반 Arduino UNO에서 실제로 작동하는 핀은 4 개뿐입니다. 4 개 이하의 서보를 구동해야하고 다른 것에 타이머가 필요하지 않은 경우 좋은 선택입니다.
Jon Watte

21

이것을 "버즈"라고합니다.

그것을 일으킬 몇 가지가 있습니다. 서보 전원의 불안정성이 일반적인 원인입니다. R / C 서보는 모터를 처음 작동시킬 때 약간의 스파이크를 유발할 수 있습니다.

몇 년 전, Tower Hobbies Royal Titan Standard 서보로 555와 1 트랜지스터 인버터로 제어했습니다. Dead-simple 제어 회로. 서보 모터가 연속 운동을하는 동안 5V 전원에서 250mA를 소비한다는 것을 알았습니다. 윙윙 거리는 소리는 반암 스파이크를 쉽게 그렸습니다. (아마도 : 전류 감지 션트의 범위를 정하지 않고 벤치 공급 장치에서 전류 미터를 모니터링하고 있었을 것입니다.)

그것을 길들이기 위해 서보를 가로 질러 220 uF가 직접 걸렸습니다.

서보에 전기적으로 가깝게 서보의 전원 공급 장치를 통해 전해 커패시터 (100 uF 이상)를 직접 사용해보십시오.

이러한 실험을 바탕으로 커패시터를 추가하지 않고는 R / C 서보를 사용하여 모든 것을 고려하지 않았습니다. 여기에는 무선 제어 모델이 포함됩니다.

이는 서보 내부의 서보 포트에 먼지가있을 때도 발생할 수 있습니다. 먼저 커패시터를 사용해보십시오.


6

윙윙 거리거나 흔들리는 것은 서보의 한계 (0도 또는 180도)에 있거나 근접한 경우에만 발생합니까? 그렇다면 간단한 해결책이있을 수 있습니다. 저렴한 서보는 움직임의 한계에 잘 머무르는 방법을 모른다는 것을 알았습니다. 그러나 범위를 10 ~ 170 도로 제한하면 문제가 해결됩니다.

그것이 충분하지 않으면 더 나은 전력, 더 나은 서보 센서 등과 같은 다른 답변에서 언급 된보다 복잡한 수정 사항을 따를 수 있습니다.


예, SG90의 경우이 값은 18 ~ 162입니다. 실제로 32도에 도달 할 수 없었습니다.
Maxim Kachurovskiy

5

이동 후 "서보 스위치 끄기"로 문제를 해결했습니다. 예:

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

PIN서보에 연결된 PWM 핀입니다. 입력 모드로 전환하여 진동을 차단할 수있었습니다. 이것은 최적의 솔루션이 아니며 다른 솔루션을 먼저 사용해 보는 것이 좋습니다.


다른 솔루션을 시도했지만 이것이 작동하는 유일한 방법입니다 (+1). 다른 모든 것이 실패했을 때 좋은 생각!
Snappawapa

3

MG90S 서보 (지 터링)와 동일한 문제가 있었으며, 신호 라인이 비교적 길어서 (60 ~ 70cm) 신호 위에 103 (10nF) 커패시터를 배치하고 접지 라인으로 인해 문제가 해결되었습니다 (커패시터를 어딘가에 배치했습니다) 원래 서보 케이블이 내 내부 케이블에 연결되는 지점).

또한 표준 서보 라이브러리를 사용할 수 없었습니다. Arduino Mega에서 잡는 첫 번째 타이머는 타이머 5이므로 주파수 측정에 필요합니다. 10 개의 서보 만 사용함에 따라 서보 라이브러리에서 키 코드를 추출하여 Timer-1을 사용하도록 변경했습니다 (각 타이머는 Mega에서 최대 12 개의 서보를 지원합니다).

독립형 코드는 참조 용으로 아래에 있습니다. 자신의 프로젝트에 코드를 포함하려면 상단 부분 만 사용할 수 있고 하단 부분은 상단 부분을 테스트하는 것입니다 (직렬 포트에서 수신 대기, sX를 제공 할 수 있음) 그리고 sX가 서보를 선택하는 vX 명령, s0은 첫 번째 서보를 선택하고 vX는 서보 위치를 설정하므로 v1500은 s0 명령을 먼저 지정한 것으로 가정하여 서보 0을 중간 위치로 설정합니다.

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

추신. 이것은 실제로 품질이 아니며 해결 방법입니다.


1

다른 사람들 이이 서보 버즈 문제에 대한 다양한 솔루션을 제안했지만이 스레드 및 다른 Arduino 포럼에서는 다음과 같습니다.

  • 자신의 펄스를 생성
  • 5V 전원을 별도로 공급
  • 한계까지 밀지 마십시오 (예 : 0-180 대신 10-170 사용)
  • 에 걸쳐 커패시터를 실행
  • 이동 후 분리

필자의 경우, 9V / 2A 전원 공급 장치를 Arduino 보드에 꽂으면 윙윙 거리는 소리가 멈췄습니다. 그러나 가장 쉬운 방법은 단순히 서보를 천천히 움직이는 것입니다.

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

나에게 이것은 피드백 루프의 오류 또는 잘못된 조정처럼 보입니다. 고급 서보 제어 시스템은 모터 특성 (인덕턴스, 토크, 피크 전류, 극 수), 부하 (관성 모멘트) 및 순간 조건 (위치, rpm, 역기전력, 전류)에 대한 지식이 있습니다. 이 정보를 사용하여 모터 제어 프로그램은 컨트롤러로부터 주어진 입력 (즉, 전류 / 전압 입력)에 응답하여 서보가 무엇을하는지 예측할 수 있으며,이를 바탕으로 원하는 출력을 달성하기위한 최적의 입력을 생성 할 수 있습니다.

당신이 상상할 수 있듯이, 이것은 다소 복잡한 일이지만 서보 피드백에 대한 인터넷 검색은 당신을 시작할 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.