接上一篇,Node::StartTrajectoryWithDefaultTopics
实际就一个Node::AddTrajectory
,它的流程图:
1 | const std::set<cartographer::mapping::TrajectoryBuilderInterface::SensorId> |
Node::ComputeExpectedSensorIds
函数返回的是std::set<TrajectoryBuilderInterface::SensorId> expected_topics;
类型:1
2
3
4
5
6
7
8
9
10
11
12
13struct SensorId
{
enum class SensorType {
RANGE = 0,
IMU,
ODOMETRY,
FIXED_FRAME_POSE,
LANDMARK,
LOCAL_SLAM_RESULT
};
SensorType type;
std::string id;
}
它记录的是所有传感器的类型和对应id,id是一个字符串,记录了传感器所对应的ROS主题名称。 如果有多个雷达,id就是scan1
,scan2
……
至于为什么这样命名,由下面的函数规定,字符串在cartographer_ros\node_constants.h
中赋值1
2
3
4
5
6
7
8
9
10
11cartographer_ros_msgs::SensorTopics DefaultSensorTopics() {
cartographer_ros_msgs::SensorTopics topics;
topics.laser_scan_topic = kLaserScanTopic;
topics.multi_echo_laser_scan_topic = kMultiEchoLaserScanTopic;
topics.point_cloud2_topic = kPointCloud2Topic;
topics.imu_topic = kImuTopic;
topics.odometry_topic = kOdometryTopic;
topics.nav_sat_fix_topic = kNavSatFixTopic;
topics.landmark_topic = kLandmarkTopic;
return topics;
}
map_builder_bridge_.AddTrajectory
两个参数是const std::set<TrajectoryBuilderInterface::SensorId>& expected_sensor_ids
和 const TrajectoryOptions& trajectory_options
expected_sensor_ids
记录了用于建图的所有传感器名称和类型
MapBuilderBridge
的构造函数就是加载配置和map_builder1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// 添加一个轨迹跟踪器,同时将构建成功的索引返回
const int trajectory_id = map_builder_->AddTrajectoryBuilder(
expected_sensor_ids, trajectory_options.trajectory_builder_options,
// 这个参数相当于注册了一个回调函数 OnLocalSlamResult , 用于响应map_builder_完成
// 一个局部SLAM 或者 说是成功构建了一个子图的事件
::std::bind(&MapBuilderBridge::OnLocalSlamResult, this,
::std::placeholders::_1, ::std::placeholders::_2,
::std::placeholders::_3, ::std::placeholders::_4,
::std::placeholders::_5) );
LOG(INFO) << "Added trajectory with ID '" << trajectory_id << "'.";
// std::unordered_map<int, std::unique_ptr<SensorBridge>> sensor_bridges_;
// 检查 trajectory_id, 确保之前没有使用过
CHECK_EQ(sensor_bridges_.count(trajectory_id), 0);
// SensorBridge将ROS的消息转换成 Cartographer 中的传感器数据类型
// 前面都是参数,最后的参数是Cartographer的一个核心对象,TrajectoryBuilderInterface指针
// 通过sensor_bridge对象转换后的数据都是通过它喂给Cartographer的
sensor_bridges_[trajectory_id] =
cartographer::common::make_unique<SensorBridge>(
trajectory_options.num_subdivisions_per_laser_scan,
trajectory_options.tracking_frame,
node_options_.lookup_transform_timeout_sec, tf_buffer_,
// 实际类型是 CollatedTrajectoryBuilder
map_builder_->GetTrajectoryBuilder(trajectory_id) );
// 轨迹相关的配置保存到容器对象trajectory_options_中并检查后,返回刚刚生成的索引trajectory_id
auto emplace_result = trajectory_options_.emplace(trajectory_id, trajectory_options);
CHECK(emplace_result.second == true);
return trajectory_id;MapBuilder::AddTrajectoryBuilder
和 GetTrajectoryBuilder
到后面继续看
回调函数 OnLocalSlamResult
1 | void MapBuilderBridge::OnLocalSlamResult( |
最后赋值给了trajectory_state_data_
,唯一应用的地方在MapBuilderBridge::GetTrajectoryStates()
可以对此函数做修改,添加日志等功能
Node::AddExtrapolator
1 | constexpr double kExtrapolationEstimationTimeSec = 0.001; // 1 ms |
添加用于位姿插值的对象extrapolators_
, 类型std::map<int, PoseExtrapolator> extrapolators_;
Node::AddSensorSamplers
1 | sensor_samplers_.emplace( |
类型std::unordered_map<int, TrajectorySensorSamplers> sensor_samplers_;
对cartographer中的采样器(fixed_ratio_sampler.cc
)的封装,用一个计数器来按照一个指定的频率对原始的数据进行降采样,采样频率可以通过轨迹参数文件来配置
LaunchSubscribers
完成传感器消息的订阅,在传感器数据的作用下驱动系统运转,进而完成位姿估计和建图的任务。详细分析见第四篇