Welcome!
Hi! Welcome to A Friendly Solution Guide to F1Tenth Labs! We hope this guide will make it easy to get started with ROS (the Robot Operating System) and the F1Tenth Labs.
Who We Are
This page is courtesy of North Carolina State University's Embedded Machine Learning Club! Find out more about us at our homepage and our GitHub.
A Quick Guide to Navigating this Book
To the left, on the summary list, you can find all the chapters, subchapters, and whatnot that this book is comprised of! At the top of the page, next to the sidebar, you will find a hamburger menu, a paintbrush, and a magnifying glass. The hamburger menu allows you to collapse the sidebar, the paint brush lets you change the theme, and the magnifying glass allows you to search all the textin this book!
Things to Know!
This guide will be using ROS Noetic, so we uh highly recommend using it. Though a fair amount of ROS stuff should stay consistent between versions, you may have to do some extra research to get stuff up and running.
With that out of the way, let's move on to setting up our development environment!
Setting Up Your Development Environment
Table of Contents
- Things You'll Need
- Quick Docker Command Summary
- Getting the ROS Docker VNC Image (technically optional)
- Running the Container
- Entering the Container
P.S.: Please use NeoVim, it's awesome and I (Arvin) built on an excellent configuration here. Also, if you use emacs, you're dead to me /j.
A lot of this guide is completed in a Docker container. I know some nerd's gonna say something along the lines of "why would you ever use a Docker container as a development environment," and to them I say, "leave me alone, I'm new to Docker."
Now, if you want to guide to how to use Docker, check out the next subchapter, (after you get Docker set up, of course). It should be noted, however, that the rest of the guide will directly use Docker commands without too much explanation about why exactly we use those commands. Also, if this guide is not sufficient, feel free to google, I'm by no means an expert in Dockering.
Feel free to just skip this entire page if you already has ROS Noetic.
Things You'll Need
Sorry, I didn't warn you this page was structured like a cookbook /j.
- Pineapples
-
A Docker installation (see here)
- Note: If you're using Windows, I highly recommend the WSL version of installation.
- A decent internet connection (the ability to download about 4GB of data.)
Before proceeding, it may be useful to check out the next subchapter.
Quick Docker Command Summary
Note, command options marked in square brackets ([]
) are optional. Additionally,
command options marked with angle brackets (<>
) customizable. For example, command [-a] <name>
means that both command -a hypotenuse
and command pineapple
are valid commands.
Command | Subset of Usage | Meaning | More Info |
---|---|---|---|
ps | docker ps [-a] | List all the containers | Info |
images | docker images | See all docker images | Info |
cp | docker cp <host-directory> <container-name>:<container-directory> | Copy a directory from the host to the container with the matching name | Info |
cp | docker cp <container-name>:<container-directory> <host-directory> | Copy a directory from the container with the matching name to the host | Info |
run | docker run <image-name> [--name <container-name>] | Create and start a docker container. This command has a lot of options, see the info | Info |
start | docker start <container-name> | Start a stopped docker container. | Info |
stop | docker stop <container-name> | Stop a docker container. | Info |
Getting the ROS Docker VNC Image (technically optional)
If you head to our GitHub project embedml/docker-ros-desktop-vnc, you'll find more thorough instructions.
Pulling the Image
Let's start by grabbing our Docker image from hub.docker.com. This can be done with the following command:
$ docker pull arvinskushwaha/ros-noetic-desktop-vnc
If you get some sort of error that looks like this:
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json?all=1": dial unix /var/run/docker.sock: connect: permission denied
Don't worry, it's just a permissions error. You'll have to rerun the command with sudo
as follows:
$ sudo docker pull arvinskushwaha/ros-noetic-desktop-vnc
Same usually goes for any future errors of that form. More information on this can
be found here. If you
must use VSCode to edit within the container, you may have to do some wizardy from
the aforementioned link to get the sudo
prefix to not be required.
Now, when you do this command, you'll see something that looks like the following:
Running the Container
From our Docker image, we can build a container. This is done with the following command (do not run the following command yet):
$ docker run --name <container-name> -d arvinskushwaha/ros-noetic-desktop-vnc
The above command is essentially the base command which we add modifiers to. Also,
where it says <container-name>
, replace it with a name of your choice.
For these options, it is important to remember that they cannot be changed without just deleting and recreating the container. For tons of information on the options, check here.
Here are what the current command-line options mean:
--name <container-name>
: Each container is given a unique ID, and this option lets you customize it. It is important to note that this value must not conflict with other containers. This will be the identifier by which you reference a container.-d
: This tells Docker to launch the container "detached". Without this option your terminal would simply freeze, as the container's main process just sleeps indefinitely.
Other command-line options you should know of:
-
--net=<network>
: This tells Docker to use a different network than the default (bridge). Values are typicallyhost
,bridge
,container
, ornone
. You can also create custom network configurations with Docker, but I am definitely not knowledgeable enough to say anything about that. Essentially, if you set--net=host
, you never have to worry about container and host ports. -
-p <host-port>:<container-port>
: Now, to worry about container and host ports. The VNC/NoVNC servers the container runs use port 5900 and 80 respectively. This means we can control how they are exposed on the host-side with the-p
option, or just use them as-is with the--net=host
option. Additionally, if there are other servers running inside the container, additional-p
options may be added for those. As an example, if I wanted to map the VNC server port to port 3000 onlocalhost
, I would add-p 3000:5900
as an option. -
--device=<host-path>:<container-path>[:modifier]
: This option allows you to pick and chose which devices are passed through to the container. Since each device is a file in linux, we must pass in the device paths. For example,/dev/sda
. If I wanted to map joystick 0 (/dev/js0
) from the host to the container, I would add--device=/dev/js0:/dev/js0
as an option. I don't know must about the modifiers, so I'll just link the reference here. -
--privileged
: This option is kind of dangerous. It, for all intents and purposes, gives full access to your device from the container. This can be useful to passthrough any device needed. See the reference above.
For my default setup, I'm going with the following:
$ docker --name rosenv -p 80:80 -d arvinskushwaha/ros-noetic-desktop-vnc
This allows me to just access the NoVNC server by typing in localhost
in my
browser.
Entering the Container
Now that we've made our container, it would be useful to be able to actually do things in it. There are two main ways of interacting with the container: terminal and desktop.
Desktop Interface
If you would like to use the desktop interface, you must have mapped either the VNC or NoVNC server ports such that it's accessible to the local host.
Then, for VNC, you can download some form of VNC server and connect to
localhost:<mapped-vnc-port>
. Or, in the case of NoVNC, just open
localhost:<mapped-novnc-port>
in the browser.
This is what it looks like:
Terminal Interface
If you would prefer using the terminal, execute the following command:
$ docker exec -it <container-name> bash
This will open a shell prompt that looks something like the following:
root@67c3c27e7401:/root#
Now, you may enter ./login
, a nice simple login script we've written, or just do
cd /home/ubuntu; su ubuntu -
.
root@67c3c27e7401:/root# ./login
This shows yet another prompt:
root@67c3c27e7401:/root# ./login
ubuntu@67c3c27e7401:~$
To exit, execute exit
twice. The first time should exit back to root
and the
second should exit the container.
root@67c3c27e7401:/root# ./login
ubuntu@67c3c27e7401:~$ exit
exit
root@67c3c27e7401:/root# exit
exit
Great! Now that we've got that down, let's talk about our in-container development environment.
In the home directory (/home/ubuntu
), you will find two files and a directory:
-
install_dev_tools
: This file is run when the image was originally built. It contains information about the development tools installed on the container, i.e. NeoVim. This makes it easy for those who choose to primarily use the terminal to edit their files. -
upgrade
: This script essentially just updates Ubuntu packages. It's recommended to execute this every once in a while and as soon as you open this container for the first time. It may take some time to execute. -
ros_ws
: This directory is the main ROS workspace. It is where you will do most of your development.
Editing with VSCode
If you want to use VSCode to do most of your development and don't want to bother with having to manually open the Docker container and whatnot all the time, I highly suggest adding this VSCode extension pack. More information about remote development in VSCode may be found here.
The Magic of Docker
If you're looking at this guide, you're probably relatively familiar with Virtual Machines (VMs). If not, feel free to check out the appendix of this page.
Note: This page is a ROUGH DRAFT and has been written by someone who is not an expert on Docker. If you find any inaccuracies, please file an issue along with a source or citation and we'll fix it ASAP.
Docker is a set of services to perform virtualization. The software you'll become most familiar with is called Docker Engine. Unlike Virtual Machines, which virtualize all aspects of a system down to the hardware, Docker containers build upon the Linux kernel itself. The Linux kernel provides utilities that the Docker Engine takes advantage of to containerize and isolate the applications run in Docker containers.
For operating systems like Windows and MacOS, Docker must still run on a Linux kernel. For MacOS, this is done through a small Linux VM, and for Windows, this is done either with Hyper-V or WSL.
Why are we using Docker?
Unfortunately, ROS (Robot Operating System) is not particularly portable, so to make up for that, we use Docker to sort of bridge between the OS we are currently running on and the OS that ROS is supported on (which for ROS Noetic, is Ubuntu 20.04).
Appendix
Welcome to the Appendix! Here we will be looking at virtual machines (and maybe more stuff) in a bit more detail.
Virtual Machines
Virtual Machines are awesome.
Getting into ROS
Before we get into the F1Tenth labs, let's talk ROS. ROS, the Robot Operating System, is somewhat of a misnomer. It's not really an operating system and more of a set of development tools for robots. It provides some low-level device control and a message-passing framework, amongst other things.
Following this page, you will find many introductions to different ROS concepts.
Nodes and Message Passing
Topics, Subscribers, and Publishers
Introduction to F1Tenth
F1Tenth is a community of individuals enthusiastic about Autonomous Systems. This amazing community has put together many labs and competitions to bring attention to and showcase the science behind autonomous vehicles. The following chapters aim to behave as a sort of walkthrough for each of the F1Tenth labs.
Lab 1
Welcome to Lab 1! Below, you will find an embedded PDF with the Lab 1 guide. Feel free to open it up in another tab.
A small preface: commands to be executed in the terminal will be given as follows:
(<path-to-directory>) $ <command> [<arguments> ...]
If the (<path-to-directory>)
segment is omitted, the command may be executed from anywhere.
Setting up the ROS Workspace
If you are using the docker container, you will find that the ros_ws
directory has already been created
for you. Otherwise, you will have to create it yourself. This can be done with the mkdir
command.
Navigate to your home directory (any directory should work, but we will use the home directory throughout
this guide) and execute:
(~/) $ mkdir ros_ws
Now navigate inside the ros_ws
folder and make a folder named src
. By now, the folder structure
should look like:
ros_ws
`-- src
From here, we can use the catkin_make
command-line utility. Run the following command from inside the
ros_ws
directory:
(~/ros_ws/) $ catkin_make
From here, catkin (our build tool) will set up the rest of the folder structure:
ros_ws
|-- build
| |-- CATKIN_IGNORE
| |-- CMakeCache.txt
| |-- CMakeFiles
| |-- CTestConfiguration.ini
| |-- CTestCustom.cmake
| |-- CTestTestfile.cmake
| |-- Makefile
| |-- atomic_configure
| |-- bin
| |-- catkin
| |-- catkin_generated
| |-- catkin_make.cache
| |-- cmake_install.cmake
| |-- gtest
| `-- test_results
|-- devel
| |-- _setup_util.py
| |-- cmake.lock
| |-- env.sh
| |-- lib
| |-- local_setup.bash
| |-- local_setup.sh
| |-- local_setup.zsh
| |-- setup.bash
| |-- setup.sh
| `-- setup.zsh
`-- src
`-- CMakeLists.txt
Don't let that scare you off though, most of it is completely irrelevant to us!
As you can see, under the devel
folder, a setup.bash
file was created. This
file essentially gives us access to a bunch of ROS utilities and sets some
environment variables that describe the location of our workspace. We will now
"source" that file as follows:
(~/ros_ws/) $ source devel/setup.bash
Assuming you are using the Docker container and the workspace named ros_ws
, the following, less
verbose command:
$ rossource
may be used instead of the source
command preceding it.
Creating our Package
For lab 1, we will put our code in a package titled lab1
(so original, I know). To create
such a package, we use the catkin_create_package
command (a bit verbose, yes?), which has
the following syntax:
catkin_create_package <package-name> [<package-dependencies> ...]
Here are some basic dependencies you should be aware of:
roscpp
: If you prefer to use C++ for your ROS shenanigans,roscpp
is a necessary dependency.rospy
: If you prefer Python to be your weapon of choice,rospy
is essential.std_msgs
: Sometimes you need to communicate with other ROS nodes (like always). If your messages are a primitive type, likeFloat32
orBool
,std_msgs
provides those.sensor_msgs
: If your communication involves sensor data, you will likely usesensor_msgs
. For example, theLaserScan
message.
Of course, you can always add or remove dependencies later, so don't stress too much about it.
To start with, we'll use rospy
, roscpp
, std_msgs
, and sensor_msgs
. Hence, run the following
command:
(~/ros_ws/src/) $ catkin_create_package lab1 roscpp rospy std_msgs sensor_msgs
Note that this command is executed in the ~/ros_ws/src/
directory and not the ~/ros_ws/
directory.
This command generates a folder called lab1
in the src
directory. Let's look at what's inside:
lab1
|-- CMakeLists.txt
|-- include
| `-- lab1
|-- package.xml
`-- src
A bit oddly, catkin_create_package
does not create the scripts
directory, which is typically where
the python code goes, but regardless, the majority of the structure is setup for us!
Investigating our package.xml
Every package has two things in common: package.xml
, and CMakeLists.txt
. Let's look at package.xml
first. package.xml
behaves as a sort of metadata file containing information about who is managing a
package, what dependencies a package has, and the license for the package, amongst other things. It is
important to keep this file up-to-date, as it kind of serves as a living project tracker.
Let's start by setting the description, maintainer, author, and license. You'll find that the dependencies have been set up for us.
Starting description:
<description>The lab1 package</description>
Let's update this description to something a bit more useful, like this:
<description>
A lidar processing node that outputs the closest and furthest data points, along with the range of data points.
</description>
Similarly, we can update the maintainer, author, and license to the following:
<maintainer email="your.email@example.com">Your Name</maintainer>
<license>MIT</license> <!-- MIT is quite nice, but be sure to research licenses! -->
<author email="your.email@example.com">Your Name</author>
Remember to delete or comment out the template XML if you are replacing it!