cartographer全局重定位的实现

cartographer全局重定位的实现

0.使用说明

由于cartographerr_ros中提供了relocation的服务,因此可以通过以下命令实现重定位功能;

客户端:

ros2 topic pub /beefast/navigation std_msgs/msg/String data:\ \'relocation\'\  --once

RelocationService.srv 数据结构定义:

float32 min_score
---
geometry_msgs/Pose new_pose

1.简介

本博客主要介绍cartographer全局重定位的实现,目前已经测试能够实现在6米180度范围内的全局重定位,原则上应该支持100平空间的全局重定位。

核心流程

这个重定位函数的流程,然后逐个部分给出实现代码。

1.创建线程池;
2.点云过一次体素滤波;
3.为每一个 Submap 创建 FastCorrelativeScanMatcher 匹配器,在线程池中执行,并等待所有匹配器创建完成;
4.遍历每一个匹配器,调用 MatchFullSubmap 方法,记录匹配结果和分数,在线程池中执行,并等待所有匹配完成;
5.找出匹配得分最高的位姿,创建 CeresScanMatcher2D 匹配器再次匹配得到准确的位姿。

实现代码如下:

bool PoseGraph2D::PerformGlobalLocalization(cartographer::sensor::PointCloud laser_point_cloud,float cutoff, transform::Rigid2d* best_pose_estimate, float* best_score)
{
    auto my_thread_pool = std::make_unique<common::ThreadPool>(std::thread::hardware_concurrency());
    assert(my_thread_pool != nullptr);
    LOG(INFO) << "laser_point_cloud.points_.size()1 : " << laser_point_cloud.points().size();
    const cartographer::sensor::PointCloud filtered_point_cloud = cartographer::sensor::VoxelFilter(laser_point_cloud, 0.05);  
    // const cartographer::sensor::PointCloud filtered_point_cloud = match_point_cloud_;
    if (filtered_point_cloud.empty()) {
        LOG(ERROR) << "Filtered point cloud is empty!";
        return false;
    }
    LOG(INFO) << "filtered_point_cloud.points_.size() : " << filtered_point_cloud.points().size();
    LOG(INFO) << "cutoff : " << cutoff;
    int32_t submap_size = static_cast<int>(data_.submap_data.size());
    absl::BlockingCounter created_counter{submap_size};
    std::vector<std::shared_ptr<scan_matching::FastCorrelativeScanMatcher2D>> matchers(submap_size);
    std::vector<const cartographer::mapping::Grid2D*> submaps(submap_size);
    LOG(INFO) << "Submap size: " << submap_size;
    size_t index = 0;
    for (const auto& submap_id_data : data_.submap_data) 
    {
        if (submap_id_data.id.trajectory_id != 0) {
            created_counter.DecrementCount();
            continue;
        }
        auto task = absl::make_unique<common::Task>();
        task->SetWorkItem([this, &matchers, &created_counter, index, submap_id = submap_id_data.id, &submaps] {
            try {
                const auto& submap_data = data_.submap_data.at(submap_id);
                if (!submap_data.submap) {
                    LOG(ERROR) << "Submap is null for index " << index;
                    throw std::runtime_error("Submap is null");
                }
                submaps[index] = static_cast<const Submap2D*>(submap_data.submap.get())->grid();
                matchers[index] = std::make_unique<scan_matching::FastCorrelativeScanMatcher2D>(
                    *submaps[index],
                    options_.constraint_builder_options().fast_correlative_scan_matcher_options());
                LOG(INFO) << "Task completed for index: " << index;
            } catch (const std::exception& e) {
                LOG(ERROR) << "Error in task for index " << index << ": " << e.what();
            }
            created_counter.DecrementCount();
        });

        my_thread_pool->Schedule(std::move(task));
        index++;
    }

    LOG(INFO) << "Total submaps processed: " << index;
    created_counter.Wait();
    LOG(INFO) << "PoseGraph2D::PerformGlobalLocalization 728.";
    size_t matcher_size = index;
    std::vector<float> score_set(matcher_size, -std::numeric_limits<float>::infinity());
    std::vector<transform::Rigid2d> pose_set(matcher_size);    
    absl::BlockingCounter matched_counter{matcher_size};
    std::atomic_bool has_matched{false};    
    for (size_t i = 0; i < matcher_size; i++) 
    {
        auto task = absl::make_unique<common::Task>();
        task->SetWorkItem([i, &filtered_point_cloud, &matchers, &score_set, &pose_set, cutoff, &matched_counter, &has_matched] {
            if (!matchers[i]) 
            {
                LOG(ERROR) << "Matcher is null at index " << i;
                matched_counter.DecrementCount();
                return;
            }
            float score = -1;
            transform::Rigid2d pose_estimate = transform::Rigid2d::Identity();
            LOG(INFO) << "Processing2 matcher index: " << i;
            try {
                if (matchers[i]->MatchFullSubmap(filtered_point_cloud, cutoff, &score, &pose_estimate)) 
                {
                    score_set[i] = score;
                    pose_set[i] = pose_estimate;
                    has_matched = true;
                } else {
                  LOG(INFO) << "match failed. ";
                }
            } catch (const std::exception& e) {
                LOG(ERROR) << "Exception in MatchFullSubmap at index " << i << ": " << e.what();
            }

            matched_counter.DecrementCount();
        });
        my_thread_pool->Schedule(std::move(task));
    }

    matched_counter.Wait();

    if (!has_matched) 
    {
        LOG(ERROR) << "No matches found!";
        return false;
    }


    int max_position = std::distance(score_set.begin(), std::max_element(score_set.begin(), score_set.end()));
    *best_score = score_set[max_position];
    *best_pose_estimate = pose_set[max_position];

    auto csm = std::make_unique<scan_matching::CeresScanMatcher2D>(
        options_.constraint_builder_options().ceres_scan_matcher_options());

    ceres::Solver::Summary unused_summary;

    try {
        csm->Match(best_pose_estimate->translation(), *best_pose_estimate,
                   filtered_point_cloud, *submaps[max_position],
                   best_pose_estimate, &unused_summary);
    } catch (const std::exception& e) {
        LOG(ERROR) << "CeresScanMatcher2D failed: " << e.what();
        return false;
    }

    LOG(INFO) << "PoseGraph2D::PerformGlobalLocalization end.";
    return true;
}

链接代码:

https://github.com/zkk123456/cartographer_global_relocation.git

### Cartographer SLAM 定位功能介绍 #### 1. Cartographer 的核心特性 Cartographer 是由 Google 开发的一个开源实时室内 SLAM (Simultaneous Localization and Mapping) 工具,主要用于机器人导航中的建图与定位任务。它采用了基于 Ceres Solver 的非线性优化方法来处理传感器数据并生成高精度的地图[^1]。这种优化方式使得 Cartographer 能够有效减少累积误差,并提高地图的质量。 #### 2. 子图机制的应用 Cartographer 利用了 **子图(Submap)** 的概念,在局部范围内构建多个小型地图片段,随后通过全局优化将这些子图拼接成完整的地图。这种方法不仅提高了计算效率,还能够显著降低动态障碍物对建图过程的影响。因此,Cartographer 特别适合应用于复杂的室内外环境。 #### 3. 多传感器融合能力 Cartographer 支持多种类型的传感器输入,包括里程计(Odometry)、惯性测量单元(IMU)以及激光扫描仪(LaserScan)。这使其能够在不同硬件配置下灵活部署,无论是低成本的小型设备还是高性能的工业级平台都能适用。此外,Cartographer 同时支持 2D 和 3D 地图创建,进一步扩展了其应用场景范围。 #### 4. ORB-SLAM 辅助下的重定位改进 为了提升 Cartographer 在复杂场景中的表现,研究者提出了结合视觉 SLAM 技术(如 ORB-SLAM)的方式来进行全局重定位。具体而言,当 Cartographer 面临大量相似帧被剔除的情况时,可能会导致特征点不足的问题,而引入 ORB-SLAM 可以为系统提供更多可靠的匹配信息,从而改善整体性能[^2]。 #### 5. 实验验证与实际效果 一项针对大型地下停车场(约五万平方米面积)的研究表明,经过特定参数调整后的 Cartographer 算法可以将重定位时间缩短到仅需 3.35 秒左右[^3]。这一成果证明了该技术在真实世界大规模环境中具备良好的实用价值。 #### 6. 寻找初始位姿的重要性 无论是在已知地图上的重新定位还是未知区域探索期间遇到闭环检测挑战时,“寻找初始位姿”都是一个关键环节。如果缺乏准确的位置估计,则难以利用 ceres_scan_matching 方法完成精确定位需求[^4]。因此,如何快速可靠地获取当前位置成为整个流程中不可或缺的一部分。 ```python import cartographer_ros.msg as crm def initialize_pose(map_data, sensor_readings): """ 初始化姿态函数示例 参数: map_data: 当前可用地图数据 sensor_readings: 最新传感器读数 返回值: estimated_position: 计算得出的最佳位置猜测 """ pass # 此处省略具体实现逻辑 ``` ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值