代码结构
main函数:
ImageProjection IP;
在初始化ros后,只实例化出一个ImageProjection对象:IP,没进行其他相关操作,因此调用该类的构造函数
构造函数
ImageProjection():
nh("~"){
//nh("~") 意味着创建一个私有的节点句柄。
//使用 "~" 作为参数意味着这个节点句柄将只访问与当前节点相关的参数,而不是全局参数。这对于避免命名冲突和管理参数非常有用。
subLaserCloud = nh.subscribe<sensor_msgs::PointCloud2>(pointCloudTopic, 1, &ImageProjection::cloudHandler, this);
extern const string pointCloudTopic = "/velodyne_points";
subLaserCloud订阅话题pointCloudTopic,当该话题中来新消息时会触发回调函数
&ImageProjection::cloudHandler,1 表示缓冲队列长度为 1,最新消息会覆盖旧消息。
回调函数cloudHandler
主要负责对话题中的点云数据消息进行处理。
void cloudHandler(const sensor_msgs::PointCloud2ConstPtr& laserCloudMsg){
// 1. Convert ros message to pcl point cloud
copyPointCloud(laserCloudMsg);
//将 laserCloudMsg 中的点云数据复制到另一个数据结构中,以便后续的处理或分析
//将来自传感器的原始点云数据转换成自己定义的点云格式,或者进行必要的预处理
// 2. Start and end angle of a scan// 2. 找到开始时刻和结束时刻的方向角度
findStartEndAngle();
// 3. Range image projection
projectPointCloud();
// 4. Mark ground points
groundRemoval();
// 5. Point cloud segmentation
cloudSegmentation();
// 6. Publish all clouds
publishCloud();
// 7. Reset parameters for next iteration
resetParameters();
}
copyPointCloud(laserCloudMsg);函数
功能:
1.复制消息头到cloudHeader,包括输入消息的头部信息(如时间戳、帧 ID);
2.将换 PointCloud2 消息到 PCL 格式,将ROS 消息格式的点云转换为 PCL 格式,并存储在 laserCloudIn 中;
3.移除laserCloudIn中的无效点(NaN 点) pcl::removeNaNFromPointCloud(*laserCloudIn, *laserCloudIn, indices);
removeNaNFromPointCloud函数:
如果输入点云是稠密点云,则直接复制到cloud_out中,即如果laserCloudIn为稠密点云,则不进行什么操作;
cpp if (cloud_in.is_dense) { // Simply copy the data cloud_out = cloud_in; for (std::size_t j = 0; j < cloud_out.points.size (); ++j) index[j] = static_cast<int>(j); }
如果laserCloudIn是稀疏点云,则进行下面操作:
if (!std::isfinite (cloud_in.points[i].x) ||
!std::isfinite (cloud_in.points[i].y) ||
!std::isfinite (cloud_in.points[i].z))
continue;
cloud_out.points[j] = cloud_in.points[i];
index[j] = static_cast<int>(i);
j++;
判断cloud_out的点是否为有效点,若某点的坐标x,y,z不是有限值,即为无效点,则continue,判断下个点,若该点是有效值,则将该点添加到cloud_out中,即对laserCloudIn点云数据进行剔除无效点,并将有效点在原点云中的索引记录到indices中;
cloud_out.height = 1;
cloud_out.width = static_cast<std::uint32_t>(j);
// Removing bad points => dense (note: 'dense' doesn't mean 'organized')
cloud_out.is_dense = true;
最终剔除无效点的点云laserCloudIn设置为单行点云,稠密;
4.检查点云是否包含“环号”信息,if (useCloudRing == true){
如果有ring信息,则将原始点云信息转化为PCL格式,存储在 laserCloudInRing 中,如果laserCloudInRing点云不是稠密的,则输出错误信息并关闭 ROS 节点(ros::shutdown())。
**useCloudRing**标志,为bool类型,
```cpp
extern const bool useCloudRing = true; // if true, ang_res_y and ang_bottom are not used
```
解释:
注释说明了 useCloudRing 的作用:
如果 useCloudRing 为 true,则程序不需要依赖 ang_res_y(垂直角分辨率)和 ang_bottom(激光束底部角度)来计算激光雷达的点云分布。
这是因为环号信息可以直接提供每个点所属激光束的信息,替代这些计算。
激光雷达环号(**ring**)信息:某些激光雷达(如 Velodyne 系列)会为每个点云点提供“环号”(ring),表示该点由哪一根激光束生成。
findStartEndAngle() 函数
1. 上一个函数处理后,剔除无效点的laserCloudIn点云数据,对其第一个点,计算初始点的扫描方向角(orientation),存储到segMsg.startOrientation中;
segMsg.startOrientation = -atan2(laserCloudIn->points[0].y, laserCloudIn->points[0].x);
负号为约定逆时针为负,顺时针为正。
2. 计算最后一个点的方向角,存储到segMsg.endOrientation中;
segMsg.endOrientation = -atan2(laserCloudIn->points[laserCloudIn->points.size() - 1].y,