1. 程式人生 > >pcl_ros讀取bag鐳射雷達點雲的實現

pcl_ros讀取bag鐳射雷達點雲的實現

以下博文除了註釋以外的部分均來自AdamShan大佬,本人只是對內容作了註釋,還不一定對,只為自己學習,如有補充和錯誤,敬請提出,額,還有希望原文大佬看不見。
作者:AdamShan 
來源:CSDN 
原文:https://blog.csdn.net/AdamShan/article/details/82901295 
 

1.編寫include/pcl_test_core.h

#pragma once

#include <ros/ros.h>      
//匯入ROS系統包含核心公共標頭檔案

#include <pcl_conversions/pcl_conversions.h>
#include <pcl/point_types.h>   
//點型別標頭檔案
#include <pcl/conversions.h>
#include <pcl_ros/transforms.h>  

#include <pcl/filters/voxel_grid.h>

#include <sensor_msgs/PointCloud2.h>
//這些 .h檔案都是一系列模板檔案,類似於python中的numpy和matplotlib等庫檔案,可以直接呼叫,簡化程式設計。但具體每個庫是幹什麼的,沒查到。

class PclTestCore
{

  private:
    ros::Subscriber sub_point_cloud_;      //為接收點雲資訊建立了一個訂閱節點

    ros::Publisher pub_filtered_points_;   //建立了一個釋出濾波的節點

    void point_cb(const sensor_msgs::PointCloud2ConstPtr& in_cloud);
//void point_cb是宣告一個函式,這裡面設定了一個數據型別為sensor_msgs::PointCloud2ConstPtr& in_cloud形參,const在這裡修飾函式中的引數。將點雲格式sensor_mgs/pointcloud2轉換為pcl/pointcloud

  public:
    PclTestCore(ros::NodeHandle &nh);    
    ~PclTestCore();
    void Spin();
};

//1、public修飾的成員變數
//在程式的任何地方都可以被訪問,就是公共變數的意思,不需要通過成員函式就可以由類的例項直接訪問
//2、private修飾的成員變數
//只有類內可直接訪問,私有的,類的例項要通過成員函式才可以訪問,這個可以起到資訊隱藏

標頭檔案定義了 類 PclTestCore  分為private和public。private中用ros建立了兩個節點,為接受點雲資訊建立了訂閱節點(輸入),為濾波後的點雲建立了釋出節點(結果)。聲明瞭一個數據型別

2.編寫pcl_test_node.cpp

#include "pcl_test_core.h"

int main(int argc, char **argv)   //main函式,節點入口
{
    ros::init(argc, argv, "pcl_test");   //初始化節點,第三個引數  node_name,節點引數

    ros::NodeHandle nh;    //nh每個節點都對應一個控制代碼,例項化節點?

    PclTestCore core(nh); //   沒查到,反正與後續的節點啟動有關吧
    return 0;
}

這是main()函式,是節點的入口。 

ros::init() 初始化節點,pcl_test是節點名稱

ros::NodeHandle nh("/my_node_handle_namespace");

nh代表控制代碼的意思,每一個node有一個控制代碼。例項化節點?

3.編寫pcl_test_core.cpp

#include "pcl_test_core.h"

PclTestCore::PclTestCore(ros::NodeHandle &nh){
    sub_point_cloud_ = nh.subscribe("/velodyne_points",10, &PclTestCore::point_cb, this);  //這裡的sub_point_cloud在pcl_test_core.h中的prviate

    pub_filtered_points_ = nh.advertise<sensor_msgs::PointCloud2>("/filtered_points", 10);

    ros::spin(); //回撥函式
}

PclTestCore::~PclTestCore(){}

void PclTestCore::Spin(){
    
}
//可以看出以上三步呼應pcl_test_node.cpp,yeah!!!

void PclTestCore::point_cb(const sensor_msgs::PointCloud2ConstPtr & in_cloud_ptr){
//定義了兩個點雲指標,宣告儲存原始資料與濾波後的資料的點雲的格式
    pcl::PointCloud<pcl::PointXYZI>::Ptr current_pc_ptr(new pcl::PointCloud<pcl::PointXYZI>);
//pcl::PointCloud<pcl::PointXYZ>::Ptr (在這斷開的) cloud_Ptr(new pcl::PointCloud<pcl::PointXYZ>);  
//指的是pcl中指標和非指標的轉換。其中new pcl::PointCloud<pcl::PointXYZ>為原始點雲的資料格式。
//在函式返回指標時,經常會出現不知道的錯誤,不用返回指標,直接得到PointXYZ,再將其轉化為Ptr。
    pcl::PointCloud<pcl::PointXYZI>::Ptr filtered_pc_ptr(new pcl::PointCloud<pcl::PointXYZI>);
//new pcl::PointCloud<pcl::PointXYZI>,儲存濾波後的資料格式。

    pcl::fromROSMsg(*in_cloud_ptr, *current_pc_ptr);
// ros轉化為PCL中的點雲的資料格式,sensor_msgs::PointCloud2轉pcl::PointCloud<pcl::PointXYZ> 

    pcl::VoxelGrid<pcl::PointXYZI> vg;
//例項化濾波,vg為voxelgrid的縮寫,前面全部的代替,為後面程式設計提供方便,相當於import numpy as np  np.xxx

    vg.setInputCloud(current_pc_ptr); //設定輸入的濾波
    vg.setLeafSize(0.2f, 0.2f, 0.2f); //設定體素網格大小
    vg.filter(*filtered_pc_ptr);      //儲存濾波後的點雲


//再將濾波後的點雲轉換為ros下的資料格式釋出出去。
    sensor_msgs::PointCloud2 pub_pc;   //宣告輸出的點雲格式。
    pcl::toROSMsg(*filtered_pc_ptr, pub_pc);//將pcl點雲格式轉換為ros下的點雲格式,pcl::PointCloud<pcl::PointXYZ>轉sensor_msgs::PointCloud2。第一個引數是濾波後的pcl xyz格式點雲,第二個引數是抓換後的ros pointcloud2格式,之後釋出就釋出該點雲。

    pub_pc.header = in_cloud_ptr->header; 
//在此將in_cloude_ptr的索引賦給pub_pc.header???這個沒查到。

    pub_filtered_points_.publish(pub_pc);   
// pub_filtered_points_為之前建立的釋出節點,在此為該node賦值。
}


//從這裡以下參考sitwangmin博主,感謝!
//作者:sitwangmin 
//來源:CSDN 
//原文:https://blog.csdn.net/u010284636/article/details/79214841 

//ROS轉PCL資料格式
//sensor_msgs::PointCloud2轉pcl::PCLPointCloud2 
//pcl_conversions::toPCL(sensor_msgs::PointCloud2, pcl::PCLPointCloud2)

//sensor_msgs::PointCloud2轉pcl::PointCloud<pcl::PointXYZ> 
//pcl::fromROSMsg (sensor_msgs::PointCloud2, pcl::PointCloud<pcl::PointXYZ>);

//PCL轉ROS資料
//pcl::PCLPointCloud2轉sensor_msgs::PointCloud2
//pcl_conversions::fromPCL(pcl::PCLPointCloud2, sensor_msgs::PointCloud2);

//pcl::PointCloud<pcl::PointXYZ>轉sensor_msgs::PointCloud2
//pcl::toROSMsg (pcl::PointCloud<pcl::PointXYZ>,sensor_msgs::PointCloud2);

//PCL中資料互轉 
//pcl::PCLPointCloud2轉pcl::PointCloud <pcl::PointXYZ>
//pcl::fromPCLPointCloud2(pcl::PCLPointCloud2,pcl::PointCloud<pcl::PointXYZ>);

//pcl::PointCloud<pcl::PointXYZ>轉pcl::PCLPointCloud2
//pcl::toPCLPointCloud2(pcl::PointCloud<pcl::PointXYZ>, pcl::PCLPointCloud2);

!!!我的天啊,終於把上面三個檔案捋了一遍,但是仍然有好多無法理解。還有很多問題沒有解決。

4.寫一個launch檔案pcl_test.launch來啟動這個節點:

<launch>
    <node pkg="pcl_test" type="pcl_test_node" name="pcl_test_node" output="screen"/>
</launch>
//pkg 包名稱  type是什麼?貌似和name是一個東東。

5.雜七雜八

1)回到workspace 目錄,使用catkin_make 編譯:

catkin_make

2)啟動這個節點:

roslaunch pcl_test pcl_test.launch

3)新建終端,並執行我們的測試bag(測試bag下載連結:https://pan.baidu.com/s/1HOhs9StXUmZ_5sCALgKG3w)

rosbag play --clock test.bag

注意這裡,輸入這行命令一定是在bag所在位置,不然會報錯。

4)開啟第三個終端,啟動Rviz:

rosrun rviz rviz 

5)配置Rviz的Frame為velodyne,並且載入原始點雲和過濾以後的點雲的display。

在frame中輸入velodyne,在add中的topic裡面新增原始點雲和過濾後的點雲。

原始點雲:

降取樣之後的點雲(即我們的節點的輸出):