任务动机:在openEuler下部署和测试cartographer,为cartographer优化创建环境。
任务描述:Turtlebot2上安装cartographer,添加雷达和底盘驱动后,对软件所5号楼12层2000平方米办公场所进行制图,对比Lidar、Lidar+IMU和Lidar+odom解决方案制图效果。
我这边直接安装的是turtlebot版本,本质上与单纯的cartographer版本的区别就是多了一个turtlebot的依赖包。前面编译步骤没有区别。
第一步:安装相关依赖包
sudo apt-get update
sudo apt-get install -y python-wstool python-rosdep ninja-build
第二步:创建并初始化 名为 catkin_ws 的工作空间
mkdir catkin_ws
cd catkin_ws
wstool init src
第三步:生成编译所需依赖包
wstool merge -t src https://raw.githubusercontent.com/googlecartographer/cartographer_turtlebot/master/cartographer_turtlebot.rosinstall
wstool update -t src
第四步:安装第三步中的包文件中的所需ros依赖
src/cartographer/scripts/install_proto3.sh
sudo rosdep init
rosdep update
rosdep install --from-paths src --ignore-src --rosdistro=${ROS_DISTRO} -y
第五步:编译安装
catkin_make_isolated --install --use-ninja -j1
source install_isolated/setup.bash
注意事项
1. 如果没有科学上网,在执行wstool update -t src之前,需要将src/.rosinstall文件修改成以下内容,以解决ceres-solver下载不了的问题
vim src/.rosinstall
# 更改ceres-solver中地址改为下面的地址:
#>>uri: https://github.com/ceres-solver/ceres-solver.git
2. 网络要保持连同,因为在执行过程中还会从github下载软件包
3. 如果编译失败,就删掉工作空间目录下除了src目录之外其他所有的文件夹,重新编译
4. 扩展2Gswap:apt-get install dphys-swapfile
5. 使用单核编译,对应上述流程的第五步:catkin_make_isolated --install --use-ninja -j1
测试
source ~/catkin_ws/install_isolated/setup.bash
使用博物馆数据集:
https://storage.googleapis.com/cartographer-public-data/bags/backpack_2d/cartographer_paper_deutsches_museum.bag
roslaunch cartographer_ros demo_backpack_2d.launch bag_filename:=${HOME}/Downloads/cartographer_paper_deutsches_museum.bag
测试结果:
下载驱动:
git clone https://github.com/leishen-lidar/LS01B
构建对应工作空间
直接roslaunch 驱动即可。
roslaunch ls01b_v2 ls01b_v2.launch
如果需要可视化的话: 在rviz中将固定的frame设置为laser_link,添加对应的laserScan模块即可,其中lidar的topic_name: /scan
首先打开kobuki底盘电源,USB线连接cp机,确认绿灯亮起后输入命令。
roscore
roslaunch turtlebot_bringup minimal.launch
roslaunch turtlebot_teleop keyboard_teleop.launch
通过键盘控制机器人移动。
分别在kobuki底盘上使用两种不同的雷达型号进行cpu和mem的占用测试。
1> cartographer运行中资源占用情况
cartographer这部分很正常,不存在什么问题。跟turtlebot的cpu占用差不多,排除cartographer的问题。
2> Ydlidar雷达的资源占用和镭神雷达对比
通过上述可以看出:
3> test method
pip install psrecord
alias psrecord='/home/《your_name》/.local/bin/psrecord'
psrecord --interval 1 --plot .png
4> 更换ydlidar的SDK之后的两个雷达测试对比
yd_lidar
nums of each scan: 909
frequency : 10hz
leishen_lidar
nums of each scan: 1440
frequency: 10hz
1. **.lua 文件关键参数说明(基于博物馆lua文件)
tracking_frame = "base_link",
published_frame = "base_link",
odom_frame = "odom",
provide_odom_frame = true,
---
use_odometry = false,
通常情况下,tracking_frame应该等于imu_link, 但是在博物馆lua文件中,写的是base_link,究其原因是博物馆的base_link == imu_link:
上图中,我们可以看到由节点/robot_state_publisher维护的tf变换中,transform一直都是0;
odom_frame 只有当参数provide_odom_frame is true的时候,该参数才有意义。该frame连接 published_frame 和map_frame,表示机器人没有遇到回环的时候局部SLAM的定位结果。
他只是这个frame的名字,为了防止和真正机器人提供的odom混淆,建议改名字为"odom_by_cartographer":
provide_odom_frame
true的话:提供纯粹的由cartographer生成的里程计frame
false的话, the tf_tree like this:
use_odometry 启用的话,cartographer的节点在话题"/odom"上订阅消息类型为nav_msgs/Odometry的消息类型。 注意: 这里的odom是topic,而不是 frame
2. 在cartographer中使用更多的传感器、信息
1. wheel encoder it can be used to improve cartographer's localization. just modify the .lua
files:
use_odometry
2. gps
this topic can improve the global SLAM.
3. landmark For landmarks publishing on a cartographer_ros_msgs/LandmarkList topic named landmarks.
the msg info:
std_msgs/Header header
uint32 seq
time stamp
string frame_id
cartographer_ros_msgs/LandmarkEntry[] landmark
string id
geometry_msgs/Pose tracking_from_landmark_transform
geometry_msgs/Point position
float64 x
float64 y
float64 z
geometry_msgs/Quaternion orientation
float64 x
float64 y
float64 z
float64 w
float64 translation_weight
float64 rotation_weight
3. 相关知识
Cartographer需要三轴的加速度来估计重力方向。
如果已经建好地图, 可以使用cartographer进行纯定位.在cartographer_node的参数中配置 -load_state_filename
,然后再lua文件中做如下定义:
TRAJECTORY_BUILDER.pure_localization_trimmer = {
max_submaps_to_keep = 3,
}
3. IMU-lidar的外参标定
ceres优化器可以帮助提高外参标定的准确度。使用过程中,多走一些回环,有助于提高外参标定的结果精度。
如果需要我们自己维护tf_tree的时候,有如下两种方法
小结:想要在turtlebot上正确运行cartographer,需要对lua文件和launch文件进行更改。
使用odom+lidar的配置:
include "map_builder.lua"
include "trajectory_builder.lua"
options = {
map_builder = MAP_BUILDER,
trajectory_builder = TRAJECTORY_BUILDER,
map_frame = "map",
tracking_frame = "odom",
published_frame = "odom",
odom_frame = "odom",
provide_odom_frame = false,
publish_frame_projected_to_2d = true,
use_pose_extrapolator = true,
use_odometry = true,
use_nav_sat = false,
use_landmarks = 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.5,
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_BUILDER.use_trajectory_builder_2d = true
MAP_BUILDER.num_background_threads = 5
TRAJECTORY_BUILDER_2D.min_range = 0.1
TRAJECTORY_BUILDER_2D.max_range = 12
TRAJECTORY_BUILDER_2D.voxel_filter_size = 0.05
TRAJECTORY_BUILDER_2D.adaptive_voxel_filter.max_length = 1.0
TRAJECTORY_BUILDER_2D.adaptive_voxel_filter.min_num_points = 200
TRAJECTORY_BUILDER_2D.adaptive_voxel_filter.max_range = 50
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 1.
TRAJECTORY_BUILDER_2D.submaps.num_range_data = 50
TRAJECTORY_BUILDER_2D.use_imu_data = false
--POSE_GRAPH.optimization_problem.local_slam_pose_translation_weight
--POSE_GRAPH.optimization_problem.local_slam_pose_rotation_weight
--POSE_GRAPH.optimization_problem.odometry_translation_weight
--POSE_GRAPH.optimization_problem.odometry_rotation_weight
-- TRAJECTORY_BUILDER_2D.ceres_scan_matcher.tranlation_weight = 2e2
-- TRAJECTORY_BUILDER_2D.ceres_scan_matcher.rotation_weight = 4e2
TRAJECTORY_BUILDER_2D.use_online_correlative_scan_matching = true
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.linear_search_window = 0.1
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.translation_delta_cost_weight = 10.
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.rotation_delta_cost_weight = 6e-1
POSE_GRAPH.optimize_every_n_nodes = 70
POSE_GRAPH.global_sampling_ratio = 0.001
POSE_GRAPH.global_constraint_search_after_n_seconds = 10
POSE_GRAPH.constraint_builder.sampling_ratio = 0.3
POSE_GRAPH.constraint_builder.min_score= 0.65
POSE_GRAPH.optimization_problem.ceres_solver_options.max_num_iterations = 50
POSE_GRAPH.optimization_problem.huber_scale = 1e2
return options
使用lidar + imu的lua配置:
include "map_builder.lua"
include "trajectory_builder.lua"
options = {
map_builder = MAP_BUILDER,
trajectory_builder = TRAJECTORY_BUILDER,
map_frame = "map",
tracking_frame = "imu",
published_frame = "odom",
odom_frame = "odom",
provide_odom_frame = false,
publish_frame_projected_to_2d = true,
use_pose_extrapolator = true,
use_odometry = false,
use_nav_sat = false,
use_landmarks = 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.5,
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_BUILDER.use_trajectory_builder_2d = true
MAP_BUILDER.num_background_threads = 8
TRAJECTORY_BUILDER_2D.min_range = 0.1
TRAJECTORY_BUILDER_2D.max_range = 12
TRAJECTORY_BUILDER_2D.voxel_filter_size = 0.05
TRAJECTORY_BUILDER_2D.adaptive_voxel_filter.max_length = 1.0
TRAJECTORY_BUILDER_2D.adaptive_voxel_filter.min_num_points = 200
TRAJECTORY_BUILDER_2D.adaptive_voxel_filter.max_range = 50
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 1.
TRAJECTORY_BUILDER_2D.submaps.num_range_data = 50
TRAJECTORY_BUILDER_2D.use_imu_data = true
TRAJECTORY_BUILDER_2D.use_online_correlative_scan_matching = true
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.linear_search_window = 0.1
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.translation_delta_cost_weight = 10.
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.rotation_delta_cost_weight = 6e-1
POSE_GRAPH.optimize_every_n_nodes = 70
POSE_GRAPH.global_sampling_ratio = 0.001
POSE_GRAPH.global_constraint_search_after_n_seconds = 10
POSE_GRAPH.constraint_builder.sampling_ratio = 0.3
POSE_GRAPH.constraint_builder.min_score= 0.65
POSE_GRAPH.optimization_problem.ceres_solver_options.max_num_iterations = 50
POSE_GRAPH.optimization_problem.huber_scale = 1e2
return options
在整个软件所5号楼12层构建地图,全部面积达到2000+平方米。
分离试验结果:
从上图可以看到,ODOM + LIDAR在局部存在纠偏没纠回来的可能。