MoveIt 예제 목록 (ex01 ~ ex15) 완전 정리

개요

MoveIt은 ROS 기반 로봇 매니퓰레이션의 핵심 프레임워크입니다. 이 포스트에서는 ex01부터 ex15까지 총 16개의 예제를 기본부터 응용까지 순서대로 정리합니다. 각 예제가 어떤 개념을 다루고, 어떻게 연결되는지 한눈에 파악할 수 있도록 구성했습니다.

예제는 기본 상태 읽기 → 단일 관절 제어 → 카테시안 제어 → 그리퍼 → 종합 태스크 → 고급 기능 순서로 난이도가 올라갑니다.


예제 전체 흐름

flowchart LR
    subgraph 기본["🔧 기본 (ex01~ex03)"]
        A[ex01<br/>Joint State Reader] --> B[ex02<br/>Named Pose]
        B --> B1[ex02_1<br/>MoveItPy Named Pose]
        B --> C[ex03<br/>Joint Goal]
    end
    subgraph 카테시안["📐 카테시안 (ex04~ex05)"]
        D[ex04<br/>Pose Goal] --> E[ex05<br/>Cartesian Path]
    end
    subgraph 그리퍼["🤖 그리퍼 & 태스크 (ex06~ex07)"]
        F[ex06<br/>Gripper Control] --> G[ex07<br/>Pick and Place]
    end
    subgraph 고급["⚡ 고급 (ex08~ex15)"]
        H[ex08<br/>Path Constraints]
        I[ex09<br/>Collision Objects]
        J[ex10<br/>Multi-Planner]
        K[ex11<br/>Keyboard Servo]
        L[ex12<br/>Waypoint Follow]
        M[ex13<br/>Circular Path]
        N[ex14<br/>Circular Servo]
        O[ex15<br/>Keyboard IK]
    end
    기본 --> 카테시안 --> 그리퍼 --> 고급

예제 상세 정리

ex01 — Joint State Reader

/joint_states 토픽을 구독(Subscribe) 하여 7개 관절의 위치, 속도, 토크 정보를 실시간으로 출력하는 가장 기초적인 예제입니다.

핵심 개념: ROS 토픽 구독, sensor_msgs/JointState 메시지

# joint_states 토픽 구독 기본 구조
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import JointState
 
class JointStateReader(Node):
    def __init__(self):
        super().__init__('joint_state_reader')
        # /joint_states 토픽 구독
        self.subscription = self.create_subscription(
            JointState,
            '/joint_states',
            self.joint_callback,
            10
        )
 
    def joint_callback(self, msg):
        # 관절 이름과 위치를 출력
        for name, pos in zip(msg.name, msg.position):
            self.get_logger().info(f'{name}: {pos:.4f} rad')

팁: ros2 topic echo /joint_states로 터미널에서도 바로 확인할 수 있습니다.


ex02 — Named Pose

SRDF(Semantic Robot Description Format) 에 미리 정의된 이름 포즈(예: home, ready)로 순차 이동합니다.

핵심 개념: SRDF 포즈 정의, MoveGroupInterface

# Named Pose로 이동
move_group.set_named_target("home")   # SRDF에 정의된 'home' 포즈
move_group.go(wait=True)
 
move_group.set_named_target("ready")  # SRDF에 정의된 'ready' 포즈
move_group.go(wait=True)
포즈 이름용도
home로봇 초기 자세 (보통 모든 관절 0)
ready작업 준비 자세 (팔을 약간 들어올림)

ex02_1 — MoveItPy Named Pose

ex02와 동일한 동작이지만 MoveItPy 고수준 API를 사용합니다. MoveItPy는 ROS 2 환경에서 Python으로 MoveIt을 더 간결하게 사용할 수 있게 해줍니다.

from moveit.planning import MoveItPy
 
# MoveItPy 고수준 API 사용
moveit_py = MoveItPy(node_name="moveitpy_example")
arm = moveit_py.get_planning_component("arm")
 
arm.set_goal_state(configuration_name="home")
arm.plan_and_execute()

MoveGroupInterface vs MoveItPy: MoveGroupInterface는 Action 기반이고, MoveItPy는 직접 플래닝 파이프라인을 호출합니다. 신규 프로젝트라면 MoveItPy를 권장합니다.


ex03 — Joint Goal

각 관절을 하나씩 움직여서 개별 관절 동작을 이해하는 예제입니다. 로봇의 기구학적 구조를 직관적으로 파악하는 데 유용합니다.

핵심 개념: Joint Space Planning, 관절 각도 직접 지정

# 특정 관절만 목표값 설정
joint_goal = move_group.get_current_joint_values()
joint_goal[0] = 1.57   # 1번 관절만 90도 회전 (라디안)
move_group.go(joint_goal, wait=True)

ex04 — Pose Goal

카테시안 좌표(x, y, z)와 자세(orientation) 를 지정하여 엔드 이펙터를 이동시킵니다. 내부적으로 역기구학(IK, Inverse Kinematics) 을 사용합니다.

핵심 개념: Pose Goal, 역기구학(IK), geometry_msgs/Pose

flowchart LR
    A["목표 포즈<br/>(x, y, z, qx, qy, qz, qw)"] --> B["IK Solver"]
    B --> C["관절 각도 계산"]
    C --> D["모션 플래닝"]
    D --> E["로봇 이동"]
from geometry_msgs.msg import Pose
 
# 엔드 이펙터 목표 위치/자세 설정
target_pose = Pose()
target_pose.position.x = 0.4
target_pose.position.y = 0.1
target_pose.position.z = 0.4
target_pose.orientation.w = 1.0  # 쿼터니언
 
move_group.set_pose_target(target_pose)
move_group.go(wait=True)

주의: IK 솔루션이 여러 개 존재할 수 있으므로, 로봇이 예상과 다른 자세를 취할 수 있습니다. 관절 제한(joint limits)을 적절히 설정하세요.


ex05 — Cartesian Path

여러 웨이포인트(waypoint) 를 지정하여 엔드 이펙터가 직선 경로를 따라 이동합니다. 사각형 궤적 그리기 등에 활용됩니다.

핵심 개념: compute_cartesian_path(), 웨이포인트 리스트, 직선 보간

waypoints = []
 
# 사각형 궤적의 웨이포인트 4개 추가
wpose = move_group.get_current_pose().pose
wpose.position.z += 0.1   # 위로
waypoints.append(copy.deepcopy(wpose))
 
wpose.position.y += 0.1   # 옆으로
waypoints.append(copy.deepcopy(wpose))
 
wpose.position.z -= 0.1   # 아래로
waypoints.append(copy.deepcopy(wpose))
 
wpose.position.y -= 0.1   # 원래 위치로
waypoints.append(copy.deepcopy(wpose))
 
# 카테시안 경로 계산 (eef_step: 보간 간격, jump_threshold: 관절 점프 허용치)
(plan, fraction) = move_group.compute_cartesian_path(
    waypoints, eef_step=0.01, jump_threshold=0.0
)
move_group.execute(plan, wait=True)
파라미터설명권장값
eef_step웨이포인트 간 보간 간격(m)0.01
jump_threshold관절 공간 점프 제한 (0.0 = 비활성화)0.0 또는 5.0
fraction계획 성공 비율 (1.0 = 100%)

ex06 — Gripper Control

GripperCommand Action 인터페이스로 그리퍼를 열고 닫는 예제입니다.

핵심 개념: Action Client, control_msgs/GripperCommand

from control_msgs.action import GripperCommand
 
# 그리퍼 닫기 (position 값이 작을수록 닫힘)
goal = GripperCommand.Goal()
goal.command.position = 0.0    # 완전히 닫기
goal.command.max_effort = 50.0  # 최대 힘 (N)
gripper_client.send_goal(goal)
 
# 그리퍼 열기
goal.command.position = 0.04   # 40mm 열기
gripper_client.send_goal(goal)

ex07 — Pick and Place

물체를 잡고(Pick) → 이동 → 놓기(Place) 하는 전체 시퀀스를 수행합니다. ex01~ex06의 개념을 종합적으로 활용하는 예제입니다.

sequenceDiagram
    participant Robot
    participant Gripper
    participant Scene
 
    Robot->>Robot: 물체 위로 이동 (Pose Goal)
    Robot->>Robot: 물체 위치로 하강
    Robot->>Gripper: 그리퍼 닫기 (Grasp)
    Scene-->>Robot: 물체 Attach
    Robot->>Robot: 들어올리기
    Robot->>Robot: 놓을 위치로 이동
    Robot->>Gripper: 그리퍼 열기 (Release)
    Scene-->>Robot: 물체 Detach
    Robot->>Robot: 후퇴

핵심: attach_object()detach_object()로 물체를 로봇에 부착/분리해야 충돌 체크가 정확하게 동작합니다.


ex08 — Path Constraints

엔드 이펙터의 자세를 제한(constraint) 하면서 이동합니다. 예를 들어, 컵에 물을 담고 이동할 때 수평을 유지해야 하는 경우에 사용합니다.

핵심 개념: OrientationConstraint, Path Constraints

from moveit_msgs.msg import OrientationConstraint, Constraints
 
# 수평 유지 제약 조건
orientation_constraint = OrientationConstraint()
orientation_constraint.link_name = "end_effector_link"
orientation_constraint.header.frame_id = "base_link"
orientation_constraint.orientation.w = 1.0       # 수평 자세
orientation_constraint.absolute_x_axis_tolerance = 0.1  # 허용 오차 (라디안)
orientation_constraint.absolute_y_axis_tolerance = 0.1
orientation_constraint.absolute_z_axis_tolerance = 3.14  # Z축 회전은 자유
orientation_constraint.weight = 1.0
 
constraints = Constraints()
constraints.orientation_constraints.append(orientation_constraint)
move_group.set_path_constraints(constraints)

주의: Path Constraints를 사용하면 플래닝 시간이 크게 증가할 수 있습니다. set_planning_time()으로 충분한 시간을 설정하세요.


ex09 — Collision Objects

Planning Scene에 장애물을 추가하고, 이를 회피하는 경로를 계획합니다.

핵심 개념: PlanningSceneInterface, CollisionObject, 충돌 회피

from moveit_msgs.msg import CollisionObject
from shape_msgs.msg import SolidPrimitive
 
# 박스 형태의 장애물 추가
collision_object = CollisionObject()
collision_object.id = "box_obstacle"
collision_object.header.frame_id = "base_link"
 
box = SolidPrimitive()
box.type = SolidPrimitive.BOX
box.dimensions = [0.1, 0.5, 0.4]  # 가로, 세로, 높이 (m)
 
collision_object.primitives.append(box)
# 위치 설정 후 Scene에 추가
planning_scene.add_object(collision_object)
flowchart TD
    A["장애물 정의<br/>(Box, Cylinder, Mesh)"] --> B["Planning Scene에 추가"]
    B --> C["모션 플래너 실행"]
    C --> D{"충돌 없는<br/>경로 존재?"}
    D -->|Yes| E["경로 실행"]
    D -->|No| F["플래닝 실패<br/>(다른 플래너 시도)"]

ex10 — Multi-Planner

여러 OMPL(Open Motion Planning Library) 플래너를 비교하여 계획 시간, 경로 길이, 성공률 등을 분석합니다.

핵심 개념: OMPL 플래너, 플래너 벤치마킹

플래너특징적합한 상황
RRTConnect양방향 탐색, 빠른 계획일반적인 모션 플래닝
RRT*최적 경로 탐색짧은 경로가 필요할 때
PRM로드맵 기반, 반복 쿼리에 유리같은 환경에서 반복 계획
STOMP최적화 기반, 부드러운 경로산업용 매니퓰레이터
CHOMP비용 함수 최적화장애물 근처 부드러운 이동
# 플래너 변경
move_group.set_planner_id("RRTConnectkConfigDefault")
move_group.set_planning_time(5.0)  # 최대 계획 시간
 
plan = move_group.plan()

ex11 — Keyboard Servo

MoveIt Servo를 사용하여 키보드로 로봇을 실시간 텔레오퍼레이션합니다. Servo는 Twist 또는 JointJog 명령을 받아 실시간으로 로봇을 제어합니다.

핵심 개념: MoveIt Servo, 실시간 제어, geometry_msgs/TwistStamped

flowchart LR
    A["키보드 입력"] --> B["Twist/JointJog<br/>메시지 생성"]
    B --> C["MoveIt Servo"]
    C --> D["실시간 IK 계산"]
    D --> E["관절 명령 전송"]
    E --> F["로봇 이동"]

Servo vs Plan-and-Execute: Servo는 사전 계획 없이 실시간으로 제어하므로 반응이 빠르지만, 충돌 회피가 제한적입니다.


ex12 — Waypoint Follow

YAML 파일에 정의된 웨이포인트를 로드하여 순차적으로 추적합니다. 반복적인 경로를 설정 파일로 관리할 수 있어 실용적입니다.

핵심 개념: YAML 파라미터 로딩, 웨이포인트 순차 추적

# waypoints.yaml 예시
waypoints:
  - position: {x: 0.3, y: 0.0, z: 0.5}
    orientation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0}
  - position: {x: 0.3, y: 0.2, z: 0.5}
    orientation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0}
  - position: {x: 0.3, y: -0.2, z: 0.3}
    orientation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0}

ex13 — Circular Path (오프라인)

YZ 평면에 원 궤적을 그리는 예제입니다. 전체 경로를 **사전에 계획(오프라인)**한 후 한 번에 실행합니다.

핵심 개념: 원형 궤적 생성, compute_cartesian_path(), 삼각함수 웨이포인트

import math
 
waypoints = []
radius = 0.1     # 원의 반지름 (m)
num_points = 36   # 원을 몇 개의 점으로 분할
 
for i in range(num_points + 1):
    angle = 2 * math.pi * i / num_points
    wpose = move_group.get_current_pose().pose
    wpose.position.y = center_y + radius * math.cos(angle)
    wpose.position.z = center_z + radius * math.sin(angle)
    waypoints.append(copy.deepcopy(wpose))
 
# 전체 원 경로를 한번에 계획
(plan, fraction) = move_group.compute_cartesian_path(
    waypoints, eef_step=0.005, jump_threshold=0.0
)

원 위의 점의 좌표는 다음과 같이 계산됩니다:

y=yc+rcosθ,z=zc+rsinθy = y_c + r \cos\theta, \quad z = z_c + r \sin\theta

여기서 rr은 반지름, (yc,zc)(y_c, z_c)는 원의 중심, θ\theta는 각도(0 ~ 2π2\pi)입니다.


ex14 — Circular Servo (실시간)

ex13과 동일한 원 궤적을 실시간 IK + 피드백 제어로 수행합니다. MoveIt Servo를 활용하여 각 시점의 목표를 실시간으로 전달합니다.

핵심 개념: 실시간 서보 제어, 피드백 루프

stateDiagram-v2
    [*] --> 초기화
    초기화 --> 목표계산: Servo 시작
    목표계산 --> IK풀이: 다음 각도의 좌표 계산
    IK풀이 --> 명령전송: 관절 속도 명령
    명령전송 --> 피드백확인: 로봇 이동
    피드백확인 --> 목표계산: 현재 위치 읽기
    피드백확인 --> [*]: 원 완성
비교ex13 (오프라인)ex14 (실시간)
계획 방식전체 경로 사전 계획매 스텝 실시간 계산
사용 APIcompute_cartesian_path()MoveIt Servo
장점안정적, 충돌 검사 가능외부 입력에 즉시 반응
단점환경 변화 대응 불가충돌 회피 제한적

ex15 — Keyboard IK

MoveIt Servo 없이 직접 IK 서비스를 호출하여 키보드 텔레오퍼레이션을 구현합니다. Servo의 내부 동작을 이해하는 데 도움이 됩니다.

핵심 개념: IK 서비스 직접 호출, compute_ik 서비스

flowchart LR
    A["키보드 입력"] --> B["목표 Pose 계산<br/>(현재 위치 + 이동량)"]
    B --> C["/compute_ik<br/>서비스 호출"]
    C --> D["관절 각도 응답"]
    D --> E["JointTrajectory<br/>명령 전송"]
    E --> F["로봇 이동"]

ex11 vs ex15: ex11은 MoveIt Servo가 IK와 속도 제어를 자동으로 처리하지만, ex15는 모든 과정을 직접 구현합니다. 내부 동작 원리를 학습하기에 좋습니다.


예제 분류 맵

flowchart TB
    subgraph JointSpace["관절 공간 제어"]
        ex01["ex01 Joint State Reader"]
        ex02["ex02 Named Pose"]
        ex02_1["ex02_1 MoveItPy"]
        ex03["ex03 Joint Goal"]
    end
    subgraph CartesianSpace["카테시안 공간 제어"]
        ex04["ex04 Pose Goal"]
        ex05["ex05 Cartesian Path"]
        ex13["ex13 Circular Path"]
    end
    subgraph Manipulation["매니퓰레이션"]
        ex06["ex06 Gripper"]
        ex07["ex07 Pick & Place"]
    end
    subgraph Advanced["고급 플래닝"]
        ex08["ex08 Path Constraints"]
        ex09["ex09 Collision Objects"]
        ex10["ex10 Multi-Planner"]
    end
    subgraph Realtime["실시간 제어"]
        ex11["ex11 Keyboard Servo"]
        ex12["ex12 Waypoint Follow"]
        ex14["ex14 Circular Servo"]
        ex15["ex15 Keyboard IK"]
    end

핵심 요약

단계예제배우는 것
기초ex01~ex03토픽 구독, SRDF 포즈, 관절 제어
카테시안ex04~ex05IK 기반 위치 제어, 직선 경로
그리퍼ex06~ex07그리퍼 Action, Pick & Place 통합
제약/충돌ex08~ex09자세 제약, 장애물 회피
플래너ex10OMPL 플래너 비교 분석
실시간ex11~ex15Servo, 웨이포인트 추적, IK 직접 제어

이 예제들을 ex01부터 순서대로 진행하면 MoveIt의 핵심 기능을 체계적으로 학습할 수 있습니다. 특히 ex07(Pick and Place)까지 완료하면 기본적인 매니퓰레이션 태스크를 구현할 수 있고, ex08 이후의 고급 예제로 실전 응용력을 키울 수 있습니다.