一、操作
# 先把所有topic 打印出来。
rostopic list
比如:显示如下
/cari_points_back
/cari_points_front
/cari_points_top
/clock
/imu
/rosout
/rosout_agg
找到你的激光雷达的topic。
#然后选择你要查看的topic
rostopic info /cari_points_top
显示如下:
Type: sensor_msgs/PointCloud2
Publishers:
* /play_1719830163167041338 (http://tkpad20:44499/)
Subscribers: None
发现这个topic发布的话题的消息类型是:sensor_msgs/PointCloud2
然后查看这个消息类型的结构。
rosmsg info sensor_msgs/PointCloud2
显示如下:
std_msgs/Header header
uint32 seq
time stamp
string frame_id
uint32 height
uint32 width
sensor_msgs/PointField[] fields
uint8 INT8=1
uint8 UINT8=2
uint8 INT16=3
uint8 UINT16=4
uint8 INT32=5
uint8 UINT32=6
uint8 FLOAT32=7
uint8 FLOAT64=8
string name
uint32 offset
uint8 datatype
uint32 count
bool is_bigendian
uint32 point_step
uint32 row_step
uint8[] data
bool is_dense
二、解析
针对这个类型,进行解析
先整体来看:这个类型是由两部分组成的。
组成:
- 第一部分是header
-
header标头包含时间戳和ROS中常用的坐标帧信息。
-
seq代表消息序列
-
stamp:发布的系统时间:这个值在真实中由两部分组成:secs和nsecs纳秒。
-
frame_id:坐标系
-
uint32 height:data的高度,一帧点云通常height=1,即表示无序点云;
-
uint32 width:data的宽度,即每行对应点的数量;
-
-
- 第二部分是fields。最值得关注的变量
- 每个点的数据类型;包含每个点的字段属性信息,详见下文。
- 首先是一个enum罗列了数据类型。
- 然后罗列了数据定义的格式内容。[sensor_msgs/PointCloud2](sensor_msgs/PointCloud2 Documentation)
- string name # 点的字段名
- uint32 offset # 相对于结构体起始地址的字节数;偏移字节
- uint8 datatype # 该字段所占用的字节数
- uint32 count # 该字段的数量,通常为1
- bool is_bigendian:点云是否按正序排列;大端
- uint32 point_step:每个点占用的比特数,1字节=8比特,与PointField里所有字节数之和相等。
- uint32 row_step:每行占用的比特数,= 点的数量 * Point_step; = width * point_step
- uint8[] data:序列化后的点云二进制数据,所有点信息都在一串字符中,无法通过data[i]取值。
- bool is_dense:是否存在无效点。
三、实例
当echo topic: /radar_all_tracker
时,如果只想接收header时,使用方式如下,
ros topic echo /your_topic_name | grep header -A4
-A4表示接收 header 后四行。
rostopic echo /cari_points_top | grep header -A41
我测试了一下,打印41行,刚好,显示到row_step。如果42,就可以打印一大堆数据了。
实例一:
header:
seq: 17674
stamp:
secs: 1693988715
nsecs: 723284245
frame_id: "cari_lidar_top"
height: 16
width: 1996
fields:
-
name: "x"
offset: 0
datatype: 7
count: 1
-
name: "y"
offset: 4
datatype: 7
count: 1
-
name: "z"
offset: 8
datatype: 7
count: 1
-
name: "intensity"
offset: 12
datatype: 7
count: 1
-
name: "ring"
offset: 16
datatype: 4
count: 1
-
name: "time"
offset: 18
datatype: 7
count: 1
is_bigendian: False
point_step: 22
row_step: 43912
--
备注:row_step 43912= width 1996 * point_step 22;
实例二:
header: // 点云的头信息
seq: 4873
stamp: // 时间戳
secs: 595
nsecs: 698295140
frame_id: "livox_frame" // 坐标系
height: 1 // 如果cloud 是无序的 height 是 1
width: 9984 // 点云的长度
fields: // 该消息以PointCloud2消息格式保存一个点条目的描述。
-
name: "x" // “x”坐标信息
offset: 0 // 从点结构开始的偏移量
datatype: 7 // 枚举数据类型 FLOAT32
count: 1 // 域中有多少个元素
-
name: "y" // “y”坐标信息
offset: 4
datatype: 7 // FLOAT32 占4个字节
count: 1
-
name: "z" // “z”坐标信息
offset: 8
datatype: 7 // FLOAT32 占4个字节
count: 1
-
name: "intensity" // 反射强度坐标信息
offset: 12
datatype: 7 // FLOAT32 占4个字节
count: 1
-
name: "tag" // 回波信息
offset: 16
datatype: 2 // UINT8 占1个字节
count: 1
-
name: "line"
offset: 17
datatype: 2 // UINT8 占1个字节
count: 1
is_bigendian: False // 数据存储方式,包括大端与小端,具体解释见后文参考链接
point_step: 18 // 一个点占的字节数 4 * 3 + intensity 4 + tag 1 + line 1 = 18;
row_step: 179712 // 一行的长度占用的字节数 18 * 9984
data:[省略]
is_dense: True // 没有非法数据点
---
实例三:
fields[0]、fields[1]、fields[2]分别为每个点的x、y、z坐标字段,均为float类型,4个字节。
fields[3]为点云反射强度、float类型,4个字节。
fields[4]为每个点对应的时间戳,double类型,8个字节,64比特,
fields[5]为每个点对应的线ID,值为0~15,2个字节。
四、类型转换
ROS中已经集成了对PCL的API接口,PCL中也集成了用于转换的结构体PCLPointCloud2。因此我们可以直接调用函数。
ROS的PCL转换接口主要在pcl_conversions.h中,里边提供了丰富的类型转换函数。实际上,该函数是基于pcl的功能实现了类型转换,对应pcl的函数文件conversions.h
函数名中带move和不带move的区别是,移动(剪切)和拷贝的区别。可以自行转到定义查看他复制data时的操作。
五、格式转换
rslidar_to_velodyne
https://github.com/EpsAvlc/rslidar_to_velodyne.git
with rings
https://github.com/HViktorTsoi/rs_to_velodyne.git