How to spawn a Gazebo robot using XML launch files

How to spawn a Gazebo robot using XML launch files

What we are going to learn

  1. How to start Gazebo
  2. How to spawn a robot to Gazebo
  3. How to run the Robot State Publisher node
  4. How to start Rviz configured

List of resources used in this post

  1. Use the rosject: https://app.theconstructsim.com/l/56476c77/
  2. The Construct: https://app.theconstructsim.com/
  3. ROS2 Courses –▸
    1. ROS2 Basics in 5 Days Humble (Python): https://app.theconstructsim.com/Course/132
    2. ROS2 Basics in 5 Days Humble (C++): https://app.theconstructsim.com/Course/133

Overview

While many examples demonstrate how to spawn Gazebo robots using Python launch files, in this post, we will be learning how to achieve the same result using XML launch files. Let’s get started!

ROS Inside!

ROS Inside

ROS Inside

Before anything else, if you want to use the logo above on your own robot or computer, feel free to download it and attach it to your robot. It is really free. Find it in the link below:

ROS Inside logo

Opening the rosject

In order to follow this tutorial, we need to have ROS2 installed in our system, and ideally a ros2_ws (ROS2 Workspace). To make your life easier, we have already prepared a rosject with a simulation for that: https://app.theconstructsim.com/l/56476c77/.

You can download the rosject on your own computer if you want to work locally, but just by copying the rosject (clicking the link), you will have a setup already prepared for you.

After the rosject has been successfully copied to your own area, you should see a Run button. Just click that button to launch the rosject (below you have a rosject example).

Learn ROS2 Parameters - Run rosject

How to spawn a Gazebo robot using XML launch files – Run rosject (example of the RUN button)

 

After pressing the Run button, you should have the rosject loaded. Now, let’s head to the next section to get some real practice.

Compiling the workspace

As you may already know, instead of using a real robot, we are going to use a simulation. In order to spawn that simulated robot, we need to have our workspace compiled, and for that, we need a terminal.

Let’s open a terminal by clicking the Open a new terminal button.

 

Open a new Terminal

Open a new Terminal

 

Once inside the first terminal, let’s run the commands below, to compile the workspace

cd ~/ros2_ws
colcon build
source install/setup.bash
There may be some warning messages when running “colcon build”. Let’s just ignore those messages for now.
If everything went well, you should have a message saying that 3 packages were compiled:
How to spawn a Gazebo robot using XML launch files - ros2_ws compiled

How to spawn a Gazebo robot using XML launch files – ros2_ws compiled

Starting the Gazebo simulator

Now that our workspace is compiled, let’s run a gazebo simulation and RViz using normal python launch files.

For that, run the following command in the terminal:

ros2 launch minimal_diff_drive_robot gazebo_and_rviz.launch.py
Again, you may see some error messages. As long as the simulation appears, you can just ignore those error messages.
Now, in a second terminal, let’s also launch the Robot State Publisher, so that we can properly see the robot in RViz (Robot Visualization tool).

 

ros2 run joint_state_publisher joint_state_publisher
Now you should be able to see both Gazebo simulator and RViz, similar to what we can see in the image below:
How to spawn a Gazebo robot using XML launch files - Gazebo and RViz launched

How to spawn a Gazebo robot using XML launch files – Gazebo and RViz launched

In case you want to know, the content of the file used to spawn the robot can be seen with:

cat ~/ros2_ws/src/minimal_diff_drive_robot/launch/gazebo_and_rviz.launch.py

 

Moving the robot around

To make sure everything is working as expected so far, you can also run a new command to move the robot around using the keyboard. For that, open a third terminal, and run the following command:

ros2 run teleop_twist_keyboard teleop_twist_keyboard

Now, to move the robot around just press the keys “i“, “k“, or other keys presented in the terminal where you launched the teleop_twist_keyboard command.

The XML file for launching spawning the robot

As we mentioned earlier, the code for the python launch file can be found seen with:

cat ~/ros2_ws/src/minimal_diff_drive_robot/launch/gazebo_and_rviz.launch.py

 

and for the XML file? The XML file is in the exact same folder, but the file has an XML extension. The content of the file can be seen with:

cat ~/ros2_ws/src/minimal_diff_drive_robot/launch/gazebo_and_rviz.launch.xml
The command above outputs the following:
<?xml version="1.0"?>

<launch>
  <arg name="model" default="$(find-pkg-share minimal_diff_drive_robot)/urdf/minimal_diff_drive_robot.urdf" />

  <arg name="start_gazebo" default="true" />
  <arg name="start_rviz" default="true" />

  <!-- Start Gazebo -->
  <group if="$(var start_gazebo)">
    <include file="$(find-pkg-share gazebo_ros)/launch/gazebo.launch.py">
      <!--arg name="paused" value="true"/>
      <arg name="use_sim_time" value="true"/>
      <arg name="gui" value="true"/>
      <arg name="recording" value="false"/>
      <arg name="debug" value="false"/>
      <arg name="verbose" value="true"/-->
    </include>

    <!-- Spawn robot in Gazebo -->
    <node name="spawn_robot_urdf" pkg="gazebo_ros" exec="spawn_entity.py"
      args="-file $(var model) -x 0.0 -y 0.0 -z 0.0 -entity my_robot" output="screen" />
  </group>

  <!-- TF description -->
  <node name="robot_state_publisher" pkg="robot_state_publisher" exec="robot_state_publisher" output="screen">
    <param name="robot_description" value="$(command 'cat $(var model)')"/>
    <param name="use_sim_time" value="true" />
  </node>

  <!-- Show in Rviz   -->
  <group if="$(var start_rviz)">
    <node name="rviz" pkg="rviz2" exec="rviz2" args="-d $(find-pkg-share minimal_diff_drive_robot)/config/robot.rviz">
      <param name="use_sim_time" value="true" />
    </node>
  </group>

</launch>

If we check carefully the output above, we can see that we start launching the Gazebo simulator, and in the same <group> we spawn the robot in Gazebo by calling spawn_entity.py

Then we launch the Robot State Publisher to be able to see the robot in RViz, and finally, we launch RViz itself.

When launching RViz, we tell it to use a file named config/robot.rviz, as we can see at:

$(find-pkg-share minimal_diff_drive_robot)/config/robot.rviz
That “command” translates to the following path:
cat ~/ros2_ws/src/minimal_diff_drive_robot/config/robot.rviz
Feel free to check the content of that file, be it through the Code Editor, or in the terminal by checking what the cat command outputs.

Spawning the robot in Gazebo using XML launch files

Similar to what we did with Python, you can just run the following command to spawn the robot using XML file.

Please, remember to kill the previous programs by pressing CTRL+C in the terminals where you launched the commands previously.

Assuming that now all previous programs are terminated, let’s spawn gazebo using XML in the first terminal:

ros2 launch minimal_diff_drive_robot gazebo_and_rviz.launch.xml
Now, in the second terminal, let’s launch the Joint State Publisher to be able to correctly see the robot wheels in RViz:
ros2 run joint_state_publisher joint_state_publisher

And on the third terminal, you can start the command to move the robot around:

ros2 run teleop_twist_keyboard teleop_twist_keyboard

And that is basically it

Congratulations. Now you know how to spawn a robot in Gazebo using Python and also using XML launch files.

We hope this post was really helpful to you. If you want a live version of this post with more details, please check the video in the next section.

Youtube video

So this is the post for today. Remember that we have the live version of this post on YouTube. If you liked the content, please consider subscribing to our youtube channel. We are publishing new content ~every day.

Keep pushing your ROS Learning.

Related Courses & Training

If you want to learn more about ROS and ROS2, we recommend the following courses:

Get ROS2 Industrial Ready- Hands-On Training by The Construct cover.png

Get ROS2 Industrial Ready- Hands-On Training by The Construct cover.png

[Gazebo in 5 minutes] 011 – How to use the joint_state_publisher

[Gazebo in 5 minutes] 011 – How to use the joint_state_publisher

In this post, you will learn how to use the joint_state_publisher node. At the end of the post, you will be able to debug your robot model joints using a graphical interface. Let’s go!

Step 1: Fire up a system with ROS installation

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

Once logged in, click here to copy the ROS project (rosject) we’ll use for this post. Once copied, click the Run button to open the rosject.

You might also want to try this on a local PC if you have ROS installed. However, please note that we don’t 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. You should be able to replicate the same on any ROS 1 distro, with minor changes.

Step 2: Identify the joints in your robot

For this post, we are using the following robot model, which you’ll find by opening the IDE and locating the file as shown in the image below.

<?xml version="1.0" ?>
<robot name="my_robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
    
    <!-- 1st link -->
  <link name="link_chassis">
    <pose>0 0 0.1 0 0 0</pose>
    
    <inertial>
      <mass value="5"/>
      <origin xyz="0 0 0.1" rpy="0 0 0"/>
      <inertia ixx="0.0395416666667" ixy="0" ixz="0" iyy="0.106208333333" iyz="0" izz="0.106208333333"/>
    </inertial>
    
    <collision name="collision_chassis">
      <geometry>
        <box size="1 1 2"/>
      </geometry>
    </collision>
    
    <visual>
      <origin rpy="0 0 0" xyz="0 0 0"/>
      <geometry>
        <box size="1 1 2"/>
      </geometry>
    </visual>
  </link>
  
  <joint name="joint1" type="continuous">
    <origin xyz="0.6 0 1" rpy="0 0 0"/>
    <parent link="link_chassis"/>
    <child link="link_arm"/>
  </joint>
  
  <!-- 2nd link -->
  <link name="link_arm">
    <pose>0 0 0 0 0 0</pose>
    
    <inertial>
      <mass value="0.5"/>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <inertia ixx="0.0395416666667" ixy="0" ixz="0" iyy="0.106208333333" iyz="0" izz="0.106208333333"/>
    </inertial>
    
    <collision name="collision_chassis">
      <origin xyz="0 0 0.5" rpy="0 0 0"/>
      <geometry>
        <box size="0.2 0.2 1"/>
      </geometry>
    </collision>
    
    <visual>
      <origin xyz="0 0 0.5" rpy="0 0 0"/>
      <geometry>
        <box size="0.2 0.2 1"/>
      </geometry>
    </visual>
  </link>
 
</robot>

The joint in question here is highlighted in the code above.

Step 3: Use the joint_state_publisher node with the rviz node

The launch file is in simulation_ws/src/my_robot_description/launch/rviz.launch. The lines where the joint_state_publisher node is started is highlighted below.

<?xml version="1.0"?>
<launch>

    <param name="robot_description" command="$(find xacro)/xacro --inorder '$(find my_robot_description)/urdf/robot.urdf'" />    

    <!-- send fake joint values -->
    <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher">
        <param name="use_gui" value="True"/>
    </node>
    
    <!-- Combine joint values -->
    <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher"/>
    
    <!-- Show in Rviz   -->
    <node name="rviz" pkg="rviz" type="rviz" args="-d $(find my_robot_description)/rviz/robotmodel.rviz" />

</launch>

Step 4: Use the joint_state_publisher to move the joint

Open a web shell and launch rviz with the command shown below.

roslaunch my_robot_description rviz.launch

You should now see two displays: rviz and the joint_state_publisher gui. You should be able to move the joint as shown in the image below.

Step 5: Check your learning

Do you now understand how to use the joint_state_published node? If not, please go over the post again, more carefully this time.

Step 6 (Optional): watch the video to learn more

Feedback

Did you like this video? Whatever the case, 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 on the comments area, and we will do a video about it.

Thank you!

[Gazebo in 5 mins] 010 – How to launch RViz using a configuration file

[Gazebo in 5 mins] 010 – How to launch RViz using a configuration file

In this post, you’ll learn how to launch RViz using a configuration file. You will learn how to close and open RViz as many times you need without having to configure or customize it all over again.

Step 1: Fire up a system with ROS installation

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

Once logged in, click here to copy the ROS project (rosject) we’ll use for this post. Once copied, click the Run button to open the rosject.

You might also want to try this on a local PC if you have ROS installed. However, please note that we don’t 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. You should be able to replicate the same on any ROS 1 distro, with minor changes.

Step 2: Run RViz without loading a configuration file

Open the Code Editor and create a new file rviz_plain.launch in the location shown in the image below:

Here is the content of the file:

<?xml version="1.0"?>
<launch>

    <param name="robot_description" command="$(find xacro)/xacro --inorder '$(find my_robot_description)/urdf/robot.urdf'" />    

    <!-- send fake joint values -->
    <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher">
        <param name="use_gui" value="True"/>
    </node>
    
    <!-- Combine joint values -->
    <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher"/>
    
    <!-- Show in Rviz   -->
    <node name="rviz" pkg="rviz" type="rviz" />

</launch>

Next, launch the rviz_plain.launch file. Open a Web Shell and type the command that follows.

roslaunch my_robot_description rviz_plain.launch

Now you should see the rviz window, with no display added. We would need to add the display one by one and then configure them. But this is not necessary, since we have a configuration file saved already. Let’s load it next.

Step 3: Run RViz in a launch file and pass the configuration

Stop the current program in the web shell using Ctrl + C and run the following command instead.

roslaunch my_robot_description rviz.launch

Now you should see a different rviz screen, generated from the configuration!

Can you spot some differences in the two launch files? Here is the rviz.launch file.

<?xml version="1.0"?>
<launch>

    <param name="robot_description" command="$(find xacro)/xacro --inorder '$(find my_robot_description)/urdf/robot.urdf'" />    

    <!-- send fake joint values -->
    <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher">
        <param name="use_gui" value="True"/>
    </node>
    
    <!-- Combine joint values -->
    <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher"/>
    
    <!-- Show in Rviz   -->
    <node name="rviz" pkg="rviz" type="rviz" args="-d $(find my_robot_description)/rviz/robotmodel.rviz" />

</launch>

Step 4: Check your learning + Extra Tip

Do you now understand how to launch rviz using a configuration file? If not, please go over the post again, more carefully this time.

You can create a configuration file by using the File -> Save Config As... command on the rviz window. Use File -> Save Config to update the current config.

Step 5 (optional): watch the video

Feedback

Did you like this video? Whatever the case, 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 on the comments area, and we will do a video about it.

Thank you!

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.

How to use the Gazebo differential drive plugin in ROS 2

How to use the Gazebo differential drive plugin in ROS 2

In this post, you will learn how to use the Gazebo differential drive plugin in ros 2. This post answers the following question posted on ROS Answers.

Step 1: Copy a sample project with a ROS 2 Gazebo simulation using the differential drive plugin

“Hey, do I have to install ros2 first?” Absolutely not! We will be using The Construct to get access to virtual machines pre-installed with ROS.

Click here to copy the ROS2 TurtleBot3 sandbox project. Once copied, click the red RUN button to launch the project in a virtual machine. Please be patient while the environment loads.

PS: You will need to log in or create an account to copy the packages.


You might also want to try this on a local PC if you have ros2 and some executables 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 post assumes that you are working on The Construct; please adapt them to your local PC and ros2 installation.

Step 2: Find and explore the Gazebo model file containing the differential drive plugin

Now, we will find a Gazebo model file with the differential drive plugin. For this post, we’ll use the turtlebot3_ws/src/turtlebot3_simulations/turtlebot3_gazebo/models/turtlebot3_burger/model.sdf file. Sometimes it’s also defined as a .xacro file.

Head over to the Code Editor to explore this file as well as other files.

Open the Code Editor
Gazebo model file

Let’s examine the differential drive section of this plugin file and compare it with the one on the ROS Answers post.

turtlebot3_ws/src/turtlebot3_simulations/turtlebot3_gazebo/models/turtlebot3_burger/model.sdf

<plugin name="turtlebot3_diff_drive" filename="libgazebo_ros_diff_drive.so">

      <ros>
        <!-- <namespace>/tb3</namespace> -->
      </ros>

      <update_rate>30</update_rate>

      <!-- wheels -->
      <left_joint>wheel_left_joint</left_joint>
      <right_joint>wheel_right_joint</right_joint>

      <!-- kinematics -->
      <wheel_separation>0.160</wheel_separation>
      <wheel_diameter>0.066</wheel_diameter>

      <!-- limits -->
      <max_wheel_torque>20</max_wheel_torque>
      <max_wheel_acceleration>1.0</max_wheel_acceleration>

      <command_topic>cmd_vel</command_topic>

      <!-- output -->
      <publish_odom>true</publish_odom>
      <publish_odom_tf>true</publish_odom_tf>
      <publish_wheel_tf>false</publish_wheel_tf>

      <odometry_topic>odom</odometry_topic>
      <odometry_frame>odom</odometry_frame>
      <robot_base_frame>base_footprint</robot_base_frame>

    </plugin>

On lines 385 and 392, we see entries for the /cmd_vel (<command_topic>cmd_vel</command_topic>) and /odom (<odometry_topic>odom</odometry_topic>) topics respectively. However, these entries are missing in the file on ROS Answers.

Gazebo plugin file: mising command and odometry entries

<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro"  name="robot">

   <gazebo>
        <plugin name="diff_drive" filename="libgazebo_ros_diff_drive.so">
            <!-- Wheel info-->
            <left_joint>left_wheel_joint</left_joint>
            <right_joint>right_wheel_joint</right_joint>
            <wheel_separation>0.35</wheel_separation>
            <wheel_diameter>0.1</wheel_diameter>

            <!-- Limits-->
            <max_wheel_torque>200</max_wheel_torque>
            <max_wheel_acceleration>10.0</max_wheel_acceleration>

            <!-- Output-->
            <odometry_frame>odom</odometry_frame>
            <robot_base_frame>base_link</robot_base_frame>

            <publish_odom>true</publish_odom> 
            <publish_odom_tf>true</publish_odom_tf> 
            <publish_wheel_tf>true</publish_wheel_tf> 

        </plugin>
     </gazebo>

</robot>

Are these lines really necessary for moving the robot and getting its odometry? Let’s find out!

Step 3: Investigate the impacts of the of the command and odometry topic tags

Let’s comment out those tags in the file, and see if we can find the /cmd_vel and /odom topics, and if they work.

turtlebot3_ws/src/turtlebot3_simulations/turtlebot3_gazebo/models/turtlebot3_burger/model.sdf (modified)

<plugin name="turtlebot3_diff_drive" filename="libgazebo_ros_diff_drive.so">

      <ros>
        <!-- <namespace>/tb3</namespace> -->
      </ros>

      <update_rate>30</update_rate>

      <!-- wheels -->
      <left_joint>wheel_left_joint</left_joint>
      <right_joint>wheel_right_joint</right_joint>

      <!-- kinematics -->
      <wheel_separation>0.160</wheel_separation>
      <wheel_diameter>0.066</wheel_diameter>

      <!-- limits -->
      <max_wheel_torque>20</max_wheel_torque>
      <max_wheel_acceleration>1.0</max_wheel_acceleration>

      <!-- <command_topic>cmd_vel</command_topic> -->

      <!-- output -->
      <publish_odom>true</publish_odom>
      <publish_odom_tf>true</publish_odom_tf>
      <publish_wheel_tf>false</publish_wheel_tf>

      <!-- <odometry_topic>odom</odometry_topic> -->
      <odometry_frame>odom</odometry_frame>
      <robot_base_frame>base_footprint</robot_base_frame>

    </plugin>

Now let’s run the package…in Terminal 1

cd
source turtlebot3_ws/install/setup.bash
export TURTLEBOT3_MODEL=burger
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py

…check the topics in Terminal 2

ros2 topic list

# output:
/clock
/cmd_vel
/imu
/joint_states
/odom
/parameter_events
/performance_metrics
/robot_description
/rosout
/scan
/tf
/tf_static

Well, the topics are there, but are they working? Let’s publish to the /cmd_vel topic and see if the robot moves. We also echo the /odom in another terminal. Run the following in Terminal 2:

# Try to move the robot with teleop
ros2 run teleop_twist_keyboard teleop_twist_keyboard

Then in Terminal 3:

ros2 topic echo /odom

The robot didn’t move and nothing was echoing from /odom!

Now we need to confirm that it’s not working because of those tags. Let’s modify the launch command in Terminal 1. Press Ctrl + C to stop the simulation and run the following commands instead:

# note that we changed the Turtlebot3 model
export TURTLEBOT3_MODEL=waffle
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py

Now try to move the robot again and check the /odom eching…Poof, both working! Why? We modified the model file for “burger”; the one for “waffle” was intact!

Final confirmation: uncomment the lines in turtlebot3_ws/src/turtlebot3_simulations/turtlebot3_gazebo/models/turtlebot3_burger/model.sdf , stop the simulation in Terminal 1 and run the following commands.

# note that we changed the Turtlebot3 model back to burger
export TURTLEBOT3_MODEL=burger
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py

Done! Now everything should be working!

So we have confirmed that the command and odometry tags are necessary.

Step 4: Check your learning

Do you understand how to use the Gazebo differential drive plugin in ROS 2? If you don’t know it yet, please go over the post again, more carefully this time.

(Extra) Step 5: Watch the video to understand how to use the Gazebo differential drive plugin in ROS 2

Here you go:

Feedback

Did you like this post? Do you have any questions about how to use the Gazebo differential drive plugin in ROS 2? 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 ROS2 topics, please let us know in the comments area and we will do a video or post about it.

Get ROS2 Industrial Ready- Hands-On Training by The Construct cover.png
Get ROS2 Industrial Ready- Hands-On Training by The Construct cover.png

Pin It on Pinterest