AEGLOSLAB ROS 2 Jazzy 대전

제9부 매니퓰레이션

9.2 계획 파이프라인 — OMPL·Pilz·STOMP

9.2.1 이 장에서 배우는 것 / 선수 지식

"팔을 A에서 B로 옮겨라"는 요청이 궤적이 되기까지 무슨 일이 일어나는가. 이 장이 그 과정이다.

핵심 난점은 차원이다. 6자유도 팔의 관절 공간은 6차원이고, 그 안에서 충돌 없는 경로를 찾아야 한다. 격자로 나누면 셀 수가 기하급수적으로 늘어나므로(각 축 100분할이면 $100^6 = 10^{12}$), 격자 탐색이 불가능하다. 이것이 Nav2(8.5)와의 근본적 차이이며, 표본 기반 계획이 쓰이는 이유다.

배우는 것:

  • 계획 요청의 구조 — 무엇을 지정하는가.
  • 파이프라인 — 어댑터가 무엇을 하는가.
  • OMPL — 표본 기반 계획의 성질.
  • Pilz — 산업용 결정론적 동작.
  • 최적화 기반 — CHOMP·STOMP.
  • 직교 경로(Cartesian) — 직선 이동.
  • 시간 파라미터화 — 경로가 궤적이 되는 단계.
  • 실패 진단 — 계획이 안 될 때.
  • 2.12.4 플러그인 전수 대조(9.2.12) — 어댑터 개편·플래너 클래스명·저크 설정·오류 코드 전체.

선수 지식: 9.1(MoveIt 구조·SRDF), 9.3(Planning Scene — 함께 읽으면 좋다), 6.5.5(궤적 컨트롤러), 8.5(Nav2 플래너 — 대조).

실행 확인(2026-08-16): MoveIt 2.12.4를 설치해 대조했다. STOMP·CHOMP·Pilz는 Jazzy에 전부 실재한다 — ros-jazzy-moveit-planners-stomp·-chomp·ros-jazzy-pilz-industrial-motion-planner(모두 2.12.4)로 설치되며 각각 플러그인 XML을 등록한다. 아래 OMPL 플래너 이름도 실물과 대조했다 — geometric::RRTConnect·geometric::RRTstar·geometric::PRM·geometric::RRT·geometric::TRRT·geometric::BiTRRT·geometric::LBKPIECE 등 전부 실재한다. 파라미터 이름·기본값·성능은 여전히 미검증이다.

정직: 플래너 목록·파라미터는 배포판마다 다르다(확인 필요). 계획 시간·성공률 수치는 로봇·환경 의존이므로 제시하지 않는다(3.6.1). 알고리즘 이론은 자매서 robotics 제11부(RB-277~RB-300)가 다룬다(집필 상태는 A.5.9). 이 환경에서 검증하지 않았다(목차 3).

9.2.2 계획 요청

무엇을 지정하는가.

항목설명
시작 상태대개 현재 관절 상태(5.5)
목표관절 값·자세·제약
계획 그룹SRDF의 <group>(9.1.3)
제약(constraints)경로 중 지켜야 할 조건
허용오차목표 도달 판정
계획 시간상한
시도 횟수재시도
속도·가속 스케일궤적 속도

목표의 세 형태:

// ① 관절 공간 — IK 불필요, 확실
move_group.setJointValueTarget({0.0, -1.57, 1.57, 0.0, 1.57, 0.0});

// ② 자세 공간 — IK 필요
move_group.setPoseTarget(target_pose);

// ③ 이름 붙은 자세 (SRDF)
move_group.setNamedTarget("home");
형태장점단점
관절 공간IK 불필요·해가 유일손끝 위치를 직접 지정 못 함
자세 공간직관적IK 실패 가능·다중해
이름 자세재현성고정

함정: 자세 목표는 IK가 먼저 풀려야 한다. IK가 실패하면 계획은 시작도 못 하며, 오류가 "계획 실패"로 보여 원인을 오해하기 쉽다. 작업 공간 밖이거나 특이점 근처면 IK가 실패한다(9.6.3).

견해: 반복 작업의 자세는 관절 공간으로 저장하는 것이 안정적이다. 자세 목표는 IK가 매번 다른 해를 낼 수 있어 팔의 자세(elbow up/down)가 바뀔 수 있다. 한 번 좋은 해를 찾았으면 관절 값으로 기록해 두면 재현성이 보장된다.

제약(constraints):

moveit_msgs::msg::OrientationConstraint ocm;
ocm.link_name = "tool0";
ocm.header.frame_id = "base_link";
ocm.orientation.w = 1.0;
ocm.absolute_x_axis_tolerance = 0.1;
ocm.absolute_y_axis_tolerance = 0.1;
ocm.absolute_z_axis_tolerance = 3.14;    // z축 회전은 자유
ocm.weight = 1.0;

moveit_msgs::msg::Constraints constraints;
constraints.orientation_constraints.push_back(ocm);
move_group.setPathConstraints(constraints);
제약 종류용도
방향(orientation)컵을 기울이지 않고 이송
위치(position)특정 영역 안에서만
관절(joint)관절 범위 제한
가시성(visibility)카메라 시야 유지

함정: 제약이 있으면 계획이 크게 어려워진다. 표본 기반 플래너는 무작위 표본이 제약을 만족해야 하는데, 제약이 좁으면 유효 표본이 거의 안 나온다. 계획 시간이 급증하거나 실패하며, 이것이 "제약을 설정했더니 계획이 안 된다"의 원인이다.

견해: 제약을 최소한으로 쓰라. 컵을 나르는 경우처럼 정말 필요할 때만 쓰고, 허용오차를 가능한 한 넓게 준다. 회전 대칭 축(예: 원통형 물체의 z축)은 3.14로 열어 두는 것이 계획 성공률을 크게 높인다.

9.2.3 파이프라인 구조

계획 요청 Pre-adapter 체인 플래너 OMPL · Pilz · CHOMP … Post-adapter 체인 궤적(JointTrajectory) FixStartStateBounds FixStartStateCollision FixWorkspaceBounds AddTimeParameterization 경로 평활화

어댑터가 하는 일:

어댑터역할
CheckStartStateBounds시작 상태가 한계를 약간 벗어난 경우 보정
CheckStartStateCollision시작이 충돌 중이면 소폭 이동
ValidateWorkspaceBounds작업 공간 기본값 설정
ResolveConstraintFrames제약 프레임 해석
AddTimeOptimalParameterization경로 → 시간 궤적(9.2.7)
AddRuckigTrajectorySmoothing부드러운 가감속(실행 확인)

위 이름은 MoveIt 2.12.4에서 확인한 것이다. 예전 문서·예제에 나오는 Fix* 계열 이름은 더 이상 쓰이지 않는다 — 무엇이 어떻게 바뀌었는지는 9.2.12.1.

2.12.4에서 전수 확인했다(9.2.12.1) — 요청 어댑터 5개·응답 어댑터 4개. 배포판이 바뀌면 또 달라질 수 있으므로 share/moveit_ros_planning/*_plugin_description.xml을 직접 보는 습관이 낫다. 설정은 대개 launch나 파라미터에 있다.

FixStartStateBounds가 실무적으로 중요하다. 실제 로봇의 관절이 한계를 아주 약간 넘은 상태로 정지하는 일이 흔한데(엔코더 오차·기계적 여유), 이 어댑터가 없으면 "시작 상태가 유효하지 않다"로 계획이 실패한다.

함정: FixStartStateCollision이 시작 충돌을 "해결"하는 것은 임시방편이다. 실제로 팔이 무언가에 닿아 있다면 그것을 인지하고 처리해야 하며, 자동 보정에 의존하면 실제 충돌 상태에서 계획이 나올 수 있다.

9.2.4 OMPL — 표본 기반

MoveIt의 기본 플래너 라이브러리다.

동작 원리: 관절 공간에서 무작위 표본을 추출하고, 충돌 없는 표본들을 연결해 그래프를 만들고, 시작에서 목표까지의 경로를 찾는다.

자유 예 충돌 아니오 무작위 표본 생성 충돌 검사 기존 트리에 연결 시도 목표에 도달? 경로 반환

주요 알고리즘:

알고리즘특징
RRTConnect양방향 트리 — 빠름. 널리 쓰이는 기본
RRT*점근적 최적 — 시간을 더 쓰면 나은 경로
PRM로드맵 — 반복 질의에 유리
BiTRRT·LBKPIECE 등다양한 변형
# ompl_planning.yaml
arm:
  default_planner_config: RRTConnectkConfigDefault
  planner_configs:
    RRTConnectkConfigDefault:
      type: geometric::RRTConnect
      range: 0.0                    # 0이면 자동
    RRTstarkConfigDefault:
      type: geometric::RRTstar
      range: 0.0
      goal_bias: 0.05
      delay_collision_checking: 1
  longest_valid_segment_fraction: 0.005
  projection_evaluator: joints(joint1,joint2)

표본 기반의 성질 — 이해해야 할 것들:

성질함의
확률적 완전성해가 있으면 시간이 무한하면 찾는다. 유한 시간에는 보장 없음
비결정적실행마다 다른 경로
경로가 예쁘지 않음무작위 표본을 잇는다
좁은 통로에 약함표본이 그 안에 잘 안 떨어짐
차원에 상대적으로 강건격자 탐색보다 유리

함정: "실행마다 다른 경로"가 산업 응용에서 문제가 된다. 같은 작업을 반복하는데 매번 팔이 다르게 움직이면 사이클 타임이 흔들리고, 주변 안전 검토가 어렵다. 결정론적 동작이 필요하면 Pilz(9.2.5)나 관절 궤적을 미리 기록해 재생하는 방법을 쓴다.

함정: 계획 시간을 늘리면 성공률은 오르지만 응답이 느려진다. 기본값(대개 5초)이 길게 느껴질 수 있고, 실패 시 그 시간을 다 쓴다. 응용에 맞춰 조정하되, 너무 짧으면 어려운 계획이 실패한다.

move_group.setPlanningTime(2.0);          // 초
move_group.setNumPlanningAttempts(10);    // 여러 번 시도 후 최선 선택

견해: setNumPlanningAttempts를 늘리는 것이 유용하다. 여러 번 계획해 가장 짧은 것을 고르면 경로 품질이 개선된다. RRTConnect가 빠르므로 10회 시도해도 부담이 크지 않은 경우가 많다.

9.2.5 Pilz — 산업용 결정론적 동작

산업용 로봇 프로그래밍의 익숙한 동작 명령을 제공한다.

동작의미
PTP(Point To Point)관절 공간 최단 — 손끝 경로는 곡선
LIN(Linear)손끝이 직선으로 이동
CIRC(Circular)손끝이 원호로
# 플래너 지정
move_group.setPlannerId("PTP");    # 또는 "LIN", "CIRC"

OMPL과의 근본적 차이:

축OMPLPilz
결정성비결정적결정적
경로 예측어려움명확
장애물 회피가능없음(직선·PTP 고정)
계획 시간수백 ms~초매우 짧음
용도복잡한 환경구조화된 산업 환경

견해: Pilz는 "환경이 정해져 있고 경로가 예측 가능해야 하는" 산업 응용에 맞는다. 컨베이어에서 집어 정해진 곳에 놓는 반복 작업이라면, 장애물 회피가 필요 없고 결정적 동작이 훨씬 낫다. OMPL은 환경이 변하고 회피가 필요할 때 쓴다.

함정: Pilz LIN은 충돌 회피를 하지 않는다. 직선 경로에 장애물이 있으면 계획이 실패하거나(충돌 검사가 켜져 있으면) 충돌한다. 경로상에 아무것도 없음을 보장해야 한다.

함정: LIN 경로가 특이점을 지나면 실패한다. 손끝이 직선으로 가려면 관절이 특정 방식으로 움직여야 하는데, 특이점 근처에서는 관절 속도가 발산한다(9.4.4). 작업 공간 배치 단계에서 이를 고려해야 한다(9.6.3).

9.2.6 최적화 기반과 직교 경로

9.2.6.1 CHOMP·STOMP

초기 경로를 받아 비용 함수를 최소화하며 개선한다.

축표본 기반(OMPL)최적화 기반
접근무작위 탐색경사 하강·확률적 개선
경로 품질거칠다부드럽다
초기 해 필요아니오대개 필요
지역 최소없음빠질 수 있다

Jazzy 실재는 확인했다 — ros-jazzy-moveit-planners-chomp·ros-jazzy-moveit-planners-stomp 둘 다 2.12.4이고 플러그인도 등록되어 있다(9.2.12.3). 성숙도·실제 성능은 여전히 미확인이다(확인 필요: 성숙도). 이들을 후처리(OMPL로 찾고 최적화로 다듬기)로 쓰는 구성도 가능하다.

9.2.6.2 직교 경로(Cartesian Path)

손끝을 지정한 웨이포인트를 따라 직선으로 움직인다.

std::vector<geometry_msgs::msg::Pose> waypoints;
waypoints.push_back(start_pose);
waypoints.push_back(approach_pose);
waypoints.push_back(grasp_pose);

moveit_msgs::msg::RobotTrajectory trajectory;
const double eef_step = 0.01;         // 보간 간격(m)
const double jump_threshold = 0.0;    // 관절 점프 검사
double fraction = move_group.computeCartesianPath(
    waypoints, eef_step, jump_threshold, trajectory);

if (fraction < 0.95) {
  RCLCPP_WARN(logger, "직교 경로 %.1f%%만 계획됨", fraction * 100);
  // 실패 처리 — 부분 실행은 위험할 수 있다
}

fraction이 핵심 반환값이다 — 요청한 경로의 몇 %를 계획했는지 알려준다.

fraction해석
1.0전체 성공
< 1.0일부만 — 충돌·IK 실패·특이점

함정: fraction < 1.0인데 그대로 실행하면 중간에 멈춘다. 파지 접근처럼 끝까지 가야 하는 동작에서는 위험하다. 임계(예: 0.95)를 두고 미달이면 실패 처리하라.

함정: jump_threshold를 0으로 두면 검사가 비활성화된다. 그러면 IK 해가 갑자기 바뀌어 관절이 급격히 회전하는 궤적이 나올 수 있다 — 물리적으로 위험하다. 0이 아닌 값을 주는 것이 안전하지만, 값 설정이 까다롭고 오탐도 있다(확인 필요: 권장 값·대안 API).

견해: 직교 경로는 파지 접근·이탈에 필수적이다(9.5.4). 물체를 향해 직선으로 접근하고 직선으로 빠져나와야 물체나 주변을 치지 않는다. OMPL로 계획하면 곡선으로 접근해 물체를 칠 수 있다.

9.2.7 시간 파라미터화

플래너가 만드는 것은 "경로"(위치의 열)이지 "궤적"(시간이 붙은)이 아니다.

경로: q0 → q1 → q2 → ... (시간 정보 없음) 시간 파라미터화 궤적: (q0,t0) → (q1,t1) → ... + 속도 · 가속도 joint_trajectory_controller 6.5.5

속도·가속 한계 안에서 가능한 한 빠르게 시간을 배정한다.

알고리즘특징
TOTG(Time-Optimal Trajectory Generation)널리 쓰임
Ruckig저크(jerk) 제한 지원(실행 확인 — 9.2.12.4)
IPTP구형

속도 스케일링:

move_group.setMaxVelocityScalingFactor(0.5);      // 한계의 50%
move_group.setMaxAccelerationScalingFactor(0.3);

한계는 joint_limits.yaml에서 온다(9.1.4).

견해: 속도·가속 스케일링이 실무에서 가장 자주 조정하는 값이다. 개발 중에는 0.1~0.2로 안전하게, 검증 후 높인다. 가속을 낮추는 것이 속도를 낮추는 것보다 부드러움에 효과적인 경우가 많다.

함정: 시간 파라미터화 결과가 컨트롤러의 능력을 넘으면 추종 오차가 커진다. joint_limits.yaml의 값이 실제 로봇보다 크면, 궤적이 물리적으로 불가능해져 컨트롤러가 못 따라가고 허용오차 초과로 중단된다(6.5.5). 실제 사양을 넣으라(9.1.4).

함정: 저크(가속도의 변화율)를 제한하지 않으면 동작이 거칠다. 진동이나 소음이 있다면 저크 제한을 켜라 — 설정 위치는 joint_limits.yaml의 has_jerk_limits·max_jerk이고, AddRuckigTrajectorySmoothing 응답 어댑터가 그것을 적용한다(실행 확인, 9.2.12.4). 정의하지 않으면 기본값을 쓰고 경고만 남긴다.

9.2.8 플래너 선택

아니오 예 아니오 플래너 선택 장애물 회피 필요한가? 직선 이동? Pilz LIN 또는 직교 경로 Pilz PTP
예 아니오 예 예 장애물 회피 필요한가? 결정성이 중요한가? 경로 기록·재생 또는 제약된 OMPL OMPL RRTConnect 경로 품질 중요? 시도 횟수 증가 또는 최적화 후처리
(계속 — 장애물 회피 필요한 경우)
상황선택
일반적 자유 이동OMPL RRTConnect
파지 접근·이탈직교 경로(9.2.6)
구조화된 산업 반복Pilz PTP·LIN
좁은 공간 통과OMPL + 시간 증가
부드러운 경로 필요최적화 후처리·Ruckig
제약 있는 이송OMPL + 제약(성공률 주의)

견해: 하나의 응용에서 여러 플래너를 섞어 쓰는 것이 정상이다. 자유 이동은 OMPL, 접근·이탈은 직교 경로, 정해진 자세 간 이동은 Pilz PTP. 작업 단계마다 적절한 것을 고르는 것이 실무의 모습이다(9.5).

9.2.9 계획 실패 진단

"계획이 안 된다"의 원인 후보들이다.

예 예 아니오 좁은 통로 아니오 아니오 예 제약 때문 계획 실패 IK가 풀리나? 목표가 작업 공간 밖 · 특이점 · 자세 불가능 시작 상태가 유효한가? 충돌 중 · 한계 초과 9.2.3 목표가 충돌? Planning Scene 확인 9.3 경로가 존재하나? 계획 시간 증가 · 중간 웨이포인트 제약 완화 9.2.2

오류 코드 확인:

auto result = move_group.plan(plan);
switch (result.val) {
  case moveit::core::MoveItErrorCode::SUCCESS: break;
  case moveit::core::MoveItErrorCode::PLANNING_FAILED:
    RCLCPP_ERROR(logger, "계획 실패 — 경로를 못 찾음"); break;
  case moveit::core::MoveItErrorCode::INVALID_MOTION_PLAN:
    RCLCPP_ERROR(logger, "유효하지 않은 계획"); break;
  case moveit::core::MoveItErrorCode::START_STATE_IN_COLLISION:
    RCLCPP_ERROR(logger, "시작 상태가 충돌 중"); break;
  case moveit::core::MoveItErrorCode::GOAL_IN_COLLISION:
    RCLCPP_ERROR(logger, "목표가 충돌 중"); break;
  case moveit::core::MoveItErrorCode::NO_IK_SOLUTION:
    RCLCPP_ERROR(logger, "IK 해가 없음"); break;
  // ...
}

위 여섯 코드는 Jazzy에 모두 실재한다(실행 확인). 전체 목록과 값은 9.2.12.5. 코드를 로그에 남기는 것이 진단의 출발점이며, 이것 없이는 원인을 추측하게 된다.

RViz2에서의 진단(9.1.9):

관찰의미
목표 마커가 빨갛다충돌 또는 IK 실패
목표 마커가 초록유효 — 계획은 별개 문제
시작 상태가 빨갛다현재 충돌 중
Planning Scene에 비정상적인 물체오래된 충돌 물체(9.3)

견해: RViz2의 목표 마커 색이 IK·충돌을 즉시 알려준다. 코드에서 실패를 겪기 전에 마우스로 목표를 끌어 보면 그 자세가 도달 가능한지 바로 안다. 작업 공간 설계 단계에서 이것을 활용하라(9.6.3).

흔한 원인:

원인대처
IK 실패(작업 공간 밖)로봇 배치·목표 조정(9.6.3)
시작 상태 충돌팔을 안전 자세로·Scene 정리
오래된 충돌 물체Scene 갱신(9.3.9)
자기 충돌 행렬 부실Setup Assistant 재생성(9.1.3)
제약이 너무 좁음완화(9.2.2)
계획 시간 부족증가
좁은 통로중간 웨이포인트로 분할
옥토맵 노이즈센서·필터 조정(9.3.5)

견해: "중간 웨이포인트로 분할"이 좁은 통로 문제의 실용적 해법이다. A→B가 안 되면 A→M→B로 나눈다. 사람이 보기에 명백한 중간 지점을 지정하면 각 구간이 쉬워진다. 표본 기반 플래너가 못 찾는 것을 사람의 지식으로 보완하는 것이다.

9.2.12 MoveIt 2.12.4의 플러그인·상수를 전수 대조했다 — 실측

9.1.12에서 깐 ros-jazzy-moveit 2.12.4를 그대로 써서, 이 장이 "배포판마다 다르다"며 남긴 항목을 플러그인 등록 XML·헤더·메시지 정의와 대조했다.

9.2.12.1 어댑터 이름 전체가 바뀌었고, 구조도 둘로 갈렸다

가장 큰 변화는 어댑터가 요청용·응답용으로 분리된 것이다.

요청 어댑터(default_planning_request_adapters/)응답 어댑터(default_planning_response_adapters/)
CheckStartStateBoundsAddTimeOptimalParameterization
CheckStartStateCollisionAddRuckigTrajectorySmoothing
ValidateWorkspaceBoundsDisplayMotionPath
ResolveConstraintFramesValidateSolution
CheckForStackedConstraints

책이 적었던 이름과의 대응:

이 책의 옛 이름2.12.4의 이름비고
FixStartStateBoundsCheckStartStateBounds요청
FixStartStateCollisionCheckStartStateCollision요청
FixWorkspaceBoundsValidateWorkspaceBounds요청
ResolveConstraintFrames그대로요청
AddTimeParameterizationAddTimeOptimalParameterization응답
AddRuckigTrajectorySmoothing그대로응답
—CheckForStackedConstraints·DisplayMotionPath·ValidateSolution신규

이름이 Fix*에서 Check*로 바뀌었지만 동작은 여전히 고친다. 플러그인 설명문이 그대로 말한다 — "Fixes the start state to be within the joint limits specified in the URDF." 즉 9.2.4의 서술은 유효하고 이름만 낡았던 것이다.

다만 고치는 동작이 파라미터로 갈린다. fix_start_state가 있고, 설명은 이렇다 — "If true and the start state has continuous, planar, or floating joints, the start state of the MotionPlanRequest will be updated to normalize the rotation values of those joints." 회전 관절의 값 정규화가 이 파라미터에 달려 있다.

한계를 벗어났을 때 남기는 로그도 확인했다 — 관절 이름과 초과량을 그대로 출력한다.

Joint '<이름>' from the starting state is outside bounds by: [...] should be in the range [...], [...].

이 메시지가 보이면 원인이 확정된 것이다. 9.2.4가 말한 "엔코더 오차로 한계를 약간 넘은 상태"가 실제로 이렇게 드러난다.

9.2.12.2 플래너 플러그인의 정확한 클래스 이름

플래너등록된 클래스
OMPLompl_interface::OMPLPlannerManager
CHOMPchomp_interface::CHOMPPlannerManager
STOMPstomp_moveit::StompPlannerManager
Pilzpilz_industrial_motion_planner::CommandPlanner

Pilz만 PlannerManager로 끝나지 않는다 — 이름 규칙이 다르므로 설정에 적을 때 복사해 쓰라.

패키지 이름도 Pilz만 다르다.

플래너패키지
OMPL·CHOMP·STOMPros-jazzy-moveit-planners-{ompl,chomp,stomp}
Pilzros-jazzy-pilz-industrial-motion-planner — moveit-planners- 접두어가 없다

9.2.12.3 CHOMP·STOMP는 Jazzy에 실재한다

패키지버전
ros-jazzy-moveit-planners-ompl2.12.4
ros-jazzy-moveit-planners-chomp2.12.4
ros-jazzy-moveit-planners-stomp2.12.4
ros-jazzy-pilz-industrial-motion-planner2.12.4

넷 다 MoveIt 본체와 같은 버전으로 배포된다 — MTC가 0.1.5로 따로 버전을 매기는 것(9.1.7)과 대조된다. 실재와 버전은 확정됐지만 성숙도·성능은 여전히 측정해 보지 않았다(확인 필요: 성숙도).

9.2.12.4 저크 제한 — 지원하고, 설정 위치는 joint_limits.yaml이다

RuckigSmoothing의 헤더가 명확하다.

// "Apply smoothing to a time-parameterized trajectory so that jerk limits are not violated."
static bool applySmoothing(robot_trajectory::RobotTrajectory& trajectory,
                           const std::unordered_map<std::string, double>& jerk_limits,  // rad/s^3
                           double max_velocity_scaling_factor = 1.0,
                           double max_acceleration_scaling_factor = 1.0,
                           bool mitigate_overshoot = false,
                           double overshoot_threshold = 0.01);

저크 한계는 관절별 $\mathrm{rad/s}^3$다. 명시하지 않는 오버로드도 있는데, 그때는 로봇 모델에서 읽는다 — 그리고 없으면 이렇게 경고한다.

Joint jerk limits are not defined. Using the default … rad/s^3.
You can define jerk limits in joint_limits.yaml.

설정 방법이 이 메시지 안에 있다. joint_limits.yaml에 관절별로 넣는다.

joint_limits:
  joint_1:
    has_jerk_limits: true      # 기본 false
    max_jerk: 100.0            # rad/s^3
항목값
has_jerk_limits 기본값false
max_jerk 미지정 시NaN → 라이브러리 기본값 사용 + 경고
적용 주체AddRuckigTrajectorySmoothing(응답 어댑터)
추가 손잡이mitigate_overshoot(기본 false)·overshoot_threshold(기본 0.01)

함정: 저크 제한은 기본이 꺼져 있다. "Ruckig을 쓰니 저크가 제한된다"고 믿기 쉽지만, has_jerk_limits를 켜지 않으면 라이브러리 기본값으로 매끈하게만 만들 뿐 로봇의 실제 저크 사양을 지키지 않는다. 진동·소음이 문제라면 먼저 이 경고가 로그에 있는지 보라.

9.2.12.5 MoveItErrorCodes 전체 목록

본문 예시의 여섯 코드는 모두 실재한다. 전체는 이렇다.

값이름값이름
1SUCCESS-17INVALID_ROBOT_STATE
0UNDEFINED-18INVALID_LINK_NAME
99999FAILURE-19INVALID_OBJECT_NAME
-1PLANNING_FAILED-21FRAME_TRANSFORM_FAILURE
-2INVALID_MOTION_PLAN-22COLLISION_CHECKING_UNAVAILABLE
-3MOTION_PLAN_INVALIDATED_BY_ENVIRONMENT_CHANGE-23ROBOT_STATE_STALE
-4CONTROL_FAILED-24SENSOR_INFO_STALE
-5UNABLE_TO_AQUIRE_SENSOR_DATA-25COMMUNICATION_FAILURE
-6TIMED_OUT-26START_STATE_INVALID
-7PREEMPTED-27GOAL_STATE_INVALID
-10START_STATE_IN_COLLISION-28UNRECOGNIZED_GOAL_TYPE
-11START_STATE_VIOLATES_PATH_CONSTRAINTS-29CRASH
-12GOAL_IN_COLLISION-30ABORT
-13GOAL_VIOLATES_PATH_CONSTRAINTS-31NO_IK_SOLUTION
-14GOAL_CONSTRAINTS_VIOLATED
-15INVALID_GROUP_NAME
-16INVALID_GOAL_CONSTRAINTS

읽는 방법 셋:

  • 음수 구간이 의미별로 묶여 있다 — -1~-7은 계획·실행 자체, -10~-14는 시작/목표 상태, -15~-19는 요청의 이름·인자, -21~-25는 인프라(tf·센서·통신).
  • UNABLE_TO_AQUIRE_SENSOR_DATA의 철자가 틀려 있다(AQUIRE — ACQUIRE가 아니다). 문자열로 검색할 때 검색되지 않는 원인이 된다.
  • FAILURE = 99999만 양수다. val < 0으로 실패를 판정하면 이것을 놓친다 — val != SUCCESS로 판정하는 편이 안전하다.

9.2.10 함정 카탈로그

#함정증상대처
1IK 실패를 계획 실패로 오해잘못된 곳 조사오류 코드 확인
2자세 목표의 다중해팔 자세가 매번 다름관절 목표로 기록
3제약을 과하게계획 시간 급증·실패최소화·허용오차 확대
4OMPL 비결정성사이클 타임 흔들림Pilz·경로 기록
5Pilz LIN에 회피 기대충돌·실패경로상 확인
6LIN이 특이점 통과실패·급격한 동작배치 검토(9.6.3)
7fraction < 1.0인데 실행중간에 멈춤임계 검사
8jump_threshold: 0관절 급회전 궤적값 설정·대안
9joint_limits.yaml 과대추종 오차·중단실제 사양
10저크 미제한진동·소음저크 제한 알고리즘
11계획 시간 과소어려운 계획 실패응용에 맞게
12시작 충돌 자동 보정 의존실제 충돌 은폐원인 처리
13좁은 통로에 단일 계획반복 실패웨이포인트 분할
14오류 코드 미기록원인 추측로그에 남기기
15예전 문서의 Fix* 어댑터 이름 사용로드 실패2.12는 Check*·Validate*(9.2.12.1)
16Ruckig 쓰니 저크가 제한된다고 가정기본은 꺼져 있다has_jerk_limits: true(9.2.12.4)
17val < 0으로 실패 판정FAILURE=99999를 놓침val != SUCCESS(9.2.12.5)
18Pilz를 moveit-planners- 접두어로 검색패키지를 못 찾음ros-jazzy-pilz-industrial-motion-planner(9.2.12.2)

9.2.11 체크포인트

  • 계획 요청의 여덟 항목과, 목표의 세 형태·각각의 장단을 말할 수 있는가.
  • 반복 작업의 자세를 관절 공간으로 저장하라는 조언의 근거는?
  • 제약이 계획을 어렵게 만드는 이유를 표본 기반 특성으로 설명할 수 있는가. 회전 대칭 축을 어떻게 다루는가.
  • 파이프라인의 어댑터 여섯 가지 중 FixStartStateBounds가 실무적으로 중요한 이유는?
  • 표본 기반 계획의 다섯 가지 성질을 말할 수 있는가. "확률적 완전성"이 실무에서 무엇을 의미하는가.
  • OMPL의 비결정성이 산업 응용에서 문제가 되는 이유와 대안은?
  • Pilz의 세 동작과, OMPL과의 근본적 차이 다섯 가지는? LIN의 두 가지 함정은?
  • 직교 경로의 fraction이 무엇이며, 임계 검사를 해야 하는 이유는?
  • jump_threshold를 0으로 두면 무슨 위험이 있는가.
  • 시간 파라미터화가 무엇을 하며, joint_limits.yaml이 실제 사양과 다르면 어떤 문제가 생기는가.
  • 플래너 선택 흐름을 그릴 수 있는가. 하나의 응용에서 여러 플래너를 섞는 것이 왜 정상인가.
  • 계획 실패 진단 흐름과, RViz2에서 즉시 알 수 있는 것은?
  • 좁은 통로 문제의 실용적 해법은?
  • MoveIt 2.12의 어댑터는 어떻게 둘로 나뉘며, 옛 Fix* 이름은 무엇으로 바뀌었는가(9.2.12.1).
  • 저크 제한을 실제로 켜려면 어디에 무엇을 적는가. 안 켜면 어떻게 되는가(9.2.12.4).
  • MoveItErrorCodes에서 val < 0으로 실패를 판정하면 안 되는 이유는(9.2.12.5).

다음 장 9.3는 계획의 전제가 되는 환경 표현 — Planning Scene과 충돌 처리를 다룬다.