Fortress And Humble Tutorial
Build a Simple Robot
Introduction and explanation
What software are we using and why
All of these instructions have been tested using the current version of Ubuntu: Jammy Jellyfish.We are going to use two packages: Gazebo for the physics based Simulator and ROS (Robot Operating System) for the control system. There are many releases of each of these, and only some combinations are mutually compatible. Also, since we are focusing on a stable development platform, we will use the Long Term Support (LTS) versions that are current at the time we are posting this. As of 20Mar2024, this means we will use Gazebo Fortress (release 6) which is supported until 2025.
One quirk: the current LTS version of Gazebo only seems to work with the LTS version of Ubuntu
How will the demonstration proceed.We will go through 7 steps: 1) Install Gazebo Fortress and confirm that it is correct. 2) Examine an empty world 3) Build a simple robot and test it using keyboard controls. 4) Build a 'world' for our robot to explore. 5) Install ROS (and its components) and confirm the installation. 6) Add Lidar to the robot 7) Write control software in ROS to make the robot autonomous.
This post covers Step 3
One quirk: the current LTS version of Gazebo only seems to work with the LTS version of Ubuntu
How will the demonstration proceed.We will go through 7 steps: 1) Install Gazebo Fortress and confirm that it is correct. 2) Examine an empty world 3) Build a simple robot and test it using keyboard controls. 4) Build a 'world' for our robot to explore. 5) Install ROS (and its components) and confirm the installation. 6) Add Lidar to the robot 7) Write control software in ROS to make the robot autonomous.
This post covers Step 3
Build a simple robot
NOTE: If you are familiar with the code elements needed to describe a robot you can skip ahead to the next part
Before we do anything else, let's rename our world. As you recall, the default world tag for an empty world names the world as (wait for it...) 'empty'. This should be updated so that the simulator knows what kind of a world it is dealing with.
First Change: give your world a better name: change ‘empty’ to be ‘pong-world’:
replace <world name='pong-world'> with <world name='pong-world'>
and save the file.
If you want to download the complete code to follow along it is here: Pong-World-2.sdf
Now we are going to add a robot to the world. This will be another model, so it will be bracketed with the usual
<model>
…
</model>
tags.
Note: this is based on the excellent tutorial at:https://gazebosim.org/docs/fortress/building_robot
For our example, we will add in pong-bot, a simple two wheeled, differential drive robot. • The physical robot consists of 4 Links (parts) ◦ Chassis ◦ Two drive wheels ◦ Tail caster • The parts are connected with 3 Joints ◦ Each drive wheel is connected to the chassis with a ‘revolute’ Joint so they can revolve on an axis ◦ The tail caster is connected to the chassis with a ‘ball’ joint so that it can spin in any direction as the robot moves. • Last, we need to apply power to the drive wheels with a plugin ◦ Differential Drive So - all in all we will need to add 8 different components to our model to make our robot work. We will do them one at a time - and after each part is added, we will take a look at it in the simulator to make sure it looks correct. At ReiFx, we strongly recommend this iterative approach. It makes the development go more smoothly and it provides "designer passification" You get to see the gradual eveloution of your system, step by step. Let's get started!
For our example, we will add in pong-bot, a simple two wheeled, differential drive robot. • The physical robot consists of 4 Links (parts) ◦ Chassis ◦ Two drive wheels ◦ Tail caster • The parts are connected with 3 Joints ◦ Each drive wheel is connected to the chassis with a ‘revolute’ Joint so they can revolve on an axis ◦ The tail caster is connected to the chassis with a ‘ball’ joint so that it can spin in any direction as the robot moves. • Last, we need to apply power to the drive wheels with a plugin ◦ Differential Drive So - all in all we will need to add 8 different components to our model to make our robot work. We will do them one at a time - and after each part is added, we will take a look at it in the simulator to make sure it looks correct. At ReiFx, we strongly recommend this iterative approach. It makes the development go more smoothly and it provides "designer passification" You get to see the gradual eveloution of your system, step by step. Let's get started!
Pong_Bot Model
Model definition
Let's begin opening our Pong bot definition file, and saving a new copy as "pong-world-2.sdf" This gives us a backup just in case.
Scroll down to the end of the ground plane model (search for </model>) and let's add a new model tag set:
This places our model at the origin of the world and names it pong_bot. The canonical_link tag tells the simulator that everything defined in the model will be defined relative to the chassis link. So, we better write that next.
Link 1: Chassis Link
This will be the body of our simple robot - a rectangular box. Since it is a link, we will need to define all 5 parts that are required: A name for the component, The collision surface, The inertial properties, The visual properties, and A pose to define where it is in the world.
Add the following to the pong_bot model section:
Okay, at this point you can open gazebo and see your robot chassis: > ign gazebo pong-world-1.sdfAnd you should see this:
Link 2: Left Wheel
Now we will add a wheel to the left side of the robot, the description is below.
Again, we add a link inside our model, this one is named 'left-wheel'
One difference is we define the pose relative to the chassis - that way, when the chassis moves, it will also move the wheel.
Then we describe the properties of the wheel - a simple cylinder, with its own collision, inertial, and visual properties.
We have to specify the X, Y, Z position of the center of the wheel relative to the chassis: -0.5 meters back from the center, 0.6m to the left (-0.5m on the X-axis) side of the chassis center point, and 0.0m above/below the vertical centerline of the chassis.
We also have to specify the orientation of the cylinder - we want it to act like a wheel, so the center axis of the cylinder needs to be horizontal. We do this by specifying that the cylinder (which by default is oriented with the axis vertically) needa to be rotated in the Roll direction (around the X axis) by -1.5707 radians (pi/2 or 90 degrees). That is captured in the pose specification:
<pose relative_to="chassis">-0.5 0.6 0 -1.5707 0 0</pose>
Here is the complete specification of the left_wheel link:
Link 3: Right Wheel
The right wheel link is basically the same as the left, but attached to the opposite side of the chassis.
Note that the Y offset from the chassis center point is positve 0.6m, where the left wheel was offset a negative distance (to the left)
With both wheels in place it should look like this
Link 4: Caster
With the caster we add an additional option: a frame. A frame is generally used to describe the structure of a complex sub-assembly. You define a single reference point and set the poses of each of the parts relative to that point. Then you just have to anchor the frame to the model and it keeps all the components correctly aligned.
Here we don't really have a complex sub assembly, just a ball caster, but it is a good point to introduce the concept.
So, we define the frame to be at pose (0.8 0.0, -0.2 0.0 0.0 0.0) then anchor the sphere to the origin of frame.
That finishes up all four links that make up our pong-bot. Next we connect the pieces with Joints.
Lets talk about Joints
So far, we have described the four links that make up our pong-bot: the chassis, the left and right wheels, and the caster.
We have described where they are relative to each other and how they are oriented. So if you look at the model, it looks just fine.
But, we have not described how they are connected or how they move relative to the chassis.
That is what a Joint does: it tells the simulator that this is how one Link is allowed to move relative to another Link, in this case a wheel relative to the chassis. Hopefully it will rotate on its axis!
Remember: the Joints are part of the robot model, and go inside the model tag set.
Left Wheel Joint
We need to tell the simulator that the left wheel can only rotate about its axis. This is done by specifying a 'revolute' Joint.
Joints always connect two Links together, so we specify the Parent (chassis) and the child (left_wheel).
A revolute joint requires that the axis of rotation be specified. Recall that we rotated the cylinder that defined the wheel 90 degrees around the X axis (Roll), so the axis now runs in the Y direction. so we specify and axis in model space of X=0 Y=1 Z=0
Finally, since this is a wheel that can keep turning forever, we tell the simulator that the limits of rotation are infinite. If we were specifying an elbow on an arm, for example, the limits would be defined to be much smaller.
Right Wheel Joint
Again, the right wheel is a copy of the left wheel.
Caster Joint
The Caster Joint is even simpler - since it is a ball joint the only things that need to be specified are the parent and the child.
Adding a drive system
Okay, it has been a long haul, but we are almost done!
The robot body parts (Links) have been defined, the way they connect together (Joints) have been specified, now we need to make them move.
Actions in the simulation are controlled by plugins. We ran into plugins in Part 2, when we examined the empty world. Those were global plugings that told the simulator how to act.
Now we are going to add a plugin for the drive system: a simple differential drive.
To work, this drive system needs to know a coulple of things:
* What is it connected to (the two wheels)
* What is the geometry (how big are the wheels, how far apart they are).
* How often should the odometry be calculated
* Where should the drive listen for instructions.
This last item probably needs some explanation. In Gazebo (and later on in ROS when we add it) each component can listen to topics for messages that affect it, and they can publish information to topics for others to use. We will go into more detail later, but for now - the drive system needs to be told how fast to spin the wheels, so someone needs to tell it how fast to go. So, it will sit, listening on a topic (in this case the cmd_vel topic) waiting to be told to move.
This plugin is specific to the robot model - and goes inside the model tag set
How do we tell the robot to move, then?
Here we are - we have a robot defined for the simulator, and it is waiting to be told what to do. So how do we tell it?
The Gazebo simulator knows that the robot will be listening on cmd_vel, so we need a way to issue commands;
For now, we do it the easy way - just like an old school computer game we will issue commands via keyboard key presses, specifically the arrow keys (although we could use any keys we want)
Gazebo provides a built in Key Publisher so we need to add some instructions (yep, another plugin) that will read the keypress, and publish a command on the cmd_vel topic to the Differential Drive (which has been patiently waiting for instructions) will issue motion commands to the drive wheels
To get started, we will only handle one command - go forward! In the next part we will add turns, backing up and stopping.
Looking at the plugin we see that it loads the triggered publisher system - this is the connection to the simulator's ability to monitor keypresses
We specify what type of message is expected, in this case "ignition.msgs.Int32" - you guessed it a 32 bit integer
We specify where to look for the message: it should come in on the topic named "/keyboard/keypress", which is where the simulator interface will publish any key presses.
We need to look for a specific integer in this case 16777235 the integer associated with the up arrow key.
Then we need to do something when that key is pressed:
We are going to send an output of type "ignition.msgs.Twist" This is a message format the lets us define motion in any combination of X, Y, or Z in either linear or angular e.g., either along an axis (or combination of axes) or rotaion around and axis or combination. In this case, since these wheels are aligned with the X-axis of the robot chassis, we specify move along the robot's X-axis at 0.5 m/s and cancel any rotation around the Z-axis.
In short: "Hey Pong-Bot: stop any turns and go straight ahead in the direction you are facing."
We publish this Twist message on the cmd_vel topic - where the Differential Drive is (still) patiently waiting for something to do.
This plugin is specific to our Pong-Bot, so it goes into the Model tags, just like the Joints and the Links
Remember: Add the Keypress plugin code to the pong-bot model section and save your file as pong-world-2.sdf
With this final piece in place we can (finally) test our robot.
You will need to:
1) launch gazebo with the pong-world-2.sdf file : at this point everything is loaded
2) Tell Gazebo that you want to send keypresses to the robot:
a) in the upper left, click on the three dots
b) scroll down through the list of available services and select Key Press
c) you should see the Keypress system show up in the left panel
Now we need to actually start the simulator
3) in the lower left of the screen press the start button (just like a video player)
And VOILA! Nothing seems to happen!
But,
* the simulator is now running,
* the keypress handler is waiting for you to press a key,
* the Moving Forward Plugin is listening for a key press to show up so it can send a Twist message,
* the DifferentialDrive is waiting for a Twist message so it can spin the wheels.
So what are you waiting for? move the cursor into the display window and hit the up arrow key!
The robot should begin its journey trundling off into the distance on this infinite plane.
Maybe we should have added a stop button.
What we did
We have:
1) Defined the four parts (Links) that make up out robot, and assembled them into the correct positions
2) Specified the three Joints needed to connect the parts together
3) Added a drive system to our pong-bot so the wheels can be driven
4) Added a connection to the Simulator so that the robot can monitor for a key stroke and activate the Drive System
5) Launched the simulator, activated the Key press system and started the simulation running
6) We then sent our little bot on a one way trip to infinity
Now that we have the full system running (and running, and running) let's add some additional controls to the robot and make the world a little more interesting in Part 4
Now that we have the full system running (and running, and running) let's add some additional controls to the robot and make the world a little more interesting in Part 4
Previous Step | Current Step | Next Step |
Build a Simple Robot |