autoware.universe源碼略讀(3.7)--perception:elevation_map_loader/euclidean_cluster

autoware.universe源碼略讀3.7--perception:elevation_map_loader/euclidean_cluster

  • elevation_map_loader
  • euclidean_cluster
    • euclidean_cluster
    • voxel_grid_based_euclidean_cluster
    • 節點類
    • launch文件

elevation_map_loader

在這里插入圖片描述

在上一篇文章有提到compare_map_segmentation這個濾波的操作,這個模塊就是生成海拔圖用的。這個也是很標準的一個節點類(在網上又看了一點ROS2的知識,似乎叫組件更合理一些?) 這里我們可以看到,使用到了grid_map_pcl_loader_這樣一個變量

  grid_map_pcl_loader_ = pcl::make_shared<grid_map::GridMapPclLoader>(grid_map_logger);grid_map_pcl_loader_->loadParameters(param_file_path);

然后在話題的訂閱與發布中,分別訂閱了三個話題:map_hash,pointcloud_map以及vector_map

  sub_map_hash_ = create_subscription<tier4_external_api_msgs::msg::MapHash>("/api/autoware/get/map/info/hash", durable_qos,std::bind(&ElevationMapLoaderNode::onMapHash, this, _1));sub_pointcloud_map_ = this->create_subscription<sensor_msgs::msg::PointCloud2>("input/pointcloud_map", durable_qos,std::bind(&ElevationMapLoaderNode::onPointcloudMap, this, _1));sub_vector_map_ = this->create_subscription<autoware_auto_mapping_msgs::msg::HADMapBin>("input/vector_map", durable_qos, std::bind(&ElevationMapLoaderNode::onVectorMap, this, _1));

在三個訂閱話題的回調函數里都是根據消息進行了一些準備工作,最重要的變量就是data_manager_,三個回調函數分別設置了這個變量的一些對象

  // onMapHashdata_manager_.elevation_map_path_ = std::make_unique<std::filesystem::path>(std::filesystem::path(elevation_map_directory_) / elevation_map_hash);// onPointcloudMapdata_manager_.map_pcl_ptr_ = pcl::make_shared<pcl::PointCloud<pcl::PointXYZ>>(map_pcl);// onVectorMapdata_manager_.lanelet_map_ptr_ = std::make_shared<lanelet::LaneletMap>();lanelet::utils::conversion::fromBinMsg(*vector_map, data_manager_.lanelet_map_ptr_);const lanelet::ConstLanelets all_lanelets =lanelet::utils::query::laneletLayer(data_manager_.lanelet_map_ptr_);lane_filter_.road_lanelets_ = lanelet::utils::query::roadLanelets(all_lanelets);

data_manager_.isInitialized()通過的時候,會調用到publish函數,這里并不是一定要有已經存在的海拔圖的,可以看到在函數最前面先判斷海拔地圖是否存在

  1. 如果不存在的話,就來生成一個
  if (stat(data_manager_.elevation_map_path_->c_str(), &info) != 0) {RCLCPP_INFO(this->get_logger(), "Create elevation map from pointcloud map ");createElevationMap();

生成海拔地圖是分成了是否使用車道線濾波兩種方式,如果不使用就直接為grid_map_pcl_loader_設置輸入點云了,使用的話就先對點云進行一下濾波的操作,然后把濾波過的點云作為輸入點云

    const auto convex_hull = getConvexHull(data_manager_.map_pcl_ptr_);lanelet::ConstLanelets intersected_lanelets =getIntersectedLanelets(convex_hull, lane_filter_.road_lanelets_);pcl::PointCloud<pcl::PointXYZ>::Ptr lane_filtered_map_pcl_ptr =getLaneFilteredPointCloud(intersected_lanelets, data_manager_.map_pcl_ptr_);grid_map_pcl_loader_->setInputCloud(lane_filtered_map_pcl_ptr);

具體生成的部分createElevationMapFromPointcloud,應該是調用grid_map庫來實現的

void ElevationMapLoaderNode::createElevationMapFromPointcloud()
{const auto start = std::chrono::high_resolution_clock::now();grid_map_pcl_loader_->preProcessInputCloud();grid_map_pcl_loader_->initializeGridMapGeometryFromInputCloud();grid_map_pcl_loader_->addLayerFromInputCloud(layer_name_);grid_map::grid_map_pcl::printTimeElapsedToRosInfoStream(start, "Finish creating elevation map. Total time: ", this->get_logger());
}

接下來還有一步就是inpaintElevationMap,事實上經過之前的操作,我們已經得到了elevation_map_,但現在的這個海拔圖可能有很多孔雀,也并不連貫,所以這個函數的作用就是讓海拔圖更連續一些,具體是直接用cv::inpaint(original_image, mask, filled_image, radius_in_pixels, cv::INPAINT_NS);來對整個海拔圖進行修補,然后把圖像再轉回elevation_map_

  1. 如果海拔圖存在的話,會嘗試加載海拔圖,加載成功就直接用已經有的海拔圖了,加載失敗的話就調用createElevationMap()函數去生成,這里也是用到了grid_map
  } else if (info.st_mode & S_IFDIR) {RCLCPP_INFO(this->get_logger(), "Load elevation map from: %s",data_manager_.elevation_map_path_->c_str());// Check if bag can be loadedbool is_bag_loaded = false;try {is_bag_loaded = grid_map::GridMapRosConverter::loadFromBag(*data_manager_.elevation_map_path_, "elevation_map", elevation_map_);} catch (rosbag2_storage_plugins::SqliteException & e) {is_bag_loaded = false;}if (!is_bag_loaded) {// Delete directory including elevation map if bag is brokenRCLCPP_ERROR(this->get_logger(), "Try to loading bag, but bag is broken. Remove %s",data_manager_.elevation_map_path_->c_str());std::filesystem::remove_all(data_manager_.elevation_map_path_->c_str());// Create elevation map from pointcloud map if bag is brokenRCLCPP_INFO(this->get_logger(), "Create elevation map from pointcloud map ");createElevationMap();}}

最后就是對海拔地圖,以及海拔地圖對應的點云進行發布了

  pub_elevation_map_->publish(std::move(msg));  // 這里用到move是因為后邊就不用msg了,所以能避免不必要的copyif (use_elevation_map_cloud_publisher_) {pcl::PointCloud<pcl::PointXYZ>::Ptr elevation_map_cloud_ptr =createPointcloudFromElevationMap();sensor_msgs::msg::PointCloud2 elevation_map_cloud_msg;pcl::toROSMsg(*elevation_map_cloud_ptr, elevation_map_cloud_msg);pub_elevation_map_cloud_->publish(elevation_map_cloud_msg);}

當然,這個包也是最新的版本會比galatic更復雜嚴謹一些的,不過這里沒看最新版本的是什么樣的

euclidean_cluster

歐式聚類,這個在上邊剛剛用到,簡單來說就是把點云聚類成更小的部分以進行物體的分類的步驟。這里是分了兩種方法,一種是歐式聚類,也就是euclidean_cluster,另一種是voxel_grid_based_euclidean_cluster,看起來是根據體素網格進行分類的一種辦法。

euclidean_cluster

這里的構造函數輸入了幾個參數,分別是是否使用高度信息,聚類最少點數,最多點數以及點之間的最大距離閾值,然后這里整個類EuclideanCluster是繼承自EuclideanClusterInterface的,所以有幾個成員變量也是繼承過來的。然后主要的聚類操作的函數就在clustrer,這里我們可以看到,其實也是用到了KD樹的

  // create treepcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);tree->setInputCloud(pointcloud_ptr);

然后直接用PCL庫中的聚類函數實現

  // clusteringstd::vector<pcl::PointIndices> cluster_indices;pcl::EuclideanClusterExtraction<pcl::PointXYZ> pcl_euclidean_cluster;pcl_euclidean_cluster.setClusterTolerance(tolerance_);pcl_euclidean_cluster.setMinClusterSize(min_cluster_size_);pcl_euclidean_cluster.setMaxClusterSize(max_cluster_size_);pcl_euclidean_cluster.setSearchMethod(tree);pcl_euclidean_cluster.setInputCloud(pointcloud_ptr);pcl_euclidean_cluster.extract(cluster_indices);

然后就是輸出了

voxel_grid_based_euclidean_cluster

VoxelGridBasedEuclideanCluster這個類也是繼承自EuclideanClusterInterface的,所以這里的構造函數和EuclideanCluster一樣,也是對接口類的幾個成員變量進行了設置,這里還有兩個成員變量,一個是 voxel_leaf_size_(voxel_leaf_size),這個代表的應該就是體素網格的大小;另一個是min_points_number_per_voxel_,這里對應的應該是一個體素網格里最少的點數。然后這個方法的核心操作也是在clustre函數中,這里一上來也是設置了一些參數

  pcl::PointCloud<pcl::PointXYZ>::Ptr voxel_map_ptr(new pcl::PointCloud<pcl::PointXYZ>);voxel_grid_.setLeafSize(voxel_leaf_size_, voxel_leaf_size_, 100000.0);voxel_grid_.setMinimumPointsNumberPerVoxel(min_points_number_per_voxel_);voxel_grid_.setInputCloud(pointcloud);voxel_grid_.setSaveLeafLayout(true);voxel_grid_.filter(*voxel_map_ptr);

我們可以看到劃分網格的時候,z方向其實是不劃分的,所以這樣網格就可以看成是一個二維的網格了。然后這里的filter我的理解應該就是把點云按照體素網格劃分開來了。之后也是用到了KD樹,值得注意的是,因為這里其實是不考慮Z軸信息的,所以點云也是被轉成了2D點云

  // voxel is pressed 2dpcl::PointCloud<pcl::PointXYZ>::Ptr pointcloud_2d_ptr(new pcl::PointCloud<pcl::PointXYZ>);for (const auto & point : voxel_map_ptr->points) {pcl::PointXYZ point2d;point2d.x = point.x;point2d.y = point.y;point2d.z = 0.0;pointcloud_2d_ptr->push_back(point2d);}// create treepcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);tree->setInputCloud(pointcloud_2d_ptr);

然后聚類也是一樣,直接用PCL的pcl::EuclideanClusterExtraction

  // clusteringstd::vector<pcl::PointIndices> cluster_indices;pcl::EuclideanClusterExtraction<pcl::PointXYZ> pcl_euclidean_cluster;pcl_euclidean_cluster.setClusterTolerance(tolerance_);pcl_euclidean_cluster.setMinClusterSize(1);pcl_euclidean_cluster.setMaxClusterSize(max_cluster_size_);pcl_euclidean_cluster.setSearchMethod(tree);pcl_euclidean_cluster.setInputCloud(pointcloud_2d_ptr);pcl_euclidean_cluster.extract(cluster_indices);

將點云中的點根據其在體素網格中的位置進行聚類,結果存儲在temporary_clusters中。這樣,就可以通過體素網格索引快速查找點所在的聚類。

  // create map to search cluster index from voxel grid indexstd::unordered_map</* voxel grid index */ int, /* cluster index */ int> map;for (size_t cluster_idx = 0; cluster_idx < cluster_indices.size(); ++cluster_idx) {const auto & cluster = cluster_indices.at(cluster_idx);for (const auto & point_idx : cluster.indices) {map[point_idx] = cluster_idx;}}// create vector of point cloud cluster. vector index is voxel grid index.std::vector<pcl::PointCloud<pcl::PointXYZ>> temporary_clusters;  // no check about cluster sizetemporary_clusters.resize(cluster_indices.size());for (const auto & point : pointcloud->points) {const int index =voxel_grid_.getCentroidIndexAt(voxel_grid_.getGridCoordinates(point.x, point.y, point.z));if (map.find(index) != map.end()) {temporary_clusters.at(map[index]).points.push_back(point);}}

最后也是把這些作為結果輸出就好了

節點類

這兩個的節點類是差不多的,構造函數加載參數,實例化聚類的類,然后訂閱話題是點云

  using std::placeholders::_1;pointcloud_sub_ = this->create_subscription<sensor_msgs::msg::PointCloud2>("input", rclcpp::SensorDataQoS().keep_last(1),std::bind(&VoxelGridBasedEuclideanClusterNode::onPointCloud, this, _1));

然后在回調函數中,執行了聚類操作以及消息類型的各種轉換

  // convert ros to pclpcl::PointCloud<pcl::PointXYZ>::Ptr raw_pointcloud_ptr(new pcl::PointCloud<pcl::PointXYZ>);pcl::fromROSMsg(*input_msg, *raw_pointcloud_ptr);// clusteringstd::vector<pcl::PointCloud<pcl::PointXYZ>> clusters;cluster_->cluster(raw_pointcloud_ptr, clusters);// build output msgtier4_perception_msgs::msg::DetectedObjectsWithFeature output;convertPointCloudClusters2Msg(input_msg->header, clusters, output);cluster_pub_->publish(output);

launch文件

這里的launch文件是使用了launch+py文件的這種形式,在launch文件里主要是加載了程序要使用到的各種參數,然后在python文件中是具體的啟動了節點,在python文件中,可以看到還加載了幾個組件

    # set voxel grid filter as a componentvoxel_grid_filter_component = ComposableNode(package="pointcloud_preprocessor",plugin="pointcloud_preprocessor::VoxelGridDownsampleFilterComponent",name=AnonName("voxel_grid_filter"),remappings=[("input", LaunchConfiguration("input_pointcloud")),("output", "voxel_grid_filtered/pointcloud"),],parameters=[load_composable_node_param("voxel_grid_param_path")],)# set compare map filter as a componentcompare_map_filter_component = ComposableNode(package="compare_map_segmentation",plugin="compare_map_segmentation::VoxelBasedCompareMapFilterComponent",name=AnonName("compare_map_filter"),remappings=[("input", "voxel_grid_filtered/pointcloud"),("map", LaunchConfiguration("input_map")),("output", "compare_map_filtered/pointcloud"),],)

然后再具體一點,我們可以看到話題的名字在這里是有remapping操作的,所以代碼里都是input和output

    use_map_euclidean_cluster_component = ComposableNode(package=pkg,plugin="euclidean_cluster::EuclideanClusterNode",name=AnonName("euclidean_cluster"),remappings=[("input", "compare_map_filtered/pointcloud"),("output", LaunchConfiguration("output_clusters")),],parameters=[load_composable_node_param("euclidean_param_path")],)

然后其實具體的啟動函數是在generate_launch_description中的,里面會調用launch_setup這個函數

def generate_launch_description():def add_launch_arg(name: str, default_value=None):return DeclareLaunchArgument(name, default_value=default_value)return launch.LaunchDescription([add_launch_arg("input_pointcloud", "/perception/obstacle_segmentation/pointcloud"),add_launch_arg("input_map", "/map/pointcloud_map"),add_launch_arg("output_clusters", "clusters"),add_launch_arg("use_pointcloud_map", "false"),add_launch_arg("voxel_grid_param_path",[FindPackageShare("euclidean_cluster"), "/config/voxel_grid.param.yaml"],),add_launch_arg("euclidean_param_path",[FindPackageShare("euclidean_cluster"), "/config/euclidean_cluster.param.yaml"],),OpaqueFunction(function=launch_setup),])

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/39056.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/39056.shtml
英文地址,請注明出處:http://en.pswp.cn/web/39056.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于java+springboot+vue實現的家政服務平臺(文末源碼+Lw)299

摘 要 現代經濟快節奏發展以及不斷完善升級的信息化技術&#xff0c;讓傳統數據信息的管理升級為軟件存儲&#xff0c;歸納&#xff0c;集中處理數據信息的管理方式。本家政服務平臺就是在這樣的大環境下誕生&#xff0c;其可以幫助管理者在短時間內處理完畢龐大的數據信息&a…

Redis中hash類型的操作命令(命令的語法、返回值、時間復雜度、注意事項、操作演示)

文章目錄 字符串和哈希類型相比hset 命令hget 命令hexistshdelhkeyshvalshgetallhmgethlenhsetnxhincrbyhincrbyfloat 字符串和哈希類型相比 假設有以下一種場景&#xff1a;現在要在 Redis 中存儲一個用戶的基本信息(id1、namezhangsan、age17)&#xff0c;下圖表示使用字符串…

2024護網整體工作預案示例

目錄 第1章 HW整體工作工作部署 1.1 工作組織架構 1.2 各部門工作職責 1.3 演練期間工作機制 1.3.1 工作匯報機制 1.3.2 應急響應機制 第2章 系統資產梳理整改 2.1 敏感信息梳理整改 2.2 互聯網資產發現 2.3 第三方供應商梳理 2.4 業務連接單位梳理 第3…

下載nginx搭建的文件服務器(爬蟲)

下載nginx搭建的文件服務器&#xff08;爬蟲&#xff09; windows版 需要下載python包&#xff1a;pip install requests import requests import re import os#開始訪問的url地址&#xff0c;必須以/結尾 index_url "https://www.aaa.com/aaaaa/" #下載到本地的地…

win10顯示毫秒-上午-下午及星期幾,24小時制

關于毫秒 winr regedit 計算機\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced 新建ShowSecondsInSystemClock&#xff0c;編輯1顯示&#xff0c;不顯示就刪了它 然后重啟 資源管理器可能有多個全部重啟&#xff0c;就可以啦 根據自己喜好…

黨建科普3D數字化展館支持實時更新迭代

3D虛擬策展逐漸成為新時代下的主流方式&#xff0c;深圳華銳視點作為專業的web3d開發公司&#xff0c;具有專業化的3D數字化空間還原能力&#xff0c;能根據企業/個人不同需求和預算&#xff0c;為您打造純線上虛擬3D藝術展&#xff0c;讓您徹底擺脫實體美術館的限制&#xff0…

面試官:Java文件是如何被加載到內存中的?

面試連環call Java文件是如何被加載到內存中的&#xff1f;Java類的聲明周期都有哪些階段&#xff1f;JVM加載的class文件都有哪些來源&#xff1f;JVM在加載class文件時&#xff0c;何時判斷class文件的格式是否符合要求&#xff1f; 類生命周期 一個類從被加載到虛擬機內存…

【計算機體系結構】緩存的false sharing

在介紹緩存的false sharing之前&#xff0c;本文先介紹一下多核系統中緩存一致性是如何維護的。 目前主流的多核系統中的緩存一致性協議是MESI協議及其衍生協議。 MESI協議 MESI協議的4種狀態 MESI協議有4種狀態。MESI是4種狀態的首字母縮寫&#xff0c;緩存行的4種狀態分別…

【Linux】—Xshell、Xftp安裝

文章目錄 前言一、下載Xshell、Xftp二、安裝Xshell三、使用XShell連接Linux服務器四、修改windows的主機映射文件&#xff08;hosts文件&#xff09;五、遠程連接hadoop102/hadoop103/hadoop104服務器六、安裝Xftp 前言 XShell遠程管理工具&#xff0c;可以在Windows界面下來訪…

[數據集][目標檢測]螺絲螺母檢測數據集VOC+YOLO格式2400張2類別

數據集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路徑的txt文件&#xff0c;僅僅包含jpg圖片以及對應的VOC格式xml文件和yolo格式txt文件) 圖片數量(jpg文件個數)&#xff1a;2400 標注數量(xml文件個數)&#xff1a;2400 標注數量(txt文件個數)&#xff1a;2400 標注…

SpringBoot 整合 Minio 實現文件切片極速上傳技術

Centos7安裝Minio 創建目標文件夾 mkdir minio使用docker查看目標鏡像狀況 大家需要注意&#xff0c;此處我們首先需要安裝docker&#xff0c;對于相關安裝教程&#xff0c;大家可以查看我之前的文章&#xff0c;按部就班就可以&#xff0c;此處不再贅述&#xff01;&#x…

uniapp入門

一、新建項目 進入到主界面&#xff0c;左上角點擊新建——1.項目 輸入項目名稱&#xff0c;Vue版本選擇3 二、創建頁面 選中左側文件目錄里的pages文件夾&#xff0c;右鍵&#xff0c;選擇新建頁面 1輸入名稱 2選中“創建同名目錄” 3選擇模板&…

將json對象轉為xml進行操作屬性

將json對象轉為xml進行操作屬性 文章目錄 將json對象轉為xml進行操作屬性前端發送json數據格式寫入數據庫格式-content字段存儲&#xff08;varchar(2000)&#xff09;Question實體類-接口映射對象QuestionContent 接收參數對象DAO持久層Mapper層Service層Controller控制層接收…

《每天5分鐘用Flask搭建一個管理系統》第13章:性能優化

第13章&#xff1a;性能優化 13.1 性能優化的重要性 性能優化確保應用能夠處理高并發請求&#xff0c;減少響應時間&#xff0c;提高用戶體驗和應用的可擴展性。 13.2 Flask緩存機制 緩存是提高Web應用性能的關鍵技術之一&#xff0c;它可以減少數據庫查詢次數和服務器計算…

Java 開發環境配置

配置Java開發環境涉及幾個主要步驟&#xff0c;包括安裝Java Development Kit (JDK)、配置環境變量和選擇集成開發環境&#xff08;IDE&#xff09;。以下是詳細的配置步驟&#xff1a; ### 1. 安裝 Java Development Kit (JDK) 1. **下載 JDK**&#xff1a; 訪問 Oracle …

完全指南:在Linux上安裝和精通Conda

前言 Conda是一個強大的包管理和環境管理工具&#xff0c;特別適用于數據科學和機器學習項目。本文將詳細指導你在Linux系統上安裝、配置和充分利用Conda的方法。 步驟一&#xff1a;下載和安裝Conda 下載安裝包&#xff1a; wget https://repo.anaconda.com/miniconda/Minic…

普元EOS學習筆記-低開實現圖書的增刪改查

前言 在前一篇《普元EOS學習筆記-創建精簡應用》中&#xff0c;我已經創建了EOS精簡應用。 我之前說過&#xff0c;EOS精簡應用就是自己創建的EOS精簡版&#xff0c;該項目中&#xff0c;開發者可以進行低代碼開發&#xff0c;也可以進行高代碼開發。 本文我就記錄一下自己在…

Golang中swtich中如何強制執行下一個代碼塊

switch 語句中的 case 代碼塊會默認帶上 break&#xff0c;但可以使用 fallthrough 來強制執行下一個 case 代碼塊。 package mainimport ("fmt" )func main() {isSpace : func(char byte) bool {switch char {case : // 空格符會直接 break&#xff0c;返回 false…

2024年6月 | deepin 深度應用商店-應用更新記錄

新增應用 序號應用名稱depein 系統版本應用分類應用類型1bkViewer 照片瀏覽器deepin 20.9 deepin V23網絡應用wine291助手deepin 20.9 deepin V23編程開發wine3風云CAD轉換器deepin 20.9 deepin V23編程開發wine4Disk Savvydeepin 20.9 deepin V23系統工具wine5飛貓盤…

miniconda3 安裝jupyter notebook并配置網絡訪問

由于服務器安裝的miniconda3&#xff0c;無jupyter notebook&#xff0c;所以手工安裝jupyter notebook 1 先conda 安裝相關包 在base 環境下 conda install ipython conda install jupyter notebook 2 生成配置文件 jupyter notebook --generate-config Writing defaul…