虽然gazebo可以创建仿真墙壁、添加内置的房屋、障碍物、树木等模型
但本文想更加灵感配置自己的环境,这里以简单模型为例,旨在演示整个流程
1、在SW中建立自己的环境三维模型

2、注意模型的原点以及坐标系的方向,需要定义为和之前一样的,右手笛卡尔坐标系
然后导出为URDF文件

注意导出需要为装配图,因此将零件导入装配体,将原点重合

为了导出为自己的坐标,此处建立坐标系

然后通过工具导出

因为就一个零件,所以没有Joint

因为作为环境,因此不必在意惯性

直接导出为功能包

3、将功能包放在之前的工作空间的src文件夹下

重新编译整个工作空间

然后 source 一下环境
source devel/setup.bash
我们需要调整模型的位置,以及将其和world坐标系固定
<!-- 生成地图环境模型 -->
<node
name="spawn_underground_environment"
pkg="gazebo_ros"
type="spawn_model"
args="-file $(find underground_environment)/urdf/underground_environment.urdf
-urdf
-model underground_environment
-x 0.0 -y 0.0 -z 0.0
-R 0.0 -P 0.0 -Y 0.0"
output="screen" />
<!-- === 将地图环境固定到世界坐标系 === -->
<node name="world_to_environment_tf" pkg="tf" type="static_transform_publisher"
args="0 0 0 0 0 0 world underground_environment 100" />
<node
打开环境看看
roslaunch underground_environment gazebo.launch

4、加载环境和小车模型
启动文件如下:
<launch>
<!-- 设置使用仿真时间 -->
<param name="/use_sim_time" value="true"/>
<!-- 启动Gazebo时确保时间同步 -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<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>
<!-- ===== 加载地图环境 ===== -->
<group ns="environment">
<!-- 生成地下环境模型 -->
<node name="spawn_underground_environment" pkg="gazebo_ros" type="spawn_model"
args="-file $(find underground_environment)/urdf/underground_environment.urdf
-urdf
-model underground_environment
-x 0.0 -y 0.0 -z 0.0
-R 0.0 -P 0.0 -Y 0.0"
output="screen" />
<!-- 固定地图到世界坐标系 -->
<node name="world_to_environment_tf" pkg="tf" type="static_transform_publisher"
args="0 0 0 0 0 0 world underground_environment 100" />
</group>
<!-- 将xacro先加载到robot_description,再加载到Gazebo仿真环境中 -->
<param
name="robot_description"
command="$(find xacro)/xacro $(find tyut_jjc)/urdf/tyut_jjc_model.xacro" />
<node
name="spawn_model"
pkg="gazebo_ros"
type="spawn_model"
args="-param robot_description -urdf -model tyut_jjc_model -x 2.4 -y 0 -z 0 -R 0 -P 0 -Y 0"
output="screen" />
<!-- 加载控制启动文件 -->
<include file="$(find my_robot_control)/launch/my_robot_control.launch" />
</launch>

5、为了启动launch更加规范,可以参考关于从solid works中使用udrf导出机器人小车与gazebo地图模型在gazebo导中做三维导航的仿真_将solidworks三维模型导入gazebo中使用-CSDN博客
XML
<launch>
<!-- ============================================== -->
<!-- 1. 定义Launch文件的可配置参数(便于灵活修改) -->
<!-- ============================================== -->
<!-- 世界文件路径:指定Gazebo要加载的地图文件,默认指向自定义的test_world.world -->
<arg name="world_name" default="$(find zzmxc_model)/world/test_world.world"/>
<!-- 仿真暂停开关:默认false(启动后直接运行),设为true则启动后暂停仿真 -->
<arg name="paused" default="false"/>
<!-- 仿真时间开关:默认true(使用Gazebo的仿真时间,而非系统时间,ROS导航等功能依赖此参数) -->
<arg name="use_sim_time" default="true"/>
<!-- GUI显示开关:默认true(启动Gazebo可视化界面),设为false则只启动后台仿真(无界面) -->
<arg name="gui" default="true"/>
<!-- 无头模式开关:默认false(正常启动),设为true用于无显示器的服务器环境(配合gui=false使用) -->
<arg name="headless" default="false"/>
<!-- 调试模式开关:默认false(正常启动),设为true会输出Gazebo详细调试日志 -->
<arg name="debug" default="false"/>
<!-- 详细日志开关:默认true(输出关键信息日志,便于排查启动问题) -->
<arg name="verbose" default="true"/>
<!-- ============================================== -->
<!-- 2. 设置ROS核心参数(仿真时间配置) -->
<!-- ============================================== -->
<!-- 向ROS参数服务器写入use_sim_time参数,所有依赖时间的节点(如导航、TF)会自动使用仿真时间 -->
<param name="/use_sim_time" value="true" />
<!-- ============================================== -->
<!-- 3. 加载机器人URDF模型到参数服务器(关键步骤) -->
<!-- ============================================== -->
<!-- 将机器人的URDF文件内容写入参数服务器的"robot_description"键下 -->
<!-- 后续节点(如spawn_model、robot_state_publisher)会从该参数读取机器人模型信息 -->
<param name="robot_description" textfile="$(find zzmxc_model)/urdf/zzmxc_model.urdf" />
<!-- ============================================== -->
<!-- 4. 启动Gazebo仿真环境并加载地图 -->
<!-- ============================================== -->
<!-- 包含Gazebo官方的empty_world.launch(基础启动文件,已封装gzserver/gzclient节点) -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<!-- 传递参数:指定要加载的自定义世界文件(覆盖empty_world默认的空地图) -->
<arg name="world_name" value="$(arg world_name)" />
<!-- 传递参数:是否启动后暂停仿真 -->
<arg name="paused" value="$(arg paused)"/>
<!-- 传递参数:是否使用仿真时间 -->
<arg name="use_sim_time" value="$(arg use_sim_time)"/>
<!-- 传递参数:是否显示Gazebo可视化界面 -->
<arg name="gui" value="$(arg gui)"/>
<!-- 传递参数:是否启用无头模式 -->
<arg name="headless" value="$(arg headless)"/>
<!-- 传递参数:是否启用调试模式 -->
<arg name="debug" value="$(arg debug)"/>
<!-- 传递参数:是否输出详细日志 -->
<arg name="verbose" value="$(arg verbose)"/>
</include>
<!-- ============================================== -->
<!-- 5. (注释说明)移除问题节点,增加启动稳定性 -->
<!-- ============================================== -->
<!-- 原代码中可能存在"wait_for_gazebo"节点(用于等待Gazebo启动),但该节点可能导致启动卡住 -->
<!-- 此处已删除该节点,改用下方spawn_model的延迟启动(sleep 8),确保Gazebo完全启动后再加载机器人 -->
<!-- ============================================== -->
<!-- 6. 条件性启动静态TF变换(底座与 footprint 坐标转换) -->
<!-- ============================================== -->
<!-- 定义开关参数:是否启用静态TF,默认true(大多数机器人需要此变换) -->
<arg name="use_static_tf" default="true" />
<!-- if条件判断:只有当use_static_tf为true时,才启动该节点 -->
<node if="$(arg use_static_tf)"
name="tf_footprint_base" <!-- 节点名称(唯一) -->
pkg="tf" <!-- 所属功能包(TF坐标变换功能包) -->
type="static_transform_publisher" <!-- 节点类型(发布静态TF的可执行文件) -->
args="0 0 0 0 0 0 base_link base_footprint 40" <!-- TF参数:x y z r p y 父链接 子链接 发布频率(Hz) -->
output="screen" /> <!-- 日志输出到终端(便于查看是否启动成功) -->
<!-- ============================================== -->
<!-- 7. 加载机器人模型到Gazebo(指定生成位置) -->
<!-- ============================================== -->
<node
name="spawn_model" <!-- 节点名称(唯一,负责将模型生成到Gazebo) -->
pkg="gazebo_ros" <!-- 所属功能包(Gazebo与ROS通信的功能包) -->
type="spawn_model" <!-- 节点类型(生成模型的可执行文件) -->
<!-- 生成参数说明: -->
<!-- -param robot_description:从参数服务器读取机器人模型(替代直接指定-file路径,更规范) -->
<!-- -urdf:声明模型类型为URDF -->
<!-- -model zzmxc_model:给模型命名(Gazebo中唯一,不可重复) -->
<!-- -x/-y/-z:模型在Gazebo中的xyz坐标(此处为-10.0, 20.0, 0.5,可根据地图调整) -->
<!-- -Y 1.57:模型绕z轴旋转角度(1.57弧度≈90度,调整机器人朝向) -->
args="-param robot_description -urdf -model zzmxc_model -x -10.0 -y 20.0 -z 0.5 -Y 1.57"
output="screen" <!-- 日志输出到终端(可查看生成成功/失败信息) -->
<!-- 延迟启动:等待8秒后再执行该节点 -->
<!-- 解决Gazebo未完全启动时,模型生成失败的问题(替代原wait_for_gazebo节点) -->
launch-prefix="bash -c 'sleep 8; $0 $@' " />
<!-- ============================================== -->
<!-- 8. 发布关节校准信号(部分机器人控制器依赖) -->
<!-- ============================================== -->
<node
name="fake_joint_calibration" <!-- 节点名称(唯一) -->
pkg="rostopic" <!-- 所属功能包(用于发布ROS话题的功能包) -->
type="rostopic" <!-- 节点类型(话题发布工具) -->
<!-- 发布内容:向/calibrated话题发布std_msgs/Bool类型的true消息 -->
<!-- 作用:告知关节控制器"关节已校准",避免控制器因等待校准信号而卡住 -->
args="pub /calibrated std_msgs/Bool true"
<!-- 延迟10秒启动:确保机器人模型和控制器完全加载后再发布校准信号 -->
launch-prefix="bash -c 'sleep 10; $0 $@' " />
<!-- ============================================== -->
<!-- 9. 启动机器人状态发布节点(TF树与关节状态) -->
<!-- ============================================== -->
<node name="robot_state_publisher" <!-- 节点名称(唯一,ROS标准节点) -->
pkg="robot_state_publisher" <!-- 所属功能包(发布机器人状态的功能包) -->
type="robot_state_publisher" <!-- 节点类型(可执行文件) -->
respawn="false" <!-- 重启开关:false表示节点退出后不自动重启 -->
output="screen"> <!-- 日志输出到终端 -->
<!-- 话题重映射:将节点默认订阅的/joint_states话题,重映射到/gazebo/joint_states -->
<!-- 原因:Gazebo会将仿真的关节状态发布到/gazebo/joint_states,此处确保节点能读取到正确的关节数据 -->
<remap from="/joint_states" to="/gazebo/joint_states" />
</node>
</launch>