Robot Operating System: Getting Started with ROS2 Simulation

Sebastian

·

1. Description

         In robotics projects, simulation is an important aspect with many uses. First, you can test the behavior code you want your bot to execute. Second, you can use simulation to test different types of hardware, such as distance sensors, cameras, or 3D point cloud sensors, to see which works best. Third, the same software that visualizes the simulation can be used in real time with a real robot, viewing the environment as the robot scans and navigates it.

        In my robotics project, I start with ROS1 and provide a basic robot model that can be visualized using RVIZ. Then, I built a working prototype, completely based on Arduino, no ROS involved. At the current stage of the project, I'm working on building a Gazebo model compatible with vision sensors.

        This article continues the series, using Gazebo to quickly start a ROS2 simulation. We'll look at the basic tools for creating simulations - ROS launch files and Gazebo - and learn to apply the basic steps - selecting 3D models, placing them in the world.

        The technical background of this article is Ubuntu 20.04, ROS2 Foxy, Gazebo11, but it should work on newer versions as well.

2. Basic Robot Simulation Terminology

        This paragraph briefly lists all terms necessary to understand aspects of physics simulation. If you're firm about your vocabulary, skip this section.

  • Kinematics: Kinematics is the branch of physics that controls how a body moves with respect to time, position, velocity, and acceleration without regard to the weight of the body or outward forces such as gravity
  • Dynamics: Another branch of physics that considers how an object's position, velocity, and acceleration change when it has mass and is affected by a force. (In robotics this is also known as robot dynamics)
  • Odometry: A method of estimating the position of an object and calculating the distance and trajectory of the object's movement by continuously recording motion data
  • Inertia: This force is the resistance of a moving object to any other force that would change its direction, velocity, or acceleration
  • Friction: the force exerted when two objects move close to one another and resist that movement
  • Joint: A joint is a mechanical element that connects two objects. There are different types of joints that describe how connected objects move.

Source: ROS Robot Programming Book

  • RPY Value: This acronym stands for Roll, Pitch, Yaw and is used to describe the motion of an object in 3D space. Objects can move on these three axes according to the diagram below:

Source:  wikipedia.org

3. Step by step: How to create a simulated world

Gazebo started out of ROS, but has since been fully integrated. It focuses more on a full physics simulation of the robot and the world. In particular, the world provides correct physics simulation through the physics engine: the robot can bump into objects, which will move and eventually even hit your robot. Gazebo comes with predefined world models, and you can even define your own.

In this tutorial, we will start a gazebo simulation with an empty world and then spawn a robot inside it. In short, the basic steps are:

  • Create a new ROS package and set up the directory structure
  • Create an empty coordinate positioning file
  • Create startup file
  • Add additional physical properties to the robot URDF model
  • Parameterize robot URDF models to run with Gazebo or RVIZ.
  • Start an empty world with a startup file

The following sections describe these tasks in detail.

3.1 Step 1: Package Creation and Directory Structure

        We'll create a package structure that looks like this:

radu_gazebo/
├── config
│   └── rviz.config
├── launch
│   └── launch.py
├── radu_bot
│   └── __init__.py
├── resource
├── scripts
├── test
├── urdf
│   └── core.xacro
└── worlds
│   └── room.world
├── package.xml
├── setup.cfg
├── setup.py

For convenience, just run the following command:

ros2 pkg create  --build-type python radu_gazebo
mkdir radu_gazebo/launch radu_gazebo/world
mkdir radu_gazebo/launch radu_gazebo/world/room.world
touch radu_gazebo/launch/room.launch

3.2 Step 2: Clear the coordinate positioning file

        This file is an SDF file and will contain labels for everything we want to simulate: objects like walls, windows and furniture. We'll start with a simple blank world and gradually add new objects.room.world<model>

<!-- FILE: world/room.world -->
<?xml version='1.0'?>
<sdf version="1.6">
<world name="room">
  <include>
    <uri>model://sun</uri>
  </include>
  <include>
    <uri>model://ground_plane</uri>
  </include>
</world>
</sdf>

You can manually load this file into bower. But since we'll eventually be generating a robot in this mod as well, it's best to go ahead and start the file directly.

3.3 Step 3: Startup file

        As we learned in the previous article, ROS2 no longer supports XML startup files, and uses Python files instead.

        The startfile we use wraps the start command in the package and provides a world parameter.gazebo_ros

#!/usr/bin/python3
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
package_name = 'radu_bot'
world_file = 'room.world'
def generate_launch_description():
    pkg_gazebo_ros = get_package_share_directory('gazebo_ros')
    pkg_radu_simulation = get_package_share_directory(package_name)
    # launch Gazebo by including its definition
    gazebo = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(
            os.path.join(pkg_gazebo_ros, 'launch', 'gazebo.launch.py'),
        )
    )
    # load the world file
    world_arg = DeclareLaunchArgument(
          'world',
          default_value=[os.path.join(pkg_radu_simulation, 'worlds', world_file), ''],
          description='SDF world file')
    return LaunchDescription([
        gazebo,
        world_arg
    ])

3.4 Step 4: Extend the robot model with Gazebo tags

        In order to use RViz's URDF model in Gazebo we need to make some changes to the model.

        First, we need to provide the robot with additional physical aspects so that it will behave correctly in the simulation. Second, the visual appearance of the robots is different. Color and text definitions for RVIZ models do not apply. If using grids, they also need to be changed. Third, we also need additional plugins for the Gazebo tool to work properly

        Let's look at these changes step by step.

3.5 Physical Simulation Properties

inertia

An object's inertia is the reaction force exerted when its current motion is affected by another object. In the bower model, tags are used to represent this aspect.<inertial>

Here is an example:

<link name="base_link">
  <inertial>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <mass value="0.25" />
    <inertia ixx="0.000486" ixy="0.0" ixz="0.0" iyy="0.000486" iyz="0.0" izz="0.000729"/>
  </inertial>
  <visual>
    <origin rpy="0 0 0" xyz="0 0 0"/>
    <geometry>
        <cylinder radius="0.09" length="0.09"/>
    </geometry>
  </visual>
</link>

        The label positions the link relative to its parent link, here you can change the value to move the estimated center of mass. By which you specify this link mass in kilograms. Finally, this element is a matrix of how the forces on x, y, and z affect the link. You can read the physics articles on Wikipedia , or use this handy python script .<origin>xyz<mass><inertial>

friction

        Another set of variables controls the friction of the links in the robot. You express this with four values. First, static and dynamic contact stiffnesses are provided. Here we use the value and , which is the default used in many ROS projects. Second, the values ​​and are the static and dynamic coefficients of friction, which you can look up on wikipedia based on the linked material .<kp><kd>1000001.0<mu1><mu2>

<gazebo reference="base_link">
  <kp>100000.0</kp>
  <kd>1.0</kd>
  <mu1>10.0</mu1>
  <mu2>10.0</mu2>  
</gazebo>

collision

        This defines the hard material boundary for the robot - it affects how gravity and other forces are applied to the robot in the simulation. These properties are represented by tags in the link. Its properties are very simple: just copy the original link and value as shown below.<collision><geometry><origin>

<link name="base_link">
  <inertial>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <mass value="0.25" />
    <inertia ixx="0.000486" ixy="0.0" ixz="0.0" iyy="0.000486" iyz="0.0" izz="0.000729"/>
  </inertial>
  <visual>
    <origin rpy="0 0 0" xyz="0 0 0"/>
    <geometry>
        <cylinder radius="0.09" length="0.09"/>
    </geometry>
  </visual>
  <collision>
    <origin rpy="0 0 0" xyz="0 0 0"/>
    <geometry>
        <cylinder radius="0.09" length="0.09"/>
    </geometry>
  </collision>
</link>

joint properties

Joints in robots should be further modeled to express their real-world behavior.

  • For all non-static, non-contiguous joints, sets and values<upper><lower>
  • For all consecutive joints, add and limit<effort><velocity>
<joint name="camera_joint">
  <limit upper="0.5" lower="-0.5"/>
</joint>
<joint name="left_wheel_joint">
  <limit effort="0.1" velocity="0.005"/>
</joint>

3.6 Visualizing Simulation Properties

To change the bot's visuals, you have the following options:

simple color

This works the same way in RViz: in a link tag you can refer to an element.<visual><color>

<material name="blue">
  <color rgba="0 0 0.8 1"/>
</material>
<link name="camera">
  <visuals>
    <material name="blue">
  </visuals>
</link>

predefined grid

        Gazebo provides a set of built-in grids, which are listed in this source code file . To apply them, add tags inside tags as shown below.<material><gazebo>

<gazebo reference="base_link">
  <material>Gazebo/Grey</material>  
</gazebo>

custom grid

        When using a custom grid to represent links, simply reference them in the link's markup, like so:<geometry>

<link name="camera">
  <mesh filename:"package://radu_bot/model/meshes/camera.dae" />
</link>

        IMPORTANT NOTE: It is not recommended to use custom mesh files in labels as this will affect simulation performance. Instead, define values ​​based on the available Box, Cylinder, and Sphere types.<collision><geometry>

3.7 Step 5: Parameterize the robot URDF model to run with Gazebo or RViz

        As you can see, the changes required are fundamental. And they are not backwards compatible: all changes required by Gazebo cannot be parsed by RVIZ.

        For these reasons, complex robotics projects split URDF aspects into separate XACRO files. After some experimentation, I came up with the following hierarchy.

  • core- Contains core macros for rendering robot links and joints
  • rviz- RVIZ master file, which defines parameters and imports other files
  • rviz_viusals- Define how the robot is visualized in RViz
  • gazebo- the bower core file, just like defining parameters and importing other files
  • gazebo_visuals- Define how to visualize the robot in bower
  • gazebo_physics- Additional macros for calculating links and tags<inertial><collision>
  • gazebo_sensor- Add sensor data
  • gazebo_controll- Added ROS control plugin and defines macros for rendering labels<transmission>

Let's see how this approach works in practice. When running Xacro to render the model, the command -o radu_rviz_compiled.urdf' will be used. This document will...xacro rviz.xacro

  1. Import other required files
<xacro:include filename="$(find radu_bot)/urdf2/core.xacro"/>
<xacro:include filename="$(find radu_bot)/urdf2/visuals.xacro"/>

2. Define the basic parameters that control the execution of the macro

<xacro:property name="gazebo_build" value="false" />
<xacro:property name="rviz_build" value="true" />

3. Execute the macro to create the URDF model

<xacro:box_link name="base_link" size="0.6 0.3 0.05" color="${torso_color_name}" color_rgb="${torso_color_rgb}" />
<xacro:wheel_link name="right_wheel_frontside" />
<xacro:wheel_joint name="base_link_right_wheel_frontside" parent="base_link" child="right_wheel_frontside" xyz="0.2 -0.2 -0.05" />
<xacro:wheel_link name="right_wheel_backside" />
<xacro:wheel_joint name="base_link_right_wheel_backside" parent="base_link" child="right_wheel_backside" xyz="-0.2 -0.2 -0.05" />
<xacro:wheel_link name="left_wheel_frontside" /> 
<xacro:wheel_joint name="base_link_left_wheel_frontside" parent="base_link" child="left_wheel_frontside" xyz="0.2 0.2 -0.05" />
<xacro:wheel_link name="left_wheel_backside" /> 
<xacro:wheel_joint name="base_link_left_wheel_backside" parent="base_link" child="left_wheel_backside" xyz="-0.2 0.2 -0.05" />

        After playing with this approach for a while, I realized that the core logic for handling variability is inside the file: the macros for rendering links and joints have distinct chunks that will be triggered by the main file. See definitions below. In line 3, the condition is evaluated to add the RViz-specific visual. In line 14, another condition checks the gazebo physical properties and applies them to the model.core.xacro<link><xacro:if>

<xacro:macro name="box_link" params="name size color color_rgb" >
  <link name="${name}">
    <xacro:if value="${rviz_build}">
      <visual>
        <origin xyz="0 0 0" rpy="0 0 0"/>
        <geometry>
          <box size="${size}"/>
        </geometry>
        <material name="${color}">
          <color rgba="${color_rgb}"/>
        </material>
      </visual>
    </xacro:if>
    <xacro:if value="${gazebo_build}">
      <pose>0 0 0 0 0 0</pose>
      <xacro:box_inertia m="0.6" x="0.7" y="0.4" z="0.2"/>
      <collision name="collision_${name}">
        <origin xyz="0 0 0" rpy="0 0 0"/>
        <geometry>
          <box size="${size}"/>
        </geometry>
      </collision>
    </xacro:if>  
  </link>
</xacro:macro>

3.8 Step 6: Generate the robot

        Gazebo nodes are started via startup files, but robots need to be spawned into the nodes. Thanks to the blog post How to generate a robot in ROS2 , I created the following startup file.

#!/usr/bin/python3
import os
import sys
import rclpy
from gazebo_msgs.srv import SpawnEntity
from ament_index_python.packages import get_package_share_directory
package_name = 'radu_bot'
def main(args=None):
    rclpy.init(args=args)
    node = rclpy.create_node('minimal_client')
    cli = node.create_client(SpawnEntity, '/spawn_entity')
    sdf_file_path = (os.path.join(get_package_share_directory(package_name), 'urdf', 'radu_gazebo_compiled.urdf')),
    model = open(sdf_file_path[0], 'r').read()
    print("MODEL %s" %model)
    
    req = SpawnEntity.Request(
        name = "radu_bot",
        xml = model,
        robot_namespace = "radu",
        reference_frame = "world",
    )
    while not cli.wait_for_service(timeout_sec=1.0):
        node.get_logger().info('service not available, waiting again...')
    future = cli.call_async(req)
    rclpy.spin_until_future_complete(node, future)
    if future.result() is not None:
        node.get_logger().info(
            'Result ' + str(future.result().success) + " " + future.result().status_message)
    else:
        node.get_logger().info('Service call failed %r' % (future.exception(),))
    node.destroy_node()
    rclpy.shutdown()
if __name__ == '__main__':
    main()

The startup file can also convert Xacro files during startup, as shown in the diff_bot example . For example, to load the Gazebo configuration, you would execute the following command:

import xacro
def generate_launch_description():
    pkg_radu_simulation = get_package_share_directory(package_name)
    robot_description_path =  os.path.join(
        pkg_radu_simulation,
        "urdf",
        "gazebo.xacro",
    )
    
    robot_description = {"robot_description": xacro.process_file(robot_description_path).toxml()}

4. Start the robot

First, we build the current workspace.

$> colcon build --symlink-install --cmake-clean-first --event-handlers console_direct+ --packages-up-to radu_bot

Then we roll out the gazebo.

$> ros2 launch radu_bot gazebo.launch.py 
[INFO] [launch]: All log files can be found below /home/devcon/.ros/log/2021-05-30-09-07-30-541933-giga-36879
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [gzserver-1]: process started with pid [36886]
[INFO] [gzclient   -2]: process started with pid [36889]

Then generate the bot.

$> ros2 run radu_bot spawn_radu
[INFO] [1622358479.722919377] [minimal_client]: Result True SpawnEntity: Successfully spawned entity [radu_bot]

Finally, render the robot:

V. Conclusion

        This article shows you how to create a Gazebo-compatible simulation in ROS2 from scratch. This turned out to be a lengthy process, requiring you to (1) create a package, (2) create a coordinate file, (3) create a startup file, (4) add Gazebo specific tags to our robot model, ( 5) Parameterize your robot model for compatibility with Gazebo and RVIZ, and (6) generate the robot entity in the simulation. Form all these steps. Adding Gazebo physics is time intensive to learn and apply, and I hope you gain valuable insights as well. Finally, we can use a custom startup script file to generate RADU in Gazebo and RViz. From here, we can move the robot around in the simulation.

Guess you like

Origin blog.csdn.net/gongdiwudu/article/details/132383518