ROS 2 + Gazebo Setup Example
Overview
This example demonstrates how to set up a basic simulation environment with ROS 2 and Gazebo for a simple robot. This will serve as a foundation for more complex humanoid robot simulations.
Prerequisites
Before starting, ensure you have:
- ROS 2 Humble Hawksbill installed
- Gazebo Garden installed
- Basic understanding of ROS 2 concepts (nodes, topics, packages)
Step 1: Create a ROS 2 Package
First, let's create a ROS 2 package for our simulation:
# Navigate to your ROS 2 workspace
cd ~/ros2_ws/src
# Create the package
ros2 pkg create --build-type ament_python my_robot_simulation --dependencies rclpy std_msgs sensor_msgs geometry_msgs
# Create necessary directories
mkdir -p ~/ros2_ws/src/my_robot_simulation/launch
mkdir -p ~/ros2_ws/src/my_robot_simulation/config
mkdir -p ~/ros2_ws/src/my_robot_simulation/worlds
Step 2: Create Robot URDF
Create a simple robot description in URDF format. Create the file ~/ros2_ws/src/my_robot_simulation/urdf/my_robot.urdf:
<?xml version="1.0"?>
<robot name="my_robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- Base Link -->
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.2" radius="0.2"/>
</geometry>
<material name="blue">
<color rgba="0 0 1 0.8"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.2" radius="0.2"/>
</geometry>
</collision>
<inertial>
<mass value="1.0"/>
<inertia ixx="0.1" ixy="0.0" ixz="0.0" iyy="0.1" iyz="0.0" izz="0.1"/>
</inertial>
</link>
<!-- Gazebo Material -->
<gazebo reference="base_link">
<material>Gazebo/Blue</material>
</gazebo>
<!-- Wheel Links -->
<link name="wheel_left">
<visual>
<geometry>
<cylinder length="0.05" radius="0.1"/>
</geometry>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.05" radius="0.1"/>
</geometry>
</collision>
<inertial>
<mass value="0.2"/>
<inertia ixx="0.001" ixy="0.0" ixz="0.0" iyy="0.001" iyz="0.0" izz="0.001"/>
</inertial>
</link>
<link name="wheel_right">
<visual>
<geometry>
<cylinder length="0.05" radius="0.1"/>
</geometry>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.05" radius="0.1"/>
</geometry>
</collision>
<inertial>
<mass value="0.2"/>
<inertia ixx="0.001" ixy="0.0" ixz="0.0" iyy="0.001" iyz="0.0" izz="0.001"/>
</inertial>
</link>
<!-- Joints -->
<joint name="wheel_left_joint" type="continuous">
<parent link="base_link"/>
<child link="wheel_left"/>
<origin xyz="0 0.15 -0.05" rpy="1.57075 0 0"/>
<axis xyz="0 0 1"/>
</joint>
<joint name="wheel_right_joint" type="continuous">
<parent link="base_link"/>
<child link="wheel_right"/>
<origin xyz="0 -0.15 -0.05" rpy="1.57075 0 0"/>
<axis xyz="0 0 1"/>
</joint>
<!-- Gazebo Plugins -->
<gazebo>
<plugin name="diff_drive" filename="libgazebo_ros_diff_drive.so">
<left_joint>wheel_left_joint</left_joint>
<right_joint>wheel_right_joint</right_joint>
<wheel_separation>0.3</wheel_separation>
<wheel_diameter>0.2</wheel_diameter>
<max_wheel_torque>20</max_wheel_torque>
<max_wheel_acceleration>1.0</max_wheel_acceleration>
<command_topic>cmd_vel</command_topic>
<publish_odom>true</publish_odom>
<publish_odom_tf>true</publish_odom_tf>
<publish_wheel_tf>true</publish_wheel_tf>
<odometry_frame>odom</odometry_frame>
<robot_base_frame>base_link</robot_base_frame>
</plugin>
</gazebo>
</robot>
Step 3: Create a Simple World File
Create a basic world file at ~/ros2_ws/src/my_robot_simulation/worlds/simple_room.sdf:
<?xml version="1.0" ?>
<sdf version="1.7">
<world name="simple_room">
<!-- Include ground plane -->
<include>
<uri>model://ground_plane</uri>
</include>
<!-- Include sun -->
<include>
<uri>model://sun</uri>
</include>
<!-- Add a simple room -->
<model name="room">
<static>true</static>
<!-- Walls will be added as links -->
<link name="wall_1">
<pose>0 5 1 0 0 0</pose>
<visual name="visual">
<geometry>
<box>
<size>10 0.2 2</size>
</box>
</geometry>
<material>
<ambient>0.8 0.8 0.8 1</ambient>
<diffuse>0.8 0.8 0.8 1</diffuse>
</material>
</visual>
<collision name="collision">
<geometry>
<box>
<size>10 0.2 2</size>
</box>
</geometry>
</collision>
</link>
<link name="wall_2">
<pose>5 0 1 0 0 1.5707</pose>
<visual name="visual">
<geometry>
<box>
<size>10 0.2 2</size>
</box>
</geometry>
<material>
<ambient>0.8 0.8 0.8 1</ambient>
<diffuse>0.8 0.8 0.8 1</diffuse>
</material>
</visual>
<collision name="collision">
<geometry>
<box>
<size>10 0.2 2</size>
</box>
</geometry>
</collision>
</link>
</model>
</world>
</sdf>
Step 4: Create Launch File
Create a launch file at ~/ros2_ws/src/my_robot_simulation/launch/robot_simulation.launch.py:
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import PathJoinSubstitution, TextSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
# Arguments
use_sim_time = DeclareLaunchArgument(
'use_sim_time',
default_value='true',
description='Use simulation (Gazebo) clock if true'
)
# Launch Gazebo
gazebo = IncludeLaunchDescription(
PythonLaunchDescriptionSource([
PathJoinSubstitution([
FindPackageShare('gazebo_ros'),
'launch',
'gazebo.launch.py'
])
]),
launch_arguments={
'world': PathJoinSubstitution([
FindPackageShare('my_robot_simulation'),
'worlds',
'simple_room.sdf'
]),
'verbose': 'false'
}.items()
)
# Robot State Publisher
robot_state_publisher = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
name='robot_state_publisher',
output='screen',
parameters=[{
'use_sim_time': True,
'robot_description': open(
PathJoinSubstitution([
FindPackageShare('my_robot_simulation'),
'urdf',
'my_robot.urdf'
]).perform(None)
).read()
}]
)
# Spawn robot in Gazebo
spawn_entity = Node(
package='gazebo_ros',
executable='spawn_entity.py',
arguments=[
'-topic', 'robot_description',
'-entity', 'my_robot',
'-x', '0.0',
'-y', '0.0',
'-z', '0.1'
],
output='screen'
)
# RViz2 for visualization (optional)
rviz = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
arguments=['-d', PathJoinSubstitution([
FindPackageShare('my_robot_simulation'),
'config',
'robot_view.rviz'
])],
parameters=[{'use_sim_time': True}],
output='screen'
)
return LaunchDescription([
use_sim_time,
gazebo,
robot_state_publisher,
spawn_entity,
# rviz # Uncomment if you have RViz config
])
Step 5: Build and Run
Now build your package and run the simulation:
# Navigate to your workspace
cd ~/ros2_ws
# Build the package
colcon build --packages-select my_robot_simulation
# Source the workspace
source install/setup.bash
# Run the simulation
ros2 launch my_robot_simulation robot_simulation.launch.py
Step 6: Control the Robot
In another terminal, you can send commands to control the robot:
# Make sure you source the workspace
source ~/ros2_ws/install/setup.bash
# Send velocity commands
ros2 topic pub /cmd_vel geometry_msgs/Twist "linear:
x: 0.5
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.2"
Verification Steps
To verify that the setup is working correctly:
-
Check that Gazebo launched successfully:
ros2 node list | grep gazebo -
Check that the robot model is loaded:
ros2 topic echo /joint_states --field name -
Verify the robot can move:
- Observe the robot moving in the Gazebo simulation
- Check that the robot's odometry is being published:
ros2 topic echo /odom --field pose.pose
-
Test the command interface:
- Send different velocity commands and observe the robot's response
Troubleshooting
Common Issues:
-
Robot not spawning in Gazebo:
- Check that the URDF file is valid
- Verify the robot description is being published:
ros2 topic echo /robot_description
-
No movement when sending commands:
- Check topic names match between the diff_drive plugin and your commands
- Verify the joints are properly defined in the URDF
-
Performance issues:
- Reduce physics update rate in the world file
- Simplify collision meshes in the URDF
Next Steps
This basic setup provides a foundation for more complex humanoid robot simulations. You can extend this example by:
- Adding more complex robot models with multiple joints and sensors
- Implementing custom controllers for more sophisticated behaviors
- Adding perception sensors like cameras or LiDAR
- Creating more complex environments with obstacles and goals
- Implementing navigation and path planning algorithms
References
This example demonstrates the fundamental setup for ROS 2 and Gazebo integration, which is essential for humanoid robot simulation and development.