[Physical AI W2D2 · 3/7]
1·2편에서 잡은 정운동학·역운동학·작업공간·자코비안 개념을 GCP VM 터미널에서 Python으로 직접 계산해 봅니다.
2축 평면 로봇 팔로 FK·IK를 구현하고, 두 개의 해(elbow-up/down)·도달 불가능한 위치·관절 제한·특이점까지 7개의 .py 파일로 눈으로 확인합니다.
이 글에서 직접 해보는 것
- GCP VM 터미널에 Python 실습 환경 준비(
~/robot_kinematics_lab)- 정운동학: 관절각 θ1·θ2 → 말단 위치 x·y 계산(
forward_kinematics.py)- 역운동학: 목표 위치 x·y → 관절각 θ1·θ2 계산, 두 개의 해(elbow-up/down) 확인(
inverse_kinematics.py)- 작업 공간: 도달 가능/불가능 판정(
workspace_check.py), 검증(FK↔IK 왕복,verify_kinematics.py·batch_kinematics_test.py)- 관절 제한(
joint_limit_check.py)·특이점/자코비안(singularity_check.py)까지 7개 파일
(1편에서 정운동학을, 2편에서 역운동학·작업공간·자코비안·특이점을 개념으로 다뤘다면, 이번 3편은 그 수식을 Python으로 직접 계산해 손으로 확인하는 단계입니다. 시각화는 4편에서 RViz2로 이어집니다.)
들어가며 — 수식을 "실행"해 본다
1·2편에서 정운동학(관절각 → 위치)과 역운동학(위치 → 관절각)의 공식을 종이 위에서 따라가 봤습니다. 이번 실습은 그 공식을 Python으로 옮겨 실행합니다.
RViz2나 Gazebo 같은 시각화 도구는 쓰지 않습니다.
터미널에서 수식 계산 결과를 숫자로 확인하면서, 관절각이 말단 장치 위치로 어떻게 변환되는지, 반대로 목표 위치가 관절각으로 어떻게 변환되는지를 손으로 이해하는 게 목적입니다.
사용하는 로봇은 가장 기본적인 2축 평면 로봇 팔입니다.
두 개의 회전 관절과 두 개의 링크로 구성됩니다.
구조는 단순하지만 정운동학·역운동학·작업 공간·해의 존재 여부·elbow-up/elbow-down 해를 이해하기에 가장 적합한 모델입니다.
💡 이번 실습의 목표 7가지 — ① 터미널 Python 환경 준비 ② 정운동학 공식 구현 ③ 관절각 → x·y 확인 ④ 목표 위치 → 관절각(역운동학) ⑤ 같은 목표에 두 가지 관절 해가 존재함을 확인 ⑥ 도달 불가능한 목표는 해가 없음을 확인 ⑦ 정운동학·역운동학 결과가 서로 일치하는지 검증.

1. 실습 구조 — 무엇을 만드는가
이번 실습에서는 다음 순서로 진행합니다. 먼저 기본 4개 파일의 역할을 잡고, 뒤로 가며 검증·관절 제한·특이점 파일을 추가합니다.
~/robot_kinematics_lab
├── forward_kinematics.py
├── inverse_kinematics.py
├── verify_kinematics.py
└── workspace_check.py
각 파일의 역할은 다음과 같습니다.
forward_kinematics.py— 관절각 θ1, θ2를 입력받아 말단 장치의 x, y 위치를 계산한다.inverse_kinematics.py— 목표 위치 x, y를 입력받아 가능한 관절각 θ1, θ2를 계산한다.verify_kinematics.py— 역운동학으로 구한 관절각을 다시 정운동학에 넣어 목표 위치와 일치하는지 검증한다.workspace_check.py— 목표 위치가 로봇 팔의 작업 공간 안에 있는지 확인한다.
2. 실습 디렉터리 생성 + Python 확인
GCP VM 터미널에서 실습 디렉터리를 생성합니다.
mkdir -p ~/robot_kinematics_lab
cd ~/robot_kinematics_lab
현재 위치를 확인합니다.
pwd
예상 출력은 다음과 비슷합니다.
/home/사용자명/robot_kinematics_lab
이어서 Python이 설치되어 있는지 확인합니다.
python3 --version
예상 출력은 다음과 비슷합니다.
Python 3.10.x
만약 Python이 설치되어 있지 않다면 다음 명령으로 설치합니다.
sudo apt update -y
sudo apt install -y python3
다시 버전을 확인합니다.
python3 --version
💡 이번 실습의 모든 계산은 표준 라이브러리
math만으로 충분합니다. 추가 패키지 설치 없이 바로 시작할 수 있습니다.
3. 정운동학 공식 확인
2축 평면 로봇 팔의 링크 길이를 다음과 같이 둡니다.
L1 = 1.0
L2 = 1.0
첫 번째 관절각을 θ1, 두 번째 관절각을 θ2라고 합니다. 정운동학 공식은 다음과 같습니다.
x = L1 cos(θ1) + L2 cos(θ1 + θ2)
y = L1 sin(θ1) + L2 sin(θ1 + θ2)
이 공식은 관절각이 주어졌을 때 로봇 팔 끝의 위치를 계산합니다.
⚠️ 각도 단위 함정 — Python의 삼각함수는 각도를 도(degree) 단위가 아니라 라디안(radian) 단위로 계산합니다.
따라서 30도, 45도, 90도 같은 값을 사용할 때는 라디안으로 변환해야 합니다.
radian = degree × π / 180
Python에서는 다음 함수를 사용합니다.
math.radians(각도)
4. 정운동학 파일 작성
다음 파일을 작성합니다.
nano forward_kinematics.py
아래 내용을 입력합니다.
import math
L1 = 1.0
L2 = 1.0
theta1_deg = 30
theta2_deg = 45
theta1 = math.radians(theta1_deg)
theta2 = math.radians(theta2_deg)
x = L1 * math.cos(theta1) + L2 * math.cos(theta1 + theta2)
y = L1 * math.sin(theta1) + L2 * math.sin(theta1 + theta2)
print("=== Forward Kinematics ===")
print(f"L1 = {L1}")
print(f"L2 = {L2}")
print(f"theta1 = {theta1_deg} deg")
print(f"theta2 = {theta2_deg} deg")
print(f"x = {x:.4f}")
print(f"y = {y:.4f}")
저장 후 종료합니다. nano에서는 다음 순서로 저장합니다.
Ctrl + O
Enter
Ctrl + X
무엇을 — 관절각을 도 단위로 정한 뒤 math.radians()로 라디안 변환하고, 정운동학 공식 그대로 x·y를 계산합니다. 왜 — 코드가 공식과 1:1로 대응하는지 먼저 눈으로 확인하기 위해서입니다.
5. 정운동학 실행
작성한 파일을 실행합니다.
python3 forward_kinematics.py
예상 출력은 다음과 비슷합니다.
=== Forward Kinematics ===
L1 = 1.0
L2 = 1.0
theta1 = 30 deg
theta2 = 45 deg
x = 1.1248
y = 1.4659
이 결과는 첫 번째 링크가 30도 회전하고, 두 번째 링크가 첫 번째 링크 기준으로 추가 45도 회전했을 때 말단 장치가 대략 x = 1.1248, y = 1.4659 위치에 있다는 뜻입니다.
여기서 중요한 점은 θ2가 절대각이 아니라 첫 번째 링크 기준의 상대각이라는 점입니다. 두 번째 링크의 실제 방향은 다음과 같습니다.
theta1 + theta2 = 30도 + 45도 = 75도
따라서 두 번째 링크는 전체 좌표계 기준으로 75도 방향을 향합니다.
6. 정운동학 값 변경 실습
파일을 다시 열어 관절각을 바꿔가며 직관을 잡습니다.
nano forward_kinematics.py
관절각을 다음과 같이 수정합니다.
theta1_deg = 0
theta2_deg = 0
다시 실행합니다.
python3 forward_kinematics.py
예상 출력은 다음과 같습니다.
x = 2.0000
y = 0.0000
이 결과는 두 링크가 모두 x축 방향으로 완전히 펴졌다는 뜻입니다.
다시 관절각을 다음과 같이 수정합니다.
theta1_deg = 90
theta2_deg = 0
실행합니다.
python3 forward_kinematics.py
예상 출력은 다음과 비슷합니다.
x = 0.0000
y = 2.0000
이 결과는 두 링크가 모두 y축 방향으로 세워졌다는 뜻입니다.
이번에는 다음과 같이 수정합니다.
theta1_deg = 90
theta2_deg = -90
실행합니다.
python3 forward_kinematics.py
예상 출력은 다음과 비슷합니다.
x = 1.0000
y = 1.0000
이 경우 첫 번째 링크는 위쪽을 향하고, 두 번째 링크는 오른쪽을 향합니다. 따라서 말단 장치는 x = 1, y = 1 위치에 놓입니다.
💡 정운동학 실습 정리 — 관절각이 바뀌면 말단 장치 위치가 바뀐다. 첫 번째 관절은 전체 로봇 팔 방향에 큰 영향을 준다.
두 번째 관절은 첫 번째 링크 끝을 기준으로 두 번째 링크 방향을 결정한다.
2축 평면 로봇 팔에서 말단 장치 위치는 두 링크의 위치 기여를 합산하여 계산한다.
Python에서 삼각함수를 사용할 때는 도 단위를 라디안으로 변환해야 한다.
7. 역운동학 공식 확인
역운동학은 목표 위치 x, y가 주어졌을 때 관절각 θ1, θ2를 구하는 과정입니다. 2축 평면 로봇 팔의 역운동학에서는 먼저 목표점까지의 거리를 계산합니다.
r² = x² + y²
이후 코사인 법칙을 사용해 θ2를 구합니다.
cos(θ2) = (x² + y² - L1² - L2²) / (2L1L2)
그리고 θ1은 다음 식으로 구합니다.
θ1 = atan2(y, x) - atan2(L2 sin(θ2), L1 + L2 cos(θ2))
여기서 atan2는 단순한 arctan보다 안전한 각도 계산 함수입니다. x와 y의 부호를 함께 고려하기 때문에 목표점이 어느 사분면에 있는지도 정확하게 처리할 수 있습니다.
💡 두 개의 해(elbow-up / elbow-down) — 역운동학에서는 같은 목표 위치에 대해 두 가지 해가 나올 수 있습니다.
하나는 elbow-up 해, 다른 하나는 elbow-down 해입니다.
2축 팔에서는 θ2를 양수로 잡는 경우와 음수로 잡는 경우가 각각 다른 팔꿈치 자세를 만듭니다.
8. 역운동학 파일 작성
다음 파일을 작성합니다.
nano inverse_kinematics.py
아래 내용을 입력합니다.
import math
L1 = 1.0
L2 = 1.0
target_x = 1.0
target_y = 1.0
r2 = target_x**2 + target_y**2
cos_theta2 = (r2 - L1**2 - L2**2) / (2 * L1 * L2)
print("=== Inverse Kinematics ===")
print(f"Target x = {target_x}")
print(f"Target y = {target_y}")
if cos_theta2 < -1 or cos_theta2 > 1:
print("No solution: target is outside the workspace")
else:
theta2_a = math.acos(cos_theta2)
theta2_b = -math.acos(cos_theta2)
theta1_a = math.atan2(target_y, target_x) - math.atan2(
L2 * math.sin(theta2_a),
L1 + L2 * math.cos(theta2_a)
)
theta1_b = math.atan2(target_y, target_x) - math.atan2(
L2 * math.sin(theta2_b),
L1 + L2 * math.cos(theta2_b)
)
print()
print("Solution A")
print(f"theta1 = {math.degrees(theta1_a):.2f} deg")
print(f"theta2 = {math.degrees(theta2_a):.2f} deg")
print()
print("Solution B")
print(f"theta1 = {math.degrees(theta1_b):.2f} deg")
print(f"theta2 = {math.degrees(theta2_b):.2f} deg")
저장 후 종료합니다.
Ctrl + O
Enter
Ctrl + X
무엇을 — cos_theta2를 먼저 계산하고, 그 값이 [-1, 1] 범위를 벗어나면 "해 없음"으로 처리합니다. 범위 안이면 +acos(Solution A)와 -acos(Solution B) 두 갈래로 θ2를 잡고, 각각에 맞는 θ1을 atan2로 계산합니다.
왜 — 한 목표 위치에 elbow-up/elbow-down 두 해가 동시에 나오는지 확인하기 위해서입니다.
9. 역운동학 실행
파일을 실행합니다.
python3 inverse_kinematics.py
예상 출력은 다음과 비슷합니다.
=== Inverse Kinematics ===
Target x = 1.0
Target y = 1.0
Solution A
theta1 = 0.00 deg
theta2 = 90.00 deg
Solution B
theta1 = 90.00 deg
theta2 = -90.00 deg
이 결과는 목표 위치 x = 1, y = 1에 도달하는 방법이 두 가지 있다는 뜻입니다.
Solution A는 첫 번째 링크가 x축 방향을 향하고, 두 번째 링크가 위쪽으로 꺾이는 자세입니다.
Solution B는 첫 번째 링크가 y축 방향을 향하고, 두 번째 링크가 오른쪽으로 꺾이는 자세입니다.
두 자세는 관절각은 다르지만 말단 장치 위치는 같습니다.
이것이 역운동학에서 해가 여러 개 존재할 수 있다는 대표적인 예입니다.
10. 역운동학 목표 위치 변경
파일을 다시 엽니다.
nano inverse_kinematics.py
목표 위치를 다음과 같이 수정합니다.
target_x = 2.0
target_y = 0.0
실행합니다.
python3 inverse_kinematics.py
예상 출력은 다음과 비슷합니다.
Solution A
theta1 = 0.00 deg
theta2 = 0.00 deg
Solution B
theta1 = 0.00 deg
theta2 = -0.00 deg
이 위치는 두 링크가 완전히 펴진 상태입니다. 이 경우 두 해가 사실상 같은 자세가 됩니다.
이번에는 목표 위치를 다음과 같이 수정합니다.
target_x = 0.0
target_y = 2.0
실행합니다.
python3 inverse_kinematics.py
예상 출력은 다음과 비슷합니다.
theta1 = 90.00 deg
theta2 = 0.00 deg
이 위치는 두 링크가 위쪽으로 완전히 펴진 상태입니다.
11. 도달 불가능한 위치 확인
이번에는 목표 위치를 로봇 팔이 도달할 수 없는 곳으로 설정합니다.
nano inverse_kinematics.py
목표 위치를 다음과 같이 수정합니다.
target_x = 3.0
target_y = 0.0
실행합니다.
python3 inverse_kinematics.py
예상 출력은 다음과 같습니다.
No solution: target is outside the workspace
⚠️ 도달 불가능한 위치 — 현재 링크 길이는
L1 = 1.0,L2 = 1.0이므로 최대 도달 거리는L1 + L2 = 2.0입니다.
목표 위치 x = 3.0, y = 0.0은 원점에서 3m 떨어져 있어 로봇 팔의 최대 도달 거리보다 멀기 때문에 역운동학 해가 존재하지 않습니다.
코드에서cos_theta2가 1을 넘어가는 것이 이 상황을 잡아냅니다.
12. 작업 공간 확인 파일 작성
목표 위치가 작업 공간 안에 있는지 확인하는 파일을 작성합니다.
nano workspace_check.py
아래 내용을 입력합니다.
import math
L1 = 1.0
L2 = 1.0
targets = [
(1.0, 1.0),
(2.0, 0.0),
(0.0, 2.0),
(3.0, 0.0),
(0.2, 0.0)
]
max_reach = L1 + L2
min_reach = abs(L1 - L2)
print("=== Workspace Check ===")
print(f"L1 = {L1}")
print(f"L2 = {L2}")
print(f"min reach = {min_reach}")
print(f"max reach = {max_reach}")
print()
for x, y in targets:
distance = math.sqrt(x**2 + y**2)
if min_reach <= distance <= max_reach:
result = "reachable"
else:
result = "not reachable"
print(f"target=({x:.1f}, {y:.1f}), distance={distance:.4f}, result={result}")
저장 후 종료합니다.
Ctrl + O
Enter
Ctrl + X
무엇을 — 목표점까지의 거리를 구해 min_reach ≤ distance ≤ max_reach 범위 안인지로 도달 가능 여부를 판정합니다. 왜 — 역운동학을 풀기 전에 "애초에 닿을 수 있는 위치인가"를 빠르게 걸러내기 위해서입니다.
13. 작업 공간 확인 실행
파일을 실행합니다.
python3 workspace_check.py
예상 출력은 다음과 비슷합니다.
=== Workspace Check ===
L1 = 1.0
L2 = 1.0
min reach = 0.0
max reach = 2.0
target=(1.0, 1.0), distance=1.4142, result=reachable
target=(2.0, 0.0), distance=2.0000, result=reachable
target=(0.0, 2.0), distance=2.0000, result=reachable
target=(3.0, 0.0), distance=3.0000, result=not reachable
target=(0.2, 0.0), distance=0.2000, result=reachable
이 결과는 각 목표점이 로봇의 작업 공간 안에 있는지 보여줍니다. 현재는 L1과 L2가 모두 1.0이므로 최소 도달 거리는 0이고 최대 도달 거리는 2입니다. 따라서 원점에서 2m 이내의 위치는 대부분 도달 가능합니다.
14. 링크 길이가 다른 경우 확인
링크 길이가 서로 다르면 내부에 도달할 수 없는 영역이 생길 수 있습니다. 예를 들어 첫 번째 링크가 1.5m이고 두 번째 링크가 0.5m라면 최소 도달 거리는 다음과 같습니다.
|L1 - L2| = |1.5 - 0.5| = 1.0
이 경우 원점에서 1m보다 가까운 위치는 도달할 수 없습니다. 파일을 다시 엽니다.
nano workspace_check.py
링크 길이를 다음과 같이 수정합니다.
L1 = 1.5
L2 = 0.5
실행합니다.
python3 workspace_check.py
예상 출력은 다음과 비슷합니다.
min reach = 1.0
max reach = 2.0
⚠️ 안쪽 도달 불가 영역 — 이제 목표점 중 원점에 너무 가까운 위치는 도달 불가능할 수 있습니다. 이것은 로봇 팔이 너무 가까운 안쪽 영역으로는 접혀 들어갈 수 없기 때문입니다(
min_reach = |L1 - L2|).
15. 역운동학 검증 파일 작성
이제 역운동학으로 구한 관절각을 다시 정운동학에 넣어 목표 위치와 일치하는지 확인합니다. 다음 파일을 작성합니다.
nano verify_kinematics.py
아래 내용을 입력합니다.
import math
L1 = 1.0
L2 = 1.0
target_x = 1.0
target_y = 1.0
def forward_kinematics(theta1, theta2):
x = L1 * math.cos(theta1) + L2 * math.cos(theta1 + theta2)
y = L1 * math.sin(theta1) + L2 * math.sin(theta1 + theta2)
return x, y
def inverse_kinematics(x, y):
r2 = x**2 + y**2
cos_theta2 = (r2 - L1**2 - L2**2) / (2 * L1 * L2)
if cos_theta2 < -1 or cos_theta2 > 1:
return None
theta2_a = math.acos(cos_theta2)
theta2_b = -math.acos(cos_theta2)
theta1_a = math.atan2(y, x) - math.atan2(
L2 * math.sin(theta2_a),
L1 + L2 * math.cos(theta2_a)
)
theta1_b = math.atan2(y, x) - math.atan2(
L2 * math.sin(theta2_b),
L1 + L2 * math.cos(theta2_b)
)
return (theta1_a, theta2_a), (theta1_b, theta2_b)
solutions = inverse_kinematics(target_x, target_y)
print("=== Verify Kinematics ===")
print(f"Target position: x={target_x}, y={target_y}")
print()
if solutions is None:
print("No inverse kinematics solution")
else:
for index, (theta1, theta2) in enumerate(solutions, start=1):
x, y = forward_kinematics(theta1, theta2)
error_x = target_x - x
error_y = target_y - y
error = math.sqrt(error_x**2 + error_y**2)
print(f"Solution {index}")
print(f"theta1 = {math.degrees(theta1):.2f} deg")
print(f"theta2 = {math.degrees(theta2):.2f} deg")
print(f"forward x = {x:.4f}")
print(f"forward y = {y:.4f}")
print(f"error = {error:.8f}")
print()
저장 후 종료합니다.
Ctrl + O
Enter
Ctrl + X
무엇을 — 정운동학과 역운동학을 함수로 만든 뒤, 역운동학으로 구한 각 해를 다시 정운동학에 넣어 원래 목표 위치와의 오차(error)를 계산합니다. 왜 — 역운동학 결과가 진짜 맞는지는 정운동학으로 되짚어야 확인되기 때문입니다.
16. 역운동학 검증 실행
파일을 실행합니다.
python3 verify_kinematics.py
예상 출력은 다음과 비슷합니다.
=== Verify Kinematics ===
Target position: x=1.0, y=1.0
Solution 1
theta1 = 0.00 deg
theta2 = 90.00 deg
forward x = 1.0000
forward y = 1.0000
error = 0.00000000
Solution 2
theta1 = 90.00 deg
theta2 = -90.00 deg
forward x = 1.0000
forward y = 1.0000
error = 0.00000000
이 결과는 역운동학으로 구한 두 관절 해가 모두 목표 위치에 정확히 도달한다는 것을 보여줍니다(error = 0). 여기서 중요한 구조는 다음과 같습니다.
- 역운동학으로 θ1, θ2를 계산한다.
- 계산된 θ1, θ2를 정운동학에 다시 넣는다.
- 정운동학 결과가 원래 목표 위치와 일치하는지 확인한다.
💡 이 검증 과정은 로봇 제어에서 매우 중요합니다. 역운동학 결과가 올바른지 확인하려면 반드시 정운동학으로 다시 계산해 보는 습관이 필요합니다.
17. 여러 목표 위치를 한 번에 검증하기
이번에는 여러 목표 위치에 대해 역운동학과 정운동학 검증을 한 번에 수행합니다. 다음 파일을 작성합니다.
nano batch_kinematics_test.py
아래 내용을 입력합니다.
import math
L1 = 1.0
L2 = 1.0
targets = [
(1.0, 1.0),
(2.0, 0.0),
(0.0, 2.0),
(1.5, 0.5),
(3.0, 0.0)
]
def forward_kinematics(theta1, theta2):
x = L1 * math.cos(theta1) + L2 * math.cos(theta1 + theta2)
y = L1 * math.sin(theta1) + L2 * math.sin(theta1 + theta2)
return x, y
def inverse_kinematics(x, y):
r2 = x**2 + y**2
cos_theta2 = (r2 - L1**2 - L2**2) / (2 * L1 * L2)
if cos_theta2 < -1 or cos_theta2 > 1:
return None
theta2_a = math.acos(cos_theta2)
theta2_b = -math.acos(cos_theta2)
theta1_a = math.atan2(y, x) - math.atan2(
L2 * math.sin(theta2_a),
L1 + L2 * math.cos(theta2_a)
)
theta1_b = math.atan2(y, x) - math.atan2(
L2 * math.sin(theta2_b),
L1 + L2 * math.cos(theta2_b)
)
return (theta1_a, theta2_a), (theta1_b, theta2_b)
print("=== Batch Kinematics Test ===")
print()
for target_x, target_y in targets:
print(f"Target: x={target_x}, y={target_y}")
solutions = inverse_kinematics(target_x, target_y)
if solutions is None:
print("result: no solution")
print()
continue
for index, (theta1, theta2) in enumerate(solutions, start=1):
x, y = forward_kinematics(theta1, theta2)
error = math.sqrt((target_x - x)**2 + (target_y - y)**2)
print(f" Solution {index}")
print(f" theta1 = {math.degrees(theta1):.2f} deg")
print(f" theta2 = {math.degrees(theta2):.2f} deg")
print(f" forward = ({x:.4f}, {y:.4f})")
print(f" error = {error:.8f}")
print()
저장 후 종료합니다.
Ctrl + O
Enter
Ctrl + X
무엇을 — 여러 목표 위치를 리스트로 두고, 각각에 대해 역운동학 → 정운동학 검증을 반복합니다. 도달 불가능한 목표는 result: no solution으로 건너뜁니다. 왜 — 한 번에 여러 케이스를 돌려 "해가 둘", "해가 사실상 하나", "해 없음"을 한눈에 비교하기 위해서입니다.
18. 여러 목표 위치 검증 실행
파일을 실행합니다.
python3 batch_kinematics_test.py
예상 출력은 다음과 비슷합니다.
=== Batch Kinematics Test ===
Target: x=1.0, y=1.0
Solution 1
theta1 = 0.00 deg
theta2 = 90.00 deg
forward = (1.0000, 1.0000)
error = 0.00000000
Solution 2
theta1 = 90.00 deg
theta2 = -90.00 deg
forward = (1.0000, 1.0000)
error = 0.00000000
Target: x=2.0, y=0.0
Solution 1
theta1 = 0.00 deg
theta2 = 0.00 deg
forward = (2.0000, 0.0000)
error = 0.00000000
Solution 2
theta1 = 0.00 deg
theta2 = -0.00 deg
forward = (2.0000, 0.0000)
error = 0.00000000
Target: x=3.0, y=0.0
result: no solution
이 실습은 역운동학이 항상 해를 갖는 것이 아니라는 점을 명확히 보여줍니다. 목표 위치가 작업 공간 안에 있으면 해가 계산되고, 작업 공간 밖에 있으면 해가 존재하지 않습니다.
19. 관절 제한 조건 추가
실제 로봇은 관절을 무한히 움직일 수 없습니다. 각 관절에는 최소각과 최대각이 존재합니다. 예를 들어 다음과 같이 제한할 수 있습니다.
theta1: -90도 이상 90도 이하
theta2: -135도 이상 135도 이하
⚠️ 수학적 해 ≠ 실제 사용 가능한 해 — 역운동학 해가 수학적으로 존재하더라도 이 범위를 벗어나면 실제 로봇에서는 사용할 수 없습니다.
관절 제한을 확인하는 파일을 작성합니다.
nano joint_limit_check.py
아래 내용을 입력합니다.
import math
L1 = 1.0
L2 = 1.0
target_x = -1.0
target_y = 1.0
theta1_min = -90
theta1_max = 90
theta2_min = -135
theta2_max = 135
def is_within_limit(theta1_deg, theta2_deg):
theta1_ok = theta1_min <= theta1_deg <= theta1_max
theta2_ok = theta2_min <= theta2_deg <= theta2_max
return theta1_ok and theta2_ok
def inverse_kinematics(x, y):
r2 = x**2 + y**2
cos_theta2 = (r2 - L1**2 - L2**2) / (2 * L1 * L2)
if cos_theta2 < -1 or cos_theta2 > 1:
return None
theta2_a = math.acos(cos_theta2)
theta2_b = -math.acos(cos_theta2)
theta1_a = math.atan2(y, x) - math.atan2(
L2 * math.sin(theta2_a),
L1 + L2 * math.cos(theta2_a)
)
theta1_b = math.atan2(y, x) - math.atan2(
L2 * math.sin(theta2_b),
L1 + L2 * math.cos(theta2_b)
)
return (theta1_a, theta2_a), (theta1_b, theta2_b)
print("=== Joint Limit Check ===")
print(f"Target x = {target_x}")
print(f"Target y = {target_y}")
print()
solutions = inverse_kinematics(target_x, target_y)
if solutions is None:
print("No solution")
else:
for index, (theta1, theta2) in enumerate(solutions, start=1):
theta1_deg = math.degrees(theta1)
theta2_deg = math.degrees(theta2)
valid = is_within_limit(theta1_deg, theta2_deg)
print(f"Solution {index}")
print(f"theta1 = {theta1_deg:.2f} deg")
print(f"theta2 = {theta2_deg:.2f} deg")
print(f"within joint limit = {valid}")
print()
저장 후 종료합니다.
Ctrl + O
Enter
Ctrl + X
무엇을 — 역운동학으로 구한 각 해의 θ1·θ2가 미리 정한 관절 범위 안에 들어오는지를 is_within_limit()으로 판정합니다. 왜 — 같은 목표 위치라도 어떤 해는 실제 로봇이 쓸 수 있고, 어떤 해는 쓸 수 없음을 보이기 위해서입니다.
20. 관절 제한 조건 실행
파일을 실행합니다.
python3 joint_limit_check.py
예상 출력은 다음과 비슷합니다.
=== Joint Limit Check ===
Target x = -1.0
Target y = 1.0
Solution 1
theta1 = 90.00 deg
theta2 = 90.00 deg
within joint limit = True
Solution 2
theta1 = 180.00 deg
theta2 = -90.00 deg
within joint limit = False
이 결과는 두 개의 역운동학 해 중 하나만 실제 관절 제한 안에 들어온다는 것을 보여줍니다(Solution 2의 θ1 = 180도는 ±90도 범위를 벗어남). 실제 로봇 제어에서는 단순히 역운동학 해를 구하는 것만으로 충분하지 않습니다. 반드시 관절 제한, 속도 제한, 충돌 가능성, 작업 방향 등을 함께 고려해야 합니다.
21. 특이점 근처 확인
2축 평면 로봇 팔에서는 두 링크가 완전히 펴진 자세가 대표적인 특이점입니다. 예를 들어 목표 위치가 다음과 같을 때 로봇 팔은 완전히 펴집니다.
x = 2.0
y = 0.0
이 상태에서는 팔이 특정 방향으로 움직이기 어려워집니다. 간단한 자코비안 행렬을 계산하여 특이점 여부를 확인할 수 있습니다. 2축 평면 로봇 팔의 자코비안은 다음과 같습니다.
J11 = -L1 sin(θ1) - L2 sin(θ1 + θ2)
J12 = -L2 sin(θ1 + θ2)
J21 = L1 cos(θ1) + L2 cos(θ1 + θ2)
J22 = L2 cos(θ1 + θ2)
💡 특이점 판정 — 2x2 자코비안의 행렬식(determinant)이 0에 가까우면 특이점에 가깝다고 볼 수 있습니다.
22. 자코비안 특이점 확인 파일 작성
다음 파일을 작성합니다.
nano singularity_check.py
아래 내용을 입력합니다.
import math
L1 = 1.0
L2 = 1.0
test_angles = [
(0, 0),
(30, 45),
(90, -90),
(0, 180)
]
def jacobian(theta1, theta2):
j11 = -L1 * math.sin(theta1) - L2 * math.sin(theta1 + theta2)
j12 = -L2 * math.sin(theta1 + theta2)
j21 = L1 * math.cos(theta1) + L2 * math.cos(theta1 + theta2)
j22 = L2 * math.cos(theta1 + theta2)
return j11, j12, j21, j22
def determinant_2x2(j11, j12, j21, j22):
return j11 * j22 - j12 * j21
print("=== Singularity Check ===")
print()
for theta1_deg, theta2_deg in test_angles:
theta1 = math.radians(theta1_deg)
theta2 = math.radians(theta2_deg)
j11, j12, j21, j22 = jacobian(theta1, theta2)
det = determinant_2x2(j11, j12, j21, j22)
if abs(det) < 1e-6:
result = "singular"
else:
result = "not singular"
print(f"theta1={theta1_deg:>6.1f} deg, theta2={theta2_deg:>6.1f} deg")
print(f"determinant = {det:.8f}")
print(f"result = {result}")
print()
저장 후 종료합니다.
Ctrl + O
Enter
Ctrl + X
무엇을 — 여러 관절각에 대해 자코비안 4개 성분을 구하고, 행렬식 j11*j22 - j12*j21을 계산합니다. 절댓값이 1e-6보다 작으면 singular로 판정합니다. 왜 — 어떤 자세가 운동 능력을 잃는 특이점인지 숫자로 확인하기 위해서입니다.
23. 자코비안 특이점 확인 실행
파일을 실행합니다.
python3 singularity_check.py
예상 출력은 다음과 비슷합니다.
=== Singularity Check ===
theta1= 0.0 deg, theta2= 0.0 deg
determinant = 0.00000000
result = singular
theta1= 30.0 deg, theta2= 45.0 deg
determinant = 0.70710678
result = not singular
theta1= 90.0 deg, theta2= -90.0 deg
determinant = -1.00000000
result = not singular
theta1= 0.0 deg, theta2= 180.0 deg
determinant = 0.00000000
result = singular
⚠️ 특이점 자세 — 이 결과에서 θ2가 0도이거나 180도인 경우 특이점이 됩니다. θ2 = 0도는 두 링크가 완전히 펴진 상태, θ2 = 180도는 두 링크가 완전히 접힌 상태입니다. 두 경우 모두 자코비안 행렬식이 0에 가까워지고, 로봇은 특정 방향의 운동 능력을 잃습니다.
24. 전체 파일 목록 확인
지금까지 작성한 파일을 확인합니다.
ls -al
예상 출력은 다음과 비슷합니다.
forward_kinematics.py
inverse_kinematics.py
workspace_check.py
verify_kinematics.py
batch_kinematics_test.py
joint_limit_check.py
singularity_check.py
각 파일의 실행 명령은 다음과 같습니다.
python3 forward_kinematics.py
python3 inverse_kinematics.py
python3 workspace_check.py
python3 verify_kinematics.py
python3 batch_kinematics_test.py
python3 joint_limit_check.py
python3 singularity_check.py
25. 실습 결과 정리
이번 실습에서 확인한 내용은 다음과 같습니다.
- 정운동학은 관절각을 입력으로 받아 말단 장치 위치를 계산한다.
- 2축 평면 로봇 팔의 정운동학은 삼각함수로 계산할 수 있다.
- 역운동학은 목표 위치를 입력으로 받아 가능한 관절각을 계산한다.
- 같은 목표 위치에 대해 두 가지 관절 해가 존재할 수 있다.
- 목표 위치가 작업 공간 밖에 있으면 역운동학 해가 존재하지 않는다.
- 역운동학 결과는 다시 정운동학으로 검증할 수 있다.
- 실제 로봇에서는 관절 제한 조건을 반드시 확인해야 한다.
- 자코비안 행렬식이 0에 가까우면 특이점에 가까운 상태이다.
26. 확인 과제
직접 다음을 풀어보며 개념을 굳혀 봅니다.
- 다음 조건을 사용하여 정운동학 결과를 확인한다. 말단 장치의 x, y 위치를 계산한다.
L1 = 1.0
L2 = 1.0
theta1 = 45도
theta2 = 45도
- 다음 목표 위치에 대해 역운동학 해를 구한다. 두 개의 해가 나오는지 확인한다. 역운동학으로 구한 해를 다시 정운동학에 넣어 목표 위치와 일치하는지 확인한다.
target_x = 1.5
target_y = 0.5
- 다음 목표 위치가 도달 가능한지 확인한다.
target_x = 2.5
target_y = 0.0
- 다음 관절각이 특이점인지 확인한다.
theta1 = 45도
theta2 = 0도
3편 정리
- GCP VM 터미널에서
math만으로 2축 평면 로봇 팔의 정운동학·역운동학을 직접 계산했다. - 정운동학: 관절각 → x·y(
forward_kinematics.py). θ2는 첫 링크 기준 상대각. - 역운동학: x·y → 관절각, 두 개의 해(elbow-up/down). 작업 공간 밖이면 해 없음.
- 검증: 역운동학 해를 정운동학에 되넣어 error ≈ 0 확인(
verify·batch). - 현실 제약: 관절 제한으로 한 해만 사용 가능, 자코비안 행렬식 ≈ 0이면 특이점.
다음 편 예고
이번 편은 숫자로만 확인했습니다. 4편에서는 이 정운동학·역운동학 계산 결과를 RViz2 화면으로 띄웁니다. 계산한 관절각·말단 위치가 화면 위 로봇 팔로 어떻게 그려지는지(FK/IK → 화면) 직접 봅니다.
시리즈 목차
- [Physical AI W2D2] 1/7 — 운동학 개념 ①: 정운동학(링크·관절·자유도·2축 팔·동차변환행렬)
- [Physical AI W2D2] 2/7 — 운동학 개념 ②: 역운동학·작업공간·자코비안·특이점
- [Physical AI W2D2] 3/7 — 정운동학·역운동학 Python 실습(직접 계산·검증·관절제한·특이점) — 이번 글
- [Physical AI W2D2] 4/7 — 계산 결과를 RViz2로 시각화(FK/IK → 화면)
- [Physical AI W2D2] 5/7 — MoveIt 개념 ①: 경로계획 핵심(Planning Group·Scene·Collision·Trajectory)
- [Physical AI W2D2] 6/7 — MoveIt 개념 ②: 계획 기법·Move Group·ROS 2 Control
- [Physical AI W2D2] 7/7 — MoveIt 경로계획 RViz2 시각화 실습(+ 오류 점검 7종)
'피지컬AI' 카테고리의 다른 글
| [Physical AI W2D2] 5/7 — MoveIt 개념 ①: 경로계획 핵심(Planning Group·Scene·Collision·Trajectory) (0) | 2026.06.21 |
|---|---|
| [Physical AI W2D2] 4/7 — 계산 결과를 RViz2로 시각화(FK/IK → 화면) (0) | 2026.06.21 |
| [Physical AI W2D2] 2/7 — 운동학 개념 ②: 역운동학·작업공간·자코비안·특이점 (0) | 2026.06.21 |
| [Physical AI W2D2] 1/7 — 운동학 개념 ①: 정운동학(링크·관절·자유도·2축 팔·동차변환행렬) (0) | 2026.06.21 |
| [Physical AI W1D2] 톺아보기 — ROS 2 통신·패키지 20문제 완전 풀이 (0) | 2026.06.20 |