Week 4: Physics Simulation in Gazebo
This week focuses on physics simulation in Gazebo, a powerful 3D simulation environment for robotics. You'll learn to import URDF models, configure physical properties, integrate sensors, and develop control interfaces for humanoid robots.
Learning Objectives
By the end of this week, you will be able to:
- Set up Gazebo simulation environments for humanoid robots
- Import and configure URDF models in Gazebo
- Configure physical properties and contacts
- Integrate various sensors (LiDAR, cameras, IMUs)
- Develop control interfaces and plugins for robot simulation
4.1 Gazebo Fundamentals and Physics Engine
Gazebo is a 3D simulation environment that provides physics simulation, realistic rendering, and sensor simulation for robotics applications. It's built on the Open Dynamics Engine (ODE) and supports multiple physics engines.
Key Components of Gazebo
- Physics Engine: Handles collision detection, contact simulation, and dynamics
- Rendering Engine: Provides realistic visualization using OGRE
- Sensor Simulation: Models various sensors like cameras, LiDAR, IMUs
- Plugin System: Extensible architecture for custom behaviors
- Transport System: Message passing for communication
Gazebo vs. Gazebo Classic vs. Ignition Gazebo
- Gazebo Classic: The original version (versions 1-11), being phased out
- Ignition Gazebo: The newer modular version (part of Ignition Robotics)
- Gazebo Garden: The latest version in the Ignition family (recommended)
4.2 Importing URDF Models into Gazebo
Basic URDF Integration
To use your URDF model in Gazebo, you need to add Gazebo-specific tags to your URDF file. Here's how to extend the humanoid URDF from Module 1:
<?xml version="1.0"?>
<robot name="simple_humanoid" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- Include your existing URDF content here -->
<!-- Gazebo-specific tags for simulation -->
<gazebo reference="base_link">
<material>Gazebo/Blue</material>
<mu1>0.2</mu1>
<mu2>0.2</mu2>
<kp>1000000.0</kp>
<kd>1.0</kd>
</gazebo>
<!-- Gazebo plugin for joint state publishing -->
<gazebo>
<plugin name="joint_state_publisher" filename="libgazebo_ros_joint_state_publisher.so">
<update_rate>30</update_rate>
<joint_name>left_shoulder_joint</joint_name>
<joint_name>right_shoulder_joint</joint_name>
<joint_name>left_elbow_joint</joint_name>
<joint_name>right_elbow_joint</joint_name>
</plugin>
</gazebo>
<!-- Gazebo plugin for controlling joints -->
<gazebo>
<plugin name="position_controllers" filename="libgazebo_ros_joint_position.so">
<robotNamespace>/simple_humanoid</robotNamespace>
<jointName>left_shoulder_joint</jointName>
<topicName>left_shoulder_position</topicName>
</plugin>
</gazebo>
</robot>
Gazebo-Specific Tags
<gazebo reference="link_name">: Applies properties to a specific link<material>: Sets the visual material for rendering<mu1>,<mu2>: Friction coefficients<kp>,<kd>: Spring stiffness and damping coefficients<plugin>: Adds functionality through plugins
4.3 Configuring Physical Properties and Contacts
Material Properties
<gazebo reference="left_foot">
<mu1>0.8</mu1> <!-- Primary friction coefficient -->
<mu2>0.8</mu2> <!-- Secondary friction coefficient -->
<kp>1000000.0</kp> <!-- Contact stiffness -->
<kd>1000000.0</kd> <!-- Contact damping -->
<minDepth>0.001</minDepth> <!-- Penetration depth before contact force -->
<maxVel>10.0</maxVel> <!-- Maximum contact correction velocity -->
</gazebo>
Collision Properties
<link name="left_foot">
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.2 0.1 0.05"/>
</geometry>
<surface>
<friction>
<ode>
<mu>0.8</mu>
<mu2>0.8</mu2>
<fdir1>0 0 1</fdir1>
<slip1>0.0</slip1>
<slip2>0.0</slip2>
</ode>
</friction>
<bounce>
<restitution_coefficient>0.01</restitution_coefficient>
<threshold>100000</threshold>
</bounce>
<contact>
<ode>
<soft_cfm>0</soft_cfm>
<soft_erp>0.2</soft_erp>
<kp>1e+10</kp>
<kd>1</kd>
<max_vel>100.0</max_vel>
<min_depth>0.001</min_depth>
</ode>
</contact>
</surface>
</collision>
</link>
4.4 Sensor Integration (LiDAR, Cameras, IMUs)
LiDAR Sensor Integration
<gazebo reference="laser_link">
<sensor name="laser" type="ray">
<pose>0 0 0 0 0 0</pose>
<visualize>true</visualize>
<update_rate>10</update_rate>
<ray>
<scan>
<horizontal>
<samples>720</samples>
<resolution>1</resolution>
<min_angle>-1.570796</min_angle>
<max_angle>1.570796</max_angle>
</horizontal>
</scan>
<range>
<min>0.1</min>
<max>30.0</max>
<resolution>0.01</resolution>
</range>
</ray>
<plugin name="laser_controller" filename="libgazebo_ros_laser.so">
<topicName>/scan</topicName>
<frameName>laser_link</frameName>
</plugin>
</sensor>
</gazebo>
Camera Sensor Integration
<gazebo reference="camera_link">
<sensor name="camera" type="camera">
<update_rate>30</update_rate>
<camera name="head">
<horizontal_fov>1.3962634</horizontal_fov>
<image>
<width>640</width>
<height>480</height>
<format>R8G8B8</format>
</image>
<clip>
<near>0.1</near>
<far>100</far>
</clip>
</camera>
<plugin name="camera_controller" filename="libgazebo_ros_camera.so">
<frame_name>camera_link</frame_name>
<topic_name>/camera/image_raw</topic_name>
<hack_baseline>0.07</hack_baseline>
</plugin>
</sensor>
</gazebo>
IMU Sensor Integration
<gazebo reference="imu_link">
<sensor name="imu" type="imu">
<always_on>true</always_on>
<update_rate>100</update_rate>
<visualize>false</visualize>
<imu>
<angular_velocity>
<x>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>2e-4</stddev>
</noise>
</x>
<y>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>2e-4</stddev>
</noise>
</y>
<z>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>2e-4</stddev>
</noise>
</z>
</angular_velocity>
<linear_acceleration>
<x>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>1.7e-2</stddev>
</noise>
</x>
<y>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>1.7e-2</stddev>
</noise>
</y>
<z>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>1.7e-2</stddev>
</noise>
</z>
</linear_acceleration>
</imu>
<plugin name="imu_controller" filename="libgazebo_ros_imu.so">
<topicName>/imu/data</topicName>
<bodyName>imu_link</bodyName>
<frameName>imu_link</frameName>
<serviceName>/imu/service</serviceName>
</plugin>
</sensor>
</gazebo>
4.5 Control Interfaces and Plugin Development
Joint Control Plugins
Gazebo provides various plugins for controlling robot joints:
<!-- Position control plugin -->
<gazebo>
<plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
<robotNamespace>/simple_humanoid</robotNamespace>
<robotSimType>gazebo_ros_control/DefaultRobotHWSim</robotSimType>
</plugin>
</gazebo>
Creating a Custom Gazebo Plugin
Here's a simple example of a custom Gazebo plugin for humanoid control:
#include <gazebo/gazebo.hh>
#include <gazebo/physics/physics.hh>
#include <gazebo/common/common.hh>
#include <stdio.h>
namespace gazebo
{
class HumanoidControlPlugin : public ModelPlugin
{
public: void Load(physics::ModelPtr _parent, sdf::ElementPtr /*_sdf*/)
{
// Store the model pointer for convenience
this->model = _parent;
// Listen to the update event. This event is broadcast every
// simulation iteration.
this->updateConnection = event::Events::ConnectWorldUpdateBegin(
std::bind(&HumanoidControlPlugin::OnUpdate, this));
}
// Called by the world update start event
public: void OnUpdate()
{
// Apply a small linear velocity to the model
this->model->SetLinearVel(math::Vector3(0.03, 0, 0));
}
// Pointer to the model
private: physics::ModelPtr model;
// Pointer to the update event connection
private: event::ConnectionPtr updateConnection;
};
// Register this plugin with the simulator
GZ_REGISTER_MODEL_PLUGIN(HumanoidControlPlugin)
}
ROS 2 Integration with Gazebo
To integrate ROS 2 with Gazebo, you typically use:
- gazebo_ros_pkgs: Provides ROS 2 interfaces for Gazebo
- robot_state_publisher: Publishes TF transforms
- joint_state_publisher: Publishes joint states
- Controller managers: For hardware interfaces
4.6 Practical Exercise: Simulating a Walking Humanoid
Let's create a complete example that simulates a simple walking motion:
Extended URDF with Gazebo Plugins
<?xml version="1.0"?>
<robot name="walking_humanoid" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- Include your humanoid model from Module 1 here -->
<!-- Gazebo plugin for ROS control -->
<gazebo>
<plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
<robotNamespace>/walking_humanoid</robotNamespace>
<controlPeriod>0.001</controlPeriod>
<robotSimType>gazebo_ros_control/DefaultRobotHWSim</robotSimType>
</plugin>
</gazebo>
<!-- Joint state publisher plugin -->
<gazebo>
<plugin name="joint_state_publisher" filename="libgazebo_ros_joint_state_publisher.so">
<update_rate>30</update_rate>
<joint_name>left_hip_joint</joint_name>
<joint_name>left_knee_joint</joint_name>
<joint_name>left_ankle_joint</joint_name>
<joint_name>right_hip_joint</joint_name>
<joint_name>right_knee_joint</joint_name>
<joint_name>right_ankle_joint</joint_name>
</plugin>
</gazebo>
<!-- Example of a simple controller configuration -->
<ros2_control name="GazeboSystem" type="system">
<hardware>
<plugin>gazebo_ros2_control/GazeboSystem</plugin>
</hardware>
<joint name="left_hip_joint">
<command_interface name="position"/>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
<!-- Add similar interfaces for other joints -->
</ros2_control>
</robot>
Launch File for Simulation
Create a launch file to start the simulation:
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
# Launch configuration variables
use_sim_time = LaunchConfiguration('use_sim_time')
# Declare launch arguments
declare_use_sim_time_cmd = DeclareLaunchArgument(
'use_sim_time',
default_value='true',
description='Use simulation (Gazebo) clock if true'
)
# Start Gazebo
gazebo = IncludeLaunchDescription(
PythonLaunchDescriptionSource([
PathJoinSubstitution([
FindPackageShare('gazebo_ros'),
'launch',
'gazebo.launch.py'
])
])
)
# 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': use_sim_time,
'robot_description': open('path/to/your/robot.urdf').read()
}]
)
# Spawn robot in Gazebo
spawn_entity = Node(
package='gazebo_ros',
executable='spawn_entity.py',
arguments=['-topic', 'robot_description', '-entity', 'walking_humanoid'],
output='screen'
)
# Create the launch description and populate
ld = LaunchDescription()
# Declare launch options
ld.add_action(declare_use_sim_time_cmd)
# Add nodes and launches
ld.add_action(gazebo)
ld.add_action(robot_state_publisher)
ld.add_action(spawn_entity)
return ld
Running the Simulation
# Terminal 1: Start Gazebo
ros2 launch your_package simulation.launch.py
# Terminal 2: Send joint commands
ros2 topic pub /walking_humanoid/left_hip_joint/position std_msgs/msg/Float64 "data: 0.5"
# Terminal 3: Monitor joint states
ros2 topic echo /joint_states
4.7 Flowchart: Gazebo-Unity Simulation Pipeline
graph TD
A[URDF Model] --> B[Gazebo Physics Simulation]
B --> C[Sensor Data Generation]
C --> D[ROS 2 Message Publishing]
D --> E[Controller Commands]
E --> F[Robot Actuation]
F --> B
G[Unity Visualization] --> H[High-Fidelity Rendering]
B --> H
H --> G
Summary
This week covered physics simulation in Gazebo for humanoid robots. You learned to import URDF models, configure physical properties, integrate sensors, and develop control interfaces. These skills are essential for testing humanoid robot behaviors in a safe, controlled environment before deploying on real hardware.
Next Week Preview
Next week, we'll explore high-fidelity visualization using Unity and learn how to integrate it with ROS 2 for enhanced simulation experiences.