손잡이를 돌리면 로봇이 고개를 돌린다
지금까지 우리는 센서, 모터, 통신, 제어를 각각 따로 배웠습니다. 이번 장부터는 그 조각들을 하나씩 묶어 실제로 움직이는 작은 프로젝트를 만들어봅니다. 첫 프로젝트는 서보 모터 제어기입니다.
서보 모터 제어기는 구조가 단순합니다. 사용자가 원하는 각도를 입력하면, 제어 보드가 그 값을 읽고, 서보 모터가 해당 각도로 움직입니다. 여기에 조금만 기구물을 붙이면 좌우로 도는 팬 구조가 되고, 서보를 하나 더 추가하면 좌우·상하로 움직이는 팬틸트 구조가 됩니다. 초음파 센서를 올리면 주변을 훑어보는 거리 측정 장치가 되고, 작은 카메라를 올리면 고개를 돌리는 로봇의 눈이 됩니다.
이 장의 목표는 거창한 로봇을 한 번에 만드는 것이 아닙니다. 작은 서보 하나를 안정적으로 움직이고, 입력값을 각도로 바꾸고, 너무 급하지 않게 부드럽게 움직이는 감각을 익히는 것입니다. 로봇 제작에서 중요한 경험은 바로 이런 작은 성공에서 시작됩니다.

준비물
이번 실습은 아두이노 또는 ESP32 중 하나로 진행할 수 있습니다. 두 보드 모두 서보 모터를 제어할 수 있지만, 전원 구성과 핀 선택에서 약간 차이가 있습니다.
기본 실습에 필요한 부품은 다음과 같습니다.
아두이노 Uno/Nano 또는 ESP32 개발 보드
SG90, MG90S 같은 소형 서보 모터 1개 또는 2개
외부 5V 전원, 예를 들어 4×AA 배터리 팩, 5V USB 보조배터리, 또는 5V 벅 컨버터
가변저항 10kΩ 1개 또는 2개
버튼 2개, 필요하다면 풀다운 저항 10kΩ 2개
브레드보드와 점퍼선
팬틸트 브래킷 또는 3D 프린트/아크릴/종이보드로 만든 간단한 지지대
소형 서보 하나만 돌릴 때는 보드의 5V 핀에서 잠깐 동작하는 경우도 있습니다. 하지만 이 책에서는 처음부터 서보 전원은 별도로 공급하는 방식을 권장합니다. 실제 로봇에서는 모터가 순간적으로 큰 전류를 요구하고, 그 순간 보드 전압이 흔들리면 리셋이나 오동작이 생깁니다. 작은 프로젝트에서도 전원 습관을 제대로 들여야 나중에 큰 로봇을 만들 때 덜 고생합니다.
서보 모터의 세 선 이해하기
일반적인 hobby 서보 모터에는 선이 3개 있습니다. 색상은 제조사마다 조금 다르지만 역할은 거의 같습니다.
빨간색 선은 전원 VCC입니다. 보통 소형 서보는 5V 근처에서 사용합니다.
갈색 또는 검은색 선은 GND입니다.
주황색, 노란색, 흰색 계열 선은 신호선입니다.
여기서 꼭 기억해야 할 점은 전원선과 신호선의 역할이 다르다는 것입니다. 서보를 실제로 움직이는 힘은 전원선에서 옵니다. 제어 보드의 신호핀은 "몇 도로 가라"는 명령만 보냅니다. 그러므로 서보 전원은 외부 5V 전원에 연결하고, 신호선만 아두이노나 ESP32의 GPIO 핀에 연결합니다.
다만 외부 전원을 쓴다고 해서 제어 보드와 완전히 따로 놀게 만들면 안 됩니다. 서보가 신호를 제대로 이해하려면 기준점이 같아야 합니다. 그래서 외부 전원의 GND와 제어 보드의 GND를 반드시 연결해야 합니다. 이 책에서 계속 반복해온 공통 GND가 여기서도 핵심입니다.
가장 기본적인 연결은 다음과 같습니다.
외부 5V 전원 + ───── 서보 빨간색 선
외부 5V 전원 - ─┬─── 서보 갈색/검은색 선
└─── 아두이노/ESP32 GND
제어 보드 신호핀 ───── 서보 주황/노란/흰색 선아두이노 예제에서는 신호핀으로 D9를 사용하겠습니다. ESP32 예제에서는 GPIO 18을 사용하겠습니다. ESP32는 핀 기능이 보드마다 조금씩 다를 수 있으므로, 실제 사용하는 개발 보드의 핀맵을 확인하고 PWM 출력에 적합한 핀을 고르면 됩니다.
전원부터 안전하게 구성하기
서보 모터를 처음 연결할 때 가장 흔한 실수는 전원을 너무 가볍게 생각하는 것입니다. 코드가 맞고 핀도 맞는데 서보가 덜덜 떨거나, 보드가 갑자기 리셋되거나, USB 연결이 끊겼다가 다시 잡히는 일이 생깁니다. 대개는 전원 문제입니다.
소형 서보도 멈춰 있는 것처럼 보여도 내부에서는 목표 각도를 유지하려고 계속 힘을 씁니다. 손으로 서보 혼을 잡고 버티면 더 큰 전류가 흐릅니다. 이 상태를 오래 유지하면 서보가 뜨거워지고 전원도 불안정해집니다.
실습할 때는 다음 원칙을 지키는 것이 좋습니다.
서보의 VCC는 보드의 5V 핀보다 외부 5V 전원에 연결합니다.
외부 전원 GND와 보드 GND는 반드시 연결합니다.
서보가 덜덜 떨면 코드보다 전원 용량과 GND 연결을 먼저 확인합니다.
서보 혼을 손으로 억지로 막은 채 오래 두지 않습니다.
서보 전원 근처에 470µF 정도의 전해 커패시터를 추가하면 순간적인 전압 흔들림을 줄이는 데 도움이 됩니다.
커패시터는 작은 물탱크처럼 생각하면 됩니다. 모터가 순간적으로 전류를 많이 당겨갈 때 잠깐 보태주는 역할을 합니다. 필수는 아니지만, 서보가 움직일 때 보드가 리셋된다면 시도해볼 만한 방법입니다.
첫 번째 실습: 정해진 각도로 움직이기
먼저 가장 단순한 코드로 서보가 제대로 움직이는지 확인해보겠습니다. 아두이노에서는 Servo 라이브러리를 사용하면 복잡한 펄스 폭 계산을 직접 하지 않아도 됩니다.
#include <Servo.h>
Servo myServo;
const int servoPin = 9;
void setup() {
myServo.attach(servoPin);
myServo.write(90); // 가운데 위치
delay(1000);
}
void loop() {
myServo.write(0);
delay(1000);
myServo.write(90);
delay(1000);
myServo.write(180);
delay(1000);
myServo.write(90);
delay(1000);
}업로드한 뒤 서보가 0도, 90도, 180도 근처로 반복해서 움직이면 기본 연결은 성공입니다. 이때 실제 서보의 물리적 이동 범위는 제품마다 다릅니다. 어떤 서보는 0도나 180도에서 내부 끝에 걸리며 '지지직' 소리를 낼 수 있습니다. 그런 경우에는 10도에서 170도처럼 범위를 조금 줄여 사용하세요.
서보가 움직이지 않는다면 바로 코드를 고치기 전에 다음을 확인합니다. 빨간 선이 5V에 연결되었는지, 검은색 또는 갈색 선이 GND에 연결되었는지, 신호선이 D9에 연결되었는지, 외부 전원 GND와 보드 GND가 연결되었는지 차례대로 보면 됩니다. 대부분의 문제는 이 네 가지 안에서 발견됩니다.
가변저항으로 목표 각도 정하기
이제 사용자가 직접 각도를 정하게 해보겠습니다. 가장 직관적인 입력 장치는 가변저항입니다. 손잡이를 돌리면 전압이 변하고, 제어 보드는 그 전압을 아날로그 입력으로 읽습니다. 이 값을 0도에서 180도 사이의 각도로 바꾸면 손잡이 방향대로 서보가 따라 움직입니다.
가변저항에는 다리가 3개 있습니다. 양쪽 다리는 5V와 GND에 연결하고, 가운데 다리는 아날로그 입력 핀에 연결합니다. 아두이노에서는 A0 핀을 사용하겠습니다.
가변저항 한쪽 다리 ─── 5V
가변저항 반대쪽 다리 ─── GND
가변저항 가운데 다리 ─── A0아두이노 코드는 다음과 같습니다.
#include <Servo.h>
Servo myServo;
const int servoPin = 9;
const int potPin = A0;
void setup() {
myServo.attach(servoPin);
}
void loop() {
int potValue = analogRead(potPin); // 0~1023
int angle = map(potValue, 0, 1023, 0, 180); // 0~180도로 변환
angle = constrain(angle, 0, 180);
myServo.write(angle);
delay(15);
}analogRead()는 가변저항의 전압을 숫자로 바꿉니다. 아두이노 Uno 기준으로 0V에 가까우면 0, 5V에 가까우면 1023 근처 값이 나옵니다. map()은 이 범위를 서보 각도 범위로 바꿔줍니다.
ESP32를 사용할 때는 가변저항 양쪽을 3.3V와 GND에 연결하는 것이 안전합니다. ESP32의 아날로그 입력은 보통 3.3V 기준으로 사용하기 때문입니다. 서보 전원은 여전히 별도 5V를 사용하되, 가변저항 입력 회로는 ESP32의 3.3V 영역에 둔다고 생각하면 됩니다.
ESP32에서 Arduino IDE를 쓴다면 보통 ESP32Servo 라이브러리를 사용할 수 있습니다. 예시는 다음과 같습니다.
#include <ESP32Servo.h>
Servo myServo;
const int servoPin = 18;
const int potPin = 34; // 입력 전용 ADC 핀으로 사용 가능
void setup() {
myServo.attach(servoPin, 500, 2400);
}
void loop() {
int potValue = analogRead(potPin); // 대략 0~4095
int angle = map(potValue, 0, 4095, 0, 180);
angle = constrain(angle, 0, 180);
myServo.write(angle);
delay(15);
}ESP32의 신호 전압은 3.3V입니다. 많은 hobby 서보는 3.3V 신호도 잘 인식하지만, 제품에 따라 불안정할 수 있습니다. 신호가 이상하게 먹히지 않는다면 레벨 시프터를 사용하거나 3.3V 신호 입력을 잘 받아들이는 서보를 선택하는 것이 좋습니다.
버튼으로 조금씩 움직이기
가변저항은 직관적이지만, 로봇 조종에서는 버튼식 입력도 자주 사용합니다. 예를 들어 왼쪽 버튼을 누르면 각도가 줄고, 오른쪽 버튼을 누르면 각도가 늘어나는 식입니다. 카메라를 조금씩 돌리거나 로봇팔 관절을 미세하게 조정할 때 유용합니다.
다음 코드는 아두이노에서 버튼 2개로 서보 각도를 조절하는 예입니다. 버튼은 내부 풀업을 사용하므로 버튼의 한쪽은 핀에, 다른 한쪽은 GND에 연결합니다. 내부 풀업을 쓰면 버튼을 누르지 않았을 때 HIGH, 눌렀을 때 LOW가 됩니다.
#include <Servo.h>
Servo myServo;
const int servoPin = 9;
const int leftButtonPin = 2;
const int rightButtonPin = 3;
int angle = 90;
void setup() {
myServo.attach(servoPin);
myServo.write(angle);
pinMode(leftButtonPin, INPUT_PULLUP);
pinMode(rightButtonPin, INPUT_PULLUP);
}
void loop() {
bool leftPressed = digitalRead(leftButtonPin) == LOW;
bool rightPressed = digitalRead(rightButtonPin) == LOW;
if (leftPressed) {
angle -= 1;
}
if (rightPressed) {
angle += 1;
}
angle = constrain(angle, 0, 180);
myServo.write(angle);
delay(30);
}이 코드는 매우 단순하지만 실제로 써보면 버튼을 누르는 동안 서보가 계속 움직입니다. delay(30) 때문에 약간 천천히 움직이며, 숫자를 키우면 더 느리게, 줄이면 더 빠르게 움직입니다.
버튼 입력에서는 채터링이라는 현상이 생길 수 있습니다. 채터링은 버튼 접점이 눌리는 순간 아주 짧은 시간 동안 여러 번 튀는 현상입니다. 지금 예제처럼 누르고 있는 동안 계속 움직이는 방식에서는 큰 문제가 되지 않지만, "한 번 누를 때 정확히 10도 이동" 같은 기능을 만들 때는 디바운싱 처리가 필요합니다. 디바운싱은 버튼 상태가 일정 시간 안정적으로 유지되었을 때만 입력으로 인정하는 방법입니다.
시리얼 입력으로 각도 명령 보내기
다음은 PC에서 시리얼 모니터로 각도를 입력해 서보를 움직이는 방식입니다. 이 방식은 뒤에서 라즈베리파이나 다른 보드가 마이크로컨트롤러에 명령을 보내는 구조와도 연결됩니다. 예를 들어 PC나 라즈베리파이가 120이라고 보내면 서보가 120도로 이동하는 식입니다.
#include <Servo.h>
Servo myServo;
const int servoPin = 9;
int angle = 90;
void setup() {
Serial.begin(115200);
myServo.attach(servoPin);
myServo.write(angle);
Serial.println("Servo controller ready.");
Serial.println("Type angle 0~180 and press Enter.");
}
void loop() {
if (Serial.available() > 0) {
int inputAngle = Serial.parseInt();
if (inputAngle >= 0 && inputAngle <= 180) {
angle = inputAngle;
myServo.write(angle);
Serial.print("Angle: ");
Serial.println(angle);
} else {
Serial.println("Please enter 0~180.");
}
while (Serial.available() > 0) {
Serial.read();
}
}
}시리얼 모니터에서 줄 끝 설정을 Newline 또는 Both NL & CR로 해두면 입력이 더 안정적으로 처리됩니다. 이 예제는 숫자 하나만 받지만, 조금 확장하면 PAN 120, TILT 60, STOP 같은 명령 형식도 만들 수 있습니다. 20장에서 다룬 라즈베리파이와 마이크로컨트롤러의 역할 분리 구조가 바로 이런 식으로 출발합니다.
부드러운 움직임 만들기
지금까지의 코드는 목표 각도가 바뀌면 바로 myServo.write(angle)을 호출했습니다. 작은 서보에서는 별문제 없어 보일 수 있지만, 로봇에서는 움직임이 갑작스러우면 기구물이 흔들리고 센서값도 튑니다. 카메라를 달았다면 화면이 휙 돌아가 어지러워 보이고, 거리 센서를 달았다면 측정값이 안정되기 전에 방향이 바뀔 수 있습니다.
그래서 실제 프로젝트에서는 현재 각도에서 목표 각도로 한 번에 뛰지 않고, 조금씩 이동시키는 방식이 좋습니다. 핵심 아이디어는 간단합니다. 현재 각도가 목표보다 작으면 1도 증가시키고, 현재 각도가 목표보다 크면 1도 감소시킵니다. 이 과정을 일정 시간 간격으로 반복하면 부드러운 움직임처럼 보입니다.
아래 코드는 가변저항으로 목표 각도를 정하되, 서보는 천천히 따라가도록 만든 예제입니다.
#include <Servo.h>
Servo myServo;
const int servoPin = 9;
const int potPin = A0;
int currentAngle = 90;
int targetAngle = 90;
unsigned long lastMoveTime = 0;
const unsigned long moveInterval = 15; // ms
const int stepSize = 1; // 한 번에 움직일 각도
void setup() {
myServo.attach(servoPin);
myServo.write(currentAngle);
}
void loop() {
int potValue = analogRead(potPin);
targetAngle = map(potValue, 0, 1023, 0, 180);
targetAngle = constrain(targetAngle, 0, 180);
unsigned long now = millis();
if (now - lastMoveTime >= moveInterval) {
lastMoveTime = now;
if (currentAngle < targetAngle) {
currentAngle += stepSize;
if (currentAngle > targetAngle) {
currentAngle = targetAngle;
}
} else if (currentAngle > targetAngle) {
currentAngle -= stepSize;
if (currentAngle < targetAngle) {
currentAngle = targetAngle;
}
}
myServo.write(currentAngle);
}
}여기서 moveInterval은 움직임의 시간 간격입니다. 값이 작을수록 빠르게 움직이고, 클수록 느리게 움직입니다. stepSize는 한 번에 움직이는 각도입니다. stepSize를 2나 3으로 키우면 더 빠르지만 덜 부드럽고, 1로 두면 느리지만 안정적입니다.
중요한 점은 delay()를 많이 쓰지 않았다는 것입니다. delay()는 코드 전체를 잠시 멈춥니다. 작은 예제에서는 편하지만, 나중에 센서도 읽고 통신도 처리하고 모터도 제어해야 하는 로봇에서는 긴 delay()가 발목을 잡습니다. millis()를 이용해 "시간이 지났을 때만 조금 움직이는 방식"을 쓰면 다른 작업과 함께 실행하기 좋습니다.
팬틸트 구조 만들기
서보 하나가 좌우로 움직이면 팬 구조입니다. 여기에 서보를 하나 더 올려 상하 방향을 제어하면 팬틸트 구조가 됩니다. 팬은 좌우 회전, 틸트는 위아래 회전을 뜻합니다. CCTV, 카메라 짐벌, 거리 센서 스캐너, 로봇 머리 구조에서 흔히 볼 수 있는 방식입니다.
기구적으로는 아래쪽 서보가 전체 구조를 좌우로 돌리고, 그 위에 브래킷을 올린 뒤, 옆으로 눕힌 두 번째 서보가 센서나 카메라를 위아래로 기울입니다. 시중에 판매되는 소형 팬틸트 브래킷을 써도 되고, 두꺼운 종이보드나 아크릴판으로 직접 만들어도 됩니다.
만들 때는 다음 사항을 신경 쓰면 좋습니다.
위쪽에 올리는 센서나 카메라가 너무 무거우면 서보가 떨리거나 내려앉습니다.
틸트 축은 가능한 한 센서 무게중심 근처에 두어야 서보 부담이 줄어듭니다.
케이블은 회전할 여유를 남겨야 하며, 서보가 움직일 때 선이 당겨지면 안 됩니다.
처음에는 0~180도 전체 범위를 쓰지 말고, 기구물에 걸리지 않는 안전 범위부터 찾습니다.
나사로 고정하기 전에 테이프나 임시 고정으로 동작 범위를 먼저 확인합니다.
팬틸트 구조에서는 각도 제한이 특히 중요합니다. 코드상으로 0도에서 180도까지 가능하더라도, 실제 브래킷은 30도에서 150도까지만 안전할 수 있습니다. 서보가 기구물 끝에 걸린 상태에서 계속 힘을 주면 전류가 커지고 서보가 뜨거워집니다. 그래서 소프트웨어에서 안전 범위를 제한해야 합니다.
두 개의 가변저항으로 팬틸트 제어하기
이제 서보 2개와 가변저항 2개를 사용해 팬틸트 제어기를 만들어보겠습니다. 첫 번째 가변저항은 좌우 팬 각도를 정하고, 두 번째 가변저항은 상하 틸트 각도를 정합니다.
아두이노 기준 연결은 다음과 같습니다.
팬 서보 신호선 ─── D9
틸트 서보 신호선 ─── D10
두 서보 VCC ─── 외부 5V +
두 서보 GND ─── 외부 5V - 및 아두이노 GND
팬 가변저항 가운데 다리 ─── A0
틸트 가변저항 가운데 다리 ─── A1
각 가변저항 양쪽 다리 ─── 5V, GND다음 코드는 두 서보가 각각의 목표 각도를 부드럽게 따라가도록 합니다.
#include <Servo.h>
Servo panServo;
Servo tiltServo;
const int panServoPin = 9;
const int tiltServoPin = 10;
const int panPotPin = A0;
const int tiltPotPin = A1;
int currentPan = 90;
int currentTilt = 90;
int targetPan = 90;
int targetTilt = 90;
unsigned long lastMoveTime = 0;
const unsigned long moveInterval = 15;
const int stepSize = 1;
int moveToward(int current, int target) {
if (current < target) {
current += stepSize;
if (current > target) current = target;
} else if (current > target) {
current -= stepSize;
if (current < target) current = target;
}
return current;
}
void setup() {
panServo.attach(panServoPin);
tiltServo.attach(tiltServoPin);
panServo.write(currentPan);
tiltServo.write(currentTilt);
}
void loop() {
int panValue = analogRead(panPotPin);
int tiltValue = analogRead(tiltPotPin);
targetPan = map(panValue, 0, 1023, 20, 160);
targetTilt = map(tiltValue, 0, 1023, 30, 150);
targetPan = constrain(targetPan, 20, 160);
targetTilt = constrain(targetTilt, 30, 150);
unsigned long now = millis();
if (now - lastMoveTime >= moveInterval) {
lastMoveTime = now;
currentPan = moveToward(currentPan, targetPan);
currentTilt = moveToward(currentTilt, targetTilt);
panServo.write(currentPan);
tiltServo.write(currentTilt);
}
}이 코드에서 팬 범위는 20~160도, 틸트 범위는 30~150도로 제한했습니다. 실제 범위는 자신이 만든 구조에 맞게 조정해야 합니다. 가장 안전한 방법은 서보 혼을 연결하지 않은 상태에서 먼저 서보를 90도로 맞추고, 그다음 기구물을 중앙 위치에 조립하는 것입니다. 그런 뒤 조금씩 범위를 넓히며 어디까지 움직여도 걸리지 않는지 확인합니다.
시리얼 명령으로 팬틸트 움직이기
가변저항은 손으로 조작하기 좋지만, 로봇 프로젝트에서는 다른 프로그램이 각도 명령을 보내는 경우가 많습니다. 예를 들어 라즈베리파이에서 OpenCV로 사람 얼굴이나 색상 물체의 위치를 찾고, 그 결과에 따라 ESP32나 아두이노에 팬틸트 각도를 보내는 구조를 만들 수 있습니다.
다음은 시리얼 모니터에서 P120 또는 T70처럼 입력해 팬과 틸트를 움직이는 간단한 예입니다.
#include <Servo.h>
Servo panServo;
Servo tiltServo;
const int panServoPin = 9;
const int tiltServoPin = 10;
int currentPan = 90;
int currentTilt = 90;
int targetPan = 90;
int targetTilt = 90;
unsigned long lastMoveTime = 0;
const unsigned long moveInterval = 15;
int moveToward(int current, int target) {
if (current < target) current++;
else if (current > target) current--;
return current;
}
void setup() {
Serial.begin(115200);
panServo.attach(panServoPin);
tiltServo.attach(tiltServoPin);
panServo.write(currentPan);
tiltServo.write(currentTilt);
Serial.println("Pan-tilt ready.");
Serial.println("Use P0~P180 or T0~T180. Example: P120");
}
void loop() {
if (Serial.available() > 0) {
char axis = Serial.read();
int value = Serial.parseInt();
if (axis == 'P' || axis == 'p') {
targetPan = constrain(value, 20, 160);
Serial.print("Pan target: ");
Serial.println(targetPan);
} else if (axis == 'T' || axis == 't') {
targetTilt = constrain(value, 30, 150);
Serial.print("Tilt target: ");
Serial.println(targetTilt);
}
while (Serial.available() > 0) {
Serial.read();
}
}
unsigned long now = millis();
if (now - lastMoveTime >= moveInterval) {
lastMoveTime = now;
currentPan = moveToward(currentPan, targetPan);
currentTilt = moveToward(currentTilt, targetTilt);
panServo.write(currentPan);
tiltServo.write(currentTilt);
}
}이제 시리얼 모니터에 P30, P150, T60, T120 같은 명령을 보내보세요. 서보가 한 번에 튀지 않고 천천히 목표 각도를 향해 움직이면 성공입니다.
이 구조는 이후 프로젝트의 중요한 출발점이 됩니다. 초음파 센서가 정면 거리만 재는 대신 팬 서보 위에 올라가 좌우를 훑으면, 로봇은 어느 쪽이 더 비어 있는지 판단할 수 있습니다. 카메라가 팬틸트 위에 올라가면 화면 속 물체를 따라 고개를 돌릴 수 있습니다. 작은 서보 제어기가 로봇의 목이 되는 순간입니다.
센서나 카메라를 올릴 때의 요령
팬틸트 위에 센서나 카메라를 올리면 갑자기 로봇다운 느낌이 납니다. 하지만 기구물이 조금만 불안정해도 센서값이 흔들리고, 영상이 떨리고, 서보가 계속 미세하게 떨 수 있습니다.
초음파 센서를 올린다면 센서가 정면을 정확히 바라보도록 고정해야 합니다. 센서가 살짝 기울어져 있으면 측정 방향도 함께 틀어집니다. ToF 거리 센서나 적외선 거리 센서도 마찬가지입니다. 카메라를 올릴 때는 무게가 더 중요합니다. 작은 서보에 무거운 웹캠을 올리면 움직일 수는 있어도 오래 버티기 어렵습니다. 처음에는 ESP32-CAM 같은 소형 카메라 모듈이나 가벼운 카메라를 사용하는 편이 좋습니다.
케이블도 기구물의 일부입니다. 많은 초보자가 센서와 카메라만 보고 케이블을 잊어버립니다. 서보가 좌우로 돌 때 케이블이 팽팽해지면 서보가 원하는 위치까지 가지 못하거나, 브레드보드에서 선이 빠질 수 있습니다. 케이블은 한 바퀴 감길 만큼 길게 두는 것이 아니라, 필요한 회전 범위 안에서 자연스럽게 휘어질 정도로 여유를 주면 됩니다.
흔한 문제와 해결 방법
서보 프로젝트에서 자주 만나는 문제는 몇 가지로 압축됩니다. 증상만 보고 코드를 계속 바꾸기보다, 전원, 배선, 기구물, 코드 순서로 차근차근 확인하는 습관이 중요합니다.
서보가 전혀 움직이지 않으면 신호핀 번호, GND 공통 연결, 전원 극성을 먼저 확인합니다.
서보가 덜덜 떨면 외부 전원 전류 용량이 부족하거나, 기구물이 끝에 걸렸거나, 목표 각도가 빠르게 흔들리고 있을 가능성이 큽니다.
보드가 리셋되면 서보 전원을 보드 5V에서 빼서 별도 전원으로 바꾸고, GND만 공통으로 연결합니다.
가변저항을 조금만 건드려도 서보가 떨리면 입력값에 작은 변화가 계속 들어오는 것이므로 평균값을 내거나 목표 각도 변화가 일정 값 이상일 때만 반영합니다.
팬틸트가 한쪽으로만 움직이면 서보 혼을 90도 기준으로 다시 조립하고, 코드의 최소·최대 각도 제한을 조정합니다.
특히 가변저항 입력은 완벽하게 고정된 숫자가 나오지 않습니다. 손을 대지 않아도 512, 513, 511처럼 조금씩 흔들릴 수 있습니다. 이 흔들림이 그대로 목표 각도에 반영되면 서보도 미세하게 떨 수 있습니다. 간단한 해결책은 이전 목표값과 새 목표값의 차이가 2도 이상일 때만 반영하는 것입니다.
int newTarget = map(analogRead(potPin), 0, 1023, 0, 180);
if (abs(newTarget - targetAngle) >= 2) {
targetAngle = newTarget;
}이런 작은 처리 하나가 로봇의 움직임을 훨씬 차분하게 만들어줍니다.
프로젝트 확장 아이디어
이번 장에서 만든 서보 제어기는 그 자체로도 완성된 작은 장치입니다. 하지만 진짜 재미는 여기에 센서와 통신을 붙이기 시작할 때 나옵니다.
초음파 센서를 팬 서보 위에 올려 왼쪽, 정면, 오른쪽 거리를 차례대로 측정합니다.
카메라를 팬틸트 위에 올려 화면 속 특정 색상 물체를 따라가게 만듭니다.
버튼 대신 스마트폰이나 웹 인터페이스에서 팬틸트 각도를 보내도록 바꿉니다.
서보 이동 속도를 상황에 따라 바꿔 빠른 탐색 모드와 부드러운 추적 모드를 만듭니다.
현재 각도와 목표 각도를 OLED 디스플레이나 시리얼 모니터에 표시합니다.
중요한 것은 한 번에 모든 기능을 넣지 않는 것입니다. 먼저 서보 하나를 안정적으로 움직이고, 그다음 입력 장치를 붙이고, 그다음 부드러운 이동을 넣고, 마지막으로 팬틸트 구조로 확장하세요. 로봇 제작은 부품을 많이 붙이는 싸움이 아니라, 하나씩 붙인 기능이 서로 방해하지 않도록 정리하는 과정입니다.
작은 목이 생기면 로봇은 주변을 보기 시작한다
이번 장에서는 서보 모터와 입력 장치를 연결해 각도를 제어하고, 현재 각도에서 목표 각도까지 부드럽게 이동하는 방법을 실습했습니다. 또한 두 개의 서보로 팬틸트 구조를 만들고, 그 위에 센서나 카메라를 올릴 때 고려해야 할 점도 살펴보았습니다.
처음에는 단순히 "서보가 돈다"로 보였을 것입니다. 하지만 전원을 따로 공급하고, 공통 GND를 연결하고, 입력값을 각도로 바꾸고, 각도를 제한하고, 움직임을 부드럽게 만드는 과정을 거치면 이 작은 장치가 로봇의 한 부분으로 바뀝니다.
다음 장에서는 이 경험을 거리 센서와 결합합니다. 서보 없이도 거리를 잴 수 있지만, 거리값에 따라 멈추고 LED나 부저로 알려주는 로봇을 만들면 센서 입력과 모터 제어가 더 분명하게 연결됩니다. 이제 로봇은 고개를 돌릴 준비를 마쳤고, 곧 앞에 무엇이 있는지 직접 확인하게 될 것입니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.