Simulation Fundamentals - UTRA-ART/Caffeine GitHub Wiki

Written by: Erick Mejia Uzeda

To speed up the development process and enable collaboration between multiple developers without requiring each one to have their own robot we use simulations extensively. The goal of this wiki is to introduce the simulation environment and enable one to build a working robot in simulation. The topics to covered are:

  • RViz
  • URDF
    • links + joints
    • meshes
    • Publishing the TF tree
  • Gazebo
    • inertials
    • plugins
    • worlds

To consolidate everything, the last section of the wiki will have a sample workflow for how to build a robot in simulation.

Screenshot of Gazebo (bottom left), RViz (bottom right) and a launch file (top left) for spawning/viewing Caffeine in Ubuntu 18.04 LTS

RViz

RViz is not a simulator, but rather used for topic visualization. Whereas a simulator creates the world itself, RViz displays what the robot thinks the world looks like. Discrepancies between Gazebo (simulator) and RViz can occur, but if anything is too off, then there is most likely an issue with the robot!

RViz does not require Gazebo to work, so we can check whether our robot is being assembled correctly in RViz before spawning the robot in Gazebo by visualizing the RobotModel. There are many topics that can be visualized in RViz, so I will point you to its ROS wiki for more information.

Lastly, a convenience of RViz is that we can launch it as a node with a specified configuration using:

<!-- RViz config files are generally saved in the /rviz folder -->
<node name="rviz" pkg="rviz" type="rviz" args="-d $(find package)/rviz/robot.rviz" required="true"/>

This way one does not need to re-add topics to visualize in RViz every session.

URDF

URDF stands for Unified Robot Description Format and describes the layout of all of the robot's parts. We will use xacro to make writing URDFs less verbose. A URDF is parsed and added to the parameter server in a launch file using:

<!-- Parse URDF with xacro -->
<param name="robot_description" command="xacro '$(find package)/urdf/robot.urdf.xacro'"/>

Where robot.urdf.xacro is the robot's URDF generally placed in the /urdf folder. Every URDF file must be enclosed in

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

  <link name="base_link"/>

  <!-- Your URDF code here -->

</robot>

links + joints

Each part of the robot is specified by a <link> tag and connected to the robot via a <joint> tag. Moreover, every robot must have a base_link as defined above - it serves as the robot's origin. All other links of the robot must be connected to this link or to another link which is eventually connected to base_link.

A <link> can have attributes which can be found here. The attributes of primary interest are:

<visual>

Describes the visual element (eg. mesh, cube) that will be displayed at that link.

<collision>

A mesh (or simple geometry) used in the simulation of collisions between objects.

<inertial>

Specifies the mass and inertia of the part associated to the link. Required to subject the robot to the physics engine.

A <joint> can be of many types to enable mobility, which are found here. A simple fixed joint is defined as follows

<joint name="joint" type="fixed">
  <parent link="parent_link"/>
  <child link="child_link"/>
  <origin xyz="0 0 0" rpy="0 0 0"/>
</joint>

A joint always connects two links. In terms of the tf_tree, it creates an edge from parent_link to child_link.

meshes

For complex geometries, one can display them using a mesh. Moreover, meshes can be used to define collisions as well - but are significantly more computationally expensive for the physics engine than simple geometries. In our case, these meshes represent parts of the robot that we designed in (eg.) CAD.

To use a mesh in Gazebo, it must be either a .dae file (Collada) or .stl file (STL). Collada files can also hold texture information, whereas STL files only store geometry. To make life easy, it is best to set the origin of the mesh to its center of mass and record the transform between its center of mass and the center of mass of the part it will be connected to.

Meshes can be used in a <visual> or <collision> by using

<mesh filename="package://package/urdf/meshes/mesh.dae"/>

where mesh.dae is in the /urdf/meshes folder.

Publishing the TF tree

To publish the tf_tree of the robot as described by the URDF, we use the joint_state_publisher and robot_state_publisher nodes. A more in-depth explanation of how this is done can be found in the Publishing the TF Tree wiki. The state publisher nodes are called with the parsed URDF as input as nodes in a launch file.

To check whether the URDF is working, use rqt_tf_tree to inspect the tf_tree and RViz to view your robot!

Gazebo

Gazebo is a simulator with a physics engine. In essence it mimics the real world, eliminating the necessity for building a real robot for the sake of testing and development. Gazebo is quite computationally demanding and is thus generally launched without a GUI unless needed.

inertials

The <inertial> tag must have a non-zero <mass> attribute so that the corresponding link is displayed/simulated in Gazebo. One must also specify an <inertia> attribute within the <inertial> tag using a 3x3 inertia tensor. Since an inertia tensor is symmetric, it can be specified using 6 values (instead of 9).

To obtain the correct values to use for the <inertial> tag, go to your CAD and compute the part's mass and inertia tensor with respect to its center of mass. You also want to make sure that the x-y-z axes correspond to front-left-up in your part's local frame.

plugins

Plugins are Gazebo (simulation) specific, and are not needed for the physical robot. For that reason we generally add plugins in a separate file and include it to the URDF for parsing. Note. gazebo plugin code is ignored unless running Gazebo.

Plugins are generally added in a file plugins.gazebo.xacro inside the /urdf folder. plugins.gazebo.xacro is structured the same way robot.urdf.xacro is, but does not contain any links or joints and instead primarily contains <gazebo> tags that specify a variety of controllers. Adding a plugin varies depending on which plugin you are adding, so it is recommended to consult the Gazebo website.

<!-- Differential Drive Controller -->
<gazebo>
    <plugin name="differential_drive_controller" filename="libgazebo_ros_diff_drive.so">

        <alwaysOn>true</alwaysOn>
        <legacyMode>false</legacyMode>
        <rosDebugLevel>na</rosDebugLevel>

        <!-- Plugin update rate (Hz) -->
        <updateRate>30</updateRate>
            
        <!-- Name of joints connecting to wheels -->
        <leftJoint>front_left_wheel_joint</leftJoint>
        <rightJoint>front_right_wheel_joint</rightJoint>

        <!-- Wheel properties -->
        <wheelSeparation>${wheel_separation}</wheelSeparation>
        <wheelDiameter>${2.0 * wheel_radius}</wheelDiameter>
        <wheelAcceleration>0</wheelAcceleration>
        <wheelTorque>5</wheelTorque>
        <torque>5</torque>

        <!-- Topic to receive geometry_msgs/Twist message commands -->
        <commandTopic>cmd_vel</commandTopic>

        <!-- Odometry (nav_msgs/Odometry) settings -->
        <odometrySource>world</odometrySource>
        <odometryTopic>odom</odometryTopic>
        <odometryFrame>odom</odometryFrame>

        <!-- Robot frame to calculate odometry from -->
        <robotBaseFrame>base_link</robotBaseFrame>

        <!-- Publish TF frames -->
        <publishTf>true</publishTf>
        <publishOdomTF>true</publishOdomTF>

        <publishWheelTF>false</publishWheelTF>
        <publishWheelJointState>true</publishWheelJointState>

    </plugin>
</gazebo>

<!-- ROS Control: Reads transmission tags -->
<gazebo>
    <plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so"/>
</gazebo>

Example of using the differential drive controller plugin

Lastly, to include the gazebo plugin file into your URDF add the following line of code to robot.urdf.xacro:

<xacro:include filename="$(find package)/urdf/plugins.gazebo.xacro"/>

worlds

Gazebo worlds are written in SDF (Simulation Description Format) and saved as .world files. There are quite a few different tags used to describe a world using SDF which can be found here. I will not go over much of world building because I've only built a simple one and world building is not necessarily done very often (ie. one can find pre-built worlds from other repositories - please cite them if you use them!).

The key point when it comes to world building is adding custom models (eg. a mesh of the world layout). Models are kept in the /models folder and have various components:

/model_name
  model.config                       # identifies the model
  model.sdf                          # describes the model in SDF - references used materials
  /materials/scripts/shader.material # material shader for rendering textures - references used textures 
  /materials/textures/texture.png    # texture to be rendered by the material shader

Note. A model can only be loaded by Gazebo if it (/model_name and all its contents) exists in the ~/.gazebo/models folder. So once a model is created it needs to be copied there!

Workflow

The process of building a robot in simulation is not difficult but one generally encounters unexpected issues. It is always a good idea to do things in steps, ensuring that everything works as expected before moving on. Use rqt_tf_tree to double check your robot is built as expected! Here are the steps I would follow:

  1. Create robot_spawn.launch

Every time one checks if their robot is working, they will launch this launch file. Make sure that the URDF is parsed correctly and that joint_state_publisher and robot_state_publisher are running (check using rqt_graph). I would create a URDF only containing base_link for testing.

  1. Build the visuals of the robot

Add a <link> then connect it to your robot via a <joint>. It is a good idea to already add the mesh of the corresponding part in the link's <visual> tag, so as to make visualization and debugging easier. Adding parts one-by-one then verifying the model in RViz is recommended.

  1. Spawn the robot in Gazebo

First update your launch file to start Gazebo and spawn your robot in Gazebo. Note. without any inertial properties, the link is not displayed in Gazebo. Re-visit you CAD model and find the inertias + masses for all of your parts. Use that information to populate the <inertial> tag for all of your links. Once done, your robot should spawn and be visible in both Gazebo and Rviz!

  1. Add the motor plugin

Add a motor (drive controller) gazebo plugin to enable the robot to move. Running

<node name="rqt_robot_steering" pkg="rqt_robot_steering" type="rqt_robot_steering">
  <param name="default_topic" value="/cmd_vel"/> 
</node>

will open a GUI to control the robot via the /cmd_vel topic. If you are using the diff_drive_controller plugin, check that odom and cmd_vel topic exist. If your robot behaves all wonky when moving, the issue is generally wrong inertia values or collisions, so double check those.

  1. Add other sensors

Create a new link (+ joint) for any sensors that will be added, with the appropriate Gazebo plugin. Test them by verifying the correct topics are being published.

Extra

A common convention is to build the robot in a package called description. You can find an example robot description for a Sumo bot in this repo.

Xacro has this nice feature called macros which enable code re-use. I suggest searching it up and using it, it makes coding up a robot a lot less verbose!

⚠️ **GitHub.com Fallback** ⚠️