前言
使用ROS处理点云数据时,经常需要进行数据格式转换,这里做个记录。
数据格式
首先要了解点云常见的两种格式sensor_msgs/PointCloud2
和pcl::PointT
。
sensor_msgs/PointCloud2
是传感器中用的,像订阅激光点云或者相机点云topic返回的格式。格式如下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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41header: // 点云的头信息
seq: 963 // 次数
stamp: // 时间戳
secs: 1541143772
nsecs: 912011000
frame_id: "/camera_init"
height: 1 // 如果cloud 是无序的 height 是 1
width: 852578 // 点云的长度
fields: // sensor_msgs/PointField[] fields
-
name: "x"
offset: 0
datatype: 7 // uint8 INT8 = 1
// uint8 UINT8 = 2
// uint8 INT16 = 3
// uint8 UINT16 = 4
// uint8 INT32 = 5
// uint8 UINT32 = 6
// uint8 FLOAT32 = 7
// uint8 FLOAT64 = 8
count: 1
-
name: "y"
offset: 4
datatype: 7
count: 1
-
name: "z"
offset: 8
datatype: 7
count: 1
-
name: "intensity"
offset: 16
datatype: 7
count: 1
is_bigendian: False
point_step: 32 // 一个点占的比特数
row_step: 27282496 // 一行的长度占用的比特数,就是width*point_step的大小
data: [ ................... ] // Actual point data, size is (row_step*height)
is_dense: True // 没有非法数据点pcl::PointT
是一类格式,包括PointXYZ
、PointXYZI
、PointXYZRGBA
、PointXYZRGB
、PointXY
等。一般处理点云就要将PointCloud2
转为PCL
格式,再进行操作。PointXYZI
格式如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18union
{
float data[4];
struct
{
float x;
float y;
float z;
};
};
union
{
struct
{
float intensity;
};
float data_c[4];
};
PointCloud2转PointXYZI
有两种方法,一种是直接调用pcl
自带的函数,一种是采用mencpy
的方式,直接从地址提取部分点云。
方法一
直接调用pcl自带的函数函数pcl::fromROSMsg(const sensor_msgs::PointCloud2 &cloud, pcl::PointCloud<T> &pcl_cloud)
。
注意这个需要添加头文件#include <pcl_conversions/pcl_conversions.h>
使用举例:
1 | void callback(const sensor_msgs::PointCloud2ConstPtr& cloud) { |
我们可以看下pcl::fromROSMsg
做了啥
1 | namespace pcl { |
根据这个,其实我们也可以直接用pcl::fromPCLPointCloud2
来实现
1 | void toPCL(const sensor_msgs::PointCloud2& cloud, |
方法二
当订阅得到的初始点云话题数据量过大时,使用pcl::fromROSMsg
转成pcl
点云再做处理,会用去大量的时间。
因此采用mencpy
的方式,直接从地址提取部分点云,可以有效节省时间。mencpy
格式为void *memcpy(void *dest, const void *src, size_t n);
第一个参数是接收数据的参数,第二个参数是数据的地址,第三个参数是数据的大小。
根据上面sensor_msgs/PointCloud2
的格式,datatype
是7,所以对应的是FLOAT32
。也就是说每个点都有x,y,z,intensity
四个数据,数据类型都是float32
,占四个字节。
另外,point_step
是32,即4x8
。所以x,y,z
占用前面12个字节,再空出4个字节后,存储intensity
,再空出12个字节
这四个数据的存储方式如下所示:
x | y | z | intensity |
---|
所有点云的数据是保存在uint8[] data
中的,所以我们只要每隔32个字节,把其中对应的数据取出,就完成了ROS话题转换pcl话题:
1 | void callback(const sensor_msgs::PointCloud2ConstPtr& cloud) { |
PointXYZI转PointCloud2
和上面类似,直接调用pcl自带的函数函数pcl::toROSMsg(const pcl::PointCloud<T>& pcl_cloud, sensor_msgs::PointCloud2& cloud)
。
使用举例:
1 | void callback(const const pcl::PointCloud<pcl::PointXYZI>::ConstPtr pcl_cloud) { |
同样,也可以直接调用pcl::toPCLPointCloud2
来实现
1 | void fromPCL(const pcl::PointCloud<pcl::PointXYZI>& pcl_cloud, |