Arduino, 서보모터

서보모터 SG-90#

  • 모터드라이버, 회전센서, 모터, 제어회로가 내장된 기어 박스를 포함하고 있는 형태의 모터로, 스텝모터보다 힘이 강함
  • 보통 선이 3가닥이며, 회전수와 각도 등의 움직임 제어가 가능
  • 아두이노에서 많이 다루는 SG90 제품의 경우 0~180˚ 제어가 가능
  • 아두이노UNO의 USB 전류량이 500mA 정도가 한계이므로 별도의 전원을 사용하지 않으면 1~2개의 서보모터만 사용 가능

라이브러리 추가하기#

서보모터를 사용하기 위한 Servo 라이브러리는 아두이노 IDE에 기본으로 포함되어 있다.

  • 스케치> 라이브러리 포함하기> Servo 를 선택하면,
  • #include <servo.h> 라이브러리가 스케치에 추가 된다.

schematic#

SG90BrownRedOrange
ArduinoGND5V10

sketch : 서보모터 180º 회전 왕복하기#

#include <Servo.h>

Servo servomotor;                                // servomotor 선언
int position = 0;

void setup() {
  servomotor.attach(10);                         // servomotor 핀 설정
}

void loop() {
  for(position = 0; position < 180; position++)
  {
    servomotor.write(position);                  // pos값의 위치로 이동
    delay(15);                       
  }                                 
  for(position = 180; position > 0; position--)  // 같은 방법으로 역회전
  {
    servomotor.write(position);
    delay(15);
  }
}
  • servomotor.write(숫자)를 사용할때, 숫자는 현재 위치에서의 ‘회전각’을 의미하는 것이 아니다!
  • 초기 위치를 0º로 기준삼아, 표시된 숫자의 각도 위치로 이동하라는 의미이다. 예를들어서 servomotor.write(90)와 servomotor.write(30)을 연속으로 실행하면,
    • 90도를 회전한 뒤, 추가로 30도를 회전하여 120도 위치에 있다 → ×
    • 90도의 위치로 회전한 뒤, 역회전하여 (처음위치를 기준으로) 30도의 위치로 이동한다. → ○
  • 단, servo.h 라이브러리를 사용하면, 스케치 내에서 analogWrite를 사용할 수 없다는 단점도 있다.

sketch: 시리얼모니터에서 각도값을 입력받아 회전시키기#

#include <Servo.h>
Servo servomotor;
int a = 0;

void setup() {
  servomotor.attach(10);
  Serial.begin(9600);                  // Serial 통신을 설정
  while(!Serial);
  Serial.println("Servor Mortor");
}

void loop() {
  if(Serial.available()){              // Serial 모니터창에 어떤 값이 들어오면 실행
    a = Serial.parseInt();             // Serial 모니터창에서 받은 값을 a에 넘겨줌
    if(a>=0 && a<=180)
    {
      Serial.print("angle : ");
      Serial.println(a);
      servomotor.write(a);             // a값에 해당하는 각도의 위치로 이동
      delay(15);
    }
  }
}


서보모터 SG-90: 라이브러리 없이 사용하기#

for SG90 with Arduino#

  • Frequency of that signal should be 50hz.
  • Its range is 544-2450 micro seconds for 0-180 degree angle.
  • Again its a little complex.
  • For example 0 degree signal will be something like 5 volts for 544 micro seconds and 0 volt for 19465.
  • highDelay
    • the time in which we want to keep voltage high on pin 10 and this unit is in micro seconds.
  • lowDelay
    • the time in which want to keep voltage zero across pin 10 again this unit is going to be in micro seconds
  • only HIGH part of signal wont operate servo. we have to send complete HIGH and LOW signal to make servo work.
  • deg_factor
    • signal range (2450 - 544) for SG90 divided by 180 which is equal to 10.6 something that we make 11 as an integer.
  • Loop = 20000;
    • 20000 micro seconds or 20 milli seconds is signal cycle or u can say total signal should be equal to 20 milli seconds including HIGH and LOW input. (50Hz)
int servoPin = 10;        // servo is connected to pin 10.
int initialDelay = 544;
int highDelay, lowDelay;

int deg;
int deg_factor = 10;      
int Loop = 20000;         

void setup() {
  pinMode (servoPin, OUTPUT);
}

void loop() {
  for (deg = 0;deg <= 180; deg++) {
    servoWrite(servoPin, deg);
  }
  for (deg = 180;deg >= 0; deg--) {
    servoWrite(servoPin, deg);
  }
}

void servoWrite(int servo, int duty)
{
  highDelay = initialDelay + (duty * deg_factor); // setting angle

  digitalWrite(servo, HIGH);
  delayMicroseconds(highDelay);
  
  lowDelay = Loop - highDelay;
  digitalWrite(servo, LOW);
  delayMicroseconds(lowDelay);

  delay(500);
}

시리얼 모니터에 입력한 문자로 서보모터 회전시키기#

int servoPin = 10;        // servo is connected to pin 10.
int initialDelay = 544;
int highDelay, lowDelay;

int deg = 90;             // servo initial degree
int deg_factor = 10;      
int Loop = 20000;         

void setup() {
  Serial.begin(9600);
  pinMode (servoPin, OUTPUT);
}

void loop() {
  servoWrite(servoPin, deg);  
    
  while(Serial.available() > 0) 
  {
    char flag=Serial.read();
    delay(2);
      
    if(flag=='l') {
        deg = deg - 5;
        if(deg < 0) {
            deg = 0;
        }
    }
    
    if(flag=='r') {
        deg = deg + 5;
        if(deg > 180) {
            deg = 180;
        }
    }      
}

void servoWrite(int servo, int duty)
{
  highDelay = initialDelay + (duty * deg_factor); // setting angle

  digitalWrite(servo, HIGH);
  delayMicroseconds(highDelay);
  
  lowDelay = Loop - highDelay;
  digitalWrite(servo, LOW);
  delayMicroseconds(lowDelay);

  delay(500);
}
  • l 을 입력하면, 5도 좌회전하며, ll을 입력하면 10도 좌회전한다.
  • r 을 입력하면, 5도 우회전하며, rr을 입력하면 10도 우회전한다.


서보모터와 인터럽트 동시에 사용하기#

서보모터가 180도 회전하는 동안, 스위치를 누르면 13번 LED가 토글되도록 만들어보자.


schematic#


sketch#

int servoPin = 10;        // servo is connected to pin 10.
int initialDelay = 544;   
int highDelay, lowDelay;  

int deg;
int deg_factor = 10;
int Loop = 20000;

#define swPin 2
#define ledPin 13
#define debounceTime 500 // Set debounce Time (unit ms) 버튼을 누른 후 0.5sec 이내에는 버튼 체크하지 않음

boolean state = false;

void setup() {
  pinMode(servoPin, OUTPUT);
  pinMode(swPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(swPin), swInterrupt, FALLING);
}

void loop() {
  for (deg = 0;deg <= 180; deg++) {
    servoWrite(servoPin, deg);
  }
  for (deg = 180;deg >= 0; deg--) {
    servoWrite(servoPin, deg);
  }
}

void servoWrite(int servo, int duty)
{
  highDelay = initialDelay + (duty * deg_factor); // setting angle

  digitalWrite(servo, HIGH);
  delayMicroseconds(highDelay);
  
  lowDelay = Loop - highDelay;
  digitalWrite(servo, LOW);
  delayMicroseconds(lowDelay);

  delay (15);
}

void swInterrupt() {
  static unsigned long lastTime = 0;
  unsigned long now = millis();
  
  if((now-lastTime) > debounceTime) {
    state=!state;
  }
  lastTime = now;
  
  digitalWrite(ledPin, state);
}
  • 스위치를 눌렀다 뗄 때, 물리적 요인에 의해 다시 스위치가 눌리는 효과가 나타나기도 함 (chattering)
  • Chattering 효과를 방지하기 위해, 하드웨어적으로 콘덴서를 붙이거나, 소프트웨어적으로 debounce 시간을 만들어 해결해야 한다.


레이저 모듈 추가: 서보모터 날개에 부착#

schematic#


sketch#

13번 핀에 레이저모듈을 연결하므로 위 스케치를 그대로 이용



조도센서 추가#

반대편에 조도센서를 놓고, 레이저 모듈이 움직이는 동안 버튼을 눌러 조도센서를 정확하게 맞추면 점수가 올라가는 게임을 만들어보자.



Keypad에서 값을 입력받아 회전시키기#

schematic#


sketch#

#include <Servo.h>
#include <Keypad.h>

Servo servomotor;
int pos = 0;

const byte ROWS = 4;                          // 키패드 배열 선언
const byte COLS = 4;

char keys[ROWS][COLS] = {                     // 키패드 정의
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'},
};

byte rowPins[ROWS] = {9, 8, 7, 6};            // 키패드 연결 핀 설정
byte colPins[COLS] = {5, 4, 3, 2};

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup() {
  servomotor.attach(10);
}

void loop() {
  char keyValue = keypad.getKey();
  
  if(keyValue) {
     switch(keyValue) {
      case '0' :                    // 시리얼 모니터에 0을 입력하면
        servomotor.write(0);       // 0도 위치로 이동
        delay(15);
        break;
      case '1' :                    // 시리얼 모니터에 1을 입력하면
        servomotor.write(15);       // 15도 위치로 이동
        delay(15);
        break;
      case '2' :
        servomotor.write(30);       // 30도 위치로 이동
        delay(15);
        break;
      case '3' :
        servomotor.write(45);       // 45도 위치로 이동
        delay(15);
        break;
      case '4' :
        servomotor.write(60);       // 60도 위치로 이동
        delay(15);
        break;
      case '5' :
        servomotor.write(75);       // 75도 위치로 이동
        delay(15);
        break;
      case '6' :
        servomotor.write(90);       // 90도 위치로 이동
        delay(15);
        break;
      case '7' :
        servomotor.write(120);       // 120도 위치로 이동
        delay(15);
        break;
      case '8' :
        servomotor.write(150);       // 150도 위치로 이동
        delay(15);
        break;
      case '9' :
        servomotor.write(180);       // 180도 위치로 이동
        delay(15);
        break;
      default :
        break;
     }
  }
  delay(10);
}


비밀번호 확인하여 서보모터 회전시키기#

서보모터의 날개를 금고의 개폐장치로 사용하여, 비밀번호 금고를 만들어 보자.


sketch#

#include <Keypad.h>
#include <Servo.h>

int tru=0;                        // 비밀번호가 맞는지 확인
int count=0;                      // 4자리수 카운트
char PW[4]={'1','2','3','A'};     //비밀번호

Servo servomotor;

// 키패드 설정 시작
const byte ROWS = 4;
const byte COLS = 4;

byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};

char keys[ROWS][COLS] = {
  {'1','2','3', 'A'},
  {'4','5','6', 'B'},
  {'7','8','9', 'C'},
  {'*','0','#', 'D'}
};

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
// 키패드 설정 끝

void setup() {
  Serial.begin(9600);
  servomotor.attach(10);          // 서보모터 핀 설정
}

void loop() {
  char key = keypad.getKey();     // 키패드 입력 변수

  if (key) {
    Serial.println(key);
    if(key==PW[count]) {          // 입력번호와 비밀번호가 맞으면
        count++;                  // 자리수 카운트 1증가
        tru++;                    // 맞은수 값 1증가
     }
     else if(key!=PW[count]) {    // 입력번호와 비밀번호가 틀리면
        count++;                  // 자리수 카운트 값만 1증가
     }

     if(key=='#')                 // #을 누르면
        re();                     // 초기화
     if(count==4) {               // 4자리 입력받은 값이
        if(tru==4)                // 모두 맞으면
          Su();                   // 성공
        else                      // 그렇지 않으면(4자리는 입력했는데, 모두 맞지는 않았으면)
          Fa();                   // 실패
        tru=0;                    // tru, count값 초기화
        count=0;
      }
   }
}

void Su() {                          // 성공했을 때 실행
  servomotor.write(90);              // 90도 돌려서 도어락 열림
  Serial.println("open the door");
}

void Fa() {                        // 실패한 경우는 도어락 잠금 유지
  servomotor.write(0);
  Serial.println("close the door");
}

void re() {                        // 비밀번호 입력중 #을 입력하면 초기화하고 도어락 잠금 유지
  tru=0;
  count=0;
  servomotor.write(0);
  Serial.println("password reset");
}


Joystick을 사용하여 2개의 서보모터 제어하기#

schematic#


sketch#

#define JSTICKPIN A0      // joystick is connected to pin 11

int servoPin = 10;        // servo is connected to pin 10
int initialDelay = 544;
int highDelay, lowDelay;

int deg, Jstick, duty;
int deg_factor = 10;      
int Loop = 20000;

void setup() {
  pinMode (servoPin, OUTPUT);
}

void loop() {
  PrintValue();
    
  Jstick = analogRead(JSTICKPIN);          
  deg = map(Jstick, 0, 1023, 0, 180);     
  servoWrite(servoPin, deg);
}

void servoWrite(int servo, int duty)
{
  highDelay = initialDelay + (duty * deg_factor); // setting angle

  digitalWrite(servo, HIGH);
  delayMicroseconds(highDelay);

  lowDelay = Loop - highDelay;
  digitalWrite(servo, LOW);
  delayMicroseconds(lowDelay);

  delay (15);
}

void PrintValue() {
    Serial.print(analogRead(JSTICKPIN));
    Serial.println (" Joystick Value");
}