https://moveit.picknik.ai/main/doc/examples/move_group_interface/move_group_interface_tutorial.html#
Move Group C++ Interface — MoveIt Documentation: Rolling documentation
Move Group C++ Interface In MoveIt, the simplest user interface is through the MoveGroupInterface class. It provides easy to use functionality for most operations that a user may want to carry out, specifically setting joint or pose goals, creating motion
moveit.picknik.ai
Planning to a Pose goal
geometry_msgs::msg::Pose target_pose1;
target_pose1.orientation.w = 1.0;
target_pose1.position.x = 0.28;
target_pose1.position.y = -0.2;
target_pose1.position.z = 0.5;
move_group.setPoseTarget(target_pose1);
엔드 이펙터 (로봇팔의 끝, 여기서는 그리퍼) 위치를 설정해줌
moveit::planning_interface::MoveGroupInterface::Plan my_plan;
bool success = (move_group.plan(my_plan) == moveit::core::MoveItErrorCode::SUCCESS);
RCLCPP_INFO(LOGGER, "Visualizing plan 1 (pose goal) %s", success ? "" : "FAILED");
실제로 움직이지는 않고 경로를 계산
변수 success에 성공여부를 담는다.
//목표 자세를 축과 레이블로 표시
visual_tools.publishAxisLabeled(target_pose1, "pose1");
//Rviz에 텍스트 추가
visual_tools.publishText(text_pose, "Pose_Goal", rvt::WHITE, rvt::XLARGE);
//계획된 경로 선으로 표시
visual_tools.publishTrajectoryLine(my_plan.trajectory, joint_model_group);
//모든 마커 한번에 업데이트
visual_tools.trigger();
//다음 단계로 이동하라고 사용자에게 알림
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to continue the demo");
/* Uncomment below line when working with a real robot */
/* move_group.move(); */
Blocking 함수로 실제 로봇이 있고 컨트롤러가 활성화되어야 한다.
Blocking 함수는 작업 완료 전까지 프로그램의 실행 흐름이 멈춤
로봇 제어나 실시간 시스템처럼 연속적으로 데이터를 처리해야 하는 경우에는 Non-blocking 함수가 더 적합.
Planning to a joint-space goal
로봇의 각 관절(joint)의 각도를 목표로 설정하여 로봇을 제어하는 방식
moveit::core::RobotStatePtr current_state = move_group.getCurrentState(10);
로봇의 현재 상태를 10ms 내에 가져온다 .
std::vector<double> joint_group_positions;
current_state->copyJointGroupPositions(joint_model_group, joint_group_positions);
joint_model_group : 해당그룹의 관절모델 참조
joint_group_positions : 로봇의 각 관절(joint)의 현재 위치(각도)를 저장하는 벡터
joint_group_positions[0] = -1.0; // radians
bool within_bounds = move_group.setJointValueTarget(joint_group_positions);
첫번째 관절의 각도를 -1.0으로 설정(라디안 단위)
if (!within_bounds)
{
RCLCPP_WARN(LOGGER, "Target joint position(s) were outside of limits, but we will plan and clamp to the limits ");
}
허용각도를 벗어났는지 안정성 확인
move_group.setMaxVelocityScalingFactor(0.05);
move_group.setMaxAccelerationScalingFactor(0.05);
속도, 가속도 설정
기본값은 10%
이러한 기본값은 joint_limits.yaml 파일에서 설정
후에 위와같이 모션화, 시각화 함
Planning with Path Constraints
OrientationConstraint는 로봇의 특정 링크가 움직이는 동안 지정된 방향(orientation)을 유지하도록 제한을 설정
복잡한 작업이나 제약된 특정 공간에서 사용하면 유용함
moveit_msgs::msg::OrientationConstraint ocm;
ocm.link_name = "panda_link7"; // 제한을 적용할 링크 이름
ocm.header.frame_id = "panda_link0"; // 기준이 되는 프레임
ocm.orientation.w = 1.0; // 링크의 목표 쿼터니언 값 (여기선 단순히 특정 방향을 유지)
ocm.absolute_x_axis_tolerance = 0.1; // x축 방향의 허용 오차
ocm.absolute_y_axis_tolerance = 0.1; // y축 방향의 허용 오차
ocm.absolute_z_axis_tolerance = 0.1; // z축 방향의 허용 오차
ocm.weight = 1.0; // 이 제약 조건의 중요도 (1.0은 가장 중요)
moveit_msgs::msg::Constraints test_constraints;
test_constraints.orientation_constraints.push_back(ocm); // OrientationConstraint 추가
move_group.setPathConstraints(test_constraints); // 경로 제약 조건 설정
Enforce Planning in Joint Space
Cartesian Space: 로봇의 엔드 이펙터(끝 부분)가 3D 공간에서 움직이는 경로를 직접적으로 고려.
Joint Space: 로봇의 관절 각도를 기준으로 움직임을 계획.
Path Constraints
엔드 이펙터가 특정 방향이나 자세를 유지해야하는 경우 사용된다.
기본적으로 MoveIt은 IK(역운동학)를 사용해 Cartesian Space에서 표본을 생성.
Joint Space 강제 설정
enforce_joint_model_state_space: true로 설정 시 모든 계획이 Joint Space에서 수행됩니다. 이 경우 rejection sampling(유효하지 않은 표본은 버림)을 사용하여 적합한 표본을 찾습니다.
1.
// 현재 로봇 상태를 기반으로 새로운 상태 생성
moveit::core::RobotState start_state(*move_group.getCurrentState());
// 새로운 시작 포즈 정의
geometry_msgs::msg::Pose start_pose2;
start_pose2.orientation.w = 1.0;
start_pose2.position.x = 0.55; // X 위치
start_pose2.position.y = -0.05; // Y 위치
start_pose2.position.z = 0.8; // Z 위치
// Inverse Kinematics를 사용하여 Joint Model Group에 적합한 상태 설정
start_state.setFromIK(joint_model_group, start_pose2);
// MoveGroup에 시작 상태 설정
move_group.setStartState(start_state);
새로운 시작 위치를 설정한다.
* 여기서 아직 target_pose가 없는 역기구학을 왜 쓰는지?
시작포즈에 도달하기 위해서도 역기구학 필요
setFromIK() 함수는 주로 목표 포즈에 도달하기 위한 로봇의 시작 상태를 설정할 때 사용됨
이는 역기구학을 통해 로봇의 **관절 값(joint angles)**을 계산하여 로봇이 목표 포즈에 도달할 수 있는 초기 상태를 설정
* plan() 과 setFromIK()
- **setFromIK()**는 목표 포즈에 대해 로봇이 도달할 수 있는 관절 값을 계산하고, 이를 로봇의 시작 상태로 설정하는 데 사용됩니다. 즉, 목표에 도달할 수 있는 상태를 초기화하는 것입니다.
- **plan()**은 목표 포즈 또는 목표 관절 값을 설정한 후, 로봇이 그 목표에 도달하는 경로를 계획하고 생성하는 데 사용됩니다. 이 함수는 로봇의 경로를 계산하고, 이를 RViz에 시각화할 수 있도록 합니다.
둘다 역기구학 씀
2.
// 목표 포즈 설정
move_group.setPoseTarget(target_pose1);
3.
// 기본 5초에서 10초로 계획 시간을 증가
move_group.setPlanningTime(10.0);
// 계획 생성
success = (move_group.plan(my_plan) == moveit::core::MoveItErrorCode::SUCCESS);
RCLCPP_INFO(LOGGER, "Visualizing plan 3 (constraints) %s", success ? "" : "FAILED");
경로제약조건은 계산이 오래 걸려서 시간 조정 해야함.
4.
시각화후
5.
move_group.clearPathConstraints();
경로제약조건 제거해야함
Cartesian Paths
엔드 이펙터가 여러 웨이포인트를 따라 이동하도록 카르테시안 경로(Cartesian Path)를 계획
카르테시안 경로 계획은 엔드 이펙터가 지나야 하는 위치들을 지정하고, 그 위치들을 따라 부드럽게 이동하는 경로를 생성.
1. 웨이포인트 정의
std::vector<geometry_msgs::msg::Pose> waypoints;
waypoints.push_back(start_pose2); // 시작 포즈 추가 (초기 위치)
geometry_msgs::msg::Pose target_pose3 = start_pose2; // 시작 포즈를 복사해서 수정
// target_pose3을 아래, 오른쪽, 그 후 위와 왼쪽으로 이동시킴
target_pose3.position.z -= 0.2;
waypoints.push_back(target_pose3); // 아래로 이동
target_pose3.position.y -= 0.2;
waypoints.push_back(target_pose3); // 오른쪽으로 이동
target_pose3.position.z += 0.2;
target_pose3.position.y += 0.2;
target_pose3.position.x -= 0.2;
waypoints.push_back(target_pose3); // 위로 + 왼쪽으로 이동
2. 카르테시안 경로 계산
const double eef_step = 0.01; // 엔드 이펙터가 한 번에 이동하는 최대 거리 (1cm)
moveit_msgs::msg::RobotTrajectory trajectory;
double fraction = move_group.computeCartesianPath(waypoints, eef_step, trajectory);
RCLCPP_INFO(LOGGER, "Visualizing plan 4 (Cartesian path) (%.2f%% achieved)", fraction * 100.0);
3. 시각화

Attaching objects to the robot
로봇에 객체를 부착하고, 부착된 객체를 함께 움직이며 경로 계획
1. 부착할 객체 정의
moveit_msgs::msg::CollisionObject object_to_attach;
object_to_attach.id = "cylinder1";
shape_msgs::msg::SolidPrimitive cylinder_primitive;
cylinder_primitive.type = primitive.CYLINDER;
cylinder_primitive.dimensions.resize(2);
cylinder_primitive.dimensions[primitive.CYLINDER_HEIGHT] = 0.20;
cylinder_primitive.dimensions[primitive.CYLINDER_RADIUS] = 0.04;
2. 객체의 위치와 프레임 정의
object_to_attach.header.frame_id = move_group.getEndEffectorLink();
geometry_msgs::msg::Pose grab_pose;
grab_pose.orientation.w = 1.0;
grab_pose.position.z = 0.2;
frame_id 는 엔드 이펙터 링크로 z방향으로 0.2m 떨어진곳에 설정
3. 객체 월드에 추가
object_to_attach.primitives.push_back(cylinder_primitive);
object_to_attach.primitive_poses.push_back(grab_pose);
object_to_attach.operation = object_to_attach.ADD;
planning_scene_interface.applyCollisionObject(object_to_attach);
객체를 환경에 놓고 집을 수 있는 위치에 놓는다.
4. 객체를 로봇에 부착
RCLCPP_INFO(LOGGER, "Attach the object to the robot");
std::vector<std::string> touch_links;
//터치 링크를 정의하여, 그리퍼의 손가락과의 충돌을 허용
touch_links.push_back("panda_rightfinger");
touch_links.push_back("panda_leftfinger");
//객체가 로봇손에 부착되도록함 panda_hand는 객체가 부착될 로봇 링크
move_group.attachObject(object_to_attach.id, "panda_hand", touch_links);
5. 경로 계획후 시각화
move_group.setStartStateToCurrentState();
success = (move_group.plan(my_plan) == moveit::core::MoveItErrorCode::SUCCESS);
RCLCPP_INFO(LOGGER, "Visualizing plan 7 (move around cuboid with cylinder) %s", success ? "" : "FAILED");
visual_tools.publishTrajectoryLine(my_plan.trajectory, joint_model_group);
visual_tools.trigger();
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window once the plan is complete");
