ROS2 Turtlesim 주행 제어기 시리즈 — Bang-Bang·PID·Dual PID·State Machine
초록
본 자료는 ROS2 turtlesim을 대상으로 특정 위치·각도에 멈추는 제어기를 단계적으로 구현한 4개 강의(Docs 25~28)를 정리한다. Bang-Bang 제어의 한계에서 출발해 PID 각도 제어, 벡터 내적 기반 Dual PID 위치 제어, 유한 상태 머신(State Machine) 기반 주행 제어기까지를 다루며, 각 단계에서 발생하는 제어 문제와 해결 전략을 설명한다.
1. Bang-Bang 제어와 각도 정규화 (Doc 25)
1.1 목표
turtlesim의 turtle1/pose를 구독해 현재 각도를 읽고, goal_theta 토픽으로 받은 목표 각도로 회전시킨다.
1.2 각도 오차 계산의 함정
단순 뺄셈 error = θ_target - θ_current는 순환(wraparound) 문제를 유발한다.
예: 현재 30°, 목표 330° → error = 300° → CCW 300° 회전
실제 최단 경로는 CW 60° 회전!
해결책: atan2 정규화
error = math.atan2(math.sin(error), math.cos(error))
# 결과: 항상 -π ~ π 범위, 자동으로 최단 경로 보장
1.3 Bang-Bang 제어
if abs(error) > self.tolerance:
angular.z = +angular_speed if error > 0 else -angular_speed
else:
angular.z = 0.0
한계: tolerance 근처에서 지터(Jitter) 발생.
| Tolerance | 정밀도 | 안정성 |
|---|---|---|
| 큰 값 (0.1 rad) | 낮음 | 안정 |
| 작은 값 (0.01 rad) | 높음 | 지터 발생 |
→ PID 제어기 필요
2. PID 각도 제어기 (Doc 26)
2.1 PID 개념
| 요소 | 수식 | 역할 |
|---|---|---|
| P (비례) | Kp × e(t) | 현재 오차에 비례한 교정 |
| I (적분) | Ki × ∫e(t)dt | 정상상태 오차 제거 |
| D (미분) | Kd × de(t)/dt | 오버슈트 억제 |
2.2 PID 클래스 (control_apps.py)
class PID:
def update(self, state):
self.dt = time.time() - self.pre_time
state_D = (state - self.pre_state) / self.dt if self.dt > 0 else 0
state_I = state + self.integrated_state
out = self.P * state + self.D * state_D + self.I * state_I * self.dt
# Saturation
out = max(self.min_state, min(self.max_state, out))
self.pre_state = state
self.integrated_state = state_I
self.pre_time = time.time()
return out
2.3 PID 튜닝 가이드
| 튜닝 | 효과 | 부작용 |
|---|---|---|
| P 증가 | 응답 속도 향상 | 오버슈트, 진동 가능 |
| I 증가 | 정상상태 오차 제거 | 오버슈트, 적분 와인드업 |
| D 증가 | 오버슈트 감소·안정화 | 노이즈 민감 |
P=1.0만으로도 지수적 수렴 가능 (1차 시스템 특성).
3. Dual PID 위치 제어기 (Doc 27)
3.1 Multi Input 제어 개념
2D 평면 위치 제어는 두 개의 제어 입력이 필요하다.
- 선속도(linear): 목표까지의 거리 오차 제어
- 각속도(angular): 목표 방향으로의 회전 제어
3.2 벡터 내적을 활용한 Signed Distance
유클리드 거리는 항상 양수이므로 전진/후진 판단이 불가하다.
벡터 내적으로 부호 있는 거리(signed distance)를 구하면 방향 정보를 포함한다.
dx = goal.x - current.x
dy = goal.y - current.y
# 현재 방향 벡터 h = (cosθ, sinθ)와 오차 벡터 e = (dx, dy)의 내적
distance_error = dx * math.cos(theta) + dy * math.sin(theta)
# 양수 → 목표가 앞쪽 → 전진
# 음수 → 목표가 뒤쪽 → 후진
3.3 각도 오차 계산
desired_heading = math.atan2(goal.y - current.y, goal.x - current.x)
angle_error = normalize_angle(desired_heading - current_pose.theta)
# normalize_angle = atan2(sin(angle), cos(angle))
3.4 PyQt5 모니터링 GUI
TurtleMonitor 클래스는 matplotlib + PyQt5로 실시간 위치·목표 추적 GUI를 제공한다.
turtle map에서 마우스 클릭으로 goal_pose 토픽을 발행하여 대화형 제어가 가능하다.
4. State Machine 기반 주행 제어기 (Doc 28)
4.1 이전 방식의 한계
선속도·각속도 두 PID를 동시에 적용하면 방향이 잘못된 상태에서 전진하는 문제가 발생한다.
4.2 삼단계 주행 전략
State 1: rotate_to_goal → 목표 방향으로 회전
State 2: move_to_goal → 목표 위치까지 직진
State 3: rotate_to_final → 최종 목표 각도로 회전
4.3 State Machine 개념
| 구성 요소 | 설명 |
|---|---|
| State | 시스템이 현재 있는 상태 |
| Transition | 상태 전이 조건 |
| Action | 각 상태에서 수행하는 동작 |
if-else 중첩 대비 장점: 각 상태의 동작이 명확히 분리되어 유지보수가 용이하다.
4.4 핵심 상태 전이
rotate_to_goal:
abs(heading_error) < angle_tol → move_to_goal으로 전이
move_to_goal:
abs(distance_error) < dist_tol → rotate_to_final으로 전이
rotate_to_final:
abs(final_angle_error) < angle_tol → DONE
전체 시리즈 학습 포인트
- atan2 정규화: 각도 오차는 반드시
atan2(sin, cos)로 정규화 → 최단 경로 보장 - Bang-Bang → PID: tolerance 딜레마를 PID 비례 제어로 해결
- Signed Distance: 벡터 내적으로 전진/후진 방향 자동 판별
- State Machine: 복잡한 주행 시나리오를 명확한 상태로 분리하여 제어
출처: PinkLAB 강의자료 Docs 25~28. 정리: Moojoco (hb5u), 2026-06-22.
