Seed State(시드 상태)란? - IK 솔버의 초기 추정값 이해하기

개요

로봇팔을 제어할 때, 원하는 위치로 엔드 이펙터를 이동시키려면 역기구학(Inverse Kinematics, IK) 솔버가 필요합니다. 이때 IK 솔버에게 "이 관절 각도 근처에서 답을 찾아라" 라고 알려주는 초기 추정값이 바로 **Seed State(시드 상태)**입니다.

핵심: Seed State = "지금 팔이 이 자세니까, 여기서 크게 안 벗어나는 답을 줘"


왜 Seed State가 필요한가?

6축 로봇팔에서 같은 엔드 이펙터 위치에 도달하는 관절 조합은 여러 개 존재합니다. 이를 다중 해(Multiple Solutions) 문제라고 합니다.

목표: (x=0.3, y=0.0, z=0.5)
 
해 1: [  0°,  45°,  -30°, 0°, 60°, 0°]   ← 팔꿈치 위로 (Elbow-up)
해 2: [  0°, -45°,   30°, 0°, 60°, 0°]   ← 팔꿈치 아래로 (Elbow-down)
해 3: [180°,  45°,  -30°, 0°, 60°, 0°]   ← 뒤로 돌아서 (Shoulder-flip)
...

시드 없이 풀면 어떤 해가 나올지 예측 불가하고, 관절이 갑자기 뒤집히는 문제가 발생합니다.

flowchart TD
    TARGET["🎯 목표 위치<br/>(x=0.3, y=0.0, z=0.5)"]
 
    TARGET --> SOL1["해 1: 팔꿈치 위로<br/>[0°, 45°, -30°, 0°, 60°, 0°]"]
    TARGET --> SOL2["해 2: 팔꿈치 아래로<br/>[0°, -45°, 30°, 0°, 60°, 0°]"]
    TARGET --> SOL3["해 3: 뒤로 돌아서<br/>[180°, 45°, -30°, 0°, 60°, 0°]"]
 
    style TARGET fill:#ff6b6b,color:#fff
    style SOL1 fill:#51cf66,color:#fff
    style SOL2 fill:#339af0,color:#fff
    style SOL3 fill:#fcc419,color:#333

Seed State가 하는 일

Seed State를 전달하면, IK 솔버는 해당 값 근처에서 탐색을 시작하여 가장 가까운 해를 반환합니다.

sequenceDiagram
    participant C as 컨트롤러
    participant S as IK 솔버 (KDL)
    participant R as 로봇
 
    C->>C: 현재 관절 상태 읽기<br/>[10°, 45°, -30°, 5°, 60°, 0°]
    C->>S: IK 요청 + Seed State 전달
    S->>S: Seed 근처에서 탐색 시작
    S-->>C: 결과: [11°, 46°, -29°, 5°, 61°, 0°]
    C->>R: 부드러운 관절 이동 명령

시드와 비슷한 해를 반환하기 때문에, 동작이 부드럽고 연속적입니다.


Seed 없이 동작하면?

Seed State 없이 IK를 풀면, 솔버가 매번 임의의 해를 반환할 수 있어 관절이 순간적으로 크게 뒤집히는 현상이 발생합니다.

시간관절 상태상태
t=0[0°, 45°, -30°, ...]정상
t=1[1°, 46°, -29°, ...]정상
t=2[180°, -45°, 30°, ...]갑자기 뒤집힘!

이런 경우 로봇이 순간적으로 크게 회전하게 되어 위험하고 비정상적인 동작을 하게 됩니다.

stateDiagram-v2
    [*] --> 정상동작: Seed 사용
    [*] --> 비정상동작: Seed 미사용
 
    정상동작 --> 정상동작: 연속적인 해 선택
    비정상동작 --> 관절뒤집힘: 임의의 해 선택
    관절뒤집힘 --> 위험상황: 급격한 회전

코드로 보는 Seed State 설정

ROS에서 IK 서비스를 호출할 때, RobotState에 현재 관절 상태를 담아 Seed로 전달합니다.

from moveit_msgs.msg import RobotState
from sensor_msgs.msg import JointState
 
# 현재 관절 상태를 시드로 설정
rs = RobotState()
js = JointState()
js.name = list(self._current_joints.keys())       # ['joint1', ..., 'joint6']
js.position = list(self._current_joints.values())  # 현재 각도들 (라디안)
rs.joint_state = js
 
# IK 요청에 시드 상태 포함
request.ik_request.robot_state = rs  # ← 이게 시드!

여기서 self._current_joints/joint_states 토픽을 구독하여 실시간으로 받아온 현재 관절 각도입니다.

import rospy
from sensor_msgs.msg import JointState
 
class RobotController:
    def __init__(self):
        self._current_joints = {}
        # /joint_states 토픽 구독
        rospy.Subscriber('/joint_states', JointState, self._joint_callback)
 
    def _joint_callback(self, msg):
        """실시간 관절 상태 업데이트"""
        for name, position in zip(msg.name, msg.position):
            self._current_joints[name] = position

IK 솔버별 Seed 처리 방식

ROS/MoveIt에서 사용되는 주요 IK 솔버마다 Seed State를 처리하는 방식이 다릅니다.

솔버Seed 활용 방식특징
KDLNewton-Raphson 반복법의 초기값으로 사용기본 솔버, Seed 의존도 높음
TRAC-IKKDL + SQP 두 알고리즘 병렬 실행Seed에서 시작하되 더 넓게 탐색
IKFast해석적 풀이 후 Seed에 가장 가까운 해 선택가장 빠르고 안정적
BioIK진화 알고리즘 초기 집단에 Seed 포함복잡한 구속 조건에 강함

팁: 실제 로봇 프로젝트에서는 TRAC-IKIKFast를 권장합니다. KDL은 Seed에 따라 실패할 확률이 높습니다.


실전 팁과 주의사항

  • 항상 최신 관절 상태를 Seed로 사용하세요. 오래된 값을 쓰면 솔버가 엉뚱한 해를 반환할 수 있습니다.
  • Seed를 0으로 초기화하지 마세요. 모든 관절이 0인 상태는 특이점(Singularity)에 가까울 수 있어 솔버가 실패하기 쉽습니다.
  • 연속 경로 생성 시, 이전 IK 결과를 다음 Seed로 체인처럼 연결하면 부드러운 궤적을 얻을 수 있습니다.
# 경로 생성 시 Seed 체이닝
seed = current_joint_state  # 시작은 현재 상태
 
for waypoint in waypoints:
    ik_result = solve_ik(waypoint, seed=seed)
    trajectory.append(ik_result)
    seed = ik_result  # 이전 결과를 다음 Seed로 사용

정리

항목내용
Seed State란?IK 솔버에 전달하는 관절 각도 초기 추정값
왜 필요?다중 해 중 현재 자세와 가장 가까운 해를 선택하기 위해
없으면?관절 뒤집힘, 급격한 동작, 위험한 상황 발생
어떻게 설정?/joint_states 토픽의 현재 관절 상태를 RobotState에 담아 전달
핵심 원칙항상 최신 관절 상태를 Seed로 사용하여 연속적인 동작 보장