개요
이전 포스트에서 IK(역기구학)가 무엇인지 알아보았습니다. 이번에는 실제로 IK를 어떻게 구하는지, 즉 ROS 2 + MoveIt 환경에서 IK 솔버를 설정하고 사용하는 구체적인 방법을 다룹니다.
핵심: 원하는 XYZ 좌표를 관절 각도로 변환하는 과정이 IK이며, ROS에서는 KDL 솔버가 이 계산을 수행한다.
KDL 솔버란
**KDL (Kinematics and Dynamics Library)**은 Orocos 프로젝트에서 개발한 운동학/동역학 라이브러리입니다. MoveIt의 기본 IK 솔버로 탑재되어 있으며, 수치적(numerical) 방법으로 IK를 풀어냅니다.
| 구분 | 해석적(Analytical) IK | 수치적(Numerical) IK |
|---|---|---|
| 방법 | 수학 공식으로 직접 해를 계산 | 반복 계산으로 해에 수렴 |
| 속도 | 매우 빠름 | 상대적으로 느림 |
| 범용성 | 특정 로봇 구조에만 적용 | 임의의 로봇에 적용 가능 |
| 해 보장 | 모든 해를 구할 수 있음 | 초기값에 따라 다른 해 수렴 |
| 대표 솔버 | IKFast | KDL, TRAC-IK |
KDL은 범용성이 큰 장점입니다. URDF만 있으면 어떤 로봇이든 IK를 풀 수 있습니다.
Newton-Raphson 반복법
KDL 솔버의 핵심 알고리즘은 Newton-Raphson 반복법입니다. 현재 관절 상태에서 목표까지의 오차를 계산하고, 이를 점진적으로 줄여나가는 방식입니다.
현재 관절 각도 에서 목표 위치 까지의 오차를 다음과 같이 정의합니다:
여기서 는 정기구학(FK) 함수입니다. 관절 각도 업데이트는 **야코비안(Jacobian)**의 유사역행렬을 이용합니다:
- : 야코비안의 유사역행렬 (pseudoinverse)
- 이 과정을 (수렴 조건)이 될 때까지 반복합니다.
flowchart TD
A["초기 추정값 q₀ (시드 상태)"] --> B["FK 계산: x = f(q)"]
B --> C{"오차 Δx = x_d - f(q)\n충분히 작은가?"}
C -->|"‖Δx‖ < ε"| D["수렴 완료\n관절 각도 q 반환"]
C -->|"‖Δx‖ ≥ ε"| E["야코비안 J(q) 계산"]
E --> F["q = q + J†·Δx"]
F --> G{"반복 횟수 초과?"}
G -->|"아니오"| B
G -->|"예"| H["실패: 해를 찾지 못함"]Tip: Newton-Raphson은 **초기값(시드)**에 민감합니다. 좋은 시드를 주면 빠르게 수렴하지만, 나쁜 시드를 주면 수렴하지 못하거나 원치 않는 해에 도달할 수 있습니다.
IK 솔버 설정
MoveIt에서 KDL 솔버는 kinematics.yaml 파일에서 설정합니다:
# kinematics.yaml - MoveIt IK 솔버 설정
manipulator:
kinematics_solver: kdl_kinematics_plugin/KDLKinematicsPlugin
kinematics_solver_search_resolution: 0.005 # 탐색 해상도 (rad)
kinematics_solver_timeout: 0.05 # 타임아웃 50ms
kinematics_solver_attempts: 3 # 최대 3회 시도 (서로 다른 시드)| 파라미터 | 설명 | 권장값 |
|---|---|---|
kinematics_solver | 사용할 IK 플러그인 | kdl_kinematics_plugin/KDLKinematicsPlugin |
search_resolution | 관절 공간 탐색 해상도 (rad) | 0.005 |
timeout | 1회 시도 타임아웃 (초) | 0.005 ~ 0.1 |
attempts | 실패 시 재시도 횟수 | 3 ~ 5 |
주의:
timeout을 너무 짧게 설정하면 솔버가 수렴하기 전에 포기합니다. 실시간 제어에서는 50ms 이내, 경로 계획에서는 넉넉하게 잡는 것이 좋습니다.
IK 사용 방식 두 가지
ROS 2에서 IK를 사용하는 방법은 크게 두 가지입니다.
flowchart LR
subgraph "방법 A: 간접 호출"
A1["목표 Pose 설정"] --> A2["MoveGroup"]
A2 --> A3["IK 자동 계산"]
A3 --> A4["경로 계획"]
A4 --> A5["실행"]
end
subgraph "방법 B: 직접 호출"
B1["목표 Pose 설정"] --> B2["/compute_ik 서비스"]
B2 --> B3["관절각 반환"]
B3 --> B4["컨트롤러에 직접 발행"]
endMoveGroup 인터페이스를 통해 목표 Pose만 지정하면, MoveIt이 내부적으로 IK 계산 + 경로 계획 + 충돌 검사를 모두 처리합니다.
# MoveIt을 통한 간접 IK 호출
# 목표 pose만 지정하면 IK + 경로 계획이 자동 수행됨
pose = make_pose(x, y, z, roll, pitch, yaw)
helper.plan_to_pose_goal(pose) # IK → 경로 계획 → 실행까지 한 번에장점: 충돌 회피, 경로 최적화가 자동으로 포함됩니다. 단점: 계산 시간이 길어 실시간 제어에는 부적합합니다.
/compute_ik 서비스를 직접 호출하여 관절 각도만 빠르게 계산합니다. 텔레오퍼레이션이나 서보 제어처럼 실시간으로 반복 호출이 필요한 경우에 사용합니다.
# /compute_ik 서비스를 직접 호출하여 IK만 계산
request = GetPositionIK.Request()
request.ik_request.group_name = 'manipulator'
# 원하는 목표 위치/자세 설정
request.ik_request.pose_stamped.pose = target_pose
# 시드(seed) 상태: 현재 관절 각도를 초기값으로 전달
request.ik_request.robot_state = current_robot_state
# 타임아웃: 50ms 이내에 해를 찾아야 함
request.ik_request.timeout.nanosec = int(0.05 * 1e9)
# 서비스 호출 → 관절 각도 반환
result = ik_client.call(request)
joint_angles = result.solution.joint_state
# → {joint1: θ1, joint2: θ2, ..., joint6: θ6}장점: 빠른 응답 (수 ms), 실시간 제어에 적합합니다. 단점: 경로 계획/충돌 검사가 없으므로 별도 처리가 필요합니다.
Seed State의 중요성
IK는 일반적으로 **다중 해(multiple solutions)**가 존재합니다. 같은 목표 위치라도 로봇 팔이 도달할 수 있는 관절 조합이 여러 가지이기 때문입니다. **시드 상태(Seed State)**는 이 중에서 어떤 해를 선택할지를 결정하는 핵심 요소입니다.
stateDiagram-v2
state "현재 관절 상태 [θ1...θ6]" as Current
state "IK 솔버 (KDL)" as Solver
state "가장 가까운 해 선택" as Select
state "부드러운 연속 동작" as Smooth
Current --> Solver : 시드로 전달
Solver --> Select : 현재 상태 근처에서 탐색
Select --> Smooth : 관절 점프 없이 이동
state "시드 없음" as NoSeed
state "IK 솔버" as Solver2
state "임의의 해 반환" as Random
state "관절 점프/불연속 동작" as Jump
NoSeed --> Solver2 : 초기값 불명확
Solver2 --> Random : 매번 다른 해 수렴
Random --> Jump : 위험한 동작 발생 가능- 관절 점프(Joint Flip): 갑자기 관절이 180도 회전하는 현상
- 불연속 동작: 매 프레임마다 완전히 다른 자세로 전환
- 로봇 손상 위험: 급격한 관절 변화로 기구적 충격 발생
# 올바른 시드 상태 전달 예시
from sensor_msgs.msg import JointState
# 현재 관절 상태를 구독하여 시드로 사용
current_joints = get_current_joint_state() # 현재 관절 각도 구독
request.ik_request.robot_state.joint_state = current_joints
# → 솔버가 현재 상태에서 가장 가까운 해를 반환실전 팁: 실시간 서보 제어에서는 반드시 매 프레임마다 현재 관절 상태를 시드로 전달해야 합니다. 이전 IK 결과를 시드로 재사용하면 실제 로봇 상태와 괴리가 생길 수 있습니다.
IK 결과를 로봇에 적용하기
IK로 얻은 관절 각도를 실제 로봇에 적용하는 전체 파이프라인입니다.
sequenceDiagram
participant User as 사용자 입력
participant Node as ROS 노드
participant IK as /compute_ik
participant Ctrl as /arm_controller
loop 30Hz 실시간 루프
User->>Node: 목표 좌표 (x, y, z)
Node->>Node: 현재 관절 상태 조회 (시드)
Node->>IK: IK 요청 (목표 + 시드)
IK-->>Node: 관절 각도 [θ1...θ6]
Node->>Ctrl: JointTrajectory 발행
Ctrl-->>Node: 로봇 동작 실행
endfrom trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
from builtin_interfaces.msg import Duration
# IK로 얻은 관절각을 trajectory 메시지로 변환
msg = JointTrajectory()
msg.joint_names = ['joint1', 'joint2', 'joint3',
'joint4', 'joint5', 'joint6']
point = JointTrajectoryPoint()
point.positions = [θ1, θ2, θ3, θ4, θ5, θ6] # IK 결과
point.time_from_start = Duration(sec=0, nanosec=int(0.05 * 1e9)) # 50ms
msg.points = [point]
# 컨트롤러 토픽에 발행 → 로봇 동작
publisher.publish(msg) # → /arm_controller/joint_trajectory주의:
time_from_start는 "이 지점에 도달할 목표 시간"입니다. 너무 짧으면 컨트롤러가 따라가지 못하고, 너무 길면 반응이 느려집니다. 실시간 서보 제어에서는 제어 주기(예: 33ms)에 맞추는 것이 좋습니다.
방식 비교 요약
| 항목 | 간접 호출 (MoveGroup) | 직접 호출 (/compute_ik) |
|---|---|---|
| 방식 | MoveGroup이 IK + 경로 계획 수행 | /compute_ik 서비스 직접 호출 |
| 호출 주기 | 1회성 (계획 후 실행) | 실시간 30Hz 반복 호출 |
| 용도 | 점-대-점(PTP) 이동 | 텔레오퍼레이션, 서보 제어 |
| 시드 관리 | 플래너가 자동 관리 | 현재 관절 상태를 명시적으로 전달 |
| 충돌 검사 | 자동 포함 | 별도 처리 필요 |
| 응답 속도 | 수백 ms ~ 수 초 | 수 ms |
IK 솔버 대안: TRAC-IK
KDL이 기본 솔버이지만, 성능이 부족할 때 TRAC-IK를 대안으로 사용할 수 있습니다. TRAC-IK는 KDL의 Newton-Raphson과 SQP(Sequential Quadratic Programming)를 병렬로 실행하여 성공률을 크게 높인 솔버입니다.
# TRAC-IK로 솔버 변경
manipulator:
kinematics_solver: trac_ik_kinematics_plugin/TRAC_IKKinematicsPlugin
kinematics_solver_timeout: 0.05| 비교 | KDL | TRAC-IK |
|---|---|---|
| 성공률 | ~60-70% | ~95%+ |
| 알고리즘 | Newton-Raphson 단일 | NR + SQP 병렬 |
| 설치 | MoveIt 기본 포함 | 별도 패키지 설치 필요 |
정리
- KDL 솔버는 Newton-Raphson 반복법으로 IK를 수치적으로 풀며, URDF만 있으면 어떤 로봇이든 적용 가능합니다.
- IK 사용 방식은 간접 호출(MoveGroup)과 직접 호출(
/compute_ik)로 나뉘며, 용도에 따라 선택합니다. - 시드 상태를 올바르게 전달하는 것이 안정적인 IK 해를 얻는 핵심입니다.
- 전체 파이프라인: 목표 좌표 →
/compute_ik→ 관절 각도 → JointTrajectory 발행 → 로봇 동작 - 성공률이 중요하다면 TRAC-IK 솔버로 교체를 고려하세요.