How to create Gazebo plugins in ROS, Part 2

How to create Gazebo plugins in ROS, Part 2

In this post, you will learn how to create Gazebo plugins by creating a more complex one integrated with ROS subscribers, to make an EarthQuake plugin. This is the second part of the response to the question posted on Gazebo Answers.

Before following this post, please ensure you have read and implemented the previous post, which created a simple plugin that merely printed “Hello World!” This post assumes you have read the first post. Let’s go!

Step 1: Add the source code for the new Gazebo plugin: EarthQuake

Create a new file in the src folder of the example_plugins_gazebo package.

cd ~/catkin_ws/src/example_plugins_gazebo/src
touch earthquake.cc

Now paste the following source code into the earthquake.cc file. Please read the explanations within the code.

#include <functional>
#include <gazebo/gazebo.hh>
#include <gazebo/physics/physics.hh>
#include <gazebo/common/common.hh>
#include <ignition/math/Vector3.hh>
#include <thread>
#include "ros/ros.h"
#include "ros/callback_queue.h"
#include "ros/subscribe_options.h"
#include "std_msgs/Float32.h"
#include <gazebo/transport/transport.hh>
#include <gazebo/msgs/msgs.hh>

namespace gazebo
{
  class ModelQuake : public ModelPlugin
  {
    public: void Load(physics::ModelPtr _parent, sdf::ElementPtr /*_sdf*/)
    {
      // Store the pointer to the model
      this->model = _parent;

      // Listen to the update event. This event is broadcast every
      // simulation iteration.
      this->updateConnection = event::Events::ConnectWorldUpdateBegin(
          std::bind(&amp;ModelQuake::OnUpdate, this));
      
      this->old_secs =ros::Time::now().toSec();
      
      // Create a topic name
      std::string earthquake_freq_topicName = "/earthquake_freq";
      std::string earthquake_magnitud_topicName = "/earthquake_magnitud";

      // Initialize ros, if it has not already bee initialized.
      if (!ros::isInitialized())
      {
        int argc = 0;
        char **argv = NULL;
        ros::init(argc, argv, "earthquake_rosnode",
            ros::init_options::NoSigintHandler);
      }
         
      // Create our ROS node. This acts in a similar manner to
      // the Gazebo node
      this->rosNode.reset(new ros::NodeHandle("earthquake_rosnode"));
      
      // Freq
      ros::SubscribeOptions so =
        ros::SubscribeOptions::create<std_msgs::Float32>(
            earthquake_freq_topicName,
            1,
            boost::bind(&amp;ModelQuake::OnRosMsg, this, _1),
            ros::VoidPtr(), &amp;this->rosQueue);
      this->rosSub = this->rosNode->subscribe(so);
      
      // Spin up the queue helper thread.
      this->rosQueueThread =
        std::thread(std::bind(&amp;ModelQuake::QueueThread, this));
        
        
      // Magnitude
      ros::SubscribeOptions so2 =
        ros::SubscribeOptions::create<std_msgs::Float32>(
            earthquake_magnitud_topicName,
            1,
            boost::bind(&amp;ModelQuake::OnRosMsg_Magn, this, _1),
            ros::VoidPtr(), &amp;this->rosQueue2);
      this->rosSub2 = this->rosNode->subscribe(so2);
      
      // Spin up the queue helper thread.
      this->rosQueueThread2 =
        std::thread(std::bind(&amp;ModelQuake::QueueThread2, this));
         
      ROS_WARN("Loaded ModelQuake Plugin with parent...%s, only X Axis Freq Supported in this V-1.0", this->model->GetName().c_str());
      
    }

    // Called by the world update start event
    public: void OnUpdate()
    {
      double new_secs =ros::Time::now().toSec();
      double delta = new_secs - this->old_secs;
      
      double max_delta = 0.0;
      
      if (this->x_axis_freq != 0.0)
      {
        max_delta = 1.0 / this->x_axis_freq;
      }
      
      double magnitude_speed = this->x_axis_magn;
      
      
      if (delta > max_delta &amp;&amp; delta != 0.0)
      {
        // We change Direction
        this->direction = this->direction * -1;
        this->old_secs = new_secs;
        ROS_DEBUG("Change Direction...");
      }
      
      double speed = magnitude_speed * this->direction;
      
      // Apply a small linear velocity to the model.
      this->model->SetLinearVel(ignition::math::Vector3d(speed, 0, 0));
      this->model->SetAngularVel(ignition::math::Vector3d(0, 0, 0));
    }
    
    
    public: void SetFrequency(const double &amp;_freq)
    {
      this->x_axis_freq = _freq;
      ROS_WARN("x_axis_freq >> %f", this->x_axis_freq);
    }
    
    public: void SetMagnitude(const double &amp;_magn)
    {
      this->x_axis_magn = _magn;
      ROS_WARN("x_axis_magn >> %f", this->x_axis_magn);
    }
    
    
    public: void OnRosMsg(const std_msgs::Float32ConstPtr &amp;_msg)
    {
      this->SetFrequency(_msg->data);
    }
    
    /// \brief ROS helper function that processes messages
    private: void QueueThread()
    {
      static const double timeout = 0.01;
      while (this->rosNode->ok())
      {
        this->rosQueue.callAvailable(ros::WallDuration(timeout));
      }
    }
    
    public: void OnRosMsg_Magn(const std_msgs::Float32ConstPtr &amp;_msg)
    {
      this->SetMagnitude(_msg->data);
    }
    
    /// \brief ROS helper function that processes messages
    private: void QueueThread2()
    {
      static const double timeout = 0.01;
      while (this->rosNode->ok())
      {
        this->rosQueue2.callAvailable(ros::WallDuration(timeout));
      }
    }
    

    // Pointer to the model
    private: physics::ModelPtr model;

    // Pointer to the update event connection
    private: event::ConnectionPtr updateConnection;
    
    // Time Memory
    double old_secs;
    
    // Direction Value
    int direction = 1;
    // Frequency of earthquake
    double x_axis_freq = 1.0;
    // Magnitude of the Oscilations
    double x_axis_magn = 1.0;
    
    /// \brief A node use for ROS transport
    private: std::unique_ptr<ros::NodeHandle> rosNode;
    
    /// \brief A ROS subscriber
    private: ros::Subscriber rosSub;
    /// \brief A ROS callbackqueue that helps process messages
    private: ros::CallbackQueue rosQueue;
    /// \brief A thread the keeps running the rosQueue
    private: std::thread rosQueueThread;
    
    
    /// \brief A ROS subscriber
    private: ros::Subscriber rosSub2;
    /// \brief A ROS callbackqueue that helps process messages
    private: ros::CallbackQueue rosQueue2;
    /// \brief A thread the keeps running the rosQueue
    private: std::thread rosQueueThread2;
    
  };

  // Register this plugin with the simulator
  GZ_REGISTER_MODEL_PLUGIN(ModelQuake)
}

Add the new source to your CMakeLists.txt file so that it now looks like this:

cmake_minimum_required(VERSION 2.8.3)
project(example_plugins_gazebo)

## Add support for C++11, supported in ROS Kinetic and newer
add_definitions(-std=c++11)

# Load catkin and all dependencies required for this package
find_package(catkin REQUIRED COMPONENTS 
  roscpp 
  gazebo_ros 
)

# Depend on system install of Gazebo
find_package(gazebo REQUIRED)

link_directories(${GAZEBO_LIBRARY_DIRS})
include_directories(${Boost_INCLUDE_DIR} ${catkin_INCLUDE_DIRS} ${GAZEBO_INCLUDE_DIRS})

# For tc_simple_world_plugin plugin
add_library(simple_world_plugin src/simple_world_plugin.cpp)
target_link_libraries(simple_world_plugin ${catkin_LIBRARIES} ${GAZEBO_LIBRARIES})

# For earthquake_plugin plugin
add_library(earthquake src/earthquake.cc)
target_link_libraries(earthquake ${catkin_LIBRARIES} ${GAZEBO_LIBRARIES})

catkin_package(
  DEPENDS 
    roscpp 
    gazebo_ros 
)

Step 2: Create a new world that will use the Gazebo plugin file we just created

Open a web shell and run the following commands to create the package. When creating Gazebo plugins, you need, at a minimum, the roscpp and gazebo_ros dependencies.

cd ~/catkin_ws/src/example_plugins_gazebo/worlds
touch earthquake.world

Open earthquake.world and paste in the following:

<?xml version="1.0"?> 
<sdf version="1.4">
  <world name="default">
  
    <include>
      <uri>model://sun</uri>
    </include>
    <include>
      <uri>model://ground_plane</uri>
    </include>


    <!-- CUSTOM Ground Plane -->
    <model name="custom_ground_plane_box">
      <pose>0 0 0.1 0 0 0</pose>
      <link name="link">
        <collision name="collision">
          <geometry>
            <box>
              <size>20 20 0.1</size>
            </box>
          </geometry>
          <surface>
              <friction>
                <ode>
                  <mu>10000000000</mu>
                  <mu2>10000000000</mu2>
                </ode>
              </friction>
            </surface>
        </collision>

        <visual name="visual">
          <geometry>
            <box>
              <size>20 20 0.1</size>
            </box>
          </geometry>
          <material>
              <script>
                <uri>file://media/materials/scripts/gazebo.material</uri>
                <name>Gazebo/Green</name>
              </script>
            </material>
        </visual>
        <inertial>
          <pose>0 0 0 0 0 0</pose>
          <mass>100</mass>
          <inertia>
            <ixx>3.33341666667e+3</ixx>
            <ixy>0.0</ixy>
            <ixz>0.0</ixz>
            <iyy>3.33341666667e+3</iyy>
            <iyz>0.0</iyz>
            <izz>6.66666666667e+3</izz>
          </inertia>
        </inertial>
      </link>
      <plugin name="earthquake_plugin" filename="libearthquake.so"/>
    </model>    
 
    <model name="box">
      <pose>0 0 1.5 0 0 0</pose>
      <link name="link">
        <collision name="collision">
          <geometry>
            <box>
              <size>1 1 1</size>
            </box>
          </geometry>
          <surface>
              <friction>
                <ode>
                  <mu>10000000000</mu>
                  <mu2>10000000000</mu2>
                </ode>
              </friction>
            </surface>
        </collision>

        <visual name="visual">
          <geometry>
            <box>
              <size>1 1 1</size>
            </box>
          </geometry>
        </visual>
      </link>
    </model>        
  </world>
</sdf>

Finally, create a launch file that launches the new world

cd ~/catkin_ws/src/example_plugins_gazebo/launch
touch earthquake.launch

Open earthquake.launch in the code editor and paste the following text:

<?xml version="1.0" encoding="UTF-8"?>
<launch>
  <include file="$(find gazebo_ros)/launch/empty_world.launch">
    <arg name="world_name" value="$(find example_plugins_gazebo)/worlds/earthquake.world"/>
  </include>
</launch>

So much for all the hard work – now is the time to see if it works. Time to compile the code. In the same web shell, run the following commands:

cd ~/catkin_ws
rm -rf build/ devel/
catkin_make
source devel/setup.bash

Success! We have now created a new plugin. Next, we will use it!

PS: if your code did not compile correctly, please go over the instructions and ensure you have created the files in the exact locations specified.

Step 3: Launch the new world with the Gazebo plugin we just created

Time to see the result of our labor:

roslaunch example_plugins_gazebo earthquake.launch

Now you should see some yellow warning printed in the terminal and a green plane with an ash-colored box on it. What – is the plane moving? Yes, it’s having an earthquake!

Gazebo world with EarthQuake plugin

But that’s not all! Do you remember the two topics defined in the plugin code? We can publish to them and change the “course of history”. Try these on two new terminals and see what happens (keep the roslaunch command running).

cd ~/catkin_ws
source devel/setup.bash
rostopic pub /earthquake_freq std_msgs/Float32 "data: 2.0"
cd ~/catkin_ws
source devel/setup.bash
rostopic pub /earthquake_magnitud std_msgs/Float32 "data: 3.0"

What happened there? Can you save the situation and stop the earthquake?

Step 4: Check your learning

Do you understand how to create Gazebo plugins? If you don’t know it yet, please go over the post again, more carefully this time.

Here are the links to the resources used in this tutorial:

  • http://gazebosim.org/tutorials/?tut=plugins_hello_world
  • http://gazebosim.org/tutorials?tut=ros_gzplugins
  • http://gazebosim.org/tutorials?tut=guided_i5
  • http://gazebosim.org/tutorials/?tut=ros_advanced
  • http://gazebosim.org/tutorials?tut=plugins_model

(Extra) Step 5: Watch the video to understand how to create Gazebo plugins

Here you go:

Feedback

Did you like this post? Do you have any questions about how to create Gazebo plugins? Please leave a comment in the comments section below, so we can interact and learn from each other.

If you want to learn about other ROS topics, please let us know in the comments area and we will do a video or post about it.

How to create Gazebo plugins in ROS, Part 1

How to create Gazebo plugins in ROS, Part 1

In this post, you will learn how to create Gazebo plugins by making the Gazebo plugin tutorial, Hello World, compile and work in a ROS package. This is a response to the question posted on Gazebo Answers.

Step 1: Fire up a system with ROS installation

“Hey, do you mean I have to install ros2 first?” Absolutely not! Just log in to The Construct to get access to virtual machines pre-installed with ROS.

Once logged in, click on My Rosjects, then Create a New Rosject, supply the information as shown in the image below, and click Create. Then RUN the rosject.

Create a new Rosject

You might also want to try this on a local PC if you have ROS installed. However, please note that we cannot support local PCs and you will have to fix any errors you run into on your own. The rest of the instruction assumes that you are working on The Construct; please adapt them to your local PC and ROS installation.

PS: We are using ROS Kinetic here, but you should be able to use any ROS 1 distro.

Step 2: Create a new package that we’ll use to demonstrate creating Gazebo plugins

Open Code Editor
Open a web shell

Open a web shell and run the following commands to create the package. When creating Gazebo plugins, you need, at a minimum, the roscpp and gazebo_ros dependencies.

cd catkin_ws/src
source /opt/ros/kinetic/setup.bash
catkin_create_pkg example_plugins_gazebo roscpp gazebo_ros

Create a new C++ file in the package you just created:

cd example_plugins_gazebo/src
touch simple_world_plugin.cpp

Now head over to the Code Editor to make changes to the C++ file. Check the image below for how to open the Code Editor.

Open the Code Editor

Locate the C++ file in the code editor: catkin_ws > src > example_plugins_gazebo > src > simple_world_plugin.cpp and paste in the following code.

#include <gazebo/common/Plugin.hh>
#include <ros/ros.h>

namespace gazebo
{
class WorldPluginTutorial : public WorldPlugin
{
public:
  WorldPluginTutorial() : WorldPlugin()
  {
  }

  void Load(physics::WorldPtr _world, sdf::ElementPtr _sdf)
  {
    // Make sure the ROS node for Gazebo has already been initialized                                                                                    
    if (!ros::isInitialized())
    {
      ROS_FATAL_STREAM("A ROS node for Gazebo has not been initialized, unable to load plugin. "
        << "Load the Gazebo system plugin 'libgazebo_ros_api_plugin.so' in the gazebo_ros package)");
      return;
    }

    ROS_WARN("Hello World!");
  }

};
GZ_REGISTER_WORLD_PLUGIN(WorldPluginTutorial)
}

Next, replace the package’s CMakeLists.txt with the following:

cmake_minimum_required(VERSION 2.8.3)
project(example_plugins_gazebo)

## Add support for C++11, supported in ROS Kinetic and newer
add_definitions(-std=c++11)

# Load catkin and all dependencies required for this package
find_package(catkin REQUIRED COMPONENTS 
  roscpp 
  gazebo_ros 
)

# Depend on system install of Gazebo
find_package(gazebo REQUIRED)

link_directories(${GAZEBO_LIBRARY_DIRS})
include_directories(${Boost_INCLUDE_DIR} ${catkin_INCLUDE_DIRS} ${GAZEBO_INCLUDE_DIRS})

# For tc_simple_world_plugin plugin
add_library(simple_world_plugin src/simple_world_plugin.cpp)
target_link_libraries(simple_world_plugin ${catkin_LIBRARIES} ${GAZEBO_LIBRARIES})

catkin_package(
  DEPENDS 
    roscpp 
    gazebo_ros 
)

Also, replace the package.xml file content with the following:

<?xml version="1.0"?>
<package>
  <name>example_plugins_gazebo</name>
  <version>0.0.0</version>
  <description>The example_plugins_gazebo package</description>
  <maintainer email="name@email.com">user</maintainer>

  <license>TODO</license>

  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>gazebo_ros</build_depend>
  <build_depend>roscpp</build_depend>
  <run_depend>gazebo_ros</run_depend>
  <run_depend>roscpp</run_depend>


  <!-- The export tag contains other, unspecified, tags -->
  <export>
    <gazebo_ros plugin_path="${prefix}/lib" gazebo_media_path="${prefix}" />
  </export>
</package>

So much for all the hard work – now is the time to see if it works. Time to compile the code. In the same web shell, run the following commands:

cd ~/catkin_ws
catkin_make
source devel/setup.bash

Success! We have now created a new plugin. Next, we will use it!

PS: if your code did not compile correctly, please go over the instructions and ensure you have created the files in the exact locations specified.

Step 3: Use the new Gazebo plugin we just created

Create a new folder in the package. We’ll create Gazebo worlds in it, so it’s so named!

cd ~/catkin_ws/src/example_plugins_gazebo
mkdir -p worlds
cd worlds
touch simple_world.world

Open the simple_world.world file in the code editor and paste in the following:

<?xml version="1.0" ?>
<sdf version="1.4">
  <world name="default">
    <include>
      <uri>model://ground_plane</uri>
    </include>

    <include>
      <uri>model://sun</uri>
    </include>

    <!-- reference to your plugin -->
    <plugin name="simple_world_plugin" filename="libsimple_world_plugin.so"/>
  </world>
</sdf>

But, wait…how did we know the right filename to use in the line highlighted in the world file? It is by convention named lib{name_of_cpp_executable}.so, and we should find it in ~/catkin_ws/src/devel/lib/

cd ~/catkin_ws/devel/lib/
ls

That covered, we move. Now we’ll create a launch file to launch our world with the Gazebo plugin.

cd ~/catkin_ws/src/example_plugins_gazebo
mkdir -p launch
cd launch
touch simple_world.launch

Open the simple_world.launch file in the code editor and paste in the following content.

<?xml version="1.0" encoding="UTF-8"?>
<launch>
  <include file="$(find gazebo_ros)/launch/empty_world.launch">
    <arg name="world_name" value="$(find example_plugins_gazebo)/worlds/simple_world.world"/>
  </include>
</launch>

Perfect! Time to launch the package.

roslaunch example_plugins_gazebo simple_world.launch

Now you should see something like this: Hello World! printed on the terminal and an empty Gazebo world. We are done here!

Gazebo world with custom “Hello World” plugin

Step 4: Check your learning

Do you understand how to create Gazebo plugins? If you don’t know it yet, please go over the post again, more carefully this time.

Here are the links to the resources used in this tutorial:

  • http://gazebosim.org/tutorials/?tut=plugins_hello_world
  • http://gazebosim.org/tutorials?tut=ros_gzplugins
  • http://gazebosim.org/tutorials?tut=guided_i5
  • http://gazebosim.org/tutorials/?tut=ros_advanced
  • http://gazebosim.org/tutorials?tut=plugins_model

(Extra) Step 5: Watch the video to understand how to create Gazebo plugins

Here you go:

Feedback

Did you like this post? Do you have any questions about how to create Gazebo plugins? Please leave a comment in the comments section below, so we can interact and learn from each other.

If you want to learn about other ROS topics, please let us know in the comments area and we will do a video or post about it.

[ROS Q&A] 130 – How to launch multiple robots in Gazebo simulator?

 

In this video, we are going to see how can we launch multiple robots in a single Gazebo simulation.

We will be introduced to the concept of namespace and tf_prefix, which are essential to make sure that the robots will be able to work correctly. We will create a series of launch files that will enable us to easily add robots into our Gazebo simulation.

This is a video based on the following post on ROS Answers: https://answers.ros.org/question/41433/multiple-robots-simulation-and-navigation/

NOTICE: most of the code would be the same in the ROS Answer, however, we’ll make some change to make sure it is compatible with ROS kinetic. Please use the code below.

Step 1. Create a project in ROS Development Studio(ROSDS)

ROSDS helps you follow our tutorial in a fast pace without dealing without setting up an environment locally. If you haven’t had an account yet, you can create a free account here.

Step 2. Create a package

We’ll create a package to put our source code for the project with the following command

cd ~/catkin_ws/src
catkin_create_pkg multi_robot rospy gazebo_ros

Then we’ll create a folder called launch under the package directory and create 3 launch files that we need.

<launch>
    <arg name="robot_name"/>
    <arg name="init_pose"/>

    <node name="spawn_minibot_model" pkg="gazebo_ros" type="spawn_model"
     args="$(arg init_pose) -urdf -param /robot_description -model $(arg robot_name)"
     respawn="false" output="screen" />

    <node pkg="robot_state_publisher" type="state_publisher" 
          name="robot_state_publisher" output="screen"/>

    <!-- The odometry estimator, throttling, fake laser etc. go here -->
    <!-- All the stuff as from usual robot launch file -->
</launch>

This launch file will launch one robot in the simulation.

<launch>
  <!-- No namespace here as we will share this description. 
       Access with slash at the beginning -->
  <param name="robot_description"
    command="$(find xacro)/xacro.py $(find turtlebot_description)/robots/kobuki_hexagons_asus_xtion_pro.urdf.xacro" />

  <!-- BEGIN ROBOT 1-->
  <group ns="robot1">
    <param name="tf_prefix" value="robot1_tf" />
    <include file="$(find multi_robot)/launch/one_robot.launch" >
      <arg name="init_pose" value="-x 1 -y 1 -z 0" />
      <arg name="robot_name"  value="Robot1" />
    </include>
  </group>

  <!-- BEGIN ROBOT 2-->
  <group ns="robot2">
    <param name="tf_prefix" value="robot2_tf" />
    <include file="$(find multi_robot)/launch/one_robot.launch" >
      <arg name="init_pose" value="-x -1 -y 1 -z 0" />
      <arg name="robot_name"  value="Robot2" />
    </include>
  </group>
</launch>

Please notice that it should have a different namespace and tf_prefix for each robot.

<launch>
  <param name="/use_sim_time" value="true" />

  <!-- start world -->
  <node name="gazebo" pkg="gazebo_ros" type="gazebo" 
   args="$(find turtlebot_gazebo)/worlds/empty_wall.world" respawn="false" output="screen" />

  <!-- start gui -->
  <!-- <node name="gazebo_gui" pkg="gazebo" type="gui" respawn="false" output="screen"/> -->

  <!-- include our robots -->
  <include file="$(find multi_robot)/launch/robots.launch"/>
</launch>

You can launch the simulation with the following command

roslaunch multi_robot main.launch

Then you have to open the gazebo window from Tools->Gazebo

You should see 2 robots are spawned in the simulation. You can spawn more by changing the robots.launch if you want.

Step 3. Move the robot

Open another terminal, if you type rostopic list , you’ll see there are 2 cmd_vel topics for the robots. The simplest way to control them is with the teleop_twist. We can run it and remap the cmd_vel with the robot you want to control. For example

rosrun teleop_twist_keyboard teleop_twist_keyboard.py /cmd_vel:=/robot1/cmd_vel

Now you can control the robot you want.

 

Edit by: Tony Huang

 

 

// RELATED LINKS

▸ Original question: https://answers.ros.org/question/41433/multiple-robots-simulation-and-navigation/
ROS Development Studio (RDS)
Robot Ignite Academy


Feedback

Did you like this video? Do you have questions about what is explained? Whatever the case, please leave a comment on the comments section below, so we can interact and learn from each other.

If you want to learn about other ROS topics, please let us know on the comments area and we will do a video about it.

ROS Developers LIVE-Class #18: Let’s Simulate a World in Gazebo Simulator

ROS Developers LIVE-Class #18: Let’s Simulate a World in Gazebo Simulator

 

In this ROS LIVE-Class we’re going to create a world in the Gazebo simulator for the previous differential drive manipulator we created in the previous class, so the robot can navigate around and interact with the objects.

The model of the robot was created using URDF. However, the model of the environment will be created using SDF.

We will see:
▸ How to create the world for the robot using SDF
▸ How to add models of any object you may think of
▸ How to spawn ROS based robots in the world

Part 1

Part 2

Every Wednesday at 18:00 CET/CEST. 

This is a LIVE Class on how to develop with ROS. In Live Classes you practice with me at the same time that I explain, with the provided free ROS material.

IMPORTANT: Remember to be on time for the class because at the beginning of the class we will share the code with the attendants.

IMPORTANT 2: in order to start practicing quickly, we are using the ROS Development Studio for doing the practice. You will need a free account to attend the class. Go to http://rds.theconstructsim.com and create an account prior to the class.

// RELATED LINKS
▸ Gazebo simulator
▸ SDF format

[ROS Projects] Create a Hopper Robot in Gazebo Step-by-Step

[ROS Projects] Create a Hopper Robot in Gazebo Step-by-Step

 

Locomotion is one of the most challenging topics in robotics. Creating the hard-coded algorithms and the kinematics models is not an easy task. Therefore, its no surprise that AI has been used to try and make robots learn how to move by themselves. In this project you will learn step by step, how to create a Monopod robot simulation working in Gazebo and then set up everything to use OpenAI-Gym infrastructure. OpenAI-Gym allows you to separate learning algorithms from the physical/simulated robot, so that you can test different learning algorithms easily. It also allows you to compare your results with other people in the same conditions.

Part 1

In this first video of a new ROS Development Studio video series, you are going to learn step by step

how to create your own hopper simulation, and may be a real version if there is high support to this videos.

You will learn in this video how to create your ROS packages, modify a URDF given by Alexander W. Winkler, https://github.com/leggedrobotics/xpp to give it control and physics and add all the needed sensors like IMU, odometry and contact sensor.

Here are the steps to create the hopper robot as shown in the video:

Step 1
Head to Robot Development Studio and create a new project.
Provide a suitable project name and some useful description.
Open the project (this will take few seconds)
Once the project is loaded run the IDE from the tools menu. Also verify that the initial directory structure should look like following:

.
├────── ai_ws
├────── catkin_ws
│     ├─── build
│     ├─── devel
│     └─── 
├────── notebook_ws
│     ├─── default.ipynb
│     └─── images
└────── simulation_ws
      ├─── build
      ├─── devel
      └─── src

Note that we use simulation_ws to contain all the files related to simulations. Those files not related to simulations will go to catkin_ws (like python scripts, launch files etc)


Step 2
Now we create two catkin packages with names my_legged_robots_description and my_legged_robots_sims. We will add rospy as dependency for both of them.

Start a SHELL from tools menu and navigate to ~simulation_ws/src directory as follows

$ cd simulation_ws/src

Now we create the first catkin package with following command

$ catkin_create_pkg my_legged_robots_description rospy

Then create second catkin_package with the following command

$ catkin_create_pkg my_legged_robots_sims rospy

At this point we should have the following directory structure

.
├────── ai_ws
├────── catkin_ws
│     ├─── build
│     ├─── devel
│     └─── 
├────── notebook_ws
│     ├─── default.ipynb
│     └─── images
└────── simulation_ws
      ├─── build
      ├─── devel
      └─── src
         ├──── CMakeLists.txt 
         ├──── my_legged_robots_description
         │    ├─── CMakeLists.txt
         │    ├─── package.xml
         │    └─── src
         └──── my_legged_robots_sims
              ├─── CMakeLists.txt
              ├─── package.xml
              └─── src

Step 3
Now we need to copy the mesh for the hopper robot from github. Use the following command to clone the github repository

git clone https://github.com/leggedrobotics/xpp.git

Once the cloning is complete, we should have a new directory with name xpp inside the ~simulation_ws/src directory

This new directory contains the mesh models for various robots such as biped, quadruped etc. We only need the mesh for monoped so we will do following

  • copy the meshes folder from ~simulation_ws/src/xpp/robots/xpp_hyq/ to ~simulation_ws/src/my_legged_robots_description/ directory
  • copy the urdf folder from ~simulation_ws/src/xpp/robots/xpp_hyq/ to ~simulation_ws/src/my_legged_robots_sims/ directory
  • delete the files in ~simulation_ws/src/my_legged_robots_sims/urdf/ directory except the file monoped.urdf

Step 4
Lets analyze the urdf file for the monoped.
Open the monoped.urdf file in the IDE. The file contains 4 links and 3 joints. Moreover the links have only visual properties which means we can’t yet simulate it. However we can load it in rviz for display.

For simulation ability we need to define inertia and collision properties into the monoped.urdf file.
We will add these properties to the monoped.urdf file. Now before we make any changes, its a good idea that we create a copy of monoped.urdf with name monoped_controlled.urdf

Here is the monoped_controlled.urdf file content after editing

<robot name="monoped">
    <link name="base">
        <inertial>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <mass value="0.18" />
            <inertia ixx="0.0002835" ixy="0.0" ixz="0.0" iyy="0.0002835" iyz="0.0" izz="0.000324"/>
        </inertial>
        <collision>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <geometry>
                <box size="0.20 0.20 0.30"/>
            </geometry>
        </collision>
	    <visual>
	      <geometry>
	        <box size="0.20 0.20 0.30"/>
	      </geometry>
	      <material name="red">
	        <color rgba="1.0 0 0 1.0"/>
	      </material>
	    </visual>        
    </link>
    
    <gazebo reference="base">
        <kp>1000.0</kp>
        <kd>1000.0</kd>
        <mu1>0.5</mu1>
        <mu2>0.5</mu2>
        <material>Gazebo/Red</material>
    </gazebo>
    
    <link name="hipassembly">
        <inertial>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <mass value="0.18" />
            <inertia ixx="0.0002835" ixy="0.0" ixz="0.0" iyy="0.0002835" iyz="0.0" izz="0.000324"/>
        </inertial>
        <collision>
            <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
	      <geometry>
	        <mesh filename="package://my_legged_robots_description/meshes/leg/hipassembly.dae" scale="1 1 1"/>
	      </geometry>
        </collision>
	    <visual>
	      <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
	      <geometry>
	        <mesh filename="package://my_legged_robots_description/meshes/leg/hipassembly.dae" scale="1 1 1"/>
	      </geometry>
	      <material name="white"/>
	    </visual>        
    </link>

    <gazebo reference="hipassembly">
        <kp>1000.0</kp>
        <kd>1000.0</kd>
        <mu1>0.5</mu1>
        <mu2>0.5</mu2>
        <material>Gazebo/Blue</material>
    </gazebo>


    <link name="upperleg">
        <inertial>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <mass value="0.18" />
            <inertia ixx="0.0002835" ixy="0.0" ixz="0.0" iyy="0.0002835" iyz="0.0" izz="0.000324"/>
        </inertial>
        <collision>
            <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
	      <geometry>
	        <mesh filename="package://my_legged_robots_description/meshes/leg/upperleg.dae" scale="1 1 1"/>
	      </geometry>
        </collision>
	    <visual>
	      <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
	      <geometry>
	        <mesh filename="package://my_legged_robots_description/meshes/leg/upperleg.dae" scale="1 1 1"/>
	      </geometry>
	      <material name="blue"/>
	    </visual>
    </link>

    <gazebo reference="upperleg">
        <kp>1000.0</kp>
        <kd>1000.0</kd>
        <mu1>0.5</mu1>
        <mu2>0.5</mu2>
        <material>Gazebo/Blue</material>
    </gazebo>


    <link name="lowerleg">
        <inertial>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <mass value="0.18" />
            <inertia ixx="0.0002835" ixy="0.0" ixz="0.0" iyy="0.0002835" iyz="0.0" izz="0.000324"/>
        </inertial>
        <collision>
            <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
	      <geometry>
	        <mesh filename="package://my_legged_robots_description/meshes/leg/lowerleg.dae" scale="1 1 1"/>
	      </geometry>
        </collision>
	    <visual>
	      <geometry>
	        <mesh filename="package://my_legged_robots_description/meshes/leg/lowerleg.dae" scale="1 1 1"/>
	      </geometry>
	      <material name="blue"/>
	    </visual>        
    </link>

    <gazebo reference="lowerleg">
        <kp>1000.0</kp>
        <kd>1000.0</kd>
        <mu1>0.5</mu1>
        <mu2>0.5</mu2>
        <material>Gazebo/Blue</material>
    </gazebo>
    
    <link name="lowerleg_contactsensor_link">
 	    <inertial >
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <mass value="0.01" />
            <inertia ixx="1.28e-06" ixy="0.0" ixz="0.0" iyy="1.28e-06" iyz="0.0" izz="1.28e-06"/>
        </inertial>
        <collision>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <geometry>
                <sphere radius="0.020"/>
            </geometry>
        </collision>
        <visual>
            <origin rpy="0.0 0 0" xyz="0 0 0"/>
            <geometry>
                <sphere radius="0.020"/>
            </geometry>
            <material name="red">
	            <color rgba="1.0 0 0 1.0"/>
	        </material>
        </visual>
	</link>

    <gazebo reference="lowerleg_contactsensor_link">
        <kp>1000.0</kp>
        <kd>1000.0</kd>
        <mu1>10.0</mu1>
        <mu2>10.0</mu2>
        <material>Gazebo/Red</material>
    </gazebo>

    <joint name="lowerleg_contactsensor_link_joint" type="fixed">
        <parent link="lowerleg"/>
        <child link="lowerleg_contactsensor_link"/>
        <origin xyz="0.35 0 0" rpy="0 0 0"/>
    </joint>
    
   
    <joint name="haa_joint" type="revolute">
        <origin xyz="0.0 0.0 -0.15000" rpy="2.0344439357957036 1.5707962290814481 -1.1071487177940917"/>
        <parent link="base"/>
        <child  link="hipassembly"/>
        <limit effort="200" lower="-1.6" upper="1.6" velocity="10.0"/>
        <axis xyz="0 0 1"/>
    </joint>
    <joint name="hfe_joint" type="revolute">
        <origin xyz="0.08000 0.00000 0.00000" rpy="1.5707963705062866 -0.0 0.0"/>
        <parent link="hipassembly"/>
        <child  link="upperleg"/>
        <limit effort="200" lower="-1.6" upper="1.6" velocity="10.0"/>
        <axis xyz="0 0 1"/>
    </joint>
    <joint name="kfe_joint" type="revolute">
        <origin xyz="0.35000 0.00000 0.00000" rpy="0.0 0.0 0.0"/>
        <parent link="upperleg"/>
        <child  link="lowerleg"/>
        <limit effort="200" lower="-1.6" upper="1.6" velocity="10.0"/>
        <axis xyz="0 0 1"/>
    </joint>
    
</robot>

In addition to the inertia and collision tags we have added a few tags.
First is the gazebo tag, this tag is required to simulate the block in gazebo, it contains information about the material hardness (whether the material is easily deformable or not) and friction values (static and dynamic).
The Second tag that was added is a link tag with name lowerleg_contactsensor_link. This link will help us detect the contact with ground in later part of this project.
Another added tag is a joint tag by name lowerleg_contactsensor_link_joint, this combined with previous link tag completes the contact sensor positioning on the robot.

Note : In the above code the values used for various inertia is calculated using inertial_calculator tool ( it is a part of ROS). Moreover, to simplify calculation of inertia of different blocks with different shapes, a bounding box approximation is applied i.e. we compute the inertia with respect to the dimensions of the bounding box. Though such value might not be perfect, it is reasonably good to work with.


Step 5
Now we will simulate this robot.

To do so first we need to create a world file. Create a directory named worlds inside ~simulation_ws/src/my_legged_robots_sims/ directory.

Using the IDE create a file low_gravity.world inside worlds directory. This is our world file. Write the following content to it:

<sdf version="1.4">
<world name="default">
    <include>
        <uri>model://sun</uri>
    </include>
    <gravity>0 0 0.0</gravity>
    <include>
        <uri>model://ground_plane</uri>
    </include>
</world>
</sdf>

In this file the tag gravity helps us to manipulate the gravity inside the gazebo world.

Next, to launch the world we need to create a launch file main.launch. Before creating this file we create a launch directory inside ~simulation_ws/src/my_legged_robots_sims/ directory.

Using the IDE we add the following code to main.launch file

<?xml version="1.0" encoding="UTF-8"?>
<launch>
    <arg name="robot" default="machines"/>
    <arg name="debug" default="false"/>
    <arg name="gui" default="true"/>
    <arg name="headless" default="false"/>
    <arg name="pause" default="false"/>  <!-- Start Gazebo with a blank world -->
    <include file="$(find gazebo_ros)/launch/empty_world.launch">
        <arg name="world_name" value="$(find my_legged_robots_sims)/worlds/low_gravity.world"/>
        <arg name="debug" value="$(arg debug)" />
        <arg name="gui" value="$(arg gui)" />
        <arg name="paused" value="$(arg pause)"/>
        <arg name="use_sim_time" value="true"/>
        <arg name="headless" value="$(arg headless)"/>
        <env name="GAZEBO_MODEL_PATH" value="$(find my_legged_robots_sims)/models:$(optenv GAZEBO_MODEL_PATH)"/>
    </include>
</launch>

<?xml version="1.0" encoding="UTF-8"?> 
<launch> 
 <include file="$(find spawn_robot_tools_pkg)/launch/spawn_robot_urdf.launch"> 
  <arg name="x" default="0.0" /> 
  <arg name="y" default="0.0" /> 
  <arg name="z" default="1.0" /> 
  <arg name="roll" default="0"/> 
  <arg name="pitch" default="0"/> 
  <arg name="yaw" default="0.0" /> 
  <arg name="urdf_robot_file" default="$(find my_legged_robots_sims)/urdf/monoped_controlled.urdf" />
  <arg name="robot_name" default="monoped" /> 
 </include> 
</launch>

Notice in line 9 we have provided the name of our world file low_gravity.world.
This launch file will only launch an empty world in gazebo with zero gravity. To launch it click on the Simulation menu and select Select launch file option and choose the main.launch item.

To spawn the monoped we need to create another launch file inside the ~simulation_ws/src/my_legged_robots_sims/launch/ directory.
Use IDE to create a new file named spawn_monoped.launch and add following content:

<?xml version="1.0" encoding="UTF-8"?>
<launch>
    <include file="$(find spawn_robot_tools_pkg)/launch/spawn_robot_urdf.launch">
        <arg name="x" default="0.0" />
        <arg name="y" default="0.0" />
        <arg name="z" default="1.0" />
        <arg name="roll" default="0"/>
        <arg name="pitch" default="0"/>
        <arg name="yaw" default="0.0" />
        <arg name="urdf_robot_file" default="$(find my_legged_robots_sims)/urdf/monoped_controlled.urdf" />
        <arg name="robot_name" default="monoped" />
    </include>
</launch>

To run it, start a SHELL from tools menu and execute following command.

$ roslaunch my_legged_robots_sims spawn_monoped.launch

You should see the monoped load in the gazebo world.
We can change the gravity settings in low_gravity.world file and relaunch the robot to see the effect of gravity. Since we have not actuated the robot the robot will fall under the influence of gravity, which is totally fine. This finishes the steps to create the hopper robot using Robot Development Studio as shown in the video.

Checkout the URDF robot creation course in RobotIgniteAcademy: https://goo.gl/NJHwq3

[irp posts=”8194″ name=”All about Gazebo 9 with ROS”]

Part 2

In this second video of a new ROS Development Studio video series, you are going to continue to learn step by step
how to create your own hopper simulation, and may be a real version if there is high support to this videos.

You will learn in this video

  • how to create your ROS packages,
  • modify a URDF given by Alexander W. Winkler, https://github.com/leggedrobotics/xpp to give it control and physics
  • and add all the needed sensors like IMU, odometry and contact sensor.

All the code of the project will be uploaded to this public repo, don’t hesitate to make improvements and add more content:
https://bitbucket.org/theconstructcore/hopper/src/master/

Checkout the URDF robot creation course in RobotIgniteAcademy: www.robotigniteacademy.com

 


Robot-Creation-with-URDF-ROS--banner

Pin It on Pinterest