ROS 로봇 속도 단위가 1000배 어긋나는 순간: mm/s를 m/s로 검증하기
linear.x에 숫자를 넣기 전에 확인할 것
ROS 메시지의 linear.x에 소수 하나를 넣는 코드는 숫자만 보면 간단하다. 그러나 그 숫자가 밀리미터 기준인지 미터 기준인지는 자료형에 나타나지 않는다. ROS 로봇 속도 단위 오류를 찾으려면, 메시지가 전달됐는지보다 장치에서 받은 값에 어디서 단위를 붙이고 바꿨는지를 먼저 살펴야 한다.
한 축의 이동거리와 속도·시간의 곱셈을 이용하면 이 변환을 작은 테스트로 분리할 수 있다. 여기서는 실제 장치를 움직이지 않고, 공식 메시지 정의와 설명용 산술 대조군으로 어떤 입력을 함께 검사해야 하는지 정한다.
ROS 2 Rolling의 geometry_msgs/Twist는 속도를 선형 linear 성분과 각 angular 성분으로 나눈다. 여기서 선속도(linear velocity)는 한 축을 따라 어느 방향으로 얼마나 빠르게 움직이는지를 나타내는 값이다. 각 성분을 담는 Vector3에는 float64 x, y, z가 있고, 단위를 함께 운반하는 필드는 없다. 0.25와 250은 모두 정상적인 float64다. 직렬화와 토픽 전달이 성공했다는 사실만으로 어느 쪽이 의도한 m/s 값인지는 알 수 없다.
그렇다면 수신자는 무엇을 믿고 단위를 해석할까. ROS REP-103은 ROS 내부의 표준 단위로 SI를 택하며 길이에 metre, 시간에 second를 사용한다. 표준 ROS 인터페이스의 선속도는 그 조합인 m/s로 해석한다. 다만 REP-103은 정당하고 문서화된 예외도 허용한다. 장치의 원시 API까지 무조건 m/s라고 추정하지 말고 제조사 명세와 드라이버 문서를 따로 확인해야 한다.
송신자와 수신자가 숫자의 물리량과 단위에 합의한 규칙이 단위 계약(unit contract)이다. 메시지 필드와 이 규칙을 함께 읽어야 숫자의 뜻이 정해진다.
변환 위치를 정할 때 SI 입력도 남겨 둔다
장치별 값이 ROS 내부 표현으로 들어오는 접점을 입력 경계(input boundary)라고 한다. 이곳에서 원래 단위를 확인해 m/s로 한 번만 정규화하면, 이후 계산에서 변환 여부를 다시 추측할 필요가 줄어든다. 개념적인 함수는 normalize_linear_velocity(value, source_unit) -> m/s처럼 값과 출처 단위를 함께 받게 설계할 수 있다. 단위를 알 수 없거나 지원하지 않으면 숫자를 추정해서 반환하지 않고 오류로 처리한다.
이 설계의 테스트에는 두 정상 입력이 필요하다. 설명용 장치가 250 mm/s를 보낸다고 가정하자. 이것 하나만 넣어 보면 /1000 누락은 찾을 수 있지만, 모든 입력에 무조건 /1000을 적용하는 구현도 통과할 수 있다. 이미 SI인 0.25 m/s도 함께 넣어야 두 번째 입력을 잘못 줄이는지 드러난다. 특정 제품에서 이 값이 관측됐거나 실제 오류가 발생했다는 뜻은 아니다.
계산에서는 로봇이 한 축에서 일정한 속도로 움직인다고 가정한다. 제어 주기(control period)는 제어 계산을 한 번 갱신하는 시간 간격이며, 여기서는 Δt를 정확히 0.4초로 둔다. 이동거리 증분 Δx는 속도 v와 이 시간의 곱이다.
Δx = v × Δt
두 입력은 같은 속도이므로 둘 다 같은 0.1 m를 내야 한다. 실패 출력까지 적어 두면 어느 변환을 살펴야 하는지도 분명해진다.
| 사례 | 입력 계약 | 0.4초 예상 거리 | 실패했을 때의 의미 |
|---|---|---|---|
| A | 250 mm/s |
0.1 m |
100 m면 mm/s→m/s 변환 누락 |
| B | 0.25 m/s |
0.1 m |
0.0001 m면 이미 SI인 값에 /1000 중복 적용 |
| C | 단위 없음 또는 미지원 단위 | 숫자 대신 명시적 오류 | 숫자를 추정 변환하거나 기본 단위를 조용히 적용함 |

그림은 입력 단위를 확인한 뒤 변환할지 그대로 둘지를 나누는 흐름이다. 거리 계산 곳곳에 보정값을 덧붙이는 대신, 단위별 분기와 Δx = vΔt 계산을 분리한다.
출력이 크면 누락을, 너무 작으면 중복을 찾는다
국제도량형국(BIPM)의 SI 접두어 표에서 milli는 10^-3이다. 1 mm = 0.001 m이므로 A의 올바른 계산은 다음과 같다. 공식 자료와 계산은 2026년 9월 20일에 확인했다.
v = 250 mm/s × (1 m / 1000 mm) = 0.25 m/s
Δx = 0.25 m/s × 0.4 s = 0.1 m
A가 100 m를 냈다면 원시 숫자 250을 곧바로 m/s로 읽었는지 본다.
Δx_wrong = 250 m/s × 0.4 s = 100 m
error factor = 100 m / 0.1 m = 1000
absolute difference = 99.9 m
반대로 B가 너무 작아졌다면 어떤가. 이미 0.25 m/s인 입력에 /1000을 한 번 더 적용하면 0.4초 거리는 0.25 / 1000 × 0.4 = 0.0001 m다. 예상한 0.1 m의 1/1000이다. 이것이 비-SI 사례 하나만 통과시키는 테스트를 피해야 하는 이유다. A와 B가 모두 틀리면 변환 위치뿐 아니라 시간 단위와 거리 계산을 각각 확인한다. C가 조용히 숫자를 반환하면 입력 단위가 빠졌음을 탐지하지 못한 것이다.
입력 숫자가 달라져도 판단은 유지되는지 확인해 보자. 500 mm/s가 0.2초 유지된다면 거리는 얼마여야 할까? 500 / 1000 = 0.5 m/s이므로 0.5 m/s × 0.2 s = 0.1 m다. 숫자 500을 m/s로 읽으면 100 m가 되어 역시 1000배다.
다만 모든 단위 오류가 1000배인 것은 아니다. 80 cm/s를 0.5초 유지하는 경우에는 어떤가? centi는 10^-2이므로 80 cm/s = 0.8 m/s이고 올바른 거리는 0.4 m다. 변환 없이 숫자 80을 m/s로 읽으면 40 m로 100배가 된다. 접두어가 바뀌면 검사에서 기대할 오류 배율도 달라진다. 이 거리들은 일정 속도 모형의 산술 결과이지 실제 주행 기록이 아니다.
공개 검산이 확인한 것과 아직 시험하지 않은 것
공개된 산술 예제 코드는 변환 누락·중복의 대조를 math.isclose assertion으로 검사한다. Ubuntu 기반 환경의 Python 3.12.3에서 2026년 9월 20일 KST에 고정 커밋 코드를 재실행한 실제 출력은 다음과 같았고, 종료 코드는 0이었다.
correct_distance_m=0.1
wrong_distance_m=100.0
error_factor=1000.0
cm_correct_distance_m=0.4
cm_wrong_distance_m=40.0
si_distance_m=0.1
double_converted_si_distance_m=0.0001
기대값, 실제값, 실행 명령과 한계는 공개 검증 기록에서도 확인할 수 있다. math.isclose의 허용오차는 부동소수점 산술 비교를 위한 값이다. 실물 로봇의 허용 위치 오차로 가져다 쓰면 안 된다. 또한 공개 코드는 계산식의 대조군이며, 앞서 제안한 단위별 함수와 C의 오류 처리를 구현한 ROS 어댑터는 아니다.
이번에는 ROS 노드, 시뮬레이터, 물리 엔진, 실제 하드웨어를 실행하지 않았다. 1차원 일정 속도 모형에는 가감속, 회전, 바퀴 미끄럼, 제어 주기 지터, 통신 지연, 엔코더 분해능과 누적 오도메트리 오차도 들어 있지 않다. 오도메트리(odometry)는 로봇의 위치와 속도를 추정하는 과정이다. nav_msgs/Odometry에서는 pose와 twist가 서로 다른 지정 좌표계에 표현될 수 있으므로 단위와 좌표 프레임은 별도 항목으로 검사해야 한다.
실제 어댑터를 확인할 때에는 격리된 ROS 노드나 bag 입력으로 변환 전후의 토픽 값, timestamp, frame, 실제 Δt를 봐야 한다. 속도 제한을 둔 가상 로봇의 command와 odometry 흐름을 확인하는 시뮬레이션도 별도 검증이며, 그 결과가 실물 성능을 입증하지는 않는다.
실물 시험은 제조사 문서와 기구·제어기 제한을 먼저 확인한 뒤에만 계획한다. 구동 바퀴를 지면에서 분리한 시험대 또는 제조사가 허용한 안전 상태를 사용하고, 사람과 장애물을 작업 구역에서 치워야 한다. 낮은 속도 제한, 손에 닿는 독립적인 비상정지와 전원 차단 수단을 사전에 확인한다. 명령이 끊겼을 때 멈추는 timeout/watchdog, 명시적인 0 속도 명령, controller stop 또는 disable의 순서도 해당 장치 문서에 맞춰 정해야 한다. 중지 방법을 사전에 확인할 수 없다면 시험을 시작하지 않는다.
ROS 2 Rolling의 diff_drive_controller 문서는 cmd_vel_timeout을 stale 명령 판정 시간으로 설명한다. 오래되어 더 이상 유효하게 쓰지 않을 명령을 가려내는 설정이지, 물리적 비상정지나 안전 인증을 대신하는 기능은 아니다(문서 확인: 2026-09-20). 이 글에는 제휴·협찬이 없다.
변환 함수의 통과 조건을 정한다
자신의 장치 명세에서 원래 단위를 확인한 뒤, 비-SI 입력과 이미 SI인 입력을 같은 테스트에 추가한다. 두 입력이 같은 물리량으로 수렴해야 하고, 단위가 없는 입력은 조용히 통과해서는 안 된다. 250 mm/s 한 사례가 맞는 것과 변환 함수가 맞는 것은 다른 판단이다. 숫자를 더 보정하기 전에, 그대로 두어야 할 입력까지 함께 통과하는지 확인하는 것이 이 테스트의 끝이다.