规划[3/5]:笛卡儿规划

  • 生成轨迹中points点数至少“1+路点数”,1是机器人当前状态。此时每个路点都没有分步长。
  • 笛卡儿规划要用运动学求器解,用不到路径规划器。

示例

[0/2]{p: (0.281330, -0.000289, 0.296321), M: (-0.418335i+0.258038j+-0.736527k+0.464692)}

[1/2]{p: (0.381415, -0.000289, 0.118198), M: (-0.737963i+0.340484j+-0.526792k+0.248938)}

[2/2]{p: (0.298734, -0.000288, 0.293430), M: (-0.277421i+0.271752j+-0.650418k+0.652796)}

一、MoveGroupCartesianPathService::computeService

bool MoveGroupCartesianPathService::computeService(moveit_msgs::GetCartesianPath::Request& req,
                                                   moveit_msgs::GetCartesianPath::Response& res)
{
  ROS_INFO_NAMED(getName(), "Received request to compute Cartesian path");
  context_->planning_scene_monitor_->updateFrameTransforms();

  moveit::core::RobotState start_state =
      planning_scene_monitor::LockedPlanningSceneRO(context_->planning_scene_monitor_)->getCurrentState();
  moveit::core::robotStateMsgToRobotState(req.start_state, start_state);
  if (const moveit::core::JointModelGroup* jmg = start_state.getJointModelGroup(req.group_name))
  {
    std::string link_name = req.link_name;
    if (link_name.empty() && !jmg->getLinkModelNames().empty())
      link_name = jmg->getLinkModelNames().back();

    bool ok = true;
    EigenSTL::vector_Isometry3d waypoints(req.waypoints.size());
    const std::string& default_frame = context_->planning_scene_monitor_->getRobotModel()->getModelFrame();
    bool no_transform = req.header.frame_id.empty() ||
                        moveit::core::Transforms::sameFrame(req.header.frame_id, default_frame) ||
                        moveit::core::Transforms::sameFrame(req.header.frame_id, link_name);

    for (std::size_t i = 0; i < req.waypoints.size(); ++i)
    {
      if (no_transform)
        tf2::fromMsg(req.waypoints[i], waypoints[i]);

位姿变换[1/5]tf2::fromMsg(req.waypoints[i], waypoints[i])。位姿类型从geometry_msgs::Pose转换为Isometry3d,值不变。

waypoints[1]:{p(0.381415, -0.000289, 0.118198) quat(0.737963i+-0.340484j+0.526792k+-0.248938)}

   0.21312  -0.240252   0.947026

 -0.764808  -0.644199 0.00868566

  0.607987  -0.726145  -0.321038

      else
      {
        geometry_msgs::PoseStamped p;
        p.header = req.header;
        p.pose = req.waypoints[i];
        if (performTransform(p, default_frame))
          tf2::fromMsg(p.pose, waypoints[i]);
        else
        {
          ROS_ERROR_NAMED(getName(), "Error encountered transforming waypoints to frame '%s'", default_frame.c_str());
          ok = false;
          break;
        }
      }
    }

    if (ok)
    {
      if (req.max_step < std::numeric_limits<double>::epsilon())
      {
        ROS_ERROR_NAMED(getName(), "Maximum step to take between consecutive configrations along Cartesian path"
                                   "was not specified (this value needs to be > 0)");
        res.error_code.val = moveit_msgs::MoveItErrorCodes::FAILURE;
      }
      else
      {
        if (!waypoints.empty())
        {
          moveit::core::GroupStateValidityCallbackFn constraint_fn;
          std::unique_ptr<planning_scene_monitor::LockedPlanningSceneRO> ls;
          std::unique_ptr<kinematic_constraints::KinematicConstraintSet> kset;
          if (req.avoid_collisions || !moveit::core::isEmpty(req.path_constraints))
          {
            ls = std::make_unique<planning_scene_monitor::LockedPlanningSceneRO>(context_->planning_scene_monitor_);
            kset = std::make_unique<kinematic_constraints::KinematicConstraintSet>((*ls)->getRobotModel());
            kset->add(req.path_constraints, (*ls)->getTransforms());
            constraint_fn = std::bind(
                &isStateValid,
                req.avoid_collisions ? static_cast<const planning_scene::PlanningSceneConstPtr&>(*ls).get() : nullptr,
                kset->empty() ? nullptr : kset.get(), std::placeholders::_1, std::placeholders::_2,
                std::placeholders::_3);

req.avoid_collisions一般非0,因而会进这入口。创建了指向isStateValid的函数指针constraint_fn,它的调用时机是在运动学求解器逆解一个位姿成功后。除运动学约束,isStateValid还会把逆解出的关节状态设置向start_pose。

          }
          bool global_frame = !moveit::core::Transforms::sameFrame(link_name, req.header.frame_id);
          ROS_INFO_NAMED(getName(),
                         "Attempting to follow %u waypoints for link '%s' using a step of %lf m "
                         "and jump threshold %lf (in %s reference frame)",
                         (unsigned int)waypoints.size(), link_name.c_str(), req.max_step, req.jump_threshold,
                         global_frame ? "global" : "link");
          std::vector<moveit::core::RobotStatePtr> traj;
          res.fraction = moveit::core::CartesianInterpolator::computeCartesianPath(
              &start_state, jmg, traj, start_state.getLinkModel(link_name), waypoints, global_frame,
              moveit::core::MaxEEFStep(req.max_step), moveit::core::JumpThreshold(req.jump_threshold), constraint_fn);

traj存储着所有路点的关节状态,4个。

          moveit::core::robotStateToRobotStateMsg(start_state, res.start_state);

          robot_trajectory::RobotTrajectory rt(context_->planning_scene_monitor_->getRobotModel(), req.group_name);
          for (const moveit::core::RobotStatePtr& traj_state : traj)
            rt.addSuffixWayPoint(traj_state, 0.0);

          // time trajectory
          // \todo optionally compute timing to move the eef with constant speed
          trajectory_processing::IterativeParabolicTimeParameterization time_param;
          time_param.computeTimeStamps(rt, 1.0);

          rt.getRobotTrajectoryMsg(res.solution);
          ROS_INFO_NAMED(getName(), "Computed Cartesian path with %u points (followed %lf%% of requested trajectory)",
                         (unsigned int)traj.size(), res.fraction * 100.0);
          if (display_computed_paths_ && rt.getWayPointCount() > 0)
          {
            moveit_msgs::DisplayTrajectory disp;
            disp.model_id = context_->planning_scene_monitor_->getRobotModel()->getName();
            disp.trajectory.resize(1, res.solution);
            moveit::core::robotStateToRobotStateMsg(rt.getFirstWayPoint(), disp.trajectory_start);
            display_path_.publish(disp);
          }
        }
        res.error_code.val = moveit_msgs::MoveItErrorCodes::SUCCESS;
      }
    }
    else
      res.error_code.val = moveit_msgs::MoveItErrorCodes::FRAME_TRANSFORM_FAILURE;
  }
  else
    res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_GROUP_NAME;

  return true;
}

 

二、外层CartesianInterpolator::computeCartesianPath

double CartesianInterpolator::computeCartesianPath(RobotState* start_state, const JointModelGroup* group,
                                                   std::vector<RobotStatePtr>& traj, const LinkModel* link,
                                                   const EigenSTL::vector_Isometry3d& waypoints,
                                                   bool global_reference_frame, const MaxEEFStep& max_step,
                                                   const JumpThreshold& jump_threshold,
                                                   const GroupStateValidityCallbackFn& validCallback,
                                                   const kinematics::KinematicsQueryOptions& options)
{
  double percentage_solved = 0.0;
  for (std::size_t i = 0; i < waypoints.size(); ++i)
  {
    // Don't test joint space jumps for every waypoint, test them later on the whole trajectory.
    static const JumpThreshold NO_JOINT_SPACE_JUMP_TEST;
    std::vector<RobotStatePtr> waypoint_traj;
    double wp_percentage_solved =
        computeCartesianPath(start_state, group, waypoint_traj, link, waypoints[i], global_reference_frame, max_step,
                             NO_JOINT_SPACE_JUMP_TEST, validCallback, options);

waypoint_traj存储着此轮路点逆解出的位姿,一旦成功,最小长度是2。[0]总是start_state。

wp_percentage_solved。当前路点求解了的百分比。范围[0, 1]。

percentage_solved。累计求解了的百分比。范围[0, 1]。

    if (fabs(wp_percentage_solved - 1.0) < std::numeric_limits<double>::epsilon())
    {

此个路点中所有位姿都逆解成功。

      percentage_solved = (double)(i + 1) / (double)waypoints.size();
      std::vector<RobotStatePtr>::iterator start = waypoint_traj.begin();
      if (i > 0 && !waypoint_traj.empty())
        std::advance(start, 1);
      traj.insert(traj.end(), start, waypoint_traj.end());
    }
    else
    {

此个路点有位姿逆解失败。

      percentage_solved += wp_percentage_solved / (double)waypoints.size();
      std::vector<RobotStatePtr>::iterator start = waypoint_traj.begin();
      if (i > 0 && !waypoint_traj.empty())
        std::advance(start, 1);
      traj.insert(traj.end(), start, waypoint_traj.end());
      break;
    }
  }

if1, wp_percentage_solved: 1.000000, percentage_solved: 0.333333, waypoint_traj.size: 2

if1, wp_percentage_solved: 1.000000, percentage_solved: 0.666666, waypoint_traj.size: 2

if1, wp_percentage_solved: 1.000000, percentage_solved: 1.000000, waypoint_traj.size: 2

  percentage_solved *= checkJointSpaceJump(group, traj, jump_threshold);

  return percentage_solved;
}

 

三、内层CartesianInterpolator::computeCartesianPath

double CartesianInterpolator::computeCartesianPath(RobotState* start_state, const JointModelGroup* group,
                                                   std::vector<RobotStatePtr>& traj, const LinkModel* link,
                                                   const Eigen::Isometry3d& target, bool global_reference_frame,
                                                   const MaxEEFStep& max_step, const JumpThreshold& jump_threshold,
                                                   const GroupStateValidityCallbackFn& validCallback,
                                                   const kinematics::KinematicsQueryOptions& options)
{
  const std::vector<const JointModel*>& cjnt = group->getContinuousJointModels();
  // make sure that continuous joints wrap
  for (const JointModel* joint : cjnt)
    start_state->enforceBounds(joint);

  // this is the Cartesian pose we start from, and we move in the direction indicated
  // getGlobalLinkTransform() returns a valid isometry by contract
  Eigen::Isometry3d start_pose = start_state->getGlobalLinkTransform(link);  // valid isometry

第一个路点的start_pose是机器人当前位姿,后续路点的start_pose是上一个路点的位姿。

  ASSERT_ISOMETRY(target)  // unsanitized input, could contain a non-isometry

  // the target can be in the local reference frame (in which case we rotate it)
  Eigen::Isometry3d rotated_target = global_reference_frame ? target : start_pose * target;  // valid isometry

start_pose:机器人当前正处于的位姿。start_state: {position_: -0.56,0.79,-0.45}

start_pose: {p(0.281330, -0.000289, 0.296320) quat(0.418337i+-0.258039j+0.736526k+-0.464691)}

 -0.218111   0.468621    0.85605

 -0.900411  -0.434954 0.00868973

  0.376415  -0.768901    0.51682

target:上面由tf2::fromMsg(req.waypoints[i], waypoints[i])算出的waypoints[i]。

target: {p(0.381415, -0.000289, 0.118198) quat(0.737963i+-0.340484j+0.526792k+-0.248938)}

   0.21312  -0.240252   0.947026

 -0.764808  -0.644199 0.00868566

  0.607987  -0.726145  -0.321038

Eigen::Isometry3d rotated_target = global_reference_frame ? target : start_pose * target;

bool global_frame = !moveit::core::Transforms::sameFrame(link_name, req.header.frame_id)。global_reference_frame表示target是否是规划组末端link坐标系。false表示是末端link坐标系,像grasping_frame。那target存储的是相对于start_pose的相对位姿,否则target存储的是绝对位姿。

位姿变换[2/5]由于global_reference_frame是true,rotated_target直接取自target。

rotated_target: {p(0.381415, -0.000289, 0.118198) quat(0.737963i+-0.340484j+0.526792k+-0.248938)}

   0.21312  -0.240252   0.947026

 -0.764808  -0.644199 0.00868566

  0.607987  -0.726145  -0.321038

  Eigen::Quaterniond start_quaternion(start_pose.linear());
  Eigen::Quaterniond target_quaternion(rotated_target.linear());

  if (max_step.translation <= 0.0 && max_step.rotation <= 0.0)
  {
    ROS_ERROR_NAMED(LOGNAME,
                    "Invalid MaxEEFStep passed into computeCartesianPath. Both the MaxEEFStep.rotation and "
                    "MaxEEFStep.translation components must be non-negative and at least one component must be "
                    "greater than zero");
    return 0.0;
  }

  double rotation_distance = start_quaternion.angularDistance(target_quaternion);
  double translation_distance = (rotated_target.translation() - start_pose.translation()).norm();

  // decide how many steps we will need for this trajectory
  std::size_t translation_steps = 0;
  if (max_step.translation > 0.0)
    translation_steps = floor(translation_distance / max_step.translation);

  std::size_t rotation_steps = 0;
  if (max_step.rotation > 0.0)
    rotation_steps = floor(rotation_distance / max_step.rotation);

  // If we are testing for relative jumps, we always want at least MIN_STEPS_FOR_JUMP_THRESH steps
  std::size_t steps = std::max(translation_steps, rotation_steps) + 1;
  if (jump_threshold.factor > 0 && steps < MIN_STEPS_FOR_JUMP_THRESH)
    steps = MIN_STEPS_FOR_JUMP_THRESH;

translation_steps是位置上分出的段数,rotation_steps是角度上分出的段数,它们较大值就是此个在(start_pose, 路点)这个位姿范围内要分出的段数。

  // To limit absolute joint-space jumps, we pass consistency limits to the IK solver
  std::vector<double> consistency_limits;
  if (jump_threshold.prismatic > 0 || jump_threshold.revolute > 0)
    for (const JointModel* jm : group->getActiveJointModels())
    {
      double limit;
      switch (jm->getType())
      {
        case JointModel::REVOLUTE:
          limit = jump_threshold.revolute;
          break;
        case JointModel::PRISMATIC:
          limit = jump_threshold.prismatic;
          break;
        default:
          limit = 0.0;
      }
      if (limit == 0.0)
        limit = jm->getMaximumExtent();
      consistency_limits.push_back(limit);
    }

  traj.clear();
  traj.push_back(std::make_shared<moveit::core::RobotState>(*start_state));

由路点生成的traj,[0]肯定是start_state。

  double last_valid_percentage = 0.0;
  for (std::size_t i = 1; i <= steps; ++i)
  {
    double percentage = (double)i / (double)steps;

    Eigen::Isometry3d pose(start_quaternion.slerp(percentage, target_quaternion));

位姿变换[3/5]pose。得到按比例角度,位置保持0。

pose: {p(0, 0, 0) quat(0.737963i+-0.340484j+0.526792k+-0.248938)}

   0.21312  -0.240252   0.947026

 -0.764808  -0.644199 0.00868566

  0.607987  -0.726145  -0.321038

    pose.translation() = percentage * rotated_target.translation() + (1 - percentage) * start_pose.translation();

位姿变换[4/5]pose。加上按比例位置,至此形成一个完整的按比例位姿。

pose: {p(0.381415, -0.000289, 0.118198) quat(0.737963i+-0.340484j+0.526792k+-0.248938)}

   0.21312  -0.240252   0.947026

 -0.764808  -0.644199 0.00868566

  0.607987  -0.726145  -0.321038

    // Explicitly use a single IK attempt only: We want a smooth trajectory.
    // Random seeding (of additional attempts) would probably create IK jumps.
    if (start_state->setFromIK(group, pose, link->getName(), consistency_limits, 0.0, validCallback, options))
      traj.push_back(std::make_shared<moveit::core::RobotState>(*start_state));

setFromIK执行依次执行1)求pose的逆解。2)调用setJointGroupPositions,求出的逆解存储在start_state的position_。

traj.push_back(...)把state_state添加到traj,这同时把求出的逆解加到了traj。

percentage指是一但完成此个i位姿,那已完成的百分比。对于特例,当i==steps,percentage是100%。一量i逆解失败,那成功的就是上次的last_valid_percentage。

    else
      break;

    last_valid_percentage = percentage;
  }

  last_valid_percentage *= checkJointSpaceJump(group, traj, jump_threshold);

  return last_valid_percentage;
}

 

四、RobotState::setToIKSolverFrame

bool RobotState::setToIKSolverFrame(Eigen::Isometry3d& pose, const std::string& ik_frame)
{
  // Bring the pose to the frame of the IK solver
  if (!Transforms::sameFrame(ik_frame, robot_model_->getModelFrame()))
  {
    const LinkModel* link_model =
        getLinkModel((!ik_frame.empty() && ik_frame[0] == '/') ? ik_frame.substr(1) : ik_frame);
    if (!link_model)
    {
      ROS_ERROR_STREAM_NAMED(LOGNAME, "IK frame '" << ik_frame << "' does not exist.");
      return false;
    }
    pose = getGlobalLinkTransform(link_model).inverse() * pose;

右侧pose就此[4/5]时生成的pose,是base_link坐标系下的位姿。变量ik_frame值是arm_joint,getGlobalLinkTransform(link_model)返回base_link <-- arm_joint的变换矩阵。位姿变换[5/5]pose是乘法算出的arm_joint下位姿。由于是要从base_link算出arm_joint,要用变换矩阵的逆。

getGlobalLinkTransform(link_model): {p(0.117869, 0, 0.078854) quat(0.274591i+0.274592j+0.651614k+0.651611)}

-3.67321e-06    -0.698398      0.71571

           1 -2.56536e-06  2.62895e-06

-2.60902e-15      0.71571     0.698398

位姿变换[5/5]pose是乘法算出的arm_joint下位姿。此个pose就是要送入运动学求解器要逆解的位姿。

pose: {p(-0.000289, -0.155900, 0.216099) quat(0.182704i+-0.489721j+0.801609k+0.290197)}

 -0.764809  -0.644198 0.00868218

  0.286302  -0.351916  -0.891171

  0.577147   -0.67909   0.453584

  }
  return true;
}

全部评论: 0

    写评论: