RPP 컨트롤러
Planner가 "어디로 갈지" 경로를 그었습니다. 이제 그 경로를 실제 바퀴 속도(cmd_vel) 로 바꿔 따라가야 합니다. 그 일을 하는 게 컨트롤러이고, AMR에서 가장 널리 쓰이는 게 Regulated Pure Pursuit(RPP) 입니다.
당근을 쫓는 토끼 — Pure Pursuit
Pure Pursuit의 직관은 단순합니다. 경로 위 앞쪽 한 점(lookahead point) 을 정하고, 그 점을 향해 부드러운 호를 그리며 따라갑니다. lookahead 거리를 늘리면 완만하게, 줄이면 민첩하게 돕니다.
도식 렌더링 중…
"Regulated"가 붙은 이유는, 곡률이 크거나 장애물이 가까우면 속도를 알아서 줄이는 규제(regulation)가 들어가기 때문입니다. 주요 파라미터:
- lookahead_dist — 앞을 얼마나 멀리 볼지
- desired_linear_vel — 목표 직진 속도
- rotate_to_heading — 제자리 회전으로 방향부터 맞출지
Pure Pursuit의 핵심은 곡률 한 줄입니다.
# lookahead 점을 향한 호(arc)의 곡률 → cmd_vel
def pure_pursuit(lookahead_pt, lookahead_dist, desired_v):
y = lookahead_pt.y # 로봇 좌표계의 횡방향 오차
curvature = 2 * y / lookahead_dist**2 # 그 점을 지나는 원호
return desired_v, desired_v * curvature # (vx, wz)
회전이 0.05 rad/s에 갇혔다 — odom 클램프 버그
여기서 우리가 겪은 까다로운 버그가 있습니다. 로봇이 회전해야 하는데 각속도가 0.05 rad/s에 영구히 갇혀 거의 안 도는 현상이었습니다.
처음엔 velocity_smoother, 바퀴 구동, 콜백 등을 의심했지만 전부 빗나갔습니다. 토픽 사슬 곳곳에 로그를 심어 1회 실행하니(instrument-first) 범인은 RPP 자신이었습니다 — RPP가 odom의 현재 각속도를 읽어 가속을 제한(clamp) 하는데, 물리 시뮬의 odom이 한 틱 지연되면서 "현재 각속도≈0"으로 읽혀, 가속 한계(max_angular_accel)가 너무 낮으면 0.05를 못 벗어나는 악순환에 빠진 것이었습니다.
RPP 각속도 — odom 피드백 클램프 버그. 목표 1.0 rad/s인데 가속한계 1.0(빨강)에선 0.05에 갇히고, 3.2로 올리면(시안) 목표까지 도달한다.
해결은 가속 한계를 현실적인 값으로 올리는 것이었습니다(max_angular_accel 1.0 → 3.2). 위 차트처럼, 같은 명령인데 한 파라미터로 회전이 살아났습니다.
⚠️ 시뮬 odom 지연을 잊지 말 것 — closed-loop 컨트롤러가 odom 피드백으로 가속을 제한할 때, 시뮬레이션 물리의 한 틱 지연이 실로봇과 다른 거동을 만들 수 있습니다. "코드만 읽고 추측" 대신 토픽 사슬에 로그를 심어 어디서 값이 0.05로 갇히는지 데이터로 확인한 게 해결의 열쇠였습니다.
💡 RPP의 출력은 곧장 바퀴로 가지 않고 velocity_smoother를 거쳐 가감속이 매끄럽게 다듬어집니다. 컨트롤러–smoother–DriveAPI로 이어지는 사슬을 기억해 두면 디버깅이 쉽습니다.
한 줄 정리
📌 RPP는 경로 앞 lookahead 점을 호로 쫓아 cmd_vel을 만드는 컨트롤러다(곡률·장애물에 따라 속도 규제). 시뮬에서 odom 가속 클램프가 회전을 0.05에 가둘 수 있으며, 범인은 추측이 아니라 토픽 사슬 로깅으로 잡았다.
