2.4 Simulation with Gazebo
Gazebo is a powerful robotics simulation tool that provides a 3D environment for simulating robots, sensors, and objects. It is widely used in the ROS ecosystem for testing and developing robotics algorithms in a realistic virtual environment before deploying them to real hardware.
In ROS2 with the latest Gazebo releases the integration is facilitated by ros_gz
.
To install Gazebo Harmonic binaries on Ubuntu 24.04 simply follow our previous instruction or step by step video guide: https://youtu.be/g2hYkUEkpRQ
Prerequisite
You have followed our previous tutorials: Create a Mobile Robot with URDF.
If not, there is a start package with URDF files that you can donwload from GitHub. You should download it with the main branch (with the -b branch
flag) to your ros workspace:
cd ~/ros2_ws/src/
git clone -b main https://github.com/roboticslabs/mec_mobile.git
cd ~/ros2_ws/src/mec_mobile/mec_mobile_description/urdf
Create a folder such as gazebo_models for offline models. You can download modes here.
To let Gazebo know about the offline model library we have to set the GZ_SIM_RESOURCE_PATH
environmental variable, the best is to add it to the .bashrc
:
export GZ_SIM_RESOURCE_PATH=~/gazebo_models
Load Gazebo World
Firstly, make sure you get the start package with URDF files ready.
1. Create a new package for Sim
cd ~/ros2_ws/src/mec_mobile/
ros2 pkg create --build-type ament_cmake mec_mobile_gazebo
Create Gazebo world files
Inside the mec_mobile_gazebo package, create a worlds folder and put the world.sdf files in:
cd ~/ros2_ws/src/mec_mobile/mec_mobile_gazebo
mkdir worlds
You can open the world file with command:
gz sim world.sdf # If you have problem with the default OGRE2 rendering engine, switch to OGRE: gz sim world.sdf --render-engine ogre
To edit your own world in Gazebo, use the resource spawner and save it with .sdf
extension.
2. Launch Gazebo world from ROS
To launch Gazebo and load the world using, create a launch
folder within the mec_mobile_gazebo package. Inside this launch folder let's create a new launch file world.launch.py
.
This launch file has one argument, the world file's name - you can change the default value to your new world. It also ensures that the offline Gazebo model folder is added to the environmental variable, and finally it launches Gazebo through the ros_gz_sim
package with the right arguments (note that the simulation will start automatically because of the -r
flag).
In case you have problem with the default OGRE2 rendering engine you can switch to OGRE as before:
TextSubstitution(text=' -r -v -v1 --render-engine ogre')],
Add launch and worlds to the CMakeLists.txt and dependencies. Then build the workspace:
cd ~/ros2_ws
colcon build
source install/setup.bash
ros2 launch mec_mobile_gazebo world.launch.py
Load URDF files to Gazebo environment
We already have a mecanum mobile robot model that we see in RViz, but RViz is just a visualization tool, even if we can rotate some joints with the joint_state_publisher_gui
, it has nothing to do with the physical simulation.
To load our robot model into the Gazebo simulation environment, we have to write a new launch file that spawns the robot through the right services of Gazebo.
Let's create spawn_robot.launch.py
in our launch folder inside mec_mobile_gazebo, and it will use the world.launch.py
file we created just now to launch the simulation world:
This launch file will include the world.launch.py
that we created earlier, so we don't have to load the world into Gazebo again. It will open RViz with a pre-configured view, and with three new nodes:
create
node of theros_gz_sim
package to spawn the robot from therobot_description
topic into a specific location within the simulation.robot_state_publisher
will convert and load the URDF/xacro into therobot_description
topic it's providing the transformations between the links using static transforms from the URDF and dynamic transforms from real timejoint_states
topicjoint_state_publisher_gui
is ajoint_state_publisher
with the small graphical UI to change joint angles. This node is responsible to update dynamic changes between links through thejoint_states
topic
We've used the robot_state_publisher
and joint_state_publisher_gui
in our RViz launch file check_urdf.launch.py
. These nodes are used in the description.launch.py
and display.launch.py
files inside urdf_launch package here.
We have to add launch to the CMakeLists.txt and rebuild the workspace to try this new launch file:
ros2 launch mec_mobile_gazebo spawn_robot.launch.py
Right now, nothing publishes odometry for our robot so let's change the fixed frame to the robot_footprint
in RViz.
We see that doesn't matter how we change the wheel joint angles it has no impact on the physical simulation. We did the first step, the robot is spawned into a Gazebo simulation, but the integration just starts from here.
Gazebo integration
To finally drive our robot in the physical simulation we have to do 2 things: 1. Adding a right Gazebo plugin that can move the specified robot 2. bridging messages between ROS and Gazebo.
1. Add Gazebo plugin
Let's create a mec_mobile.gazebo
file in the URDF folder:
Let's include this new file in our robot's URDF. In the same way how we included the colors, let's add it to the top of our URDF within the <robot>
tag.
<xacro:include filename="$(find mec_mobile_description)/urdf/control/mec_mobile.gazebo"/>
Rebuild the workspace and then run it:
cd ~/ros2_ws
colcon build
source install/setup.bash
ros2 launch mec_mobile_gazebo spawn_robot.launch.py
2. ROS gz bridge
First, remove the joint_state_publisher
from the spawn_robot.launch.py
because as soon as we can forward the messages between ROS and Gazebo, Gazebo will handle updating the joint_state
topic.
After that we add another node the parameter_bridge
from the ros_gz_bridge
package.
Full launch file: spawn_robot.launch.py
Make sure
ros_gz_bridge
is installed!sudo apt install ros-jazzy-ros-gz-bridge
You can find the detailed documentation of ros_gz_bridge
here. It explains the syntax of bridging topics in details also you can see what kind of messages can be bridged.
We forward the following topics:
/clock
: The topic used for tracking simulation time or any custom time source./cmd_vel
: We'll control the simulated robot from this ROS topic./odom
: Gazebo's diff drive plugin provides this odometry topic for ROS consumers./joint_states
: Gazebo's other plugin provides the dynamic transformation of the wheel joints./tf
: Gazebo provides the real-time computation of the robot’s pose and the positions of its links, sensors, etc.
We forward the above messages bi-directionally between ROS2 and Gazebo except
/clock
. Clock should be published by Gazebo only if we are using simulated environment, but if another node already publishes to the/clock
topic, the bi-directional bridge won't be created. So we make sure that/clock
is forwarded only in the Gazebo → ROS2 direction, this we can achieve with using the[
symbol instead of@
in the arguments.
Don't forget to add the new node to the LaunchDescription()
object:
launchDescriptionObject.add_action(gz_bridge_node)
Then rebuild the workspace.
3. Driving around
Now it's time to launch the simulation:
ros2 launch mec_mobile_gazebo spawn_robot.launch.py
Use the Gazebo teleop plugin or in another terminal start the teleop_twist_keyboard
:
ros2 run teleop_twist_keyboard teleop_twist_keyboard
The friction between the wheels and the ground plane can be unrealistic, so let's add the following physical simulation parameters to the end of our URDF before the </robot>
tag:
<gazebo reference="base_link">
<mu1>0.000002</mu1>
<mu2>0.000002</mu2>
</gazebo>
Mecanum wheel
Mecanum wheel uses special wheels with rollers mounted at a 45-degree angle to the wheel’s axis.
Gazebo has detailed documentation for available plugins, specifically to mecanum drive the documentation of the plugin can be found here. Although, from the documentation it seems that mecanum drive is publishing odometry transformation, but unfortunately this feature is not properly implemented in Gazebo Harmonic as it's mentioned in this GitHub issue.
To do a workaround until this feature will be properly implemented in the future, we can use another Gazebo plugin, the odometry publisher.
Note that description of friction is more complicated than before, the following line is needed to properly simulate the 45 degree rollers in the wheels:
To set the friction direction in Gazebo to <fdir1>0 1 0</fdir1>
, you need to modify the surface
element within your model's collision
definition in the SDF (SDF format) file, specifying the fdir1
attribute with the vector 0 1 0
, which indicates a friction force acting purely along the positive Y-axis in the local frame of the colliding object.
Starting in Gazebo Garden, you should use gz:expressed_in
instead of ignition:expressed_in
with the same change in the xmlns
attribute. ignition:expressed_in
is deprecated
The gz:expressed_in
attribute is a custom attribute and uses the xml namespace gz
, so you’ll need to add xmlns:gz="http://gazebosim.org/schema
to your <robot>
tag of your xacro file. So it would like like:
<robot name="my_robot" xmlns:xacro="http://www.ros.org/wiki/xacro" xmlns:gz="http://gazebosim.org/schema">
<fdir1 gz:expressed_in="base_footprint">1 -1 0</fdir1>
Let's create mec_mobile.gazebo
with the additional odometry publisher
plugin:
Rebuild the workspace, and we can launch with the same launch file as before, just overriding the model argument to the mecanum drive model:
ros2 launch mec_mobile_gazebo spawn_robot.launch.py
We can try to drive the robot with Gazebo teleop plugin or teleop_twist_keyboard
node:
ros2 run teleop_twist_keyboard teleop_twist_keyboard
If
teleop_twist_keyboard
is not installed yet, you can install it with the following command:sudo apt install ros-jazzy-teleop-twist-keyboard
teleop_twist_keyboard
support the control of mecanum robots, by pressing the shift
key the robot moves sideways instead of turning in place.
Last updated