datetime:2023/10/10 11:03
author:nzb
该项目来源于大佬的动手学ROS2
Cartographer纯雷达定位建图
所谓纯雷达建图指不依靠IMU或者里程计信息,只使用雷达的深度点云进行建图,但我们的机器人在摩擦力较小容易打滑的地面或者没有里程计信息的机器人上,只使用激光雷达进行定位和建图往往可以取得较好的效果。
从里程计+雷达SLAM,切换成纯雷达SLAM只需要修改Cartographer的参数即可。
本代码基于Ubuntu22.04+ROS 2.0 humble
版本进行测试。 机器人使用fishbot代码:
在fishbot的工作空间下新建cartographer配置文件 src/fishbot_cartographer/config/fishbot_laser_2d.lua
,新建launch文件:src/fishbot_cartographer/launch/cartographer_pure_laser.launch.py
配置文件
include "map_builder.lua"
include "trajectory_builder.lua"
options = {
map_builder = MAP_BUILDER,
trajectory_builder = TRAJECTORY_BUILDER,
map_frame = "map",
-- 跟踪和发布的frame都改成雷达的frameID
tracking_frame = "laser_frame",
published_frame = "laser_frame",
odom_frame = "odom",
-- true改为false,不用提供里程计数据
provide_odom_frame = true,
-- false改为true,仅发布2D位资
publish_frame_projected_to_2d = false,
use_pose_extrapolator = true,
-- true改为false,不使用里程计数据
use_odometry = false,
use_nav_sat = false,
use_landmarks = false,
-- 0改为1,使用一个雷达
num_laser_scans = 1,
-- 1改为0,不使用多波雷达
num_multi_echo_laser_scans = 0,
-- 10改为1,1/1=1等于不分割
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.,
}
-- false改为true,启动2D SLAM
MAP_BUILDER.use_trajectory_builder_2d = true
-- 0改成0.10,比机器人半径小的都忽略
TRAJECTORY_BUILDER_2D.min_range = 0.10
-- 30改成3.5,限制在雷达最大扫描范围内,越小一般越精确些
TRAJECTORY_BUILDER_2D.max_range = 5.5
-- 5改成3,传感器数据超出有效范围最大值
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 3.
-- true改成false,不使用IMU数据,大家可以开启,然后对比下效果
TRAJECTORY_BUILDER_2D.use_imu_data = false
-- false改成true,使用实时回环检测来进行前端的扫描匹配
TRAJECTORY_BUILDER_2D.use_online_correlative_scan_matching = true
-- 1.0改成0.1,提高对运动的敏感度
-- TRAJECTORY_BUILDER_2D.motion_filter.max_angle_radians = math.rad(0.1)
-- 0.55改成0.65,Fast csm的最低分数,高于此分数才进行优化。
POSE_GRAPH.constraint_builder.min_score = 0.65
--0.6改成0.7,全局定位最小分数,低于此分数则认为目前全局定位不准确
POSE_GRAPH.constraint_builder.global_localization_min_score = 0.7
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 = 1e-1
POSE_GRAPH.optimization_problem.huber_scale = 1e2
POSE_GRAPH.optimize_every_n_nodes = 35
-- 设置0可关闭全局SLAM
-- POSE_GRAPH.optimize_every_n_nodes = 0
return options
launch文件
接着我们新加一个launch文件,修改参数文件的名称,保存编译即可进行测试。
import os
from launch import LaunchDescription
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
# 定位到功能包的地址
pkg_share = FindPackageShare(package='fishbot_cartographer').find('fishbot_cartographer')
# =====================运行节点需要的配置=======================================================================
# 是否使用仿真时间,我们用gazebo,这里设置成true
use_sim_time = LaunchConfiguration('use_sim_time', default='false')
# 地图的分辨率
resolution = LaunchConfiguration('resolution', default='0.05')
# 地图的发布周期
publish_period_sec = LaunchConfiguration('publish_period_sec', default='1.0')
# 配置文件夹路径
configuration_directory = LaunchConfiguration('configuration_directory', default=os.path.join(pkg_share, 'config'))
# 配置文件
configuration_basename = LaunchConfiguration('configuration_basename', default='fishbot_laser_2d.lua')
# =====================声明三个节点,cartographer/occupancy_grid_node/rviz_node=================================
cartographer_node = Node(
package='cartographer_ros',
executable='cartographer_node',
name='cartographer_node',
output='screen',
parameters=[{'use_sim_time': use_sim_time}],
arguments=['-configuration_directory', configuration_directory,
'-configuration_basename', configuration_basename])
cartographer_occupancy_grid_node = Node(
package='cartographer_ros',
executable='cartographer_occupancy_grid_node',
name='cartographer_occupancy_grid_node',
output='screen',
parameters=[{'use_sim_time': use_sim_time}],
arguments=['-resolution', resolution, '-publish_period_sec', publish_period_sec])
rviz_node = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
# arguments=['-d', rviz_config_dir],
parameters=[{'use_sim_time': use_sim_time}],
output='screen')
# ===============================================定义启动文件========================================================
ld = LaunchDescription()
ld.add_action(cartographer_node)
ld.add_action(cartographer_occupancy_grid_node)
ld.add_action(rviz_node)
return ld