간접 호출 vs 직접 호출 — MoveGroup과 /compute_ik의 차이

개요

로봇 팔을 원하는 위치로 이동시키는 방법은 크게 두 가지로 나뉩니다.

  • 간접 호출: MoveGroup Action Server에 목표만 전달하면 나머지를 알아서 처리
  • 직접 호출: /compute_ik 서비스로 관절각만 계산받고, 실행은 직접 제어

핵심 차이: 간접은 **"어디로 갈지"**만 말하면 되고, 직접은 **"어떻게 갈지"**를 내가 책임진다.

이 포스트에서는 두 방식의 내부 동작, 성능 차이, 그리고 상황별 선택 기준을 정리합니다.


간접 호출 — MoveGroup

MoveGroup은 MoveIt의 핵심 Action Server로, 목표 Pose 하나만 넘기면 IK 계산부터 경로 계획, 충돌 검사, 궤적 실행까지 전부 처리합니다.

flowchart TD
    A["목표 Pose 전달"] --> B["MoveGroup Action Server"]
    B --> C["① IK로 목표 관절각 계산"]
    C --> D["② OMPL 플래너로 경로 계획"]
    D --> E["③ 충돌 검사"]
    E --> F["④ 시간 파라미터화 (Time Parameterization)"]
    F --> G["⑤ 전체 궤적(Trajectory) 완성"]
    G --> H["ExecuteTrajectory로 실행"]
 
    style B fill:#4a90d9,color:#fff
    style H fill:#27ae60,color:#fff

단계설명
IK (Inverse Kinematics)목표 Pose → 관절각 변환. KDL, TRAC-IK 등의 솔버 사용
경로 계획현재 관절각 → 목표 관절각까지의 경로를 OMPL(RRT, RRT* 등)로 탐색
충돌 검사Planning Scene의 장애물과 자기 자신(self-collision) 검사
시간 파라미터화경로의 각 웨이포인트에 속도·가속도 제한을 적용하여 실행 가능한 궤적으로 변환
궤적 실행FollowJointTrajectory 액션으로 컨트롤러에 전달

import rclpy
from moveit_commander import MoveGroupCommander
 
# MoveGroup 초기화
group = MoveGroupCommander("manipulator")
 
# 목표 Pose 설정
target_pose = group.get_current_pose().pose
target_pose.position.x = 0.3
target_pose.position.z = 0.5
 
# 한 번의 호출로 계획 + 실행 완료
group.set_pose_target(target_pose)
plan = group.go(wait=True)  # IK → 경로계획 → 충돌검사 → 실행까지 전부 수행
group.stop()
group.clear_pose_targets()

특징: 한 번 호출하면 IK + 경로계획 + 실행까지 다 해준다. 편하지만 1~5초 소요.


직접 호출 — /compute_ik

/compute_ik 서비스는 IK 계산만 수행합니다. 경로 계획도 없고, 충돌 검사도 없습니다. 결과로 관절각만 반환되며, 이를 컨트롤러에 직접 발행하는 것은 개발자의 몫입니다.

flowchart TD
    A["목표 Pose 전달"] --> B["/compute_ik 서비스"]
    B --> C["① IK로 목표 관절각 계산"]
    C --> D["② 관절각 반환 (끝)"]
    D --> E["개발자가 직접 컨트롤러에 발행"]
 
    style B fill:#e67e22,color:#fff
    style E fill:#e74c3c,color:#fff

import rclpy
from rclpy.node import Node
from moveit_msgs.srv import GetPositionIK
from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
 
class DirectController(Node):
    def __init__(self):
        super().__init__('direct_controller')
        # IK 서비스 클라이언트
        self.ik_client = self.create_client(GetPositionIK, '/compute_ik')
        # 관절 명령 퍼블리셔
        self.joint_pub = self.create_publisher(
            JointTrajectory, '/joint_trajectory_controller/joint_trajectory', 10
        )
        # 30Hz 제어 루프
        self.timer = self.create_timer(1.0 / 30.0, self.control_loop)
        self.target_pose = None  # 외부에서 설정
 
    def control_loop(self):
        if self.target_pose is None:
            return
 
        # ① IK로 관절각만 빠르게 계산 (~50ms)
        request = GetPositionIK.Request()
        request.ik_request.pose_stamped.pose = self.target_pose
        request.ik_request.group_name = "manipulator"
 
        future = self.ik_client.call_async(request)
        # ... 응답 처리 후 관절각을 컨트롤러에 직접 발행
# 실시간 제어의 핵심 — 30Hz 루프 안에서 조금씩 이동
target.position.x += 0.001  # 1mm씩 이동
joints = compute_ik(target)  # 관절각만 빠르게 계산 (~50ms)
publish(joints)              # 직접 컨트롤러에 발행

특징: IK만 풀어주므로 ~50ms로 매우 빠르다. 실시간 제어에 적합.


비교 정리

항목간접 호출 (MoveGroup)직접 호출 (/compute_ik)
비유내비 켜고 경로 안내받아 운전목적지 방향만 알고 직접 운전
IK 계산내부에서 자동 수행직접 서비스 호출
경로 계획O (장애물 회피 포함)X
충돌 검사OX
속도느림 (1~5초)빠름 (~50ms)
제어 주기1회성30Hz 반복 가능
용도"저기로 안전하게 가""조금씩 실시간으로 이동"
안전성높음 (자동 검증)낮음 (개발자 책임)
flowchart LR
    subgraph 간접호출["간접 호출 (MoveGroup)"]
        direction TB
        A1["IK"] --> A2["경로 계획"]
        A2 --> A3["충돌 검사"]
        A3 --> A4["궤적 실행"]
    end
 
    subgraph 직접호출["직접 호출 (/compute_ik)"]
        direction TB
        B1["IK"] --> B2["관절각 반환"]
        B2 --> B3["직접 제어"]
    end
 
    간접호출 -.- 직접호출
 
    style 간접호출 fill:#eaf2ff,stroke:#4a90d9
    style 직접호출 fill:#fff5eb,stroke:#e67e22

언제 무엇을 쓸까?

  • 목표 지점이 현재 위치에서 먼 경우
  • 작업 공간에 장애물이 존재하는 경우
  • 안전성이 중요한 환경 (산업 현장, 협동 로봇)
  • Pick & Place처럼 정확한 도달이 중요한 작업

  • 실시간 추적이 필요한 경우 (비주얼 서보잉, 물체 추적)
  • 텔레오퍼레이션 (원격 조종) 시 조이스틱/마우스 입력을 즉시 반영해야 할 때
  • 미세 조정이 필요한 경우 (표면 따라가기, 힘 제어)
  • 장애물이 없는 안전한 환경에서 빠른 반응이 필요할 때
stateDiagram-v2
    [*] --> 판단
 
    판단 --> 간접호출 : 장애물 있음 / 먼 거리
    판단 --> 직접호출 : 장애물 없음 / 실시간 필요
 
    간접호출 --> MoveGroup : plan + execute
    직접호출 --> compute_ik : IK만 계산
    compute_ik --> 컨트롤러발행 : 직접 publish
 
    MoveGroup --> [*]
    컨트롤러발행 --> [*]

실전 팁

  • 직접 호출 시 충돌 방지: /compute_ik는 충돌 검사를 하지 않으므로, 필요하다면 planning_scene 모니터를 별도로 구성하여 관절각이 충돌 상태인지 사전 검증하세요.
  • 하이브리드 접근: 먼 거리는 MoveGroup으로 대략 이동한 뒤, 미세 조정은 /compute_ik로 전환하는 하이브리드 전략이 실전에서 자주 사용됩니다.
  • IK 솔버 선택: TRAC-IK는 KDL보다 성공률이 높고 빠릅니다. 직접 호출을 많이 쓴다면 kinematics.yaml에서 솔버를 TRAC-IK로 변경하는 것을 권장합니다.
  • 속도 제한 설정: 직접 호출에서 관절 속도 제한을 코드 레벨에서 반드시 적용하세요. 급격한 관절각 변화는 로봇 손상의 원인이 됩니다.

핵심 요약

간접 호출직접 호출
한 줄 요약"어디로 갈지"만 말하면 된다"어떻게 갈지"를 내가 책임진다
장점안전하고 편리함빠르고 유연함
단점느리고 실시간 제어 불가충돌 검사 없음, 안전은 개발자 몫

간접 호출은 안전한 자동 운전, 직접 호출은 빠른 수동 운전이다. 상황에 맞게 선택하고, 필요하면 둘을 조합하는 하이브리드 방식을 활용하자.