Lego_Loam包括了Image projection、Feature association、MapOptmization、Transform Fusion四个部分,下面博主将按照算法的逻辑顺序对代码中的重要函数进行讲解。本节是解析Feature association文件。
该专栏的其他章节链接如下
https://blog.csdn.net/HUASHUDEYANJING/article/details/130332367
一、整体输入输出
LEGO-LOAM的第二个文件是Feature association,主要功能是对分割后的点云进行特征点提取和匹配操作,估算出位姿,本节将介绍整体功能和怎么从杂乱的点云中提取特征。该文件的整体框架如下:
1.1 输入如下
// 订阅了分割之后的点云
subLaserCloud=nh.subscribe("/segmented_cloud", 1, &FeatureAssociation::laserCloudHandler, this);
// 订阅的分割点云含有图像的深度信息
subLaserCloudInfo=nh.subscribe("/segmented_cloud_info", 1, &FeatureAssociation::laserCloudInfoHandler, this);
// 非聚类的点
subOutlierCloud=nh.subscribe("/outlier_cloud", 1, &FeatureAssociation::outlierCloudHandler, this);
// IMU传感器的消息
subImu=nh.subscribe(imuTopic, 50, &FeatureAssociation::imuHandler, this);
1.2 功能如下
首先对无序点云进行特征提取,之后利用特征点进行匹配计算出位姿
1. Feature Extraction特征提取
// 1.1 点云运动补偿
adjustDistortion();
// 1.2 计算平滑度
calculateSmoothness();
// 1.3 标记遮挡点
markOccludedPoints();
// 1.4 提取特征
extractFeatures();
// 1.5 发布点云
publishCloud();
2. Feature Association特征匹配
if (!systemInitedLM) {
// 2.1 检查系统初始化,点云投影到结束点
checkSystemInitialization();
return;}
// 2.2 更新初始猜测位置
updateInitialGuess();
// 2.3 特征匹配,输出Transformation
updateTransformation();
// 2.4 融合IMU坐标Transformation
integrateTransformation();
// 2.5 发布雷达里程计
publishOdometry();
// 2.6 发布点云用于建图
publishCloudsLast();
1.3 输出如下
pubCornerPointsSharp = nh.advertise("/laser_cloud_sharp", 1); //当前帧角点
pubCornerPointsLessSharp = nh.advertise("/laser_cloud_less_sharp", 1); //当前帧较缓的角点
pubSurfPointsFlat = nh.advertise("/laser_cloud_flat", 1); //当前帧面点
pubSurfPointsLessFlat = nh.advertise("/laser_cloud_less_flat", 1); //当前帧较缓的面点
pubAdjustPoints= nh.advertise("/adjust_pointcloud", 1); //修正后的分割点云
pubLaserCloudCornerLast = nh.advertise("/laser_cloud_corner_last", 2); //前一帧所有角点
pubLaserCloudSurfLast = nh.advertise("/laser_cloud_surf_last", 2); //前一帧所有面点
pubOutlierCloudLast = nh.advertise("/outlier_cloud_last", 2); //前一帧所有界外点pubLaserOdometry = nh.advertise (“/laser_odom_to_init”, 5); //激光里程计
二、Feature Extraction
提取特征能够避免使用全部点云进行计算和匹配,大大降低了内存资源,是3D激光SLAM能够实时建图的特新思想。所有LOAM系的算法均用角点和面点特征进行匹配。
- 特征识别:角点和面点是环境中的显著特征,它们通常具有高密度的信息,能够提供关于环境结构的重要信息。
- 区分度:角点和面点在图像中通常具有较高的对比度和区分度,因此它们更容易被检测和匹配。
- 减少计算负担:提取角点和面点可以减少SLAM系统需要处理的数据量,因为它们是环境中最重要的特征之一。这样可以降低定位和建图算法的计算负荷,提高系统的实时性能。
- 稳定性:角点和面点通常对于光照变化、视角变化和部分遮挡具有一定的稳定性,这使得它们在不同条件下都能够被可靠地检测和匹配。
特征提取主要包含5个函数如下所示,对点云进行一系列的预处理以提取稳健的特征点,下面将详细介绍每个函数的作用// 1 点云运动补偿 adjustDistortion(); // 2 计算平滑度 calculateSmoothness(); // 3 标记遮挡点 markOccludedPoints(); // 4 提取特征 extractFeatures(); // 5 发布点云 publishCloud();
2.1 adjustDistortion
- 作用:对每一帧点云去除运动畸变。由于激光雷达的一帧点云是在不同时刻收集的,需要将所有点云统一到一个坐标系,如下图所示。点云去畸变可以提高点云的精度和一致性,改善姿态估计,使点云与真实场景更匹配,也能够提升建图的质量。在LEGO_LOAM中是通过IMU对点云去畸变,代码如下。
- 输入:分割点云segmentedCloud imu数据
- 输出:修正点云adjustCloud
- 代码
void adjustDistortion() { for (int i = 0; i < cloudSize; i++) { //计算当前点与起始时刻的点云的相对时间,并保存到点的强度信息中: float relTime = (ori - segInfo.startOrientation) / segInfo.orientationDiff; point.intensity = int(segmentedCloud->points[i].intensity) + scanPeriod * relTime; // 速度投影到初始i=0时刻 VeloToStartIMU(); // 将点的坐标变换到当前帧初始i=0时刻 TransformToStartIMU(&point);} }
2.2 calculateSmoothness
- 作用:计算点云平滑度。LOAM采用平滑度的方式提取特征点,选择当前点的连续几个点,计算平滑度公式如下,高于阈值的是角点,低于阈值的是平面点
- 输入:分割点云信息segInfo
- 输出:点云曲率cloudSmoothness
- 代码
void calculateSmoothness() { int cloudSize = segmentedCloud->points.size(); for (int i = 5; i < cloudSize - 5; i++) { //计算相邻10个点的深度差的和 float diffRange = segInfo.segmentedCloudRange[i-5] + segInfo.segmentedCloudRange[i-4] + segInfo.segmentedCloudRange[i-3] + segInfo.segmentedCloudRange[i-2] + segInfo.segmentedCloudRange[i-1] - segInfo.segmentedCloudRange[i] * 10 + segInfo.segmentedCloudRange[i+1] + segInfo.segmentedCloudRange[i+2] + segInfo.segmentedCloudRange[i+3] + segInfo.segmentedCloudRange[i+4] + segInfo.segmentedCloudRange[i+5]; cloudCurvature[i] = diffRange*diffRange; //取平方 // 在markOccludedPoints()函数中对该参数进行重新修改 cloudNeighborPicked[i] = 0; cloudLabel[i] = 0; //1.2.2 保存曲率 cloudSmoothness[i].value = cloudCurvature[i]; cloudSmoothness[i].ind = i;}
2.3 markOccludedPoints
- 作用:标记遮挡点,在SLAM中,去除遮挡点的主要目的是提高地图的准确性和可靠性,以及改善机器人的定位和导航性能。遮挡点是指由于物体遮挡或传感器视野限制而无法观测到的点,可能会引入地图重建和定位的误差。
- 输入:分割点云信息segInfo
- 输出:标记遮挡点cloudNeighborPicked
- 代码
void markOccludedPoints() { int cloudSize = segmentedCloud->points.size(); for (int i = 5; i < cloudSize - 6; ++i){ float depth1 = segInfo.segmentedCloudRange[i]; float depth2 = segInfo.segmentedCloudRange[i+1]; int columnDiff = std::abs(int(segInfo.segmentedCloudColInd[i+1] - segInfo.segmentedCloudColInd[i])); // 标记有遮挡的点 if (columnDiff < 10){ if (depth1 - depth2 > 0.3){} else if (depth2 - depth1 > 0.3){} float diff1 = std::abs(float(segInfo.segmentedCloudRange[i-1] - segInfo.segmentedCloudRange[i])); float diff2 = std::abs(float(segInfo.segmentedCloudRange[i+1] - segInfo.segmentedCloudRange[i])); //标记离散点 if (diff1 > 0.02 * segInfo.segmentedCloudRange[i] && diff2 > 0.02 * segInfo.segmentedCloudRange[i]) cloudNeighborPicked[i] = 1;
2.4 extractFeatures
- 作用:提取出平面点和角点
- 输入:cloudSmoothness平滑度信息 segmentedCloud分割点云 cloudNeighborPicked相邻遮挡点 segInfo分割信息
- 输出://cornerPointsSharp 线特征(不为地面),每个方向上最多2个
//cornerPointsLessSharp 比cornerPointsSharp平滑的线特征(不为地面),每个方向上20个 //surfPointsFlat 面特征(为地面),每个方向上最多4个 //surfPointsLessFlat 面特征(地面或者分割点云中不为线特征的点)
- 代码
VoidextractFeatures() for(intj=0;j=ep) continue; //根据曲率排序 std::sort(cloudSmoothness.begin()+sp,cloudSmoothness.begin()+ep,by_value()); for(intk=ep;k>=sp;k--){//选择角特征 intind=cloudSmoothness[k].ind; if(cloudNeighborPicked[ind]==0&& cloudCurvature[ind]>edgeThreshold&& segInfo.segmentedCloudGroundFlag[ind]==false){} for(intk=sp;k<=ep;k++){//选择面特征 intind=cloudSmoothness[k].ind; if(cloudNeighborPicked[ind]==0&& cloudCurvature[ind]<surfThreshold&& segInfo.segmentedCloudGroundFlag[ind]==true){}
2.5 publishCloud
- 作用:发布特征点
- 输入:
cornerPointsSharp角特征 cornerPointsLessSharp缓角特征 surfPointsFlat面特征 surfPointsLessFlat缓面特征 adjustCloud修正点云
- 输出:
/laser_cloud_sharp /laser_cloud_less_sharp /laser_cloud_flat /laser_cloud_less_flat /adjust_pointcloud
- 代码
经过点云分割后,效果如下,分为最好的角点和面点voidpublishCloud() { sensor_msgs::PointCloud2laserCloudOutMsg; //最佳线特征 if(pubCornerPointsSharp.getNumSubscribers()!=0){ pcl::toROSMsg(*cornerPointsSharp,laserCloudOutMsg); laserCloudOutMsg.header.stamp=cloudHeader.stamp; laserCloudOutMsg.header.frame_id="camera"; pubCornerPointsSharp.publish(laserCloudOutMsg); } //较为平滑线特征 if(pubCornerPointsLessSharp.getNumSubscribers()!=0){ pcl::toROSMsg(*cornerPointsLessSharp,laserCloudOutMsg); laserCloudOutMsg.header.stamp=cloudHeader.stamp; laserCloudOutMsg.header.frame_id="camera"; pubCornerPointsLessSharp.publish(laserCloudOutMsg); } //最佳面特征 if(pubSurfPointsFlat.getNumSubscribers()!=0){ pcl::toROSMsg(*surfPointsFlat,laserCloudOutMsg); laserCloudOutMsg.header.stamp=cloudHeader.stamp; laserCloudOutMsg.header.frame_id="camera"; pubSurfPointsFlat.publish(laserCloudOutMsg); } //剩下的面特征 if(pubSurfPointsLessFlat.getNumSubscribers()!=0){ pcl::toROSMsg(*surfPointsLessFlat,laserCloudOutMsg); laserCloudOutMsg.header.stamp=cloudHeader.stamp; laserCloudOutMsg.header.frame_id="camera"; pubSurfPointsLessFlat.publish(laserCloudOutMsg); } //发布修正运动畸变后的分割点云 if(pubAdjustPoints.getNumSubscribers()!=0){ pcl::toROSMsg(*adjustCloud,laserCloudOutMsg); laserCloudOutMsg.header.stamp=cloudHeader.stamp; laserCloudOutMsg.header.frame_id="camera"; pubAdjustPoints.publish(laserCloudOutMsg); } }
次好的角点和面点
剩下的不被选择的点如下,可以看到特征不明显且杂乱,这些点后续只在建图时用到
下一节将解析如果使用特征点进行匹配
评论(0)
您还未登录,请登录后发表或查看评论