【10天速通Navigation2】(三) :Cartographer建图算法配置:从仿真到实车,从原理到实现
前言
- 往期内容:
- 第一期:【10天速通Navigation2】(一) 框架总览和概念解释
- 第二期:【10天速通Navigation2】(二) :ROS2gazebo阿克曼小车模型搭建-gazebo_ackermann_drive等插件的配置和说明
- 本教材将贯穿
nav2
的全部内容,使用ROS2和C++实现一些仿真乃至实车中常见的建图和路径规划算法,例如cartographer
,ORB-SLAM
,RRT
,hybrid-astar
。我们将注重与原理讲解和代码实现,去详细讲解每一步的配置过程和代码复现细节。 - 同理本教材默认大家有一些基础的ROS2和C++的编程基础,故不对一些基础部分进行详细说明。
- 本教程使用的环境:
- ROS2 humble
- ubuntu 22.04 LTS
- 本期我们将从建图算法
cartographer
原理入手,再从仿真到实车进行配置,达到以下的效果:- 仿真建图
- 实车建图
1 Cartographer原理解读
1-1 介绍
Cartographer
是由Google开发的一套基于图优化的激光SLAM(Simultaneous Localization and Mapping,即时定位与地图构建)算法。该算法同时支持2D和3D激光SLAM,能够跨平台使用,并支持多种传感器配置,如Lidar(激光雷达)、IMU(惯性测量单元)、Odometry(里程计)、GPS和Landmark等。- 为啥不使用基于
滤波方法
的Gmapping
?- 当地图很大的时候,粒子更新的计算量和实时性有问题(粒子估计计算量会成倍增加)
Gmapping
没有闭环检测。
- 下述大部分内容节选自
《机器人SLAM导航核心技术与实战》
,书本关于SLAM的内容讲得很好,这里结合了本人的理解一起呈现给大家。
1-2 基于优化方法方法的SLAM系统
- 基于优化方法方法的SLAM系统通常采用
前端建图
,闭环检测
,后端全局优化
这样的经典框架。
- 这样基于优化方法,就可以实现大规模地图的构建。
- 只要有新的传感器数据输入,前端的局部建图就会实时的进行,这样就可以保证
实时性
- 一旦检测到回环,后端建图就开始了,保证
计算量
。(后端运行的时候即使计算量很大,计算速度很慢,但是不影响前端,不影响实时性
)
- 只要有新的传感器数据输入,前端的局部建图就会实时的进行,这样就可以保证
1-3 局部建图
- 在进一步了解
Cartographer
局部地图构建的原理之前,我们首先需要知道Cartographer
的地图结构,Cartographer
采用局部地图采用局部子图(submap)
的方式来组织整个地图,而每个子图都是由一些系列雷达帧组成的,通过拼接局部地图,我们就获得了一张张局部地图submap
。全部的局部地图
拼接在一起,我们就得到了全局地图
。
局部建图
就是利用传感器扫描数据构建局部地图
的过程。
1-3-1 局部优化
-
那么在构建局部地图的过程中,我们往往需要对一系列雷达扫描到的轮廓进行匹配和合并,进而构建一张完整的局部地图,这时候就不得不介绍两种匹配合并的算法:
Scan-to-scan matching
:相邻两帧之间去匹配,去更新代价地图。Scan-to-map matching
:采用当前雷达帧和已构建出的地图进行匹配。
-
很明显
Scan-to-scan matching
使用相邻的两帧容易造成误差累积,偏差会越来越大,因此在Cartographer
中选取了Scan-to-map matching
,这样可以减小误差,特征也更加明显。
1-3-2 新雷达数据加入子图–概率栅格地图更新
-
和
Gmapping
类似,Cartographer
的子图也采用概率栅格地图
,即在栅格所划分的地图中,概率值越大,越表示该区域所存在障碍物的可能性越高。 -
之所以采用如此原理进行更新,主要是应对不同雷达帧在扫描到同一个栅格时候出现不同的结果。什么意思呢,也就是假如我连续两帧雷达在同一个位置扫描出的结果分别是有障碍物和无障碍物,那我要怎么办?
-
这里就需要进行两种情况的讨论了:
-
我们以上述栅格地图为例子:当前雷达 ζ \zeta ζ所处发射出的射线,其中的一帧雷达数据扫描结果显示 h 3 h_3 h3位置有障碍物,这时候我们要求 h 3 h_3 h3这个栅格以及这条射线扫描到的所有栅格进行分类讨论:
-
如果之前的雷达帧构建的子图中 h 3 h_3 h3和这一帧雷达经过的栅格从来没有被扫描到,那么我们就使用雷达概率观测模型的 P h i t P_{hit} Phit和 P m i s s P_{miss} Pmiss分别对这些栅格进行概率初始化:
M new ( x ) = { P hit , if state ( x ) = hit P miss , if state ( x ) = miss M_{\text{new}}(x) = \begin{cases} P_{\text{hit}}, & \text{if } \text{state}(x) = \text{hit} \\ P_{\text{miss}}, & \text{if } \text{state}(x) = \text{miss} \end{cases} Mnew(x)={Phit,Pmiss,if state(x)=hitif state(x)=miss -
如果之前的雷达帧构建的子图中栅格已经被初始化为 M o l d ( x ) M_{old}(x) Mold(x),那么这时候我们就需要使用到两次不同的数据通过下属公式对该栅格进行迭代处理:
M n e w ( x ) = { c l a m p ( o d d s − 1 ( o d d s ( M o l d ( x ) ) ⋅ o d d s ( P h i t ) ) , 当 s t a t e ( x ) = h i t 时 c l a m p ( o d d s − 1 ( o d d s ( M o l d ( x ) ) ⋅ o d d s ( P m i s s ) ) , 当 s t a t e ( x ) = m i s s 时 M_{new}(x)= \left\{ \begin{array}{ll} clamp(odd_s^{-1}(odd_s(M_{old}(x)) \cdot odds(P_{hit})), & 当state(x)=hit时 \\ clamp(odd_s^{-1}(odd_s(M_{old}(x)) \cdot odds(P_{miss})), & 当state(x)=miss时 \end{array} \right. Mnew(x)={clamp(odds−1(odds(Mold(x))⋅odds(Phit)),clamp(odds−1(odds(Mold(x))⋅odds(Pmiss)),当state(x)=hit时当state(x)=miss时
其中, o d d s ( p r o b ) = p r o b 1 − p r o b odds(prob)=\frac{prob}{1-prob} odds(prob)=1−probprob
- 通过上述更新机制,我们就能处理同一个位置不同雷达帧出现不同结果的问题,这常常被应用在处理环境中动态障碍物的干扰上。
1-3-3 雷达源局部优化
- 上述更新子图的一个重要前提是雷达的源 ζ \zeta ζ误差极小的情况下,而往往在真实过程中我们同样需要对这个雷达的源 ζ \zeta ζ进行局部优化
- 这其实是一个非线性最小二乘问题,在新的雷达数据加入子图之前通过运动预测出雷达位姿附近窗口内进行搜索匹配。而对于这个问题,
Cartographer
使用到了Ceres
非线性优化工具来解决这个问题。
arg min ζ ∑ k = 1 K ( 1 − M smooth ( T ζ h k ) ) 2 \arg \min_{\zeta} \sum_{k=1}^{K} \left( 1 - M_{\text{smooth}}(T_{\zeta} h_k) \right)^2 argζmink=1∑K(1−Msmooth(Tζhk))2
1-4 闭环检测
- 尽管我们已经通过上述优化方法对于观测源雷达的位姿进行局部优化,但是随着距离增加,误差累积,当机器人运动一圈后回到原点会发现地图无法完全闭环,出现了重影。
- 通过引入
闭环检测
,我们可以对误差的地方进行修正,最直观的原理就是判断机器人何时回到了之前走过的地方,而这通常有几种方法:暴力搜索匹配
:把雷达帧和地图上的所有点进行匹配(耗时,计算量大)分支界定匹配
:
分支界定匹配
,简单说就是把地图的分辨率降低,然后依次提高分辨率进行匹配,类似于一种二分的感觉进行从大到小的匹配。分支界定匹配
属于广度优先策略,暴力搜索匹配
可以看成分支界定匹配
处于最高分辨率情况下的特例
1-5 全局建图
- 闭环检测成功以后,会触发后端全局优化,并更新全局代价地图.
2 Cartographer配置和参数说明
2-1 安装
- 那事不宜迟,我们选择最快的方法进行
cartographer
安装
sudo apt install ros-humble-cartographer-ros
- 注意的是这里我们安装的是
cartographer-ros
而不是cartographer
本身 - 可以输入下属指令判断是否安装成功
ros2 pkg list | grep cartographer
2-2 lua配置文件
- Lua是一种轻量级的编程语言,被广泛应用于游戏开发、脚本编写和网络应用中。Lua文件通常以
.lua
为扩展名,包含了Lua语言的代码。 - 在cartographer建图算法中,我们将使用
lua
对其中的参数进行配置:- 老规矩先上完整代码我们再来细看参数
include "map_builder.lua"
include "trajectory_builder.lua"options = {map_builder = MAP_BUILDER,trajectory_builder = TRAJECTORY_BUILDER,map_frame = "map",tracking_frame = "base_footprint",published_frame = "odom",odom_frame = "odom",provide_odom_frame = false,publish_frame_projected_to_2d = false,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.2,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 =2TRAJECTORY_BUILDER_2D.num_accumulated_range_data = 1 --积累几帧激光数据作为一个标准单位scan
TRAJECTORY_BUILDER_2D.min_range = 0.1 --激光的最近有效距离
TRAJECTORY_BUILDER_2D.max_range = 12. --激光最远的有效距离
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 5. --无效激光数据设置距离为该数值
TRAJECTORY_BUILDER_2D.use_imu_data = false --是否使用imu数据TRAJECTORY_BUILDER_2D.use_online_correlative_scan_matching = true
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.linear_search_window = 0.1
--线距离搜索框,在这个框的大小内,搜索最佳scan匹配 减小该参数可以增强实时的建图效果,降低闭环优化的效果,形成闭环时,产生的重影较多
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher. angular_search_window = math.rad(10.) --角度搜索框的大小
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.translation_delta_cost_weight = 20.
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.rotation_delta_cost_weight = 2e-1
--影响的是过程中的效果,间接会影响最后的优化时间长TRAJECTORY_BUILDER_2D.ceres_scan_matcher.translation_weight = 30.
TRAJECTORY_BUILDER_2D.ceres_scan_matcher.rotation_weight = 30.
TRAJECTORY_BUILDER_2D.ceres_scan_matcher.ceres_solver_options.max_num_iterations = 20TRAJECTORY_BUILDER_2D.submaps.num_range_data = 40
--num_range_data设置的值与CPU有这样一种关系,值小(10),CPU使用率比较稳定,整体偏高,值大时,CPU短暂爆发使用(插入子图的时候),平时使用率低,呈现极大的波动状态。
TRAJECTORY_BUILDER_2D.submaps.range_data_inserter.probability_grid_range_data_inserter.hit_probability = 0.55
TRAJECTORY_BUILDER_2D.submaps.range_data_inserter.probability_grid_range_data_inserter.miss_probability = 0.49TRAJECTORY_BUILDER_2D.motion_filter.max_distance_meters = 0.05 --//尽量小点 // 如果移动距离过小, 或者时间过短, 不进行地图的更新
TRAJECTORY_BUILDER_2D.motion_filter.max_angle_radians = math.rad(0.3)
TRAJECTORY_BUILDER_2D.motion_filter.max_time_seconds = 0.5POSE_GRAPH.optimization_problem.huber_scale = 1e2 --鲁棒核函数,去噪POSE_GRAPH.optimize_every_n_nodes = 35 --后端优化节点
POSE_GRAPH.global_constraint_search_after_n_seconds = 10 POSE_GRAPH.optimization_problem.ceres_solver_options.max_num_iterations = 15 --优化迭代步数
POSE_GRAPH.optimization_problem.ceres_solver_options.num_threads = 1POSE_GRAPH.constraint_builder.max_constraint_distance = 15.
POSE_GRAPH.constraint_builder.sampling_ratio = 0.3
POSE_GRAPH.constraint_builder.min_score = 0.50
POSE_GRAPH.constraint_builder.global_localization_min_score = 0.6
POSE_GRAPH.constraint_builder.fast_correlative_scan_matcher.linear_search_window = 3.
POSE_GRAPH.constraint_builder.fast_correlative_scan_matcher.branch_and_bound_depth = 5. --搜索方法,界定分支法,求解问题构成一个搜索树,depth是构造树的深度
POSE_GRAPH.global_sampling_ratio = 0.001return options
2-2-1 Option
options
: 这是一个表(table),包含了Cartographer算法的各种配置选项。
include "map_builder.lua"
include "trajectory_builder.lua"map_builder = MAP_BUILDER,
trajectory_builder = TRAJECTORY_BUILDER,
map_builder
: 指定用于构建地图的类。
trajectory_builder
: 指定用于构建轨迹的类。
map_frame = "map",tracking_frame = "base_footprint",published_frame = "odom",odom_frame = "odom",provide_odom_frame = false,publish_frame_projected_to_2d = false,use_pose_extrapolator = true,use_odometry = true,
map_frame
: 地图的坐标系。tracking_frame
: 跟踪的坐标系,通常是机器人底盘的坐标系。published_frame
: 发布的坐标系,通常是用于导航的坐标系。注意这里用于设置cartographer是否发布从tracking_frame
到odom_frame
之间的tf树
- 为了满足建图时候的tf坐标数结构完整
- 因此如果这里设置为false,你就需要额外在开一个新的tf静态坐标转换节点用于广播
odom
到base_footprint
之间的变换
ros2 run tf2_ros static_transform_publisher 0.0 0.0 0.0 0.0 0.0 0.0 odom base_footprint
odom_frame
: 里程计坐标系。provide_odom_frame
: 是否提供里程计坐标系。publish_frame_projected_to_2d
: 是否将3D数据投影到2D进行发布。use_pose_extrapolator
: 是否使用姿态外推器。- ==
use_odometry
: 是否使用里程计数据。==通常我们需要引入odom或者IMU用于辅助定位,否则会出现偏移和无法闭环的问题(后面我们会讲到) use_nav_sat
: 是否使用导航卫星数据。use_landmarks
: 是否使用地标数据。num_laser_scans
: 使用的激光扫描仪数量。num_multi_echo_laser_scans
: 使用多回声激光扫描仪的数量。num_subdivisions_per_laser_scan
: 每个激光扫描数据的细分数量。num_point_clouds
: 使用的点云数据数量。lookup_transform_timeout_sec
: 查找变换的超时时间。submap_publish_period_sec
: 发布子地图的周期。pose_publish_period_sec
: 发布姿态的周期。trajectory_publish_period_sec
: 发布轨迹的周期。rangefinder_sampling_ratio
: 激光采样比率。odometry_sampling_ratio
: 里程计采样比率。fixed_frame_pose_sampling_ratio
: 固定帧姿态采样比率。imu_sampling_ratio
: IMU采样比率。landmarks_sampling_ratio
: 地标采样比率。
- 以下是其他配置参数的说明:
MAP_BUILDER.use_trajectory_builder_2d = true
MAP_BUILDER.num_background_threads =2TRAJECTORY_BUILDER_2D.num_accumulated_range_data = 1 --积累几帧激光数据作为一个标准单位scan
TRAJECTORY_BUILDER_2D.min_range = 0.1 --激光的最近有效距离
TRAJECTORY_BUILDER_2D.max_range = 12. --激光最远的有效距离
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 5. --无效激光数据设置距离为该数值
TRAJECTORY_BUILDER_2D.use_imu_data = false --是否使用imu数据TRAJECTORY_BUILDER_2D.use_online_correlative_scan_matching = true
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.linear_search_window = 0.1
--线距离搜索框,在这个框的大小内,搜索最佳scan匹配 减小该参数可以增强实时的建图效果,降低闭环优化的效果,形成闭环时,产生的重影较多
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher. angular_search_window = math.rad(10.) --角度搜索框的大小
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.translation_delta_cost_weight = 20.
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.rotation_delta_cost_weight = 2e-1
--影响的是过程中的效果,间接会影响最后的优化时间长TRAJECTORY_BUILDER_2D.ceres_scan_matcher.translation_weight = 30.
TRAJECTORY_BUILDER_2D.ceres_scan_matcher.rotation_weight = 30.
TRAJECTORY_BUILDER_2D.ceres_scan_matcher.ceres_solver_options.max_num_iterations = 20TRAJECTORY_BUILDER_2D.submaps.num_range_data = 40
--num_range_data设置的值与CPU有这样一种关系,值小(10),CPU使用率比较稳定,整体偏高,值大时,CPU短暂爆发使用(插入子图的时候),平时使用率低,呈现极大的波动状态。
TRAJECTORY_BUILDER_2D.submaps.range_data_inserter.probability_grid_range_data_inserter.hit_probability = 0.55
TRAJECTORY_BUILDER_2D.submaps.range_data_inserter.probability_grid_range_data_inserter.miss_probability = 0.49TRAJECTORY_BUILDER_2D.motion_filter.max_distance_meters = 0.05 --//尽量小点 // 如果移动距离过小, 或者时间过短, 不进行地图的更新
TRAJECTORY_BUILDER_2D.motion_filter.max_angle_radians = math.rad(0.3)
TRAJECTORY_BUILDER_2D.motion_filter.max_time_seconds = 0.5POSE_GRAPH.optimization_problem.huber_scale = 1e2 --鲁棒核函数,去噪POSE_GRAPH.optimize_every_n_nodes = 35 --后端优化节点
POSE_GRAPH.global_constraint_search_after_n_seconds = 10 POSE_GRAPH.optimization_problem.ceres_solver_options.max_num_iterations = 15 --优化迭代步数
POSE_GRAPH.optimization_problem.ceres_solver_options.num_threads = 1POSE_GRAPH.constraint_builder.max_constraint_distance = 15.
POSE_GRAPH.constraint_builder.sampling_ratio = 0.3
POSE_GRAPH.constraint_builder.min_score = 0.50
POSE_GRAPH.constraint_builder.global_localization_min_score = 0.6
POSE_GRAPH.constraint_builder.fast_correlative_scan_matcher.linear_search_window = 3.
POSE_GRAPH.constraint_builder.fast_correlative_scan_matcher.branch_and_bound_depth = 5. --搜索方法,界定分支法,求解问题构成一个搜索树,depth是构造树的深度
POSE_GRAPH.global_sampling_ratio = 0.001
MAP_BUILDER.use_trajectory_builder_2d
: 是否使用2D轨迹构建器。MAP_BUILDER.num_background_threads
: 地图构建器使用的后台线程数。TRAJECTORY_BUILDER_2D
相关参数:num_accumulated_range_data
: 累积多少帧激光数据作为一个扫描单位。min_range
和max_range
: 激光数据的有效距离范围。missing_data_ray_length
: 无效激光数据的设置距离。use_imu_data
: 是否使用IMU数据。use_online_correlative_scan_matching
: 是否使用在线相关扫描匹配。real_time_correlative_scan_matcher
相关参数: 实时相关扫描匹配器的配置。ceres_scan_matcher
相关参数: Ceres扫描匹配器的配置。submaps
相关参数: 子地图的配置。motion_filter
相关参数: 运动过滤器的配置。
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.angular_search_window
参数定义了在实时相关扫描匹配器中,进行角度搜索的范围。这个参数的值是以弧度为单位的角度,用于确定在匹配当前激光扫描数据与之前扫描数据时,可以接受的最大角度差异。,后续当地图出现飘逸的时候需要i修改这个参数.
POSE_GRAPH
相关参数:optimization_problem
相关参数: 优化问题的配置。optimize_every_n_nodes
: 每隔多少个节点进行一次优化。global_constraint_search_after_n_seconds
: 多少秒后进行全局约束搜索。constraint_builder
相关参数: 约束构建器的配置
2-3 启动文件
- 这里直接参考小鱼的节点
cartographer_node
:这是Cartographer的核心节点,负责处理传感器数据(如激光扫描和IMU数据),执行SLAM(Simultaneous Localization and Mapping,同时定位与建图)算法,并构建环境的地图。它还负责轨迹的优化和闭环检测。cartographer_occupancy_grid_node
:这个节点将Cartographer生成的概率网格转换为ROS中的nav_msgs/OccupancyGrid
消息格式,这样就可以被其他导航和路径规划节点使用。这个网格通常用于机器人的路径规划和避障。
import os
from launch import LaunchDescription
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageSharedef generate_launch_description():# 定位到功能包的地址pkg_share = FindPackageShare(package='qingzhou_sim').find('qingzhou_cartographer')#=====================运行节点需要的配置=======================================================================# 是否使用仿真时间,我们用gazebo,这里设置成trueuse_sim_time = LaunchConfiguration('use_sim_time', default='true')# 地图的分辨率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='qingzhou_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])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(occupancy_grid_node)ld.add_action(rviz_node)return ld
3 Cartographer仿真和实车演示
- 那么现在要做的很简单,启动以下节点:
- gazebo仿真节点,上一期提到的
- 键盘控制节点
- cartographer建图节点
3-1 仿真实操与问题解决
-
那很显然,我们遇到了第一个问题
-
如上图所示,仿真小车在前半段的地图运行一定距离时候,出现了
odom
和map
坐标系严重偏离的情况,这是因为雷达扫描不到边缘,周围的环境没有出现特征点,SLAM出现了估计错误. -
很简单,这里是仿真,把雷达扫描的范围扩大
-
由于扩大的雷达的扫描距离,就不会出现估计的偏差,如下我们完成了一圈的建图,这时候就可以开始进行地图保存
3-2 map_server
map_server
是一个用于在ROS(Robot Operating System)中提供地图数据的节点。它主要用于读取、保存和发布地图数据,这些地图数据通常是由SLAM(Simultaneous Localization and Mapping)算法生成的,或者是由其他方式创建的静态地图。- 我们写一个bash脚本用于地图保存
#!/bin/bash# 获取当前的日期和时间,格式为 YYYY-MM-DD_HH-MM-SS
current_datetime=$(date '+%Y-%m-%d_%H-%M-%S')# 构建地图文件名
map_filename="map_$current_datetime"# 切换到相应的目录并运行map_saver_cli命令
cd src/qingzhou_cartographer/maps
ros2 run nav2_map_server map_saver_cli -t map -f "$map_filename"
-
运行上述命令后
map_server
将把我们的地图保存为如下形式: -
**map.yaml
文件描述了地图的属性和相关的PGM文件
image: map_2024-10-23_23-10-11.pgm
mode: trinary
resolution: 0.05
origin: [-1.97, -3.77, 0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.25
-
image
: 地图的PGM文件名,这个文件包含了地图的像素数据。 -
mode
: 地图的模式,trinary
表示地图使用三种值:0(空闲),100(占用)和-1(未知)。 -
resolution
: 地图中每个单元格代表的实际距离,这里是0.05米。 -
origin
: 地图原点在真实世界坐标系中的位置,这里是[-1.97, -3.77, 0]
,表示地图左下角的位置。 -
negate
: 一个标志,用来指示是否反转地图的占用值,0表示不反转。 -
occupied_thresh
: 占用阈值,用于确定一个单元格是否被占用,这里是0.65,意味着如果PGM文件中单元格的值大于或等于65,则该单元格被认为是占用的。 -
free_thresh
: 空闲阈值,用于确定一个单元格是否空闲,这里是0.25,意味着如果PGM文件中单元格的值小于或等于25,则该单元格被认为是空闲的。 -
同时我们有的得到一张保存的pgm图片
-
这时候我们仿真就完成了cartographer的配置.
3-2 实车建图问题与解决
- 这里我拿另一个项目实车所遇到的问题和解决方案一同和大家分享
3-2-1 实车建图飘逸问题
-
如下图实车的雷达更新频率只有
10hz
,单纯使用雷达进行匹配容易造成地图出现飘逸 -
而往往实车中是有轮式里程计的存在的,这时候我们只需要打开
lua
配置中的使用里程计 -
这时候驱动小车移动一圈回到原点后发现地图没有出现大幅度漂移但是仍有模糊现象,这时候我们需要
-
把最小角度改小!!!
-
这样地图基本上没问题了.
3-2-2 实车建图里程计报错问题
-
Carto建图报错找不到Odom
-
可能原因一:Odom话题非发布一查话题确实掉了
-
可能原因二:tf树断开
-
本地下载tf树
sudo apt-get install ros-humble-rqt-tf-tree
- 运行
ros2 run rqt_tf_tree rqt_tf_tree --force-discover
- 发现tf树确实断开,缺少
- 完整的tf树应该这样,这里有两种方法发布
base_footprint
–>odom
- 如果
provide_odom_frame
这个参数打开,会自动帮你进行odom
到base_footprint
的转换,
- 把他关掉的话就需要自己额外进行静态坐标变换 推荐关掉自己发布
ros2 run tf2_ros static_transform_publisher 0.0 0.0 0.0 0.0 0.0 0.0 odom base_footprint
4 小节
- 本节我们介绍了cartographer的原理,并分别从仿真和实车进行配置和问题分析.
- 如有错误,欢迎指出!!!
- 下一期我们讲讲如何在仿真中配置
libgazebo_ros_camera
深度相机插件,并使用ORB-SLAM
进行建图 - 感谢大家的支持!!!
相关文章:
【10天速通Navigation2】(三) :Cartographer建图算法配置:从仿真到实车,从原理到实现
前言 往期内容: 第一期:【10天速通Navigation2】(一) 框架总览和概念解释第二期:【10天速通Navigation2】(二) :ROS2gazebo阿克曼小车模型搭建-gazebo_ackermann_drive等插件的配置和说明 本教材将贯穿nav2的全部内容,…...
测试造数,excel转insert语句
目录 excel转sql的insert语句一、背景二、直接上代码 excel转sql的insert语句 一、背景 在实际测试工作中,需要频繁地进行测试造数并插入数据库验证,常规的手写sql语句过于浪费时间,为此简单写个脚本,通过excel来造数࿰…...
Python 应用可观测重磅上线:解决 LLM 应用落地的“最后一公里”问题
作者:彦鸿 背景 随着 LLM(大语言模型)技术的不断成熟和应用场景的不断拓展,越来越多的企业开始将 LLM 技术纳入自己的产品和服务中。LLM 在自然语言处理方面表现出令人印象深刻的能力。然而,其内部机制仍然不明确&am…...
从零开始:用Spring Boot搭建厨艺分享网站
2 相关技术 2.1 Spring Boot框架简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Sprin…...
《2024中国泛娱乐出海洞察报告》解析,垂直且多元化方向发展!
随着以“社交”为代表的全球泛娱乐市场规模不断扩大以及用户需求不断细化,中国泛娱乐出海产品正朝着更加垂直化、多元化的方向发展。基于此,《2024中国泛娱乐出海洞察报告》深入剖析了中国泛娱乐行业出海进程以及各细分赛道出海现状及核心特征。针对中国…...
强化学习数学原理学习(一)
前言 总之开始学! 正文 先从一些concept开始吧,有一个脉络比较好 state 首先是就是状态和状态空间,显而易见,不多说了 action 同理,动作和动作空间 state transition 状态转换,不多说 policy 策略,不多说 reward 奖励,不多说 MDP(马尔科夫) 这里需要注意到就是这个是无…...
获 Sei 基金会投资的 MetaArena :掀起新一轮链上游戏革命
MetaArena 是一个综合性的 Web3 游戏开发和发布平台,集成了最先进的技术架构,包括 Unreal Engine 5.3、去中心化虚拟资产交易市场和分布式计算资源支持。平台不仅为开发者提供了高效的开发工具,还通过跨链功能和 AI 模块,极大简化…...
react-signature-canvas 实现画笔与橡皮擦功能
react-signature-canvas git 地址 代码示例 import React, { Component } from react import { createRoot } from react-dom/clientimport SignaturePad from ../../src/index.tsximport * as styles from ./styles.module.cssclass App extends Component {state { trimmed…...
004:ABBYY PDF Transformer安装教程
引言:本文主要讲解。 一、软件介绍 ABBYY PDF Transformer由ABBYY公司出品,属于一款家庭及商业都适用的PDF文档转换工具。它结合了ABBYY的OCR(光学字符识别)技术和Adobe PDF库技术,以确保能够便捷地处理任何类型的PDF…...
FlinkSQL之temporary join开发
在实时开发中,双流join获取目标对应时刻的属性时,经常使用temporary join。笔者在流量升级的实时迭代中,需要让流量日志精准的匹配上浏览时间里对应的商品属性,使用temporary join开发过程中踩坑不少,将一些经验沉淀在…...
第二十六节 直方图均衡化
图像直方图均衡化 图像直方图均衡化可以增强图像增强,对输入图像进行直方图均衡化处理,提升后续对象检测的准确率在Opencv人脸检测的代码演示中已经很常见了,此外对医学影像图像与卫星遥感图像也经常通过直方图均衡化来提升图像质量 Opencv…...
工单管理用什么工具好?8款推荐清单
本文推荐的8款项目工单管理系统有:1. PingCode; 2.Worktile; 3.Teambition; 4.致远OA; 5.TAPD; 6.Gitee; 7.Wrike; 8.Trello。 很多企业在处理项目工单时,依然依赖电子邮件、Excel表格,甚至是手动记录。这样做不仅效率低下,还容易导致工单遗漏…...
工地安全新突破:AI视频监控提升巡检与防护水平
在建筑工地和其他劳动密集型行业,工人的安全一直是管理工作的重中之重。为了确保工地的安全管理更加高效和智能化,AI视频监控卫士。通过人工智能技术,系统不仅能实时监控,还能自动识别工地现场的安全隐患,为工地管理者…...
World of Warcraft [CLASSIC][80][the Ulduar]
Ulduar 奥杜尔副本介绍 奥杜尔共计14个BOSS,通常说的10H就是10个苦难模式就是全通,9H就是除了【观察者奥尔加隆】,特别说明开启【观察者奥尔加隆】,是需要打掉困难模式4个守护者的。 所以人们经常说的类似“10H 观察者”、“10H…...
python实现数据库的增删改查功能,图形化版本
import tkinter from tkinter import * import psycopg2 from tkinter import messagebox#连接信息 t_conn{"dbname": "d1","user": "u1","password": "123qqq...A","port": "15400","h…...
pipeline开发笔记
pipeline开发笔记 jenkins常用插件Build Authorization Token Root配置GitLab的webhooks(钩子)配置构建触发器--示例 piblish over sshBlue OceanWorkspace Cleanup PluginGit插件PipelineLocalization: Chinese (Simplified) --中文显示Build Environment Plugin 显示构建过程…...
spark读取parquet文件
源码 parquet文件读取的入口是FileSourceScanExec,用parquet文件生成对应的RDD 非bucket文件所以走createNonBucketedReadRDD方法。 createNonBucketedReadRDD 过程: 确定文件分割参数 openCostInBytes4M 相关参数spark.sql.files.openCostInBytes4M…...
redis详细教程(1.String类型)
Redis 的 String 类型内部使用了一种叫做 SDS(Simple Dynamic String)的结构。SDS 的设计比传统的 C 语言字符串更加高效和安全,主要特点如下: 头部信息:SDS 的头部包含了一些元数据,比如字符串的长度、剩…...
用友U8接口-库存管理(7)
概括 本文的操作需要正确部署U8API主要讲述库存管理接口的使用,以产成品入库单作为说明,其他单据接口都是大同小异的!许多时候先在ERP做个单,然后仿造ERP单据参数,构造接口JSON参数是不错的做法。 获取Token访问令牌…...
Spring Boot HikariCP数据库连接池入门
1. 概述 在我们的项目中,数据库连接池基本是必不可少的组件。在目前数据库连接池的选型中,主要是 Druid ,为监控而生的数据库连接池。HikariCP ,号称性能最好的数据库连接池。 至于怎么选择,两者都非常优秀&#x…...
Docker快速上手教程:MacOS系统【安装/配置/使用/原理】全链路速通
背景 最近换了个 Macbook Air M3, 写个人项目需要用到 Docker,配置过程有一点点坎坷,还是得记录下避免重蹈覆辙。 什么。为什么是买 Air 而不是 Pro Max? 因为码农的钱也是钱啊。 这里我不会先讲原理,我认为工程的事情都是先看到现象,有了概念的轮廓,才应该去研究原理,…...
【JavaSE】认识String类,了解,进阶到熟练掌握
#1024程序员节 | 征文# 下面就让博主带领大家一起解决心中关于String类的疑问吧~~~ 1.字符串构造: 第一种和第二种(有一定的区别,在常量池上) public static void main(String[] args) { // 使用常量串构造 String s1 "h…...
vue3 vben-admin 窗口大小更改后 echarts尺寸变为 100px的问题
问题描述: 当切换切换tab 并且窗口尺寸更改时, echarts的尺寸因为父元素为 0, 自动设置为 100px 网上查找资料的结果: 1,使用vue 中的 v-if 来重新设置dom树 缺点: 频繁操作dom树结构, 极其消耗性能 优点: 自适应展示 2,设置固定宽高 缺点: 不能自适应展示, 无需消耗额外…...
Web应用框架-Django应用基础(3)-Jinja2
1.创建姓名模板 username里的数据发生改变,页面中渲染的数据发生改变,该效果称为动态数据 #hello/views:def hello_user(request):username000html <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8&quo…...
js(深浅拷贝,节流防抖,this指向,改变this指向的方法)
一、深浅拷贝 1.基本数据类型和引用数据类型的区别: 1. 基本数据类型的变量存储的是值 引用数据类型的变量存储的是地址值 2. 基本数据类型的变量存储的值在栈内存 引用数据类型的变量存储的值在堆内存 3. 基本数据类型的变量存储的是值和值之间相互不影响 引用数据…...
香橙派5(RK3588)使用npu加速yolov5推理的部署过程
香橙派5使用npu加速yolov5推理的部署过程 硬件环境 部署过程 模型训练(x86主机) 在带nvidia显卡(最好)的主机上进行yolo的配置与训练, 获取最终的best.pt模型文件, 详见另一篇文档 模型转换(x86主机) 下载airockchip提供的yolov5(从pt到onnx) 一定要下这个版本的yolov5, …...
基于MWORKS的蓝桥杯「智能装备数字化建模大赛」正式发布,首期培训本周六开启
为强化装备数字化人才培养,推动装备数字化技术快速发展,第十六届蓝桥杯全国软件和信息技术专业人才大赛设置专项赛暨智能装备数字化建模大赛,使用MWORKS作为参赛软件。关于参赛软件授权、技术支持与培训、教材与案例开发支持、成果转化培训及…...
021、深入解析前端请求拦截器
目录 深入解析前端请求拦截器: 1. 引言 2. 核心实现与基础概念 2.1 基础拦截器实现 2.2 响应拦截器配置 3. 实际应用场景 3.1 完整的用户认证系统 3.2 文件上传系统 3.3 API请求缓存系统 3.4 请求重试机制 3.5 国际化处理 4. 性能优化实践 4.1 请求合并…...
windows中的tracert命令
在 Windows 操作系统中,tracert(全称 Trace Route)是一个用于确定 IP 数据包到达目标主机所经过的路径的命令行工具。它通过发送具有不同生存时间(TTL)的 ICMP(Internet Control Message Protocolÿ…...
【玩儿】Java 数字炸弹小游戏(控制台版)+ IO 数据存储
Java 数字炸弹小游戏(控制台版) IO 数据存储 数字炸弹小游戏概述功能实现实体类User.java 玩家信息实体类GameRecode.java 游戏记录实体类 自定义异常AccountLockedException.java 账号锁定异常PasswordErrorException.java 密码错误异常UnknowAccountEx…...
直播平台网站建设/太原网站建设方案咨询
当我打算将数据处理成二维形式时,我这么做的 x [] list [] for i in range(num):x.clear()for j in range(len(data[i])):x.append(data[i][j])list.append() 但是结果却是这种形式 [[xxxx],[xxxx]......] 也就是说 list里面的小列表完全是重复的(…...
wordpress模板在哪/个人网站备案
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则表明,这三个要素最多只能同时…...
net服装网站建设/app注册推广平台
SMOD : SAPLV01Z 函数: EXIT_SAPLV01Z_002 在MIGO界面,当批次属性没有值的时候,会进入这个增强,当有值的时候,不走此段逻辑。 以下是样例代码 *&---------------------------------------------------------------------* …...
中英语网站制作方法/湖南长沙今日疫情
在使用java数组之前必须先对数组对象进行初始化,当数组所有元素都被分配了合适的内存空间,并指定了初始值时,数组初始化完成,即数组元素的内存空间非配结束,程序以后将不能重新改变数组对象在内存中的位置和大小。 jav…...
做网站go和python/微信群推广
笔记:1、if(no % 5) 括号内不为0则执行。2、运算符的运算顺序最小,比还小3、a > b ? c : d只有条件运算符属于三目运算符,其他都是单目或两目4、如果前后没有关系,不能用else if 只能用if5、%s,打印字符串练习题3-…...
网站建设日程表/网络营销的方式都有哪些
概述 在MVC中,Controller用来处理和回应用户的交互,选择使用哪个View来进行显示,需要往视图中传递什么样的视图数据等。ASP.NET MVC Framework中提供了IController接口和Controller基类两种类型,其中在Controller提供了一些MVC中常…...