☰
ROS2 Humble + Gazebo 仿真:SLAM 建图与 Nav2 导航全链路实战
2026/10/29 19:22:01 网站建设 项目流程

1. 从一台扫地机器人说起:为什么我要跑通这条全链路

去年年底我接手了一个小项目,需求说起来很简单:让一台差速轮式机器人(底盘结构跟主流扫地机几乎一样)在未知的室内环境里自己跑一圈,边跑边建图,建完图之后能自己规划路径、绕开障碍、回到充电桩。听起来是不是特别像扫地机器人的日常?但真动手做的时候,我发现从点云数据到一张能用的栅格地图,再到 Nav2 真正把路径走稳,中间隔着一堆坑。

这个项目我前后折腾了大概三周,中间推倒重来过两次。第一次是因为雷达和 IMU 的外参没标定好,建出来的图墙壁是弯的;第二次是因为 Nav2 的代价地图参数没调,机器人贴着墙走的时候疯狂画龙。最后跑通的那一刻,看着 RViz2 里机器人沿着规划路径平滑地绕开桌子腿,那种感觉比打游戏通关还爽。

这篇文章我想把整条链路完整地拆一遍:从 ROS2 环境搭建、Gazebo 仿真环境配置、LiDAR 与 IMU 的标定、SLAM 建图、地图保存与后处理,一直到 Nav2 的配置与调参。适合谁看?如果你已经装好了 ROS2,能跑通 turtlebot3 的基础例程,但一到自己搭车、自己配导航就卡壳,那这篇就是写给你的。如果你是完全的新手,也没关系,我会把每个关键参数为什么这么设讲清楚,你照着抄作业也能跑起来。

整条链路的核心关键词就几个:SLAM、Nav2、ROS2、LiDAR、Gazebo。我会用一台仿真扫地机器人作为载体,把每个环节的原理、操作、参数、坑点都过一遍。文章会比较长,建议收藏后对着电脑一步步操作。

2. 整体方案设计与技术选型思路

2.1 为什么选 ROS2 Humble 而不是 ROS1

先说版本选择这件事。ROS1 Noetic 是 2025 年 5 月 EOL 的,虽然现在还有很多教程在讲,但新项目真没必要再往 ROS1 上投入了。ROS2 Humble 是 2022 年发布的 LTS 版本,支持到 2027 年,生态已经相当成熟,Nav2、slam_toolbox、robot_localization 这些关键包都有稳定的 Humble 版本。

我实测下来,Humble 在 Ubuntu 22.04 上的安装体验比早期版本好太多,ros-humble-desktop一个命令就能把 RViz2、rqt、demo 节点全装上。Gazebo 方面,Humble 默认搭配的是 Gazebo Classic 11,虽然 Gazebo Sim(也就是 Ignition)是未来方向,但 Classic 的教程和模型库更全,对新手更友好。如果你用的是 Ubuntu 24.04,那对应的是 ROS2 Jazzy,Gazebo 也换成了 Gazebo Sim,配置方式会有差异,这个后面会提。

提示:如果你还在用 Ubuntu 20.04,建议直接重装系统到 22.04。ROS2 Humble 在 20.04 上虽然能装,但很多二进制包不完整,编译起来很痛苦。

2.2 仿真优先:为什么不在真车上调

很多人一上来就想在真车上跑,我的建议是先在 Gazebo 里把整条链路跑通。原因有三个:第一,仿真环境可以随时重置,撞墙了、翻车了、参数调崩了,重启一下就行,真车撞一次可能就几百块;第二,Gazebo 可以精确输出真值(ground truth),你能直接对比 SLAM 建出来的图和真实环境的差距,这在真车上很难做到;第三,仿真里调好的参数,迁移到真车上只需要微调,因为传感器模型和运动学模型是一致的。

当然仿真也有局限,比如雷达噪声模型和真实激光雷达有差异,轮子打滑、地面摩擦这些在仿真里很难完全复现。所以我的流程是:Gazebo 里跑通 -> 录 bag -> 用 bag 反复调 SLAM 和 Nav2 参数 -> 再上真车微调。这样效率最高。

2.3 传感器配置:2D 雷达还是 3D 雷达

这是很多人纠结的点。标题里提到了 LiDAR,但没说清是 2D 还是 3D。我的建议是:如果你的机器人只在单层平面移动,2D 雷达足够。扫地机器人就是典型场景,它不需要知道桌子底下有多高,只需要知道平面上哪里有障碍。

但如果你要做八叉树地图(OctoMap)导航,或者机器人有机械臂需要避障,那就得上 3D 雷达。3D 雷达的点云数据量大,对 SLAM 算法和算力要求更高。我这次用的是 2D 雷达做 SLAM,同时用深度相机做局部避障的补充,这样兼顾了建图精度和算力开销。

传感器方案适用场景算力需求建图效果
2D LiDAR单层平面移动机器人低栅格图清晰,够用
3D LiDAR多层、复杂地形高3D 点云,可转八叉树
2D LiDAR + 深度相机平面移动 + 局部避障中栅格图 + 局部代价地图

2.4 SLAM 方案选型:slam_toolbox 还是 Cartographer

ROS2 里主流的 2D SLAM 方案有两个:slam_toolbox 和 Cartographer。Cartographer 建图质量确实好,尤其是回环检测,但它在 ROS2 上的维护不如 slam_toolbox 活跃,编译依赖也多。slam_toolbox 是 ROS2 官方推荐的,支持在线建图和离线建图两种模式,参数相对好调,和 Nav2 的集成也更顺。

我这次选的是 slam_toolbox 的在线异步模式(online_async),因为它对算力要求低,适合扫地机器人这种嵌入式平台。如果你追求极致建图质量,可以用离线模式,先录 bag 再慢慢建。

3. 环境搭建与仿真世界配置

3.1 ROS2 Humble 安装与常见报错处理

安装 ROS2 Humble 的官方步骤我就不复制了,网上教程很多。这里只说几个我踩过的坑。

第一个坑是软件源。很多人执行apt update的时候会遇到获取:1 http://packages.ros.org/ros2/ubuntu jammy InRelease [4,682 B] 错误:1这种报错。这通常是网络问题或者源地址配置错误。我的做法是换成国内镜像源,比如清华或者中科大的 ROS2 镜像,速度会快很多,也不会中途断连。

第二个坑是 locale 设置。ROS2 对 locale 有要求,如果没设成 UTF-8,编译的时候会报一堆奇怪的错。安装前先执行:

sudo apt update && sudo apt install locales sudo locale-gen en_US en_US.UTF-8 sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 export LANG=en_US.UTF-8

第三个坑是rosdep init失败。这个命令会去访问 raw.githubusercontent.com,国内网络经常连不上。解决办法是手动创建源文件,或者用 rosdepc 这个国内替代工具。我一般直接手动配置:

sudo mkdir -p /etc/ros/rosdep/sources.list.d sudo curl -o /etc/ros/rosdep/sources.list.d/20-default.list https://mirrors.tuna.tsinghua.edu.cn/github-raw/ros/rosdistro/master/rosdep/sources.list.d/20-default.list

装完之后验证一下:

source /opt/ros/humble/setup.bash ros2 run demo_nodes_cpp talker

另开一个终端跑 listener,能看到消息就说明装好了。

3.2 Gazebo 仿真环境搭建:从零建一个房间

Gazebo 里建环境有两种方式:直接用现成的 world 文件,或者自己用 Building Editor 画。我建议新手先用现成的,比如 turtlebot3 的 world 文件,跑通之后再自己建。

自己建环境的话,我习惯用 Gazebo 的 Building Editor 画墙和家具,然后导出成.world文件。这里有个细节:墙的厚度不要设得太薄,否则雷达打上去点云很稀疏,SLAM 容易丢。我一般设 0.15 米厚,高度 2.5 米。

world 文件里还需要配置物理引擎参数。默认的 ODE 引擎在机器人快速转向时容易抖动,可以调一下:

<physics type="ode"> <max_step_size>0.001</max_step_size> <real_time_factor>1.0</real_time_factor> <real_time_update_rate>1000</real_time_update_rate> </physics>

max_step_size设小一点,仿真更稳定,但计算量会增大。0.001 是常用值,如果电脑卡可以调到 0.002。

3.3 机器人模型:URDF 还是 SDF

Gazebo 原生用的是 SDF 格式,但 ROS2 生态里 URDF 更通用,因为 robot_state_publisher、RViz2 这些工具都认 URDF。我的做法是写 URDF,然后用gazebo_ros的插件把 URDF 转成 SDF 加载到 Gazebo 里。

URDF 里几个关键部分:base_link是底盘,laser_link是雷达安装位置,imu_link是 IMU 位置。雷达和 IMU 的安装位置一定要和实际一致,否则后面标定会出问题。我一般把雷达装在底盘正上方中心,IMU 装在底盘几何中心,这样外参简单。

差速驱动的插件配置:

<gazebo> <plugin name="diff_drive" filename="libgazebo_ros_diff_drive.so"> <ros> <namespace>/</namespace> </ros> <left_joint>left_wheel_joint</left_joint> <right_joint>right_wheel_joint</right_joint> <wheel_separation>0.3</wheel_separation> <wheel_diameter>0.1</wheel_diameter> <max_wheel_torque>20</max_wheel_torque> <max_wheel_acceleration>1.0</max_wheel_acceleration> <command_topic>cmd_vel</command_topic> <odometry_topic>odom</odometry_topic> <odometry_frame>odom</odometry_frame> <robot_base_frame>base_footprint</robot_base_frame> </plugin> </gazebo>

wheel_separation是左右轮间距,wheel_diameter是轮子直径,这两个参数直接影响里程计精度,一定要量准。我见过有人把轮径写错,结果机器人走一米,odom 显示走了两米,SLAM 建图直接废掉。

3.4 雷达与 IMU 插件配置

2D 雷达用libgazebo_ros_ray_sensor.so插件:

<gazebo reference="laser_link"> <sensor type="ray" name="laser"> <pose>0 0 0 0 0 0</pose> <visualize>true</visualize> <update_rate>10</update_rate> <ray> <scan> <horizontal> <samples>360</samples> <resolution>1</resolution> <min_angle>-3.14159</min_angle> <max_angle>3.14159</max_angle> </horizontal> </scan> <range> <min>0.12</min> <max>8.0</max> <resolution>0.01</resolution> </range> </ray> <plugin name="laser_controller" filename="libgazebo_ros_ray_sensor.so"> <ros> <remapping>~/out:=scan</remapping> </ros> <output_type>sensor_msgs/LaserScan</output_type> <frame_name>laser_link</frame_name> </plugin> </sensor> </gazebo>

samples是每圈采样点数,360 表示 1 度一个点,够用了。update_rate设 10Hz,和真实雷达一致。min和max是测距范围,扫地机器人一般 0.1 到 8 米。

IMU 插件用libgazebo_ros_imu_sensor.so,配置里要注意噪声参数。Gazebo 默认的 IMU 是理想值,没有噪声,这会导致 SLAM 里的 IMU 融合效果过于完美,迁移到真车就崩。我一般手动加一点高斯噪声:

<plugin name="imu_plugin" filename="libgazebo_ros_imu_sensor.so"> <ros> <remapping>~/out:=imu</remapping> </ros> <initial_orientation_as_reference>false</initial_orientation_as_reference> <frame_name>imu_link</frame_name> </plugin>

4. LiDAR 与 IMU 标定:建图不弯的关键

4.1 为什么必须标定

很多人觉得仿真里不需要标定,因为雷达和 IMU 的位置都是自己设的,理论上外参已知。但实际跑起来你会发现,如果 URDF 里雷达的pose和 Gazebo 插件里的pose不一致,或者 TF 树里laser_link到base_link的变换有偏差,建出来的图就会歪。

更关键的是,IMU 和雷达的时间戳如果不同步,SLAM 前端做点云匹配的时候会引入误差。仿真里时间戳是 Gazebo 统一发的,一般没问题,但真车上雷达和 IMU 是独立设备,时间同步必须做。

4.2 外参标定:手眼标定思路

外参标定就是求雷达坐标系到 IMU 坐标系的旋转和平移。仿真里我们可以直接读 URDF 里的值,但为了验证,我一般会跑一遍标定流程。

方法很简单:让机器人原地旋转一圈,记录雷达点云和 IMU 的角速度。如果外参正确,雷达点云匹配出来的旋转量应该和 IMU 积分出来的旋转量一致。如果差很多,说明外参有问题。

具体操作:

# 启动仿真 ros2 launch my_robot bringup.launch.py # 另开终端,让机器人原地转 ros2 topic pub /cmd_vel geometry_msgs/msg/Twist "{angular: {z: 0.5}}" # 录 bag ros2 bag record /scan /imu /tf /tf_static

录完之后用ros2 bag play回放,在 RViz2 里看雷达点云和 IMU 的朝向。如果点云匹配的旋转中心和 IMU 的 Z 轴不一致,就要调 URDF 里的laser_link位置。

4.3 时间同步:仿真与真车的差异

仿真里所有传感器共用 Gazebo 的时钟,时间戳天然同步。真车上就不一样了,雷达走网口,IMU 走串口,时间戳来源不同。我的做法是用message_filters做时间戳对齐,或者用 PTP 协议做硬件同步。

ROS2 里可以用ros2 topic hz看各个话题的频率,如果雷达 10Hz、IMU 100Hz,那 SLAM 里做融合的时候要注意降采样。slam_toolbox 默认只用雷达,不用 IMU,所以这个问题在纯 2D SLAM 里不突出。但如果你用 robot_localization 做 EKF 融合,时间同步就必须处理。

注意:仿真里 IMU 的initial_orientation_as_reference设成 false,否则 IMU 会把初始朝向当成零,导致和雷达的朝向对不上。

5. SLAM 建图:从点云到栅格地图

5.1 slam_toolbox 配置详解

slam_toolbox 的配置文件是.yaml,关键参数我列一下:

slam_toolbox: ros__parameters: solver_plugin: solver_plugins::CeresSolver ceres_linear_solver: SPARSE_NORMAL_CHOLESKY ceres_preconditioner: SCHUR_JACOBI ceres_trust_strategy: LEVENBERG_MARQUARDT ceres_dogleg_type: TRADITIONAL_DOGLEG ceres_loss_function: None odom_frame: odom map_frame: map base_frame: base_footprint scan_topic: /scan mode: mapping debug_logging: false throttle_scans: 1 transform_publish_period: 0.02 map_update_interval: 5.0 resolution: 0.05 max_laser_range: 8.0 minimum_time_interval: 0.5 transform_timeout: 0.2 tf_buffer_duration: 30.0 stack_size_to_use: 40000000 enable_interactive_mode: true

几个关键参数解释:

  • resolution: 0.05表示栅格地图每个格子 5 厘米。这个值越小地图越精细,但内存占用越大。扫地机器人用 5 厘米足够。
  • map_update_interval: 5.0表示每 5 秒更新一次地图。如果机器人移动快,可以调小到 2 秒。
  • minimum_time_interval: 0.5表示两次扫描匹配的最小时间间隔。雷达 10Hz 的话,理论上 0.1 秒一帧,但 slam_toolbox 默认会降采样,0.5 秒一帧够用。
  • max_laser_range: 8.0要和雷达的实际量程一致,设大了会把噪声当障碍。

5.2 建图实操:启动顺序与 RViz2 配置

启动顺序很重要,我一般这样:

# 终端1:启动 Gazebo 和机器人 ros2 launch my_robot gazebo.launch.py # 终端2:启动 slam_toolbox ros2 launch slam_toolbox online_async_launch.py params_file:=./slam_params.yaml # 终端3:启动 RViz2 rviz2 -d ./slam.rviz

RViz2 里要加几个显示:LaserScan(话题 /scan)、Map(话题 /map)、TF、RobotModel。Fixed Frame 设成 map。

然后启动键盘控制:

ros2 run teleop_twist_keyboard teleop_twist_keyboard

控制机器人慢慢走一圈,速度不要超过 0.3 m/s,角速度不要超过 0.5 rad/s。走太快雷达点云匹配跟不上,地图会糊。

5.3 建图质量判断与常见问题

建图过程中要盯着 RViz2 看几点:

第一,墙壁是不是直的。如果墙壁是弯的,说明里程计或者外参有问题。第二,回环有没有闭合。走回起点的时候,如果地图上的墙壁能对上,说明回环检测成功。第三,有没有重影。如果同一面墙出现两条线,说明位姿估计漂移了。

常见问题排查表:

现象可能原因解决办法
墙壁弯曲里程计标定不准重新标定轮径和轮距
地图重影雷达外参偏差检查 TF 树 laser_link 位置
回环不闭合回环检测参数太严调小 loop_search_maximum_distance
地图有空洞雷达量程不够调大 max_laser_range
建图卡顿算力不足调大 map_update_interval

5.4 地图保存与后处理

建完图之后保存:

ros2 run nav2_map_server map_saver_cli -f ./my_map

会生成my_map.pgm和my_map.yaml两个文件。pgm 是灰度图,yaml 是元数据。

后处理我一般做两件事:一是用图像编辑工具把噪声点擦掉,二是调整 yaml 里的origin和resolution。origin是地图左下角在 map 坐标系里的位置,如果建图起点不在原点,这个值要改。

image: my_map.pgm resolution: 0.05 origin: [-10.0, -10.0, 0.0] negate: 0 occupied_thresh: 0.65 free_thresh: 0.196

occupied_thresh是判定为障碍的阈值,0.65 表示像素值大于 65% 认为是障碍。free_thresh是判定为空闲的阈值。这两个值根据建图效果微调。

6. Nav2 导航配置与调参实战

6.1 Nav2 架构与核心插件

Nav2 的架构比 ROS1 的 move_base 复杂得多,它是基于行为树(Behavior Tree)的。核心节点包括:

  • bt_navigator:行为树导航器,负责调度整个导航流程。
  • planner_server:全局路径规划,默认用 NavFn 或 Smac。
  • controller_server:局部路径跟踪,默认用 DWB 或 RPP。
  • recoveries_server:恢复行为,比如原地旋转、后退。
  • costmap_2d:代价地图,分全局和局部两层。

行为树的好处是灵活,你可以自定义导航逻辑,比如“先尝试规划,失败就旋转,再失败就后退”。但坏处是配置复杂,新手容易懵。

6.2 代价地图参数:让机器人不撞墙

代价地图是 Nav2 里最容易出问题的地方。全局代价地图用静态地图 + 膨胀层,局部代价地图用雷达实时数据 + 膨胀层。

关键参数:

global_costmap: global_costmap: ros__parameters: update_frequency: 1.0 publish_frequency: 1.0 global_frame: map robot_base_frame: base_footprint robot_radius: 0.22 resolution: 0.05 plugins: ["static_layer", "obstacle_layer", "inflation_layer"] inflation_layer: plugin: "nav2_costmap_2d::InflationLayer" cost_scaling_factor: 3.0 inflation_radius: 0.55

robot_radius是机器人半径,扫地机器人一般 0.2 到 0.25 米。inflation_radius是膨胀半径,一般设成机器人半径的 2 到 2.5 倍。cost_scaling_factor控制代价衰减速度,值越大衰减越快,机器人越敢贴墙走。

我踩过的坑:inflation_radius设太小,机器人贴着墙走,雷达噪声一抖就撞墙;设太大,窄门口过不去。我的经验是,门宽 0.8 米的话,inflation_radius不要超过 0.4 米。

6.3 全局规划器与局部控制器选型

全局规划器我推荐 Smac 2D,它比 NavFn 更灵活,支持自定义代价函数。配置:

planner_server: ros__parameters: expected_planner_frequency: 20.0 planner_plugins: ["GridBased"] GridBased: plugin: "nav2_smac_planner/SmacPlanner2D" tolerance: 0.125 downsample_costmap: false allow_unknown: true max_iterations: 1000000 max_on_approach_iterations: 1000 max_planning_time: 2.0 motion_model_for_search: "MOORE"

局部控制器我推荐 RPP(Regulated Pure Pursuit),它比 DWB 好调,适合差速机器人:

controller_server: ros__parameters: controller_frequency: 20.0 min_x_velocity_threshold: 0.001 min_y_velocity_threshold: 0.5 min_theta_velocity_threshold: 0.001 controller_plugins: ["FollowPath"] FollowPath: plugin: "nav2_regulated_pure_pursuit_controller::RegulatedPurePursuitController" desired_linear_vel: 0.3 lookahead_dist: 0.4 min_lookahead_dist: 0.2 max_lookahead_dist: 0.6 lookahead_time: 1.5 rotate_to_heading_angular_vel: 1.0 transform_tolerance: 0.1 use_velocity_scaled_lookahead_dist: true min_approach_linear_velocity: 0.05 approach_velocity_scaling_dist: 0.6 use_collision_detection: true max_allowed_time_to_collision_up_to_carrot: 1.0

desired_linear_vel是期望线速度,扫地机器人 0.3 m/s 比较合适。lookahead_dist是前视距离,太小机器人会画龙,太大切弯不及时。我一般设成 0.4 米,然后开use_velocity_scaled_lookahead_dist,让前视距离随速度自适应。

6.4 行为树配置与恢复行为

Nav2 默认的行为树是navigate_to_pose_w_replanning_and_recovery.xml,包含规划、控制、恢复三个主要分支。我一般会改一下恢复行为,加上“清除代价地图”和“等待”:

<RecoveryNode number_of_retries="6" name="NavigateRecovery"> <SequenceStar name="RecoveryActions"> <ClearEntireCostmap name="ClearLocalCostmap" service_name="local_costmap/clear_entirely_local_costmap"/> <ClearEntireCostmap name="ClearGlobalCostmap" service_name="global_costmap/clear_entirely_global_costmap"/> <Spin name="Spin" spin_dist="1.57"/> <Wait name="Wait" wait_duration="5.0"/> <BackUp name="BackUp" backup_dist="0.3" backup_speed="0.1"/> </SequenceStar> </RecoveryNode>

恢复行为的顺序很重要。先清代价地图,因为很多时候规划失败是因为代价地图里有残留障碍;然后旋转,尝试重新观测;再等待,给传感器一点时间;最后后退,脱离困境。

6.5 导航实操:从起点到目标点

启动导航:

# 终端1:启动 Gazebo 和机器人 ros2 launch my_robot gazebo.launch.py # 终端2:启动 Nav2 ros2 launch nav2_bringup bringup_launch.py map:=./my_map.yaml params_file:=./nav2_params.yaml # 终端3:启动 RViz2 rviz2 -d ./nav2.rviz

RViz2 里用“2D Pose Estimate”设初始位姿,用“2D Goal Pose”设目标点。机器人应该会自己规划路径并走过去。

如果机器人不动,先看/cmd_vel有没有输出,再看bt_navigator的日志。常见问题是 TF 树不完整,或者代价地图没更新。

7. 常见问题与排查技巧实录

7.1 建图与导航高频问题速查

问题排查方向解决方法
机器人不动/cmd_vel 无输出检查 controller_server 是否启动
规划失败代价地图全黑检查静态地图和雷达数据
定位丢失TF map->odom 跳变重启 SLAM 或 AMCL
机器人画龙前视距离太小调大 lookahead_dist
撞墙膨胀半径太小调大 inflation_radius
窄门过不去膨胀半径太大调小 inflation_radius
导航超时规划时间太短调大 max_planning_time

7.2 我踩过的三个大坑

第一个坑:TF 树断裂。有一次导航怎么都不动,查了半天发现是base_footprint到base_link的 TF 没发。原因是 URDF 里写了base_footprint,但robot_state_publisher没启动。解决办法是在 launch 文件里确保robot_state_publisher和joint_state_publisher都启动了。

第二个坑:代价地图不更新。局部代价地图的update_frequency设成了 0.5Hz,机器人走快了之后障碍物位置对不上,直接撞上去。改成 5Hz 之后就好了。但频率也不能太高,否则 CPU 扛不住。

第三个坑:AMCL 定位跳变。用 AMCL 做定位的时候,如果初始位姿设得不准,粒子滤波会收敛到错误的位置。我的做法是先用 RViz2 的“2D Pose Estimate”给一个大概的初始位姿,然后让机器人原地旋转一圈,让 AMCL 充分观测。

7.3 性能优化:让老电脑也能跑

Gazebo 和 Nav2 都很吃 CPU。如果你的电脑配置一般,可以这样优化:

  • Gazebo 里把real_time_update_rate从 1000 降到 500。
  • 雷达update_rate从 10Hz 降到 5Hz。
  • Nav2 的controller_frequency从 20Hz 降到 10Hz。
  • 关掉 RViz2 里不必要的显示,比如 PointCloud2。

我实测下来,这些调整能让帧率从 15fps 提到 30fps,导航流畅度明显提升。

8. 从仿真到真车的迁移经验

仿真跑通之后,上真车之前有几件事必须做。第一,重新标定轮径和轮距,真车的轮子有磨损,和 URDF 里的值会有偏差。第二,重新标定雷达外参,真车雷达安装位置不可能和仿真完全一致。第三,检查时间同步,真车雷达和 IMU 的时间戳要对齐。

真车上我遇到的最大问题是雷达噪声。仿真里雷达是理想值,真车雷达在玻璃、镜面、黑色物体上会丢点。解决办法是在 SLAM 里加一个滤波,把距离突变超过阈值的点去掉。slam_toolbox 里可以调max_laser_range和minimum_time_interval来缓解。

另一个问题是地面打滑。仿真里轮子不会打滑,真车在地毯上转向会打滑,导致里程计漂移。我的做法是用 IMU 的角速度做融合,robot_localization 的 EKF 节点可以配置成用 IMU 的 yaw 角速度修正里程计。

最后再分享一个小技巧:如果你要录 bag 做离线调试,记得把/tf、/tf_static、/scan、/odom、/imu都录上。很多人只录/scan,回放的时候 TF 树不全,SLAM 根本跑不起来。录 bag 的时候用--all参数最省事,但文件会很大,我一般手动指定话题。

这个项目后续还可以扩展的方向:一是加视觉 SLAM,用深度相机做视觉里程计,和雷达做融合;二是加八叉树地图,做三维导航;三是用强化学习做局部路径规划,替代传统的 DWA 或 RPP。这些我还在折腾,等跑通了再写一篇。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询