Theย Robot Operating Systemย (ROS) is not an actual operating system, but a framework and set of tools that provide functionality of an operating system on a heterogeneous computer cluster. Its usefulness is not limited to robots, but the majority of tools provided are focused on working with peripheral hardware.
ROSย is split up in more than 2000 packages, each package providing specialized functionality. The number of tools connected to the framework are probably its biggest power.
Why Should I Use Robot OS?
ROS provides functionality for hardware abstraction, device drivers, communication between processes over multiple machines, tools for testing and visualization, and much more.
The key feature of ROS is the way the software is run and the way it communicates, allowing you to design complex software without knowing how certain hardware works. ROS provides a way to connect a network of processes (nodes) with a central hub. Nodes can be run on multiple devices, and they connect to that hub in various ways.
The main ways of creating the network are providing requestable services, or defining publisher/subscriber connections with other nodes. Both methods communicate via specified message types. Some types are provided by the core packages, but message types can be defined by individual packages.
Developers can assemble a complex system by connecting existing solutions for small problems. The way the system is implemented, it allows us to:
- Replace components with similar interfaces on the fly, removing the need of stopping the system for various changes
- Multiplexing outputs of multiple components into one input for another component, allowing parallel solving of various problems
- Connect components made in various programming languages by just implementing the proper connectors to the messaging system, making it easy to develop software by connecting existing modules from various developers
- Create nodes over a network of devices, without worrying about where code is run and implementing Interprocess communication (IPC) and Remote Procedure Call (RPC) systems
- Directly connect to feeds on demand from remote hardware without writing any extra code, by employing the previous two bullet points
We plan on demonstrating how useful that is by iteratively developing a simple solution. There are several key advantages compared to other approaches. ROS has multi platform support and allows connections between processes over multiple devices via peer-to-peer connections that are handled behind the scene. The design allows support for any language by wrapping the C++ communication classes, or manually developing classes for the language interface.
ROS is made by its own community, meant for its community. After several years, that resulted in a great amount of reusable packages that are simple to integrate, thanks to the architecture of the system.
Alternative approaches likeย http://www.mrpt.org/,ย http://carmen.sourceforge.net/intro.html,ย http://lcm-proj.github.io/,ย http://playerstage.sourceforge.net/,ย https://msdn.microsoft.com/en-us/library/bb648760.aspxand others provide some of those features, but not all. Most of the time, the design downfalls are language support limitations, unoptimized communication between processes, or the lack of support for various devices which is arguably the hardest problem to fix.
What Are We Going to Build?
Since our focus is the framework and not the actual algorithms for particular problems, the given problem will be fairly simple. Our goal is to build software for an onboard computer that allows us to remotely control and monitor a robot, connected to us via Wi-Fi, by using a gamepad on our computer and a feed from the camera mounted on the robot.
First of all, weโll make a simple program connect to a simple simulation, just to demonstrate basic principles of ROS. We will attach a gamepad to a computer and try to design a good control scheme for turning gamepad input into control signals for a robot.
The main languages for writing ROS code are C++ and Python, C++ being preferred due to better performance. We will explain our examples inย Pythonย due to less boilerplate in code and no need for explicit building.
Installation and Configuration
ROS versions are referred to by name. As of this date, the latest release isย Jade Turtle, and the latest LTS versionย Indigo Igloo. Going for the LTS version is preferable, and backwards compatibility isnโt guaranteed in ROS, so all the examples will be written forย Indigo.
ROS is available on various *NIX platforms. The officially supported version is on Ubuntu. The OS X, Arch Linux, Debian, Raspbian and Android versions are supported by the community.
We will go through the installation process for Ubuntu 14.04 on desktop. The processes for all supported versions and platforms are available on the officialย website. Virtual machines with ROS installed are also available.
Installation is platform dependent (and most platforms have packages provided), but workspace configuration is the same for all platforms.
Installation on Ubuntu
ROS provides its own repositories. The first step is adding them.
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt-key adv --keyserver hkp://pool.sks-keyservers.net --recv-key 0xB01FA116
sudo apt-get update
After that youโll have all hosted packages for all ROS versions available for your Ubuntu version. For example, Ubuntu 14.04 supportsย indigo
ย andย jade
.
Installing the base packages on desktop has one of three options:
sudo apt-get install ros-indigo-ros-base
ย for a minimal installationsudo apt-get install ros-indigo-desktop
ย for having the basic additional GUI toolssudo apt-get install ros-indigo-desktop-full
ย for having all official features, including various simulators and libraries for navigation and perception
For the best working experience, the full option is recommended. For installation on devices that will only be used to run nodes, the base version is sufficient. No matter what option you choose, you can install any needed package namedย package_name
ย by running:
sudo apt-get install ros-indigo-<package-name>
Underscores are replaced by hyphens in the final name, soย stage_ros
ย will be in the packageย ros-indigo-stage-ros
.
The next step is to initializeย rosdep
. Packages in ROS can declare what components they depend on.ย rosdep
ย allows you to compile those packages without too much manual dependency handling. To initialize it, call:
sudo rosdep init
rosdep update
ROS has several environment variables used by its tools. With the default installation, the bash script to initialize them is located inย /opt/ros/indigo/setup.bash
. Variables need to be initialized within every bash session, so the best solution is to add them toย ~/.bashrc
.
echo "source /opt/ros/indigo/setup.bash" >> ~/.bashrc
source ~/.bashrc
Some packages install external dependencies viaย rosinstall
, which is available as a package and installed viaย sudo apt-get install python-rosinstall
.
This is the end of the installation on Ubuntu. What follows is a short introduction to installing workspaces.
Configuration
Ever sinceย Groovy Galapagos, ROS workspaces are managed viaย catkin
. We need to define a directory for all packages that we host. Within the directory we create aย src
ย folder, and callย catkin_init_workspace
ย form inside it. That will create various symbolic links to the currently sourced ROS version. The next step is to add this workspace to environment variables as well.
To perform this whole workspace configuration, choose an empty directory and execute the following commands:
mkdir src
cd src
catkin_init_workspace
cd ..
catkin_make
echo "source $(pwd)/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc
You have now created a workspace within which you can create your own ROS packages.
Getting Familiar with the Tools
Creating any code is a big jump. Letโs first get familiar with some of the systems running behind the scene. Our first step will be running the basic GUI and seeing what messages it generates.
To run anything in ROS, a core process needs to be launched. Itโs as easy as opening a new terminal window and typing:
roscore
In your whole connected network of devices,ย roscore
ย needs to be launched only once, on the device that will host the central hub for communication dispatching.
The main role ofย roscore
ย is to tell nodes which other nodes they should connect to, and in which way (whether via a network port or shared memory). The goal is to allow nodes to only care about what data they want to know, rather than what node they want to connect to, while minimizing the time and bandwidth needed to perform all communication.
rqt
After runningย roscore
, we can launch the main GUI tool for ROS:ย rqt
. What we see is very underwhelming – an empty window.ย rqt
ย hosts a wide variety of plugins that can be configured into any visual configuration and any number of predefined views.
For a start, letโs run theย Robot Steeringย plugin, by choosing it inย Plugins > Robot Tools > Robot Steering
. What we get is two sliders, representing the linear and rotational motion we want our robot to have. At the top of the plugin we see a text box withย /cmd_vel
ย in it. We can rename it to anything we want. It represents the name of the topic to which the steering is publishing. The terminal tools are the best place to see whatโs going on in the background.
Terminal Tools
ROS has several powerful tools for inspecting what is happening in the system. The first tool weโll introduce isย rostopic
. It allows us to inspect topics that nodes can subscribe and publish to. Runningย rostopic list
ย will yield:
/cmd_vel
/rosout
/rosout_agg
The latter 2 topics are always running and are related to central ROS systems. Theย /cmd_vel
ย topic is being published by our steering. Renaming the topic in the steering will rename it here as well. Now, we are interested in whatโs going on within the topic. Runningย rostopic echo /cmd_vel
ย will show us nothing (unless you tinkered with the sliders). The process runs until we cancel it. Let us now move the vertical slider to 20 m/s. Looking at the echo, we can see the following repeated over and over again:
linear:
x: 0.2
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.0
How often does it spam this message?ย rostopic hz /cmd_vel
ย says at an average rate of 10 Hz. Well, how many topics like this can I run through my slow Wi-Fi connection?ย rostopic bw /cmd_vel
ย detects an average of 480 B/s.
Now thatโs all well and good, but we talked about message types. This data is good for a human, but an application will need the raw data, and will need to know the message type so it can interpret the data. The type can be determined withย rostopic type /cmd_vel
, telling us itโs aย geometry_msgs/Twist
. All of ROS terminal tools called without any arguments return a standard help message.
The ROS Wiki is good enough to make a web search for this string result in a Wiki explanation to what it contains and how itโs structured. But we donโt have to rely on it.ย rosmsg
ย is the general tool for message types. Runningย rosmsg show geometry_msgs/Twist
ย will return:
geometry_msgs/Vector3 linear
float64 x
float64 y
float64 z
geometry_msgs/Vector3 angular
float64 x
float64 y
float64 z
The message consists of two 3D vectors, representing linear and angular velocity in 3D space.
If we want what topics a node is connected to,ย rosnode info <node-name>
ย will give us detailed data about the node. The toolsย rostopic
,ย rosmsg
ย andย rosnode
ย are the main tools for inspecting raw ROS functionality. ROS has a lot more GUI and terminal tools, but those are out of our scope for this introduction.
The main tools for running ROS nodes areย rusrun
ย andย roslaunch
.ย rosrun
ย can run nodes viaย rosrun <package_name> <node_name>
, andย roslaunch
ย runs nodes based on launch files, which weโll get familiar with to a tiny extent since they are the most complex element of the ROS automation.
We can shut down everything we ran to start working on our first code. For future reference, it will go without saying that running anything ROS related requires an active instance ofย roscore
. A lot of issues you run into can be resolved by closing the terminal window thatย roscore
ย is run within, and opening a new one to relaunch it. This updates all dependencies that needed to be reloaded, both inย bash
ย and inย roscore
.
Creating Gamepad Teleoperation
Our first goal is to imitate the functionality ofย Robot Steering
ย by creating a node that publishesย geometry_msgs/Twist
ย data toย /cmd_vel
ย based on gamepad input. Our first stop is theย joy
ย package.
Theย joy
ย Package
Theย joy
ย package provides generic ROS drivers for joysticks and gamepads. It is not included in the default installation, so it needs to be installed via:
sudo apt-get install ros-indigo-joy
After the installation, we can runย rosrun joy joy_node
. This will connect us to the default joystick or gamepad. Runningย rostopic list
ย shows us that we have a topic calledย /joy
. Listening to it viaย rostopic echo
ย shows us messages of the following format (note that you have to interact with the gamepad or joystick for messages to be published).
header:
seq: 4156
stamp:
secs: 1450707466
nsecs: 204517084
frame_id: ''
axes: [0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0]
buttons: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
You can ignore headers for now. Other than that, we haveย axes
ย andย buttons
, explaining nicely what they represent. Moving axes and pushing buttons on the controller will result in these numbers changing. Using our tools, we can determine that the message type isย sensor_msgs/Joy
ย and the format is:
std_msgs/Header header
uint32 seq
time stamp
string frame_id
float32[] axes
int32[] buttons
Creating Our Teleoperation
The first step to writing code is making a package. Within theย src
ย folder of the workspace, run:
catkin_create_pkg toptal_tutorial rospy joy geometry_msgs sensor_msgs
Here we state the name of the package weโre creating, followed by packages we plan to depend upon. No worries, dependencies can be updated manually later on.
We now have aย toptal_tutorial
ย folder. Within the folder, create aย scripts
ย folder that will house all our Python scripts.
Letโs create a file calledย teleop.py
, and within it weโll set:
#!/usr/bin/env python
import rospy
from sensor_msgs.msg import Joy
def joy_callback(data):
print data
def main():
rospy.init_node('teleop')
rospy.Subscriber('joy', Joy, joy_callback)
while not rospy.is_shutdown():
pass
if __name__ == '__main__':
main()
Weโll also need to setย chmod +x teleop.py
ย so the script becomes runnable. Runningย rosrun joy joy_node
ย in one terminal andย rosrun toptal_tutorial teleop.py
ย in another will result inย teleop.py
โs terminal output being filled with Joy messages.
Letโs examine what the code does.
First, we import rospy, which hosts the library for interacting with the ROS framework. Each package that defines messages has aย msg
ย subpackage with message definitions in it. We are importingย Joy
ย to handle the input. There is no need to import embedded message types (likeย Header
ย fromย std_msgs.msg
ย that is in theย Joy
ย message) unless we want to explicitly mention them.
Our first step is initializing a node with a specific name (in this case, we call it โteleopโ). After that we create a subscriber that subscribes to the โjoyโ topic of typeย sensor_msgs.msg.Joy
, and that handles each message by calling theย joy_callback
ย function. Callbacks receive one parameter, the data from the message. Accessing members of the data is simple. If we wanted to print the state of the first axis, if we recall the message type, we would callย print data.axes[0]
, and it will be a float. The loop at the end loops until ROS is shut down.
Our next step would be to handle our data somehow. We should create a Twist message that changes depending on the input, and then we would publish it to theย cmd_vel
ย topic.
#!/usr/bin/env python
import rospy
from sensor_msgs.msg import Joy
from geometry_msgs.msg import Twist # new
from functools import partial # new
def joy_callback(pub, data): # modified
cmd_vel = Twist() # new
cmd_vel.linear.x = data.axes[1] # new
cmd_vel.angular.z = data.axes[0] # new
pub.publish(cmd_vel) # new
def main():
rospy.init_node('teleop')
pub = rospy.Publisher('cmd_vel', Twist, queue_size=1000) # new
rospy.Subscriber('joy', Joy, partial(joy_callback, pub)) # modified
while not rospy.is_shutdown():
pass
if __name__ == '__main__':
main()
First, we add theย Twist
ย message, and we add support for binding function arguments viaย functools.partial
. We create a publisher,ย pub
, that publishes toย cmd_vel
ย a message of typeย Twist
. We bind that publisher to the callback, and make it publish a Twist message on every input with the speeds being represented by the first two axes. This code does what we expect it to, and we can see the resulting output viaย rostopic echo /cmd_vel
.
We still have one issue. Theย /joy
ย topic can publish at great rates. If we monitor theย rostopic hz /cmd_vel
and move the analog stick in circles, we can see great numbers of messages. Not only does that result in a great amount of communication, but the processes that receive these messages have to process each one of them. There is no need to publish that data so frequently, and we are better off just publishing at a stable rate of 10 Hz. We can accomplish that with the following code.
#!/usr/bin/env python
import rospy
from sensor_msgs.msg import Joy
from geometry_msgs.msg import Twist
from functools import partial
def joy_callback(cmd_vel, data): # modified
cmd_vel.linear.x = data.axes[1]
cmd_vel.angular.z = data.axes[0]
# moved pub.publish(cmd_vel) to main loop
def main():
rospy.init_node('teleop')
cmd_vel = Twist() # new
pub = rospy.Publisher('cmd_vel', Twist, queue_size=1000)
rospy.Subscriber('joy', Joy, partial(joy_callback, cmd_vel)) # modified
rate = rospy.Rate(10) # new
while not rospy.is_shutdown():
pub.publish(cmd_vel) # new
rate.sleep() # new
if __name__ == '__main__':
main()
We modified the callback to receive the mutableย Twist
ย object and modify it within the loop. Theย sleep
function fromย rospy.Rate
ย maintains a stable output frequency.
The final code will result in theย /cmd_vel
ย topic getting velocity commands at 10 Hz, imitating the output of theย Robot Steeringย rqt
ย plugin.
Running a Simulated System
Simulating the World
Our first goal is to create an environment in which we can simulate a scenario we want to achieve. The nodeย stageros
ย within theย stage_ros
ย package allows us to run one robot within a 2D stage defined via an image. There is a whole syntax, described within theย stage_ros
ย packageย for world files and how to generate them. Itโs fairly simple, but outside of our scope. Luckily, the package comes with several demo world. First, letโs go to the filesโ directory by running:
roscd stage_ros
cd world
Within the folder there are several files. Letโs run one.
rosrun stage_ros stageros willow-erratic.world
This created several topics. The meaning of each of them is also documented with the package. The important part is that it hasย cmd_vel
.
Within the displayed stage, there is a blue square, representing the robot you control. By using either our code orย Robot Steering, we can control this robot. Try it out.
Setting up our system via launch files
letโs create aย launch
ย folder within our package, and within it create a file calledย teleop.launch
. The final folder structure should look like this:
toptal_tutorial/
โโโ CMakeLists.txt
โโโ launch
โ โโโ teleop.launch
โโโ package.xml
โโโ scripts
โ โโโ teleop.py
โโโ src
Within theย teleop.launch
ย file we will define a set of nodes and their interconnections.
<launch>
<arg name="world_file" default="$(find stage_ros)/world/willow-four-erratics-multisensor.world" />
<node pkg="stage_ros" type="stageros" name="simulated_world" args="$(arg world_file)"></node>
<group ns="robot_0">
<node pkg="joy" type="joy_node" name="joy_input"></node>
<node pkg="toptal_tutorial" type="teleop.py" name="joy_convert"></node>
</group>
</launch>
The new world consists of four robots, and each of their topics has a prefix ofย robot_<n>
. So, the robot number 0 has a velocity command topic calledย robot_0/cmd_vel
. That is why we put our control within a namespace calledย robot_0
, to adjust their names to the new form. In that sense, you can think of topic names as folders in a filesystem.
To run launchfiles, noย roscore
ย is needed. In a sense,ย roscore
ย is just a special case of a launchfile that does nothing. If aย roscore
ย is missing, only the first launchfile launched will run a core, while the rest will connect to it. Now, we run the launch with:
roslaunch toptal_tutorial teleop.launch
If all is correct, this will result in a simulator with 4 robots, one of which is controlled with our gamepad or joystick. This world has a lot more under the hood than the previous one. Each of the four robots has:
/robot_<n>/base_pose_ground_truth
/robot_<n>/base_scan_0
/robot_<n>/base_scan_1
/robot_<n>/camera_info_0
/robot_<n>/camera_info_1
/robot_<n>/cmd_vel
/robot_<n>/depth_0
/robot_<n>/depth_1
/robot_<n>/image_0
/robot_<n>/image_1
/robot_<n>/odom
We replaceย <n>
ย with 0, 1, 2, or 3. This brings us to our last topic.
Viewing Our Data withย rqt
We didnโt go too deep intoย rqt
, but it is the perfect tool for viewing more complex data. You can experiment with all the topics, but weโll focus on theย image_0
,ย image_1
,ย depth_0
, andย depth_1
ย topics.
Letโs launchย rqt
ย and remove any opened plugins. Now weโll open 4 image visualizers (Plugins > Visualization > Image View
), and place them in a 2×2 grid formation. Finally, in the top left corner of each of the views, letโs choose one of the four stated topics forย robot_0
.
What we get is stereo vision with depth perception, with low resolution cameras. Bear in mind that we could have even gotten this result without our input system. If we just run this (from within theย stage_ros/world
folder):
rosrun stage_ros stageros willow-four-erratics-multisensor.world
and add theย Robot Steeringย plugin with a topic calledย /robot_0/cmd_vel
, we would have gotten the same results with the controls being on-screen sliders.
Applying the Results to a Real System
A lot of hardware has full support for ROS, very often provided by third party volunteers. Many robot platforms have drivers that generate these types of messages, and ROS has nodes that take a webcam and publish an image feed.
While the last result was a simulation of what we want to achieve, the same can be achieved with the following modifications:
- Install ROS on the onboard computer of your robot
- Create a launchfile for the onboard computer that connects ROS to the underlying platform and all high level sensors like cameras, laser range finders and others. The needed nodes can already exist, or can be implemented by creating a publisher/subscriber to ROS on one side, and a driver for serial communications on the other
- Have the launchfile run at startup
- On your remote computer addย
export ROS_MASTER_URI=http://<robot_hostname>:11311/
ย to your bash startup, making the remote computer look forยroscore
ย on that given hostname and port - Launchย
rqt
ย and/or any scripts for monitoring and controlling the robot
What this really comes down to is just exporting the proper environment variable on the remote device, and the rest handles itself. Running ROS on a computer cluster only needs that one step being done for every machine.
Conclusion
We have demonstrated how, with very little coding, you can have a complex system of variables that you can manipulate at your whim. The simple publisher/subscriber system allows you to quickly develop software pipeline that processes data in a cluster of computers, while not worrying about the underlying implementation of certain elements.
While we used a simple simulator, more complex simulators likeย gazebo
ย (also included in the full desktop version) allow you to createย 3D worlds with physics and complex sensors, and can give you an experience of the final results and product long before itโs developed.
This introduction was a very basic one, but the hope is that you became more interested in working with this versatile framework.
Author:ย Adnan Ademovi
Via:ย Toptal
This was sent in to TechBooky via Toptal
Toptal wasย created by engineers.ย We are entrepreneurs, all passionate about working with top tech talent and exciting companies from all over the world. T=Read more about them on theirย website