├── README.md └── pcd2pgm ├── CMakeLists.txt ├── launch └── run.launch ├── package.xml └── src └── pcd2pgm.cpp /README.md: -------------------------------------------------------------------------------- 1 | # pcd2pgm_package 2 | 点云pcd文件转二维栅格地图 3 | -------------------------------------------------------------------------------- /pcd2pgm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | project(pcd2pgm) 3 | 4 | ## is used, also find other catkin packages 5 | find_package(catkin REQUIRED COMPONENTS 6 | nav_msgs 7 | pcl_ros 8 | roscpp 9 | rospy 10 | std_msgs 11 | geometry_msgs 12 | sensor_msgs 13 | ) 14 | 15 | catkin_package( 16 | # INCLUDE_DIRS include 17 | # LIBRARIES pcd2pgm 18 | # CATKIN_DEPENDS nav_msgs pcl roscpp rospy std_msgs 19 | # DEPENDS system_lib 20 | ) 21 | 22 | 23 | include_directories( 24 | # include 25 | ${catkin_INCLUDE_DIRS} 26 | ) 27 | 28 | 29 | find_package(PCL REQUIRED) 30 | 31 | include_directories( 32 | include 33 | ${PCL_INCLUDE_DIRS}) 34 | 35 | 36 | add_executable(pcd2pgm src/pcd2pgm.cpp) 37 | target_link_libraries(pcd2pgm ${catkin_LIBRARIES} ${PCL_LIBRARIES} ) 38 | 39 | 40 | -------------------------------------------------------------------------------- /pcd2pgm/launch/run.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /pcd2pgm/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | pcd2pgm 4 | 0.0.0 5 | The pcd2pgm package 6 | 7 | 8 | 9 | 10 | facker 11 | 12 | 13 | 14 | 15 | 16 | TODO 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | catkin 52 | geometry_msgs 53 | nav_msgs 54 | pcl_ros 55 | roscpp 56 | sensor_msgs 57 | std_msgs 58 | geometry_msgs 59 | nav_msgs 60 | pcl_ros 61 | roscpp 62 | sensor_msgs 63 | std_msgs 64 | geometry_msgs 65 | nav_msgs 66 | pcl_ros 67 | roscpp 68 | sensor_msgs 69 | std_msgs 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /pcd2pgm/src/pcd2pgm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include //条件滤波器头文件 11 | #include //直通滤波器头文件 12 | #include //半径滤波器头文件 13 | #include //统计滤波器头文件 14 | #include //体素滤波器头文件 15 | #include 16 | 17 | std::string file_directory; 18 | std::string file_name; 19 | std::string pcd_file; 20 | 21 | std::string map_topic_name; 22 | 23 | const std::string pcd_format = ".pcd"; 24 | 25 | nav_msgs::OccupancyGrid map_topic_msg; 26 | //最小和最大高度 27 | double thre_z_min = 0.3; 28 | double thre_z_max = 2.0; 29 | int flag_pass_through = 0; 30 | double map_resolution = 0.05; 31 | double thre_radius = 0.1; 32 | //半径滤波的点数阈值 33 | int thres_point_count = 10; 34 | 35 | //直通滤波后数据指针 36 | pcl::PointCloud::Ptr 37 | cloud_after_PassThrough(new pcl::PointCloud); 38 | //半径滤波后数据指针 39 | pcl::PointCloud::Ptr 40 | cloud_after_Radius(new pcl::PointCloud); 41 | pcl::PointCloud::Ptr 42 | pcd_cloud(new pcl::PointCloud); 43 | 44 | //直通滤波 45 | void PassThroughFilter(const double &thre_low, const double &thre_high, 46 | const bool &flag_in); 47 | //半径滤波 48 | void RadiusOutlierFilter(const pcl::PointCloud::Ptr &pcd_cloud, 49 | const double &radius, const int &thre_count); 50 | //转换为栅格地图数据并发布 51 | void SetMapTopicMsg(const pcl::PointCloud::Ptr cloud, 52 | nav_msgs::OccupancyGrid &msg); 53 | 54 | int main(int argc, char **argv) { 55 | ros::init(argc, argv, "pcl_filters"); 56 | ros::NodeHandle nh; 57 | ros::NodeHandle private_nh("~"); 58 | 59 | ros::Rate loop_rate(1.0); 60 | 61 | private_nh.param("file_directory", file_directory, std::string("/home/")); 62 | 63 | private_nh.param("file_name", file_name, std::string("map")); 64 | 65 | pcd_file = file_directory + file_name + pcd_format; 66 | 67 | private_nh.param("thre_z_min", thre_z_min, 0.2); 68 | private_nh.param("thre_z_max", thre_z_max, 2.0); 69 | private_nh.param("flag_pass_through", flag_pass_through, 0); 70 | private_nh.param("thre_radius", thre_radius, 0.5); 71 | private_nh.param("map_resolution", map_resolution, 0.05); 72 | private_nh.param("thres_point_count", thres_point_count, 10); 73 | private_nh.param("map_topic_name", map_topic_name, std::string("map")); 74 | 75 | ros::Publisher map_topic_pub = 76 | nh.advertise(map_topic_name, 1); 77 | 78 | // 下载pcd文件 79 | if (pcl::io::loadPCDFile(pcd_file, *pcd_cloud) == -1) { 80 | PCL_ERROR("Couldn't read file: %s \n", pcd_file.c_str()); 81 | return (-1); 82 | } 83 | 84 | std::cout << "初始点云数据点数:" << pcd_cloud->points.size() << std::endl; 85 | //对数据进行直通滤波 86 | PassThroughFilter(thre_z_min, thre_z_max, bool(flag_pass_through)); 87 | //对数据进行半径滤波 88 | RadiusOutlierFilter(cloud_after_PassThrough, thre_radius, thres_point_count); 89 | //转换为栅格地图数据并发布 90 | SetMapTopicMsg(cloud_after_Radius, map_topic_msg); 91 | // SetMapTopicMsg(cloud_after_PassThrough, map_topic_msg); 92 | 93 | while (ros::ok()) { 94 | map_topic_pub.publish(map_topic_msg); 95 | 96 | loop_rate.sleep(); 97 | 98 | ros::spinOnce(); 99 | } 100 | 101 | return 0; 102 | } 103 | 104 | //直通滤波器对点云进行过滤,获取设定高度范围内的数据 105 | void PassThroughFilter(const double &thre_low, const double &thre_high, 106 | const bool &flag_in) { 107 | // 创建滤波器对象 108 | pcl::PassThrough passthrough; 109 | //输入点云 110 | passthrough.setInputCloud(pcd_cloud); 111 | //设置对z轴进行操作 112 | passthrough.setFilterFieldName("z"); 113 | //设置滤波范围 114 | passthrough.setFilterLimits(thre_low, thre_high); 115 | // true表示保留滤波范围外,false表示保留范围内 116 | passthrough.setFilterLimitsNegative(flag_in); 117 | //执行滤波并存储 118 | passthrough.filter(*cloud_after_PassThrough); 119 | // test 保存滤波后的点云到文件 120 | pcl::io::savePCDFile(file_directory + "map_filter.pcd", 121 | *cloud_after_PassThrough); 122 | std::cout << "直通滤波后点云数据点数:" 123 | << cloud_after_PassThrough->points.size() << std::endl; 124 | } 125 | 126 | //半径滤波 127 | void RadiusOutlierFilter(const pcl::PointCloud::Ptr &pcd_cloud0, 128 | const double &radius, const int &thre_count) { 129 | //创建滤波器 130 | pcl::RadiusOutlierRemoval radiusoutlier; 131 | //设置输入点云 132 | radiusoutlier.setInputCloud(pcd_cloud0); 133 | //设置半径,在该范围内找临近点 134 | radiusoutlier.setRadiusSearch(radius); 135 | //设置查询点的邻域点集数,小于该阈值的删除 136 | radiusoutlier.setMinNeighborsInRadius(thre_count); 137 | radiusoutlier.filter(*cloud_after_Radius); 138 | // test 保存滤波后的点云到文件 139 | pcl::io::savePCDFile(file_directory + "map_radius_filter.pcd", 140 | *cloud_after_Radius); 141 | std::cout << "半径滤波后点云数据点数:" << cloud_after_Radius->points.size() 142 | << std::endl; 143 | } 144 | 145 | //转换为栅格地图数据并发布 146 | void SetMapTopicMsg(const pcl::PointCloud::Ptr cloud, 147 | nav_msgs::OccupancyGrid &msg) { 148 | msg.header.seq = 0; 149 | msg.header.stamp = ros::Time::now(); 150 | msg.header.frame_id = "map"; 151 | 152 | msg.info.map_load_time = ros::Time::now(); 153 | msg.info.resolution = map_resolution; 154 | 155 | double x_min, x_max, y_min, y_max; 156 | double z_max_grey_rate = 0.05; 157 | double z_min_grey_rate = 0.95; 158 | //? ? ?? 159 | double k_line = 160 | (z_max_grey_rate - z_min_grey_rate) / (thre_z_max - thre_z_min); 161 | double b_line = 162 | (thre_z_max * z_min_grey_rate - thre_z_min * z_max_grey_rate) / 163 | (thre_z_max - thre_z_min); 164 | 165 | if (cloud->points.empty()) { 166 | ROS_WARN("pcd is empty!\n"); 167 | return; 168 | } 169 | 170 | for (int i = 0; i < cloud->points.size() - 1; i++) { 171 | if (i == 0) { 172 | x_min = x_max = cloud->points[i].x; 173 | y_min = y_max = cloud->points[i].y; 174 | } 175 | 176 | double x = cloud->points[i].x; 177 | double y = cloud->points[i].y; 178 | 179 | if (x < x_min) 180 | x_min = x; 181 | if (x > x_max) 182 | x_max = x; 183 | 184 | if (y < y_min) 185 | y_min = y; 186 | if (y > y_max) 187 | y_max = y; 188 | } 189 | // origin的确定 190 | msg.info.origin.position.x = x_min; 191 | msg.info.origin.position.y = y_min; 192 | msg.info.origin.position.z = 0.0; 193 | msg.info.origin.orientation.x = 0.0; 194 | msg.info.origin.orientation.y = 0.0; 195 | msg.info.origin.orientation.z = 0.0; 196 | msg.info.origin.orientation.w = 1.0; 197 | //设置栅格地图大小 198 | msg.info.width = int((x_max - x_min) / map_resolution); 199 | msg.info.height = int((y_max - y_min) / map_resolution); 200 | //实际地图中某点坐标为(x,y),对应栅格地图中坐标为[x*map.info.width+y] 201 | msg.data.resize(msg.info.width * msg.info.height); 202 | msg.data.assign(msg.info.width * msg.info.height, 0); 203 | 204 | ROS_INFO("data size = %d\n", msg.data.size()); 205 | 206 | for (int iter = 0; iter < cloud->points.size(); iter++) { 207 | int i = int((cloud->points[iter].x - x_min) / map_resolution); 208 | if (i < 0 || i >= msg.info.width) 209 | continue; 210 | 211 | int j = int((cloud->points[iter].y - y_min) / map_resolution); 212 | if (j < 0 || j >= msg.info.height - 1) 213 | continue; 214 | // 栅格地图的占有概率[0,100],这里设置为占据 215 | msg.data[i + j * msg.info.width] = 100; 216 | // msg.data[i + j * msg.info.width] = int(255 * (cloud->points[iter].z * 217 | // k_line + b_line)) % 255; 218 | } 219 | } 220 | --------------------------------------------------------------------------------