Ubuntu 20.04下Gazebo仿真环境搭建与SLAM建图导航实战
1. Ubuntu 20.04环境准备与ROS安装
在开始Gazebo仿真环境搭建之前,我们需要确保系统环境已经准备就绪。Ubuntu 20.04作为长期支持版本,是机器人开发的理想选择。我建议使用全新安装的系统开始,这样可以避免各种依赖冲突问题。
首先更新系统软件包:
sudo apt update && sudo apt upgrade -y接下来安装ROS Noetic,这是官方支持Ubuntu 20.04的最新ROS版本。我推荐使用桌面完整版安装,它包含了ROS、rqt、rviz、机器人通用库等所有基础组件:
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 sudo apt update sudo apt install ros-noetic-desktop-full安装完成后,记得将ROS环境变量添加到bashrc中。这个步骤很关键,我见过不少新手因为漏掉这一步导致各种命令找不到:
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc source ~/.bashrc为了后续开发方便,我们还需要安装一些基础工具。这些工具在编译和调试时非常有用:
sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential sudo rosdep init rosdep update2. Gazebo仿真环境搭建
2.1 核心依赖安装
Gazebo是ROS生态中最常用的仿真工具之一。在安装Gazebo前,我们需要确保所有依赖都已就位。根据我的经验,以下这些包是必须安装的:
sudo apt install ros-noetic-gazebo-ros-pkgs ros-noetic-gazebo-ros-control \ ros-noetic-effort-controllers ros-noetic-joint-state-controller \ ros-noetic-ackermann-msgs ros-noetic-teb-local-planner \ ros-noetic-rtabmap-ros tcl-dev tk-dev python3-tk如果遇到依赖问题,可以尝试先更新软件源:
sudo apt-get update2.2 创建工作空间
我习惯为每个项目创建独立的工作空间,这样可以保持环境整洁。下面创建一个名为catkin_gazebo_ws的工作空间:
mkdir -p ~/catkin_gazebo_ws/src cd ~/catkin_gazebo_ws/src catkin_init_workspace接下来我们可以下载Racecar仿真包。这个包包含了完整的轮式机器人模型和Gazebo配置:
git clone https://github.com/soonuse/racecar.git cd .. catkin_make编译时可能会遇到Python版本问题,这是Ubuntu 20.04常见的问题。解决方法是指定Python3解释器:
catkin_make -DPYTHON_EXECUTABLE=/usr/bin/python32.3 环境配置
编译完成后,需要将工作空间的环境变量加入bashrc。这一步确保每次打开终端都能正确加载我们的工作空间:
echo "source ~/catkin_gazebo_ws/devel/setup.bash" >> ~/.bashrc source ~/.bashrc3. Gazebo世界构建与模型使用
3.1 创建自定义地图
Gazebo提供了强大的地图编辑器,我们可以用它创建自己的仿真环境。启动Gazebo后,点击左上角的Edit→Building Editor,就可以开始绘制地图了。
我建议先绘制简单的矩形房间作为起点:
- 选择Wall工具绘制四面墙
- 使用Door工具添加出入口
- 保存为house模型
保存时要注意文件格式,Gazebo模型通常包含.config和.dae文件。我习惯将模型保存在~/.gazebo/models目录下,这样Gazebo启动时会自动加载。
3.2 使用预置模型
除了自己创建模型,Gazebo还提供了大量现成的模型。我们可以通过以下命令下载官方模型库:
mkdir -p ~/.gazebo/models cd ~/.gazebo/models wget http://file.ncnynl.com/ros/gazebo_models.txt wget -i gazebo_models.txt ls model.tar.g* | xargs -n1 tar xzvf下载完成后,在Gazebo的Insert面板中就能看到这些模型了。我特别喜欢使用cafe模型来测试导航算法,它的复杂度适中,非常适合调试。
3.3 启动自定义世界
为了在ROS中使用自定义世界,我们需要创建launch文件。下面是一个典型的house_world.launch文件示例:
<launch> <include file="$(find gazebo_ros)/launch/empty_world.launch"> <arg name="world_name" value="$(find racecar_gazebo)/worlds/house.world"/> <arg name="paused" value="false"/> <arg name="use_sim_time" value="true"/> <arg name="gui" value="true"/> <arg name="headless" value="false"/> <arg name="debug" value="false"/> </include> </launch>启动这个launch文件就能加载我们的自定义世界了:
roslaunch racecar_gazebo house_world.launch4. SLAM建图实战
4.1 Gmapping算法配置
Gmapping是ROS中最常用的SLAM算法之一。我们需要先确保gmapping包已安装:
sudo apt install ros-noetic-slam-gmapping然后创建gmapping的launch文件。这个文件配置了激光雷达参数、地图更新频率等关键参数:
<launch> <node pkg="gmapping" type="slam_gmapping" name="slam_gmapping"> <param name="base_frame" value="base_link"/> <param name="odom_frame" value="odom"/> <param name="map_update_interval" value="0.1"/> <param name="maxUrange" value="10.0"/> <param name="linearUpdate" value="0.05"/> <param name="angularUpdate" value="0.0436"/> <param name="particles" value="30"/> </node> </launch>4.2 建图过程
建图需要三个终端同时工作:
- 第一个终端启动Gazebo世界和小车:
roslaunch racecar_gazebo racecar.launch- 第二个终端启动gmapping:
roslaunch racecar_gazebo slam_gmapping.launch- 第三个终端启动键盘控制:
rosrun teleop_twist_keyboard teleop_twist_keyboard.py在建图过程中,我建议先让小车沿着墙壁缓慢移动,确保激光雷达能扫描到所有障碍物。建图质量很大程度上取决于移动的路径是否覆盖了整个环境。
4.3 地图保存
建图完成后,可以使用map_server保存地图:
rosrun map_server map_saver -f ~/catkin_gazebo_ws/src/racecar/racecar_gazebo/map/house这会生成house.pgm和house.yaml两个文件。PGM是地图图像,YAML文件包含了地图的元数据。
5. 自主导航实现
5.1 导航栈配置
ROS导航栈需要配置多个参数文件。这些文件通常存放在包的config目录下。最重要的四个文件是:
- costmap_common_params.yaml - 定义代价地图的通用参数
- global_costmap_params.yaml - 全局代价地图配置
- local_costmap_params.yaml - 局部代价地图配置
- teb_local_planner_params.yaml - 局部路径规划器参数
我特别推荐使用teb_local_planner作为局部规划器,它对轮式机器人的支持非常好。
5.2 导航launch文件
下面是一个完整的导航launch文件示例:
<launch> <include file="$(find racecar_gazebo)/launch/racecar.launch"> <arg name="world_name" value="house"/> </include> <node name="map_server" pkg="map_server" type="map_server" args="$(find racecar_gazebo)/map/house.yaml"/> <node pkg="move_base" type="move_base" 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"/> </node> </launch>5.3 路径跟踪实现
为了让小车能够跟随规划出的路径,我们需要编写路径跟踪脚本。下面是一个简化的Python实现:
#!/usr/bin/env python import rospy from nav_msgs.msg import Path from ackermann_msgs.msg import AckermannDriveStamped from geometry_msgs.msg import PoseStamped import math class PathFollower: def __init__(self): self.path_sub = rospy.Subscriber('/move_base/TebLocalPlannerROS/global_plan', Path, self.path_callback) self.cmd_pub = rospy.Publisher('/vesc/low_level/ackermann_cmd_mux/input/navigation', AckermannDriveStamped, queue_size=1) self.lookahead_distance = 0.5 self.max_speed = 1.0 def path_callback(self, msg): if len(msg.poses) == 0: return # 找到距离lookahead_distance最近的点 target_idx = self.find_target_point(msg.poses) target = msg.poses[target_idx] # 计算转向角度 angle = self.calculate_steering_angle(target) # 发布控制命令 cmd = AckermannDriveStamped() cmd.drive.speed = self.calculate_speed(angle) cmd.drive.steering_angle = angle self.cmd_pub.publish(cmd) def find_target_point(self, poses): # 简化实现,实际应该考虑车辆当前位置 return min(len(poses)-1, 5) # 选择路径上第5个点 def calculate_steering_angle(self, target): # 简化实现,实际应该考虑车辆当前位姿 return math.atan2(target.pose.position.y, target.pose.position.x) def calculate_speed(self, angle): # 根据转向角度调整速度 return self.max_speed * (1 - 0.5 * abs(angle)/math.pi) if __name__ == '__main__': rospy.init_node('path_follower') pf = PathFollower() rospy.spin()5.4 完整导航测试
启动导航系统的完整流程如下:
- 启动Gazebo环境和地图服务器:
roslaunch racecar_gazebo racecar_navigation.launch- 启动RViz可视化界面:
roslaunch racecar_gazebo view_navigation.launch在RViz中使用"2D Nav Goal"工具设置目标点
启动路径跟踪节点:
rosrun racecar_gazebo path_follower.py在实际测试中,我发现teb_local_planner对动态障碍物的处理非常出色。通过调整costmap参数,可以让小车更好地避开未知障碍物。如果发现小车规划出的路径不够平滑,可以尝试减小inflation_radius参数。