小工具知识网

当前位置: 首页 > 行业知识 > 5.【自动驾驶与机器人中的SLAM技术】2D点云的scan matching算法 和 检测退化场景的思路

行业知识

5.【自动驾驶与机器人中的SLAM技术】2D点云的scan matching算法 和 检测退化场景的思路

2026-11-05 06:06:26 来源:互联网转载

目录

  • 1. 基于优化的点到点/线的配准
  • 2. 对似然场图像进行插值,提高匹配精度
  • 3. 对二维激光点云中会对SLAM功能产生退化场景的检测
  • 4. 在诸如扫地机器人等这样基于2D激光雷达导航的机器人,如何处理悬空/低矮物体
  • 5. 也���迎大家来我的读书号--过千帆,学习交流。

    1. 基于优化的点到点/线的配准

    这里实现了基于g2o优化器的优化方法。

    图优化中涉及两个概念-顶点和边。我们的优化变量认为是顶点,误差项就是边。我们通过g2o声明一个图模型,然后往图模型中添加顶点和与顶点相关联的边,再选定优化算法(比如LM)就可以进行优化了。想熟悉g2o的小伙伴们感兴趣的话,可以到这个链接看一下。

    g2o的基本框架和编程套路如下图:

    基于g2o的点对点的配准算法代码实现如下:

    bool Icp2d::AlignWithG2oP2P(SE2& init_pose){    double rk_delta = 0.8;    // 核函数阈值    float max_dis2 = 0.01;    // 最近邻时的最远距离(平方)    int min_effect_pts = 20;  // 最小有效点数    // 构建图优化,先设定g2o    typedef g2o::BlockSolver BlockSolverType;  // 每个误差项优化变量维度为3, 误差值维度是2    typedef g2o::LinearSolverDense LinearSolverType; // 线性求解器类型    // 梯度下降方法,可以从GN, LM, DogLeg 中选    auto solver = new g2o::OptimizationAlgorithmLevenberg(            g2o::make_unique(g2o::make_unique()));    g2o::SparseOptimizer optimizer;   // 图模型    optimizer.setAlgorithm(solver);   // 设置求解器    optimizer.setVerbose(false);       // 打开调试输出    // 往图中添加顶点    VertexSE2 *v = new VertexSE2(); // 新建SE2位姿顶点    v->setId(0);                    // 设置顶点的id    v->setEstimate(init_pose);      // 设置顶点的估计值为初始位姿    optimizer.addVertex(v);         // 将顶点添加到优化器中    // 往图中添加边    int effective_num = 0;  // 有效点数    // 遍历source    for (size_t i = 0; i ranges.size(); ++i)     {        float range = source_scan_->ranges[i];        if (range range_min || range > source_scan_->range_max)         {            continue;        }        float angle = source_scan_->angle_min + i * source_scan_->angle_increment;        float theta = v->estimate().so2().log();        Vec2d pw = v->estimate() * Vec2d(range * std::cos(angle), range * std::sin(angle));        Point2d pt;        pt.x = pw.x();        pt.y = pw.y();        // 最近邻        std::vector nn_idx;        std::vector dis;        kdtree_.nearestKSearch(pt, 1, nn_idx, dis);        if (nn_idx.size() > 0 && dis[0] points[nn_idx[0]].x, target_cloud_->points[nn_idx[0]].y);   // 当前激光点在目标点云中的最近邻点坐标            auto *edge = new EdgeIcpP2P(range, angle, theta);   // 构建约束边            edge->setVertex(0, v);                   // 设置连接的顶点            edge->setMeasurement(qw);                // 观测,即最近邻            edge->setInformation(Mat2d::Identity()); // 观测为2维点坐标,因此信息矩阵需设为2x2单位矩阵            auto rk = new g2o::RobustKernelHuber;    // Huber鲁棒核函数            rk->setDelta(rk_delta);                  // 设置阈值            edge->setRobustKernel(rk);               // 为边设置鲁棒核函数            optimizer.addEdge(edge);                 // 将约束边添加到优化器中        }    }    if (effective_num estimate();    LOG(INFO)     double rk_delta = 0.8;    // 核函数阈值    float max_dis2 = 0.01;    // 最近邻时的最远距离(平方)    int min_effect_pts = 20;  // 最小有效点数    // 构建图优化,先设定g2o    typedef g2o::BlockSolversetEstimate(init_pose);      // 设置顶点的估计值为初始位姿    optimizer.addVertex(v);         // 将顶点添加到优化器中    // 往图中添加边    int effective_num = 0;  // 有效点数    // 遍历source    for (size_t i = 0; i ranges.size(); ++i)     {        float range = source_scan_->ranges[i];        if (range range_min || range > source_scan_->range_max)         {            continue;        }        float angle = source_scan_->angle_min + i * source_scan_->angle_increment;        float theta = v->estimate().so2().log();        Vec2d pw = v->estimate() * Vec2d(range * std::cos(angle), range * std::sin(angle));        Point2d pt;        pt.x = pw.x();        pt.y = pw.y();        // 查找5个最近邻        std::vector nn_idx;        std::vector dis;        kdtree_.nearestKSearch(pt, 5, nn_idx, dis);        std::vector effective_pts;  // 有效点        for (int j = 0; j points[nn_idx[j]].x, target_cloud_->points[nn_idx[j]].y));            }        }        if (effective_pts.size() setVertex(0, v);                   // 设置连接的顶点            edge->setInformation(Eigen::Matrix::Identity()); // 误差为1维            auto rk = new g2o::RobustKernelHuber;    // Huber鲁棒核函数            rk->setDelta(rk_delta);                  // 设置阈值            edge->setRobustKernel(rk);               // 为边设置鲁棒核函数            optimizer.addEdge(edge);                 // 将约束边添加到优化器中        }    }    if (effective_num estimate();    LOG(INFO)     if (data.size()  

    双线性插值的过程如图:

    对应到程序中:

    a = y y ; a=yy; a=yy;

    b = x x ; b=xx; b=xx;

    v 1 = d a t a [ 0 ] ; v1=data[0]; v1=data[0];

    v 2 = d a t a [ 1 ] ; v2=data[1]; v2=data[1];

    v 3 = d a t a [ i m g . s t e p / s i z e o f ( T ) ] ; v3=data[img.step / sizeof(T)]; v3=data[img.step/sizeof(T)];

    v 4 = d a t a [ i m g . s t e p / s i z e o f ( T ) + 1 ] ; v4=data[img.step / sizeof(T) + 1]; v4=data[img.step/sizeof(T)+1];

    ===================================>可得:

    v 7 = f l o a t ( ( 1 − x x ) ∗ ( 1 − y y ) ∗ d a t a [ 0 ] + x x ∗ ( 1 − y y ) ∗ d a t a [ 1 ] + ( 1 − x x ) ∗ y y ∗ d a t a [ i m g . s t e p / s i z e o f ( T ) ] + x x ∗ y y ∗ d a t a [ i m g . s t e p / s i z e o f ( T ) + 1 ] ) ; v7=float((1 - xx) * (1 - yy) * data[0] + xx * (1 - yy) * data[1] + (1 - xx) * yy * data[img.step / sizeof(T)] + xx * yy * data[img.step / sizeof(T) + 1]); v7=float((1−xx)∗(1−yy)∗data[0]+xx∗(1−yy)∗data[1]+(1−xx)∗yy∗data[img.step/sizeof(T)]+xx∗yy∗data[img.step/sizeof(T)+1]);

    效果展示:

    3. 对二维激光点云中会对SLAM功能产生退化场景的检测

    视频中高博讲到对于2DSLAM来说,可以对点云进行直线拟合,比较场景中直线方向是否“大体一致”。

    具体思路:可以对场景中的点云进行聚类,然后对每个类簇中的点进行直线拟合,保存所有直线的系数。参考提示中的条件判断实现该功能。

    实现代码如下:

    bool Icp2d::IsDegeneration(){    if (target_cloud_->empty())     {        LOG(ERROR) size();    if(point_size range_min || target_scan_->ranges[i] > target_scan_->range_max)         {            continue;        }        double real_angle = target_scan_->angle_min + i * target_scan_->angle_increment;        PointType p;        p.x = target_scan_->ranges[i] * std::cos(real_angle);        p.y = target_scan_->ranges[i] * std::sin(real_angle);        p.z = 1.0;        target_cloud->points.push_back(p);    }    pcl::search::KdTree::Ptr kdtree; // (new pcl::search::KdTree)    kdtree = boost::make_shared();    // 构建kdtree    kdtree->setInputCloud(target_cloud);            pcl::EuclideanClusterExtraction clusterExtractor;    // 创建一个向量来存储聚类的结果    std::vector cluster_indices;    clusterExtractor.setClusterTolerance(0.02);        // 设置聚类的距离阈值    clusterExtractor.setMinClusterSize(10);            // 设置聚类的最小点数    clusterExtractor.setMaxClusterSize(1000);          // 设置聚类的最大点数     clusterExtractor.setSearchMethod(kdtree);         // 使用kdtree树进行加速    clusterExtractor.setInputCloud(target_cloud);             // 设置点云聚类对象的输入点云数据    clusterExtractor.extract(cluster_indices);         // 执行点云聚类    LOG(INFO)         LOG(INFO)             pts.emplace_back(Eigen::Vector2d(pt.x, pt.y));        }                    // 拟合直线,组装J、H和误差        Eigen::Vector3d line_coeffs;        // 利用当前点附近的几个有效近邻点,基于SVD奇异值分解,拟合出ax+by+c=0 中的最小直线系数 a,b,c,对应公式(6.11)        if (math::FitLine2D(pts, line_coeffs))         {            line_coeffs_vector.push_back(line_coeffs);        }        // 为聚类分配不同的颜色        double r = static_cast        if(clusterNumber == 1) return true; // 如果仅有一条直线,就是退化场景        float max_score = 0;        for(int i = 0; i 

上一篇:MAC-OS低版本升级到高版本——亲测有效

下一篇:服务器raid的读写怎么测试?