Skip to main content
Summary

디지털 핀으로 '중간 세기'를 만드는 방법

로봇을 처음 만들 때 가장 신기한 순간 중 하나는 모터 속도를 마음대로 바꾸는 순간입니다. 버튼을 누르면 모터가 켜지고, 다시 누르면 꺼지는 정도는 이해하기 쉽습니다. 그런데 바퀴가 천천히 돌다가 점점 빨라지고, LED가 어둡게 켜졌다가 밝아지고, 서보 모터가 0도에서 90도, 다시 180도로 움직이는 장면을 보면 이런 생각이 듭니다.

"디지털 핀은 HIGH 아니면 LOW라면서, 어떻게 중간값을 만들지?"

여기서 등장하는 기술이 PWM, 즉 Pulse Width Modulation입니다. 한국어로는 보통 펄스 폭 변조라고 부릅니다. 이름은 조금 딱딱하지만 원리는 간단합니다. 전압을 진짜로 1V, 2V, 3V처럼 부드럽게 바꾸는 것이 아니라, 핀을 아주 빠르게 켰다 껐다 하면서 연결된 장치가 평균적으로 더 약하거나 강하게 동작하는 것처럼 만드는 방식입니다.

예를 들어 5V 출력 핀이 있다고 해봅시다. 이 핀은 기본적으로 0V 또는 5V만 낼 수 있습니다. 그런데 1초 동안 절반은 5V, 절반은 0V로 아주 빠르게 반복하면 LED나 DC 모터는 대략 "절반 정도의 출력"을 받은 것처럼 반응합니다. 실제 핀 전압이 2.5V로 고정되는 것은 아닙니다. 오실로스코프로 보면 여전히 0V와 5V를 오가는 사각파입니다. 다만 LED의 밝기, 모터의 회전, 사람의 눈과 귀, 그리고 모터의 관성은 그 빠른 깜빡임을 그대로 따라가지 못합니다. 그래서 평균적인 세기처럼 보입니다.

PWM은 로봇 제작에서 아주 자주 쓰입니다. LED 밝기를 조절할 때도 쓰고, DC 모터 속도를 조절할 때도 쓰고, 서보 모터에 목표 각도를 알려줄 때도 쓰입니다. 앞 장에서 모터 드라이버가 "제어 보드의 작은 신호로 모터의 큰 전류를 대신 다루는 장치"라고 배웠다면, 이번 장에서는 그 작은 신호를 어떻게 세밀하게 조절하는지 배우는 셈입니다.

PWM으로 속도와 각도 제어하기

PWM은 아날로그 출력이 아니다

아두이노에는 analogWrite()라는 함수가 있습니다. 이름만 보면 "아날로그 전압을 출력하는 함수"처럼 느껴집니다. 하지만 일반적인 아두이노 우노, 나노 같은 보드에서 analogWrite()는 진짜 아날로그 전압을 내보내는 함수가 아니라 PWM 신호를 출력하는 함수입니다. 디지털 핀이 매우 빠르게 HIGH와 LOW를 반복하고, 그 HIGH 상태가 한 주기에서 얼마나 오래 유지되는지를 바꿉니다. 아두이노 계열 보드에서 흔히 쓰는 analogWrite() 값 0~255는 8비트 해상도에 해당하며, 0은 항상 꺼짐, 255는 항상 켜짐, 128 근처는 약 50% 켜짐으로 이해할 수 있습니다1.

이때 핵심 단어가 듀티 사이클입니다. 듀티 사이클은 한 주기 동안 신호가 켜져 있는 시간의 비율입니다. 한 주기가 10ms인데 그중 3ms 동안 HIGH라면 듀티 사이클은 30%입니다. 8ms 동안 HIGH라면 80%입니다.

PWM 값듀티 사이클신호 상태느낌
00%항상 LOW꺼짐
64약 25%짧게 켜지고 길게 꺼짐약함
128약 50%절반 켜지고 절반 꺼짐중간
191약 75%길게 켜지고 짧게 꺼짐강함
255100%항상 HIGH최대

여기서 조심해야 할 점이 있습니다. PWM 값이 128이라고 해서 핀에서 2.5V가 계속 나오는 것은 아닙니다. 5V와 0V가 빠르게 반복될 뿐입니다. 그래서 멀티미터로 재면 평균값처럼 보일 수 있지만, 실제 파형은 사각파입니다. 이 차이를 이해하면 뒤에서 모터 드라이버, 서보 모터, PWM 주파수를 다룰 때 훨씬 덜 헷갈립니다.

PWM을 물 펌프에 비유해 보겠습니다. 수도꼭지를 절반만 열어 물이 계속 약하게 흐르게 하는 것이 진짜 아날로그 조절이라면, PWM은 수도꼭지를 아주 빠르게 완전히 열었다 닫았다 하는 방식입니다. 1초 동안 0.2초만 열면 물이 조금 나오고, 0.8초 동안 열면 물이 많이 나옵니다. 수도꼭지 자체는 항상 완전히 열리거나 완전히 닫히지만, 양동이에 모이는 물의 양은 중간값처럼 달라집니다.

듀티 사이클을 손으로 느껴보기: LED 밝기 실습

PWM을 가장 안전하게 이해하는 방법은 LED로 시작하는 것입니다. LED는 전류가 많이 필요하지 않고, 밝기 변화가 눈에 바로 보입니다. 모터처럼 역기전력이나 큰 전류를 걱정하지 않아도 되므로 첫 실험으로 좋습니다.

준비물은 간단합니다. 아두이노 우노 또는 나노, LED, 220Ω 정도의 저항, 점퍼선, 브레드보드가 있으면 됩니다. LED의 긴 다리인 애노드를 PWM 핀에 저항을 거쳐 연결하고, 짧은 다리인 캐소드를 GND에 연결합니다. 아두이노 우노에서는 ~ 표시가 붙은 3, 5, 6, 9, 10, 11번 핀이 PWM 출력에 자주 사용됩니다. 보드마다 PWM 핀이 다르므로 실제 사용할 때는 보드 핀맵을 확인해야 합니다.

const int ledPin = 9;

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

void loop() {
  for (int duty = 0; duty <= 255; duty += 5) {
    analogWrite(ledPin, duty);
    delay(20);
  }

  for (int duty = 255; duty >= 0; duty -= 5) {
    analogWrite(ledPin, duty);
    delay(20);
  }
}

이 코드를 올리면 LED가 서서히 밝아졌다가 다시 어두워집니다. 코드에서 duty 값은 PWM의 세기를 나타냅니다. 값이 0이면 LED는 꺼지고, 255이면 가장 밝게 켜집니다. 중간 값에서는 매우 빠르게 깜빡이지만 사람 눈에는 밝기만 달라지는 것처럼 보입니다.

이 실습에서 중요한 것은 delay(20)이 PWM 주파수를 만드는 코드가 아니라는 점입니다. PWM 파형 자체는 마이크로컨트롤러 내부의 타이머가 만들어 줍니다. delay(20)은 밝기 값을 조금씩 바꾸는 간격일 뿐입니다. 즉, LED는 내부적으로 매우 빠르게 깜빡이고 있고, 우리는 그 깜빡임의 비율을 20ms마다 조금씩 바꾸는 것입니다.

DC 모터 속도는 왜 PWM 값과 딱 맞지 않을까

LED 실습을 마치고 같은 방식으로 DC 모터를 돌려보면 약간 다른 느낌을 받습니다. PWM 값을 128로 주면 LED는 대략 절반 밝기로 보이지만, DC 모터가 꼭 절반 속도로 도는 것은 아닙니다. 어떤 모터는 PWM 값을 30이나 40까지 올려도 꿈쩍하지 않다가 어느 순간 갑자기 돌기 시작합니다. 바퀴를 바닥에 내려놓으면 공중에서 돌릴 때보다 더 느리게 움직입니다. 배터리가 약해지면 같은 PWM 값에서도 속도가 떨어집니다.

이 현상은 정상입니다. DC 모터는 LED보다 훨씬 복잡한 부품입니다. 모터 안에는 코일이 있고, 자석이 있고, 축과 기어가 있고, 마찰이 있습니다. 모터가 정지해 있을 때는 처음 움직이기 위한 힘이 필요하고, 바퀴에 로봇 무게가 실리면 더 큰 토크가 필요합니다. 토크는 회전시키는 힘입니다. 또한 모터가 빠르게 돌수록 역기전력이라는 전압이 생겨 전류 흐름을 방해합니다. 그래서 PWM 듀티 사이클과 실제 속도는 완전히 직선적인 관계가 되지 않을 수 있습니다.

특히 다음 요인은 DC 모터 속도에 큰 영향을 줍니다.

  • 부하: 로봇 무게, 바퀴 마찰, 바닥 상태가 달라지면 같은 PWM 값에서도 속도가 달라집니다.

  • 전원 상태: 배터리 전압이 낮아지거나 순간적으로 전압 강하가 생기면 모터 힘이 약해집니다.

  • 모터 드라이버 손실: L298N 같은 오래된 드라이버는 내부 전압 강하가 커서 배터리 전압이 그대로 모터에 전달되지 않습니다.

  • 출발 토크: 낮은 듀티 사이클에서는 모터가 회전을 시작할 만큼의 힘을 얻지 못해 멈춰 있을 수 있습니다.

따라서 PWM은 "속도를 명령하는 손잡이"이지, "정확한 RPM을 보장하는 마법"은 아닙니다. 정확한 속도를 유지하려면 엔코더로 실제 회전 속도를 측정하고, 뒤의 제어 장에서 배울 피드백 제어를 적용해야 합니다. 지금은 먼저 PWM으로 모터에 전달되는 평균 전력을 조절하는 감각을 익히면 됩니다.

모터는 반드시 드라이버를 통해 제어한다

아두이노나 ESP32의 GPIO 핀은 신호를 보내는 용도이지, 모터를 직접 구동하는 전원 장치가 아닙니다. 작은 DC 모터라도 회전할 때 수백 mA가 필요할 수 있고, 출발 순간이나 축이 막힌 순간에는 그보다 훨씬 큰 전류가 흐릅니다. 반면 마이크로컨트롤러 핀은 보통 수십 mA 수준의 작은 전류만 안전하게 다룰 수 있습니다. DC 모터를 핀에 직접 연결하면 보드가 재부팅되거나, 핀이 손상되거나, 최악의 경우 보드가 망가질 수 있습니다. 모터는 코일을 가진 부품이라 전원을 끊을 때 역기전력에 의한 전압 스파이크도 만들 수 있습니다.

그래서 PWM으로 DC 모터를 제어할 때는 모터 드라이버를 사용합니다. 한 방향으로만 돌릴 단순 팬이라면 MOSFET 하나와 보호 다이오드로 구성할 수 있고, 전진과 후진이 필요한 바퀴 로봇이라면 H-Bridge 드라이버를 사용합니다. 흔히 볼 수 있는 L298N, TB6612FNG, DRV8833 같은 모듈이 여기에 해당합니다. H-Bridge는 모터 양단에 걸리는 전압의 방향을 바꿔 모터를 정회전 또는 역회전시키는 회로입니다.

PWM 제어에서 드라이버를 연결할 때는 보통 방향 핀과 속도 핀을 나누어 생각합니다. 방향 핀은 모터 전류가 어느 방향으로 흐를지 결정하고, 속도 핀에는 PWM 신호를 넣어 듀티 사이클로 속도를 조절합니다.

예를 들어 L298N이나 TB6612 계열 드라이버에서 한쪽 모터를 제어한다고 해봅시다.

역할예시 핀설명
PWM 속도 제어ENA 또는 PWMAanalogWrite()로 듀티 사이클 입력
방향 제어 1IN1 또는 AIN1HIGH/LOW 조합으로 방향 결정
방향 제어 2IN2 또는 AIN2IN1과 반대 조합으로 방향 결정
모터 전원VM, Vmotor배터리 또는 외부 전원 연결
로직 전원VCC보드 로직 전압 연결
기준 전압GND제어 보드와 모터 전원의 GND 공유

여기서 공통 GND가 매우 중요합니다. 모터 전원은 별도 배터리를 쓰더라도, 제어 보드의 GND와 모터 드라이버의 GND는 반드시 연결해야 합니다. 그래야 드라이버가 "이 PWM 신호의 HIGH와 LOW가 무엇을 기준으로 한 전압인지" 이해할 수 있습니다. 이전 전원 장에서 배운 것처럼, 전원은 분리하되 기준점은 공유해야 합니다.

또 하나 중요한 것은 드라이버의 전류 용량입니다. 모터 데이터시트에 적힌 정격 전류만 보고 드라이버를 고르면 부족할 수 있습니다. 모터는 출발 순간이나 바퀴가 막힌 상태에서 훨씬 큰 전류를 요구합니다. 이를 스톨 전류 또는 기동 전류라고 부릅니다. 실전에서는 모터가 평소에 500mA를 먹는다고 해서 500mA짜리 드라이버를 고르면 안 됩니다. 여유를 두고 선택해야 하고, 발열이 심하면 방열판이나 더 효율적인 드라이버를 고려해야 합니다.

DC 모터 PWM 실습: 천천히 출발하고 천천히 멈추기

이제 DC 모터를 PWM으로 제어해 봅시다. 예시는 아두이노와 H-Bridge 드라이버를 기준으로 설명하겠습니다. L298N을 사용한다면 ENA 핀의 점퍼를 빼고 아두이노 PWM 핀에 연결해야 속도 제어가 가능합니다. 점퍼가 꽂혀 있으면 ENA가 계속 HIGH에 연결되어 모터가 항상 최대 속도로만 돌 수 있습니다.

연결 예시는 다음과 같습니다.

아두이노모터 드라이버역할
D9ENA 또는 PWMAPWM 속도 제어
D7IN1 또는 AIN1방향 제어
D8IN2 또는 AIN2방향 제어
GNDGND공통 기준점
외부 배터리 +VM 또는 Motor VCC모터 전원
외부 배터리 -GND모터 전원 GND
모터 단자 2개OUT1, OUT2모터 연결

아래 코드는 모터를 정방향으로 설정한 뒤, PWM 값을 서서히 올리고 다시 낮춥니다. 갑자기 255를 주지 않고 천천히 올리는 것을 소프트 스타트라고 생각하면 됩니다. 모터와 기어, 바퀴에 가해지는 충격을 줄이고, 출발 순간 전류가 확 튀는 것도 완화할 수 있습니다.

const int pwmPin = 9;
const int in1Pin = 7;
const int in2Pin = 8;

void setup() {
  pinMode(pwmPin, OUTPUT);
  pinMode(in1Pin, OUTPUT);
  pinMode(in2Pin, OUTPUT);

  digitalWrite(in1Pin, HIGH);
  digitalWrite(in2Pin, LOW);
}

void loop() {
  for (int pwm = 0; pwm <= 255; pwm += 5) {
    analogWrite(pwmPin, pwm);
    delay(50);
  }

  delay(1000);

  for (int pwm = 255; pwm >= 0; pwm -= 5) {
    analogWrite(pwmPin, pwm);
    delay(50);
  }

  delay(1000);
}

실습할 때는 모터를 공중에 띄운 상태와 바닥에 닿은 상태를 비교해 보세요. 공중에서는 낮은 PWM 값에서도 잘 돌던 바퀴가 바닥에 닿으면 멈추거나 떨릴 수 있습니다. 로봇이 실제로 움직일 때는 바퀴와 바닥 사이의 마찰, 프레임 무게, 배터리 상태까지 모두 영향을 줍니다. 이것이 실전 로봇 제작에서 "책상 위 회로는 되는데 바닥에 내려놓으면 이상해지는" 대표적인 이유입니다.

시리얼 모니터에 PWM 값을 출력하면서 관찰하면 더 좋습니다.

Serial.print("PWM = ");
Serial.println(pwm);

어느 값에서 모터가 처음 움직이기 시작하는지, 어느 값 이하로 내려가면 멈추는지 기록해 두면 나중에 로봇 주행 코드를 만들 때 도움이 됩니다. 예를 들어 왼쪽 모터는 PWM 45에서 출발하고 오른쪽 모터는 PWM 55에서 출발한다면, 두 모터의 특성이 다르다는 뜻입니다. 완벽히 같은 모터 두 개를 샀더라도 실제 속도는 다를 수 있습니다.

PWM 주파수: 너무 느려도, 너무 빨라도 문제다

PWM에는 듀티 사이클만 있는 것이 아닙니다. 또 하나의 중요한 값이 주파수입니다. 주파수는 1초 동안 PWM 신호가 몇 번 반복되는지를 뜻합니다. 500Hz라면 1초에 500번 켜졌다 꺼지는 것이고, 20kHz라면 1초에 20,000번 반복됩니다.

아두이노 우노의 기본 PWM 주파수는 핀과 타이머에 따라 다릅니다. 예를 들어 우노에서는 5번과 6번 핀이 약 980Hz, 3, 9, 10, 11번 핀이 약 490Hz로 동작하는 것으로 알려져 있습니다2. 이 정도 주파수는 간단한 LED 밝기 조절이나 작은 모터 실험에는 충분한 경우가 많습니다. 하지만 모터에서는 주파수에 따라 떨림이나 소음이 달라질 수 있습니다.

PWM 주파수가 너무 낮으면 모터 코일과 기계 구조가 켜짐/꺼짐의 변화를 어느 정도 따라가면서 "윙" 또는 "삐" 하는 소리가 날 수 있습니다. 특히 사람이 들을 수 있는 가청 주파수 영역에 걸리면 거슬리는 소음이 생깁니다. 반대로 주파수를 너무 높이면 모터 드라이버의 스위칭 손실이 증가합니다. 스위칭 손실은 드라이버가 켜짐과 꺼짐을 전환하는 순간마다 발생하는 에너지 손실입니다. 너무 자주 스위칭하면 드라이버가 더 뜨거워질 수 있고, 회로 설계가 좋지 않으면 전자파 잡음도 커질 수 있습니다.

즉 PWM 주파수는 "무조건 높을수록 좋다"가 아닙니다. 모터와 드라이버의 특성에 맞추어 적당한 값을 찾아야 합니다. 일반적인 DC 모터 제어에서는 수백 Hz에서 수십 kHz까지 다양한 주파수가 사용됩니다. 소음을 줄이고 싶다면 20kHz 근처처럼 사람이 듣기 어려운 영역을 고려할 수 있지만, 사용하는 드라이버가 그 주파수를 안정적으로 처리할 수 있는지 확인해야 합니다. 예를 들어 어떤 모터 드라이버는 지원 가능한 최대 PWM 주파수가 명시되어 있습니다. 이를 넘기면 정상 동작하지 않거나 발열이 커질 수 있습니다.

간단한 비교 실험을 해볼 수 있습니다. 같은 모터, 같은 배터리, 같은 듀티 사이클에서 PWM 주파수만 바꾸어 소리와 발열, 출발 성능을 관찰하는 것입니다. 아두이노 우노에서는 타이머 레지스터를 직접 건드려야 하므로 초보 단계에서는 조심해야 합니다. 특히 Timer0의 설정을 바꾸면 millis()와 delay() 같은 기본 시간 함수에 영향을 줄 수 있습니다. 반면 ESP32는 PWM 주변장치인 LEDC를 통해 주파수와 해상도를 비교적 명확하게 설정할 수 있습니다.

ESP32에서 PWM 설정하기

ESP32는 아두이노 우노와 PWM을 다루는 방식이 조금 다릅니다. 우노에서는 analogWrite(pin, value)로 바로 시작하는 경우가 많지만, ESP32는 PWM 채널, 주파수, 해상도를 설정한 뒤 특정 핀에 연결하는 구조를 사용합니다. ESP32 계열에서는 LEDC라는 하드웨어 기능으로 PWM을 생성합니다. Espressif의 서보 관련 문서에서도 ESP32의 LEDC를 이용해 여러 채널의 PWM 신호를 만들 수 있고, 서보 제어에서는 50~400Hz 범위의 주파수를 선택할 수 있다고 설명합니다3.

ESP32 Arduino 환경에서는 버전과 코어 설정에 따라 API가 조금씩 달라질 수 있습니다. 많이 쓰이는 방식은 채널을 설정하고 핀에 붙인 뒤, 듀티 값을 쓰는 흐름입니다.

const int motorPin = 18;
const int pwmChannel = 0;
const int pwmFreq = 20000;
const int pwmResolution = 8;

void setup() {
  ledcSetup(pwmChannel, pwmFreq, pwmResolution);
  ledcAttachPin(motorPin, pwmChannel);
}

void loop() {
  for (int duty = 0; duty <= 255; duty += 5) {
    ledcWrite(pwmChannel, duty);
    delay(30);
  }

  for (int duty = 255; duty >= 0; duty -= 5) {
    ledcWrite(pwmChannel, duty);
    delay(30);
  }
}

여기서 pwmFreq는 PWM 주파수입니다. 예제에서는 20kHz로 설정했습니다. pwmResolution은 해상도입니다. 8비트로 설정하면 듀티 값 범위는 0~255입니다. 10비트라면 0~1023, 12비트라면 0~4095가 됩니다.

해상도는 제어를 얼마나 잘게 나눌 수 있는지를 뜻합니다. 8비트는 256단계, 12비트는 4096단계입니다. 숫자만 보면 해상도가 높을수록 무조건 좋아 보입니다. 하지만 PWM에서는 주파수와 해상도 사이에 타이머 자원이 연결되어 있습니다. 아주 높은 주파수와 아주 높은 해상도를 동시에 요구하면 보드가 지원하지 못할 수 있습니다. 그래서 모터 속도 제어에서는 필요한 만큼의 해상도와 적당한 주파수를 균형 있게 선택해야 합니다.

ESP32는 핀 선택도 비교적 자유롭지만, 모든 GPIO를 아무렇게나 써도 된다는 뜻은 아닙니다. 부팅 모드에 영향을 주는 핀, 플래시 메모리와 연결된 핀, 입력 전용 핀 등은 피해야 합니다. 실제 로봇을 만들 때는 ESP32 보드의 핀맵을 확인하고, PWM 출력에 안전한 GPIO를 선택하세요.

아두이노와 ESP32 PWM의 차이

아두이노와 ESP32는 모두 PWM을 만들 수 있지만, 사용하는 감각이 다릅니다. 아두이노 우노는 단순하고 예측하기 쉽습니다. analogWrite()를 쓰면 대부분의 기초 실습이 바로 됩니다. 대신 기본 주파수와 해상도 변경이 제한적이고, 타이머를 직접 만지면 다른 함수에 영향을 줄 수 있습니다.

ESP32는 처음에는 설정할 것이 많아 보여 조금 복잡합니다. 하지만 주파수, 해상도, 채널을 직접 정할 수 있어 모터 소음 줄이기, 여러 개의 서보 제어, LED 밝기 제어처럼 다양한 상황에 맞추기 좋습니다. 같은 "PWM 값 128"이라도 보드의 해상도, 주파수, 드라이버, 전원 조건에 따라 실제 출력 특성은 달라질 수 있습니다.

항목아두이노 우노 계열ESP32 계열
기본 사용법analogWrite(pin, value)LEDC 채널, 주파수, 해상도 설정
흔한 해상도8비트, 0~255설정에 따라 8비트 이상 가능
주파수 변경타이머 설정 필요LEDC 설정으로 비교적 유연
PWM 핀정해진 PWM 핀 중심다수 GPIO에 매핑 가능, 단 핀 제약 확인 필요
주의점타이머 변경 시 시간 함수 영향 가능채널, 타이머, 핀 충돌 관리 필요

초보 단계에서는 아두이노로 PWM의 감각을 익히고, ESP32로 넘어가면서 주파수와 해상도를 직접 조절해 보는 흐름이 좋습니다. 이 책의 뒤쪽 프로젝트에서 ESP32를 이용해 무선 조종 로봇을 만들 때도 PWM은 계속 등장합니다.

서보 모터의 PWM은 조금 다르다

여기까지 이야기한 PWM은 주로 LED 밝기나 DC 모터 속도 제어에 관한 것이었습니다. 듀티 사이클이 커지면 평균 출력이 커지고, 그래서 LED가 밝아지거나 모터가 빨라지는 방식입니다. 그런데 서보 모터의 PWM은 느낌이 다릅니다.

일반적인 취미용 서보 모터는 PWM 신호를 "전력을 얼마나 줄 것인가"가 아니라 "어느 각도로 가라는 명령인가"로 해석합니다. 서보 안에는 작은 DC 모터, 기어박스, 위치를 감지하는 가변저항, 제어 회로가 들어 있습니다. 제어 보드는 서보에게 일정한 간격으로 펄스를 보내고, 서보 내부 회로는 그 펄스 폭을 읽어 목표 각도를 결정합니다.

대부분의 취미용 서보는 약 20ms 주기, 즉 50Hz 신호를 사용합니다. 이때 중요한 것은 전체 주기에서 HIGH 비율이 몇 퍼센트냐보다 HIGH가 유지되는 시간, 즉 펄스 폭입니다. Espressif의 서보 문서에서는 일반적인 180도 서보가 20ms 주기에서 약 0.5ms~2.5ms의 HIGH 펄스로 0도에서 180도 사이를 제어한다고 설명합니다4. 다른 자료나 서보 데이터시트에서는 1.0ms~2.0ms를 기준으로 설명하는 경우도 많습니다. 실제 범위는 서보 모델마다 다를 수 있으므로 데이터시트를 확인하거나 작은 범위부터 조심스럽게 테스트해야 합니다.

목표 각도흔한 펄스 폭 예시20ms 주기에서의 의미
0도약 0.5~1.0ms짧은 HIGH 펄스
90도약 1.5ms가운데 위치
180도약 2.0~2.5ms긴 HIGH 펄스

여기서 중요한 차이가 드러납니다. DC 모터 속도 제어에서는 50% 듀티 사이클이 대략 절반 출력이라는 의미를 가질 수 있습니다. 하지만 서보 모터에서 50% 듀티 사이클은 말이 되지 않는 값입니다. 20ms 주기에서 50% 듀티라면 HIGH 시간이 10ms인데, 일반적인 취미용 서보가 기대하는 1~2ms 범위를 훨씬 넘어갑니다. 서보는 큰 소리를 내며 떨거나, 끝에 부딪히거나, 손상될 수 있습니다.

따라서 서보 제어는 "듀티 사이클 몇 퍼센트"보다 "20ms마다 몇 마이크로초 길이의 펄스를 보낼 것인가"로 이해하는 편이 안전합니다.

서보 각도 실습: 0도, 90도, 180도

아두이노에서는 서보 모터를 직접 PWM 계산으로 제어할 수도 있지만, 처음에는 Servo 라이브러리를 사용하는 것이 편합니다. 라이브러리는 내부적으로 적절한 펄스를 만들어 주고, 우리는 각도를 입력하면 됩니다.

서보에는 보통 세 가닥의 선이 있습니다. 색상은 제조사마다 다를 수 있지만 대체로 빨간색은 전원, 갈색 또는 검은색은 GND, 주황색이나 노란색은 신호선입니다. 작은 SG90 서보 하나를 무부하로 테스트할 때는 보드의 5V에서 잠깐 움직일 수 있지만, 실전 로봇에서는 별도 5V 전원을 사용하는 것이 안전합니다. 서보가 움직일 때 순간 전류가 커져 보드가 재부팅될 수 있기 때문입니다. 이때도 제어 보드 GND와 서보 전원 GND는 반드시 연결해야 합니다.

#include <Servo.h>

Servo servo;
const int servoPin = 9;

void setup() {
  servo.attach(servoPin);
}

void loop() {
  servo.write(0);
  delay(1000);

  servo.write(90);
  delay(1000);

  servo.write(180);
  delay(1000);
}

이 코드를 실행하면 서보가 0도, 90도, 180도 위치로 차례대로 이동합니다. 만약 서보가 끝에서 윙윙거리며 계속 힘을 주거나 떨린다면 즉시 전원을 끄고 범위를 줄이세요. 저가형 서보는 실제로 0~180도 전체 범위를 정확히 지원하지 않을 수 있습니다. 어떤 서보는 10~170도 정도가 안전하고, 어떤 서보는 500~2500µs 범위 대신 600~2400µs 범위가 더 적합할 수 있습니다.

ESP32에서는 ESP32Servo 라이브러리를 사용할 수도 있고, LEDC를 직접 설정할 수도 있습니다. 라이브러리를 쓰면 아두이노와 비슷한 감각으로 write()를 사용할 수 있습니다. Arduino 라이브러리 문서에서도 ESP32Servo가 ESP32 계열을 위한 PWM 기반 서보 제어 라이브러리라고 설명합니다5.

#include <ESP32Servo.h>

Servo servo;
const int servoPin = 18;

void setup() {
  servo.setPeriodHertz(50);
  servo.attach(servoPin, 500, 2500);
}

void loop() {
  servo.write(0);
  delay(1000);

  servo.write(90);
  delay(1000);

  servo.write(180);
  delay(1000);
}

servo.attach(servoPin, 500, 2500)에서 500과 2500은 최소·최대 펄스 폭을 마이크로초 단위로 지정한 것입니다. 사용하는 서보에 따라 이 값을 조정해야 할 수 있습니다. 서보가 물리적 끝에 계속 부딪히면 전류가 많이 흐르고 기어가 손상될 수 있으므로, 처음에는 700~2300µs처럼 좁은 범위에서 테스트한 뒤 조금씩 넓히는 것이 좋습니다.

같은 PWM, 다른 의미

PWM이라는 단어 하나로 LED, DC 모터, 서보 모터를 모두 설명할 수 있지만, 실제 의미는 연결된 장치에 따라 다릅니다. 이 차이를 이해하면 로봇 회로를 훨씬 안정적으로 설계할 수 있습니다.

장치PWM이 조절하는 것주의할 점
LED평균 전류와 밝기저항 필요, 너무 낮은 주파수는 깜빡임으로 보일 수 있음
DC 모터평균 전압/전력과 대략적인 속도드라이버 필수, 부하에 따라 실제 속도 달라짐
서보 모터목표 각도주기와 펄스 폭이 중요, 전원 안정성 필요

LED는 사람 눈의 잔상 때문에 밝기가 연속적으로 바뀌는 것처럼 보입니다. DC 모터는 코일의 전기적 특성과 회전 관성 때문에 PWM 펄스를 어느 정도 평균화해서 반응합니다. 서보 모터는 내부 제어 회로가 펄스 폭을 해석해 위치 명령으로 받아들입니다. 모두 PWM을 쓰지만, "듀티를 높이면 무조건 더 강해진다"는 식으로만 이해하면 서보에서 실수하기 쉽습니다.

PWM 실험에서 자주 만나는 문제

PWM을 처음 실습하면 의외로 단순한 문제에서 오래 막히는 경우가 많습니다. 코드가 맞는데도 모터가 돌지 않거나, 모터는 도는데 속도가 변하지 않거나, 서보가 떨리기만 할 수 있습니다. 이럴 때는 복잡한 이론보다 기본 연결을 하나씩 확인하는 것이 빠릅니다.

증상가능한 원인확인할 것
LED 밝기가 변하지 않음PWM 핀이 아닌 핀 사용보드의 PWM 지원 핀 확인
모터가 전혀 돌지 않음모터 전원 미연결, GND 미공유배터리 전압, 드라이버 GND, 보드 GND 확인
모터가 항상 최대 속도ENA 점퍼가 꽂힘, PWM 핀 연결 오류드라이버 enable 핀이 PWM에 연결됐는지 확인
낮은 PWM에서 모터 떨림출발 토크 부족, 주파수 부적합최소 출발 PWM 기록, 주파수 변경 테스트
보드가 재부팅됨모터 전류로 전압 강하 발생모터 전원 분리, 충분한 배터리와 커패시터 사용
드라이버가 뜨거움전류 용량 부족, 높은 손실드라이버 정격 확인, 방열, 효율 좋은 드라이버 고려
서보가 떨림전원 부족, 펄스 범위 부적절별도 5V 전원, 공통 GND, 펄스 폭 조정

특히 "공통 GND"와 "별도 전원"은 계속 반복해서 강조할 만합니다. 모터와 서보는 전류를 많이 쓰므로 보드의 5V 핀이나 USB 전원에 모든 것을 기대하면 불안정해질 수 있습니다. 반대로 전원을 분리했는데 GND를 공유하지 않으면 PWM 신호의 기준이 없어져 제어가 이상해집니다. 로봇 회로에서 전원은 힘이고, GND는 기준선입니다. 힘을 충분히 공급하고 기준선을 맞추어야 신호가 제대로 전달됩니다.

실전 감각: PWM 값을 그대로 믿지 말기

로봇을 만들 때 PWM 값은 자주 "속도"라는 이름으로 변수에 들어갑니다. 예를 들어 leftSpeed = 150, rightSpeed = 150처럼 쓰면 왼쪽 바퀴와 오른쪽 바퀴가 같은 속도로 돌 것 같습니다. 하지만 실제 로봇은 그렇게 순순하지 않습니다. 왼쪽 모터와 오른쪽 모터의 제조 편차가 있고, 기어 마찰이 다르고, 바퀴가 바닥을 누르는 힘도 다릅니다. 배터리 전압이 떨어지면 둘 다 느려지지만 같은 비율로 느려진다는 보장도 없습니다.

그래서 PWM 기반 주행 로봇을 만들 때는 먼저 "내 로봇의 특성"을 측정해야 합니다. 왼쪽 모터가 출발하는 최소 PWM, 오른쪽 모터가 출발하는 최소 PWM, 직진할 때 어느 쪽으로 휘는지, 배터리가 가득 찼을 때와 줄어들었을 때 차이가 얼마나 나는지 기록합니다. 이 기록이 나중에 엔코더와 PID 제어를 배울 때 귀중한 기준점이 됩니다.

간단한 로봇 자동차에서는 다음과 같은 보정 방법을 쓸 수 있습니다. 예를 들어 오른쪽 바퀴가 항상 더 빠르면 오른쪽 PWM을 조금 낮춥니다.

int baseSpeed = 150;
int leftOffset = 0;
int rightOffset = -10;

analogWrite(leftPwmPin, baseSpeed + leftOffset);
analogWrite(rightPwmPin, baseSpeed + rightOffset);

이 방식은 완벽하지 않습니다. 바닥 상태가 바뀌거나 배터리 전압이 달라지면 다시 어긋날 수 있습니다. 하지만 피드백 센서가 없는 초기 로봇에서는 꽤 유용합니다. 나중에 엔코더를 붙이면 실제 회전량을 측정해 자동으로 보정할 수 있습니다.

PWM을 알면 로봇이 부드러워진다

PWM은 단순히 "속도 조절 함수"가 아닙니다. 로봇을 부드럽고 안전하게 움직이기 위한 기본 언어입니다. 모터를 갑자기 켜면 로봇이 덜컥 출발하고, 배터리 전압이 순간적으로 떨어지고, 기어에 충격이 갑니다. 반대로 PWM 값을 천천히 올리면 로봇이 훨씬 자연스럽게 출발합니다. 서보 모터도 목표 각도를 한 번에 크게 바꾸는 대신 조금씩 이동시키면 팬틸트 카메라나 로봇팔이 더 안정적으로 움직입니다.

예를 들어 서보를 0도에서 180도로 바로 보내는 대신 1도씩 이동시키면 움직임이 부드러워집니다.

for (int angle = 0; angle <= 180; angle++) {
  servo.write(angle);
  delay(10);
}

DC 모터도 마찬가지입니다. 목표 속도가 200이라면 바로 200을 쓰지 말고 현재 PWM 값에서 조금씩 접근하게 만들 수 있습니다.

int currentPwm = 0;
int targetPwm = 200;

void loop() {
  if (currentPwm < targetPwm) currentPwm++;
  if (currentPwm > targetPwm) currentPwm--;

  analogWrite(pwmPin, currentPwm);
  delay(10);
}

이 작은 차이가 실제 로봇에서는 크게 느껴집니다. 로봇이 출발할 때 앞이 들리지 않고, 정지할 때 미끄러짐이 줄고, 기구물이 덜 흔들립니다. 좋은 로봇은 단순히 움직이는 로봇이 아니라, 원하는 대로 예측 가능하게 움직이는 로봇입니다. PWM은 그 첫 단계입니다.

정리

PWM은 디지털 출력으로 아날로그처럼 보이는 제어를 만드는 핵심 기술입니다. 핀 전압을 실제로 중간 전압으로 바꾸는 것이 아니라, 빠른 ON/OFF 신호의 듀티 사이클을 조절해 연결된 장치가 평균 출력처럼 반응하게 만듭니다. LED는 밝기로, DC 모터는 대략적인 속도로, 서보 모터는 펄스 폭에 따른 각도로 반응합니다.

DC 모터를 제어할 때는 반드시 모터 드라이버를 사용해야 합니다. H-Bridge, MOSFET, TB6612 같은 드라이버는 제어 보드의 작은 PWM 신호를 받아 모터에 필요한 큰 전류를 대신 공급합니다. 이때 전류 용량, 역기전력 보호, 공통 GND, 별도 전원 연결이 안정성을 좌우합니다. PWM 주파수도 중요합니다. 너무 낮으면 떨림과 소음이 생기고, 너무 높으면 드라이버의 스위칭 손실과 발열이 증가할 수 있습니다.

서보 모터는 DC 모터 속도 PWM과 구분해야 합니다. 서보에서는 듀티 비율 자체보다 반복 주기와 펄스 폭이 각도를 결정합니다. 보드별 차이도 기억해야 합니다. 아두이노의 analogWrite()는 간단하지만 핀과 기본 주파수가 정해져 있고, ESP32는 LEDC를 통해 채널, 타이머, 해상도, 주파수를 더 유연하게 설정할 수 있습니다.

이번 장에서 PWM을 배웠으니, 이제 로봇은 단순히 켜지고 꺼지는 장치가 아니라 "세기를 조절할 수 있는 장치"가 되었습니다. 다음부터 센서를 다루기 시작하면 로봇은 바깥세상을 읽고, PWM으로 모터와 서보를 조절하며, 조금씩 스스로 반응하는 기계에 가까워질 것입니다.

참고

1How to Control Robot Speed Using PWM in Master Arduino – The Ultimate Guide

2Arduino PWM Motor Speed Control: Complete Guide - From Basics to Advanced Applications

3Servo — ESP-IoT-Solution documentation

4Servo — ESP-IoT-Solution documentation

5ESP32Servo | Arduino Documentation

책 목록

센서와 모터로 시작하는 실전 로봇 만들기

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기