Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Table of Contents
Introduction .................................................................................................................................... 3
1.
3.
Introduction
This document is an attempt to give an overview of ROS to robotic hobbyist. While we cant
hope to provide you with a full coverage of ROS capabilities, we present some of the most
important key concepts and in the second part, go through a practical usage with the NXT
Robot platform. You are in the meantime invited to visit the official documentation
maintained by the developer of ROS, where you will find number of tutorials, at
http://www.ros.org
1. ROS Basics
What is ROS?
ROS (Robot Operating System) is an open-source, meta-operating system for your robot. It
provides libraries and tools to help software developers create robot application. It also
provide hardware abstraction, device drivers, visualizers, message-passing, package
management, and more. ROS was originally developed in 2007 under the name switchyard
by the Stanford AI Laboratory and was further developed at Willow Garage with
contributions all around the world.
ROS Distributions
ROS releases might not be compatible with other. They are often referred to by code name
rather than version number. The major releases so far are listed in the table below:
ROS Distributions
ROS Groovy
ROS Fuerte
ROS Electric
ROS Diamondback
ROS C Turtle
ROS Box Turtle
Release Date
December, 2012
April, 2012
August, 2011
March, 2011
August, 2010
March, 2010
Wiki Page
http://ros.org/wiki/groovy
http://ros.org/wiki/fuerte
http://ros.org/wiki/electric
http://ros.org/wiki/diamondback
http://ros.org/wiki/cturtle
http://ros.org/wiki/boxturtle
ROS currently runs on Unix-based platform. Software for ROS is primarily tested on Ubuntu
and Mac OS X systems. The ROS community has been contributing support for Fedora, Arch
Linux, and other Linux platforms. A Port to Microsoft Windows is currently under
development and still at an experimental stage.
It is highly recommended to install ROS on Ubuntu. Go to the following link for installation
guide.
http://www.ros.org/wiki/ROS/Installation
Where is it used?
From small differential-drive robots to mobile manipulators to autonomous cars, robots of
every size and shape are using ROS to do interesting research and applications
development. Groups around the world are also releasing free, open-source software to get
you started on your own robot. Find a list of robots using ROS at:
http://www.ros.org/wiki/Robots
The previous pictures depict a situation where we would like to control a simple differential
drive robot using ROS. The robot is connected to a PC running ROS, and is connected via
USB. On the ROS software layout, we have a Node for our Robot, robot-node that is set to
receive velocities on cmd_vel Topic. The robot Node also creates a Topic for all the sensors
available, on which it publishes their respective values. On the other hand we have a control
node which in this case receives distance values from the Ultrasonic sensor on Topic
ultrasonic. Control node then publishes velocities on Topic cmd_vel.
The ROS Master here plays a very essential role in the system. When systems starts every
Nodes needs to present themselves to the Master and specify the topic they will be
subscribing or publishing to. The Master is responsible for setting the communication
between Nodes and making sure that every Node finds each other and exchange messages
through the proper channels.
Assume we want to add a camera to our system, in order to capture image and move the
robot based on what we receive. All we would have to do is create a Node to handle camera
that will publish a message images on Topic image. Then a Node camvision that receives
images on topic image then publish camdata on topic image_data. Lastly our control node
would be modified to subscribe to topic image-data instead and still publish velocities on
cmd_vel. It would at the end look something like this
Next we create a sandbox directory where we will store packages created during the tutorial.
New packages need to be put in a path that is in the variable ROS_PACKAGE_PATH. All
directory managed by rosws are automatically added to the ROS_PACKAGE_PATH.
$ mkdir ~/ros_workspace/sandbox
$ rosws set ~/ros_workspace/sandbox
This will create a sandbox folder in our workspace. Next we need to re-source
~/ros_workspace/setup.bash to make sure that the updated ROS_PACKAGE_PATH is used.
Next we are going to create a package for our next section where we will be writing our first
ROS node in C++.
Create a ROS Package
To do so open a new terminal windows. Move to the sandbox directory.
$ cd ~/ros_workspace/sandbox
where [package_name] will be the name of your package followed by the dependencies of
that package [depend1] [depend2] etc.
For our tutorial we will type in the following. Our package will depend on the roscpp and
std_msgs packages
$ roscreate-pkg ros_intro std_msgs roscpp
Now that we created the package we can start writing some code.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv){
ros::init(argc, argv, "talker");
ros::NodeHandle n;
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
int count = 0;
while (ros::ok()){
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
Code Breakdown
1.
2.
3.
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
ros/ros.h is a convenience include that includes all the headers necessary to use the most
common public pieces of the ROS system.
Std_msgs/String.h is includes the description of the message type that will be used for this
example. You need to include a different header if you have to use a different message type.
stream is responsible for some string manipulation in C++.
6.
7.
ros::init Initializes ROS. It is responsible for collecting ROS specific information from
arguments passed at the comment line. This is also where we specify the name of our node.
Node names must be unique in a running system.
ros::NodeHandle Create a handle to this process' node. The first NodeHandle created will
actually do the initialization of the node, and the last one destructed will clean up any
resources the node was using. It allows you to interact with the node associated with this
process.
1.
2.
int count = 0;
while (ros::ok()){
count here will just be used to keep track of the number of messages transmitted. Its value
will be attached to the string message that is published.
By default roscpp will install a SIGINT handler which provides Ctrl-C handling which will
cause ros::ok() to return false if that happens.
ros::ok() will return false if:
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
These 4 lines perform some strings manipulation too add count to the message the will be
broadcasted.
17.
18.
ROS_INFO("%s", msg.data.c_str());
chatter_pub.publish(msg);
ROS_INFO is the ROS replacement for printf or cout. For more details, see the rosconsole
documentation.
ros::Publisher::publish() sends the message to all the subscribers.
19.
ros::spinOnce();
20.
21.
loop_rate.sleep();
++count;
Calling the ros::spinOnce() in this example is note really necessary, because we are not
subscribing to any topic. The callbacks for receiving messages on those topics are not called
immediately, instead they are placed in a queue which is processed when a call to
ros::spinOnce() is made. But it is good practice to always have it in a program.
ros::Rate::sleep() allows us here to keep a particular publishing frequency
count gets incremented to keep track of messages.
To sum up, here is a condensed version of whats going on with the talker program:
Initialize the ROS system
Advertise that we are going to be publishing std_msgs/String messages on the chatter
topic to the master
Loop while publishing messages to chatter 10 times a second
#include "ros/ros.h"
#include "std_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr& msg){
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv){
ros::init(argc, argv, "listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
ros::spin();
return 0;
}
Code Breakdown
1.
2.
3.
4.
5.
6.
#include "ros/ros.h"
#include "std_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr& msg){
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
This will create two executables, talker and listener, which by default go into the bin
directory. Save and exit.
For more information on using CMake with ROS, check CMakeLists
Now lets build our package.
$ rosmake ros_intro
Talker Node
Listener Node
Replace <username> in the previous command with yours. Next create a udev rules file for
the lego group that you just created.
$ echo "BUS==\"usb\", ATTRS{idVendor}==\"0694\", GROUP=\"lego\", MODE=\"0660\"" > /tmp/70lego.rules && sudo mv /tmp/70-lego.rules /etc/udev/rules.d/70-lego.rules
Once that is done open a terminal window and setup up your computer to accept software
from ROS.org
$ sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu oneiric main" >
/etc/apt/sources.list.d/ros-latest.list'
This is for Ubuntu 11.10. If you are running a different version please refers to this page1 to
get the correct command.
Next set up your Keys
$ wget http://packages.ros.org/ros.key -O - | sudo apt-key add -
Installation of ROS Electric. We will get the Desktop-Full Install which includes ROS packages,
robot-generic libraries, 2D/3D simulators, navigation and 2D/3D perception.
$ sudo apt-get update
$ sudo apt-get install ros-electric-desktop-full
The installation will take quite a while. You need to make sure your internet connection is
always on. Once ROS installation is complete. Set up your environment variables:
$ echo source /opt/ros/electric/setup.bash >> ~/.bashrc
. ~/.bashrc
Now that the installation of ROS is complete, you can install the NXT-ROS bindings:
http://www.ros.org/wiki/Robots/NXT/electric#Installation_Instructions
When installation is complete you should be ready to test it with your robot.
Lets Try It!
IMPORTANT: For NXT-ROS work with your Mindstorms, you need to have the firmware 1.28 or higher. If you do not know
how to upgrade, you can follow the following tutorial: Updating NXT Firmware.
Make sure you have your NXT ready and connected to your PC. We are now going to run a
program to test if the NXT can indeed talk to ROS. For that connect a touch sensor to Port
1 of you NXT. Then in a terminal window, type the following
$ roscore
You should get the following results in your terminal. When press the touch sensor, it will
print True and False when released.
If instead you see the following, then you probably didnt set something right or the Brick
is not connected or powered on.
If you got the touch sensor test right, we can now write a small application to run our NXT
using the robot car sensor1 model provided with the ROS package. If you like you can go
through this tutorial2 to create your own robot for ROS.
1
2
Inside the nxt_robot_car_sensor package, there a file robot.yaml which is a description file
for the robot, where we need to specify the parameters for the sensors, motors, and define
on which port they will be physically connected.
On my PC the file is located at the following location.
/opt/ros/electric/stacks/nxt_robots/nxt_robot_sensor_car
You will have to edit this file in order to match your robot configuration. The initial file
looks like this.
robot.yaml
1. nxt_robot:
2.
- type: motor
3.
name: r_wheel_joint
4.
port: PORT_A
5.
desired_frequency: 20.0
6.
7.
- type: motor
8.
name: l_wheel_joint
9.
port: PORT_B
10.
desired_frequency: 20.0
11.
12.
- type: motor
13.
name: m_wheel_joint
14.
port: PORT_C
15.
desired_frequency: 1.0
16.
17.
- type: gyro
18.
name: gyro
19.
frame_id: gyro_link
20.
port: PORT_3
21.
offset: 0
22.
desired_frequency: 20.0
23.
24.
- type: ultrasonic
25.
frame_id: ultrasonic_link
26.
name: ultrasonic_sensor
27.
port: PORT_2
28.
spread_angle: 0.2
29.
min_range: 0.01
30.
max_range: 2.5
31.
desired_frequency: 5.0
32.
33.
- type: color
34.
frame_id: color_link
35.
name: color_sensor
36.
port: PORT_1
37.
desired_frequency: 10.0
This file defines a robot with a gyroscope, ultrasonic sensor, color sensor, and 3 motors. The
one I will be using only make use of 2 motors (PORT A and B) and an ultrasonic sensor
(PORT 4). I will then have to edit this file so it looks like this.
robot.yaml
1. nxt_robot:
2.
- type: motor
3.
name: r_wheel_joint
4.
port: PORT_A
5.
desired_frequency: 20.0
6.
7.
- type: motor
8.
name: l_wheel_joint
9.
port: PORT_B
10.
desired_frequency: 20.0
11.
12.
- type: ultrasonic
13.
frame_id: ultrasonic_link
14.
name: ultrasonic_sensor
15.
port: PORT_4
16.
spread_angle: 0.2
17.
min_range: 0.01
18.
max_range: 2.5
19.
desired_frequency: 5.0
20.
Feel free to add any sensors you might have to the robot.yaml file. After editing the file we
now have to build the package again from the terminal for our system to consider the
changes.
$ rosmake nxt_robot_sensor_car
Once thats done, you can now run the robot with the following command.
$ roscore
First start the ROS Master with roscore. Always make sure its running before you try to
execute a ROS process. Now execute the following from another terminal
$ roslaunch nxt_robot_sensor_car robot.launch
Keep this program running, then open a new terminal window and run the control program.
$ roslaunch nxt_teleop teleop_keyboard.launch
You should have a prompt on your screen instructing you to use the direction keys to move
the robot. If you will like to see a graph showing all the running nodes and their connections
run the following command
$ rxgraph
You can also use a joystick to control your robot. Instead of the teleop_keyboard.launch, you
will just need to launch the teleop_joy.launch
$ roslaunch nxt_teleop teleop_joy.launch
We will create a new package in the sandbox folder of our workspace (see section 1.5).
$ roscreate-pkg nxt_follow_obj nxt_ros geometry_msgs nxt_msgs
This package will depend on the nxt_ros package, the geometry_msgs, and nxt_msgs.
Next we need to write the code for the node.
The code
Create the src/follow_obj.cpp file within the nxt_follow_obj package and write the code
provided below.
C++ Code follow_obj.cpp
1. #include <ros/ros.h>
2. #include <geometry_msgs/Twist.h>
3. #include <nxt_msgs/Range.h>
4. #include <signal.h>
5. #include <termios.h>
6. #include <stdio.h>
7. #include "boost/thread/mutex.hpp"
8. #include "boost/thread/thread.hpp"
9.
10. class NxtFollow {
11.
public:
12.
NxtFollow();
13.
void controlLoop();
14.
void watchdog();
15.
16.
private:
17.
ros::NodeHandle nh_, ph_;
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
} else if (dist_from_obj_ < 0.1) {
80.
ROS_INFO("Moving away");
81.
ROS_DEBUG("Move away from the Object");
82.
linear_ = -1.0;
83.
angular_ = 0.0;
84.
} else {
85.
ROS_INFO("Not Moving");
86.
ROS_DEBUG("Don't Move");
87.
linear_ = 0.0;
88.
angular_ = 0.0;
89.
}
90.
91.
boost::mutex::scoped_lock lock(publish_mutex_);
92.
last_publish_ = ros::Time::now();
93.
publish(angular_, linear_);
94.
}
95.
96. }
97.
98. void NxtFollow::callback(const nxt_msgs::Range::ConstPtr& Range) {
99.
ROS_INFO("Range: [%f]", Range->range);
100.
dist_from_obj_ = Range->range;
101. }
102.
103. void NxtFollow::publish(double angular, double linear) {
104.
geometry_msgs::Twist vel;
105.
vel.angular.z = a_scale_*angular;
106.
vel.linear.x = l_scale_*linear;
107.
108.
vel_pub_.publish(vel);
109.
110.
return;
111. }
112.
113. void quit(int sig) {
114.
tcsetattr(kfd, TCSANOW, &cooked);
115.
ros::shutdown();
116.
exit(0);
117. }
118.
119. int main(int argc, char** argv) {
120.
ros::init(argc, argv, "nxt_follow");
121.
NxtFollow nxt_follow;
122.
123.
ros::NodeHandle n;
124.
125.
signal(SIGINT, quit);
126.
127.
boost::thread my_thread(boost::bind(&NxtFollow::controlLoop, &nxt_follow));
128.
129.
ros::Timer timer = n.createTimer(ros::Duration(0.1), boost::bind(&NxtFollow::watchdog, &nxt
_follow));
130.
131.
ros::spin();
132.
133.
my_thread.interrupt();
134.
my_thread.join();
135.
136.
return(0);
137. }
Code Breakdown
Now lets go through the code and see whats happening
The header files
1.
2.
3.
4.
5.
6.
7.
8.
#include
#include
#include
#include
#include
#include
#include
#include
<ros/ros.h>
<geometry_msgs/Twist.h>
<nxt_msgs/Range.h>
<signal.h>
<termios.h>
<stdio.h>
"boost/thread/mutex.hpp"
"boost/thread/thread.hpp"
ros/ros.h is a convenience include that includes all the headers necessary to use the most
common public pieces of the ROS system.
geometry_msgs provides messages from common geometric primitives. Twist.h expresses
the velocity in free space broken into its linear and angular parts.
nxt_msgs/Range.h is the message type published by the ultrasonic sensor. We will need this
to extract the range (distance) returned by our ultrasonic sensor.
signal.h required to handle signals during the execution of the program. A signal is a limited
form of inter-process communication used in Unix. It is an asynchronous notification sent to
a process within a process in order to notify it of an event that occurred. They can be viewed
as Interrupts Events.
termios.h functions describe a general terminal interface that is provided to control
asynchronous communications ports.
stdio.h C++ standart I/O Library
mutex.hpp and thread.hpp both from the boost C++ Libraries are used for multithreading.
12.
13.
NxtFollow();
//Class Constructor
void controlLoop();
//ControlLoop this where we will check distance and decide to
//move the robot or not
14.
void watchdog();
15.
16.
private:
17.
ros::NodeHandle nh_, ph_; //Node Handles
18.
19.
double linear_, angular_; //Linear and angular composite of the velocity
20.
double l_scale_, a_scale_; // The amount to scale the keyboard input for the command
//velocity output.
21.
double dist_from_obj_;
//Will hold the distance from the object
22.
23.
ros::Publisher vel_pub_;
//Publisher Will publish velocities
24.
ros::Subscriber range_sub_;
//Subscriber Will subscribe to ultrasonic sensor
25.
ros::Time last_publish_;
26.
27.
boost::mutex publish_mutex_;
28.
29.
void publish(double, double);
//Method will be used to publish velocity
30.
void callback(const nxt_msgs::Range::ConstPtr& Range);
//Callback
31. };
The constructor
The constructor will be call when a new NxtFollow object will be created to initialize the data
members and parameters to default values.
Here we set the linear and angular composite of the velocity to 0
33. NxtFollow::NxtFollow() : linear_(0),
34.
angular_(0),
35.
l_scale_(1.0),
36.
a_scale_(1.0)
37. {
38.
ph_.param("scale_angular", a_scale_, a_scale_);
39.
ph_.param("scale_linear", l_scale_, l_scale_);
40.
41.
vel_pub_ = nh_.advertise<geometry_msgs::Twist>("cmd_vel", 1);
42.
43.
range_sub_ = nh_.subscribe("ultrasonic_sensor", 1, &NxtFollow::callback, this);
44. }
nh_.advertise() will return a ros::Publisher object, which contains a publish() method that will
let us publish messages onto the cmd_vel topic, and when it goes out of scope, it will
automatically unadvertise. This is the function in charge of making the XML/RPC call to the
ROS Master advertising geometry_msgs::Twist on the topic named cmd_vel
nh_.subscribe() makes an XML/RPC call to the ROS Master. It subscribes to the topic
ultrasonic_sensor. The second argument is the queue size, in case we are not able to
process messages fast enough. In this case, if the queue reaches 1 message, we will start
throwing away old messages as new ones arrive. ROS will call the callback() function
whenever a new message arrives. Since we are using a class, we need to pass in a 4th
parameter which is the address of the Node.
The while loop will execute until Ctrl-C is pressed, which will cause the program to stop. This
is where we check for the distance from the object, then decide whether we want the robot
to move towards the object, move away from it or not move at all. We assign the values to
linear_ and angular_ variable then at the end of the loop we make a call to the publish() to
publish to the topic cmd_vel.
The callback() method gets called everytime we receive a message on the subscribed topic.
Then we extract the range and assign it to the variable dist_from_obj that is used in the
control loop to decide on the robots move.
The publish method
103. void NxtFollow::publish(double angular, double linear) {
104.
geometry_msgs::Twist vel;
105.
vel.angular.z = a_scale_*angular;
106.
vel.linear.x = l_scale_*linear;
107.
108.
vel_pub_.publish(vel);
109.
110.
return;
111. }
The publish() gets called in the control loop to publish the velocity to the cmd_vel.
The main
119. int main(int argc, char** argv) {
120.
ros::init(argc, argv, "nxt_follow"); //Initialize ROS
121.
NxtFollow nxt_follow;
//Instanciate an NxtFollow object
122.
123.
ros::NodeHandle n; //NodeHandle
124.
125.
signal(SIGINT, quit); //When Ctrl-C signal is sent from keyboard, quit() is called
126.
127.
boost::thread my_thread(boost::bind(&NxtFollow::controlLoop, &nxt_follow));
128.
129.
ros::Timer timer = n.createTimer(ros::Duration(0.1), boost::bind(&NxtFollow::watchdog, &nxt_fo
llow));
130.
131.
ros::spin();
132.
133.
my_thread.interrupt();
134.
my_thread.join();
135.
136.
return(0);
137. }
In the main we create instance of the NxtFollow class that will subscribe to ultrasonic_sensor
topic and publish cmd_vel topic
This will create the executable for follow_obj, which by default go into the bin directory. Save
and exit.
Now lets build our package.
$ rosmake nxt_follow_obj
You should see the following results. And if you place an object in front of you robot it
should start following it.
In the previous windows, we have our follow_obj running and continuously printing the state
of the robot.
3. Useful Links
Learn C++
http://www.learncpp.com/
http://www.cplusplus.com/doc/tutorial/
ROS Wiki
http://www.ros.org
http://www.ros.org/wiki/roscpp
http://www.ros.org/wiki/roscpp_tutorials/Tutorials/UsingClassMethodsAsCallbacks
http://www.ros.org/wiki/roscpp/Overview/NodeHandles
NXT ROS
http://www.ros.org/wiki/Robots/NXT/
http://www.ros.org/wiki/nxt_robot_sensor_car?distro=electric