本次閱讀的源碼爲 release-1.0 版本的代碼
https://github.com/googlecartographer/cartographer_ros/tree/release-1.0
https://github.com/googlecartographer/cartographer/tree/release-1.0
也可以下載我上傳的 全套工作空間的代碼,包括 protobuf, cartographer, cartographer_ros, ceres,
https://download.csdn.net/download/tiancailx/11224156,現在上傳資源自己不能選下載需要的積分。。。完全靠系統。
cartographer/mapping/map_builder_interface.h
carto的程序入口爲 map_builder_interface.h 。首先生成一個指向 MapBuilder類的指針,同時,也會進行 一個指向TrajectoryBuilderInterface的指針的vector,TrajectoryBuilderInterface實現了 從數據的到來 是如何形成個有序的隊列的(按照數據接受的時間進行排序)。
- AddTrajectoryBuilder() 方法,這個方法是 整體SLAM 的初始化函數,可以將一個回調函數當做輸入,這個回調函數的作用就是獲取 匹配好的位姿以及傳感器數據 , AddTrajectoryBuilder() 在初始化整體SLAM之後返回一個 軌跡的id,就是一個機器人運行一次SLAM就是一個軌跡,定位的時候就是2個軌跡。 local SLAM 是根據 姿態推測器 和 scan match 獲得當前的 什麼 在 什麼裏 的座標變換。global 是將local SLAM 的結果傳入到pose_graph 中進行優化。
- 之後通過 map_builder_->pose_graph() 進行後端優化 ??? 還不確定pose_graph()是否就是後端優化
- 之後通過 trajectory_builder->AddSensorData() 添加傳感器數據,並調用回調函數進行數據的處理(將已經按時間排好序的數據隊列傳到SLAM)
- 之後通過 map_builder_->FinishTrajectory(trajectory_id) 結束SLAM, 並通過 map_builder_->pose_graph()->RunFinalOptimization() 進行最後一次的不要求實時性的全局優化。
示例如下:
// cartographer/mapping/map_builder_test.cc
TEST_F(MapBuilderTest, GlobalSlam2D) {
SetOptionsEnableGlobalOptimization();
BuildMapBuilder();
int trajectory_id = map_builder_->AddTrajectoryBuilder(
{kRangeSensorId}, trajectory_builder_options_,
GetLocalSlamResultCallback());
TrajectoryBuilderInterface* trajectory_builder =
map_builder_->GetTrajectoryBuilder(trajectory_id);
const auto measurements = test::GenerateFakeRangeMeasurements(
kTravelDistance, kDuration, kTimeStep);
for (const auto& measurement : measurements) {
trajectory_builder->AddSensorData(kRangeSensorId.id, measurement);
}
map_builder_->FinishTrajectory(trajectory_id);
map_builder_->pose_graph()->RunFinalOptimization();
EXPECT_EQ(local_slam_result_poses_.size(), measurements.size());
EXPECT_NEAR(kTravelDistance,
(local_slam_result_poses_.back().translation() -
local_slam_result_poses_.front().translation())
.norm(),
0.1 * kTravelDistance);
EXPECT_GE(map_builder_->pose_graph()->constraints().size(), 50);
const auto trajectory_nodes =
map_builder_->pose_graph()->GetTrajectoryNodes();
EXPECT_GE(trajectory_nodes.SizeOfTrajectoryOrZero(trajectory_id), 20);
const auto submap_data = map_builder_->pose_graph()->GetAllSubmapData();
EXPECT_GE(submap_data.SizeOfTrajectoryOrZero(trajectory_id), 5);
const transform::Rigid3d final_pose =
map_builder_->pose_graph()->GetLocalToGlobalTransform(trajectory_id) *
local_slam_result_poses_.back();
EXPECT_NEAR(kTravelDistance, final_pose.translation().norm(),
0.1 * kTravelDistance);
}
MapBuilder::MapBuilder(const proto::MapBuilderOptions& options)
: options_(options), thread_pool_(options.num_background_threads()) {
CHECK(options.use_trajectory_builder_2d() ^
options.use_trajectory_builder_3d());
if (options.use_trajectory_builder_2d()) {
pose_graph_ = common::make_unique<PoseGraph2D>(
options_.pose_graph_options(),
common::make_unique<optimization::OptimizationProblem2D>(
options_.pose_graph_options().optimization_problem_options()),
&thread_pool_);
}
if (options.use_trajectory_builder_3d()) {
pose_graph_ = common::make_unique<PoseGraph3D>(
options_.pose_graph_options(),
common::make_unique<optimization::OptimizationProblem3D>(
options_.pose_graph_options().optimization_problem_options()),
&thread_pool_);
}
if (options.collate_by_trajectory()) {
sensor_collator_ = common::make_unique<sensor::TrajectoryCollator>();
} else {
sensor_collator_ = common::make_unique<sensor::Collator>();
}
}
初始化pose_grapher_,2d情況就用PoseGraph2D初始化,3d情況就用PoseGraph3D初始化,初始化時又用了OptimizationProblem2D()。sensor_collator有2種選擇:TrajectoryCollator() 和 Collator(). (不知道哪裏配置的)
int MapBuilder::AddTrajectoryBuilder(
const std::set<SensorId>& expected_sensor_ids,
const proto::TrajectoryBuilderOptions& trajectory_options,
LocalSlamResultCallback local_slam_result_callback) {
const int trajectory_id = trajectory_builders_.size();
if (options_.use_trajectory_builder_3d()) {
std::unique_ptr<LocalTrajectoryBuilder3D> local_trajectory_builder;
if (trajectory_options.has_trajectory_builder_3d_options()) {
local_trajectory_builder = common::make_unique<LocalTrajectoryBuilder3D>(
trajectory_options.trajectory_builder_3d_options(),
SelectRangeSensorIds(expected_sensor_ids));
}
DCHECK(dynamic_cast<PoseGraph3D*>(pose_graph_.get()));
trajectory_builders_.push_back(
common::make_unique<CollatedTrajectoryBuilder>(
sensor_collator_.get(), trajectory_id, expected_sensor_ids,
CreateGlobalTrajectoryBuilder3D(
std::move(local_trajectory_builder), trajectory_id,
static_cast<PoseGraph3D*>(pose_graph_.get()),
local_slam_result_callback)));
} else {
std::unique_ptr<LocalTrajectoryBuilder2D> local_trajectory_builder;
if (trajectory_options.has_trajectory_builder_2d_options()) {
local_trajectory_builder = common::make_unique<LocalTrajectoryBuilder2D>(
trajectory_options.trajectory_builder_2d_options(),
SelectRangeSensorIds(expected_sensor_ids));
}
DCHECK(dynamic_cast<PoseGraph2D*>(pose_graph_.get()));
trajectory_builders_.push_back(
common::make_unique<CollatedTrajectoryBuilder>(
sensor_collator_.get(), trajectory_id, expected_sensor_ids,
CreateGlobalTrajectoryBuilder2D(
std::move(local_trajectory_builder), trajectory_id,
static_cast<PoseGraph2D*>(pose_graph_.get()),
local_slam_result_callback)));
if (trajectory_options.has_overlapping_submaps_trimmer_2d()) {
const auto& trimmer_options =
trajectory_options.overlapping_submaps_trimmer_2d();
pose_graph_->AddTrimmer(common::make_unique<OverlappingSubmapsTrimmer2D>(
trimmer_options.fresh_submaps_count(),
trimmer_options.min_covered_area() /
common::Pow2(trajectory_options.trajectory_builder_2d_options()
.submaps_options()
.grid_options_2d()
.resolution()),
trimmer_options.min_added_submaps_count()));
}
}
if (trajectory_options.pure_localization()) {
constexpr int kSubmapsToKeep = 3;
pose_graph_->AddTrimmer(common::make_unique<PureLocalizationTrimmer>(
trajectory_id, kSubmapsToKeep));
}
if (trajectory_options.has_initial_trajectory_pose()) {
const auto& initial_trajectory_pose =
trajectory_options.initial_trajectory_pose();
pose_graph_->SetInitialTrajectoryPose(
trajectory_id, initial_trajectory_pose.to_trajectory_id(),
transform::ToRigid3(initial_trajectory_pose.relative_pose()),
common::FromUniversal(initial_trajectory_pose.timestamp()));
}
proto::TrajectoryBuilderOptionsWithSensorIds options_with_sensor_ids_proto;
for (const auto& sensor_id : expected_sensor_ids) {
*options_with_sensor_ids_proto.add_sensor_id() = ToProto(sensor_id);
}
*options_with_sensor_ids_proto.mutable_trajectory_builder_options() =
trajectory_options;
all_trajectory_builder_options_.push_back(options_with_sensor_ids_proto);
CHECK_EQ(trajectory_builders_.size(), all_trajectory_builder_options_.size());
return trajectory_id;
}
Cartographer SLAM 文章的索引爲:
前端匹配
2 傳感器數據處理過程: https://blog.csdn.net/tiancailx/article/details/96138354
3 scan match: https://blog.csdn.net/tiancailx/article/details/97393967
後端優化
4 約束計算: https://blog.csdn.net/tiancailx/article/details/97759161
5 閉環約束: https://blog.csdn.net/tiancailx/article/details/100739192
6 優化求解: https://blog.csdn.net/tiancailx/article/details/100739634
Cartographer_ros
7 cartographer_ros: https://blog.csdn.net/tiancailx/article/details/92796292