☰
ROS2仿真导航实战:Gazebo+Cartographer+Nav2从建图到自主导航
2026/10/9 4:08:57 网站建设 项目流程

说实话,整个项目最开始的动机特别朴素:我想在 ROS2 里把一台仿真小车从零跑到自主导航,完整走通“Gazebo 建环境 → Cartographer 建图 → move_base(准确说是 ROS2 里的 Nav2 导航栈)领航”这条链路。结果一上手发现,网上教程各说各话,有的讲 ROS1 的 move_base,有的讲 ROS2 的 Nav2,还有一些教程直接默认你有 TurtleBot3 官方包,压根没解释清楚它们之间的关系。我前后折腾了大概两周,踩了不少坑,最后把 Gazebo + Cartographer + Nav2 在 Ubuntu 22.04 + ROS2 Humble 下完整跑通了。这篇文章就把我反复验证过的方案、参数和排错经验整理出来,给你一条可以直接照着抄的路。如果你正在做轮式机器人的仿真导航、毕设项目,或者刚把底盘逻辑从 ROS1 迁到 ROS2,这篇应该能帮你少走很多弯路。

1. 方案选型:为什么是 Gazebo + Cartographer + Nav2 这套组合

1.1 这套组合到底解决了什么问题

仿真导航开发的核心诉求是“硬件没到位,算法先验证”。Gazebo 提供物理环境和传感器仿真,Cartographer 负责把激光雷达数据变成占用栅格地图,Nav2 接管定位、全局规划、局部跟踪和速度输出。三者串联起来,你可以在不碰真实底盘的前提下,把一套完整的导航系统从“能建图”推到“能避障到达目标点”,这也是移动机器人开发里最标准的仿真先行路径。

这套链路里,每个环节承担的任务都很清晰:Gazebo 负责模拟传感器噪声和物理碰撞,Cartographer 负责把雷达扫描帧拼成可用于 Nav2 的地图,Nav2 里的 AMCL 负责定位,planner 和 controller 负责路径规划与跟踪。搞懂每个节点在干什么,比背下一堆命令重要得多。我后面排查问题时,几乎所有 bug 都是因为某个中间环节的数据流断了,而不是算法本身有问题。

1.2 版本选型:Ubuntu 22.04 + ROS2 Humble + Gazebo Classic 是最稳组合

如果你问我当前最推荐哪套版本搭配,答案很明确:Ubuntu 22.04 + ROS2 Humble + Gazebo Classic(也就是 Gazebo 11)。Humble 是 ROS2 里 LTS 长期支持版本,官方仓库二进制包齐全,网上遇到的问题也基本都能搜到解决方案。Gazebo Classic 在 Humble 时代虽然已经被 Ignition(现在叫 Gazebo 新版本)逐步取代,但机器人社区里大量旧教学、旧插件、旧 URDF 仍然基于它,和 Cartographer、Nav2 的兼容性也最成熟。

这里特别提醒一句:不要看到 Ubuntu 24.04 或者 ROS2 Jazzy 出来就急着上新版本。ROS2 Jazzy 默认的 Gazebo 已经是新版 Gazebo Fortress / Harmonic,很多旧插件接口变了,Cartographer 适配也有额外的编译工作量。仿真导航这种项目,核心价值在打通算法链路,不在追新工具链。

1.3 标题里的“movebase”在 ROS2 里到底是谁

这是很多人一开始最懵的地方:ROS1 里有个节点就叫 move_base,负责接收目标点、发布 cmd_vel,但 ROS2 里并没有一个叫 move_base 的单一节点。ROS2 的导航能力由 Navigation2(简称 Nav2)这套栈提供,它是由 map_server、amcl、planner_server、controller_server、bt_navigator、behavior_server 等多个节点组合成的。也就是说,标题里写的“movebase”导航,在 ROS2 语境下就是 Nav2 导航栈。

我用下面这个表格帮你把 ROS1 和 ROS2 的对应关系理清楚,后面看教程就不会被名词弄混:

功能角色ROS1 时代的实现ROS2 时代的实现
地图加载与提供map_servernav2_map_server
定位amclnav2_amcl
全局路径规划move_base 中的 global_plannernav2_planner
局部路径规划与速度控制move_base 中的 local_plannernav2_controller
行为调度(恢复、旋转、等待)move_base 内部 recovery_behaviorsnav2_behavior_tree
接收目标并调度整个任务move_base 主节点nav2_bt_navigator

理解了这张表,你在看启动日志时就清楚每个节点在干什么了。Nav2 不是把 move_base 换个名字,而是把原来一个大而全的节点拆成了多个可独立运行的组件,这样做的好处是你可以只替换其中一个模块,比如换掉局部规划器,不影响全局规划。

2. 仿真环境搭建:从零搞一台带激光雷达的小车

2.1 基础环境与依赖安装

我先假定你的 ROS2 Humble 已经装好了。如果还没装,按官方文档用 deb 方式安装即可,选 ros-humble-desktop 就够用,里面包含 Rviz2、turtlebot 相关仿真支持等基础工具。接下来需要补齐 Gazebo、Cartographer、Nav2 三块:

sudo apt update sudo apt install ros-humble-gazebo-ros-pkgs ros-humble-gazebo-ros sudo apt install ros-humble-gazebo-ros2-control ros-humble-gazebo-ros2-control-demos sudo apt install ros-humble-cartographer ros-humble-cartographer-ros sudo apt install ros-humble-navigation2 ros-humble-nav2-bringup sudo apt install ros-humble-tf2-tools ros-humble-teleop-twist-keyboard

Cartographer 在 Humble 的 apt 源里已经有了二进制,直接用起来问题不大。如果你是其他 ROS2 版本,大概率需要从源码编译 cartographer 和 cartographer_ros,那时候会多耗不少时间,这也是我推荐 Humble 的另一个原因:能少编译一个“老顽固”就少编译一个。

安装完成之后,记得给你的 shell 配置环境变量。我一般直接写进~/.bashrc:

source /opt/ros/humble/setup.bash export TURTLEBOT3_MODEL=burger

如果你后面要手动编译自己的功能包,再补上source ~/ros2_ws/install/setup.bash。

2.2 机器人的 URDF 模型:关键在坐标系和传感器插件

很多人卡在仿真环境搭建,其实是卡在 URDF 模型上。URDF 不只是画个外形,它直接决定了 TF 树结构,而 TF 树是 Cartographer 和 Nav2 能工作的地基。

我用的两轮差速小车模型,核心坐标系是这几层:

  • map:全局地图坐标系,由 Cartographer 或 AMCL 发布map -> odom
  • odom:里程计坐标系,由 Gazebo 的差速驱动插件发布odom -> base_footprint
  • base_footprint:底盘在地面的投影,通常作为机器人本体根坐标系
  • base_link:底盘中心。如果底盘是平面移动,base_footprint和base_link之间往往只是一个高度偏移,甚至可以合并
  • laser_link:激光雷达所在位置,通过固定关节挂在base_link上

这个坐标系设计有一个关键原则:odom到base_link之间的变换必须由里程计来源发布,不能写死在 URDF 里。因为里程计是随时间变化的动态 TF,而 URDF 只能声明固定关节。很多人直接在 URDF 里写了odom -> base_link的固定 joint,结果 Cartographer 一启动 TF 树就冲突,这是非常典型的报错。

URDF 里激光雷达那一节的写法,重点是在<gazebo>标签里挂上 ray 类型传感器插件,这样 Gazebo 才能在仿真里生成/scan话题:

<gazebo reference="laser_link"> <sensor name="laser_sensor" type="ray"> <always_on>true</always_on> <update_rate>20</update_rate> <visualize>true</visualize> <plugin filename="libgazebo_ros2_ray_sensor.so" name="gazebo_ros2_ray"> <ros> <remapping>sensor_msgs/msg/LaserScan:=scan</remapping> </ros> <ray> <scan> <horizontal> <samples>360</samples> <resolution>1</resolution> <min_angle>-3.141592653589793</min_angle> <max_angle>3.141592653589793</max_angle> </horizontal> </scan> <range> <min>0.10</min> <max>30.0</max> </range> </ray> </plugin> </sensor> </gazebo>

注意这里的<remapping>命名空间:它把真正的话题名映射成了/scan。如果你发现启动后/scan没有数据,先查是不是这个 remap 没生效,或者话题名拼写不一致。

底盘驱动我用的也是 Gazebo 官方插件libgazebo_ros2_diff_drive.so。它订阅/cmd_vel,输出/odom,同时发布odom -> base_footprint的 TF。下面是参数配置里容易被忽略的几个:

<plugin filename="libgazebo_ros2_diff_drive.so" name="diff_drive_controller"> <ros> <remapping>cmd_vel:=cmd_vel</remapping> <remapping>odometry:=odom</remapping> </ros> <update_rate>50</update_rate> <left_joint>left_wheel_joint</left_joint> <right_joint>right_wheel_joint</right_joint> <wheel_separation>0.16</wheel_separation> <wheel_diameter>0.066</wheel_diameter> <max_wheel_torque>20</max_wheel_torque> <max_wheel_acceleration>1.0</max_wheel_acceleration> </plugin>

wheel_separation和wheel_diameter必须和你的模型几何一致,否则建图和导航时机器人会做成一个“不匹配的预期运动”,导致轨迹乱转。max_wheel_acceleration 也不要给太大,仿真里加速度过猛会让雷达数据出现畸变,增加 Cartographer 匹配负担。

2.3 启动 Gazebo 后先验收什么

把小车模型写好、世界文件准备好之后,启动命令大致是:

ros2 launch gazebo_ros gazebo.launch.py world:=my_world.world ros2 run gazebo_ros spawn_entity.py -topic /robot_description -entity my_robot

启动完不要急着往下走,先花几分钟检查三件事:

第一,/scan话题是否有数据,频率是否正常。第二,/odom话题频率和数值是否合理。第三,tf2_echo能否打印出base_link -> laser_link的静态变换。如果这三项都正常,说明底盘、雷达、TF 三个地基稳了,才能开始建图。

我用一条命令快速看话题频率:

ros2 topic hz /scan ros2 topic hz /odom

正常情况下/scan的频率约等于你在插件里设置的 update_rate,也就是 20 Hz 左右;/odom在 50 Hz 左右。如果频率差别太大,优先检查 CPU 负载和插件 update_rate 设置。

3. Cartographer 建图实操:把激光数据变成能导航的地图

3.1 建图前必须理解的 Cartographer 工作逻辑

Cartographer 之所以比 Gmapping 稳健,是因为它不只是逐帧做粒子滤波匹配,而是维护了一张由“子图”组成的地图。每个激光帧先和当前子图做扫描匹配,同时约束位姿,子图积累到一定帧数后封存,再开新子图,最后通过后端做回环检测来优化整体位姿。

用生活化一点的说法:它不是在每帧里独立猜“我在哪”,而是把一段时间内走过的所有帧当成一个连环画册,一边画一边纠正前面歪掉的页。这使得 Cartographer 在回环明显的环境下建出来的图比 Gmapping 干净得多,对运动速度的要求也没那么苛刻。

但 Cartographer 也有一个代价:参数多,配置不细心很容易建出重影地图。你最需要关注的是它要求的三个坐标系:map、odom和tracking_frame。在我们这套方案里:

  • map是全局地图坐标
  • odom是里程计坐标
  • tracking_frame建议设为base_footprint,因为这是底盘位姿估计的基准

3.2 lua 配置文件里的关键参数

Cartographer 的配置集中在 lua 文件里,ROS2 版本同样不例外。我这边反复验证后认为最关键的是下面这几个:

tracking_frame = "base_footprint" map_frame = "map" odom_frame = "odom" provide_odom_frame = false use_odometry = true num_laser_scans = 1 num_subdivisions_per_laser_scan = 1

provide_odom_frame这个参数是坑最多的,这里一定要理解。因为我们的底盘插件已经在发布odom -> base_footprint的 TF 了,所以 Cartographer 的provide_odom_frame必须设为false,否则 Cartographer 也会尝试发布map -> odom,造成 TF 树里同一条边有两个发布者,直接报 TF 重复的错。

use_odometry = true的意思是 Cartographer 会把里程计当作预估依据,结合激光帧做匹配。对于两轮差速小车来说,开这个能让建图稳定不少。如果你用的是没有轮式里程计、只有 IMU 和雷达的平台,这里就要改成 false,并且要额外配置 IMU。

雷达方面,num_laser_scans表示使用几路二维激光,我们的模型只有一路,所以是 1。还有一个常见误解是有人把num_subdivisions_per_laser_scan调大,以为能提高精度,其实这个参数主要控制每帧扫描数据的稠密程度,在 2D 雷达上保持默认即可。

3.3 启动 Cartographer 并手动建图

Cartographer 在 ROS2 中启动需要同时拉起两个节点:cartographer_node负责计算,occupancy_grid_node负责把内部地图发布成 ROS 里的占用栅格地图,供 Rviz2 显示。

启动命令核心部分如下:

ros2 run cartographer_ros cartographer_node \ --ros-args \ -r __node:=cartographer_node \ --params-file config/cartographer_ros2.yaml ros2 run cartographer_ros occupancy_grid_node \ --ros-args \ -r __node:=occupancy_grid_node \ --params-file config/cartographer_ros2.yaml

说得更直白一点,我建议你直接用 launch 文件把这些都包起来,方便以后反复启动:

from launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ Node( package='cartographer_ros', executable='cartographer_node', name='cartographer_node', output='screen', parameters=[{'use_sim_time': True}], ), Node( package='cartographer_ros', executable='occupancy_grid_node', name='occupancy_grid_node', output='screen', parameters=[{'use_sim_time': True}], ), ])

建图时,一边开 Rviz2 显示/map,一边用键盘发速度指令:

ros2 run teleop_twist_keyboard teleop_twist_keyboard

建图的操作技巧,我自己的体会是“慢、直、闭环”三字诀。慢是指线速度控制在 0.2 m/s 以内,太快激光帧畸变明显;直是指转弯时尽量原地转,不要边转边走,减少累积误差;闭环是指一定要让机器人走回出发位置附近,这样 Cartographer 能触发回环优化,地图质量会明显提升。

地图基本成型后,保存:

ros2 run nav2_map_server map_saver_cli -f ~/map/my_map

这会生成my_map.pgm和my_map.yaml。my_map.yaml里默认分辨率是 0.05 米每像素,也就是 20 cm 一个栅格?不,是每像素 5 厘米。如果你觉得地图太密或者太粗,保存后可以手动改 yaml 里的resolution,但要注意 pgm 文件也得对应重采样,否则地图会被错位。所以我建议保存时就保持默认,后面不要轻易改分辨率。

4. Nav2 导航部署:让 move_base 在 ROS2 里真正跑起来

4.1 Nav2 各节点的启动顺序和依赖

Nav2 启动比 Cartographer 要复杂,因为参与节点多。初学者最容易犯的错是同时把所有节点一股脑拉起来,根本不看日志顺序。我建议按这个顺序逐个确认:map_server 先加载地图,amcl 开始定位,planner_server 启动后开始能发布全局路径,controller_server 启动后开始输出速度指令,最后 bt_navigator 才对外暴露导航目标点接口。

之前用的nav2_bringup提供了现成 launch 文件,可以直接用:

ros2 launch nav2_bringup bringup_launch.py map:=/home/xxx/map/my_map.yaml use_sim_time:=true

这个 launch 会一次性把所有 Nav2 节点都拉起来。对于第一次验证,省心不少。但是用默认配置前,必须做一件事:把nav2_bringup/params/nav2_params.yaml里机器人的尺寸和速度参数改成你自己小车的参数。

如果你不想直接用默认文件,也可以把自己修改好的 yaml 通过params_file参数传进去:

ros2 launch nav2_bringup bringup_launch.py \ map:=/home/xxx/map/my_map.yaml \ use_sim_time:=true \ params_file:=/home/xxx/config/custom_nav2_params.yaml

4.2 参数调整:速度、足迹、膨胀半径一个都不能省

Nav2 参数是 YAML 文件,按节点分组。我这边重点调整三个分组:

第一个是robot_base_frame。大多数小车用base_footprint,如果你在 URDF 里用的是base_link,这里必须改对应名字,不然 AMCL 和规划器找不到机器人在哪。这个参数在 amcl、planner_server、controller_server 三个节点里都要检查。

第二个是footprint。默认配置里写的是 TurtleBot3 的矩形足迹,如果你的模型是一台窄底盘小车,默认值会比实际大很多,导航时会出现“明明前方还能走,但代价地图判定过不去”的情况。我自己的小车设置如下:

footprint: [[-0.12, -0.10], [0.12, -0.10], [0.12, 0.10], [-0.12, 0.10]]

这个足迹要稍微比机器人外形放大一点点,但不能大太多,否则膨胀层会吃掉过多可行区域。

第三个是速度上限。Gazebo 差速驱动插件里你定义了物理速度上限,Nav2 这里的max_vel_x、max_vel_theta必须和它匹配。如果你的插件允许最大线速度 0.5 m/s,但 Nav2 控制器算出来的指令速度是 1.0 m/s,就会出现机器人一直追不上规划速度、抖动甚至打滑的现象。我在一个项目里就吃过这个亏,整车在仿真里呈现“鬼畜”前进。

controller_server: ros__parameters: max_vel_x: 0.5 min_vel_x: 0.0 max_vel_theta: 1.5 min_vel_theta: -1.5 accel_lim_x: 1.0 decel_lim_x: 1.0

4.3 在 Rviz2 里指定初始位姿和目标点

Nav2 跑起来以后,打开 Rviz2,添加 Map Display 选择话题/map,可以看到刚才保存的地图。但此时 AMCL 还不知道机器人在哪,所以界面上的机器人模型可能飘在地图外,或者显示的位置完全不对。

这一步大量新手会卡住:明明地图显示了,按了“2D Goal Pose”后机器人就是不动。原因通常是两点:第一,你没有先给 AMCL 一个初始位姿;第二,你给的目标点是在错误的 frame 下发布的。

正确的操作顺序是:先在 Rviz2 工具栏点 “2D Pose Estimate”,在地图上找到机器人实际所在的起点位置,按住左键拖出一个朝向,松手,等待激光点云和地图对上。然后才能点 “2D Goal Pose”,选目标点和最终朝向。

判断 AMCL 定位是否成功,看两个信号:一是在 Rviz2 里 LaserScan 的点云骨架是否贴合地图墙壁;二是在 Navigation2 面板上,是否有 Pose 的协方差椭圆缩小。如果激光点云和地图始终错开一大截,说明初始位姿没有给对,或者雷达的外参有问题。

4.4 从建图切到导航的姿势

不少人不清楚 Cartographer 建完图后,要怎么样切换到 Nav2。这里的要点是:建图阶段和导航阶段不能同时跑 Cartographer 和 AMCL,因为两者都在尝试维护map -> odom变换,会产生冲突。正确流程是:建完图保存之后,先 Ctrl+C 杀掉 Cartographer 节点和键盘遥控节点,再启动 Nav2。也就是说,你的控制器来源从“键盘 + Cartographer 位姿估计”切换为“Nav2 的 controller_server + AMCL”。

如果是在 launch 文件里一次性启动两套东西,建议用单独的会话分别启动,调试时也方便看日志。

5. 高频故障与排查技巧实录

5.1 机器人完全不动:先查 cmd_vel 话题到底连到哪了

这是导航阶段最高频的问题。表现是 Rviz2 里已经收到全局路径和局部路径,但 Gazebo 里的机器人一动不动。多数情况下不是 Nav2 没规划,而是速度指令根本没有传到 Gazebo 插件。

我在实际调试时第一步永远是检查话题通讯链条,一条命令看到底:

ros2 topic echo /cmd_vel

如果一直不输出任何消息,说明 controller_server 没有在发速度指令,问题在 Nav2 内部。如果/cmd_vel有数据但机器人不动,再查 Gazebo 插件订阅的话题名是否叫/cmd_vel,以及插件是否正常加载。

还有一个容易忽略的点:Nav2 默认的 controller_server 输出话题名也是/cmd_vel,但如果你之前为了避开底盘插件的默认话题改过 remap,比如把 Nav2 输出改成了/cmd_vel_nav,那必须把底盘插件订阅名改成一致,或者加一个 remap。最省事的做法是保持全局都用/cmd_vel。

5.2 引导性排查:Nav2 的 bt_navigator 一直不 active

启动 Nav2 后,用ros2 lifecycle get /bt_navigator查看状态,它可能显示 unconfigured 或 inactive。Nav2 节点用了 ROS2 的 lifecycle 管理机制,不是启动完就直接干活,需要 configure 和 activate。

用nav2_bringup的 launch 文件时,这个流程自动化了。如果你是手动启动各节点,必须手动调用:

ros2 lifecycle set /map_server configure ros2 lifecycle set /map_server activate

注意顺序,所有节点要先 configure 成功,再 activate。如果 configure 失败,多半是参数文件里引用了不存在的 frame 或地图文件路径错误。

5.3 TF 报错怎么看:view_frames 一锤定音

TF 相关报错是 Cartographer 和 Nav2 的“万恶之源”。常见的提示是Could not find transform from base_link to map,或者odom passed to lookupTransform argument target_frame does not exist。

应对这类问题,我的标准动作是输出当前 TF 树文档,直接看结构:

ros2 run tf2_tools view_frames

这个命令会在当前目录生成frames.gv和frames.pdf,用浏览器打开 PDF,你能一眼看到哪条边缺失、哪个坐标系没有发布者。我遇到过一种典型情况:机器人动起来后,odom -> base_footprint的 TF 一直存在,但一停下机器人,这个 TF 就报超时。原因是 Gazebo 的差速驱动插件在没有订阅者时会休眠,Nav2 暂停时没有速度指令,插件就不更新 odom。解决办法是保持 Gazebo 插件always_on为 true,或者检查是否在 launch 里加了-r odom:=not_used之类的错误 remap。

use_sim_time不一致也会造成 TF 时间戳错乱。Gazebo 里的时间来自仿真时钟,如果 Cartographer 或 Nav2 节点没有设置use_sim_time: true,它们拿到的时间戳和 TF 时间戳对不上,常见表现就是“TF 距离太远”之类的报错。启动时务必保证 Gazebo、Cartographer、Nav2、Rviz2 四个都开仿真时间。

5.4 建图常见质量问题速查表

问题现象根本原因处理办法
地图出现重影/墙被拉宽建图速度过快或雷达扫描畸变把线速度降到 0.2 m/s 以下,转弯时原地转而不是边走边转
地图边缘有很多黑点噪声激光雷达 max_range 设置过大把 ray 插件里 max_range 设为实际可用量程,比如 10 米或 15 米
小车走回原点后地图仍不对齐没有触发回环检测走闭环路线,并确保地图上没有大片空旷区域导致扫描匹配退化
地图里出现一条一条的断线雷达数据丢帧检查 update_rate 和 CPU 负载,降低最大传感器刷新频率
建图时小车突然“瞬移”里程计跳变检查 wheel_separation 和 wheel_diameter 是否与模型一致

5.5 导航参数速查表

调试目标调整位置建议值范围
机器人容易撞墙controller_server 的 max_vel_x,或局部代价地图膨胀半径降低速度到 0.3 m/s,增大inflation_radius到 0.3
全局路径总是贴墙全局代价地图的 inflation_radius调大到 0.4
避障太激进controller_server 中 DWB 的min_vel_x和max_vel_theta降低max_vel_theta到 1.0
到达目标后停止不准bt_navigator 的 goal_checker把goal_checker_plugins里的 xy_goal_tolerance 调小,比如 0.05
在狭窄通道反复规划失败planner_server 中使用 Smac Planner 还是 NavFn如果不是特别狭窄环境,保持 NavFn 即可;狭窄环境换 Smac Hybrid-A*

Nav2 参数文件很长,不要一次全改。我个人的经验是每次只改一个分组,跑一次测试,用 Rviz2 里的机器人轨迹和速度曲线判断效果。一次性大改通常会让问题变得更难排查。

6. 最后分享两个我自己很受用的小技巧

我在整个项目里养成的最重要习惯是“慢启动,快确认”。所谓慢启动,就是不要试图用一个 launch 文件把 Gazebo、Cartographer、Nav2 全部拉起来然后一把梭。我第一次尝试时直接在 launch 里把三个系统全部串联,结果日志刷了一屏又一屏,报错混在一起根本分不清是谁的问题。后面改成三个终端分别启动,每个终端只看一个系统,问题定位立刻清晰了。

第二个技巧是善用 Rviz2 的 “TF” 面板。调试导航时,把 Fixed Frame 设为map,打开 TF Display,你能实时看到map、odom、base_footprint、laser_link各自的位置。一旦某个坐标系出现跳动或飘移,可能就是它的父坐标系发不稳定了。这个视觉化检查比看文本日志直观得多。

还有一点值得说:仿真导航跑通的意义不只是“在假世界里玩小车”,它验证了传感器模型、底盘运动学、算法参数一整条逻辑链。后面你换真车,只需要把 Gazebo 里的传感器插件换成真实驱动,把 odom 来源从仿真插件换成 IMU/轮式里程计融合结果,Nav2 和 Cartographer 这一层几乎不用动。这也是我为什么坚持在仿真阶段就把每一个坐标系的发布者、每一条话题的流向吃透,因为那才是真正能带到真机上的经验。

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

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

立即咨询