개요
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
)원 위의 점의 좌표는 다음과 같이 계산됩니다:
여기서 은 반지름, 는 원의 중심, 는 각도(0 ~ )입니다.
ex14 — Circular Servo (실시간)
ex13과 동일한 원 궤적을 실시간 IK + 피드백 제어로 수행합니다. MoveIt Servo를 활용하여 각 시점의 목표를 실시간으로 전달합니다.
핵심 개념: 실시간 서보 제어, 피드백 루프
stateDiagram-v2
[*] --> 초기화
초기화 --> 목표계산: Servo 시작
목표계산 --> IK풀이: 다음 각도의 좌표 계산
IK풀이 --> 명령전송: 관절 속도 명령
명령전송 --> 피드백확인: 로봇 이동
피드백확인 --> 목표계산: 현재 위치 읽기
피드백확인 --> [*]: 원 완성| 비교 | ex13 (오프라인) | ex14 (실시간) |
|---|---|---|
| 계획 방식 | 전체 경로 사전 계획 | 매 스텝 실시간 계산 |
| 사용 API | compute_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~ex05 | IK 기반 위치 제어, 직선 경로 |
| 그리퍼 | ex06~ex07 | 그리퍼 Action, Pick & Place 통합 |
| 제약/충돌 | ex08~ex09 | 자세 제약, 장애물 회피 |
| 플래너 | ex10 | OMPL 플래너 비교 분석 |
| 실시간 | ex11~ex15 | Servo, 웨이포인트 추적, IK 직접 제어 |
이 예제들을 ex01부터 순서대로 진행하면 MoveIt의 핵심 기능을 체계적으로 학습할 수 있습니다. 특히 ex07(Pick and Place)까지 완료하면 기본적인 매니퓰레이션 태스크를 구현할 수 있고, ex08 이후의 고급 예제로 실전 응용력을 키울 수 있습니다.