jjzjj

三维pcd地图转二维栅格地图

向阳花开_ 2023-08-18 原文

1.概述

在使用导航时,通常会根据二维栅格地图做路径规划,需要将三维点云地图转化成栅格地图。
本文采用滤波及投影的方法,
主要步骤包括

  • 对输入点云进行直通滤波,获取限定高度范围的数据
  • 在进行半径滤波,去除部分孤立点
  • 转换为栅格地图

2.方法说明

完整程序代码:github

运行方法:
下载编译后,

mkdir -p test_ws/src&& cd test_ws/src
git clone -b develop  https://github.com/Hinson-A/pcd2pgm_package
cd ../
catkin_make

编译完成后,查看 src/pcd2pgm_package/pcd2pgm/launch/中的run.launch文件,修改相应的文件路径及名称

launch文件如下

<!-- -->
<launch>
<node pkg="pcd2pgm" name="pcd2pgm" type="pcd2pgm" output="screen">
<!-- 存放pcd文件的路径-->
<param name="file_directory" value= "/home/robot/map/" />
<!-- pcd文件名称-->
<param name="file_name" value= "map" />
<!-- 选取的范围 最小的高度-->
<param name="thre_z_min" value= "0.1" />
<!-- 选取的范围 最大的高度-->
<param name="thre_z_max" value= "1.5" />
<!--0 选取高度范围内的,1选取高度范围外的-->
<param name="flag_pass_through" value= "0" />
<!-- 半径滤波的半径-->
<param name="thre_radius" value= "0.5" />
<!-- 半径滤波的要求点数个数-->
<param name="thres_point_count" value= "10" />
<!-- 存储的栅格map的分辨率-->
<param name="map_resolution" value= "0.05" />
<!-- 转换后发布的二维地图的topic,默认使用map即可,可使用map_server保存-->
<param name="map_topic_name" value= "map" />
</node>

</launch>

修改完成后,运行程序

roslaun run.launch


此时,通过map_server 保存该栅格地图

rosrun map_server map_saver

生成对应的map文件

同时在原pcd文件夹下,也保存了直通滤波和半径滤波后的pcd文件。

原图:

直通滤波:

半径滤波:

二维栅格地图

3.关键程序解析

3.1 获取pcd文件

  // 下载pcd文件
  if (pcl::io::loadPCDFile<pcl::PointXYZ>(pcd_file, *pcd_cloud) == -1) {
    PCL_ERROR("Couldn't read file: %s \n", pcd_file.c_str());
    return (-1);
  }

3.2 进行直通滤波

指定字段,指定坐标范围进行裁剪。可以选择保留范围内的点或者范围外的点。
这里滤出设置范围之外的点云数据

//直通滤波器对点云进行过滤,获取设定高度范围内的数据
void PassThroughFilter(const double &thre_low, const double &thre_high,
                       const bool &flag_in) {
  // 创建滤波器对象
  pcl::PassThrough<pcl::PointXYZ> passthrough;
  //输入点云
  passthrough.setInputCloud(pcd_cloud);
  //设置对z轴进行操作
  passthrough.setFilterFieldName("z");
  //设置滤波范围
  passthrough.setFilterLimits(thre_low, thre_high);
  // true表示保留滤波范围外,false表示保留范围内
  passthrough.setFilterLimitsNegative(flag_in);
  //执行滤波并存储
  passthrough.filter(*cloud_after_PassThrough);
  // test 保存滤波后的点云到文件
  pcl::io::savePCDFile<pcl::PointXYZ>(file_directory + "map_filter.pcd",
                                      *cloud_after_PassThrough);
  std::cout << "直通滤波后点云数据点数:"
            << cloud_after_PassThrough->points.size() << std::endl;
}

3.3 进行半径滤波

半径滤波说明:
点云半径滤波也叫基于连通分析的点云滤波,该方法的基本思想是:假定原始点云中每个激光点在指定的半径邻域中至少包含一定数量的近邻点。原始点云中符合假设条件的激光点被视为正常点进行保留,反之,则视为噪声点并进行去除。
该方法对原始激光点云中存在的一些悬空的孤立点或无效点具有很好的去除效果。

//半径滤波
void RadiusOutlierFilter(const pcl::PointCloud<pcl::PointXYZ>::Ptr &pcd_cloud0,
                         const double &radius, const int &thre_count) {
  //创建滤波器
  pcl::RadiusOutlierRemoval<pcl::PointXYZ> radiusoutlier;
  //设置输入点云
  radiusoutlier.setInputCloud(pcd_cloud0);
  //设置半径,在该范围内找临近点
  radiusoutlier.setRadiusSearch(radius);
  //设置查询点的邻域点集数,小于该阈值的删除
  radiusoutlier.setMinNeighborsInRadius(thre_count);
  radiusoutlier.filter(*cloud_after_Radius);
  // test 保存滤波后的点云到文件
  pcl::io::savePCDFile<pcl::PointXYZ>(file_directory + "map_radius_filter.pcd",
                                      *cloud_after_Radius);
  std::cout << "半径滤波后点云数据点数:" << cloud_after_Radius->points.size()
            << std::endl;
}

3.4 发布map

转换为栅格地图数据并发布

//转换为栅格地图数据并发布
void SetMapTopicMsg(const pcl::PointCloud<pcl::PointXYZ>::Ptr cloud,
                    nav_msgs::OccupancyGrid &msg) {
  msg.header.seq = 0;
  msg.header.stamp = ros::Time::now();
  msg.header.frame_id = "map";

  msg.info.map_load_time = ros::Time::now();
  msg.info.resolution = map_resolution;

  double x_min, x_max, y_min, y_max;
  double z_max_grey_rate = 0.05;
  double z_min_grey_rate = 0.95;
  //? ? ??
  double k_line =
      (z_max_grey_rate - z_min_grey_rate) / (thre_z_max - thre_z_min);
  double b_line =
      (thre_z_max * z_min_grey_rate - thre_z_min * z_max_grey_rate) /
      (thre_z_max - thre_z_min);

  if (cloud->points.empty()) {
    ROS_WARN("pcd is empty!\n");
    return;
  }

  for (int i = 0; i < cloud->points.size() - 1; i++) {
    if (i == 0) {
      x_min = x_max = cloud->points[i].x;
      y_min = y_max = cloud->points[i].y;
    }

    double x = cloud->points[i].x;
    double y = cloud->points[i].y;

    if (x < x_min)
      x_min = x;
    if (x > x_max)
      x_max = x;

    if (y < y_min)
      y_min = y;
    if (y > y_max)
      y_max = y;
  }
  // origin的确定
  msg.info.origin.position.x = x_min;
  msg.info.origin.position.y = y_min;
  msg.info.origin.position.z = 0.0;
  msg.info.origin.orientation.x = 0.0;
  msg.info.origin.orientation.y = 0.0;
  msg.info.origin.orientation.z = 0.0;
  msg.info.origin.orientation.w = 1.0;
  //设置栅格地图大小
  msg.info.width = int((x_max - x_min) / map_resolution);
  msg.info.height = int((y_max - y_min) / map_resolution);
  //实际地图中某点坐标为(x,y),对应栅格地图中坐标为[x*map.info.width+y]
  msg.data.resize(msg.info.width * msg.info.height);
  msg.data.assign(msg.info.width * msg.info.height, 0);

  ROS_INFO("data size = %d\n", msg.data.size());

  for (int iter = 0; iter < cloud->points.size(); iter++) {
    int i = int((cloud->points[iter].x - x_min) / map_resolution);
    if (i < 0 || i >= msg.info.width)
      continue;

    int j = int((cloud->points[iter].y - y_min) / map_resolution);
    if (j < 0 || j >= msg.info.height - 1)
      continue;
    // 栅格地图的占有概率[0,100],这里设置为占据
    msg.data[i + j * msg.info.width] = 100;
    //    msg.data[i + j * msg.info.width] = int(255 * (cloud->points[iter].z *
    //    k_line + b_line)) % 255;
  }
}

reference pcd转pgm/3d点云转2d灰度图

有关三维pcd地图转二维栅格地图的更多相关文章

  1. Ruby -> 写入二维数组 - 2

    我正在处理http://prepwork.appacademy.io/mini-curriculum/array/中概述的数组问题我正在尝试创建函数my_transpose,它接受一个矩阵并返回其转置。我对写入二维数组感到很困惑!这是一个代码片段,突出了我的困惑。rows=[[0,1,2],[3,4,5],[6,7,8]]columns=Array.new(3,Array.new(3))putscolumns.to_s#Outputisa3x3arrayfilledwithnilcolumns[0][0]=0putscolumns.to_s#Outputis[[0,nil,nil],[

  2. ruby-on-rails - 使用 gmaps4rails 动态加载谷歌地图标记 - 2

    如何只加载map边界内的标记gmaps4rails?当然,在平移和/或缩放后加载新的。与此直接相关的是,如何获取map的当前边界和缩放级别? 最佳答案 我是这样做的,我只在用户完成平移或缩放后替换标记,如果您需要不同的行为,请使用不同的事件监听器:在你看来(index.html.erb):{"zoom"=>15,"auto_adjust"=>false,"detect_location"=>true,"center_on_user"=>true}},false,true)%>在View的底部添加:functiongmaps4rail

  3. 最新版人脸识别小程序 图片识别 生成二维码签到 地图上选点进行位置签到 计算签到距离 课程会议活动打卡日常考勤 上课签到打卡考勤口令签到 - 2

    技术选型1,前端小程序原生MINA框架cssJavaScriptWxml2,管理后台云开发Cms内容管理系统web网页3,数据后台小程序云开发云函数云开发数据库(基于MongoDB)云存储4,人脸识别算法基于百度智能云实现人脸识别一,用户端效果图预览老规矩我们先来看效果图,如果效果图符合你的需求,就继续往下看,如果不符合你的需求,可以跳过。1-1,登录注册页可以看到登录页有注册入口,注册页如下我们的注册,需要管理员审核,审核通过后才可以正常登录使用小程序1-2,个人中心页登录成功以后,我们会进入个人中心页我们在个人中心页可以注册人脸,因为我们做人脸识别签到,需要先注册人脸才可以进行人脸比对,进

  4. ruby-on-rails - 在 rails 中生成二维码 - 2

    我想在ruby​​onrails中生成QR码,以便在我用rails编写的网站后台运行。看到这个http://code.google.com/p/qrcode-rails/但无法弄清楚如何让它为我工作。基本上在RoR中,我想:向生成器传递一个字符串、我的唯一代码、一个20个字符长度的数字(例如32032928889998887776)并生成一个名为“代码”_qr.jpg的图像并保存在资源文件夹中以附加到我的电子邮件中程序将发出。我该怎么做,有人知道吗?虽然我在问(不是很重要,我现在得到这个答案)但是我如何实现QR码读取,以从网络摄像头取回该代码?谢谢。 最佳答

  5. arrays - 在二维数组中搜索零并创建相应的行和列 0 - 2

    这是我的代码,可以运行,但它太大了。我想重构它。req_row=-1req_col=-1a.each_with_indexdo|row,index|row.each_with_indexdo|col,i|ifcol==0req_row=indexreq_col=ibreakendendendifreq_col>-1andreq_row>-1a.each_with_indexdo|row,index|row.each_with_indexdo|col,i|print(req_row==indexori==req_col)?0:colprint""endputs"\r"endend输入:二

  6. ruby - 如何在二维数组中查找 - 2

    我正在尝试学习如何在二维数组中进行搜索;例如:array=[[1,1],[1,2],[1,3],[2,1],[2,4],[2,5]]我想知道如何在数组中搜索格式为[1,y]的数组,然后显示其他y数字是什么:[1,2,3]。如果有人能帮助我了解如何仅使用数字进行搜索(因为我发现的很多示例都包含字符串或哈希),甚至可以帮助我了解在哪里寻找正确的资源,那将会很有帮助。 最佳答案 Ruby允许您通过在block参数中使用圆括号来查看元素。select和map只分配一个block参数,但您可以查看元素:array.select{|(x,y)|

  7. 视频融合技术解决方案,三维全景拼接赋能平台 - 2

    近年来,随着信息化时代的到来,三维全景拼接以视频监控领域为代表的智能硬件公司迅速崛起,随后全国各地在视频监控领域进行了大量的建设。但随着摄像头数量的增加,视频监控画面离散、庞杂、关联性差等诸多问题日渐凸显。如何优化现有视频技术,助力管理者或使用者有效、直观、准确地掌控现场实时动态,成为我国信息化前行路上面临的新课题。视频融合技术平台解决方案北京智汇云舟科技有限公司成立于2012年,专注于创新性的“视频孪生(实时实景数字孪生)”技术研发与应用。公司依托自研三维地理信息引擎(3DGIS),融合建筑信息模型(BIM)、视频监控(Video)、人工智能(AI)及物联网(IOT)等多种技术,并在此基础上

  8. ruby-on-rails - 谷歌地图、Ruby on Rails、用一个标记缩放级别 - 2

    我正在使用apneadiving/Google-Maps-for-Rails添加googlemaps支持(感谢awesomegem)但是,我发现了一个小故障,这很可能是我的错。当有多个标记时,auto_zoom效果很好。但是,当只有一个标记时,它会放大到不漂亮的最大级别。“缩放”仅在auto_zoom为false时有效,所以这不是我想要的。因此您可以使用“maxZoom”,但现在用户无法手动放大超出该点,这不是我想要的。有解决办法吗?我的解释有道理吗?这是GoogleMapsAPI的限制吗?谢谢... 最佳答案 此行为是由于goog

  9. Ruby 获取二维数组中的对角线元素 - 2

    我正在尝试使用我的2Druby​​数组解决一些问题,当我进行数组切片时,我的LOC减少了很多。例如,require"test/unit"classLibraryTest我想知道是否有办法得到对角切片?假设我想从[0,0]开始并想要一个3的对角线切片。然后我会从[0,0]、[1,1]、[2,2]获取元素,我会得到一个数组[1,4,7]上面的例子。是否有任何神奇的单行ruby代码可以实现这一目标?3.次做{一些神奇的东西?} 最佳答案 puts(0..2).collect{|i|array[i][i]}

  10. ruby - 通过 ESC/POS 热敏打印机打印二维码? - 2

    我正在打印一些QR码(来自Ruby脚本),将ESC/POS命令写入EpsonTM-T20热敏打印机。顺便说一句,我正在编写一个简单的ESC/POS命令打印机“驱动程序”。我用的打印机是EpsonTM-T20(USB接口(interface))我正在使用serialportgem从Windows7主机进行一些测试。关于为打印格式化文本和线性条形码编写ESC/POS命令的一切都很好,但是我在理解打印QR代码的命令协议(protocol)时遇到问题,使用Epson提供的唯一可用文档(据我所知),请参阅:http://www.novopos.ch/client/EPSON/TM-T20/TM-

随机推荐