前言
LIO-SAM的全称是:Tightly-coupled Lidar Inertial Odometry via Smoothing and Mapping
从全称上可以看出,该算法是一个紧耦合的雷达惯导里程计(Tightly-coupled Lidar Inertial Odometry),借助的手段就是利用GT-SAM库中的方法。
LIO-SAM 提出了一个利用GT-SAM的紧耦合激光雷达惯导里程计的框架。
其中点云运动畸变矫正的代码在图像投影的节点中
	
可以看到该节点 订阅 3种消息:
原始点云数据
原始imu数据
imu预积分后预测的imu里程计数据其中完成的一个主要功能就是进行畸变矫正。
本篇博客将解读其畸变矫正处理流程部分。
	
畸变矫正
将点云投影到一个矩阵上,并保存每个点的信息,并在内部进行畸变矫正
  void projectPointCloud()  {
    int cloudSize = laserCloudIn->points.size();    for (int i = 0; i < cloudSize; ++i)    {
遍历整个点云
      PointType thisPoint;       thisPoint.x = laserCloudIn->points[i].x;      thisPoint.y = laserCloudIn->points[i].y;      thisPoint.z = laserCloudIn->points[i].z;      thisPoint.intensity = laserCloudIn->points[i].intensity;
取出对应的某个点
float range = pointDistance(thisPoint);
计算这个点距离lidar中心的距离
      if (range < lidarMinRange || range > lidarMaxRange)        continue;
距离太小或者太远都认为是异常点
      int rowIdn = laserCloudIn->points[i].ring;      if (rowIdn < 0 || rowIdn >= N_SCAN)        continue;      if (rowIdn % downsampleRate != 0)        continue;
取出对应的在第几根scan上
	
	scan id 合理判断
	
	如果需要降采样,就根据scan id 适当跳过
      float horizonAngle = atan2(thisPoint.x, thisPoint.y) * 180 / M_PI;       static float ang_res_x = 360.0/float(Horizon_SCAN);      int columnIdn = -round((horizonAngle-90.0)/ang_res_x) + Horizon_SCAN/2;      if (columnIdn >= Horizon_SCAN)        columnIdn -= Horizon_SCAN;      if (columnIdn < 0 || columnIdn >= Horizon_SCAN)        continue;
计算水平角
计算水平分辨率
	
	计算水平线束id ,转换到x负方向为起始,顺时针为正方向,范围[0-H]
	
	对水平角做补偿,因为雷达是顺时针旋转,
	
	对水平id进行检查
      if (rangeMat.at(rowIdn, columnIdn) != FLT_MAX)        continue; 
如果这个位置有填充了就跳过
	
	点云不是完全的360度,可能会多一些
      thisPoint = deskewPoint(&thisPoint, laserCloudIn->points[i].time);
对点做运动补偿
rangeMat.at(rowIdn, columnIdn) = range; 
将这个点的距离数据保存进这个range矩阵种
int index = columnIdn + rowIdn * Horizon_SCAN;
算出点的索引
fullCloud->points[index] = thisPoint;
保存这个点的坐标
之后来看下运动补偿得函数deskewPoint
  PointType deskewPoint(PointType *point, double relTime)  {
    if (deskewFlag == -1 || cloudInfo.imuAvailable == false)      return *point;
判断是否可以进行运动补偿,不能得话则之间返回原点
	
	判断依据:
deskewFlag 是原始点云 没有 time得标签 则为-1
cloudInfo.imuAvailable 的原始imu里面的数据判断
    double pointTime = timeScanCur + relTime;
relTime 是相对时间,加上起始时间就是绝对时间
    float rotXCur, rotYCur, rotZCur;    findRotation(pointTime, &rotXCur, &rotYCur, &rotZCur);
通过findRotation函数 计算当前点 相对起始点的相对旋转
其内部为:
  void findRotation(double pointTime, float *rotXCur, float *rotYCur, float *rotZCur)  {    *rotXCur = 0; *rotYCur = 0; *rotZCur = 0;
先将相对旋转至0
    int imuPointerFront = 0;    while (imuPointerFront < imuPointerCur)    {      if (pointTime < imuTime[imuPointerFront])        break;      ++imuPointerFront;    }
找到距离该点云时间最近的 大于该点云时间的点
    if (pointTime > imuTime[imuPointerFront] || imuPointerFront == 0)    {      *rotXCur = imuRotX[imuPointerFront];      *rotYCur = imuRotY[imuPointerFront];      *rotZCur = imuRotZ[imuPointerFront];    }
如果时间戳不在两个imu的旋转之间,就直接赋值了
    } else {       int imuPointerBack = imuPointerFront - 1;      double ratioFront = (pointTime - imuTime[imuPointerBack]) / (imuTime[imuPointerFront] - imuTime[imuPointerBack]);      double ratioBack = (imuTime[imuPointerFront] - pointTime) / (imuTime[imuPointerFront] - imuTime[imuPointerBack]);      *rotXCur = imuRotX[imuPointerFront] * ratioFront + imuRotX[imuPointerBack] * ratioBack;      *rotYCur = imuRotY[imuPointerFront] * ratioFront + imuRotY[imuPointerBack] * ratioBack;      *rotZCur = imuRotZ[imuPointerFront] * ratioFront + imuRotZ[imuPointerBack] * ratioBack;    }
否则 作一个线性插值,得到相对旋转
	
	算两个权重 进行 插值
    float posXCur, posYCur, posZCur;    findPosition(relTime, &posXCur, &posYCur, &posZCur);
这里没有计算平移补偿 如果运动不快的话
    if (firstPointFlag == true)    {      transStartInverse = (pcl::getTransformation(posXCur, posYCur, posZCur, rotXCur, rotYCur, rotZCur)).inverse();      firstPointFlag = false;    }
计算第一个点的相对位姿
    Eigen::Affine3f transFinal = pcl::getTransformation(posXCur, posYCur, posZCur, rotXCur, rotYCur, rotZCur);    Eigen::Affine3f transBt = transStartInverse * transFinal;
计算当前点和第一点的相对位姿
    newPoint.x = transBt(0,0) * point->x + transBt(0,1) * point->y + transBt(0,2) * point->z + transBt(0,3);    newPoint.y = transBt(1,0) * point->x + transBt(1,1) * point->y + transBt(1,2) * point->z + transBt(1,3);    newPoint.z = transBt(2,0) * point->x + transBt(2,1) * point->y + transBt(2,2) * point->z + transBt(2,3);    newPoint.intensity = point->intensity;    return newPoint;
就是R*p+t ,把点补偿到第一个点对应的时刻的位姿
然后看提取出有效的点的信息 函数cloudExtraction
  void cloudExtraction()  {
    for (int i = 0; i < N_SCAN; ++i)    {
遍历每一根scan
cloudInfo.startRingIndex[i] = count - 1 + 5;
这个scan可以计算曲率的起始点(计算曲率需要左右各五个点)
      for (int j = 0; j < Horizon_SCAN; ++j)      {
遍历该 scan上的每 个点
        if (rangeMat.at(i,j) != FLT_MAX)//FLT_MAX就是最大的浮点数        { 
判断该点 是否 是一个 有效的点
	
	rangeMat的每个点初始化为FLT_MAX ,如果点有效,则会赋值为 range
cloudInfo.pointColInd[count] = j;
点云信息里面 这个点对应着哪一个垂直线
cloudInfo.pointRange[count] = rangeMat.at(i,j); 
点云信息里面 保存它的距离信息
extractedCloud->push_back(fullCloud->points[j + i*Horizon_SCAN]);
他的3d坐标信息
cloudInfo.endRingIndex[i] = count -1 - 5;
这个scan可以计算曲率的终端
在上面处理完后
	
	即可发布点云
  void publishClouds()  {    cloudInfo.header = cloudHeader;    cloudInfo.cloud_deskewed = publishCloud(&pubExtractedCloud, extractedCloud, cloudHeader.stamp, lidarFrame);    pubLaserCloudInfo.publish(cloudInfo);  }
最后将处理后的点云发布出去
result
	
	
	
	
	审核编辑:刘清
- 
                                3D
                                +关注
关注
9文章
2875浏览量
107490 - 
                                SAM
                                +关注
关注
0文章
112浏览量
33519 - 
                                激光雷达
                                +关注
关注
968文章
3971浏览量
189855 
原文标题:LIO-SAM点云预处理前端:畸变矫正
文章出处:【微信号:3D视觉工坊,微信公众号:3D视觉工坊】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
科普:一文了解固态和半固态激光雷达
激光雷达在SLAM算法中的应用综述
    
激光雷达的维护与故障排查技巧
激光雷达威廉希尔官方网站 的基于深度学习的进步
激光雷达威廉希尔官方网站 的发展趋势
LIDAR激光雷达逆向建模能用到revit当中吗
光学雷达和激光雷达的区别是什么
激光雷达点云数据包含哪些信息
亮道智能:发布全新一代激光雷达,未来主攻固态激光雷达低价市场
做一个激光雷达,需要哪些基本部件?
    
华为激光雷达参数怎么设置
禾赛科技推出AT512激光雷达
禾赛科技激光雷达累计交付突破30万台 全球首个创下此里程碑的车载激光雷达公司
    
          
        
        
一个利用GT-SAM的紧耦合激光雷达惯导里程计的框架
 
    
    
           
            
            
                
            
评论