In this post we will use ROS to simulate a differential drive robot. We will discuss the following
The robot that we will be simulating is called Odisseus. You can find all the files used in this post on Odisseus GitHub repository.
Odisseus package structure
Let’s see how the Odisseus package is structured so that we can navigate in it easily. Overall, the Odisseus package comprises of the following directories:
launch directory only contains a
.launch file which we use to launch the simulation. The
include directory contains header files needed for the simulation. The
maps directory contains various maps that can be used to simulate various environments. The
msg directory contains the type of messages that specifically Odisseus uses. The
naviagtion directory contains files describing various parameters needed for local and global localization. The
urdf directory contains the URDF description for Odisseus. Finally, the
src directory contains functionality used by the
Note that we need a model in order to be able to visualize our robot. You can checkout construct a robot model with urdf I post to see how to represent Odisseus using URDF.
Odisseus simulation is using four nodes. Namely:
The first three nodes are required by the ROS navigation stack in order to function properly. Concretely, the
map_server node provides a static map that is required by the navigation and mission planer nodes in order to be able to localize and devise plans.
amcl node localizes the robot against the static map using an adaptive Monte Carlo localization technique (see Wikipedia article here for Monte Carlo localization).
move_base node handles global planning and local control for the robot.
mission_planner node devises a goal for the robot given a map and odometry data and publishes this goal using
actionlib. Another way to supply goals to the robot is via
rviz, the 3D GUI that ROS comes with.
Now that we have taken some of the required logistics out of the way, let’s discuss in more detail what is happening. Let’s start with the
map_server, we need a static map. We use the maps in the
/maps directory. The map that is used is declared in the
map_server XML node in the
In this post we will use an
empty_world.yaml for simplicity.
Note that we also need to put Odisseus in a simulated 3D world that matches the 2D map we are using.
We use the
amcl package, to localize the robot within the world, using the map.
amcl is extremely configurable and generally does need to be tuned for good performance. In our case, we will use the example configuration for differential-drive robots, which is provided as a launch file in the
The next node we have is
move_base is a complex node, with plenty of room for configuration. Fortunately, its default configuration is pretty close to what we need, leaving just a handful of things for us to change. First, we need to set the parameters that will be common to both the global and local costmaps that are used by
move_base. For the
move_base node to work we need to setup the following files:
You can find these files in the
mission_planner node is responsible for creating goals for the robot and publishing them. Let’s see how both these tasks are done.
Establishing a goal
Sending the goal
Once the goal has been established by the
MissionPlanner it must be published. We do this with the help of actionlib. We wrap the message into a
move_base_msgs::MoveBaseGoal. The message has to specify the following:
- The x,y position of the goal
- The orientation of the goal
- The frame id
- A time stamp
Once these are established, the node simply sends the goal by using the member function
Launching the simulation
We can launch the simulation and observe Odisseus by typing on a terminal
roslaunch odisseus odisseus.launch
This should launch, among others things, Gazebo showing Odisseus in the middle of an empty world. Furthermore, you can open another terminal and bring up
rviz by typing
Once you have the simulation launched you can view the topics that are used by typing, in another terminal,