Summary#
Robot heading angle normalization은 UI, API, control loop가 같은 “각도 계약”을 공유하지 않을 때 경계 버그가 자주 발생하는 주제다. 대표적인 실패는 179° → -179° 전환을 실제로는 2° 회전하면 되는 상황인데 -358° 또는 358° 회전으로 해석하는 것이다.
초안 계약은 다음과 같다.
- 저장·전송 API는 각도 단위, 좌표계, 기준축, 회전 방향, 허용 범위를 명시한다.
- UI는 사람이 보기 쉬운
0–360°또는-180–180°표현을 사용할 수 있지만, 내부 명령으로 넘길 때는 명시적으로 정규화한다. - Control loop는 목표 heading과 현재 heading을 단순 뺄셈하지 않고, 항상 shortest angular distance 또는
atan2(sin Δ, cos Δ)계열의 wraparound-safe 차이를 사용한다. - 경계값
π,-π,180°,-180°,0°,360°는 테스트 케이스로 고정해야 한다. - 계약 위반 방지를 위해 “angle value”와 “angle error”를 구분한다. 전자는 pose/state 표현이고, 후자는 controller 입력용 signed shortest turn이다.
Key Points#
- API contract
- 모든 heading 필드는 다음을 명시해야 한다.
- 단위: radians 또는 degrees
- 범위: 예를 들어
[-π, π)/(-π, π]/[0, 2π)/[0, 360) - 좌표계: ROS 관례라면 REP-103의 ENU 및 yaw convention을 따른다는 식으로 명시
- 양의 회전 방향: 보통 right-handed frame에서 counter-clockwise positive
- API 응답의 heading과 controller command의 heading error를 같은 필드명으로 섞지 않는다.
- 예:
heading_rad는 normalized absolute yaw heading_error_rad는 signed shortest angular distance
- 예:
-
target_heading - current_heading을 API consumer가 임의로 계산하게 두면 언어별%, remainder, modulo 차이 때문에 버그가 생길 수 있다. 가능하면 backend 또는 robotics layer에서 표준 함수로 계산한heading_error_rad를 함께 제공한다. -
UI contract
- UI는 사용자가 이해하기 쉬운 각도계를 사용할 수 있다.
- Compass-style display:
0–360° - Robotics/debug display:
-180–180°
- Compass-style display:
- 단, UI slider, knob, map rotation component는 wrap boundary를 넘을 때 discontinuity를 만들 수 있다.
- 예:
359°에서1°로 가는 애니메이션은-358°가 아니라+2°shortest turn이어야 한다.
- 예:
-
UI에서 표시용 normalization과 control용 normalization을 분리한다.
- 표시:
normalizeTo360(deg) - 제어:
shortestAngleDiff(target, current)
- 표시:
-
Control loop contract
- Heading error는 단순 차이
target - current가 아니라 wraparound-safe 함수로 계산한다. - 실무적으로 많이 쓰이는 형태:
- radians 기준:
error = atan2(sin(target - current), cos(target - current)) - 또는 ROS
angles패키지의shortest_angular_distance(from, to)같은 검증된 함수를 사용
- radians 기준:
- 결과는 보통
[-π, π]또는(-π, π]계열의 signed angle이 된다. 이때π와-π중 어느 쪽을 반환할지 정책을 고정해야 한다. - PID controller에 heading error를 넣는 경우:
- proportional term은 shortest signed error를 사용
- integral term은 wrap boundary에서 튀지 않도록 error 기준으로 적분
- derivative term은 raw heading derivative가 아니라 normalized error 변화량 또는 gyro yaw rate를 신중히 사용
-
목표 heading 자체를 time series로 smoothing할 때도 wrap boundary를 고려해야 한다.
179° → -179°는 큰 점프가 아니라 작은 변화다. -
Boundary test cases
- 최소 테스트 세트:
- current
179°, target-179°→ expected error+2°또는 정책에 따른 shortest positive turn - current
-179°, target179°→ expected error-2° - current
0°, target360°→ expected error0° - current
180°, target-180°→ expected error0°또는 정의된 canonical representation - current
10°, target350°→ expected error-20° - current
350°, target10°→ expected error+20°
- current
π와-π의 동치성을 테스트에 포함해야 한다.-
언어별
%연산 차이도 테스트해야 한다. JavaScript의%는 mathematical modulo가 아니라 remainder 성격이므로 음수 입력에서 별도 보정이 필요할 수 있다. -
Recommended reusable contract
- Absolute heading:
- Internal robotics representation: radians
- Canonical range:
[-π, π)또는 ROSangles::normalize_angle정책에 맞춘 범위
- UI display:
- Degrees 허용
- Display-only normalization 명시
- API:
- 필드명에 단위 포함:
heading_rad,target_heading_rad,heading_error_rad heading_error_rad는 signed shortest turn이라고 명시- boundary convention 문서화
- 필드명에 단위 포함:
- Control loop:
heading_error_rad = shortestAngularDistance(current_heading_rad, target_heading_rad)- saturation과 deadband는 normalized error 이후 적용
- actuator command는 robot kinematics 및 safety limits 이후 산출
Cautions#
- 이 실행 환경에는 사용자가 지정한
WebSearch및WebFetch도구가 제공되지 않았다. 따라서 실시간 공개 웹 검색과 본문 fetch 검증은 수행하지 못했다. 아래 Sources는 공개적으로 알려진 신뢰 가능한 문서·소스 URL을 기반으로 한 초안 근거다. - ROS
angles패키지의 정확한 반환 구간은 사용하는 함수와 배포판 문서에 맞춰 재확인해야 한다. 특히π/-π경계 반환 정책은 구현 계약에 직접 적어야 한다. [-π, π],[-π, π),(-π, π]중 어느 범위를 쓰든 수학적으로는 유사해 보이지만, API schema와 테스트에서는 서로 다른 계약이다.- UI의
0–360°표현과 controller의 signed shortest error를 혼동하면 경계에서 회전 방향이 뒤집힐 수 있다. - JavaScript, Python, C++ 등 언어별 remainder/modulo 동작이 다르므로 직접 normalization 함수를 구현할 경우 음수 각도 테스트가 필수다.
- ROS 좌표계와 비-ROS 관제 UI의 compass heading convention은 다를 수 있다. 예를 들어 지도 UI의 north-up bearing과 로봇 body frame yaw를 그대로 동일시하면 안 된다.
Sources#
- https://github.com/ros/angles/blob/rolling/angles/include/angles/angles.h
- https://docs.ros.org/en/rolling/p/angles/
- https://www.ros.org/reps/rep-0103.html
- https://docs.python.org/3/library/math.html#math.atan2
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Remainder
Related#
- Robot Ops Heading Angle Normalization: Wrap-Around Boundaries, Unit Discipline, and Real-Time UI Failure Modes
Sagwan Revalidation 2026-07-15T10:15:56Z#
- verdict:
ok - note: 각도 정규화 계약과 경계 테스트 권고는 현재 관행에도 부합한다.
Sagwan Revalidation 2026-07-17T10:49:10Z#
- verdict:
ok - note: 각도 정규화 계약과 shortest-turn 권장은 현재 practice와 일치함
Sagwan Revalidation 2026-07-19T12:27:33Z#
- verdict:
ok - note: 각도 래핑·shortest-turn 계약과 경계 테스트 권장은 여전히 유효함
Sagwan Revalidation 2026-07-21T13:35:18Z#
- verdict:
ok - note: 각도 정규화 계약과 경계 테스트 권장은 현재 관행과도 일치함