Wiki
Gz Sim(Gazebo Harmonic)
Existing English translations were reused. 10 current source blocks remain in Simplified Chinese, without triggering paid translation.
1.Gz Sim (Gazebo Harmonic and later versions (ROS2 Jazzy and later versions))
新版Gazebo 是 ROS2 中使用的全新机器人仿真工具,它是 Gazebo 的升级版本。在Humble他叫Ignition Gazebo(也叫Gazebo Fortress),在Jazzy中叫Gazebo Harmonic(去掉了Ignition的名字)(https://community.gazebosim.org/t/a-new-era-for-gazebo/1356)。它具备更好的性能和可用性,并通过紧密集成 ROS2 来提供强大的仿真环境。
1.1.Gazebo Installation and Operation
Official tutorial: https://docs.ros.org/en/jazzy/Tutorials/Advanced/Simulators/Gazebo/Gazebo.html
The following website is the official tutorial (ROS2 Gazebo Harmonic for Jazzy):
https://gazebosim.org/docs/harmonic/getstarted/
https://gazebosim.org/docs/harmonic/library_reference_nav/
Source code:https://github.com/gazebosim/docs/blob/master/harmonic/tutorials
Install
Gazebo is an independent project that does not depend on ROS2 and can be installed on its own. However, if ROS2 is installed, the corresponding version of Gazebo is already integrated in the ROS2 repository, and it can be installed directly by invoking the following command:
# 通用命令
sudo apt install ros-${ROS_DISTRO}-ros-gz
# Humble版本
sudo apt install ros-humble-ros-gz
# Jazzy版本
sudo apt install ros-jazzy-ros-gz

Run
After Gazebo is installed, it can be started in two ways.
Method 1: Start in Gazebo mode with the following command:
# Humble版本
ign gazebo
# Jazzy版本
gz sim
Method 2: Start in the ROS2 manner, with the following command:
ros2 launch ros_gz_sim gz_sim.launch.py
The results of both are consistent, as shown in the figure below: In the pop-up window, select the simulation environment and then click therunbutton to run.

Interface Introduction
Next, taking the Empty simulation environment as an example, we will introduce the interface components of Gazebo.

Note: If your Gazebo is not lagging, but the Gazebo is extremely lagging, please confirm that Gazebo is opened with the discrete GPU, not the integrated GPU.
If you do not know how to switch the application's graphics card, you can simply disable the integrated graphics and switch from hybrid output to discrete graphics card output.
Toolbar
- The top toolbar contains two buttons, the file menu button on the left (horizontal stripes) and the plugins button on the right (vertical ellipsis).
- File menu button (horizontal stripes)

- The File menu button contains settings such as saving the simulation environment to a file, saving and loading interface configurations, and customizing the interface style.
- Plugin button on the right (vertical ellipsis)

- The plugin button lists all available plugins. Clicking it will pop up a plugin list; scroll down this list to view all plugins. When one is selected, its interface will appear in the right panel.
3D viewport
- The toolbar in the upper left contains buttons for various geometric bodies (sphere, box, cylinder) and transformation controls. Through the collection body buttons, box, sphere, or cylinder models can be directly inserted into the simulation environment. Simply click the shape to be inserted, then place it into the environment. The shape will automatically snap to the ground plane.


- The main view displays the simulation environment, and we can navigate the scene in different ways using the mouse, with the relevant operations as follows:
左键单击:选择实体
右键单击:打开带有选项的菜单:
Move to:移动到以实体为中心的场景
Follow:选择一个实体让视图保持居中,无论是移动还是平移
Remove:从模拟中删除实体
Copy:复制实体
Past: 粘贴实体
View:显示实体的重心(Center of Mass)、碰撞边界(Collisions)、惯性(Inertia)、
关节(Joints)、坐标系(Frames)、透明度(Transparent)、线框(Wireframe)等属性
左键单击并拖动:在场景中平移
右键单击并拖动:放大和缩小
滚轮向前/向后:放大和缩小
滚轮单击并拖动:旋转场景
- To move this ball, you need to click the move mode in the upper left corner, then left-click to select the object


- At the bottom of the window, from left to right, are the play button, the step size button, and the real-time factor (Real-Time Factor, RTF). Clicking the play button will start running the simulation environment, and clicking it again will pause the run. The step size button is used to set the discrete unit of simulation time, and the step size can be customized by hovering the mouse over the button. The real-time factor indicates the ratio of the simulation running speed relative to real time.

Right panel
The right panel is used to display plugins. The current simulation environment contains two plugins by default: Model and Entity Tree.

-
The Entity Tree displays the list of entities in the simulation environment;
-
After clicking an entity in the Entity Tree, the relevant information of that entity can be displayed in the Model.
-
You can also hold Ctrl and click to select multiple entities;
-
You can also right-click any plugin to open basic settings or close it.

Many plugins are built into Gazebo and can be added by clicking the button on the right side of the toolbar. For example, you can select the Grid Config plugin to adjust the characteristics of the world grid, including cell size, grid position, cell count, or color, etc.
As the application deepens later, some other plugins will also be introduced successively.


1.2.Integration with ROS2
This section will introduce how to implement the integration of Ignition Gazebo with ROS2 to enable interaction between them, for example, the robot's motion can be controlled through ROS2's keyboard control node, and the robot's odometry (odom) data can be displayed in rviz2. The general process is as follows:
-
Start the Ignition Gazebo simulation environment;
-
Establish a connection between ros_gz_bridge and Ignition Gazebo via ROS2;
-
Start nodes related to ROS2 to implement data transmission and reception with Ignition Gazebo.
All integration implementations of Ignition Gazebo with ROS2 basically follow the above process.
Start the simulation environment
When installing Ignition Gazebo, some simulation environments are already built in and can be launched directly. Here we can use a simulation file namedvisualize_lidar.sdfThis file's corresponding simulation environment includes the simulation of a differential drive robot and a lidar. The launch command is as follows:
ign gazebo -v 4 -r visualize_lidar.sdf
#或者
gz sim -v 4 -r visualize_lidar.sdf
Alternatively, it can also be started by ROS2 launch, with the following command:
ros2 launch ros_gz_sim gz_sim.launch.py gz_args:="-v 4 -r visualize_lidar.sdf"
The two methods are essentially the same, both start Ignition Gazebo and load the visualize_lidar.sdf file.

Establish Connection
虽然仿真环境中的机器人已经配置了运动控制插件,可以通过/model/vehicle_blue/cmd_vel话题订阅速度指令并运动,但是Gazebo与ROS2中的消息格式并不一致,所以还需要通过ros_gz_bridge这一桥接功能包,实现二者之间消息的转换,调用指令如下:
ros2 run ros_gz_bridge parameter_bridge /model/vehicle_blue/cmd_vel@geometry_msgs/msg/Twist]gz.msgs.Twist
Through this command, messages of type /model/vehicle_blue/cmd_vel published on the topic geometry_msgs/msg/TwistROS2 can be converted into messages of type gz.msgs.Twist that can be recognized by Gzebo.
Start ROS2 node
Start the keyboard control node of ROS2, and remap the topic to/model/vehicle_blue/cmd_velThe command is as follows:
ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r /cmd_vel:=/model/vehicle_blue/cmd_vel
Next, you can use the keyboard to control the robot's movement.

1.3.ros_gz_bridge
ros_gz_bridge is the bridge connecting ROS2 and Gazebo. The messages used by ROS2 and Gazebo are not compatible and must be converted through ros_gz_bridge.
ros_gz_bridge使用语法
ROS2 Bridging with Gazebo is implemented through the ros_gz_bridge node in the parameter_bridge package, and its usage syntax is as follows:
parameter_bridge [<topic@ROS2_type@Gz_type> ..] [<service@ROS2_srv_type[@Gz_req_type@Gz_rep_type]> ..]
在话题Topic中, 第一个@ 符号是话题名称和消息类型的 分隔符 。
After the first @ symbol is the ROS message type.
After the ROS message type is @,the symbol:
-
@ indicates bidirectional bridging;
-
\[ indicates bridging from Gazebo to ROS;
-
\] indicates bridging from ROS to Gazebo.
The direction symbol is followed by the Gazebo Transport message type.
(the two @ symbols do not have the same meaning)
In the service Service, the first @ symbol is the separator between the service name and type.
After the first @ symbol is the ROS service type. Optionally, include the Gazebo request and response types, separated by an @ symbol between them.
Only supports exposing Gazebo services as ROS services, i.e., the ROS service forwards requests to the Gazebo service, and then forwards the response back to the ROS client.
Bidirectional bridging example:
parameter_bridge /chatter@std_msgs/msg/String@gz.msgs.StringMsg
Example of bridging from Gazebo to ROS:
parameter_bridge /chatter@std_msgs/msg/String[gz.msgs.StringMsg
Example of bridging from ROS to Gazebo:
parameter_bridge /chatter@std_msgs/msg/String]gz.msgs.StringMsg
Service bridging example:
parameter_bridge /world/default/control@ros_gz_interfaces/srv/ControlWorld
或者:
parameter_bridge /world/default/control@ros_gz_interfaces/srv/ControlWorld@gz.msgs.WorldControl@gz.msgs.Boolean
ros_gz_bridge Supported message types
The following lists some mappings currently commonly used by Jazzy and listed in the official documentation; for the complete mappings, refer to the ros2 run ros_gz_bridge parameter_bridge -h and ROS2 corresponding version's ros_gz_bridge documentation.
The following is the correspondence table between ROS2 and topic message types in Gazebo:
| ROS2Message type | Gazebo Transport type |
|---|---|
| builtin_interfaces/msg/Time | gz.msgs.Time |
| geometry_msgs/msg/Point | gz.msgs.Vector3d |
| geometry_msgs/msg/Pose | gz.msgs.Pose |
| geometry_msgs/msg/PoseArray | gz.msgs.Pose_V |
| geometry_msgs/msg/PoseStamped | gz.msgs.Pose |
| geometry_msgs/msg/PoseWithCovariance | gz.msgs.PoseWithCovariance |
| geometry_msgs/msg/Quaternion | gz.msgs.Quaternion |
| geometry_msgs/msg/Transform | gz.msgs.Pose |
| geometry_msgs/msg/TransformStamped | gz.msgs.Pose |
| geometry_msgs/msg/Twist | gz.msgs.Twist |
| geometry_msgs/msg/TwistWithCovariance | gz.msgs.TwistWithCovariance |
| geometry_msgs/msg/TwistWithCovarianceStamped | gz.msgs.TwistWithCovariance |
| geometry_msgs/msg/Vector3 | gz.msgs.Vector3d |
| geometry_msgs/msg/Wrench | gz.msgs.Wrench |
| geometry_msgs/msg/WrenchStamped | gz.msgs.Wrench |
| nav_msgs/msg/Odometry | gz.msgs.Odometry |
| nav_msgs/msg/Odometry | gz.msgs.OdometryWithCovariance |
| rcl_interfaces/msg/ParameterValue | gz.msgs.Any |
| ros_gz_interfaces/msg/Altimeter | gz.msgs.Altimeter |
| ros_gz_interfaces/msg/Contact | gz.msgs.Contact |
| ros_gz_interfaces/msg/Contacts | gz.msgs.Contacts |
| ros_gz_interfaces/msg/Dataframe | gz.msgs.Dataframe |
| ros_gz_interfaces/msg/Entity | gz.msgs.Entity |
| ros_gz_interfaces/msg/Float32Array | gz.msgs.Float_V |
| ros_gz_interfaces/msg/GuiCamera | gz.msgs.GUICamera |
| ros_gz_interfaces/msg/JointWrench | gz.msgs.JointWrench |
| ros_gz_interfaces/msg/Light | gz.msgs.Light |
| ros_gz_interfaces/msg/SensorNoise | gz.msgs.SensorNoise |
| ros_gz_interfaces/msg/StringVec | gz.msgs.StringMsg_V |
| ros_gz_interfaces/msg/TrackVisual | gz.msgs.TrackVisual |
| ros_gz_interfaces/msg/VideoRecord | gz.msgs.VideoRecord |
| ros_gz_interfaces/msg/WorldControl | gz.msgs.WorldControl |
| rosgraph_msgs/msg/Clock* | gz.msgs.Clock* |
| sensor_msgs/msg/BatteryState | gz.msgs.BatteryState |
| sensor_msgs/msg/CameraInfo | gz.msgs.CameraInfo |
| sensor_msgs/msg/FluidPressure | gz.msgs.FluidPressure |
| sensor_msgs/msg/Image | gz.msgs.Image |
| sensor_msgs/msg/Imu | gz.msgs.IMU |
| sensor_msgs/msg/JointState | gz.msgs.Model |
| sensor_msgs/msg/Joy | gz.msgs.Joy |
| sensor_msgs/msg/LaserScan | gz.msgs.LaserScan |
| sensor_msgs/msg/MagneticField | gz.msgs.Magnetometer |
| sensor_msgs/msg/NavSatFix | gz.msgs.NavSat |
| sensor_msgs/msg/PointCloud2 | gz.msgs.PointCloudPacked |
| std_msgs/msg/Bool | gz.msgs.Boolean |
| std_msgs/msg/ColorRGBA | gz.msgs.Color |
| std_msgs/msg/Empty | gz.msgs.Empty |
| std_msgs/msg/Float32 | gz.msgs.Float |
| std_msgs/msg/Float64 | gz.msgs.Double |
| std_msgs/msg/Header | gz.msgs.Header |
| std_msgs/msg/Int32 | gz.msgs.Int32 |
| std_msgs/msg/String | gz.msgs.StringMsg |
| std_msgs/msg/UInt32 | gz.msgs.UInt32 |
| tf2_msgs/msg/TFMessage | gz.msgs.Pose_V |
| trajectory_msgs/msg/JointTrajectory | gz.msgs.JointTrajectory |
There are also some additional types
| ROS 2 Message type | Gazebo Transport type |
|---|---|
| actuator_msgs/msg/Actuators | gz.msgs.Actuators |
| geometry_msgs/msg/PoseWithCovarianceStamped | gz.msgs.PoseWithCovariance |
| geometry_msgs/msg/TwistStamped | gz.msgs.Twist |
| gps_msgs/msg/GPSFix | gz.msgs.NavSat |
| marine_acoustic_msgs/msg/Dvl | gz.msgs.DVLVelocityTracking |
| ros_gz_interfaces/msg/EntityWrench | gz.msgs.EntityWrench |
| ros_gz_interfaces/msg/LogicalCameraImage | gz.msgs.LogicalCameraImage |
| ros_gz_interfaces/msg/LogPlaybackStatistics | gz.msgs.LogPlaybackStatistics |
| ros_gz_interfaces/msg/ParamVec | gz.msgs.Param |
| ros_gz_interfaces/msg/ParamVec | gz.msgs.Param_V |
| ros_gz_interfaces/msg/WorldStatistics | gz.msgs.WorldStatistics |
| sensor_msgs/msg/Range | gz.msgs.LaserScan |
| vision_msgs/msg/Detection2D | gz.msgs.AnnotatedAxisAligned2DBox |
| vision_msgs/msg/Detection2DArray | gz.msgs.AnnotatedAxisAligned2DBox_V |
| vision_msgs/msg/Detection3D | gz.msgs.AnnotatedOriented3DBox |
| vision_msgs/msg/Detection3DArray | gz.msgs.AnnotatedOriented3DBox_V |
And the service message type correspondence table:
| ROS2 message type | Gazebo request | Gazebo response |
|---|---|---|
| ros_gz_interfaces/srv/ControlWorld | gz.msgs.WorldControl | gz.msgs.Boolean |
1.4.Integration and optimization with ROS2
In the Gazebo and ROS2 integration implementation, different commands need to be used in the terminal to start different modules. This process is somewhat complex to implement. This section will introduce how to optimize it using launch files.
Create a new feature package
First, call the following command to create a function package:
ros2 pkg create demo_gazebo_sim

Add directory
Under the newly created feature package, add the directories: launch, rviz, and world. And add the following code in CmakeLists.txt:
install(DIRECTORY rviz world launch DESTINATION share/${PROJECT_NAME})
The launch directory is used to store launch files, the rviz directory is used to store rviz2 configuration files, and the world directory is used to store files related to the Gazebo simulation environment.

Generate the configuration file for rviz2 in the rviz directory
Start rviz2, save the default configuration directly to the rviz directory of the current package, and name the saved file sim.rviz.

Copy the world file
In the worlds directory under the ROS installation path (/opt/ros/jazzy/opt/gz_sim_vendor/share/gz/gz-sim8/worlds/), copy the visualize_lidar.sdf file to the world directory.
If it is not in the above path, it may be in the installation path of ign (humble):
/usr/share/ignition/ignition-gazebo6/worlds
If not already done, manually search for it:
sudo find / -name "visualize_lidar.sdf"

Write a launch file
Create a new launch file gazebo_sim_demo.launch.py in the launch directory, and enter the following content:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.actions import IncludeLaunchDescription
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
def generate_launch_description():
this_pkg = get_package_share_directory('demo_gazebo_sim')
pkg_ros_gz_sim = get_package_share_directory('ros_gz_sim')
world_file = os.path.join(this_pkg,'world','visualize_lidar.sdf')
gz_sim = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(pkg_ros_gz_sim, 'launch', 'gz_sim.launch.py')),
launch_arguments={
'gz_args': '-r ' + world_file
}.items(),
)
# RViz
rviz = Node(
package='rviz2',
executable='rviz2',
arguments=['-d', os.path.join(this_pkg, 'rviz', 'sim.rviz')],
condition=IfCondition(LaunchConfiguration('rviz'))
)
# Bridge
bridge = Node(
package='ros_gz_bridge',
executable='parameter_bridge',
arguments=['/model/vehicle_blue/cmd_vel@geometry_msgs/msg/Twist@gz.msgs.Twist',
'/model/vehicle_blue/odometry@nav_msgs/msg/Odometry@gz.msgs.Odometry',
'/model/vehicle_blue/tf@tf2_msgs/msg/TFMessage[gz.msgs.Pose_V',
],
parameters=[{'qos_overrides./model/vehicle_blue.subscriber.reliability': 'reliable'}],
remappings=[
('/model/vehicle_blue/tf', '/tf'),
('/model/vehicle_blue/cmd_vel','cmd_vel')
],
output='screen'
)
return LaunchDescription([
gz_sim,
DeclareLaunchArgument('rviz', default_value='true',
description='Open RViz.'),
bridge,
rviz
])
In this launch file, the Ignition Gazebo simulation environment is started, a connection between the simulation and ros_gz_bridge is established through ROS2, and the rviz2 node is launched. When establishing the connection, the conversion of messages such as velocity commands, odometry, and coordinate transforms is implemented.

build
In the terminal, enter the current workspace and compile the package:
colcon build --packages-select demo_gazebo_sim

Execute
In the terminal, navigate to the current workspace and run the launch file using the following command:
. install/setup.bash
ros2 launch demo_gazebo_sim gazebo_sim_demo.launch.py
Open a new terminal and start the keyboard control node:
ros2 run teleop_twist_keyboard teleop_twist_keyboard
Then configure rviz2,
- Set
Fixed Frametovehicle_blue/odom, - Add the TF plugin,
- Add the Odometry plugin and set the topic to
/model/vehicle_blue/odometry, When velocity commands are sent via keyboard control, the robot in the simulation environment starts moving, and messages such as coordinate transforms and odometry can be echoed in rviz2.

1.5.Simulation environment creation SDF file
In the previous sections, we used the built-in simulation environment of Ignition Gazebo. Starting from this section, we will introduce how to build a simulation environment by ourselves. The example in this section will simulate a rectangular room 10 m long and 5 m wide. In this example, you can first launch Ignition Gazebo and build the simulation environment by dragging and dropping, and then modify the files corresponding to the simulation environment to adjust the details.
Relationship between SDF, URDF, and Xacro:
-
URDF 和 SDF 的区别:
-
复杂性: SDF 支持的功能更强大,能够描述完整的仿真环境;URDF 更适合定义机器人模型。
-
用途: URDF 是 ROS 的标准;SDF 是 Gazebo 的标准。
-
物理引擎支持: URDF 通过插件支持 Gazebo;SDF 原生支持 Gazebo。
-
格式转换: URDF 可以转换为 SDF(通过 ROS 提供的工具
gz sdf -p)。
-
-
Xacro 的作用:
- Xacro 是 URDF 的生成工具,帮助用户高效编写 URDF 文件,但它与 SDF 无直接关系。
Practical Recommendations
-
In Gazebo simulation: If you are using ROS 2 and Gazebo, you can directly use SDF files, which offer more powerful features.
-
In ROS: If it is mainly used for robot control and planning, URDF or URDF generated by Xacro is recommended.
-
Combining both: Use URDF for control and SDF for simulation. For example, after defining the robot structure with URDF, convert it to SDF with the help of Gazebo plugins.
Illustrative comparison
URDF example:
<robot name="example_robot">
<link name="base_link">
<inertial>
<mass value="1.0" />
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0" />
</inertial>
</link>
</robot>
Xacro example (generating URDF):
<xacro:robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="example_robot">
<xacro:macro name="base_link" params="mass">
<link name="base_link">
<inertial>
<mass value="${mass}" />
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0" />
</inertial>
</link>
</xacro:macro>
<xacro:base_link mass="1.0" />
</xacro:robot>
SDF Example:
<sdf version="1.6">
<model name="example_robot">
<link name="base_link">
<inertial>
<mass>1.0</mass>
<inertia>
<ixx>1.0</ixx>
<iyy>1.0</iyy>
<izz>1.0</izz>
</inertia>
</inertial>
</link>
</model>
</sdf>
1. Create an SDF file
First, call the commandgz simStart Gazebo, select the Empty simulation environment, then add cubes, where each cube corresponds to a wall.
The coordinates corresponding to the up, down, left, and right cubes box, box_1, box_2, box_3 are (5.0,0.0,0.5), (-5.0,0.0,0.5), (0.0,2.5,0.5), (0.0,-2.5,0.5), respectively.
(The above coordinates refer to X, Y, Z coordinates, with no rotation angles.)
Save the file to the world directory of the package. The saved file name must have the .sdf suffix. Here, the file name is house.sdf.


<sdf version='1.10'>
<world name='empty'>
<physics name='1ms' type='ignored'>
<max_step_size>0.001</max_step_size>
<real_time_factor>1</real_time_factor>
<real_time_update_rate>1000</real_time_update_rate>
</physics>
<plugin name='gz::sim::systems::Physics' filename='gz-sim-physics-system'/>
<plugin name='gz::sim::systems::UserCommands' filename='gz-sim-user-commands-system'/>
<plugin name='gz::sim::systems::SceneBroadcaster' filename='gz-sim-scene-broadcaster-system'/>
<plugin name='gz::sim::systems::Contact' filename='gz-sim-contact-system'/>
<gravity>0 0 -9.8</gravity>
<magnetic_field>6e-06 2.3e-05 -4.2e-05</magnetic_field>
<atmosphere type='adiabatic'/>
<scene>
<ambient>0.4 0.4 0.4 1</ambient>
<background>0.7 0.7 0.7 1</background>
<shadows>true</shadows>
</scene>
<model name='ground_plane'>
<static>true</static>
<link name='link'>
<collision name='collision'>
<geometry>
<plane>
<normal>0 0 1</normal>
<size>100 100</size>
</plane>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='visual'>
<geometry>
<plane>
<normal>0 0 1</normal>
<size>100 100</size>
</plane>
</geometry>
<material>
<ambient>0.8 0.8 0.8 1</ambient>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.8 0.8 0.8 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<inertial>
<pose>0 0 0 0 -0 0</pose>
<mass>100</mass>
<inertia>
<ixx>1</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>1</iyy>
<iyz>0</iyz>
<izz>1</izz>
</inertia>
</inertial>
<enable_wind>false</enable_wind>
</link>
<pose>0 0 0 0 -0 0</pose>
<self_collide>false</self_collide>
</model>
<model name='box'>
<pose>5.0 0 0.5 -0 0 0</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<model name='box_0'>
<pose>-5.0 -0 0.50000 -0 0 0</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<model name='box_1'>
<pose>-0 -2.5 0.5 -0 -0 -0</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<model name='box_2'>
<pose>-0 2.5 0.5 0 -0 -0</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<light name='sun' type='directional'>
<pose>0 0 10 0 -0 0</pose>
<cast_shadows>true</cast_shadows>
<intensity>1</intensity>
<direction>-0.5 0.1 -0.9</direction>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.2 0.2 0.2 1</specular>
<attenuation>
<range>1000</range>
<linear>0.01</linear>
<constant>0.90000000000000002</constant>
<quadratic>0.001</quadratic>
</attenuation>
<spot>
<inner_angle>0</inner_angle>
<outer_angle>0</outer_angle>
<falloff>0</falloff>
</spot>
</light>
</world>
</sdf>
2. Modify the SDF file
Modify the SDF file to adjust the dimensions of the cubes and achieve an enclosed wall. In the SDF file, the four cubes correspond respectively to four<model>tags, whosenameattributes are respectivelybox、box_1、box_2、box_3; change theboxandbox_1in<size>1 1 1</size>to<size>0.1 5 1</size>; change thebox_2andbox_3in<size>1 1 1</size>to<size>10 0.1 1</size>(Note: under each<model>tag, there are two<size>tags, located respectively under the<collision>tag and the<visual>tag; the contents of both<size>tags need to be modified).
The modified file content with house.sdf is as follows:
<sdf version='1.10'>
<world name='empty'>
<physics name='1ms' type='ignored'>
<max_step_size>0.001</max_step_size>
<real_time_factor>1</real_time_factor>
<real_time_update_rate>1000</real_time_update_rate>
</physics>
<plugin name='gz::sim::systems::Physics' filename='gz-sim-physics-system'/>
<plugin name='gz::sim::systems::UserCommands' filename='gz-sim-user-commands-system'/>
<plugin name='gz::sim::systems::SceneBroadcaster' filename='gz-sim-scene-broadcaster-system'/>
<plugin name='gz::sim::systems::Contact' filename='gz-sim-contact-system'/>
<gravity>0 0 -9.8</gravity>
<magnetic_field>6e-06 2.3e-05 -4.2e-05</magnetic_field>
<atmosphere type='adiabatic'/>
<scene>
<ambient>0.4 0.4 0.4 1</ambient>
<background>0.7 0.7 0.7 1</background>
<shadows>true</shadows>
</scene>
<model name='ground_plane'>
<static>true</static>
<link name='link'>
<collision name='collision'>
<geometry>
<plane>
<normal>0 0 1</normal>
<size>100 100</size>
</plane>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='visual'>
<geometry>
<plane>
<normal>0 0 1</normal>
<size>100 100</size>
</plane>
</geometry>
<material>
<ambient>0.8 0.8 0.8 1</ambient>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.8 0.8 0.8 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<inertial>
<pose>0 0 0 0 -0 0</pose>
<mass>100</mass>
<inertia>
<ixx>1</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>1</iyy>
<iyz>0</iyz>
<izz>1</izz>
</inertia>
</inertial>
<enable_wind>false</enable_wind>
</link>
<pose>0 0 0 0 -0 0</pose>
<self_collide>false</self_collide>
</model>
<model name='box'>
<pose>5.0 0 0.5 -0 0 0</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<model name='box_0'>
<pose>-5.0 -0 0.50000 -0 0 0</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<model name='box_1'>
<pose>-0 -2.5 0.5 -0 -0 -0</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<model name='box_2'>
<pose>-0 2.5 0.5 0 -0 -0</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<light name='sun' type='directional'>
<pose>0 0 10 0 -0 0</pose>
<cast_shadows>true</cast_shadows>
<intensity>1</intensity>
<direction>-0.5 0.1 -0.9</direction>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.2 0.2 0.2 1</specular>
<attenuation>
<range>1000</range>
<linear>0.01</linear>
<constant>0.90000000000000002</constant>
<quadratic>0.001</quadratic>
</attenuation>
<spot>
<inner_angle>0</inner_angle>
<outer_angle>0</outer_angle>
<falloff>0</falloff>
</spot>
</light>
</world>
</sdf>
3. Write launch files
Create a new launch file gazebo_sim_world.launch.py in the launch directory, and enter the following content:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
def generate_launch_description():
this_pkg = get_package_share_directory('demo_gazebo_sim')
pkg_ros_gz_sim = get_package_share_directory('ros_gz_sim')
world_file = os.path.join(this_pkg,"world","house.sdf")
gz_sim = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(pkg_ros_gz_sim, 'launch', 'gz_sim.launch.py')),
launch_arguments={
'gz_args': '-r ' + world_file
}.items(),
)
return LaunchDescription([
gz_sim
])
4. Construction
In the terminal, enter the current workspace and compile the package:
colcon build --packages-select demo_gazebo_sim
5. Execution
In the terminal, navigate to the current workspace and run the launch file using the following command:
. install/setup.bash
ros2 launch demo_gazebo_sim gazebo_sim_world.launch.py
The execution result is shown in the figure below.
You can also continue designing the room model according to personal preference.

1.6.Add Gz Sim Model
Many simulation models are provided on the official Gazebo website, which can be downloaded and used to optimize the simulation environment, making it more diverse, visually appealing, and realistic.
Resource Download
Official model links for Gazebo simulation:
https://app.gazebosim.org/fuel/models
Select a simulation model on your own and click to enter the details page, then click the download button to save the model resource locally.
在用户目录下新建ign_models目录,将下载的资源解压缩到该目录以作备用。
这个目录的名字随便起,你想起什么起什么,但是前提是纯英文,符合Linux命名规则,别有非法。
Resource Configuration
To enable Gazebo to recognize the model resources, the next step is to modify the .bashrc file in the user directory and add the following code:
# Jazzy版本的宏如下:
export GZ_SIM_RESOURCE_PATH=~/ign_models
# Humble版本一般是下面的
export IGN_GAZEBO_RESOURCE_PATH=~/ign_models
https://gazebosim.org/docs/latest/fuel_insert/
Add Model
In the terminal, enter the world directory of the package demo_gazebo_sim and use the commandgz sim house.sdfto launch the simulation environment, click the collapse button in the upper right of the window, search forResource Spawnerand open it, clickLocal resourcesand select a model to drag into the simulation environment. Save the modified content to the house.sdf file.

After the resources are downloaded normally, they will be displayed here in local resources.
house.sdf文件示例内容如下:
<sdf version='1.10'>
<world name='empty'>
<physics name='1ms' type='ignored'>
<max_step_size>0.001</max_step_size>
<real_time_factor>1</real_time_factor>
<real_time_update_rate>1000</real_time_update_rate>
</physics>
<plugin name='gz::sim::systems::Physics' filename='gz-sim-physics-system'/>
<plugin name='gz::sim::systems::UserCommands' filename='gz-sim-user-commands-system'/>
<plugin name='gz::sim::systems::SceneBroadcaster' filename='gz-sim-scene-broadcaster-system'/>
<plugin name='gz::sim::systems::Contact' filename='gz-sim-contact-system'/>
<gravity>0 0 -9.8</gravity>
<magnetic_field>6e-06 2.3e-05 -4.2e-05</magnetic_field>
<atmosphere type='adiabatic'/>
<scene>
<ambient>0.4 0.4 0.4 1</ambient>
<background>0.7 0.7 0.7 1</background>
<shadows>true</shadows>
</scene>
<model name='ground_plane'>
<static>true</static>
<link name='link'>
<collision name='collision'>
<geometry>
<plane>
<normal>0 0 1</normal>
<size>100 100</size>
</plane>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='visual'>
<geometry>
<plane>
<normal>0 0 1</normal>
<size>100 100</size>
</plane>
</geometry>
<material>
<ambient>0.8 0.8 0.8 1</ambient>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.8 0.8 0.8 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<inertial>
<pose>0 0 0 0 -0 0</pose>
<mass>100</mass>
<inertia>
<ixx>1</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>1</iyy>
<iyz>0</iyz>
<izz>1</izz>
</inertia>
</inertial>
<enable_wind>false</enable_wind>
</link>
<pose>0 0 0 0 -0 0</pose>
<self_collide>false</self_collide>
</model>
<model name='box'>
<pose>5.05017 0 0.5 -0 0 3e-05</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<model name='box_0'>
<pose>-5.05003 -7.8e-05 0.5 -0 -0 1.8e-05</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>0.1 5 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<model name='box_1'>
<pose>0.000162 -2.53144 0.499999 0 -0 -0.001876</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<model name='box_2'>
<pose>0.00012 2.51517 0.499999 0 -0 -0.00303</pose>
<link name='box_link'>
<inertial>
<inertia>
<ixx>16.666</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>16.666</iyy>
<iyz>0</iyz>
<izz>16.666</izz>
</inertia>
<mass>100</mass>
<pose>0 0 0 0 -0 0</pose>
</inertial>
<collision name='box_collision'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<surface>
<friction>
<ode/>
</friction>
<bounce/>
<contact/>
</surface>
</collision>
<visual name='box_visual'>
<geometry>
<box>
<size>10 0.1 1</size>
</box>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1</ambient>
<diffuse>0.7 0.7 0.7 1</diffuse>
<specular>1 1 1 1</specular>
</material>
</visual>
<pose>0 0 0 0 -0 0</pose>
<enable_wind>false</enable_wind>
</link>
<static>true</static>
<self_collide>false</self_collide>
</model>
<!-- Jazzy 迁移说明开始:下面是本机 file:// 模型目录,ign_models 只是文件夹名,不属于 Gazebo API,因此保留不改。 -->
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/bed</uri>
<name>Bed</name>
<pose>2.11564 -0.080355 0 0 -0 0</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet</name>
<pose>-1.69505 1.99357 0 0 0 -1.59625</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet_1</name>
<pose>-1.71706 1.10132 0 0 0 -1.59625</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet_2</name>
<pose>-1.74096 0.206747 0 0 0 -1.59625</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet_3</name>
<pose>-1.76908 -0.654114 0 0 0 -1.59625</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet_4</name>
<pose>-3.54096 -1.97391 0 0 0 -1.59625</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet_4_1</name>
<pose>-3.51784 -1.10026 0 0 0 -1.59625</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet_4_1_1</name>
<pose>-3.44853 2.02068 0 0 0 -1.59625</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet_4_1_1_1</name>
<pose>-3.499 -0.197449 0 0 0 -1.59625</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet_3_1</name>
<pose>-0.465147 -0.678914 0 0 0 -1.59625</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet_3_1_1</name>
<pose>-1.12325 -0.88551 0 0 0 -0.016899</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet_3_1_2</name>
<pose>-0.445223 0.182127 0 0 0 -1.59625</pose>
</include>
<include>
<uri>file:///home/tungchiahui/UserFolder/MySource/ROS_WS/ign_models/WhiteCabinet</uri>
<name>WhiteCabinet_3_1_2_1</name>
<pose>-0.422109 1.02215 0 0 0 -1.59625</pose>
</include>
<!-- Jazzy 迁移说明结束:如果以后重命名本机模型目录,需要同步修改这些 file:// 路径。 -->
<light name='sun' type='directional'>
<pose>0 0 10 0 -0 0</pose>
<cast_shadows>true</cast_shadows>
<intensity>1</intensity>
<direction>-0.5 0.1 -0.9</direction>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.2 0.2 0.2 1</specular>
<attenuation>
<range>1000</range>
<linear>0.01</linear>
<constant>0.90000000000000002</constant>
<quadratic>0.001</quadratic>
</attenuation>
<spot>
<inner_angle>0</inner_angle>
<outer_angle>0</outer_angle>
<falloff>0</falloff>
</spot>
</light>
</world>
</sdf>
build
In the terminal, enter the current workspace and compile the package:
colcon build --packages-select demo_gazebo_sim
Execute
In the terminal, navigate to the current workspace and run the launch file using the following command:
. install/setup.bash
ros2 launch demo_gazebo_sim gazebo_sim_world.launch.py
The execution result is shown in the figure below.

1.7.Add a robot in Gazebo
In Gazebo, a robot model can be created directly, or a robot model in URDF format can be loaded from ROS2; here we use the latter (you may also choose to use your own URDF car, but be sure to modify the path in the launch file).
We can use the model from the previously createdcpp06_urdf.
Prepare the robot model package
Enter the following command in the workspace to create a feature package.mycar_description
cd ./src
ros2 pkg create mycar_description --build-type ament_cmake
cd ..
Create the following folders under the feature package.launch,urdf,rviz,meshes
Modify the following configuration file:
1. package.xml: In package.xml, some runtime dependencies need to be added manually, and the core content is as follows:
<exec_depend>rviz2</exec_depend>
<exec_depend>xacro</exec_depend>
<exec_depend>robot_state_publisher</exec_depend>
<exec_depend>joint_state_publisher</exec_depend>
<exec_depend>ros2launch</exec_depend>
2.CMakeLists.txt Under the function package, several directories have been newly created. Installation paths need to be configured for these directories. The core content is as follows:
install(
DIRECTORY launch urdf rviz meshes
DESTINATION share/${PROJECT_NAME}
)
把之前cpp06_urdf功能包中的urdf文件夹里的内容覆盖到mycar_description功能包中的urdf文件夹中。
ign_models中新建mycar_description目录,并将功能包mycar_description下的mesh目录复制进ign_models中的mycar_description目录。(此时meshes里是空的,很正常,你后续用到其他模型的时候可能才会有.STL文件)
Add the SDF-specific tags required by Gazebo
First modifycar_base.urdf.xacro to add various inertia, collision, friction, etc.:
<robot xmlns:xacro="http://wiki.ros.org/xacro">
<!-- PI 值 -->
<xacro:property name="PI" value="3.1416"/>
<!-- 定义车辆参数 -->
<!-- 车体长宽高 -->
<xacro:property name="base_link_x" value="0.2"/>
<xacro:property name="base_link_y" value="0.12"/>
<xacro:property name="base_link_z" value="0.07"/>
<!-- 离地间距 -->
<xacro:property name="distance" value="0.015"/>
<!-- 车轮半径 宽度 -->
<xacro:property name="wheel_radius" value="0.025"/>
<xacro:property name="wheel_length" value="0.02"/>
<!-- Gazebo 新增开始:Gazebo 物理参数,用于给 link 生成 inertial -->
<xacro:property name="base_footprint_radius" value="0.001"/>
<xacro:property name="base_footprint_mass" value="0.001"/>
<xacro:property name="base_link_mass" value="1.0"/>
<xacro:property name="wheel_mass" value="0.05"/>
<xacro:property name="wheel_mu1" value="1.0"/>
<xacro:property name="wheel_mu2" value="0.05"/>
<!-- Gazebo 新增结束:Gazebo 物理参数 -->
<!-- Gazebo 新增开始:球体惯性宏,给 base_footprint 使用 -->
<xacro:macro name="sphere_inertial" params="mass radius">
<inertial>
<mass value="${mass}"/>
<inertia
ixx="${2.0 / 5.0 * mass * radius * radius}" ixy="0.0" ixz="0.0"
iyy="${2.0 / 5.0 * mass * radius * radius}" iyz="0.0"
izz="${2.0 / 5.0 * mass * radius * radius}"/>
</inertial>
</xacro:macro>
<!-- Gazebo 新增结束:球体惯性宏 -->
<!-- Gazebo 新增开始:长方体惯性宏,给 base_link 使用 -->
<xacro:macro name="box_inertial" params="mass x y z">
<inertial>
<mass value="${mass}"/>
<inertia
ixx="${mass / 12.0 * (y * y + z * z)}" ixy="0.0" ixz="0.0"
iyy="${mass / 12.0 * (x * x + z * z)}" iyz="0.0"
izz="${mass / 12.0 * (x * x + y * y)}"/>
</inertial>
</xacro:macro>
<!-- Gazebo 新增结束:长方体惯性宏 -->
<!-- Gazebo 新增开始:圆柱车轮惯性宏,给四个轮子使用 -->
<xacro:macro name="wheel_inertial" params="mass radius length">
<inertial>
<mass value="${mass}"/>
<inertia
ixx="${mass / 12.0 * (3.0 * radius * radius + length * length)}" ixy="0.0" ixz="0.0"
iyy="${mass / 2.0 * radius * radius}" iyz="0.0"
izz="${mass / 12.0 * (3.0 * radius * radius + length * length)}"/>
</inertial>
</xacro:macro>
<!-- Gazebo 新增结束:圆柱车轮惯性宏 -->
<!-- 定义颜色 -->
<material name="yellow">
<color rgba="0.7 0.7 0 0.8" />
</material>
<material name="red">
<color rgba="0.8 0.1 0.1 0.8" />
</material>
<material name="gray">
<color rgba="0.2 0.2 0.2 0.95" />
</material>
<!-- 定义 base_footprint -->
<link name="base_footprint">
<visual>
<geometry>
<!-- Codex 修改:把原来的固定 0.001 改成上面新增的参数 -->
<sphere radius="${base_footprint_radius}"/>
</geometry>
</visual>
<!-- Gazebo 新增:base_footprint 的惯性,避免 Gazebo 转 SDF 时丢弃根 link -->
<xacro:sphere_inertial mass="${base_footprint_mass}" radius="${base_footprint_radius}"/>
</link>
<!-- 定义 base_link -->
<link name="base_link">
<visual>
<!-- 形状 -->
<geometry>
<box size="${base_link_x} ${base_link_y} ${base_link_z}" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="yellow"/>
</visual>
<!-- Gazebo 新增开始:base_link 碰撞体,Gazebo 物理仿真需要 collision -->
<collision>
<geometry>
<box size="${base_link_x} ${base_link_y} ${base_link_z}" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
</collision>
<!-- Gazebo 新增结束:base_link 碰撞体 -->
<!-- Gazebo 新增:base_link 惯性,Gazebo 物理仿真需要 inertial -->
<xacro:box_inertial mass="${base_link_mass}" x="${base_link_x}" y="${base_link_y}" z="${base_link_z}"/>
</link>
<joint name="baselink2basefootprint" type="fixed">
<parent link="base_footprint"/>
<child link="base_link"/>
<origin xyz="0.0 0.0 ${distance + base_link_z / 2}"/>
</joint>
<!-- 车轮宏定义 -->
<xacro:macro name="wheel_func" params="wheel_name is_front is_left" >
<link name="${wheel_name}_wheel">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}" />
</geometry>
<origin xyz="0 0 0" rpy="${PI / 2} 0 0" />
<material name="gray"/>
</visual>
<!-- Gazebo 新增开始:车轮碰撞体,四个轮子都会通过这个宏生成 -->
<collision>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}" />
</geometry>
<origin xyz="0 0 0" rpy="${PI / 2} 0 0" />
</collision>
<!-- Gazebo 新增结束:车轮碰撞体 -->
<!-- Gazebo 新增:车轮惯性,四个轮子都会通过这个宏生成 -->
<xacro:wheel_inertial mass="${wheel_mass}" radius="${wheel_radius}" length="${wheel_length}"/>
</link>
<joint name="${wheel_name}2baselink" type="continuous">
<parent link="base_link" />
<child link="${wheel_name}_wheel" />
<origin xyz="${(base_link_x / 2 - wheel_radius) * is_front} ${base_link_y / 2 * is_left} ${(base_link_z / 2 + distance - wheel_radius) * -1}" rpy="0 0 0" />
<axis xyz="0 1 0" />
</joint>
<!-- Gazebo 新增:四轮差速原地转弯需要轮胎横向滑移;降低 mu2 可以减少 Gazebo 中的卡顿和抖动 -->
<gazebo reference="${wheel_name}_wheel">
<mu1>${wheel_mu1}</mu1>
<mu2>${wheel_mu2}</mu2>
<fdir1>1 0 0</fdir1>
</gazebo>
</xacro:macro>
<!-- 车轮宏调用 -->
<xacro:wheel_func wheel_name="left_front" is_front="1" is_left="1" />
<xacro:wheel_func wheel_name="left_back" is_front="-1" is_left="1" />
<xacro:wheel_func wheel_name="right_front" is_front="1" is_left="-1" />
<xacro:wheel_func wheel_name="right_back" is_front="-1" is_left="-1" />
</robot>
Modifycar.urdf.xacro:
<robot name="car" xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:include filename="car_base.urdf.xacro"/>
<xacro:include filename="car_camera.urdf.xacro"/>
<xacro:include filename="car_laser.urdf.xacro"/>
</robot>
Create a new launch file under the robot model function package.
Create a new launch file mycar_desc_sim.launch.py and enter the following content:
from launch import LaunchDescription
from launch_ros.actions import Node
import os
from ament_index_python.packages import get_package_share_directory
from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command,LaunchConfiguration
from launch.actions import DeclareLaunchArgument
def generate_launch_description():
mycar_description = get_package_share_directory("mycar_description")
default_model_path = os.path.join(mycar_description,"urdf/xacro","car.urdf.xacro")
model = DeclareLaunchArgument(name="model", default_value=default_model_path)
# 加载机器人模型
# 启动 robot_state_publisher 节点并以参数方式加载 urdf 文件
robot_description = ParameterValue(Command(["xacro ",LaunchConfiguration("model")]))
robot_state_publisher = Node(
package="robot_state_publisher",
executable="robot_state_publisher",
parameters=[{"robot_description": robot_description}]
)
return LaunchDescription([
model,
robot_state_publisher,
])
Compared with previous versions, this file lacks the joint_state_publisher node, whose function is to publish articulated joint states; this functionality was subsequently implemented by ignition.
Add Robot Model
Create the gazebo_sim_robot_world.launch.py file, include the launch file for the robot model, and spawn the robot model in Gazebo. The modified code is as follows:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
def generate_launch_description():
this_pkg = get_package_share_directory("demo_gazebo_sim")
mycar_desc_pkg = get_package_share_directory("mycar_description")
pkg_ros_gz_sim = get_package_share_directory("ros_gz_sim")
world_file = os.path.join(this_pkg,"world","house.sdf")
gz_sim = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(pkg_ros_gz_sim, "launch", "gz_sim.launch.py")),
launch_arguments={
"gz_args": "-r " + world_file
}.items(),
)
mycar_desc = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(mycar_desc_pkg,"launch","mycar_desc_sim.launch.py")
)
)
spawn = Node(package="ros_gz_sim", executable="create",
arguments=[
"-name", "mycar",
"-x", "0",
"-z", "0.01", #设置为0,可能会陷进地里
"-y", "0",
"-R", "0",
"-P", "0",
"-Y", "1.57", # Yaw航向角逆时针旋转90度
"-topic", "/robot_description"],
output="screen")
return LaunchDescription([
gz_sim,
spawn,
mycar_desc,
])
build
In the terminal, enter the current workspace and compile the package:
colcon build --packages-select mycar_description demo_gazebo_sim
Execute
In the terminal, enter the current workspace and call the following command to execute the launch file: (If there are problems running it, as long as you have learned how to run URDF, you should have the ability to find errors yourself—go find them on your own.)
. install/setup.bash
ros2 launch demo_gazebo_sim gazebo_sim_robot_world.launch.py
The execution result is shown in the figure below.

1.8.Gz Sim Motion Controller
This section will introduce how to make your robot move.
The principle is to add to urdf or xacro, etc.Tags:
http://sdformat.org/tutorials?tut=sdformat_urdf_extensions&cat=specification&

https://gazebosim.org/api/plugin/2/index.html
Install library: Official tutorial: Enter https://gazebosim.org/docs/harmonic/library_reference_nav/ Click the plugin to enter https://gazebosim.org/api/plugin/2/installation.html
sudo apt-get update
sudo apt-get install lsb-release
sudo sh -c 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list'
wget http://packages.osrfoundation.org/gazebo.key -O - | sudo apt-key add -
sudo apt-get update
sudo apt install libgz-plugin2-dev
Use plugins to make the car move, such as a two-wheel differential plugin, a four-wheel Mecanum wheel plugin, etc.
At the same time, this plugin also provides some options that can control the output. Because it is a simulation, it is also necessary to tell the plugin information such as the names of the joints corresponding to the wheels, so the following parameter table is obtained:
| Configuration Item | Meaning |
|---|---|
| ros | ROS-related configuration, including namespace and topic remapping, etc. |
| update_rate | Data update rate |
| left_joint | Left wheel joint name |
| right_joint | Right wheel joint name |
| wheel_separation | Distance between left and right wheels |
| wheel_diameter | Wheel diameter |
| max_wheel_torque | Maximum wheel torque |
| max_wheel_acceleration | Maximum wheel acceleration |
| publish_odom | Whether to publish odometry |
| publish_odom_tf | Whether to publish the odometry TF switch |
| publish_wheel_tf | Whether to publish the wheel TF data switch |
| odometry_frame | The frame ID of the odometry, which is ultimately reflected in the topic and TF |
| robot_base_frame | The ID of the robot's base frame |
Modify the URDF file
For a two-wheel differential drive vehicle, under the<robot>Add the following code under the root tag:
<gazebo>
<plugin filename="gz-sim-diff-drive-system"
name="gz::sim::systems::DiffDrive">
<left_joint>left_joint</left_joint>
<right_joint>right_joint</right_joint>
<wheel_separation>0.2097</wheel_separation>
<wheel_radius>0.03415</wheel_radius>
<odom_publish_frequency>10</odom_publish_frequency>
<frame_id>odom</frame_id>
<child_frame_id>base_footprint</child_frame_id>
<topic>/cmd_vel</topic>
<max_linear_acceleration>10</max_linear_acceleration>
<min_linear_acceleration>-10</min_linear_acceleration>
<max_angular_acceleration>10</max_angular_acceleration>
<min_angular_acceleration>-10</min_angular_acceleration>
<max_linear_velocity>0.5</max_linear_velocity>
<min_linear_velocity>-0.5</min_linear_velocity>
<max_angular_velocity>1</max_angular_velocity>
<min_angular_velocity>-1</min_angular_velocity>
</plugin>
</gazebo>
<gazebo>
<plugin filename="gz-sim-joint-state-publisher-system"
name="gz::sim::systems::JointStatePublisher">
</plugin>
</gazebo>
For a 4-wheel differential drive vehicle, under the<robot>Add the following code under the root tag:
<gazebo>
<plugin
filename="gz-sim-diff-drive-system"
name="gz::sim::systems::DiffDrive">
<left_joint>left_former_joint</left_joint>
<left_joint>left_rear_joint</left_joint>
<right_joint>right_former_joint</right_joint>
<right_joint>right_rear_joint</right_joint>
<wheel_separation>0.4</wheel_separation>
<wheel_radius>0.0415</wheel_radius>
<odom_publish_frequency>50</odom_publish_frequency>
<frame_id>odom</frame_id>
<child_frame_id>base_footprint</child_frame_id>
<topic>/cmd_vel</topic>
<max_linear_acceleration>10</max_linear_acceleration>
<min_linear_acceleration>-10</min_linear_acceleration>
<max_angular_acceleration>10</max_angular_acceleration>
<min_angular_acceleration>-10</min_angular_acceleration>
<max_linear_velocity>0.5</max_linear_velocity>
<min_linear_velocity>-0.5</min_linear_velocity>
<max_angular_velocity>1</max_angular_velocity>
<min_angular_velocity>-1</min_angular_velocity>
</plugin>
</gazebo>
<gazebo>
<plugin filename="gz-sim-joint-state-publisher-system"
name="gz::sim::systems::JointStatePublisher">
</plugin>
</gazebo>
Ours here is definitely a four-wheeled vehicle. I created amove_control.urdf.xacrodedicated location for motion control code:
<robot name="car" xmlns:xacro="http://wiki.ros.org/xacro">
<!-- 运动控制插件 -->
<gazebo>
<plugin
filename="gz-sim-diff-drive-system"
name="gz::sim::systems::DiffDrive">
<left_joint>left_front2baselink</left_joint>
<left_joint>left_back2baselink</left_joint>
<right_joint>right_front2baselink</right_joint>
<right_joint>right_back2baselink</right_joint>
<!-- wheel_separation轮距 是左右轮中心线之间的距离:0.06 - (-0.06) = 0.12m -->
<wheel_separation>0.12</wheel_separation>
<wheel_radius>0.025</wheel_radius>
<odom_publish_frequency>50</odom_publish_frequency>
<frame_id>odom</frame_id>
<child_frame_id>base_footprint</child_frame_id>
<topic>/cmd_vel</topic>
<max_linear_acceleration>10</max_linear_acceleration>
<min_linear_acceleration>-10</min_linear_acceleration>
<max_angular_acceleration>10</max_angular_acceleration>
<min_angular_acceleration>-10</min_angular_acceleration>
<max_linear_velocity>0.5</max_linear_velocity>
<min_linear_velocity>-0.5</min_linear_velocity>
<max_angular_velocity>1</max_angular_velocity>
<min_angular_velocity>-1</min_angular_velocity>
</plugin>
</gazebo>
<!-- 关节状态发布 -->
<gazebo>
<plugin filename="gz-sim-joint-state-publisher-system"
name="gz::sim::systems::JointStatePublisher">
</plugin>
</gazebo>
</robot>
Modify the launch file
Modify the gazebo_sim_robot_world.launch.py file. The modified code is as follows:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
def generate_launch_description():
this_pkg = get_package_share_directory("demo_gazebo_sim")
mycar_desc_pkg = get_package_share_directory("mycar_description")
pkg_ros_gz_sim = get_package_share_directory("ros_gz_sim")
world_file = os.path.join(this_pkg,"world","house.sdf")
gz_sim = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(pkg_ros_gz_sim, "launch", "gz_sim.launch.py")),
launch_arguments={
"gz_args": "-r " + world_file
}.items(),
)
mycar_desc = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(mycar_desc_pkg,"launch","mycar_desc_sim.launch.py")
)
)
spawn = Node(package="ros_gz_sim", executable="create",
arguments=[
"-name", "mycar",
"-x", "-4",
"-z", "0.01", #设置为0,可能会陷进地里
"-y", "0",
"-R", "0",
"-P", "0",
"-Y", "1.57", #逆时针旋转90度
"-topic", "/robot_description"],
output="screen")
# Bridge
bridge = Node(
package="ros_gz_bridge",
executable="parameter_bridge",
arguments=["/cmd_vel@geometry_msgs/msg/Twist@gz.msgs.Twist",
"/model/mycar/odometry@nav_msgs/msg/Odometry@gz.msgs.Odometry",
"/model/mycar/tf@tf2_msgs/msg/TFMessage[gz.msgs.Pose_V",
"/clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock",
"/world/empty/model/mycar/joint_state@sensor_msgs/msg/JointState[gz.msgs.Model",
],
parameters=[{"qos_overrides./model/mycar.subscriber.reliability": "reliable"}],
remappings=[
("/model/mycar/tf", "/tf"),
("/world/empty/model/mycar/joint_state","joint_states"),
("/model/mycar/odometry","/odom")
],
output="screen"
)
return LaunchDescription([
gz_sim,
spawn,
mycar_desc,
bridge
])
build
In the terminal, enter the current workspace and compile the package:
colcon build --packages-select mycar_description demo_gazebo_sim
Execute
In the terminal, navigate to the current workspace and run the launch file using the following command:
. install/setup.bash
ros2 launch demo_gazebo_sim gazebo_sim_robot_world.launch.py
Then restart the keyboard control node, and you can control the robot's movement.
ros2 run teleop_twist_keyboard teleop_twist_keyboard
You can also start rviz2 to view odometry messages and coordinate transformations. In the terminal, enter the current workspace and call the following command to execute the launch file:
Start rviz2
. install/setup.bash
rviz2
The RVIZ2 software configuration is shown in the following figure:



1.9.Gz Sim Simulation: Sensors
This section will introduce how to add sensors such as lidar and cameras to a simulated robot. Adding Sensor Plugins
Before performing sensor simulation, you need to first add a plugin namedgz-sim-sensors-systemOpen the URDF file and add the following code within the<robot>root tag: (It is recommended to create agazebo_sensor.urdf.xacrodedicated storage)
<gazebo>
<plugin
filename="gz-sim-sensors-system"
name="gz::sim::systems::Sensors">
<render_engine>ogre2</render_engine>
</plugin>
</gazebo>
gz-sim-sensors-system is a plugin for the Gazebo simulation environment that provides sensor models and related functionality for creating, simulating, and testing various sensor devices. It includes common sensor models such as cameras, LiDAR, and more.
Add various sensors
(Note that your model must include models of the following sensors)
The radar model does not need collision, please delete it, otherwise it will block the laser from being emitted.
<gazebo reference="laser">
<sensor name='gpu_lidar' type='gpu_lidar'>
<topic>scan</topic>
<update_rate>30</update_rate>
<lidar>
<scan>
<horizontal>
<samples>640</samples>
<resolution>1</resolution>
<min_angle>-3.1415926</min_angle>
<max_angle>3.1415926</max_angle>
</horizontal>
<vertical>
<samples>16</samples>
<resolution>1</resolution>
<min_angle>-0.261799</min_angle>
<max_angle>0.261799</max_angle>
</vertical>
</scan>
<range>
<min>0.08</min>
<max>10.0</max>
<resolution>0.01</resolution>
</range>
</lidar>
<visualize>true</visualize>
<gz_frame_id>laser<gz_frame_id>
<pose relative_to="laser">0 0 0 0 0 0</pose>
</sensor>
</gazebo>
<gazebo reference="camera" >
<sensor name="cam_link" type="camera">
<update_rate>10.0</update_rate>
<always_on>true</always_on>
<gz_frame_id>camera</gz_frame_id>
<pose relative_to="camera">0 0 0 0 0 0</pose>
<pose>0 0 0 0 0 0</pose>
<topic>/image_raw</topic>
<camera name="my_camera">
<horizontal_fov>1.3962634</horizontal_fov>
<image>
<width>600</width>
<height>600</height>
<format>R8G8B8</format>
</image>
<clip>
<near>0.02</near>
<far>300</far>
</clip>
</camera>
</sensor>
</gazebo>
<gazebo reference="camera">
<sensor name="depth_camera" type="depth_camera">
<update_rate>10</update_rate>
<topic>depth_camera</topic>
<camera>
<horizontal_fov>1.05</horizontal_fov>
<image>
<width>256</width>
<height>256</height>
<format>R_FLOAT32</format>
</image>
<clip>
<near>0.1</near>
<far>10.0</far>
</clip>
</camera>
<always_on>1</always_on>
<gz_frame_id>camera</gz_frame_id>
<pose relative_to="camera">0 0 0 0 0 0</pose>
</sensor>
</gazebo>
The IMU sensor found from the official website
<gazebo>
<plugin filename="gz-sim-imu-system"
name="gz::sim::systems::Imu">
</plugin>
</gazebo>
<gazebo reference="base_link">
<sensor name="imu_sensor" type="imu">
<always_on>1</always_on>
<update_rate>30</update_rate>
<visualize>true</visualize>
<topic>imu</topic>
</sensor>
</gazebo>
You can useign topic -e -t /imuto test whether Gazebo has published the topic, and later just use gazebo_bridge to ROS2 the topic.
By default, rviz2 does not have a plugin for displaying IMU messages, so the relevant plugin must be installed manually. The specific installation command is as follows:
sudo apt install ros-${ROS_DISTRO}-imu-tools
sudo apt install ros-jazzy-imu-tools
The model automatically generated by SolidWorks may have flipped laser_joint. Please correct it back to upright so that rviz2 may then show the laser. Then modify the visualization model to make the model normal and not let it collide, otherwise it may block the laser.

Modify the gazebo_sim_robot_world.launch.py file. The modified code is as follows:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
def generate_launch_description():
this_pkg = get_package_share_directory("demo_gazebo_sim")
mycar_desc_pkg = get_package_share_directory("mycar_description")
pkg_ros_gz_sim = get_package_share_directory("ros_gz_sim")
world_file = os.path.join(this_pkg,"world","house.sdf")
gz_sim = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(pkg_ros_gz_sim, "launch", "gz_sim.launch.py")),
launch_arguments={
"gz_args": "-r " + world_file
}.items(),
)
mycar_desc = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(mycar_desc_pkg,"launch","mycar_desc_sim.launch.py")
)
)
spawn = Node(package="ros_gz_sim", executable="create",
arguments=[
"-name", "mycar",
"-x", "-4",
"-z", "0.01", #设置为0,可能会陷进地里
"-y", "0",
"-R", "0",
"-P", "0",
"-Y", "1.57", #逆时针旋转90度
"-topic", "/robot_description"],
output="screen")
# Bridge
bridge = Node(
package="ros_gz_bridge",
executable="parameter_bridge",
arguments=["/cmd_vel@geometry_msgs/msg/Twist@gz.msgs.Twist",
"/model/mycar/odometry@nav_msgs/msg/Odometry@gz.msgs.Odometry",
"/model/mycar/tf@tf2_msgs/msg/TFMessage[gz.msgs.Pose_V",
"/clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock",
"/world/empty/model/mycar/joint_state@sensor_msgs/msg/JointState[gz.msgs.Model",
"/scan@sensor_msgs/msg/LaserScan@gz.msgs.LaserScan",
"/scan/points@sensor_msgs/msg/PointCloud2@gz.msgs.PointCloudPacked",
"/image_raw@sensor_msgs/msg/Image@gz.msgs.Image",
"/camera_info@sensor_msgs/msg/CameraInfo@gz.msgs.CameraInfo",
"/depth_camera@sensor_msgs/msg/Image@gz.msgs.Image",
"/imu@sensor_msgs/msg/Imu[gz.msgs.IMU",
"/imu/angular_velocity@geometry_msgs/msg/Vector3[gz.msgs.Vector3d"
],
parameters=[{"qos_overrides./model/mycar.subscriber.reliability": "reliable"}],
remappings=[
("/model/mycar/tf", "/tf"),
("/world/empty/model/mycar/joint_state","joint_states"),
("/model/mycar/odometry","/odom")
],
output="screen"
)
return LaunchDescription([
gz_sim,
spawn,
mycar_desc,
bridge
])
build
In the terminal, enter the current workspace and compile the package:
colcon build --packages-select mycar_description demo_gazebo_sim
Execute
In the terminal, navigate to the current workspace and run the launch file using the following command:
. install/setup.bash
ros2 launch demo_gazebo_sim gazebo_sim_robot_world.launch.py
Heregz_frame_idmay trigger a false warning from the sdf1.10 specification; ignore this warning. In fact, this version of Gazebo will read this custom taggz_frame_id。 On sdf1.12 it seems to have been changed toframe_id。
你可以通过ros2 topic echo /scan --once | grep frame_id来查看scan的frame_id是否被修改成功了,修改成功会变成laser,否则会变成gazebo默认的mycar/base_footprint/gpu_lidar。
Problem-solving reference:https://github.com/gazebosim/gz-sensors/issues/306
Then restart the keyboard control node, and you can control the robot's movement.
You can also start rviz2 to view the various data published by the robot. In the terminal, enter the current workspace and call the following command to execute the launch file:
. install/setup.bash
rviz2


(Only after fully reproducing all of the above can the next chapter navigation be carried out; the next chapter navigation is still based on simulation.)