探索算法:dsvp学习笔记(graph_planner.cpp)

前言:笔者研0学习中,理解有限,也在学习该算法的朋友可以共同交流学习。

1.bool GraphPlanner::readParameters

bool GraphPlanner::readParameters()

该函数用来加载参数服务器参数并判断是否成功

2.void GraphPlanner::publishPath

该函数用来将planned_path_中的数据发布出去,其实就是wayPoint,根据作者描述是为了debug用的,对整体使用没有影响。

void GraphPlanner::publishPath()
{
  nav_msgs::Path planned_path; //里程计消息
  planned_path.header.frame_id = world_frame_id_;
  planned_path.header.stamp = ros::Time::now();

  if (in_progress_ && !planned_path_.empty()) //当路径列表非空并且还在运行时,添加新的点进来
  {
    planned_path.poses.resize(planned_path_.size() + 1);//调整大小
    planned_path.poses[0].pose.position = robot_pos_;
    for (int i = 0; i < planned_path_.size(); i++)
    {
      planned_path.poses[i + 1].pose.position = planned_path_[i];
    }
  }
  else
  {
    planned_path.poses.resize(1);
    planned_path.poses[0].pose.position = robot_pos_;
  }
  graph_planner_path_pub_.publish(planned_path);//将添加后的路径发布出去
}

nav_msgs消息包详细信息

3.void GraphPlanner::odometryCallback

该函数为回调函数在此处被调用

odometry_sub_ = nh_.subscribe(sub_odometry_topic_, 1, &GraphPlanner::odometryCallback, this);

其作用为订阅sub_odometry_topic话题发送的消息,获取坐标和航向角yaw,由于无法直接通过四元数得到欧拉角所以通过tf::Matrix3x3(tf::Quaternion(geo_quat.x, geo_quat.y, geo_quat.z, geo_quat.w)).getRPY(roll, pitch, yaw);间接获取yaw.为了获取机器人当前位置使用。

yaw.roll.pitch详解见该博主

关于四元数,欧拉角等转化见该博主

void GraphPlanner::odometryCallback(const nav_msgs::Odometry::ConstPtr& odometry_msg)
{
  double roll, pitch, yaw;
  geometry_msgs::Quaternion geo_quat = odometry_msg->pose.pose.orientation;
  tf::Matrix3x3(tf::Quaternion(geo_quat.x, geo_quat.y, geo_quat.z, geo_quat.w)).getRPY(roll, pitch, yaw);

  robot_yaw_ = yaw;
  robot_pos_ = odometry_msg->pose.pose.position;
}

4.void GraphPlanner::commandCallback

该函数用来处理3类command指令,当收到COMMAND_GO_TO_LOCATION后会将previous_shortest_path_size_设置为100000,为了防止机器人在探索过程中,在两个点之间不停的徘徊。

void GraphPlanner::commandCallback(const graph_planner::GraphPlannerCommand::ConstPtr& msg)
{
  graph_planner_command_ = *msg;
  if (graph_planner_command_.command == graph_planner::GraphPlannerCommand::COMMAND_GO_TO_LOCATION)
  {
    previous_shortest_path_size_ = 100000;
  }
}

5.void GraphPlanner::graphCallback

void GraphPlanner::graphCallback(const graph_utils::TopologicalGraph::ConstPtr& graph_msg)
{
  planned_graph_ = *graph_msg;
}

是用来订阅dsvplanner发来的局部图,其结构为:

TopologicalGraph

---Header header

---Vertex[] vertex #顶点

    ---geometry/Point location #每个顶点坐标

    ---int32 parent_idx #父顶点索引

    ---float32 information_gain 

    ---int32 vertex_id #顶点索引

    ---Edge[] edge #每个顶点引出的边

        ---int32 vertex_id_end #每条边的终点

        ---bool keypose_edge 

        ---float32 traversal_costs #每条边的损失

        ---float32 risks

6.void GraphPlanner::terrainCallback

void GraphPlanner::terrainCallback(const sensor_msgs::PointCloud2::ConstPtr& terrain_msg)
{
  terrain_point_->clear();
  terrain_point_crop_->clear();
  pcl::fromROSMsg(*terrain_msg, *terrain_point_);

  pcl::PointXYZI point;
  int terrainCloudSize = terrain_point_->points.size();
  for (int i = 0; i < terrainCloudSize; i++)
  {
    point = terrain_point_->points[i];

    float pointX = point.x;
    float pointY = point.y;
    float pointZ = point.z;

    if (point.intensity > kObstacleHeightThres && point.intensity < kOverheadObstacleHeightThres)
    {
      point.x = pointX;
      point.y = pointY;
      point.z = pointZ;
      terrain_point_crop_->push_back(point);
    }
  }
  pcl::VoxelGrid<pcl::PointXYZI> point_ds;
  point_ds.setLeafSize(kDownsampleSize, kDownsampleSize, kDownsampleSize);
  point_ds.setInputCloud(terrain_point_crop_);
  point_ds.filter(*terrain_point_crop_);
}

点云还没了解后期补充

7.GraphPlanner::publishInProgress

发送给dsvplanner节点,来使能或者失能规划器

void GraphPlanner::publishInProgress(bool in_progress)
{
  in_progress_ = in_progress;  // used mainly for debugging

  graph_planner::GraphPlannerStatus status;
  if (in_progress)
  {
    status.status = graph_planner::GraphPlannerStatus::STATUS_IN_PROGRESS;
  }
  else
  {
    status.status = graph_planner::GraphPlannerStatus::STATUS_DISABLED;
    state_ = ALL_OTHER_STATES;  // reset
  }

  graph_planner_status_pub_.publish(status);
}

8.void GraphPlanner::alterAndPublishWaypoint

用来发送waypoint

void GraphPlanner::alterAndPublishWaypoint()
{
  // Ship it!
  waypoint_.header.frame_id = world_frame_id_;  // added by Hong in 20191203 "/map"
  waypoint_.header.stamp = ros::Time::now();    // added by Hong in 20191203
  waypoint_pub_.publish(waypoint_);//"/way_point"
}

9.bool GraphPlanner::goToVertex

该函数为本节点的核心逻辑,思想为找到当前点到目标点的最短路径,如果该路径大小为0说明无法到达目标点,如果该路径大小小于3则说明当前点或当前点的下一个点就是目标点,直接到达即可。

如果该路径大小大于2说明无法直接到达目标点,所以首先需要在最短路径中找到满足欧式距离和路径距离都大于移动阈值的第一个点,将这个点作为到达的顶点。但是还需要判断到达这个点的路径中是否与障碍物发生碰撞,如果发送就去碰撞点的前一个点,否则不用改变。

然后再将该点作为出发点寻找到达目标点的最小路径,判断此时路径大小是否大于上次路径大小,如果大于说明路径错误,之间将上一次的目标点作为当前目标点,否则就将当前的点存储起来。然后判断寻找到的waypoint是否为目标点,如果是,那么就到达该点并通知规划器开始新的一轮探索,否则就继续前进,重新构图。

bool GraphPlanner::goToVertex(int current_vertex_idx, int goal_vertex_idx)
{
  std::vector<int> shortest_path;
  //找到起点到目标点的最短路径,并传入shortest_path中
  graph_utils_ns::ShortestPathBtwVertex(shortest_path, planned_graph_, current_vertex_idx, goal_vertex_idx);

  if (shortest_path.size() <= 0)//没找到路径时
  {
    // ROS_WARN("Graph planner did not find path to selected goal!");

    // clear the saved path
    planned_path_.clear();

    return false;
  }
  else if (shortest_path.size() <= 2)//路径大小为2,也就是当前点即为目标点或者下一个点就为目标点
  {
    geometry_msgs::Point next_waypoint = planned_graph_.vertices[shortest_path.back()].location;//back是最后一个元素也就是目标点

    waypoint_.header.stamp = ros::Time::now();
    waypoint_.point.x = next_waypoint.x;
    waypoint_.point.y = next_waypoint.y;
    waypoint_.point.z = next_waypoint.z;
    waypoint_.header.frame_id = world_frame_id_;//"/map"

    // clear the saved path
    planned_path_.clear();
    planned_path_.push_back(waypoint_.point);

    alterAndPublishWaypoint();//发布waypoint
    return false;
  }
  else//如果路径大小大于2
  {
    int next_vertex_id;
    //找到大于阈值的那个点作为下一个移动点
    next_vertex_id =
        graph_utils_ns::GetFirstVertexBeyondThreshold(robot_pos_, shortest_path, planned_graph_, kLookAheadDist);
    //判断下一个目标点的路径中是否发生碰撞,如果碰撞就去碰撞点的前一个点
    for (int i = 1; i < shortest_path.size(); i++)
    {
      //      std::cout << "i = " << i << " id is " << shortest_path[i] <<
      //      std::endl;
      //判断是否碰撞
      if (shortest_path[i] != next_vertex_id && collisionCheckByTerrain(robot_pos_, shortest_path[i]))
      {
        next_vertex_id = shortest_path[i - 1];
        break;
      }
      else if (shortest_path[i] == next_vertex_id)
      {
        next_vertex_id = shortest_path[i];
        break;
      }
    }

    std::vector<int> next_shortest_path;
    //到达新的点后再次寻找新的点到目标点的最短路径,传入next_shortest_path中
    graph_utils_ns::ShortestPathBtwVertex(next_shortest_path, planned_graph_, next_vertex_id, goal_vertex_idx);

    // prevent back and forth between two vertices
    if (next_shortest_path.size() > previous_shortest_path_size_)
    {
      next_vertex_id = previous_vertex_id_;
      backTraceCount_++;
      if (backTraceCount_ > kNextVertexMaintainTime * kExecuteFrequency) // backTraceCount_>25
      {
        backTraceCount_ = 0;
        previous_shortest_path_size_ = 100000;
      }
    }
    else
    {
      previous_vertex_id_ = next_vertex_id;
      previous_shortest_path_size_ = next_shortest_path.size();
    }

    geometry_msgs::Point next_waypoint = planned_graph_.vertices[next_vertex_id].location;
    //这是两次无法到达的话让其快速到达
    if (next_vertex_id != shortest_path.back())
    {
      next_waypoint = projectWayPoint(next_waypoint, robot_pos_);
    }
    waypoint_.header.stamp = ros::Time::now();
    waypoint_.point.x = next_waypoint.x;
    waypoint_.point.y = next_waypoint.y;
    waypoint_.point.z = next_waypoint.z;
    waypoint_.header.frame_id = world_frame_id_;

    // save path for debugging
    planned_path_.clear();
    for (const auto& v : shortest_path)
    {
      planned_path_.push_back(planned_graph_.vertices[v].location);
    }

    //到达目标点后发布
    if (next_vertex_id == shortest_path.back())
    {
      // Assume reached vertex -- publish the last waypoint and then end
      alterAndPublishWaypoint();
      return false;
    }
    else
    {
      // Keep moving along planned path
      return true;
    }
  }
}

10.bool GraphPlanner::goToPoint

获取距离机器人当前位置最近的点作为起点,将距离该点最近的点作为目标点(都是欧式距离) 

bool GraphPlanner::goToPoint(geometry_msgs::Point point)
{
  if (planned_graph_.vertices.size() <= 0)//判断顶点的size是否小于0
  {
    return false;
  }
  // Find where I am on this graph
  //robot_pos_是通过里程计获取,获取到离机器人最近的那个点的id,也就是当前在定位机器人
  int current_vertex_idx = graph_utils_ns::GetClosestVertexIdxToPoint(planned_graph_, robot_pos_);

  // find closest goal vertex
  //point为目标点(可能不在图中) 所以要找到图中离目标点最近的点(也可能就是自己),所谓id就是数组的索引
  int goal_vertex_idx = graph_utils_ns::GetClosestVertexIdxToPoint(planned_graph_, point);

  // Set the waypoint
  return goToVertex(current_vertex_idx, goal_vertex_idx);
}

11.bool GraphPlanner::collisionCheckByTerrain

障碍物碰撞检测 

bool GraphPlanner::collisionCheckByTerrain(geometry_msgs::Point robot_position, int end_vertex_idx)
{
  geometry_msgs::Point start_point = robot_position;
  geometry_msgs::Point end_point = planned_graph_.vertices[end_vertex_idx].location;

  int count = 0;
  double distance = sqrt((end_point.x - start_point.x) * (end_point.x - start_point.x) +
                         (end_point.y - start_point.y) * (end_point.y - start_point.y));
  double check_point_num = distance / (kCollisionCheckDistace);
  for (int j = 0; j < check_point_num; j++)
  {
    geometry_msgs::Point p1;
    p1.x = start_point.x + j * kCollisionCheckDistace / distance * (end_point.x - start_point.x);
    p1.y = start_point.y + j * kCollisionCheckDistace / distance * (end_point.y - start_point.y);
    for (int i = 0; i < terrain_point_crop_->points.size(); i++)
    {
      double dist = sqrt((p1.x - terrain_point_crop_->points[i].x) * (p1.x - terrain_point_crop_->points[i].x) +
                         (p1.y - terrain_point_crop_->points[i].y) * (p1.y - terrain_point_crop_->points[i].y));
      if (dist < kCollisionCheckDistace)
      {
        count++;
      }
      if (count > 0)
      {
        return true;
      }
    }
  }
  return false;
}

12.geometry_msgs::Point GraphPlanner::projectWayPoint

一种策略,为了更快的到达目标点。

geometry_msgs::Point GraphPlanner::projectWayPoint(geometry_msgs::Point next_vertex_pos, geometry_msgs::Point robot_pos)
{
  double ratio = misc_utils_ns::PointXYDist<geometry_msgs::Point, geometry_msgs::Point>(robot_pos, next_vertex_pos) /
                 kWaypointProjectionDistance;
  double x = (next_vertex_pos.x - robot_pos.x) / ratio + robot_pos.x;
  double y = (next_vertex_pos.y - robot_pos.y) / ratio + robot_pos.y;
  double z = (next_vertex_pos.z - robot_pos.z) / ratio + robot_pos.z;
  geometry_msgs::Point way_point = misc_utils_ns::GeoMsgPoint(x, y, z);
  return way_point;
}

13.bool GraphPlanner::initialize

该函数定义了目标点,以及一系列订阅者和发布者。

bool GraphPlanner::initialize()
{
  if (!readParameters())
    return false;

  // Initialize target waypoint
  waypoint_ = geometry_msgs::PointStamped();
  waypoint_.point.x = -1.0;
  waypoint_.point.y = -1.0;
  waypoint_.point.z = 0.0;
  waypoint_.header.frame_id = world_frame_id_;

  // Initialize subscribers
  graph_sub_ = nh_.subscribe(sub_graph_topic_, 1, &GraphPlanner::graphCallback, this);
  graph_planner_command_sub_ = nh_.subscribe(graph_planner_command_topic_, 1, &GraphPlanner::commandCallback, this);
  odometry_sub_ = nh_.subscribe(sub_odometry_topic_, 1, &GraphPlanner::odometryCallback, this);
  terrain_sub_ = nh_.subscribe(sub_terrain_topic_, 1, &GraphPlanner::terrainCallback, this);

  // Initialize publishers
  waypoint_pub_ = nh_.advertise<geometry_msgs::PointStamped>(pub_waypoint_topic_, 2);
  graph_planner_status_pub_ = nh_.advertise<graph_planner::GraphPlannerStatus>(graph_planner_status_topic_, 2);
  graph_planner_path_pub_ = nh_.advertise<nav_msgs::Path>(pub_path_topic_, 10);

  ROS_INFO("Successfully launched graph_planner node");

  return true;
}

14.void GraphPlanner::executeGoToOrigin

该函数控制机器人回到起点

void GraphPlanner::executeGoToOrigin()
{
  // Aim for (0,0,0)
  geometry_msgs::Point origin;
  origin.x = 0;
  origin.y = 0;
  origin.z = 0;
  bool success = goToPoint(origin);

  if (!success)
  {
    publishInProgress(false);
  }
  else
  {
    alterAndPublishWaypoint();
    publishInProgress(true);
  }
}

15.void GraphPlanner::executeGoToLocation()

该函数控制机器人到达目标点

void GraphPlanner::executeGoToLocation()
{
  // Compute waypoint
  bool success = goToPoint(graph_planner_command_.location);

  if (!success)
  {
    publishInProgress(false);
  }
  else
  {
    alterAndPublishWaypoint();
    publishInProgress(true);
  }
}

16.void GraphPlanner::executeCommand

对command的控制

void GraphPlanner::executeCommand()
{
  // Choose from one of the graph_planner variants based on the command

  if (graph_planner_command_.command == graph_planner::GraphPlannerCommand::COMMAND_GO_TO_ORIGIN)
  {
    // COMMAND_GO_TO_ORIGIN
    executeGoToOrigin();
  }
  else if (graph_planner_command_.command == graph_planner::GraphPlannerCommand::COMMAND_GO_TO_LOCATION)
  {
    // COMMAND_GO_TO_LOCATION
    executeGoToLocation();
  }
  else
  {
    publishInProgress(false);
  }
}

17.bool GraphPlanner::execute

该节点的总体逻辑,根据收到的command来进行不同的步骤实现对应功能

bool GraphPlanner::execute()
{
  ros::Rate rate(kExecuteFrequency);//5hz
  bool status = ros::ok();
  while (status)
  {
    ros::spinOnce();

    if (graph_planner_command_.command == graph_planner::GraphPlannerCommand::COMMAND_DISABLE)
    {
      // Graph planner is disabled -- do nothing 
      publishInProgress(false);
    }
    else
    {
      // Graph planner is enabled -- select waypoint according to current
      // command
      executeCommand();
    }

    // Generate and publish the path
    publishPath();

    status = ros::ok();
    rate.sleep();
  }

  return true;
}

18.总结

该节点对局部图进行处理,找到并发布waypoint

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值