client要操作机械臂,首先调用MoveGroupInterfaceImpl::plan(Plan& plan)为此次操作做规划,内中会发送moveit_msgs::MoveGroupGoal请求action。对应到server会触发MoveGroupMoveAction回调move服务的处理例程:executeMoveCallback。
- 规划轨迹先是存在ompl_simple_setup_,然后转换到robot_trajectory::RobotTrajectory类型,存放在res.trajectory_,最后通过convertToMsg转换成client希望的moveit_msgs::RobotTrajectory类型,并存储在action_res.planned_trajectory。
- 规划要使用moveit三大插件之一的运动规划器。默认使用OMPLPlannerManager。
- 运动规划器插件包括两个部分:一个派生于PlannerManager的插件对象(OMPL:OMPLPlannerManager),一个派生于PlanningContext的(OMPL:ModelBasedPlanningContext),后者负责规划出轨迹。
一、MoveGroupMoveAction::executeMoveCallback
@goal[IN]:client想让机械臂移动到的位置。
void MoveGroupMoveAction::executeMoveCallback(const moveit_msgs::MoveGroupGoalConstPtr& goal)
{
setMoveState(PLANNING);
// before we start planning, ensure that we have the latest robot state received...
context_->planning_scene_monitor_->waitForCurrentRobotState(ros::Time::now());
context_->planning_scene_monitor_->updateFrameTransforms();
moveit_msgs::MoveGroupResult action_res;
if (goal->planning_options.plan_only || !context_->allow_trajectory_execution_)
{
if (!goal->planning_options.plan_only)
ROS_WARN_NAMED(getName(), "This instance of MoveGroup is not allowed to execute trajectories "
"but the goal request has plan_only set to false. "
"Only a motion plan will be computed anyway.");参数plan_only默认值true(1),allow_trajectory_execution_来自参数allow_trajectory_execution,默认true。经过executeMoveCallbackPlanOnly后,action_res.planned_trajectory应该存储着要返回给client的、此次规划出的轨迹。
executeMoveCallbackPlanOnly(goal, action_res);
}
else
executeMoveCallbackPlanAndExecute(goal, action_res);
bool planned_trajectory_empty = trajectory_processing::isTrajectoryEmpty(action_res.planned_trajectory);
std::string response =
getActionResultString(action_res.error_code, planned_trajectory_empty, goal->planning_options.plan_only);
if (action_res.error_code.val == moveit_msgs::MoveItErrorCodes::SUCCESS)
move_action_server_->setSucceeded(action_res, response);
else
{
if (action_res.error_code.val == moveit_msgs::MoveItErrorCodes::PREEMPTED)
move_action_server_->setPreempted(action_res, response);
else
move_action_server_->setAborted(action_res, response);
}
setMoveState(IDLE);
preempt_requested_ = false;
}
二、MoveGroupMoveAction::executeMoveCallbackPlanOnly
@goal[IN]:client想让机械臂移动到的位置。
void MoveGroupMoveAction::executeMoveCallbackPlanOnly(const moveit_msgs::MoveGroupGoalConstPtr& goal,
moveit_msgs::MoveGroupResult& action_res)
{
ROS_INFO_NAMED(getName(), "Planning request received for MoveGroup action. Forwarding to planning pipeline.");
// lock the scene so that it does not modify the world representation while diff() is called
planning_scene_monitor::LockedPlanningSceneRO lscene(context_->planning_scene_monitor_);
const planning_scene::PlanningSceneConstPtr& the_scene =
(moveit::core::isEmpty(goal->planning_options.planning_scene_diff)) ?
static_cast(lscene) :
lscene->diff(goal->planning_options.planning_scene_diff);
planning_interface::MotionPlanResponse res;
if (preempt_requested_)
{
ROS_INFO_NAMED(getName(), "Preempt requested before the goal is planned.");
action_res.error_code.val = moveit_msgs::MoveItErrorCodes::PREEMPTED;
return;
}
// Select planning_pipeline to handle request
const planning_pipeline::PlanningPipelinePtr planning_pipeline = resolvePlanningPipeline(goal->request.pipeline_id);
if (!planning_pipeline)
{
action_res.error_code.val = moveit_msgs::MoveItErrorCodes::FAILURE;
return;
}
try
{
planning_pipeline->generatePlan(the_scene, goal->request, res);
generatePlan(...)执行规划任务,规划轨迹存放在res.trajectory_。
}
catch (std::exception& ex)
{
ROS_ERROR_NAMED(getName(), "Planning pipeline threw an exception: %s", ex.what());
res.error_code_.val = moveit_msgs::MoveItErrorCodes::FAILURE;
}
convertToMsg(res.trajectory_, action_res.trajectory_start, action_res.planned_trajectory);res.trajectory_存储着规划轨迹,但类型是robot_trajectory::RobotTrajectory,convertToMsg要把它转成client希望的moveit_msgs::RobotTrajectory,并存储在action_res.planned_trajectory。转换可这么认为waypoints_[n].position_对应到points[n].positions,waypoints_[n].velocities_对应到points[n].velocities。
action_res.error_code = res.error_code_; action_res.planning_time = res.planning_time_; }
三、PlanningPipeline::generatePlan
bool planning_pipeline::PlanningPipeline::generatePlan(const planning_scene::PlanningSceneConstPtr& planning_scene,
const planning_interface::MotionPlanRequest& req,
planning_interface::MotionPlanResponse& res) const
{
std::vector<std::size_t> dummy;
return generatePlan(planning_scene, req, res, dummy);
}- @req[IN]:来自中goal的request。
- @res[OUT]:规划出的结果将存放在它的res.trajectory_,类型是robot_trajectory::RobotTrajectory。
bool planning_pipeline::PlanningPipeline::generatePlan(const planning_scene::PlanningSceneConstPtr& planning_scene,
const planning_interface::MotionPlanRequest& req,
planning_interface::MotionPlanResponse& res,
std::vector<std::size_t>& adapter_added_state_index) const
{
// broadcast the request we are about to work on, if needed
if (publish_received_requests_)
received_request_publisher_.publish(req);
adapter_added_state_index.clear();
if (!planner_instance_)
{
ROS_ERROR("No planning plugin loaded. Cannot plan.");
return false;
}
bool solved = false;
try
{
if (adapter_chain_)
{
solved = adapter_chain_->adaptAndPlan(planner_instance_, planning_scene, req, res, adapter_added_state_index);
if (!adapter_added_state_index.empty())
{
std::stringstream ss;
for (std::size_t added_index : adapter_added_state_index)
ss << added_index << " ";
ROS_INFO("Planning adapters have added states at index positions: [ %s]", ss.str().c_str());
}
}
else
{
planning_interface::PlanningContextPtr context =
planner_instance_->getPlanningContext(planning_scene, req, res.error_code_);
solved = context ? context->solve(res) : false;planner_instance_就是三大插件之一的运动规划器插件,默认是OMPLPlannerManager。
context->solve(res)要计算出规划,规划轨迹放在res.trajectory_,但这时数据格式是robot_trajectory::RobotTrajectory。可认为它就是最后要返回的规划轨迹。
}
}
catch (std::exception& ex)
{
ROS_ERROR("Exception caught: '%s'", ex.what());
return false;
}
bool valid = true;
...
return solved && valid;
}
四、ModelBasedPlanningContext::solve
<libros>/moveit_planners/ompl/ompl_interface/src/model_based_planning_context.cpp
------
bool ompl_interface::ModelBasedPlanningContext::solve(planning_interface::MotionPlanResponse& res)
{
if (solve(request_.allowed_planning_time, request_.num_planning_attempts))
{solve(5, 1)执行规划,规划出的轨迹存储在ompl_simple_setup_。
double ptime = getLastPlanTime();
if (simplify_solutions_)
{
simplifySolution(request_.allowed_planning_time - ptime);
ptime += getLastSimplifyTime();
}
if (interpolate_)
interpolateSolution();
// fill the response
ROS_DEBUG_NAMED(LOGNAME, "%s: Returning successful solution with %lu states", getName().c_str(),
getOMPLSimpleSetup()->getSolutionPath().getStateCount());
res.trajectory_ = std::make_shared<robot_trajectory::RobotTrajectory>(getRobotModel(), getGroupName());
getSolutionPath(*res.trajectory_);上面已把规划出的轨迹存在ompl_simple_setup_,接下要把它转换到res.trajectory_,让看下RobotTrajectory中4个字段的赋值情况。
- moveit::core::RobotModelConstPtr robot_model_。make_shared时第一个参数getRobotModel()。
- const moveit::core::JointModelGroup* group_。make_shared时第一个参数getGroupName()。ompl把轨迹存放在ompl::geometric::PathGeometric类型的pdef_->solutions_。
- std::deque<moveit::core::RobotStatePtr> waypoints_。getSolutionPath时赋值,长度和duration_from_previous_的一样。
- std::deque<double> duration_from_previous_。getSolutionPath时赋值,长度和waypoints_的一样。
res.planning_time_ = ptime;
return true;
}
else
{
ROS_INFO_NAMED(LOGNAME, "Unable to solve the planning problem");
res.error_code_.val = moveit_msgs::MoveItErrorCodes::PLANNING_FAILED;
return false;
}
}