首页 > 基础资料 博客日记
龙芯2k0300 - 走马观碑组Gazebo仿真环境搭建
2026-04-05 18:00:01基础资料围观1次
一、概述
1.1 为什么要仿真
搭建Gazebo仿真环境对于智能车比赛(特别是涉及视觉巡线、强化学习等算法开发)来说,不是可选项,而是最优解。以下是需要搭建仿真环境的核心理由,以及它能解决的实际问题。
1.1.1 硬件不足
问题:你现在没有久久派、摄像头、电机等硬件,但需要写程序、验证算法。
Gazebo的作用:
- 提供虚拟的小车模型(带摄像头、激光雷达等传感器);
- 提供虚拟的赛道环境(可以自定义颜色、形状、材质);
- 程序写完后,直接在
Gazebo里运行,效果等同于在真车上测试;
结果:硬件还没到,你的巡线算法已经跑通了。硬件一到,只需换底层驱动即可。
1.1.2 缩短调试周期
| 调试场景 | 真车调试 | Gazebo仿真 |
|---|---|---|
| 修改PID参数 | 烧录→上电→跑一圈→观察→再烧录(5-10分钟/次) | 改代码→保存→重启仿真(10秒/次) |
| 小车撞墙 | 可能损坏硬件(电机、舵机、车架) | 重置位置,继续调试 |
| 跑完一整圈 | 需要清场、充电、防止撞人 | 无限制运行,无人值守 |
| 测试极端情况 | 可能翻车、失控 | 完全安全 |
结论:仿真环境让的调试效率提升30-50倍。
1.1.3 提供可复现的测试环境
问题:真车测试时,光线变化、地面摩擦力、电池电量都会影响结果,今天跑通的代码明天可能就失效。
Gazebo的优势:
- 每次启动都是完全相同的环境(相同的光照、相同的摩擦力、相同的传感器噪声);
- 可以精确控制变量:比如只改变线条颜色,其他不变;
- 算法性能变化只由代码改动引起,排除了环境干扰;
这对于调参、对比算法优劣至关重要。
1.1.4、支持强化学习训练
强化学习需要数百万次试错,这在真车上完全不可能:
| 训练需求 | 真车 | Gazebo仿真 |
|---|---|---|
| 试错次数 | 几百次就报废 | 无限次 |
| 训练速度 | 1倍速 | 可以加速到10-100倍 |
| 并行训练 | 需要多台真车 | 开多个Gazebo实例 |
| 复位成本 | 手动搬回起点 | 代码一键复位 |
实例:训练一个简单的巡线RL模型,真车可能需要3个月+损坏5台车,仿真只需要1周+电费。
1.1.5、提前发现算法缺陷
仿真中可以轻松制造"事故场景":
- 突然的强光照射(模拟阳光直射摄像头);
- 赛道上有污渍(模拟线条部分缺失);
- 传感器故障(模拟某个像素坏点);
这些在真车上很难刻意制造,但在仿真中可以随时开启。提前让你的算法适应这些情况,比赛时就不会翻车。
1.2 环境搭建步骤
走马观碑组Gazebo仿真环境搭建主要包含以下几个步骤:
① 环境准备(ubuntu + ROS2):这个可以参考《ROS2概述和基于RK3588的环境搭建》;
② 小车建模(URDF):有关URFD介绍可以参考《ROS2之URDF建模》;
③ 赛道建模(World);
④ 视觉巡线算法开发。
1.2.1 工程目录简介
我们可以先创建工程目录,创建目录car_ws;
zhengyang@ubuntu:~$ cd /opt/2k0300/loongson_2k300_lib
zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib$ mkdir -p car_ws/src
zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib$ cd car_ws/src
完整的目录结构大致如下,这个我们后续内容会依次创建:
car_ws/
├── src/
│ ├── car_description/ # 功能包1:机器人URDF模型
│ ├── car_gazebo/ # 功能包2:Gazebo仿真
│ └── car_vision/ # 功能包3:视觉算法
在ROS的开发规范中,src/ 下的每个子目录叫功能包(Package),而不是独立项目。它们共同组成一个完整的机器人项目。
| 功能包 | 职责 | 修改频率 |
|---|---|---|
car_description |
小车的物理模型(尺寸、颜色、传感器位置) | 低(硬件确定后很少改) |
car_gazebo |
仿真环境、赛道、启动脚本 | 中(换赛道时需要改) |
car_vision |
图像处理、巡线算法 | 高(天天调参) |
1.2.2 功能包关系
功能包之间的协作关系如下:
启动顺序:
1. car_gazebo 启动仿真世界,生成小车模型
└── 调用 car_description 中的 car.urdf 描述小车长什么样
2. car_vision 订阅摄像头图像
└── 处理图像 → 发布速度指令
3. car_gazebo 中的 Gazebo 接收速度指令
└── 驱动仿真小车运动
二、小车建模
创建car_description的Python版本的功能包;
zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/car_ws/src$ ros2 pkg create --build-type ament_python car_description
运行成功后,终端会显示创建的文件和目录信息。此时, car_description 功能包目录结构将如下所示:
zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/car_ws/src$ tree ./car_description/
./car_description/
├── car_description # 核心Python模块目录,用于存放Python代码
│ └── __init__.py
├── package.xml # 功能包的描述文件(含依赖信息)
├── resource # 资源文件夹
│ └── car_description
├── setup.cfg # setuptools 的配置文件
├── setup.py # Python 包的安装脚本
└── test # 测试文件夹
2.1 子目录
功能包创建好了,但按照规划,我们需要在 car_description 中存放小车的URDF模型文件。这些不是.py文件,放在自动生成的 car_description 子目录下并不合适。
我们可以手动创建两个目录来更好地组织文件:
urdf: 专门存放.urdf或.xacro模型文件;launch:保存相关启动文件;rviz:保存rviz的配置文件;meshes:放置URDF中引用的模型渲染文件;config: 存放Gazebo控制器的配置文件。
在终端中执行:
zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/car_ws/src$ cd car_description/
zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/car_ws/src/car_description$ mkdir urdf config launch rviz meshes
我们需要修改setup.py文件,添加配置文件:
import os
from glob import glob
...
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*.launch.py'))),
(os.path.join('share', package_name, 'urdf'), glob(os.path.join('urdf', '*.*'))),
(os.path.join('share', package_name, 'urdf/sensors'), glob(os.path.join('urdf/sensors', '*.*'))),
(os.path.join('share', package_name, 'meshes'), glob(os.path.join('meshes', '*.*'))),
(os.path.join('share', package_name, 'rviz'), glob(os.path.join('rviz', '*.rviz'))),
(os.path.join('share', package_name, 'config'), glob(os.path.join('config', '*.*'))),
],
...
2.2 模型文件
接下来就是编写一个完整的 car.xacro 文件,这个模型包含车身、四个轮子、摄像头传感器,并且配置了Gazebo仿真所需的插件;
- 有关
URDF语法可以参考《ROS2之URDF建模》; - 有关
XACRO语法可以参考《ROS2之Gazebo物理仿真平台》。
首先进入 urdf 目录并创建文件:
zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/car_ws/src/car_description$ cd urdf/
zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/car_ws/src/car_description/urdf$ vim car.xacro
内容如下:
点击查看详情
<?xml version="1.0"?>
<robot name="f_car_model" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- ==================== 参数定义 ==================== -->
<!-- 车身参数(F车模实际尺寸,单位:米) -->
<xacro:property name="body_length" value="0.28"/>
<xacro:property name="body_width" value="0.16"/>
<xacro:property name="body_height" value="0.05"/>
<!-- 轮子参数 -->
<xacro:property name="wheel_radius" value="0.032"/>
<xacro:property name="wheel_width" value="0.018"/>
<xacro:property name="wheel_base" value="0.18"/> <!-- 后轮到车尾距离 -->
<!-- 万向轮参数 -->
<xacro:property name="caster_radius" value="0.015"/>
<xacro:property name="caster_width" value="0.012"/>
<xacro:property name="caster_offset_x" value="0.12"/> <!-- 万向轮前伸距离 -->
<xacro:property name="caster_offset_z" value="-0.025"/> <!-- 万向轮下沉高度 -->
<!-- 轮距(左右后轮中心距离) -->
<xacro:property name="wheel_track" value="0.12"/>
<!-- 摄像头参数 -->
<xacro:property name="camera_x" value="0.10"/>
<xacro:property name="camera_y" value="0.0"/>
<xacro:property name="camera_z" value="0.06"/>
<xacro:property name="camera_roll" value="0.0"/>
<xacro:property name="camera_pitch" value="0.0"/>
<xacro:property name="camera_yaw" value="0.0"/>
<!-- ==================== 材质定义 ==================== -->
<material name="blue">
<color rgba="0.0 0.0 0.8 1.0"/>
</material>
<material name="black">
<color rgba="0.0 0.0 0.0 1.0"/>
</material>
<material name="red">
<color rgba="0.8 0.0 0.0 1.0"/>
</material>
<material name="gray">
<color rgba="0.5 0.5 0.5 1.0"/>
</material>
<material name="silver">
<color rgba="0.75 0.75 0.75 1.0"/>
</material>
<!-- ==================== 底盘 base_link ==================== -->
<link name="base_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="${body_length} ${body_width} ${body_height}"/>
</geometry>
<material name="blue"/>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="${body_length} ${body_width} ${body_height}"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 0" rpy="0 0 0"/>
<mass value="0.45"/>
<inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
</inertial>
</link>
<!-- ==================== 左后轮(驱动轮) ==================== -->
<joint name="left_rear_wheel_joint" type="continuous">
<origin xyz="-${wheel_base} ${wheel_track/2} -${wheel_radius}" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="left_rear_wheel"/>
<axis xyz="0 1 0"/>
</joint>
<link name="left_rear_wheel">
<visual>
<origin xyz="0 0 0" rpy="1.5708 0 0"/>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
<material name="black"/>
</visual>
<collision>
<origin xyz="0 0 0" rpy="1.5708 0 0"/>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 0" rpy="0 0 0"/>
<mass value="0.04"/>
<inertia ixx="0.00008" ixy="0" ixz="0" iyy="0.00008" iyz="0" izz="0.00008"/>
</inertial>
</link>
<!-- ==================== 右后轮(驱动轮) ==================== -->
<joint name="right_rear_wheel_joint" type="continuous">
<origin xyz="-${wheel_base} -${wheel_track/2} -${wheel_radius}" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="right_rear_wheel"/>
<axis xyz="0 1 0"/>
</joint>
<link name="right_rear_wheel">
<visual>
<origin xyz="0 0 0" rpy="1.5708 0 0"/>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
<material name="black"/>
</visual>
<collision>
<origin xyz="0 0 0" rpy="1.5708 0 0"/>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 0" rpy="0 0 0"/>
<mass value="0.04"/>
<inertia ixx="0.00008" ixy="0" ixz="0" iyy="0.00008" iyz="0" izz="0.00008"/>
</inertial>
</link>
<!-- ==================== 前万向轮(从动轮) ==================== -->
<joint name="caster_wheel_joint" type="continuous">
<origin xyz="${caster_offset_x} 0 ${caster_offset_z}" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="caster_wheel"/>
<axis xyz="1 0 0"/> <!-- 万向轮绕X轴旋转 -->
</joint>
<link name="caster_wheel">
<visual>
<origin xyz="0 0 0" rpy="1.5708 0 0"/>
<geometry>
<sphere radius="${caster_radius}"/>
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin xyz="0 0 0" rpy="1.5708 0 0"/>
<geometry>
<sphere radius="${caster_radius}"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 0" rpy="0 0 0"/>
<mass value="0.01"/>
<inertia ixx="0.00001" ixy="0" ixz="0" iyy="0.00001" iyz="0" izz="0.00001"/>
</inertial>
</link>
<!-- ==================== 摄像头支架 ==================== -->
<joint name="camera_mount_joint" type="fixed">
<origin xyz="${camera_x} ${camera_y} ${camera_z}" rpy="${camera_roll} ${camera_pitch} ${camera_yaw}"/>
<parent link="base_link"/>
<child link="camera_mount"/>
</joint>
<link name="camera_mount">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.04 0.04 0.02"/>
</geometry>
<material name="red"/>
</visual>
</link>
<!-- ==================== 摄像头传感器 ==================== -->
<joint name="camera_joint" type="fixed">
<origin xyz="0 0 0.015" rpy="0 0 0"/>
<parent link="camera_mount"/>
<child link="camera_link"/>
</joint>
<link name="camera_link"/>
<!-- ==================== Gazebo 插件配置 ==================== -->
<!-- 摄像头插件(160x128 @ 60fps) -->
<gazebo reference="camera_link">
<sensor type="camera" name="camera">
<update_rate>60.0</update_rate>
<camera name="camera">
<horizontal_fov>1.0472</horizontal_fov>
<td>
<width>160</width>
<height>128</height>
<format>R8G8B8</format>
</td>
<clip>
<near>0.05</near>
<far>10.0</far>
</clip>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.007</stddev>
</noise>
</camera>
<plugin name="camera_driver" filename="libgazebo_ros_camera.so">
<robotNamespace>/</robotNamespace>
<topicName>camera/image_raw</topicName>
<frameName>camera_link</frameName>
<hackBaseline>0.07</hackBaseline>
</plugin>
</sensor>
</gazebo>
<!-- 差速驱动插件(适配 F 车模三轮结构) -->
<gazebo>
<plugin name="diff_drive_controller" filename="libgazebo_ros_diff_drive.so">
<leftJoint>left_rear_wheel_joint</leftJoint>
<rightJoint>right_rear_wheel_joint</rightJoint>
<wheelSeparation>${wheel_track}</wheelSeparation>
<wheelDiameter>${wheel_radius * 2}</wheelDiameter>
<torque>0.3</torque>
<commandTopic>cmd_vel</commandTopic>
<odometryTopic>odom</odometryTopic>
<odometryFrame>odom</odometryFrame>
<robotBaseFrame>base_link</robotBaseFrame>
<publishOdomTF>true</publishOdomTF>
<publishWheelTF>false</publishWheelTF>
<publishWheelJointState>true</publishWheelJointState>
</plugin>
</gazebo>
</robot>
文件内容说明:
| 部分 | 作用 |
|---|---|
| 材质定义 | 定义蓝色车身、黑色轮胎、红色摄像头支架 |
| 车身尺寸 | 28x16cm(F车模实际尺寸)、轮距12cm、轮径3.2cm |
| 三轮 | 后2驱动轮 + 前1万向轮(前部中心位置,球形从动轮) |
| 摄像头传感器 | 160x128分辨率,60fps,发布到 /camera/image_raw 话题 |
| 差速驱动插件 | 接收 /cmd_vel 指令,控制小车运动 |
2.3 launch文件
在launch文件夹下创建display.launch.py文件;
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch_ros.parameter_descriptions import ParameterValue
from launch_ros.actions import Node
from launch.substitutions import Command, LaunchConfiguration
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
# xacro 文件路径(注意扩展名是 .xacro)
xacro_path = os.path.join(
get_package_share_directory('car_description'),
'urdf',
'car.xacro' # 关键:改为 .xacro
)
# 声明 model 参数
model_arg = DeclareLaunchArgument(
name='model',
default_value=xacro_path,
description='Absolute path to robot xacro file'
)
# 使用 xacro 命令解析 URDF
robot_description = ParameterValue(
Command(['xacro ', LaunchConfiguration('model')]),
value_type=str
)
return LaunchDescription([
model_arg, # 必须包含这个声明
# 机器人状态发布器
Node(
package='robot_state_publisher',
executable='robot_state_publisher',
output='screen',
parameters=[{'robot_description': robot_description}]
),
# 关节状态发布器 GUI(可手动拖动关节)
Node(
package='joint_state_publisher_gui',
executable='joint_state_publisher_gui',
name='joint_state_publisher_gui'
),
# RViz2 可视化
Node(
package='rviz2',
executable='rviz2',
name='rviz2',
arguments=['-d', os.path.join(get_package_share_directory('car_description'), 'rviz', 'car_display.rviz')],
output='screen'
)
])
这个Launch文件主要做三件事:
- 加载机器人
URDF模型(支持xacro格式); - 发布机器人的状态变换(
TF); - 在
rviz2中可视化机器人。
2.3.1 节点
脚本运行会创建以下几个节点:
joint_state_publisher_gui:发布每个joint(除fixed类型)的状态,可以通过UI界面对joint进行控制;robot_state_publisher:将机器人各个links、joints之间的关系,通过TF的形式,整理成三维姿态信息发布。rviz2:在rviz2中可视化机器人;
joint_state_publisher这是一个官方ROS2包,主要功能:
- 输入:
- 读取
URDF中的关节定义; - 接收用户或程序指定的关节角度;
- 读取
- 输出:
- 发布
/joint_states话题,消息类型为sensor_msgs/msg/JointState; - 包含所有关节的名称、位置、速度、力等信息。
- 发布
2.3.2 数据流与节点关系
数据流与节点关系:
用户通过滑动条/GUI或程序 → joint_state_publisher_gui
↓ 发布/joint_states话题
robot_state_publisher
↓ 计算并发布TF变换
rviz2 和其他节点
↓ 接收TF并可视化
2.4 car_display.rviz
创建rviz配置文件,避免手动设置rviz,在rviz目录下新建car_display.rviz文件;
Panels:
- Class: rviz_common/Displays
Name: Displays
- Class: rviz_common/Views
Name: Views
Visualization Manager:
Class: ""
Displays:
- Class: rviz_default_plugins/Grid
Name: Grid
Value: true
- Alpha: 0.8
Class: rviz_default_plugins/RobotModel
Description Source: Topic
Description Topic:
Value: /robot_description
Enabled: true
Name: RobotModel
Value: true
- Class: rviz_default_plugins/TF
Name: TF
Value: true
Global Options:
Fixed Frame: base_link
Frame Rate: 30
Name: root
Tools:
- Class: rviz_default_plugins/MoveCamera
Value: true
Views:
Current:
Class: rviz_default_plugins/Orbit
Distance: 1.7
Name: Current View
Pitch: 0.33
Value: Orbit (rviz)
Yaw: 5.5
Window Geometry:
Height: 800
Width: 1200
2.5 编译运行
2.4.1 编译
在 car_ws 目录下编译并检查:
zhengyang@ubuntu:~$ cd /opt/2k0300/loongson_2k300_lib/car_ws
zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/car_ws$ colcon build --paths src/car_description
.....
Finished <<< car_description [0.92s]
zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/car_ws$ source install/setup.sh
2.4.2 运行
启动终端,运行如下命令;
ros2 launch car_description display.launch.py
可以看到rviz窗口打开,并且显示了小车模型(蓝色车身、黑色轮子、红色摄像头支架);
三、赛道建模
四、视觉巡线算法
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签:

