3.16. 서보 제어¶
취미용(RC) 서보는 폐루프 위치 제어가 내장된, 밀봉된 케이스 안의 작은 기어드 모터입니다. 케이스 안에는 DC 모터, 감속 기어박스, 출력 축에 연결된 가변 저항기, 그리고 가변 저항기의 측정값을 외부에서 들어오는 설정값과 비교하는 작은 드라이버 보드가 들어 있습니다. 드라이버는 오차를 줄이는 방향으로 모터를 돌리고, 위치가 일치하면 멈춥니다. 카메라 쪽에서는 그 어느 것도 보이지 않습니다 – 그저 서보에게 어디로 가야 하는지 알려주기만 하면 됩니다.
3.16.1. PWM 신호¶
서보는 설정값을 고정된 50 Hz 프레임 속도의 PWM 신호로 받으며, 여기서 펄스 폭이 위치를 선택합니다:
1.0 ms 펄스는 축을 이동 범위의 한쪽 끝으로 구동합니다.
1.5 ms 펄스는 축을 중앙에 둡니다.
2.0 ms 펄스는 축을 다른 쪽 끝으로 구동합니다.
그 사이의 값은 중간 위치에 매핑됩니다.
서보의 PWM 프레임은 20 ms 길이이며, 펄스 폭(1.0 – 2.0 ms)이 위치를 선택합니다.¶
LED나 모터와 달리, 서보는 PWM을 평균하지 않습니다. 펄스 폭 자체가 명령입니다: 서보의 내부 로직은 각 펄스를 측정하여 그에 따라 목표를 설정하고, 출력이 일치할 때까지 모터를 돌립니다. 분수로 나타낸 듀티 사이클(전체 범위에서 5 %에서 10 % 사이)은 부수적인 것이며 – 중요한 것은 절대 펄스 폭이고, 이것이 소프트웨어가 제어해야 하는 대상입니다.
3.16.2. 배선¶
취미용 서보는 3선 커넥터를 사용합니다:
Power (보통 빨간색): 서보 자체의 전원으로, 일반적으로 4.8 V에서 6 V입니다. 서보에 카메라의 3.3 V 레일로 전원을 공급하지 마세요. 스톨 전류를 공급할 수 없어 레일에 브라운아웃이 발생합니다.
Ground (보통 검은색 또는 갈색): 서보 전원의 귀환 경로로, 카메라의 그라운드에 연결되어 신호도 공통 기준을 공유하게 됩니다.
Signal (보통 흰색, 노란색 또는 주황색): 카메라의 GPIO에서 나오는 PWM 라인입니다.
3.16.3. 코드¶
duty_u16()도 작동하지만, 이는 듀티를 주기에 대한 분수로 설정합니다 – 절대 펄스 폭이 중요하고 주기가 고정된 신호에는 다루기 불편합니다. duty_ns()는 펄스 폭을 나노초 단위로 직접 설정합니다:
from machine import PWM, Pin
servo = PWM(Pin("P7"), freq=50, duty_ns=1_500_000) # centre
반송파는 50 Hz(20 ms 주기)이며, 각 사이클의 하이 시간은 정확히 1500 µs입니다. 작은 헬퍼 함수가 위치-펄스 매핑을 명시적으로 만들어줍니다:
def set_position(angle):
# angle: 0..180 degrees mapped to 1.0..2.0 ms
pulse_us = 1000 + (angle * 1000) // 180
servo.duty_ns(pulse_us * 1000)
set_position(0) # full one way
set_position(90) # centre
set_position(180) # full the other way
범위 전체에 걸친 느린 스윕:
import time
for angle in range(0, 181, 5):
set_position(angle)
time.sleep_ms(20)
for angle in range(180, -1, -5):
set_position(angle)
time.sleep_ms(20)
1.0 – 2.0 ms 범위가 표준이지만, 많은 서보는 전체 이동을 위해 더 넓은 범위(흔히 500 µs에서 2500 µs)를 허용합니다. 서보의 데이터시트에 정확한 펄스 폭 한계가 나와 있으며, 그 범위를 벗어난 값은 모터를 기계적 정지점에 부딪치게 할 수 있습니다.
비표준 범위를 가진 서보의 경우, 한계값을 상수로 빼내고 매핑을 매개변수화하십시오:
PULSE_MIN_US = 500 # full one way (from the data sheet)
PULSE_MAX_US = 2500 # full the other way
def set_position(angle):
span_us = PULSE_MAX_US - PULSE_MIN_US
pulse_us = PULSE_MIN_US + (angle * span_us) // 180
servo.duty_ns(pulse_us * 1000)