1. 从零开始:搭建你的ROS智能车仿真环境

想玩转ROS智能车仿真,第一步就是把“舞台”搭好。这就像你要组装一台电脑,得先把主板、CPU、内存这些硬件备齐。对于ROS仿真来说,这个“舞台”就是一套包含了操作系统、ROS框架、仿真器和智能车模型的完整软件环境。我刚开始接触的时候,也被一堆依赖和配置搞得头大,但踩过几次坑之后,发现只要按步骤来,其实没那么复杂。

首先,你得有一个Ubuntu系统。我强烈建议使用Ubuntu 16.04搭配ROS Kinetic版本,这是经过大量项目验证的稳定组合,很多现成的功能包和教程都基于此,能帮你避开不少兼容性“暗礁”。直接在虚拟机(如VirtualBox或VMware)里安装一个是最省事的办法,这样不会影响你主机的系统。安装好Ubuntu后,打开终端,按照ROS官网的步骤安装ROS Kinetic桌面完整版。这个安装过程基本上是“复制粘贴命令”的流水线操作,记得在最后一步执行 sudo rosdep initrosdep update 时保持网络通畅,这一步是初始化ROS的依赖工具,很多人卡在这里就是因为网络问题。

环境搭好,接下来就是创建工作空间。你可以把工作空间理解为你个人的“项目车间”,所有你自己的代码、下载的第三方功能包都会放在这里。打开终端,依次执行下面几条命令:

mkdir -p ~/racecar_ws/src
cd ~/racecar_ws/src
catkin_init_workspace

这会在你的家目录下创建一个名为 racecar_ws 的文件夹,里面有一个 src 子目录,catkin_init_workspace 命令则初始化了这个工作空间。然后,我们需要把“演员”——智能车模型代码——请进来。这里我们可以使用一个非常经典的racecar仿真包。继续在 src 目录下执行:

git clone https://github.com/xmy0916/racecar.git

克隆完成后,退回工作空间根目录进行编译:

cd ~/racecar_ws
catkin_make

编译过程可能会花几分钟,屏幕上会滚动很多信息,只要最后没有红色的“ERROR”报错,看到“Build succeeded”之类的提示就成功了。最后,别忘了把这个工作空间的环境变量“激活”,让它成为系统默认的ROS工作空间。执行:

echo "source ~/racecar_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc

这样,每次打开新的终端,系统都知道该去哪里找我们刚刚编译好的racecar功能包了。至此,你的仿真舞台就基本搭建完毕。我建议在继续之前,重启一下终端,或者手动 source 一下 .bashrc 文件,确保环境变量生效。

2. 初探仿真世界:启动功能包自带地图

环境准备好了,手痒想先看看效果对吧?我们先从最简单的开始:启动功能包里已经提供好的仿真世界和智能车模型。这就像游戏里的“新手村”,地图和规则都是现成的,我们先熟悉一下基本操作。

打开一个终端,输入这条“魔法”命令:

roslaunch racecar_gazebo racecar_runway.launch

稍等片刻,你会看到两个窗口弹出来。一个是Gazebo仿真环境,里面加载了一个带跑道的场景和一辆小小的智能车;另一个是RViz可视化工具,目前可能只显示一些坐标轴。Gazebo就是我们的“物理沙盒”,它负责模拟重力、碰撞、传感器数据等物理规律;而RViz是我们的“眼睛”,用来可视化机器人看到的数据,比如激光雷达点云、地图、规划路径等。

第一次启动可能会慢一点,因为Gazebo需要在线下载一些模型。如果卡在下载界面,别担心,多等一会儿,或者检查一下网络。启动成功后,你应该能在Gazebo里看到一辆小车停在跑道上。这时候,你可以用Gazebo界面左侧的工具栏,选择“平移”、“旋转”等工具,用鼠标拖动视角,从各个角度观察你的小车和赛道。

现在,小车还只是个静态模型。怎么让它动起来呢?我们需要给它发送控制指令。在ROS里,这通常通过发布话题(Topic)来实现。但为了方便,racecar功能包已经为我们集成好了键盘控制。保持Gazebo和RViz运行,新建一个终端,输入:

roslaunch racecar_gazebo keyboard_teleop.launch

这个命令会启动一个简单的键盘控制节点。根据终端里的提示,你应该就能用键盘上的 W(前进)、A(左转)、S(后退)、D(右转) 键来控制小车在赛道上行走了。试着开一圈,感受一下仿真的物理引擎。你可能会发现,这小车油门给猛了容易打滑甚至翻车,转向太急也会侧滑,这恰恰说明了仿真的真实性——它在一定程度上模拟了真实车辆的动力学特性。

这个阶段,我建议你多花点时间熟悉控制手感,尝试让小车沿着赛道跑完一圈。同时,观察RViz窗口。默认的RViz配置可能没显示太多内容,但你可以尝试添加显示项(Add),比如添加“LaserScan”来查看小车激光雷达“看到”的世界,是一些红色的点云数据。这一步的目的,是让你对ROS仿真的几个核心组件:Gazebo(世界模拟)、RViz(数据可视化)、ROS节点(控制程序)有一个直观的认识,为后面更复杂的建图和导航打下基础。

3. 创建世界的地图:SLAM建图实战

让小车自己跑的前提是,它得知道自己在哪,以及周围环境是什么样。这就需要一张地图。在未知环境中,机器人一边移动一边构建地图的技术,就叫SLAM(即时定位与地图构建)。我们接下来就用 gmapping 这个经典的SLAM算法包,为功能包自带的赛道创建一张地图。

首先,确保你已经关闭了之前启动的所有终端(特别是键盘控制那个)。然后,我们启动建图流程。新建终端1,输入:

cd ~/racecar_ws
roslaunch racecar_gazebo slam_gmapping.launch

这个命令做了好几件事:启动了Gazebo仿真环境(包含小车和赛道)、启动了 gmapping SLAM节点、也启动了一个预设好显示配置的RViz。在RViz里,你应该能看到一个空旷的网格,中心有一个小车模型,周围开始稀疏地出现一些黑点或灰点,这就是激光雷达实时扫描到的障碍物信息,但还没有形成完整地图。

现在,我们需要像刚才一样,手动控制小车走遍赛道的每一个角落,让激光雷达扫描到所有区域。新建终端2,再次启动键盘控制:

roslaunch racecar_gazebo keyboard_teleop.launch

控制小车开始缓慢移动。这里的技巧是:慢!稳!覆盖全! 因为建图质量直接取决于你的控制。开得太快,激光扫描点会太稀疏;转弯太急,可能导致定位丢失(在RViz里表现为地图错乱或小车位置“飘移”)。我个人的经验是,让小车贴着赛道的内侧或外侧墙,匀速行驶,确保激光能扫到两侧的边界。在RViz中,你会看到一张灰度地图随着小车的移动被逐渐“绘制”出来,黑色代表障碍物(如墙壁),白色代表可通行区域,灰色代表未知区域。

当你控制小车基本跑完了整个赛道,RViz中的地图也看起来完整、清晰时,就可以保存地图了。新建终端3,切换到地图保存的目录并执行保存命令:

cd ~/racecar_ws/src/racecar/racecar_gazebo/map
rosrun map_server map_saver -f map_racecar

这条命令会在当前目录下生成两个文件:map_racecar.pgmmap_racecar.yaml.pgm 是一张灰度图片,就是你在RViz里看到的地图;.yaml 是地图的配置文件,包含了图片路径、分辨率、原点坐标等信息。建图过程中最容易出的问题就是“定位丢失”,表现为地图出现重影或严重扭曲。如果遇到这种情况,最好从头再来,控制小车时动作更平缓一些。建图成功后,你可以用图片查看器打开 .pgm 文件,欣赏一下自己的劳动成果。

4. 让小车自己跑:基于地图的定位与导航

有了高精度的地图,我们就可以让小车“自己思考”了。ROS中的导航功能包集 move_base 是实现这一点的核心。它结合了定位(AMCL算法)、全局路径规划(从起点到终点找一条大致路线)和局部路径规划(避开动态障碍物,控制车轮速度)三大功能。

首先,关闭所有正在运行的终端。然后,新建终端1,启动导航所需的仿真环境和地图服务器:

cd ~/racecar_ws
roslaunch racecar_gazebo racecar_runway_navigation.launch

这个launch文件会启动Gazebo(带小车和赛道),并加载我们上一步保存好的 map_racecar 地图。接着,新建终端2,启动专门为导航配置好的RViz:

roslaunch racecar_gazebo racecar_rviz.launch

这次RViz的界面会丰富很多。你应该能看到加载进来的完整地图(一个灰色的赛道轮廓),地图上有一个小车模型(这是AMCL算法估计出的机器人位姿,由一堆红色的小箭头表示其概率分布),以及可能的一些代价地图层(用不同颜色表示膨胀区域、障碍物等)。

现在,激动人心的时刻来了。在RViz的工具栏上,找到“2D Nav Goal”按钮(通常是一个绿色的箭头图标)。点击它,然后在地图上任意一个白色(可通行)区域,点击并拖动鼠标。你会看到一个绿色的箭头出现,箭头根部是你点击的位置(目标点),箭头方向是你拖动的方向(小车到达时的朝向)。松开鼠标,你就向导航系统发布了一个目标指令。

一瞬间,你会看到地图上出现了一条绿色的线,从小车当前位置连接到目标点。这就是全局规划器计算出的最优路径。同时,小车开始自动启动,沿着这条路径前进。在前进过程中,你还会看到一条红色的线蓝色的弧线,这是局部规划器根据实时激光数据生成的近期行驶轨迹,它会动态调整以精确跟踪全局路径并避开任何意外障碍。

为了让小车更平滑地跟踪路径,racecar包还提供了一个路径跟踪脚本。新建终端3,运行它:

rosrun racecar_gazebo path_pursuit.py

这个Python脚本会订阅 move_base 发布的路径信息,并计算生成更合适的油门和转向指令发送给小车,比单纯的导航包输出控制得更稳。现在,你可以尝试在地图上多点几个目标,看着小车自主地来回穿梭。这个过程里,你可以观察RViz中AMCL的粒子云(那些小箭头)是如何随着小车运动而收敛的,这代表了定位置信度的变化。如果小车在某个地方卡住不动了,可能是路径被障碍物挡住了,或者局部代价地图设置得太保守,这时你可以通过“2D Pose Estimate”工具(通常是一个红色的箭头图标)手动纠正一下小车在地图上的初始定位,往往就能解决问题。

5. 打造专属赛道:在Gazebo中自定义仿真环境

用现成地图固然方便,但哪有自己设计赛道来得过瘾?Gazebo自带的模型编辑器(Building Editor)就能让我们轻松搭建个性化的仿真环境。这就像玩高级版的积木,墙、柱子、斜坡、各种形状的物体都可以随意摆放。

首先,我们单独打开Gazebo的模型编辑器。在一个终端中输入:

gazebo

等待Gazebo空场景启动后,点击菜单栏的 Edit -> Building Editor。这时界面会切换到一个俯视的2D编辑网格。左侧是各种建筑部件,如墙(Wall)、窗(Window)、门(Door)、楼梯(Stairs),以及各种形状的物体(如圆柱、盒子)。我们主要用“墙”来绘制赛道边界。

绘制基本赛道:选择“墙”工具,然后在网格上点击开始绘制。你可以绘制直线段,围成一个闭合的环形赛道,比如一个“8”字形或一个带有急弯的赛道。墙的厚度和高度可以在右侧属性栏调整。画错了可以用“选择”工具选中墙体,按Delete键删除。一个关键技巧:为了让赛道更真实,避免小车撞墙角时卡住,尽量让拐角处连接平滑,或者使用“圆柱”物体作为弯道护墙。

添加挑战性障碍:一个空赛道有点无聊。我们可以在赛道中间添加一些障碍物,比如选择“盒子”(Box)或“圆柱”(Cylinder)工具,直接拖放到赛道中央。你还可以调整它们的大小和位置,制造出需要绕行的S弯或窄道。

保存模型:赛道设计满意后,点击编辑器顶部的 File -> Save Model。给它起个名字,比如 my_race_track,然后保存。Gazebo会将其保存为模型文件(.sdf.dae 格式)。保存后,关闭建筑编辑器,回到Gazebo主界面。

将赛道放入世界:现在我们需要创建一个包含小车和赛道的完整仿真世界。先关闭当前的Gazebo窗口。然后,我们启动小车的空白世界,并把赛道插进去。在终端中运行:

roslaunch racecar_gazebo racecar.launch

这会启动一个只有小车的空旷世界。在Gazebo左侧的“插入”面板(Insert tab)里,你应该能找到你刚刚保存的模型,名字可能是 my_race_track。点击它,然后在主窗口点击一下,赛道模型就被放置进来了。注意:你可能需要调整赛道和小车的相对位置,用鼠标移动、旋转工具,把小车放到赛道的起点线上。

保存世界文件:一切就绪后,点击Gazebo菜单栏的 File -> Save World As。导航到你的racecar功能包的世界文件目录:

~/racecar_ws/src/racecar/racecar_gazebo/worlds/

将世界文件命名为 my_track.world 并保存。这样,你的自定义赛道世界就永久保存下来了,以后可以随时加载。

6. 为自定义世界实现全流程导航

有了自己的赛道世界,我们就要重复之前“建图-导航”的流程,但这次是在我们设计的新环境里。这能完整验证从环境搭建到算法部署的全链条能力。

第一步:创建专属启动文件。我们创建一个新的launch文件来启动我们的自定义世界。打开终端:

cd ~/racecar_ws/src/racecar/racecar_gazebo/launch
touch my_track.launch
gedit my_track.launch

将以下内容复制进去:

<?xml version="1.0"?>
<launch>
  <!-- 启动racecar小车,并指定我们自定义的世界文件 -->
  <include file="$(find racecar_gazebo)/launch/racecar.launch">
    <arg name="world_name" value="my_track"/>
  </include>
</launch>

保存并关闭。这个文件告诉ROS,启动小车时,去 worlds 文件夹里找 my_track.world 这个世界文件。

第二步:运行自定义世界并手动控制验证。在终端运行:

roslaunch racecar_gazebo my_track.launch

如果一切正常,Gazebo会加载出你的赛道和小车。同样地,新建终端用 keyboard_teleop.launch 控制小车跑一圈,确保赛道设计合理,没有模型穿透等物理错误。

第三步:在新赛道上进行SLAM建图。保持世界运行,新建终端,启动gmapping:

roslaunch racecar_gazebo slam_gmapping.launch

再开一个终端进行键盘控制,小心翼翼地驾驶小车遍历你的整个赛道,包括所有弯道和障碍物周围。由于是自定义环境,建图时更要耐心,确保所有细节都被扫描到。建图完成后,同样保存地图,为了区分,可以命名为 my_track_map

cd ~/racecar_ws/src/racecar/racecar_gazebo/map
rosrun map_server map_saver -f my_track_map

第四步:实现自定义赛道的自主导航。这是最后一步,也是最考验前面所有工作的一步。我们需要创建一个新的导航启动文件,它要同时加载自定义世界和对应的地图。再创建一个launch文件:

cd ~/racecar_ws/src/racecar/racecar_gazebo/launch
touch my_track_nav.launch
gedit my_track_nav.launch

这个文件内容要复杂一些,它集成了世界启动、地图加载和导航栈配置。你可以参考以下内容,它基于原始文章中的配置进行了整理:

<?xml version="1.0"?>
<launch>
  <!-- 1. 启动自定义世界和小车 -->
  <include file="$(find racecar_gazebo)/launch/racecar.launch">
    <arg name="world_name" value="my_track"/>
  </include>

  <!-- 2. 加载我们为这个赛道创建的地图 -->
  <node name="map_server" pkg="map_server" type="map_server" args="$(find racecar_gazebo)/map/my_track_map.yaml" />

  <!-- 3. 设置使用仿真时间 -->
  <param name="/use_sim_time" value="true"/>

  <!-- 4. 启动move_base导航栈 -->
  <node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">
    <!-- 加载通用参数、局部/全局代价地图参数、规划器参数 -->
    <rosparam file="$(find racecar_gazebo)/config/costmap_common_params.yaml" command="load" ns="global_costmap" />
    <rosparam file="$(find racecar_gazebo)/config/costmap_common_params.yaml" command="load" ns="local_costmap" />
    <rosparam file="$(find racecar_gazebo)/config/local_costmap_params.yaml" command="load" />
    <rosparam file="$(find racecar_gazebo)/config/global_costmap_params.yaml" command="load" />
    <rosparam file="$(find racecar_gazebo)/config/teb_local_planner_params.yaml" command="load" />
    <!-- 指定全局和局部规划器 -->
    <param name="base_global_planner" value="global_planner/GlobalPlanner" />
    <param name="base_local_planner" value="teb_local_planner/TebLocalPlannerROS" />
    <!-- 设置规划频率、容忍度等 -->
    <param name="planner_frequency" value="1.0" />
    <param name="controller_frequency" value="5.0" />
  </node>
</launch>

保存这个文件。现在,关闭所有其他终端,只运行这一个启动文件:

roslaunch racecar_gazebo my_track_nav.launch

然后,再启动RViz和路径跟踪脚本:

# 终端2
roslaunch racecar_gazebo racecar_rviz.launch
# 终端3
rosrun racecar_gazebo path_pursuit.py

在RViz中,加载进 my_track_map 地图后,使用“2D Nav Goal”指定目标点。如果一切配置正确,你将看到小车在你亲手设计的赛道上,自主规划路径,绕过你设置的障碍物,稳稳地驶向终点。这个过程可能会遇到问题,比如在窄道处规划失败,或者碰到动态障碍物反应不佳,这就需要你去调整 config 文件夹下的那些YAML参数文件了,例如增大 inflation_radius(膨胀半径)让小车更早避障,或调整 max_vel_x(最大速度)让转弯更稳,这就是另一个层次的调试和优化了。

Logo

更多推荐