Cartographer_ros主要实现Topics的订阅与发布。订阅是指从IMU,激光雷达,里程计取数据,然后传给Cartographer库。发布是指从Cartographer拿处理的结果,然后发布给ROS,然后可以在rivz上显示。
1. cartographer_ros/docs/source/ros_api.rst
此文件描述 了Cartographer_ros订阅与发布了哪些topics及服务。
2.运行 cartographer_ros
roslaunch cartographer_ros demo_revo_lds.launch
3.revo_lds.lua
此配置文件,描述了发布哪些Topics及名称。在源码中。订阅的Topic在源码中通过ratio of the pulse(脉冲的比率)的方式来定时调用。发布是通过定时器的方式,定时发布。具体值,可以通过此配置文件配置。
1)定时器方式
wall_timers_.push_back(node_handle_.createWallTimer(
::ros::WallDuration(node_options_.submap_publish_period_sec),
&Node::PublishSubmapList, this));
2)ratio方式
void Node::HandlePointCloud2Message(
const int trajectory_id, const std::string& sensor_id,
const sensor_msgs::PointCloud2::ConstPtr& msg) {
absl::MutexLock lock(&mutex_);
if (!sensor_samplers_.at(trajectory_id).rangefinder_sampler.Pulse()) {
return;
}
map_builder_bridge_.sensor_bridge(trajectory_id)
->HandlePointCloud2Message(sensor_id, msg);
}
4. cartographer_node
Cartographer_ros中的一个重要节点
1.请先查看:cartographer_ros/docs/source/ros_api.rst 文档
====================
Subscribed Topics:
scan (`sensor_msgs/LaserScan`_)
echoes (`sensor_msgs/MultiEchoLaserScan`_)
points2 (`sensor_msgs/PointCloud2`_)
imu (`sensor_msgs/Imu`_)
odom (`nav_msgs/Odometry`_)
Published Topics:
submap_list (`cartographer_ros_msgs/SubmapList`_)
Services:
submap_query (`cartographer_ros_msgs/SubmapQuery`_)
start_trajectory (`cartographer_ros_msgs/StartTrajectory`_)
trajectory_query (`cartographer_ros_msgs/TrajectoryQuery`_)
finish_trajectory (`cartographer_ros_msgs/FinishTrajectory`_)
write_state (`cartographer_ros_msgs/WriteState`_)
get_trajectory_states (`cartographer_ros_msgs/GetTrajectoryStates`_)
read_metrics (`cartographer_ros_msgs/ReadMetrics`_)
====================
2. 运行cartographer_ros
roslaunch cartographer_ros demo_revo_lds.launch
================================
3.revo_lds.lua
demo_revo_lds.launch 指定加载revo_lds.lua 配置文件。指定topic的名称,发布的Topic的时间间隔。
map_frame = "map",
tracking_frame = "scan",
published_frame = "scan",
odom_frame = "odom",
provide_odom_frame = false,
use_odometry = false,
use_nav_sat = false,
use_landmarks = false,
publish_frame_projected_to_2d=false,
num_laser_scans = 1,
num_multi_echo_laser_scans = 0,
num_subdivisions_per_laser_scan = 1,
num_point_clouds = 0,
lookup_transform_timeout_sec = 0.2,
submap_publish_period_sec = 0.3,
pose_publish_period_sec = 5e-3,
trajectory_publish_period_sec = 30e-3,
rangefinder_sampling_ratio = 1.,
odometry_sampling_ratio = 1.,
fixed_frame_pose_sampling_ratio = 1.,
imu_sampling_ratio = 1.,
landmarks_sampling_ratio = 1.,
===========
map_frame:一般为“map”.用来发布submap的ROS帧ID.
tracking_frame:SLAM算法要跟踪的ROS 帧ID.?
published_frame:用来发布pose的帧ID.?
odom_frame:只要当有里程计信息的时候才会使用。即provide_odom_frame=true.
provide_odom_frame:如果为true,the local, non-loop-closed, continuous pose将会在map_frame 里以odom_frame发布?
publish_frame_projected_to_2d:如果为true,则已经发布的pose将会被完全成2D的pose,没有 roll,pitch或者z-offset?
use_odometry:如果为true,需要提供里程计信息,并话题/odom会订阅nav_msgs/Odometry类型的消 息,在SLAM过程中也会使用这个消息进行建图
use_nav_sat:如果为true,会在话题/fix上订阅sensor_msgs/NavSatFix类型的消息,并且在 globalSLAM中会用到
use_landmarks:如果为true,会在话题/landmarks上订阅cartographer_ros_msgs/LandmarkList类型 的消息,并且在SLAM过程中会用到
num_laser_scans:SLAM可以输入的/scan话题数目的最大值
num_muti_echo_laser_scans:SLAM可以输入sensor_msgs/MultiEchoLaserScan话题数目的最大值
num_subdivisions_per_laser_scan:将每个接收到的(multi_echo)激光scan分割成的点云数。 细分 scam可以在扫描仪移动时取消scanner获取的scan。 有一个相应的trajectory builder option可将细分扫描累积到将用于scan_matching的点云中。
num_point_clouds:SLAM可以输入的sensor_msgs/PointCloud2话题数目的最大值
lookup_transform_timeout_sec:使用tf2查找transform的超时时间(秒)。
submap_publish_period_sec:发布submap的时间间隔(秒)
pose_publish_period_sec:发布pose的时间间隔,值为5e-3的时候为200HZ
trajectory_publish_period_sec:发布trajectory markers(trajectory的节点)的时间间隔,值为30e-3 为30ms
rangefinder_samping_ratio:测距仪的固定采样ratio
imu_sampling_ratio:IMU message的固定采样ratio
landmarks_sampling_ratio:landmarks message的固定采样ratio,(单位是啥?)
=====================
4.RVIZ 订阅的topic
1)TF
2)/submap_list
3)/scan_matched_points2
4)/trajectory_node_list
5)/landmark_poses_list
6)/constraint_list
5.App
cartographer_assets_writer
cartographer_dev_trajectory_comparison
cartographer_offline_node
cartographer_rosbag_validate
cartographer_dev_pbstream_trajectories_to_rosbag
cartographer_node
cartographer_pbstream_map_publisher
cartographer_dev_rosbag_publisher
cartographer_occupancy_grid_node
cartographer_pbstream_to_ros_map
6. Note.cpp 源码分析
0.Node构造函数
1) 定义各种topic
submap_list_publisher_ =
node_handle_.advertise<::cartographer_ros_msgs::SubmapList>(
kSubmapListTopic, kLatestOnlyPublisherQueueSize);
trajectory_node_list_publisher_ =
node_handle_.advertise<::visualization_msgs::MarkerArray>(
kTrajectoryNodeListTopic, kLatestOnlyPublisherQueueSize);
landmark_poses_list_publisher_ =
node_handle_.advertise<::visualization_msgs::MarkerArray>(
kLandmarkPosesListTopic, kLatestOnlyPublisherQueueSize);
constraint_list_publisher_ =
node_handle_.advertise<::visualization_msgs::MarkerArray>(
kConstraintListTopic, kLatestOnlyPublisherQueueSize);
scan_matched_point_cloud_publisher_ =
node_handle_.advertise<sensor_msgs::PointCloud2>(
kScanMatchedPointCloudTopic, kLatestOnlyPublisherQueueSize);
2)定义各种服务的发布器。
service_servers_.push_back(node_handle_.advertiseService(
kSubmapQueryServiceName, &Node::HandleSubmapQuery, this));
service_servers_.push_back(node_handle_.advertiseService(
kTrajectoryQueryServiceName, &Node::HandleTrajectoryQuery, this));
service_servers_.push_back(node_handle_.advertiseService(
kStartTrajectoryServiceName, &Node::HandleStartTrajectory, this));
service_servers_.push_back(node_handle_.advertiseService(
kFinishTrajectoryServiceName, &Node::HandleFinishTrajectory, this));
service_servers_.push_back(node_handle_.advertiseService(
kWriteStateServiceName, &Node::HandleWriteState, this));
service_servers_.push_back(node_handle_.advertiseService(
kGetTrajectoryStatesServiceName, &Node::HandleGetTrajectoryStates, this));
service_servers_.push_back(node_handle_.advertiseService(
kReadMetricsServiceName, &Node::HandleReadMetrics, this));
3)给各个发布器设置一个时钟,绑定时间到了的处理函数
wall_timers_.push_back(node_handle_.createWallTimer(
::ros::WallDuration(node_options_.submap_publish_period_sec),
&Node::PublishSubmapList, this));
wall_timers_.push_back(node_handle_.createWallTimer(
::ros::WallDuration(node_options_.trajectory_publish_period_sec),
&Node::PublishTrajectoryNodeList, this));
……
1.Run
cartographer_ros/cartographer_ros/cartographer_ros/node_main.cc
1)创建一个map_builder
2)创建一个 node
3)node.StartTrajectoryWithDefaultTopics()->AddTrajectory()
2.AddTrajectory
cartographer_ros/cartographer_ros/cartographer_ros/node.cc
1)map_builder_bridge_.AddTrajectory
map_builder_->AddTrajectoryBuilder->OnLocalSlamResult
2)AddExtrapolator 插入一个Extrapolator 位姿解析器
3)AddSensorSamplers
插入一TrajectorySensorSamplers,里面有rangefinder_sampler,odometry_sampler
,fixed_frame_pose_sampler,imu_sampler,landmark_sampler
4)LaunchSubscribers:订阅相关的topics,并指安处理函数
subscribers_[trajectory_id].push_back(
{SubscribeWithHandler<sensor_msgs::LaserScan>(
&Node::HandleLaserScanMessage, trajectory_id, topic, &node_handle_,
this),
topic});
subscribers_[trajectory_id].push_back(
{SubscribeWithHandler<sensor_msgs::MultiEchoLaserScan>(
&Node::HandleMultiEchoLaserScanMessage, trajectory_id, topic,
&node_handle_, this),
topic});
如果使用IMU的数据,则:
subscribers_[trajectory_id].push_back(
{SubscribeWithHandler<sensor_msgs::Imu>(&Node::HandleImuMessage,
trajectory_id, kImuTopic,
&node_handle_, this),
如果使用里程计的数据,则:
subscribers_[trajectory_id].push_back( {SubscribeWithHandler<nav_msgs::Odometry>
(&Node::HandleOdometryMessage, trajectory_id, kOdometryTopic,
&node_handle_, this),
如果使用导航,则
subscribers_[trajectory_id].push_back(
{SubscribeWithHandler<sensor_msgs::NavSatFix>(
&Node::HandleNavSatFixMessage, trajectory_id, kNavSatFixTopic,
&node_handle_, this)
如果使用landmaks,则:
subscribers_[trajectory_id].push_back(
{SubscribeWithHandler<cartographer_ros_msgs::LandmarkList>(
&Node::HandleLandmarkMessage, trajectory_id, kLandmarkTopic,
&node_handle_, this),
1./submap_list topic的数据
header:
seq: 272
stamp:
secs: 0
nsecs: 0
frame_id: "map"
submap:
-
trajectory_id: 0
submap_index: 0
submap_version: 50
pose:
position:
x: 0.0
y: 0.0
z: 0.0
orientation:
x: 0.0
y: 0.0
z: 0.0
w: 1.0
is_frozen: False
2. /constraint_list
header:
seq: 0
stamp:
secs: 0
nsecs: 0
frame_id: "map"
ns: "Inter constraints, different trajectories"
id: 4
type: 5
action: 0
pose:
position:
x: 0.0
y: 0.0
z: 0.1
orientation:
x: 0.0
y: 0.0
z: 0.0
w: 1.0
scale:
x: 0.025
y: 0.0
z: 0.0
color:
r: 0.0
g: 0.0
b: 0.0
a: 0.0
lifetime:
secs: 0
nsecs: 0
frame_locked: False
points: []
colors: []
text: ''
mesh_resource: ''
mesh_use_embedded_materials: False
3. /trajectory_node_list
header:
seq: 0
stamp:
secs: 0
nsecs: 0
frame_id: "map"
ns: "Trajectory 0"
id: 2
type: 4
action: 0
pose:
position:
x: 0.0
y: 0.0
z: 0.05
orientation:
x: 0.0
y: 0.0
z: 0.0
w: 1.0
scale:
x: 0.07
y: 0.0
z: 0.0
color:
r: 0.207129895687
g: 0.115499980748
b: 0.769999980927
a: 0.25
lifetime:
secs: 0
nsecs: 0
frame_locked: False
points:
-
x: 1.02211357213
y: 2.15808512043
z: 0.0
-
x: 1.05474171104
y: 2.13220375749
z: 0.0
-
x: 1.0466034346
y: 2.12491221525
z: 0.0
colors: []
text: ''
mesh_resource: ''
mesh_use_embedded_materials: False
4./tf
transforms:
-
header:
seq: 0
stamp:
secs: 1574159625
nsecs: 351509220
frame_id: "/base_footprint"
child_frame_id: "/laser_frame"
transform:
translation:
x: 0.2245
y: 0.0
z: 0.2
rotation:
x: 0.0
y: 0.0
z: 0.0
w: 1.0