서보모터는 로봇 공학, 자동화 시스템, 원격 제어(RC) 모델 등 정밀한 위치 제어가 요구되는 다양한 분야에서 핵심적인 역할을 수행하는 액추에이터입니다. 아두이노와 같은 마이크로컨트롤러를 사용하여 서보모터를 제어하기 위해서는 먼저 그 내부 구조와 작동 원리에 대한 깊이 있는 이해가 선행되어야 합니다. 서보모터는 단순한 모터가 아니라, 목표 위치를 스스로 찾아가고 유지하는 지능적인 폐쇄 루프 제어 시스템입니다.1
일반적인 취미용 서보모터의 플라스틱 케이스 내부에는 정밀한 움직임을 구현하기 위한 네 가지 핵심 부품이 유기적으로 결합되어 있습니다.2 이 구성 요소들이 함께 작동하여 외부의 제어 신호에 따라 정확한 각도를 유지하는 폐쇄 루프(Closed-Loop) 시스템을 형성합니다.
이처럼 서보모터는 단순히 회전하는 장치가 아니라, 목표를 설정하고(제어 신호), 현재 상태를 파악하며(피드백), 오차를 수정하는(제어) 일련의 과정을 자체적으로 수행하는 정교한 시스템입니다. 아두이노의 역할은 이 시스템에 ‘어디로 가야 하는지’에 대한 목표값, 즉 제어 신호를 제공하는 것입니다.
서보모터를 제어하기 위해 사용되는 신호는 펄스 폭 변조(Pulse Width Modulation, PWM)라는 디지털 신호 기법입니다.5 PWM은 디지털 출력만을 할 수 있는 마이크로컨트롤러가 아날로그와 유사한 효과를 내기 위해 사용하는 핵심 기술로, 서보모터 제어뿐만 아니라 LED의 밝기 조절이나 DC 모터의 속도 제어 등 광범위하게 활용됩니다.7
PWM 신호의 핵심 개념은 다음과 같습니다:
주기(Period)와 주파수(Frequency): PWM 신호는 일정한 주기를 가지고 반복되는 펄스(Pulse)의 연속입니다. 주기는 하나의 펄스가 시작해서 다음 펄스가 시작하기까지 걸리는 시간이며, 주파수는 1초 동안 이 주기가 몇 번 반복되는지를 나타냅니다 (Frequency=1/Period).7
듀티 사이클(Duty Cycle): 한 주기 내에서 신호가 HIGH 상태(예: 5V)를 유지하는 시간의 비율을 의미합니다. 예를 들어, 듀티 사이클이 25%라면 신호는 주기 시간의 25% 동안 HIGH 상태를, 나머지 75% 동안은 LOW 상태(0V)를 유지합니다.7 듀티 사이클을 조절함으로써 신호의 평균 전압을
0V와 5V 사이에서 가변적으로 제어할 수 있습니다.
아두이노 우노 보드의 경우, 디지털 핀 번호 옆에 물결표시(~)가 있는 3, 5, 6, 9, 10, 11번 핀이 하드웨어 PWM 출력을 지원합니다.5
analogWrite(pin, value) 함수를 사용하면 이 핀들에서 PWM 신호를 출력할 수 있으며, value 값(0-255)에 따라 듀티 사이클이 0%에서 100%까지 조절됩니다. 이 핀들의 PWM 주파수는 약 490Hz 또는 980Hz로 고정되어 있습니다.7 하지만 여기서 중요한 점은, 서보모터 제어에 사용되는 PWM 신호는 이러한 일반적인 PWM과 근본적인 차이가 있다는 것입니다.
초보자들이 흔히 혼동하는 부분은 아두이노의 analogWrite() 함수로 서보모터를 직접 제어할 수 있다고 생각하는 것입니다. 그러나 취미용 서보모터는 신호의 듀티 사이클이 아닌, HIGH 상태 펄스의 절대적인 ‘폭(width)’ 또는 ‘지속 시간(duration)’에 반응합니다.6 서보모터는 약 20ms(밀리초)의 주기, 즉 50Hz의 주파수를 갖는 신호를 기대합니다.4 이 20ms의 주기 안에서 HIGH 신호의 폭을 조절함으로써 모터의 각도를 결정합니다.
이러한 제어 방식은 듀티 사이클이 아닌 펄스 폭 자체가 중요한 통신 프로토콜에 가깝습니다. 예를 들어, 20ms 주기에서 1.5ms의 펄스 폭은 7.5%의 듀티 사이클에 해당하지만, 10ms 주기에서 1.5ms의 펄스 폭은 15%의 듀티 사이클에 해당합니다. 서보모터는 이 두 신호를 동일하게 ‘90도 위치’로 해석합니다. 중요한 것은 듀티 사이클이 아니라 1.5ms라는 절대적인 펄스 폭입니다.6
일반적인 취미용 서보모터의 펄스 폭과 각도의 관계는 다음과 같습니다. 이 값들은 제조사나 모델에 따라 약간의 차이가 있을 수 있으므로, 정밀한 제어가 필요할 경우 데이터시트를 확인하는 것이 중요합니다.6
일부 서보모터는 0.5ms(500µs)에서 2.5ms(2500µs)까지 더 넓은 범위의 펄스 폭을 인식하기도 합니다.4 아두이노의
analogWrite() 함수는 이러한 정밀한 시간 제어를 위해 설계되지 않았기 때문에, 서보모터를 제어하기 위해서는 타이머와 인터럽트를 직접 제어하여 정확한 펄스를 생성해야 합니다. 다행히도 아두이노는 이러한 복잡한 과정을 추상화한 Servo.h 라이브러리를 기본으로 제공하여 사용자가 쉽게 서보모터를 제어할 수 있도록 돕습니다.
| 각도 (도) | 일반적인 펄스 폭 (ms) | 일반적인 펄스 폭 (µs) |
|---|---|---|
| 0 | 1.0 | 1000 |
| 45 | 1.25 | 1250 |
| 90 (중립) | 1.5 | 1500 |
| 135 | 1.75 | 1750 |
| 180 | 2.0 | 2000 |
표 1: 서보모터 펄스 타이밍 참조. 이 값은 일반적인 기준이며, 특정 서보모터의 데이터시트를 확인하여 정확한 값을 사용하는 것이 권장됩니다.
이론적 배경을 이해했다면, 이제 아두이노와 서보모터를 물리적으로 연결하고 첫 번째 제어 코드를 실행해 볼 차례입니다. 이 과정은 매우 간단하며, 아두이노의 강력한 Servo.h 라이브러리 덕분에 몇 줄의 코드만으로도 서보모터를 움직이게 할 수 있습니다.
가장 먼저 필요한 준비물은 아두이노 보드(우노, 나노 등), 취미용 서보모터(SG90, MG90S 등), 그리고 점퍼선 3개입니다.15
서보모터는 일반적으로 3개의 선을 가지고 있으며, 색상을 통해 쉽게 구분할 수 있습니다. 제조사마다 약간의 차이가 있을 수 있지만, 대부분 다음의 표준을 따릅니다 11:
이 세 가닥의 선을 아두이노 보드에 다음과 같이 연결합니다. 아래는 가장 일반적인 연결 방식이며, 하나의 소형 서보모터를 테스트하는 데 적합합니다.2
중요한 주의사항: 이 연결 방식은 하나의 소형 서보모터를 부하 없이 간단히 테스트할 때만 유효합니다. 서보모터는 움직일 때 상당한 전류를 소모하므로, 두 개 이상의 서보모터를 사용하거나 큰 부하가 걸리는 프로젝트에서는 아두이노의 5V 핀에서 직접 전원을 공급하면 보드가 손상되거나 오작동할 수 있습니다. 이러한 경우 반드시 외부 전원을 사용해야 하며, 이 내용은 섹션 4에서 자세히 다룹니다.2
앞서 설명했듯이, 서보모터를 제어하기 위한 50Hz의 정밀한 PWM 신호를 생성하는 것은 마이크로컨트롤러의 타이머와 인터럽트를 직접 다루어야 하는 복잡한 작업입니다. 아두이노는 이러한 복잡성을 해결하기 위해 Servo.h라는 강력한 라이브러리를 기본적으로 제공합니다.2
이 라이브러리는 아두이노 IDE에 내장되어 있어 별도의 설치 과정 없이 #include <Servo.h> 한 줄만 코드 상단에 추가하면 바로 사용할 수 있습니다.3
Servo.h 라이브러리는 내부적으로 타이머를 사용하여 필요한 PWM 신호를 생성해주므로, 사용자는 단순히 ‘몇 도로 움직여라’와 같은 직관적인 명령만 내리면 됩니다.
아두이노 서보모터 제어의 “Hello, World!”와 같은 예제가 바로 “Sweep” 스케치입니다. 이 예제는 서보모터의 축을 0도에서 180도까지 부드럽게 왕복 운동시킵니다. 아두이노 IDE의 파일 > 예제 > Servo > Sweep 경로에서 찾을 수 있습니다.3
다음은 Sweep 스케치의 전체 코드와 각 라인에 대한 상세한 설명입니다.4
#include <Servo.h> // 1. Servo 라이브러리를 코드에 포함시킵니다.
Servo myservo; // 2. 'Servo' 타입의 객체(object) 'myservo'를 생성합니다.
int pos = 0; // 3. 서보모터의 각도를 저장할 정수형 변수 'pos'를 선언하고 0으로 초기화합니다.
void setup() {
myservo.attach(9); // 4. 'myservo' 객체를 디지털 9번 핀에 연결합니다.
}
void loop() {
// 5. 0도에서 180도까지 1도씩 증가하며 이동
for (pos = 0; pos <= 180; pos += 1) {
myservo.write(pos); // 6. 'pos' 변수의 현재 값(각도)으로 서보모터를 이동시킵니다.
delay(15); // 7. 서보모터가 해당 위치로 이동할 시간을 15ms만큼 기다립니다.
}
// 8. 180도에서 0도까지 1도씩 감소하며 이동
for (pos = 180; pos >= 0; pos -= 1) {
myservo.write(pos); // 9. 'pos' 변수의 현재 값(각도)으로 서보모터를 이동시킵니다.
delay(15); // 10. 서보모터가 해당 위치로 이동할 시간을 15ms만큼 기다립니다.
}
}
코드 분석:
#include <Servo.h>: 서보모터 제어에 필요한 모든 기능이 포함된 Servo 라이브러리를 사용하겠다고 선언합니다.
Servo myservo;: Servo라는 클래스(설계도)를 바탕으로 myservo라는 이름의 실제 인스턴스(객체)를 만듭니다. 앞으로 myservo라는 이름을 통해 서보모터를 제어하게 됩니다.
int pos = 0;: 서보모터의 목표 각도를 저장하기 위한 변수입니다.
myservo.attach(9);: setup() 함수 내에서 실행되며, 우리가 생성한 myservo 객체를 물리적인 디지털 9번 핀에 연결(attach)하는 역할을 합니다. 이 명령이 실행된 후부터 9번 핀에서는 서보 제어 신호가 출력되기 시작합니다.
for (pos = 0; pos <= 180; pos += 1): pos 변수를 0부터 180까지 1씩 증가시키는 반복문입니다.
myservo.write(pos);: Servo 라이브러리의 핵심 함수 중 하나로, 괄호 안의 숫자(각도)에 해당하는 PWM 펄스를 생성하여 9번 핀으로 출력합니다.
delay(15);: 이 코드는 매우 중요합니다. myservo.write() 명령은 전기 신호를 보내는 즉시 완료되지만, 모터가 물리적으로 해당 각도까지 회전하는 데는 시간이 걸립니다. 이 delay()는 모터에게 움직일 시간을 주는 역할을 하며, 이 값(15ms)을 조절하여 스윕 속도를 변경할 수 있습니다.4 만약 이 지연이 없다면,
for 루프가 너무 빨리 실행되어 모터가 미처 따라오지 못하고 제대로 움직이지 않거나 떨림 현상이 발생할 수 있습니다.
for (pos = 180; pos >= 0; pos -= 1): pos 변수를 180부터 0까지 1씩 감소시키는 두 번째 반복문입니다.
myservo.write(pos);: 다시 write 함수를 사용하여 반대 방향으로 모터를 움직입니다.
delay(15);: 마찬가지로 반대 방향으로 움직일 시간을 부여합니다.
이 Sweep 스케치는 서보 제어의 기본을 보여주는 훌륭한 예제이지만, delay() 함수의 사용은 한계점을 가집니다. delay()가 실행되는 동안 아두이노는 다른 어떤 작업(예: 센서 값 읽기, 버튼 입력 확인)도 할 수 없는 ‘멈춤’ 상태가 됩니다. 따라서 더 복잡하고 반응성이 중요한 프로젝트에서는 delay()를 사용하지 않는 비차단(non-blocking) 방식의 프로그래밍 기법이 필요합니다.
아두이노의 Servo.h 라이브러리는 서보모터 제어를 매우 간단하게 만들어주지만, 그 내부에는 단순한 각도 제어를 넘어선 강력하고 정밀한 기능들이 포함되어 있습니다. 라이브러리의 주요 함수들을 깊이 있게 이해하면, 다양한 종류의 서보모터를 더 효과적으로 사용하고, 전력 소모를 관리하며, 발생할 수 있는 문제들을 미리 예방할 수 있습니다.
attach() 함수는 소프트웨어적으로 생성한 서보 객체를 아두이노의 특정 디지털 핀에 물리적으로 연결하는 첫 단계입니다.
기본 문법: myservo.attach(pin);
pin: 서보모터의 신호선이 연결된 아두이노의 디지털 핀 번호입니다. 이 함수가 호출되면, 지정된 핀에서 서보 제어를 위한 PWM 신호 생성이 시작됩니다.5고급 문법: myservo.attach(pin, min, max);
pin: 연결할 디지털 핀 번호입니다.
min: 서보모터의 0도에 해당하는 펄스 폭을 마이크로초(µs) 단위로 지정합니다. 기본값은 544µs입니다.
max: 서보모터의 180도에 해당하는 펄스 폭을 마이크로초(µs) 단위로 지정합니다. 기본값은 2400µs입니다.
이 고급 문법은 매우 유용합니다. 모든 서보모터가 표준적인 1000µs(0도) ~ 2000µs(180도) 범위에서 동작하지는 않습니다. 특정 서보모터가 0도에서 180도까지 완전히 회전하지 않거나, 지정된 각도를 벗어나는 경우, 이 min과 max 값을 조절하여 서보모터의 동작 범위를 정밀하게 보정(calibrate)할 수 있습니다.21 예를 들어, 데이터시트에 0도가 600µs, 180도가 2400µs로 명시된 서보모터가 있다면,
myservo.attach(9, 600, 2400);과 같이 사용하여 write() 함수가 이 범위를 기준으로 각도를 계산하도록 만들 수 있습니다.
PWM 핀에 대한 고찰: 많은 튜토리얼에서 서보모터의 신호선을 물결표시(~)가 있는 PWM 핀에 연결하라고 권장하지만 5, 이는 반드시 지켜야 할 규칙은 아닙니다.
Servo.h 라이브러리는 아두이노의 analogWrite() 함수가 사용하는 하드웨어 PWM 기능과는 별개로, 내부 타이머 인터럽트를 사용하여 소프트웨어적으로 PWM 신호를 생성합니다. 따라서 이론적으로는 아두이노의 어떤 디지털 핀이든 서보모터 제어에 사용할 수 있습니다.21 다만, 라이브러리가 특정 하드웨어 타이머를 점유하기 때문에 충돌이 발생할 수 있습니다. 예를 들어, 아두이노 우노에서 Servo.h 라이브러리를 사용하면 Timer1이 점유되어, 동일한 타이머를 사용하는 핀 9와 10의 analogWrite() 기능이 비활성화됩니다.22
서보모터를 특정 위치로 이동시키는 명령은 두 가지 함수를 통해 내릴 수 있습니다.
write(angle):
angle에 0부터 180까지의 정수 값을 입력하면, 라이브러리가 내부적으로 이 각도에 해당하는 펄스 폭(기본적으로 544µs ~ 2400µs 사이의 값)으로 변환하여 신호를 출력합니다.5writeMicroseconds(us):
myservo.writeMicroseconds(1500);은 서보모터를 정확히 1500µs 펄스에 해당하는 중앙 위치로 이동시킵니다.attach()에서 설정한 min, max 범위를 벗어나는 특수한 펄스 폭을 보내야 할 때 유용합니다.writeMicroseconds()를 사용하여 속도를 정밀하게 제어합니다. 일반적으로 1500µs는 정지, 1000µs는 한 방향으로 최대 속도, 2000µs는 반대 방향으로 최대 속도를 의미합니다.24read() 함수는 이름 때문에 초보자들에게 가장 큰 혼란을 주는 함수 중 하나입니다.
read()가 하지 않는 것: 이 함수는 서보모터의 현재 물리적인 각도를 읽어오는 기능이 아닙니다. 일반적인 취미용 서보모터는 아두이노로 현재 위치를 다시 보내주는 피드백 라인이 존재하지 않습니다.25 따라서
read()를 호출한다고 해서 서보모터의 실제 상태를 알 수는 없습니다.
read()가 실제로 하는 것: 이 함수는 단순히 가장 마지막에 write() 함수를 통해 전달했던 각도 값을 반환합니다. 즉, 아두이노의 메모리에 저장된 서보 객체의 목표 위치 변수 값을 읽어오는 것입니다.19 예를 들어,
myservo.write(90);을 실행한 직후 myservo.read();를 호출하면, 서보모터가 실제로 90도에 도달했는지 여부와 관계없이 90이라는 값이 반환됩니다.
실용적인 사용법: read() 함수의 용도는 제한적이지만, 현재 서보의 목표 위치를 별도의 전역 변수에 저장하지 않고도 코드의 다른 부분에서 참조해야 할 때 유용하게 사용할 수 있습니다.
detach() 함수는 attach()와 반대되는 개념으로, 지정된 핀에 PWM 신호 전송을 중단시킵니다.
문법: myservo.detach();
기능과 효과: 이 함수가 호출되면 서보모터는 더 이상 제어 신호를 받지 않으므로, 현재 위치를 능동적으로 유지하려는 토크를 발생시키지 않습니다. 이 상태에서는 외부 힘에 의해 축이 비교적 쉽게 돌아갈 수 있습니다.30
주요 사용 목적:
전력 소모 감소: 서보모터는 움직이지 않고 위치를 유지하고 있을 때도 계속해서 미세 조정을 위해 전류를 소모합니다(Idle Current).18 배터리로 작동하는 프로젝트에서 특정 시간 동안 서보를 움직일 필요가 없을 때
detach()를 호출하면 불필요한 전력 소모를 크게 줄일 수 있습니다.
다중 서보 제어: 아두이노의 5V 핀과 같이 제한된 전원으로 여러 개의 서보모터를 제어할 때 매우 중요한 기술입니다. 모든 서보를 동시에 attach() 상태로 두면 총 전류 소모량이 아두이노의 허용치를 초과하여 시스템이 불안정해질 수 있습니다. 이 때, 움직여야 할 서보만 순차적으로 attach()하여 움직인 뒤, 바로 detach()하는 방식을 사용하면 순간적인 최대 전류 소모량을 관리하여 시스템을 안정적으로 유지할 수 있습니다.30
| 함수 | 문법 | 파라미터 | 반환값 | 목적 및 주요 고려사항 |
|---|---|---|---|---|
attach() |
servo.attach(pin); servo.attach(pin, min, max); |
pin: 디지털 핀 번호 min: 0도 펄스 폭 (µs, 선택사항) max: 180도 펄스 폭 (µs, 선택사항) |
없음 | 서보 객체를 물리적 핀에 연결하고 PWM 신호 생성을 시작합니다. min, max 파라미터로 서보의 동작 범위를 보정할 수 있습니다. |
write() |
servo.write(angle); |
angle: 목표 각도 (0-180) |
없음 | 서보를 지정된 각도로 이동시킵니다. 가장 일반적이고 직관적인 제어 방식입니다. |
writeMicroseconds() |
servo.writeMicroseconds(us); |
us: 펄스 폭 (µs) |
없음 | 펄스 폭을 직접 지정하여 정밀하게 제어합니다. 비표준 서보나 연속 회전 서보 제어에 필수적입니다. |
read() |
servo.read(); |
없음 | int (0-180) |
주의: 실제 서보 위치가 아닌, 마지막으로 write()에 전달된 각도 값을 반환합니다. |
attached() |
servo.attached(); |
없음 | bool |
서보 객체가 현재 특정 핀에 attach 되어 있는지 여부를 확인합니다. (true/false) |
detach() |
servo.detach(); |
없음 | 없음 | 핀에서 PWM 신호 생성을 중단합니다. 전력 소모를 줄이거나 다중 서보를 안정적으로 제어할 때 사용됩니다. |
표 2: Servo.h 라이브러리 함수 참조표
아두이노 프로젝트, 특히 모터와 같은 구동 부품을 포함하는 프로젝트에서 발생하는 문제의 상당수는 코드의 논리적 오류가 아닌 부적절한 전원 공급에서 비롯됩니다. 서보모터는 정밀한 제어가 가능한 만큼 안정적인 전원 공급이 매우 중요하며, 이를 간과할 경우 예측 불가능한 오작동의 원인이 됩니다.
초보자들이 가장 쉽게 저지르는 실수 중 하나는 서보모터의 전원을 아두이노 보드의 5V 핀에서 직접 공급하는 것입니다. 하나의 소형 서보모터(SG90 등)를 부하 없이 잠시 테스트하는 것은 가능하지만, 이는 매우 제한적인 상황에서만 허용되는 방식입니다.
이러한 이유로, 프로젝트에서 서보모터가 갑자기 멈추거나, 아두이노 보드가 계속해서 재시작되는 등의 이상 현상이 발생한다면, 가장 먼저 의심해야 할 것은 코드가 아니라 전원 문제입니다. 결론적으로, 하나 이상의 서보모터를 사용하거나, 단 하나의 서보라도 부하가 걸리는 작업을 수행하는 모든 프로젝트에서는 반드시 외부 전원 공급 장치를 사용해야 합니다.2
외부 전원을 사용하는 것은 프로젝트의 안정성을 보장하는 가장 중요한 단계입니다. 올바른 외부 전원 연결 방법은 다음과 같습니다.
서로 다른 전원을 사용하는 두 개 이상의 전자 부품을 연결할 때, 모든 부품의 접지(GND)를 하나로 묶어주는 ‘공통 접지’는 회로가 올바르게 동작하기 위한 절대적인 전제 조건입니다.38
전압은 절대적인 값이 아니라 두 지점 간의 상대적인 전위차입니다. 아두이노가 디지털 핀에서 출력하는 5V 신호는 ‘아두이노 자신의 GND를 기준으로 5V 높다’는 의미입니다. 서보모터의 제어 회로는 이 신호를 ‘서보모터 자신의 GND를 기준으로’ 해석합니다.
만약 아두이노의 GND와 서보모터의 GND(외부 전원의 음극)가 연결되어 있지 않다면, 두 시스템은 서로 다른 기준점을 가지게 됩니다. 이 경우 아두이노가 보내는 신호는 서보모터에게 아무런 의미가 없는 노이즈로 인식될 수 있으며, 이는 모터가 전혀 움직이지 않거나 예측 불가능하게 흔들리는 원인이 됩니다.
모든 부품의 GND를 하나로 연결함으로써, 회로 전체에 일관된 0V 기준점이 형성됩니다. 이 공통된 기준점을 바탕으로 아두이노가 보내는 신호 전압이 서보모터에 의해 정확하게 해석될 수 있는 것입니다. 따라서 외부 전원을 사용할 때는 항상 “신호는 아두이노에서, 전원은 외부에서, 접지는 함께”라는 원칙을 기억해야 합니다.
서보모터를 단순히 정해진 순서대로 움직이는 것을 넘어, 외부 입력에 실시간으로 반응하도록 만드는 것은 프로젝트에 생명력을 불어넣는 과정입니다. 가변저항기나 조이스틱과 같은 아날로그 센서를 활용하면 서보모터를 수동으로 정밀하게 제어할 수 있으며, 여러 개의 서보모터를 조화롭게 움직이는 기술은 로봇팔과 같은 복잡한 시스템을 구현하는 기반이 됩니다.
가변저항기(Potentiometer)는 손잡이를 돌리는 각도에 따라 저항값이 변하는 부품으로, 서보모터의 각도를 직관적으로 제어하는 데 널리 사용됩니다.
회로 연결:
코드 로직 및 map() 함수:
analogRead() 함수를 사용하여 아날로그 핀(A0)의 전압을 읽으면, 아두이노는 이를 0에서 1023 사이의 정수 값으로 변환합니다. 반면, 서보모터는 0에서 180 사이의 각도 값을 입력받습니다. 이 두 개의 다른 숫자 범위를 변환해주는 핵심적인 함수가 바로 map() 함수입니다.18
map(value, fromLow, fromHigh, toLow, toHigh)는 value라는 값을 fromLow ~ fromHigh 범위에서 toLow ~ toHigh 범위로 비례하여 변환해줍니다.
#include <Servo.h>
Servo myservo;
int potPin = A0; // 가변저항기가 연결된 아날로그 핀
void setup() {
myservo.attach(9);
}
void loop() {
int potValue = analogRead(potPin); // 가변저항기 값 읽기 (0 ~ 1023)
int angle = map(potValue, 0, 1023, 0, 180); // 0~1023 범위를 0~180 범위로 변환
myservo.write(angle); // 변환된 각도로 서보모터 이동
delay(15);
}
이 코드는 가변저항기를 돌리면 그에 맞춰 서보모터의 각도가 실시간으로 변하게 합니다.18
map() 함수는 이처럼 센서의 입력 범위를 액추에이터의 출력 범위로 변환하는 물리 컴퓨팅 프로젝트의 필수적인 다리 역할을 합니다.
조이스틱 모듈은 두 개의 가변저항기를 X축과 Y축으로 결합한 형태로, 2개의 서보모터를 동시에 제어하여 카메라 팬-틸트 시스템이나 로봇팔의 관절을 제어하는 데 이상적입니다.45
회로 연결:
코드 로직:
코드는 가변저항기 제어와 유사하지만, 두 개의 아날로그 입력을 각각 읽어 두 개의 서보 객체에 개별적으로 write() 명령을 내리는 구조입니다.48
여러 개의 서보모터를 제어하는 것은 단순히 서보의 개수만 늘리는 것이 아니라, 코드 구조와 전원 관리에 대한 전략적인 접근이 필요합니다.
소프트웨어 설정:
사용할 서보모터의 개수만큼 Servo 객체를 생성해야 합니다. 각 객체는 고유한 이름을 가져야 하며, setup() 함수 내에서 각각 다른 디지털 핀에 attach() 되어야 합니다.2
#include <Servo.h>
Servo servo1;
Servo servo2;
void setup() {
servo1.attach(9);
servo2.attach(10);
}
동시 동작과 순차 동작:
write() 명령 뒤에 충분한 delay()를 추가하면 됩니다. 이 방식은 간단하지만 전체 동작 시간이 길어집니다.50loop() 함수 내에서 delay() 함수 없이 각 서보의 목표 각도를 조금씩 변경하고 빠르게 반복하면, 사람의 눈에는 여러 서보가 동시에 부드럽게 움직이는 것처럼 보이게 할 수 있습니다. 이는 더 복잡하지만 반응성이 뛰어난 로봇 시스템을 만드는 데 필수적인 비차단(non-blocking) 프로그래밍 기법입니다.다중 서보를 위한 전원 관리:
앞서 강조했듯이, 두 개 이상의 서보모터를 사용할 때는 외부 전원 공급이 필수적입니다.2 만약 적절한 외부 전원을 사용할 수 없는 제한적인 상황이라면, 섹션 3.4에서 설명한
attach()와 detach() 함수를 활용하는 기법을 고려할 수 있습니다. 즉, 한 번에 하나의 서보만 attach()하여 움직이고, 움직임이 끝나면 즉시 detach()하여 전력 소모를 최소화한 후 다음 서보를 제어하는 방식입니다.30 이 방법은 서보에 큰 부하가 걸리지 않는 경우에 유용합니다.
아두이노와 서보모터를 이용한 프로젝트를 진행하다 보면 예상치 못한 문제에 부딪히게 됩니다. 가장 흔하게 발생하는 문제인 ‘지터(Jitter)’ 현상부터 전혀 움직이지 않는 상황까지, 다양한 문제의 원인을 진단하고 해결하는 방법을 체계적으로 알아두는 것은 성공적인 프로젝트 완성을 위해 필수적입니다. 물리 컴퓨팅의 문제 해결은 코드, 하드웨어, 전원이라는 세 가지 요소를 통합적으로 고려하는 시스템적 접근이 필요합니다.
‘지터’는 서보모터가 특정 각도에서 가만히 있지 못하고 미세하게 계속 떨거나 ‘드르륵’거리는 소음을 내는 현상을 말합니다. 이는 매우 흔한 문제이며, 원인은 복합적일 수 있습니다.
원인 1: 불안정한 전원 공급 (가장 흔한 원인)
서보모터가 위치를 유지하기 위해 미세한 조정을 할 때 필요한 순간적인 전류를 전원이 안정적으로 공급하지 못하면 지터가 발생합니다. 이는 아두이노 5V 핀에서 전원을 공급할 때 특히 두드러집니다.
원인 2: 신호 노이즈
모터 자체에서 발생하는 전기적 노이즈나 주변의 다른 장치로부터의 간섭이 아두이노에서 오는 PWM 신호 라인에 유입되어 신호를 왜곡시킬 수 있습니다. 제어 회로는 이 미세하게 왜곡된 신호를 새로운 목표 위치로 오인하여 계속해서 수정을 시도하게 되고, 이것이 지터로 나타납니다.
원인 3: 코드 및 입력 값의 불안정성
가변저항기와 같은 아날로그 센서의 값이 미세하게 계속 변동하는 경우, 이 불안정한 입력 값이 map() 함수를 통해 그대로 서보모터의 목표 각도로 전달되어 지터를 유발할 수 있습니다.32
myservo.write()를 호출하도록 프로그래밍합니다.32detach() 활용): 서보모터가 목표 위치에 도달한 후, myservo.detach() 함수를 호출하여 PWM 신호 전송을 중단시킵니다. 이렇게 하면 서보가 더 이상 능동적으로 위치를 보정하지 않으므로 지터가 원천적으로 사라집니다. 다시 움직여야 할 때 myservo.attach()를 호출하여 제어를 재개합니다. 이 방법은 지터 제거에 매우 효과적입니다.31지터 외에도 다양한 오작동이 발생할 수 있습니다. 다음은 일반적인 문제와 해결을 위한 점검 목록입니다.
attach() 함수에 지정된 핀 번호가 실제 신호선이 연결된 핀 번호와 일치하는지 확인합니다.myservo.attach(pin, min, max) 문법을 사용하여 해당 서보에 맞는 최소/최대 펄스 폭을 직접 지정하여 동작 범위를 보정합니다.| 증상 | 가장 가능성 높은 원인 | 해결 방안 (우선순위 순) |
|---|---|---|
| 서보가 떨리거나 진동함 (Jitter) | 1. 불안정한 전원 공급 2. 불안정한 센서 입력 3. 신호선 노이즈 | 1. 외부 전원 공급 장치 사용 2. 서보 전원단에 전해 커패시터 추가 3. 코드에 입력값 필터링 로직 추가 (임계값 설정) 4. 이동 후 detach() 함수 호출 |
| 서보가 전혀 움직이지 않음 | 1. 배선 오류 (VCC, GND, Signal) 2. 공통 접지 누락 3. 코드와 실제 핀 불일치 | 1. 모든 배선(특히 공통 접지)을 재확인 2. 외부 전원이 켜져 있는지 확인 3. attach()의 핀 번호 확인 |
| 서보가 움직일 때 아두이노가 재부팅됨 | 아두이노 5V 핀의 전류 부족 (Brownout) | 1. 즉시 외부 전원으로 전환 |
| 서보가 윙 소리를 내며 뜨거워짐 | 스톨(Stall) 상태 (기계적 방해 또는 과부하) | 1. 전원을 차단하고 장애물 제거 또는 부하 감소 2. 더 높은 토크의 서보로 교체 |
| 동작 범위가 180도보다 좁음 | 1. 서보 자체의 기계적 한계 2. 펄스 폭 범위 불일치 | 1. attach(pin, min, max)를 사용하여 펄스 폭 범위 보정 2. (해결 불가 시) 해당 서보의 한계로 인정하고 설계에 반영 |
표 3: 서보모터 문제 해결 매트릭스
지금까지 학습한 서보모터의 원리, 배선, 프로그래밍, 전원 관리 및 문제 해결 기법들을 종합하여 실제 프로젝트를 만들어보는 것은 이론을 체화하는 가장 효과적인 방법입니다. 이 섹션에서는 세 가지 대표적인 프로젝트 예시를 통해 배운 지식을 어떻게 실용적인 창작물로 구현할 수 있는지 단계별로 안내합니다. 각 프로젝트는 준비물, 회로도, 그리고 상세한 주석이 달린 코드를 포함합니다.
2~3개의 서보모터를 사용하여 간단한 로봇팔을 제작하는 것은 다중 서보 제어와 외부 입력 연동을 연습하는 훌륭한 프로젝트입니다. 가변저항기나 조이스틱으로 각 관절을 직접 조작하며 움직임의 원리를 직관적으로 이해할 수 있습니다.49
핵심 학습 목표: 다중 서보 객체 관리, 아날로그 입력(analogRead, map)을 이용한 실시간 제어, 여러 모터 구동을 위한 외부 전원 및 공통 접지의 중요성 체득.
준비물: 아두이노 우노, 서보모터 3개(예: SG90 2개, MG996R 1개), 조이스틱 모듈 1개 또는 가변저항기 3개, 브레드보드, 점퍼선, 서보모터용 외부 전원(예: 5V 2A 어댑터), 로봇팔 프레임(3D 프린터 출력물, 하드보드지, 아이스크림 막대 등으로 제작 가능).
회로 연결:
코드 예제 (조이스틱 2축, 서보 2개 제어):
#include <Servo.h>
Servo baseServo; // 로봇팔의 좌우 회전(base) 서보
Servo armServo; // 로봇팔의 상하 움직임(arm) 서보
int joystickXPin = A0; // 조이스틱 X축
int joystickYPin = A1; // 조이스틱 Y축
void setup() {
baseServo.attach(9);
armServo.attach(10);
}
void loop() {
// 조이스틱 X축 값으로 baseServo 제어
int joystickXVal = analogRead(joystickXPin);
int baseAngle = map(joystickXVal, 0, 1023, 0, 180);
baseServo.write(baseAngle);
// 조이스틱 Y축 값으로 armServo 제어
int joystickYVal = analogRead(joystickYPin);
int armAngle = map(joystickYVal, 0, 1023, 15, 165); // 기계적 간섭을 피해 범위 제한
armServo.write(armAngle);
delay(15); // 안정적인 움직임을 위한 짧은 지연
}
이 프로젝트를 통해 사용자는 여러 개의 액추에이터와 센서를 동시에 관리하는 프로그래밍 구조를 익히고, 외부 전원의 필요성을 직접 경험하게 됩니다.53
서보모터 2개를 직교로 연결하면 카메라나 센서의 방향을 상하(Tilt) 및 좌우(Pan)로 제어할 수 있는 팬-틸트 시스템을 만들 수 있습니다. 이는 감시 카메라, 물체 추적 시스템, 태양광 추적 장치 등의 기본이 되는 메커니즘입니다.58
핵심 학습 목표: 정밀한 각도 제어, 자동 스캔 기능 구현, 수동 제어 모드와 자동 모드 전환 로직 설계.
준비물: 아두이노 우노, 서보모터 2개, 팬-틸트 브라켓, 조이스틱 모듈(수동 제어용), 외부 전원.
회로 연결: 로봇팔 프로젝트와 유사하게, 2개의 서보와 조이스틱을 연결하고 외부 전원과 공통 접지를 구성합니다.
코드 예제 (자동 스캔 기능):
#include <Servo.h>
Servo panServo; // 좌우(Pan) 제어
Servo tiltServo; // 상하(Tilt) 제어
int panAngle = 90;
int tiltAngle = 90;
int panDirection = 1; // 1: 증가, -1: 감소
void setup() {
panServo.attach(9);
tiltServo.attach(10);
panServo.write(panAngle);
tiltServo.write(tiltAngle);
delay(1000); // 초기 위치로 이동 대기
}
void loop() {
// Pan 서보 좌우로 스캔
panAngle += panDirection;
if (panAngle >= 180 || panAngle <= 0) {
panDirection *= -1; // 방향 전환
panServo.write(panAngle);
delay(20); // 스캔 속도 조절
}
이 기본 코드에 조이스틱 입력을 감지하는 로직을 추가하여, 조이스틱이 움직이면 자동 스캔을 멈추고 수동 제어 모드로 전환했다가, 일정 시간 입력이 없으면 다시 자동 스캔 모드로 복귀하는 등 더 지능적인 시스템으로 확장할 수 있습니다.[60, 61]
초음파 센서나 적외선(IR) 센서와 서보모터를 결합하면, 물체가 접근했을 때 자동으로 문이 열리는 시스템을 만들 수 있습니다. 이는 센서 입력에 따른 이벤트 기반 프로그래밍을 연습하는 데 매우 좋은 예제입니다.62
핵심 학습 목표: 디지털/아날로그 센서 데이터 읽기, 조건문(if)을 이용한 이벤트 처리, 시스템 상태(문이 열림/닫힘) 관리.
준비물: 아두이노 우노, 서보모터 1개, 초음파 센서(HC-SR04) 또는 PIR 동작 감지 센서, 외부 전원(권장), 문 역할을 할 작은 구조물.
회로 연결 (초음파 센서 기준):
코드 예제 (초음파 센서 자동문):
#include <Servo.h>
Servo doorServo;
const int trigPin = 12;
const int echoPin = 11;
const int doorOpenAngle = 90;
const int doorClosedAngle = 0;
long duration;
int distance;
void setup() {
doorServo.attach(9);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
doorServo.write(doorClosedAngle); // 시작 시 문을 닫음
}
void loop() {
// 거리 측정
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = duration * 0.034 / 2; // cm 단위로 변환
// 거리가 20cm 미만이면 문을 열고, 아니면 닫음
if (distance < 20) {
doorServo.write(doorOpenAngle);
} else {
doorServo.write(doorClosedAngle);
}
delay(100); // 센서 측정 간격
}
이 프로젝트는 센서 데이터라는 외부 환경 변화에 반응하여 물리적인 동작을 수행하는 상호작용 시스템의 기본 원리를 명확하게 보여줍니다.65
이러한 프로젝트들을 직접 만들어봄으로써, 사용자는 각 부품의 기능과 코드의 역할을 통합적으로 이해하고, 자신만의 아이디어를 현실로 구현할 수 있는 응용력과 자신감을 얻게 될 것입니다.
| Controlling Servos | Onion Omega2 Maker Kit, accessed July 20, 2025, https://docs.onion.io/omega2-maker-kit/maker-kit-servo-controlling-servo.html |
| Servo Motor Basics with Arduino | Arduino Documentation, accessed July 20, 2025, https://docs.arduino.cc/learn/electronics/servo-motors |
| write() - Servo | Reference - Particle docs, accessed July 20, 2025, https://docs.particle.io/reference/device-os/api/servo/write/ |
| Arduino | Powering Multiple Servos and Motors - YouTube, accessed July 20, 2025, https://m.youtube.com/watch?v=M4DLpyEQ_SE&pp=ygUPI3NlcnZvc3RlcHBvd2Vy |
| Arduino Robotic Arm | Arduino Project Hub, accessed July 20, 2025, https://projecthub.arduino.cc/milespeterson101/arduino-robotic-arm-8b8601 |
| Build a Robotic Arm | Science Project, accessed July 20, 2025, https://www.sciencebuddies.org/science-fair-projects/project-ideas/Robotics_p050/robotics/arduino-robotic-arm |
| [40편] 서보 모터로 자동문 만들기 | 아두이노 입문 - YouTube, accessed July 20, 2025, https://www.youtube.com/watch?v=2ZZ_Mtfw818 |
| Automatic Door | Arduino Uno Projects | PIR Sensor | Servo Motor - YouTube, accessed July 20, 2025, https://www.youtube.com/watch?v=BE8ngLA4Y2c |