代码详解
参考链接
Loam_livox 代码阅读与总结
livox_loam 第一部分详解
整体架构
解析Livox点云存储数据结构
从数据流视角解析 loam_livox
laser_feature_extractor
cpp文件
先看.cpp
文件,注册了节点scanRegistration
,创建了一个Laser_feature
对象
int main(int argc, char **argv)
{
ros::init(argc, argv, "scanRegistration"); // 注册scanRegistration节点
Laser_feature laser_feature;
ros::spin();
return 0;
}
hpp文件
定义了
Laser_feature
类
函数名称 | 功能 |
---|---|
get_ros_parameter | 从参数服务器获取对应参数 |
init_ros_env | 初始化ROS环境 |
~Laser_feature | 析构函数 |
Laser_feature | 构造函数,调用了init_ros_env 函数 |
removeClosedPointCloud | 移除点云中距离原点太近的点 |
laserCloudHandler | 回调函数,处理激光点云数据 |
init_livox_lidar_para | Init livox lidar parameters |
extract_laser_features | 从激光点云数据中提取特征 |
split_laser_scan | 将激光扫描分成多个点云 |
init_ros_env
构造函数
Laser_feature
中调用了init_ros_env
,嫌函数太长吗》//
-
先创建节点句柄,记录下此刻的时间
-
调用
init_livox_lidar_para
函数,初始化雷达类型m_lidar_type
,m_livox.m_livox_min_allow_dis
和m_livox_min_sigma
若有参数就设置并输出,这俩是干啥的暂时不知道 -
调用了
get_ros_parameter
,从参数服务器获取一大堆参数,这个类中有以下成员Livox_laser m_livox;
- 设置
m_livox
角点曲率阈值, 表面曲率阈值,最小视角,用于控制点云中点的视角范围
- 设置
-
初始化log日志,
m_map_pointcloud_full_vec_vec
,surface_vec_vec
,corner_vec_vec
,这三个是vector里是vector,支持多个雷达m_maximum_input_lidar_pointcloud
,但是我只有一个 -
遍历雷达数量,设置不同话题名称
laser_points_i
- 创建话题订阅者,订阅
laser_points_i
,存入m_sub_input_laser_cloud_vec
,回调函数为laserCloudHandler
- 初始化每一个雷达对应的那三个点云容器,
m_piecewise_number
为分段个数
- 创建话题订阅者,订阅
问题
- livox的点云到底是怎么扫描的,他与机械式雷达不一样啊,
m_piecewise_number
是啥
laserCloudHandler
回调函数,处理激光点云数据
/** * 激光雷达点云数据处理函数 * * @param laserCloudMsg 激光雷达点云数据消息 * @param topic_name 订阅的主题名称 */
数据转换成pcl点云。
- 先锁住
lock(m_mutex_lock_handler)
- 获取当前雷达id
current_lidar_index
laserCloudScans
是每个scan
的点云- 积累特定帧数据再处理,
m_para_system_delay
为帧数,超过后m_para_systemInited
为true
,实现一个降频的效果 pcl::fromROSMsg(*laserCloudMsg, laserCloudIn);
// 将ros消息转换成pcl点云
如果是 Livox雷达 ,计算特征并发布
-
m_livox.extract_laser_features
函数提取livox激光点云的边界和平面特征,并移除坏点- 这个函数的返回值
std::vector<pcl::PointCloud<pcl::PointXYZI>>
,即所有的scan
- 这个函数的返回值
-
laserCloudScans
花瓣数小于5,直接return。太少了!如果数量足够,那就初始化scanStartInd
和scanEndInd
为花瓣个数,并填充为0 -
调试模式?是啥,若开启,就进行后续操作,默认开启
-
m_if_motion_deblur
若为1,就会将一个scan作为一整段处理,不分段。 -
将一个scan分段,例如分三段,每次遍历,一次scan,半片花瓣,起始和结束位置
start_scans
和end_scans
,再m_livox.find_pt_info
找对应点的指针,这个指针类型是Pt_infos
,具体见livox_feature_extractor.hpp
定义 -
遍历分段次数
-
m_livox.get_features
获取角点,平面点,和两者之和所有点,将以下赋值m_map_pointcloud_corner_vec_vec[current_lidar_index][i] = *livox_corners; m_map_pointcloud_surface_vec_vec[current_lidar_index][i] = *livox_surface; m_map_pointcloud_full_vec_vec[current_lidar_index][i] = *livox_full;
-
-
遍历分段次数,对每段处理,发布特征点
-
遍历雷达设备个数,将所有雷达的特征点整合到一起;else只输入当前雷达的特征点。
*livox_full += m_map_pointcloud_full_vec_vec[ii][i]; *livox_surface += m_map_pointcloud_surface_vec_vec[ii][i]; *livox_corners += m_map_pointcloud_corner_vec_vec[ii][i];
-
用
pcl::toROSMsg
转换消息格式,设置时间戳和frame_id,发布消息。但是为什么要对平面点和角点进行体素网格降采样?定义了两个这个类型的变量pcl::VoxelGrid<PointType>
- 发布的话题名称为
/pc2_corners
,/pc2_surface
,/pc2_full
- 发布的话题名称为
-
如果是Velodyne
雷达, 其参考A-LOAM
,计算特征并发布。暂时不总结
话说转化成pcl点云,他数据格式是什么样的呢?
init_livox_lidar_para
livox_feature_extractor
定义了
Livox_laser
类
结构体E_point_type |
点的各种类型 |
---|---|
结构体Pt_infos |
LiDAR点云中每个点的信息 |
构造函数Livox_laser | 计算了m_max_edge_polar_pos |
eval_point | 估计强度,在投影平面上,距中心点越远的点强度小的可能性越大 |
compute_features | 计算曲率,根据曲率分为平面点和角点 |
projection_scan_3d_2d | |
extract_laser_features |
提取Livox特征点,返回值类型std::vector<pcl::PointCloud<pcl::PointXYZI>> |
eval_point
- 深度值特别小的点,e_pt_too_near
- 求解
pt_info->sigma
,小于阈值,e_pt_reflectivity_low,低反射率。见论文公式3
compute_features
-
一些变量的定义,其中
critical_rm_point
用了按位或操作,空或无穷 -
neighbor_accumulate_xyz
该点左右两个点的坐标累加,后面减去了四个该点坐标 -
计算曲率
m_pts_info_vec[idx].curvature
-
计算平面角,就是
vec_a
和vec_b
的夹角。更新m_pts_info_vec[idx].view_angle
,论文的公式4 -
大于10度的才进行标记
-
曲率小于0.01, 认为时平面点
-
曲率大于0.05,且判断与周围点深度的比较,且
if (m_pts_info_vec[idx].depth_sq2 <= m_pts_info_vec[idx - curvature_ssd_size].depth_sq2 && m_pts_info_vec[idx].depth_sq2 <= m_pts_info_vec[idx + curvature_ssd_size].depth_sq2) { if (abs(m_pts_info_vec[idx].depth_sq2 - m_pts_info_vec[idx - curvature_ssd_size].depth_sq2) < sq2_diff * m_pts_info_vec[idx].depth_sq2 || abs(m_pts_info_vec[idx].depth_sq2 - m_pts_info_vec[idx + curvature_ssd_size].depth_sq2) < sq2_diff * m_pts_info_vec[idx].depth_sq2) m_pts_info_vec[idx].pt_label |= e_label_corner; }
-
projection_scan_3d_2d
-
初始化各种数据结构和变量。
-
遍历输入点云中的每个点,执行以下操作:
m_map_pt_idx
,更新pt_info。结构体Pt_infos
- 点值为inf,
add_mask_of_point(pt_info, e_pt_nan);
,直接跳过 - 根据论文,x方向在雷达正前方,为0表示距离太近
- 若idx为0,更新
pt_info
的pt_2d_img
和polar_dis_sq2
,类型标记为e_pt_000
- idx不为0,更新为
idx - 1
的信息?,类型同样标记为e_pt_000
,跳出,后续不处理
- 若idx为0,更新
- 计算点的深度
depth_sq2
,pt_2d_img
,极坐标距离polar_dis_sq2
。 - 评估点的坐标
eval_point(pt_info);
- 检查点是否在扫描边缘,将周围的几个点设置为
e_pt_circle_edge
。 - 分割scans
- 当前点与前一个点的距离
dis_incre
,判断是大于0,还是小于0,即远离还是靠近花瓣中心 - 判断相邻的两个点的
dis_incre
检测花瓣上的转折点,若一个1一个-1,代表是转折点,记录,并continue
- 当前点与前一个点的距离
-
记录本次调用中点的数量。
split_idx.push_back(pts_size - 1);
-
split_idx
如果小于6就返回0 -
遍历所有点,
-
val_index < split_idx.size() - 2
-
在一个花瓣内
if (idx == 0 || idx > split_idx[val_index + 1])
- idx超过下一个花瓣起始,
val_index++
; - 计算花瓣点数量
internal_size
- 根据下一个花瓣的起始点的极距,判断是用倒数0.2还是0.8计算
scan_angle
- idx超过下一个花瓣起始,
-
-
这两句不知道什么意思
m_pts_info_vec[idx].polar_angle = scan_angle; // keep in mind the above, the if conditions skip points, therefore most scan_angle will be 0 scan_id_index[idx] = scan_angle; // only scan_angle of the starting point of a petal recorded
-
-
返回花瓣数量。
-
在遍历所有点之后,计算每个花瓣的扫描角度,并将其分配给相应的点。
-
返回扫描中的花瓣数量。
- 一帧点云并不是同时获得的,由于时间戳是扫描到第一个点的时刻,因此所有点的时间戳会有细微不同,按下标分配时间戳,一帧10ms,10000个点,每个点的时间为10μs。因此
m_time_internal_pts=10e-5s
.- 计算点的深度
depth_sq2
,pt_2d_img
,极坐标距离polar_dis_sq2
。这里不知道在干啥
split_laser_scan
- 遍历点云
- 分每个花瓣,分别存入
laserCloudScans[scan_idx]
点和pts_mask
mask - 遍历
laserCloudScans
,- 遍历每个点
laserCloudScans[i].size()
- 按位与操作
pts_mask[i][idx] & remove_point_pt_type
set_intensity
laser_clour_per_scan.points.push_back(temp_pt);
- 按位与操作
- 遍历每个点
- 分每个花瓣,分别存入
- 返回的就是删除000、太近、nan类型的点,然后剩下的点。
不理解,为什么设置
intensity
extract_laser_features
-
m_current_time
当前时间- 若当前时间比上次最大时间小,则认为两次同一个时间,则赋值
- 否则,认为一个新的数据
-
m_if_save_pcd_file
,默认不保存原始点云数据 -
返回一个scan的花瓣数,并将一个scan的点云分成多个花瓣点云集合
int clutter_size = projection_scan_3d_2d(laserCloudIn, scan_id_index);
-
调用
compute_features()
-
若花瓣簇为0,则直接返回
-
将点云分为不同的颜色,为rviz显示
split_laser_scan()