
简介本资源是一个面向高校机器人方向本科生与研究生的ROS2自动驾驶小车仿真教学与开发平台专为毕业设计、课程设计及机器学习算法验证场景打造解决真实硬件成本高、调试周期长、环境复现难等实践痛点。压缩包共264个文件含70个Python节点脚本实现控制逻辑与YOLOv5目标检测集成、58个YAML配置文件用于参数管理与传感器标定、17个SDF/Gazebo模型定义及18个DAE三维模型配合URDF小车结构描述与launch启动文件构成完整可运行的GazeboROS2仿真闭环整体9.74MB轻量易部署。已有62人学习下载资源提供从模型构建、场景搭建、传感器仿真含摄像头YOLOv5实时识别、控制算法接入到多节点通信基于msg自定义消息的全链路支持目录结构模块清晰autonomous_car_sim_base_ros2为核心功能包gazebo_models与urdf分工明确并附带README.md使用指南与CMakeLists.txt/package.xml等标准ROS2工程配置开箱即用。1. 项目概述从零搭建一个可玩可学的自动驾驶仿真沙盒最近在整理硬盘翻出来一个几年前做的项目压缩包名字就叫“基于ROS2的自动驾驶小车仿真平台.zip”。解压开来看着里面熟悉的代码、配置文件和一堆调试日志感觉就像打开了时光胶囊。这个项目本质上是一个在Gazebo仿真环境中用ROS2驱动一个两轮差速小车模型并实现基础自动驾驶功能比如建图、定位、导航的完整实验平台。它不是要造一辆真正的无人车而是为学习和研究自动驾驶算法提供了一个低成本、零风险、可无限复现的“数字沙盒”。如果你对机器人、自动驾驶感兴趣但苦于没有实体硬件或者担心昂贵的设备在调试中损坏那么这个仿真平台就是为你准备的。它完美复现了真实机器人开发中的核心流程传感器数据仿真激光雷达、摄像头、机器人运动控制、环境感知与地图构建、路径规划与自主导航。整个项目基于ROS2 Humble和Gazebo在Ubuntu 22.04上运行所有代码和配置都是开源的。通过它你可以安全地验证自己的SLAM算法是否鲁棒测试不同的路径规划策略甚至模拟传感器故障等极端情况而这一切只需要一台性能尚可的电脑。2. 平台核心架构与工具链选型解析2.1 为什么是ROS2 Gazebo这套组合拳在机器人仿真领域工具链的选择直接决定了开发效率和天花板。我选择ROS2 Gazebo是经过一番对比和实际踩坑后得出的结论。首先说ROS2。相比ROS1ROS2最大的改进在于其通信中间件DDS数据分发服务带来的真正分布式、实时和可靠的通信能力。在自动驾驶场景下传感器数据流庞大如激光雷达点云、摄像头图像控制指令要求低延迟和高确定性。ROS1基于TCP/UDP的通信机制在数据洪峰时可能阻塞而ROS2的DDS提供了服务质量QoS策略你可以为激光雷达数据设置“最好效果”策略为紧急制动指令设置“可靠”和“截止时间”策略确保关键信息不丢失、不延迟。这对于仿真中模拟真实世界的不确定性至关重要。再说仿真器。为什么不用Webots、CoppeliaSim而用Gazebo核心原因在于生态和深度。Gazebo与ROS/ROS2的集成度是最高的有官方维护的gazebo_ros_pkgs包提供了大量现成的插件用于模拟各类传感器如libgazebo_ros_ray_sensor.so用于激光雷达libgazebo_ros_camera.so用于摄像头。这意味着你几乎可以用声明式的URDF或SDF模型文件就配置出一个带复杂传感器的机器人无需从头编写物理模拟代码。虽然Gazebo对硬件要求较高且旧版Gazebo-Classic已停止维护但其后继者Gazebo Fortress或Ignition Gazebo现统称Gazebo与ROS2的融合正在不断深化社区资源模型、插件、教程也最为丰富。注意当前ROS2 Humble官方推荐搭配的是Gazebo Fortress或Harmonic。但很多遗留项目和教程仍基于Gazebo Classic (Gazebo 11)。本项目为了兼容性和稳定性暂时仍使用了Gazebo 11并通过ros-humble-gazebo-ros-pkgs包进行桥接。对于全新项目建议直接调研新版本Gazebo。2.2 仿真平台的整体软件架构这个平台遵循典型ROS2系统的分层架构但所有节点都运行在仿真环境中仿真层Gazebo这是世界的“上帝引擎”。它负责加载一个包含墙壁、障碍物、地面的三维环境模型.world文件并根据物理引擎通常是ODE或Bullet计算我们的小车模型一个URDF文件的运动学与动力学。同时它通过传感器插件生成模拟的激光扫描数据和摄像头图像流。驱动层ROS2 Control 传感器驱动这一层负责与仿真层对接并将仿真数据转化为ROS2话题。控制器我们使用ros2_control框架。在Gazebo中我们为小车的左右轮关节配置了gazebo_ros2_control插件。这个插件会订阅ROS2 Control控制器管理器发布的/diff_drive_controller/cmd_vel速度命令话题并将速度指令施加到仿真关节上。同时它也会将关节的状态如轮子转速、转角发布出来。传感器Gazebo的激光雷达和摄像头插件会直接将仿真数据发布为标准的ROS2消息类型如sensor_msgs/msg/LaserScan和sensor_msgs/msg/Image。这些话题名称和真实传感器驱动发布的一模一样确保了上层算法节点的通用性。算法层自动驾驶核心这是我们主要的研究和开发区域。感知slam_toolbox节点订阅/scan激光雷达数据实时构建二维栅格地图nav_msgs/msg/OccupancyGrid并同时提供小车的定位geometry_msgs/msg/PoseWithCovarianceStamped。规划与决策nav2导航栈是核心。它包含amcl用于在已有地图中定位、bt_navigator行为树导航器、planner_server全局路径规划和controller_server局部轨迹规划和速度生成等组件。它们接收一个目标点然后结合当前定位和地图计算出控制指令最终发布到/cmd_vel话题。交互与可视化层RVIZ2RVIZ2是我们的“驾驶舱”。在这里我们可以可视化激光雷达的点云、摄像头画面、实时构建的地图、机器人在地图中的位姿一个箭头、规划出的全局路径绿色线和局部规划轨迹红色线。我们也可以通过RVIZ2的插件用鼠标点击给机器人发送导航目标。这个架构的优势在于清晰的模块化。你可以轻易替换其中任何一个模块。比如把slam_toolbox换成cartographer或者不用nav2自己写一个简单的路径规划节点。仿真环境确保了你的改动不会造成物理破坏并且可以快速迭代测试。3. 从零开始环境搭建与模型创建实操3.1 基础系统与ROS2环境搭建平台的基础是Ubuntu 22.04 LTS。选择LTS版本是为了长期稳定性。ROS2的安装我强烈推荐使用国内开发者“鱼香ROS”的一键安装脚本它能完美解决网络问题和依赖冲突。# 1. 获取一键安装脚本 wget http://fishros.com/install -O fishros . fishros # 运行后选择“1. 安装ROS”然后根据提示选择ROS2 Humble版本。 # 脚本会自动配置源、安装核心包、设置环境变量。 # 2. 安装完成后验证安装 source /opt/ros/humble/setup.bash ros2 doctor # ros2 doctor会检查你的环境是否健康按照它的提示解决警告即可。 # 3. 安装Gazebo和ROS2桥接包 sudo apt install ros-humble-gazebo-ros-pkgs # 安装RVIZ2、导航等工具包 sudo apt install ros-humble-nav2-bringup ros-humble-slam-toolbox ros-humble-turtlebot3-gazebo提示安装turtlebot3-gazebo并不是为了用TurtleBot3而是因为它依赖了许多通用的Gazebo模型和插件能省去我们单独安装模型库的麻烦避免后续仿真启动时找不到模型文件的错误。3.2 创建机器人URDF模型与仿真世界我们的核心是一个两轮差速驱动的小车。URDF文件定义了它的外观连杆和关节、碰撞属性、惯性矩阵以及传感器链接。!-- my_robot.urdf.xacro (使用xacro宏以简化) -- ?xml version1.0? robot namemy_autonomous_robot xmlns:xacrohttp://www.ros.org/wiki/xacro !-- 定义常量如轮子半径、底盘尺寸 -- xacro:property namewheel_radius value0.033 / xacro:property namewheel_width value0.01 / xacro:property namebase_length value0.2 / xacro:property namebase_width value0.15 / xacro:property namebase_height value0.05 / !-- 底盘连杆 -- link namebase_link visual geometry box size${base_length} ${base_width} ${base_height}/ /geometry material nameblue color rgba0 0.2 0.8 1.0/ /material /visual collision geometry box size${base_length} ${base_width} ${base_height}/ /geometry /collision inertial mass value1.0/ inertia ixx0.01 ixy0.0 ixz0.0 iyy0.01 iyz0.0 izz0.02/ /inertial /link !-- 左轮 -- link nameleft_wheel_link visual geometry cylinder radius${wheel_radius} length${wheel_width}/ /geometry material nameblack/ /visual collision.../collision inertial.../inertial /link joint nameleft_wheel_joint typecontinuous parent linkbase_link/ child linkleft_wheel_link/ origin xyz0 ${base_width/2} -${wheel_radius} rpy0 1.5707 0/ axis xyz0 1 0/ /joint !-- 右轮对称... -- !-- 激光雷达连杆和关节 -- link namelaser_link visual geometry cylinder radius0.02 length0.05/ /geometry /visual collision.../collision inertial.../inertial /link joint namelaser_joint typefixed parent linkbase_link/ child linklaser_link/ origin xyz0.1 0 0 rpy0 0 0/ !-- 雷达安装在车头前方 -- /joint !-- Gazebo插件这是连接仿真与ROS2控制的关键 -- gazebo plugin filenamelibgazebo_ros2_control.so namegazebo_ros2_control parameters$(find my_robot_description)/config/controllers.yaml/parameters /plugin /gazebo !-- Gazebo激光雷达传感器插件 -- gazebo referencelaser_link sensor typeray namelidar_sensor pose0 0 0 0 0 0/pose visualizefalse/visualize update_rate10/update_rate ray scan horizontal samples360/samples !-- 360度扫描 -- resolution1.0/resolution min_angle-3.14159/min_angle max_angle3.14159/max_angle /horizontal /scan range min0.05/min max10.0/max !-- 最大测距10米 -- resolution0.01/resolution /range noise typegaussian/type mean0.0/mean stddev0.01/stddev /noise /ray plugin filenamelibgazebo_ros_ray_sensor.so namegazebo_ros_lidar_controller ros namespace//namespace argument~/out:scan/argument !-- 发布到/scan话题 -- /ros frame_namelaser_link/frame_name !-- 坐标系 -- /plugin /sensor /gazebo /robot同时我们需要一个controllers.yaml文件来配置ros2_control# config/controllers.yaml controller_manager: ros__parameters: update_rate: 100 # Hz left_wheel_controller: type: velocity_controllers/JointVelocityController right_wheel_controller: type: velocity_controllers/JointVelocityController diff_drive_controller: type: diff_drive_controller/DiffDriveController left_wheel_names: [left_wheel_joint] right_wheel_names: [right_wheel_joint] wheel_separation: 0.15 wheel_radius: 0.033 publish_rate: 50.0仿真世界文件.world则描述了一个简单的走廊或房间环境里面可以添加一些立方体作为障碍物。我们可以直接使用Gazebo自带的模型库或者从网上下载。3.3 集成与启动Launch文件编写艺术将所有部分串联起来的是ROS2的Launch文件。一个好的Launch文件应该清晰、可配置、容错性强。# launch/simulation.launch.py import os from launch import LaunchDescription from launch.actions import IncludeLaunchDescription, DeclareLaunchArgument, ExecuteProcess, RegisterEventHandler from launch.event_handlers import OnProcessExit from launch.launch_description_sources import PythonLaunchDescriptionSource from launch.substitutions import LaunchConfiguration, PathJoinSubstitution from launch_ros.actions import Node from launch_ros.substitutions import FindPackageShare def generate_launch_description(): # 定义可配置参数 use_sim_time LaunchConfiguration(use_sim_time, defaulttrue) world_file LaunchConfiguration(world_file, defaultempty.world) # 声明参数允许从命令行覆盖 declare_world_cmd DeclareLaunchArgument( world_file, default_valueempty.world, descriptionPath to the Gazebo world file ) # 启动Gazebo服务器和客户端 gazebo_server IncludeLaunchDescription( PythonLaunchDescriptionSource([ PathJoinSubstitution([ FindPackageShare(gazebo_ros), launch, gzserver.launch.py ]) ]), launch_arguments{world: world_file}.items() ) gazebo_client IncludeLaunchDescription( PythonLaunchDescriptionSource([ PathJoinSubstitution([ FindPackageShare(gazebo_ros), launch, gzclient.launch.py ]) ]) ) # 将机器人URDF模型生成节点并加载到Gazebo中 robot_state_publisher Node( packagerobot_state_publisher, executablerobot_state_publisher, namerobot_state_publisher, outputscreen, parameters[{ robot_description: Command([xacro , PathJoinSubstitution([FindPackageShare(my_robot_description), urdf, my_robot.urdf.xacro])]), use_sim_time: use_sim_time }] ) spawn_entity Node( packagegazebo_ros, executablespawn_entity.py, arguments[-entity, my_robot, -topic, robot_description, -x, 0.0, -y, 0.0, -z, 0.05], outputscreen ) # 启动ROS2 Control control_node Node( packagecontroller_manager, executableros2_control_node, parameters[PathJoinSubstitution([FindPackageShare(my_robot_description), config, controllers.yaml]), {use_sim_time: use_sim_time}], outputscreen ) load_diff_drive Node( packagecontroller_manager, executablespawner.py, arguments[diff_drive_controller, -c, /controller_manager], outputscreen ) # 等待robot_state_publisher启动后再加载控制器 load_diff_drive_after_spawn RegisterEventHandler( event_handlerOnProcessExit( target_actionspawn_entity, on_exit[load_diff_drive], ) ) # 启动SLAM和导航这里先不启动后续手动启动以分步测试 # slam_node Node(...) # nav2_launch IncludeLaunchDescription(...) return LaunchDescription([ declare_world_cmd, gazebo_server, gazebo_client, robot_state_publisher, spawn_entity, control_node, load_diff_drive_after_spawn, # slam_node, # nav2_launch, ])这个Launch文件做了几件关键事1) 启动Gazebo并加载指定世界2) 发布机器人模型到/robot_description话题3) 将模型生成到Gazebo仿真世界中4) 启动ROS2 Control并加载差速控制器。通过事件处理器RegisterEventHandler我们确保了控制器在机器人成功生成后才加载避免了依赖错误。4. 自动驾驶核心功能实现与调试4.1 建图SLAM让小车认识世界建图是自动驾驶的第一步。我们使用slam_toolbox它是一个功能强大且易于使用的SLAM库。启动仿真平台后在新的终端中启动SLAM节点ros2 launch slam_toolbox online_async_launch.py params_file:/path/to/your/slam_config.yaml use_sim_time:true关键的slam_config.yaml配置文件需要根据你的机器人进行调整slam_toolbox: ros__parameters: # 坐标系设置 odom_frame: odom map_frame: map base_frame: base_link # 你的机器人底盘坐标系 # 传感器话题 scan_topic: /scan # 地图分辨率 (米/像素) resolution: 0.05 # 优化和匹配参数影响建图精度和速度 throttle_scans: 1 # 每收到N帧激光数据处理一次1为全处理 transform_publish_period: 0.02 # 发布坐标变换的周期 # 是否使用里程计信息仿真中Gazebo提供的里程计通常很准可以启用 use_odom: true odom_topic: /odom # 地图保存与加载 map_file_name: /home/user/maps/my_sim_map mode: mapping # 建图模式启动后在RVIZ2中添加LaserScan显示话题/scan添加Map显示话题/map。然后使用ROS2的teleop_twist_keyboard节点通过键盘控制小车在仿真环境中缓慢、匀速地移动尽可能遍历所有区域。实操心得仿真建图比真实环境简单因为激光数据干净、里程计准确。但要注意控制速度键盘控制时线速度和角速度都不要太大建议线速度0.5 m/s角速度1.0 rad/s。速度太快会导致点云匹配失败地图出现重影或错位。闭环检测当小车回到一个已经探索过的区域时观察RVIZ2中的地图是否“严丝合缝”。slam_toolbox会自动进行闭环检测与优化。如果发现地图错位可能是闭环检测参数需要调整或者机器人运动模型噪声设置过小仿真中过于理想。地图保存建图完成后调用服务保存地图ros2 service call /slam_toolbox/save_map slam_toolbox/srv/SaveMap {name: /home/user/maps/my_sim_map}这会生成my_sim_map.pgm地图图像和my_sim_map.yaml地图元数据两个文件。4.2 导航Navigation让小车自主行动有了地图就可以让小车自主导航了。我们使用nav2导航栈。首先需要为导航准备一个配置文件nav2_params.yaml这个文件参数众多但核心部分如下# nav2_params.yaml 关键部分 amcl: ros__parameters: use_sim_time: true alpha1: 0.2 # 里程计旋转噪声 alpha2: 0.2 # 里程计平移噪声 alpha3: 0.2 # 里程计平移噪声 alpha4: 0.2 # 里程计旋转噪声 # 仿真中里程计准噪声可以设小点真实环境要调大。 bt_navigator: ros__parameters: global_frame: map robot_base_frame: base_link odom_topic: /odom use_sim_time: true planner_server: ros__parameters: expected_planner_frequency: 20.0 use_sim_time: true planner_plugins: [GridBased] GridBased: plugin: nav2_navfn_planner/NavfnPlanner tolerance: 0.5 use_astar: false # 使用Dijkstra算法路径更优但稍慢 controller_server: ros__parameters: use_sim_time: true controller_frequency: 10.0 controller_plugins: [FollowPath] FollowPath: plugin: nav2_regulated_pure_pursuit_controller/RegulatedPurePursuitController desired_linear_vel: 0.5 max_linear_vel: 0.8 lookahead_dist: 0.6 # 前视距离根据机器人速度和惯性调整然后通过一个Launch文件启动整个nav2栈ros2 launch nav2_bringup navigation_launch.py params_file:/path/to/your/nav2_params.yaml use_sim_time:true map:/home/user/maps/my_sim_map.yaml启动后在RVIZ2中添加Map显示话题为/map。添加PoseArray显示话题为/particlecloud这是amcl发布的粒子云用于可视化定位的不确定性。粒子越集中定位越准。添加Path显示话题为/plan全局路径和/trajectory局部轨迹。使用RVIZ2的“2D Pose Estimate”工具在地图上大致点击一下机器人实际的位置和朝向为amcl提供初始定位。使用“2D Nav Goal”工具在地图上点击目标点和朝向。如果一切正常你会看到一条绿色的全局路径从机器人当前位置规划到目标点同时机器人开始移动并有一条红色的局部轨迹在实时调整。控制器会发布/cmd_vel话题驱动小车前进。4.3 调试与性能优化实战仿真平台的巨大优势在于可以快速复现和调试问题。以下是我在开发中遇到的几个典型问题及解决方法问题1小车在导航时疯狂旋转或撞墙。排查首先检查/cmd_vel话题。在终端用ros2 topic echo /cmd_vel查看控制器发布的指令是否合理。如果角速度指令持续很大可能是局部规划器如RegulatedPurePursuitController的前视距离lookahead_dist设置不当。对于小型慢速机器人这个值通常设置在0.3到1.0米之间需要根据机器人尺寸和惯性调整。解决减小lookahead_dist让控制器更关注近处的路径走向。同时检查costmap代价地图配置确保膨胀半径inflation_radius设置合理通常为机器人半径安全裕度防止规划路径太贴近障碍物。问题2定位AMCL粒子发散机器人“丢失”。排查观察RVIZ2中的/particlecloud。如果粒子散落在地图各处说明定位失败。检查/scan话题是否正常激光数据是否与地图匹配。在仿真中这通常是因为初始位姿估计错误或者机器人被卡住导致里程计与激光观测严重不符。解决使用RVIZ2的“2D Pose Estimate”工具重新给出一个准确的初始位姿。同时可以适当增加AMCL的粒子数max_particles默认2000可增至5000和激光模型的噪声参数laser_model_type和laser_likelihood_max_dist提高其对异常数据的容忍度。问题3全局规划器找不到路径。排查检查目标点是否在可通行区域非障碍物。在RVIZ2中查看/global_costmap话题确认目标点所在的栅格代价不是254致命障碍或255未知区域。解决确保你的地图pgm文件中可通行区域是白色255障碍物是黑色0未知区域是灰色205。nav2默认将灰度值小于occupied_thresh如65的像素视为障碍物。如果地图边缘有灰色未知区域规划器可能无法穿过。可以在规划器参数中设置allow_unknown: true但需谨慎这可能导致机器人驶入未知危险区域。性能优化技巧仿真加速Gazebo默认是实时仿真。如果你的算法测试不需要严格实时可以在启动Gazebo服务器时加入-r 0参数arg namerate value0/在Launch文件中这会让Gazebo以最大速度运行不受实时时钟限制极大加快测试迭代速度。RViz2优化显示大量点云或粒子会拖慢RViz2。在测试时可以关闭不必要的显示项如关闭PoseArray的显示或者降低激光雷达在RViz2中的点大小。5. 平台扩展与进阶实验思路基础功能跑通后这个平台就变成了一个强大的实验床可以进行各种进阶探索1. 多传感器融合动作在URDF中添加一个摄像头插件发布/camera/image_raw话题。实验编写一个节点同时订阅/scan和/camera/image_raw。尝试用OpenCV处理图像识别红色路锥或特定标志物并将识别结果如障碍物的位置转换为costmap中的障碍物信息通过nav2_msgs/msg/Costmap话题或nav2_costmap_2d的插件接口实现视觉辅助的避障。2. 算法替换与对比动作将slam_toolbox替换为cartographer。需要编写新的Launch文件和参数配置。实验在同一个仿真环境中用相同的控制路径分别运行两个SLAM算法建图。对比生成地图的精度可以人工测量关键距离、计算资源的占用用top或ros2 topic hz查看CPU使用率和频率以及闭环检测的效果。这能让你深刻理解不同SLAM算法的优缺点。3. 引入动态障碍物动作在Gazebo的.world文件中添加一个沿固定路径移动的立方体模型。实验观察nav2的局部规划器如何应对突然出现的动态障碍物。调整local_costmap的更新频率和障碍物层参数观察其对动态障碍物反应速度的影响。你甚至可以编写一个简单的节点发布动态障碍物的预测轨迹并集成到规划器中。4. 强化学习RL智能体训练动作这是一个更高级的方向。你需要将仿真环境机器人状态、传感器数据封装成一个标准的Gym环境并定义奖励函数例如到达目标给予正奖励碰撞给予负奖励消耗时间给予小惩罚。实验使用Stable-Baselines3等RL库训练一个DQN或PPO智能体使其从激光雷达数据中直接输出速度指令/cmd_vel学习在复杂环境中导航到随机目标点。仿真平台提供了无限次试错的机会是训练RL策略的理想场所。5. 真实世界迁移测试动作将仿真中调试好的nav2参数文件、地图和核心算法节点部署到一台真实的、具有类似传感器配置如RPLidar A1的两轮差速底盘小车上。实验这是最终的验证。你会发现真实世界的传感器噪声、地面摩擦系数不一致、电机控制误差等问题都会带来新的挑战。仿真平台的价值在于它让你在投入真实硬件前已经解决了95%的逻辑和架构问题只剩下最后5%的物理适配工作。这个“基于ROS2的自动驾驶小车仿真平台”项目就像一套完整的乐高套装。它提供了所有的基础模块和连接件ROS2通信、Gazebo仿真、导航栈。你的创造力决定了能用它搭建出什么。无论是验证一个学术想法还是学习工业级的机器人软件框架它都是一个绝佳的起点。从让小车在仿真世界里稳稳地走到一个点开始每一步的扩展和遇到的问题都会让你对自动驾驶系统的理解加深一分。本文还有配套的精品资源点击获取