MoveIt教程[10]:Motion Planning Pipeline

在MoveIt中,motion planners被设置为规划路径。然而,在很多情况下,可能希望对运动规划请求进行预处理,或者对规划的路径进行后处理[例如,对时间参数化]。在这种情况下,使用规划管道,其中链的motion planner与预处理和后处理阶段。预处理和后处理阶段称为计划请求适配器,可以通过ROS参数服务器的名称进行配置。在本教程中,将运行C++代码来实例化和调用这样的规划管道。
一.Running the Code
Roslaunch launch文件运行代码直接从moveit_tutorials:

roslaunch moveit_tutorials motion_planning_pipeline_tutorial.launch

注意:本教程使用RvizVisualToolsGui面板逐步演示。要将此面板添加到RViz,请遵循可视化教程中的说明。
过一会儿,RViz窗口应该会出现,并且看起来与页面顶部的窗口相似。要完成每个演示步骤,要么按下屏幕底部RvizVisualToolsGui面板中的Next按钮,要么在屏幕顶部的Tools面板中选择Key Tool,然后在RViz聚焦时按键盘上的N。

二.Expected Output
在RViz中,应该能够看到三种最终被重放的轨迹:
[1]robot将它的右臂移动到它前面的姿势目标
[2]robot将右臂移动到关节目标的一侧
[3]robot将它的右臂移动回它前面的初始姿势目标

三.The Entire Code
全部代码参考文献[2]。
1.Start
设置开始使用规划管道非常简单。在加载规划器之前,需要两个对象,一个RobotModel和一个PlanningScene。
将从实例化一个RobotModelLoader对象开始,该对象将在ROS参数服务器上查找robot描述并构造一个供使用的RobotModel。

robot_model_loader::RobotModelLoader robot_model_loader("robot_description");
robot_model::RobotModelPtr robot_model = robot_model_loader.getModel();

利用RobotModel,可以构建一个保持世界状态[包括机器人]的PlanningScene。

planning_scene::PlanningScenePtr planning_scene(new planning_scene::PlanningScene(robot_model));

现在可以设置PlanningPipeline对象,它将使用ROS参数服务器来确定要使用的请求适配器集和planning插件。

planning_pipeline::PlanningPipelinePtr planning_pipeline(
    new planning_pipeline::PlanningPipeline(robot_model, node_handle, "planning_plugin", "request_adapters"));

2.Visualization
MoveItVisualTools包提供了许多在RViz中可视化对象、机器人和轨迹的功能,还提供了调试工具,比如脚本的分步内省。

namespace rvt = rviz_visual_tools;
moveit_visual_tools::MoveItVisualTools visual_tools("panda_link0");
visual_tools.deleteAllMarkers();

/* Remote control is an introspection tool that allows users to step through a high level script
   via buttons and keyboard shortcuts in RViz */
visual_tools.loadRemoteControl();

/* RViz provides many types of markers, in this demo we will use text, cylinders, and spheres*/
Eigen::Affine3d text_pose = Eigen::Affine3d::Identity();
text_pose.translation().z() = 1.75;
visual_tools.publishText(text_pose, "Motion Planning Pipeline Demo", rvt::WHITE, rvt::XLARGE);

/* Batch publishing is used to reduce the number of messages being sent to RViz for large visualizations */
visual_tools.trigger();

/* Sleep a little to allow time to startup rviz, etc..
   This ensures that visual_tools.prompt() isn't lost in a sea of logs*/
ros::Duration(10).sleep();

/* We can also use visual_tools to wait for user input */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to start the demo");

3.Pose Goal
现在,将为Panda的右臂创建一个运动计划请求,指定所需的末端执行器的姿势作为输入。

planning_interface::MotionPlanRequest req;
planning_interface::MotionPlanResponse res;
geometry_msgs::PoseStamped pose;
pose.header.frame_id = "panda_link0";
pose.pose.position.x = 0.3;
pose.pose.position.y = 0.0;
pose.pose.position.z = 0.75;
pose.pose.orientation.w = 1.0;

位置公差0.01m,方向公差0.01弧度。

std::vector<double> tolerance_pose(3, 0.01);
std::vector<double> tolerance_angle(3, 0.01);

将使用kinematic_constraints包中提供的帮助函数创建请求作为约束。

req.group_name = "panda_arm";
moveit_msgs::Constraints pose_goal =
    kinematic_constraints::constructGoalConstraints("panda_link8", pose, tolerance_pose, tolerance_angle);
req.goal_constraints.push_back(pose_goal);

现在,调用管道并检查规划是否成功。

planning_pipeline->generatePlan(planning_scene, req, res);
/* Check that the planning was successful */
if (res.error_code_.val != res.error_code_.SUCCESS)
{
  ROS_ERROR("Could not compute plan successfully");
  return 0;
}

4.Visualize the result

ros::Publisher display_publisher =
    node_handle.advertise<moveit_msgs::DisplayTrajectory>("/move_group/display_planned_path", 1, true);
moveit_msgs::DisplayTrajectory display_trajectory;

/* Visualize the trajectory */
ROS_INFO("Visualizing the trajectory");
moveit_msgs::MotionPlanResponse response;
res.getMessage(response);

display_trajectory.trajectory_start = response.trajectory_start;
display_trajectory.trajectory.push_back(response.trajectory);
display_publisher.publish(display_trajectory);

/* Wait for user input */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to continue the demo");

5.Joint Space Goals

/* First, set the state in the planning scene to the final state of the last plan */
robot_state::RobotState& robot_state = planning_scene->getCurrentStateNonConst();
planning_scene->setCurrentState(response.trajectory_start);
const robot_model::JointModelGroup* joint_model_group = robot_state.getJointModelGroup("panda_arm");
robot_state.setJointGroupPositions(joint_model_group, response.trajectory.joint_trajectory.points.back().positions);

现在,建立一个关节空间目标:

robot_state::RobotState goal_state(robot_model);
std::vector<double> joint_values = { -1.0, 0.7, 0.7, -1.5, -0.7, 2.0, 0.0 };
goal_state.setJointGroupPositions(joint_model_group, joint_values);
moveit_msgs::Constraints joint_goal = kinematic_constraints::constructGoalConstraints(goal_state, joint_model_group);

req.goal_constraints.clear();
req.goal_constraints.push_back(joint_goal);

调用管道并可视化轨迹:

planning_pipeline->generatePlan(planning_scene, req, res);
/* Check that the planning was successful */
if (res.error_code_.val != res.error_code_.SUCCESS)
{
  ROS_ERROR("Could not compute plan successfully");
  return 0;
}
/* Visualize the trajectory */
ROS_INFO("Visualizing the trajectory");
res.getMessage(response);
display_trajectory.trajectory_start = response.trajectory_start;
display_trajectory.trajectory.push_back(response.trajectory);

现在应该看到两个规划的轨迹串联在一起:

display_publisher.publish(display_trajectory);

/* Wait for user input */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to continue the demo");

6.Using a Planning Request Adapter
规划请求适配器允许指定一系列操作,这些操作应该在规划发生之前或在对结果路径进行规划之后发生。

/* First, set the state in the planning scene to the final state of the last plan */
robot_state = planning_scene->getCurrentStateNonConst();
planning_scene->setCurrentState(response.trajectory_start);
robot_state.setJointGroupPositions(joint_model_group, response.trajectory.joint_trajectory.points.back().positions);

现在,设置一个关节稍微超出其上限:

const robot_model::JointModel* joint_model = joint_model_group->getJointModel("panda_joint3");
const robot_model::JointModel::Bounds& joint_bounds = joint_model->getVariableBounds();
std::vector<double> tmp_values(1, 0.0);
tmp_values[0] = joint_bounds[0].min_position_ - 0.01;
robot_state.setJointPositions(joint_model, tmp_values);
req.goal_constraints.clear();
req.goal_constraints.push_back(pose_goal);

再次调用planner,并可视化轨迹:

 planning_pipeline->generatePlan(planning_scene, req, res);
  if (res.error_code_.val != res.error_code_.SUCCESS)
  {
    ROS_ERROR("Could not compute plan successfully");
    return 0;
  }
  /* Visualize the trajectory */
  ROS_INFO("Visualizing the trajectory");
  res.getMessage(response);
  display_trajectory.trajectory_start = response.trajectory_start;
  display_trajectory.trajectory.push_back(response.trajectory);
  /* Now you should see three planned trajectories in series*/
  display_publisher.publish(display_trajectory);

  /* Wait for user input */
  visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to finish the demo");

  ROS_INFO("Done");
  return 0;
}

四.The Launch File
全部launch文件参考文献[3]。本教程中的所有代码都可以从moveit_tutorials包[MoveIt的一部分]编译并运行设置。

参考文献:
[1]Motion Planning Pipeline:http://docs.ros.org/kinetic/api/moveit_tutorials/html/doc/motion_planning_pipeline/motion_planning_pipeline_tutorial.html
[2]motion_planning_pipeline:https://github.com/ros-planning/moveit_tutorials/tree/kinetic-devel/doc/motion_planning_pipeline
[3]motion_planning_pipeline_tutorial:https://github.com/ros-planning/moveit_tutorials/tree/kinetic-devel/doc/motion_planning_pipeline/launch/motion_planning_pipeline_tutorial.launch