ROS Navigation Stack之costmap_2d源码分析

这周开始又要开始阅读navigation stack的代码了,最近一段时间本人尝试将navigation stack中的代价地图和路径规划模块提取出来,丢弃掉ROS的wrapper,以便在不同平台中使用。期间撸了一下costmap的源码,关于代码地图的加载、生成、更新有了新的认识,故整理一下。

Costmap_2D类图

costmap_2d主要类图
在Ubuntu中找了几个从源码生成uml的工具,要么收费(scitool的Understand),要么不好用(不点名),没办法,只能用宇宙IDE,一键生成类图,完美!这里只显示了几个关键的包,其余的组件包没有显示,可以单独阅读。
如果一开始没有这个类图时,容易看得云里雾里,有了这个图,整个包的结构就非常清晰了。用过的都知道,这里的代码地图是分层的,有静态地图层(StaticLayer),也有用于表示传感器信息的(ObstacleLayer)等。但现实世界中,机器人是有尺寸的,所以还需要将静态地图层和障碍物层进行膨胀,从而在规划路径的时候能够考虑机器人的尺寸,而膨胀就由InflationLayer完成。

静态地图层(StaticLayer)

静态地图层就比较简单了,map_server加载地图后,将地图发布出来,costmap_2d_ros接受到地图数据后,便加载各个层,静态地图层只是简单地将灰度图中的像素值换算成ros代价地图中的代价值,然后便进入下一层的更新了。这里只是将转换的代码贴出了说一说:

void StaticLayer::incomingMap(const nav_msgs::OccupancyGridConstPtr& new_map)
{
   
   
  // 前面就是一些检查和初始化,忽略之...
  // 这里就是将图片的像素值转换成代价值了,具体转换可以看interpretValue函数
  for (unsigned int i = 0; i < size_y; ++i)
  {
   
   
    for (unsigned int j = 0; j < size_x; ++j)
    {
   
   
      unsigned char value = new_map->data[index];
      costmap_[index] = interpretValue(value);
      ++index;
    }
  }
}

// 转换函数
unsigned char StaticLayer::interpretValue(unsigned char value)
{
   
   
  // 实际上就是高于一个阈值lethal_threshold那么就认为是障碍物
  // 没有信息的就认为是未探测的区域
  // 否则为自由空间
  if (track_unknown_space_ && value == unknown_cost_value_)
    return NO_INFORMATION;
  else if (!track_unknown_space_ && value == unknown_cost_value_)
    return FREE_SPACE;
  else if (value >= lethal_threshold_)
    return LETHAL_OBSTACLE;
  else if (trinary_costmap_)
    return FREE_SPACE;
  
  // 对于其他的数值则进行插值运算
  double scale = (double) value / lethal_threshold_;
  return scale * LETHAL_OBSTACLE;
}
障碍物层(ObstacleLayer)

障碍物层是将传感器检测到的点云投影到地图中,不同的传感器来源会分别存到Observation类中。只要遍历存储Obsrvation的容器,将检测到的点云的每个点分别投影到地图中,将对应的网格的代价值设为LETHAL_OBSTACLE,其实现函数在void ObstacleLayer::updateBounds(...)中:

void ObstacleLayer::updateBounds(double robot_x, double robot_y, double robot_yaw, double* min_x, double* min_y, double* max_x, double* max_y)
{
   
   
  ...
  // 先打扫一下旧垃圾,清除一下障碍物
  for (unsigned int i = 0; i < clearing_observations.size(); ++i)
  {
   
   
    raytraceFreespace(clearing_observations[i], min_x, min_y, max_x, max_y);
  }

  // 获取每个观察层的点云数据
  for (std::vector<Observation>::const_iterator it = observations.begin(); it != observations.end(); ++it)
  {
   
   
    const Observation& obs = *it;
    const pcl::PointCloud<pcl::PointXYZ>& cloud = *(obs.cloud_);
    double sq_obstacle_range = obs.obstacle_range_ * obs.obstacle_range_;

    //将每个点云添加进障碍物层中
    for (unsigned int i = 0; i < cloud
<think>好的,我现在需要解答用户关于ROS中`costmap_2d::VoxelCloudLayer`插件配置的问题。用户给出的配置片段是`plugins: -{name: pointcloud_layer, type: "costmap_2d::VoxelCloudLayer"}`,他们可能想知道这个配置的作用以及如何正确使用它。首先,我需要回忆一下ROS中的costmap_2d包。costmap_2d用于创建机器人导航中的代价地图,通常包括障碍物层、静态层等。VoxelCloudLayer应该是处理三维点云数据的层,可能用于将点云转换为代价地图中的障碍物信息。接下来,我需要解释这个配置的具体含义。`name`参数指定了层的名称,`type`则是插件的类名。这里使用的是VoxelCloudLayer,可能用于处理体素化的点云数据。需要详细说明每个参数的作用,比如如何订阅点云话题,如何设置最大障碍物高度等。然后,用户可能需要示例代码来展示如何配置YAML文件,以及如何在ROS节点中加载这个插件。我应该提供一个完整的YAML配置示例,包括常见的参数设置,如话题名称、传感器框架、更新速率等。此外,用户可能想知道如何验证配置是否正确。可能需要指导他们使用RViz查看代价地图,或者检查话题是否正常发布。这部分需要给出具体的命令行指令,比如用rostopic检查话题,或者用RViz添加图层。 最后,相关问题的部分需要考虑用户可能进一步的问题。比如如何自定义插件、处理多传感器数据、调整体素大小,或者解决数据延迟的问题。这四个问题应该覆盖用户可能遇到的后续疑问。 整个过程中,我需要确保解释清晰,代码示例正确,并且相关的问题能够引导用户深入理解该插件的使用和扩展。</think>在ROS导航系统中,`costmap_2d::VoxelCloudLayer` 是用于处理三维点云数据的代价地图插件,以下是详细实现方案: ### 一、插件配置解析 ```yaml costmap: plugins: - {name: pointcloud_layer, type: "costmap_2d::VoxelCloudLayer"} ``` #### 关键参数说明: 1. **name**:层标识符(需全局唯一) 2. **type**:插件类名(必须与注册名称一致) 3. **topic**:点云订阅话题(默认为`/points`) 4. **max_obstacle_height**:最大障碍物高度(单位:米) ### 二、完整配置示例 ```yaml # costmap_params.yaml pointcloud_layer: enabled: true topic: /processed_cloud sensor_frame: lidar_link transform_tolerance: 0.5 observation_persistence: 0.3 expected_update_rate: 2.0 filter: "voxel" voxel_size: 0.1 max_obstacle_height: 2.5 obstacle_range: 5.0 raytrace_range: 6.0 ``` ### 三、自定义插件实现 ```cpp #include <costmap_2d/layer.h> #include <costmap_2d/layered_costmap.h> namespace costmap_2d { class CustomCloudLayer : public VoxelCloudLayer { public: virtual void onInitialize() { ros::NodeHandle nh("~/" + name_); nh.param("custom_param", custom_param_, 0.5); cloud_sub_ = nh.subscribe("/custom_cloud", 1, &CustomCloudLayer::cloudCallback, this); } virtual void updateBounds(...) { // 自定义障碍物处理逻辑 } private: void cloudCallback(const sensor_msgs::PointCloud2ConstPtr& msg) { // 自定义点云处理 } }; } PLUGINLIB_EXPORT_CLASS(costmap_2d::CustomCloudLayer, costmap_2d::Layer) ``` ### 四、插件注册配置 ```xml <!-- voxel_plugins.xml --> <library path="lib/libcustom_layers"> <class name="costmap_2d/CustomCloudLayer" type="costmap_2d::CustomCloudLayer" base_class_type="costmap_2d::Layer"> <description>Custom pointcloud processing layer</description> </class> </library> ``` ### 五、可视化验证方法 ```bash # 启动RViz观察代价地图 roslaunch navigation_stack rviz.launch # 查看层信息 rostopic echo /move_base/global_costmap/layer_names ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值