피지컬 AI 좌표계: 같은 물체의 위치가 다르게 보이는 이유
로봇 기준으로 앞쪽 1m인 점에 로봇 원점의 위치만 더하면 같은 점을 가리키지 못할 수 있다. 피지컬 AI 좌표계에서 같은 물체의 위치가 다르게 보이는 이유는 숫자 (1, 0)에 원점 정보만 빠진 것이 아니라 축 방향 정보도 빠져 있기 때문이다. 로봇의 앞쪽 축이 세계의 오른쪽 축과 다르면 방향부터 맞춘 뒤 원점 차이를 더해야 한다.
아래에서는 설명용 로봇과 점을 고정해 놓고, 같은 점을 서로 다른 기준으로 적는다. (x, y), 양수·음수, 간단한 분수만 알면 90도 회전과 이동부터 따라갈 수 있다. ROS 좌표 관례·tf2 문서와 Modern Robotics는 2026년 9월 17일 확인했으며, 공개 산술 예제는 Python 3.12.3에서 실행했다. ROS, 시뮬레이터, 카메라, 실물 로봇은 실행하지 않았다.
좌표는 숫자 두 개가 아니라 기준까지 포함한다
좌표 프레임(coordinate frame)은 위치를 재는 원점과 축 방향의 묶음이다. 좌표(coordinates)는 한 점을 그 프레임의 축으로 재어 적은 숫자다. 단위도 함께 있어야 한다. (1, 0)m라면 “어느 프레임의 x축 방향으로 1m인가”까지 알아야 실제 위치를 해석할 수 있다.
이 글에서는 세계 프레임을 W, 로봇 몸체 프레임을 B, 설명용 평면 센서 프레임을 C라고 부른다. 같은 점도 B에서 잰 숫자와 W에서 잰 숫자가 다를 수 있다. 자를 책상 왼쪽 끝에서 대느냐 오른쪽 끝에서 대느냐에 따라 눈금이 달라지는 것과 비슷하다. 다만 이 비유만으로는 부족하다. 좌표 프레임은 자의 시작점뿐 아니라 자가 향한 방향까지 정하므로, 원점 이동과 축 회전을 모두 계산해야 한다.
계산에서는 다음 약속을 쓴다.
(x, y)는 x축과 y축 방향의 거리를 차례로 적는다.- 90도 반시계 회전은
(x, y) → (-y, x)로 계산할 수 있다. - 이 글의 모든 길이 단위는 미터(m)이며, 모든 점과 변환은 같은 시각에 고정됐다고 가정한다.
앞쪽 1m에 원점만 더하면 왜 틀리는가
설명용 상황을 하나 정하자. W에서 본 B의 원점은 (2, 1)m다. B는 W에 대해 반시계 방향으로 90도 돌아 있다. B 기준 앞쪽 1m인 점은 다음과 같다.
p_B = (1, 0)m
B의 x축, 즉 로봇의 앞쪽은 W의 y축 방향을 향한다. 따라서 먼저 (1, 0)을 90도 돌려 (0, 1)로 바꾼다. 그 뒤 W에서 본 B 원점 (2, 1)을 더한다.
회전: (1, 0) -> (0, 1)
이동: (0, 1) + (2, 1) = (2, 2)
W에서 표현한 정답은 (2, 2)m다. 점이나 로봇을 실제로 움직인 것이 아니다. 같은 점을 B의 축으로는 (1, 0), W의 축으로는 (2, 2)라고 적었을 뿐이다.
원점만 더하면 다음처럼 된다.
(1, 0) + (2, 1) = (3, 1)
(3, 1)은 계산 실수로 우연히 나온 값이 아니라 비교를 위해 의도적으로 만든 오답이다. 이 계산에는 “B의 x축과 W의 x축이 같은 방향이다”라는 숨은 가정이 들어 있다. 실제 조건은 두 축이 90도 어긋나 있으므로 같은 점을 가리키지 않는다. 반대로 B가 회전하지 않았다면 축 방향이 같으므로 (3, 1)m가 맞다. 이동만 더하는 방법은 항상 틀린 것이 아니라, 회전이 없다는 조건에서만 맞는다.
| 같은 점을 적는 기준 | 좌표(m) | x축의 방향 |
|---|---|---|
| 세계 W | (2, 2) | 설명용 평면의 오른쪽 |
| 로봇 B | (1, 0) | W의 y축 양의 방향, 즉 위쪽 |
정확한 원리는 방향을 돌리고 원점을 옮기는 것이다
변환(transform)은 한 프레임의 좌표 표현을 다른 프레임의 표현으로 바꾸는 회전과 이동의 조합이다. 이 글에서는 좌표 성분을 세로로 나열하는 열벡터(column vector) 표기를 쓰고, R_WB를 “B축으로 적은 방향을 W축으로 돌려 적는 회전”, t_WB를 “W에서 표현한 B 원점의 위치”로 정의한다.
p_W = R_WB p_B + t_WB
R_WB p_B가 축 방향을 맞추고, t_WB가 두 원점의 차이를 반영한다. 위 예제에서는 R_WB가 90도 반시계 회전이고 t_WB=(2,1)m다. 아래첨자 표기법은 자료나 라이브러리마다 읽는 관습이 다를 수 있으므로, 실제 코드에서는 기호 모양보다 “어느 프레임의 값을 어느 프레임으로 바꾸는가”를 먼저 선언해야 한다.
회전과 이동을 한 행렬에 묶은 표현이 동차변환행렬(homogeneous transformation matrix)이다. 2D에서는 점 (x, y) 뒤에 1을 붙여 다음처럼 쓸 수 있다.
[ x_W ] [ 0 -1 2 ] [ x_B ]
[ y_W ] = [ 1 0 1 ] [ y_B ]
[ 1 ] [ 0 0 1 ] [ 1 ]
여기서는 길이를 m로 적은 숫자를 넣는다. 첫 두 행은 각각 x_W = -y_B + 2, y_W = x_B + 1을 뜻한다. (x_B, y_B) = (1, 0)을 넣으면 앞서 구한 (2, 2)가 그대로 나온다.
이 행렬은 좌표의 기준을 바꿀 때도, 물체를 실제로 움직이는 변위를 나타낼 때도 사용할 수 있다. 계산 도구가 같다고 의미까지 같지는 않다. 지금 계산한 것은 고정된 점의 표현 변경이며, 모터 명령이나 실제 움직임이 아니다. Modern Robotics의 동차변환 설명도 프레임 표현·기준 변경·변위라는 용도를 구분한다.
역변환은 답을 원래 좌표로 돌려놓는다
앞의 답이 맞는지 확인하려면 변환을 거꾸로 적용하면 된다. W 좌표에서 B 원점을 먼저 빼고, W축으로 적힌 방향을 B축으로 되돌리는 역회전(inverse rotation)을 적용한다. 변환의 역행렬과 합성 원리를 이 예제에 적용한 계산이다.
p_B = R_WB^-1 (p_W - t_WB)
(2, 2) - (2, 1) = (0, 1)
(0, 1)을 -90도 회전 = (1, 0)
원래 값 (1, 0)이 복원됐다. 이동 벡터의 부호만 바꾸면 끝나는 것이 아니다. 원점 차이를 제거한 뒤 어느 축으로 방향을 표현할지도 되돌려야 한다.
오답 (3, 1)에도 같은 검산을 적용해 보자.
(3, 1) - (2, 1) = (1, 0)
(1, 0)을 -90도 회전 = (0, -1)
원래의 (1, 0)이 나오지 않으므로 실패다. “그림으로 보니 그럴듯하다”보다 역변환으로 입력이 복원되는지를 확인하는 편이 명확하다.
센서에서 세계까지 갈 때는 변환 순서가 이어져야 한다
실제 로봇 소프트웨어에서는 센서 C의 점을 로봇 B로, 다시 세계 W로 표현하는 일이 흔하다. 변환 합성(transform composition)은 이렇게 이어지는 변환을 하나로 묶는 계산이다. 열벡터 표기에서는 다음 순서를 쓴다.
T_WC = T_WB T_BC
p_W = T_WC p_C
오른쪽의 T_BC가 먼저 C 좌표를 B 좌표로 바꾸고, 왼쪽의 T_WB가 그 결과를 W 좌표로 바꾼다. 여기서 T는 앞에서 본 회전·이동을 한데 묶은 변환행렬이다. 행렬 곱은 일반적으로 순서를 바꾸면 결과가 달라진다.
이제 C와 B의 축은 평행하고, C 원점이 B의 x축 방향으로 1/5m 떨어졌다고 가정하자. C에서 본 점은 p_C=(1/2, 1/5)m다. 이 수치는 설명을 위해 정한 값이지 실제 센서 장착 측정값이 아니다.
C -> B: (1/2, 1/5) + (1/5, 0) = (7/10, 1/5)
B -> W: (7/10, 1/5)을 90도 회전 + (2, 1)
= (-1/5, 7/10) + (2, 1)
= (9/5, 17/10)
C 원점을 W에서 직접 구하면 (2, 6/5)m다. C의 점을 90도 돌린 (-1/5, 1/2)를 여기에 더해도 (9/5, 17/10)이 나온다. C→B→W 경유값과 C→W 직접 합성값이 정확히 같으므로 순서와 산술을 한 번 더 확인할 수 있다.
공개 산술 예제로 계산을 재현한다
다음 명령은 2026년 9월 17일 Ubuntu와 Python 3.12.3에서 확인했다. 준비물은 Bash, curl, Python 3.12 이상이며 Bash 터미널에서 실행한다. set -euo pipefail은 다운로드가 실패했는데 빈 입력을 받은 Python만 정상 종료해 실패가 가려지는 것을 막는다. 외부 패키지 없이 표준 라이브러리 fractions를 쓰며, 고정 커밋의 공개 코드를 실행한다. 이것은 ROS나 물리 시뮬레이터 실습이 아니라 앞의 정적 2D 산술을 검산하는 코드다.
set -euo pipefail
python3 --version
curl -fsSL 'https://raw.githubusercontent.com/sungpyo9053/blog/1b45bbc1eab1f35249cbc389d9de346d858f3665/editorial/2026-09-16/physical-ai-coordinate-frames/coordinate_example.py' | python3 -
실제 관측한 출력은 다음과 같았다.
Python 3.12.3
point B -> W: (2, 2)
wrong translation-only: (3, 1)
inverse W -> B: (1, 0)
point C -> B: (7/10, 1/5)
point C -> B -> W: (9/5, 17/10)
point C -> W directly: (9/5, 17/10)
전체 명령 블록의 종료 코드는 0이었다. 코드 안의 여섯 assert 문 중 하나라도 어긋나 비정상 종료하거나, 역변환이 (1, 0)을 복원하지 못하거나, 경유값과 직접 합성값이 다르면 실패로 판정한다. 마지막 문장은 세 값의 연쇄 동등 비교다. 공개 코드의 SHA-256은 2de591a309b09eb76fbfaa38de30c5ca7615fc206eca96f6a73877c132577922로 대조했다. Python 3.12.3에서 이 계산이 맞았다는 사실은 로봇·센서 정확도나 다른 환경의 호환성을 보장하지 않는다.
이 코드의 zip 기반 덧셈·뺄셈 함수는 고정된 두 성분 입력용이다. 서로 다른 길이의 벡터를 넣었을 때 오류를 잡는 일반 라이브러리는 아니므로, 입력 검증 없이 실제 좌표 처리 코드에 옮기지 않는다.
세 문제로 적용 조건을 확인한다
문제 1. B가 회전하지 않고 원점만 W의 (2, 1)m에 있다. B 기준 앞쪽 1m인 점의 W 좌표는 무엇인가?
답은 (3, 1)m다. 축이 평행해 회전이 항등 회전, 즉 방향을 바꾸지 않는 R=I이므로 이동만 더해도 된다.
문제 2. W 좌표 (2, 2)m에서 B 원점 (2, 1)m을 빼지 않고 곧바로 -90도 회전해도 되는가?
안 된다. (2, 2)에는 W 원점에서 B 원점까지의 이동이 포함돼 있다. 먼저 원점 차이를 제거해야 B를 기준으로 한 상대 방향만 남는다.
문제 3. T_WB T_BC와 T_BC T_WB는 같은가?
일반적으로 같지 않다. 이 글의 열벡터 표기에서는 점에 가까운 오른쪽 변환부터 적용하며, C→B 다음 B→W처럼 변환 방향이 이어져야 한다. 프레임 표기와 벡터 관례를 바꾸면 식의 겉모양도 달라질 수 있으므로 라이브러리 문서를 먼저 확인해야 한다.
실제 시스템에서는 프레임·단위·방향·시간을 함께 본다
ROS REP 103은 길이에 미터를 쓰고, 오른손 좌표계와 몸체 축 x forward, y left, z up을 표준 관례로 제시한다. 그러나 이는 모든 시스템의 자연법칙이 아니다. 다른 관례를 쓸 수 있으며 그 차이를 문서화해야 한다. ROS 카메라의 _optical 프레임도 z forward, x right, y down이라는 별도 관례를 쓴다. 이 글의 C는 B와 축이 평행한 설명용 평면 센서일 뿐 카메라 화소나 _optical 프레임이 아니다.
ROS 2 tf2는 프레임 관계를 시간에 따라 버퍼링하고, source frame의 데이터를 target frame 표현으로 바꾼다. 따라서 실제 시스템에서는 숫자와 frame_id가 맞더라도 시각이 다르면 같은 장면을 올바르게 합성한 것이 아닐 수 있다. 다만 이 글은 모든 값이 같은 시각에 고정된 경우만 계산했다. Rolling 문서는 계속 바뀌는 개발 브랜치 문서이므로, 여기서는 2026년 9월 17일 확인한 개념만 사용했으며 특정 배포판 API 실행은 검증하지 않았다.
범위 밖의 조건도 분명하다. 일반 각도와 3D 자세, 스케일과 전단, 센서 노이즈, 카메라 보정, 시간 지연은 검증하지 않았다. 특히 카메라 화소를 3D 위치로 바꾸려면 깊이와 카메라 내부 파라미터 등 추가 정보가 필요하므로 이 2D 예제를 그대로 적용할 수 없다.
실물 로봇으로 확장할 때 이 계산을 곧바로 모터 명령에 연결해서는 안 된다. 구동 출력을 비활성화한 상태에서 frame_id, 단위, 축 방향, 변환 방향, timestamp를 먼저 확인해야 한다. 제조사 절차에 맞는 비상 정지 장치와 즉시 전원 또는 구동을 차단할 담당자도 필요하다. 이 글은 해당 안전 조건을 실물에서 검증하지 않았으므로 하드웨어 구동 절차를 제공하지 않는다.
좌표 숫자가 달라졌다고 물체가 움직인 것은 아니다. 같은 점을 다른 프레임으로 표현하려면 방향을 회전으로 맞추고 원점 차이를 이동으로 더해야 한다. 다음 계산에서는 좌표 옆에 프레임·단위·변환 방향을 먼저 적고, 동적 시스템이라면 시각까지 붙여라. 그런 다음 공개 산술 예제를 실행해 역변환 복원과 직접·경유 합성의 일치를 손으로 대조하면 된다.
숫자의 변환을 이해했다면 ROS 2와 tf2의 데이터·좌표 관계 연결에서 그 관계를 누가 공급하고 어느 시각으로 조회하는지 살펴볼 수 있다. 반대로 위치 숫자와 실제 상태의 구분이 낯설다면 관측·행동·피드백의 기초가 출발점이다.
참고 자료
- ROS REP 103: Standard Units of Measure and Coordinate Conventions — SI 단위, 오른손 좌표계, 몸체·카메라 축 관례를 2026년 9월 17일 확인했다.
- Modern Robotics: Homogeneous Transformation Matrices — 회전과 이동, 역변환, 합성, 표현 변경과 변위의 차이를 2026년 9월 17일 확인했다.
- ROS 2 Rolling: tf2 — source/target 변환, 프레임 트리와 시간 버퍼 개념을 2026년 9월 17일 확인했다.
- 공개 예제 검산 기록 — 원 실행 환경과 결과, 의도적 오답 및 검증 한계를 확인했다.
제휴·협찬은 없다.