Robot Operating System (ROS)
Introduction to ROS
What is ROS?
Left
- Meta-operating system, providing low level services:
- process communication over a network
- device control
- hardware abstraction
- Distributed framework of processes
Right
Why use ROS?
- “Lightweight” framework that speeds up large-scale robotic development
- Many libraries developed on top of this framework that can be
reused:
- Physics simulation (Gazebo)
- Movement + Navigation (ROS navigation)
ROS Concepts
Computational Graph
- All computation is organized as a peer-to-peer network of communicating processes.
Nodes
- Processes that perform any form of computation.
- Nodes can communicate with one another.
- Example of nodes:
- Publish sensor readings
- Receiving teleop commands and running them
- Written with ROS client libraries (rospy, roscpp)
Master (Primary) Node
- Provides name registration, node lookup to all nodes in the computational graph.
- Enables communication between nodes.
Parameter Server
- “Distributed” key-value store: all nodes can access data stored in these keys.
Topics
- Nodes communicating via the publish-subscribe semantics do so by publishing and subscribing to topics.
- Every topic has a name, e.g.
/sensors/temp1
- No access permissions
Services
- Request-response semantics (think Web servers)
- Requests are blocking
Example Computational Graph
graph G {
master[label="Master Node (RPI)", style=filled, fillcolor=yellow]
mega[label="Servo Microcontroller (Arduino Mega)", style=filled, fillcolor=yellow]
lws[label="Lewansoul MC"]
ws[label="ROS Websocket Server (RPI)", style=filled, fillcolor=yellow]
phone[label="Phone Joystick"]
master -- mega
master -- ws
ws -- mega
mega -- lws
phone -- ws[style=dotted];
}
Getting Started With ROS
ROS Environment Setup
Here I assume you have the ROS environment set up. If not, see the appendix.
Creating a ROS Workspace
Catkin is ROS’ package manager, built on top of CMake.
mkdir -p ~/catkin_ws/src # Create the directories
cd ~/catkin_ws/ # Change to the directory
catkin_make # Initial setup
Exploring ROS shell commands 1
rospack
rospack find
locates ROS packages.
rospack find roscpp # /opt/ros/melodic/share/roscpp
roscd
roscd changes you to the directory of the ros package.
roscd roscpp
pwd # /opt/ros/melodic/share/roscpp
Creating a ROS package
We use the convenience script catkin_create_pkg
to instantiate our package.
cd ~/catkin_ws/src
catkin_create_pkg workshop std_msgs rospy roscpp
# Created file workshop/CMakeLists.txt
# Created file workshop/package.xml
# Created folder workshop/include/workshop
# Created folder workshop/src
# Successfully created files in /home/jethro/catkin_ws/src/workshop. Please adjust the values in package.xml.
What’s in a ROS package?
workshop
CMakeLists.txt # Build instructions
include # For cpp deps, if any
workshop
package.xml # Details about the package
src # Contains source code
Starting ROS
We initialize the ROS master node with roscore
.
roscore
# ...
# process[master]: started with pid [16206]
# ROS_MASTER_URI=http://jethro:11311/
# setting /run_id to 05bf8c5e-efed-11e9-957b-382c4a4f3d31
# process[rosout-1]: started with pid [16217]
To kill it, press Ctrl-C
in the same terminal.
ROS Nodes
rosnode
rosnode let’s us inspect available nodes:
rosnode list # /rosout
rosnode info /rosout
What happens if master is not running?
rosnode list # ERROR: Unable to communicate with master!
Running a ROS node
A ROS package may contain many ROS nodes.
rosrun turtlesim <TAB>
# draw_square mimic turtlesim_node turtle_teleop_key
rosrun turtlesim turtlesim_node
# [ INFO] [1571214245.786246078]: Starting turtlesim with node name /turtlesim
# [ INFO] [1571214245.790986159]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]
Exercise: reinspect the node list.
ROS Topics
Now we have a visual simulation of a turtle. How do we make it move?
rosrun turtesim turtle_teleop_key
What’s going on?
turtle_teleop_key
advertises on a ROS topic, and publishes each keystroke:
rostopic list
rostopic echo /turtle1/cmd_vel
ROS Messages
- ROS messages are pre-defined formats. They are binarized and compressed before they are sent over the wire.
rostopic type /turtle1/cmd_vel # geometry_msgs/Twist
Monitoring the Topic
- The rate at which messages is published is good to monitor (in Hz).
- A topic that has too many messages can get congested, and buffer/drop many messages, or congest the ROS network.
rostopic hz /turtle1/cmd_vel
# subscribed to [/turtle1/cmd_vel]
# average rate: 13.933
# min: 0.072s max: 0.072s std dev: 0.00000s window: 2
Rosbag
- A bag is subscribes to one or more topics, and stores serialized data that is received (for logging/replay)
rosbag record /turtle1/cmd_vel
# [ INFO] [1571294982.145679913]: Subscribing to /turtle1/cmd_vel
# [ INFO] [1571294982.168808833]: Recording to 2019-10-17-14-49-42.bag
ROS Services
- Services allow request-response interactions between nodes.
rosservice list
rosservice call /clear
rosservice type /spawn | rossrv show
ROS Params
the rosparams
commandline interface allows us to store and manipulate
data on the ROS Parameter server. 2
rosparam set # set parameter
rosparam get # get parameter
rosparam load # load parameters from file
rosparam dump # dump parameters to file
rosparam delete # delete parameter
rosparam list # list parameter names
Pubsub
When do we use topics?
Previously we looked at ready-made ROS packages and how they used topics and services. Now, we’ll write our own publisher and subscriber.
The pubsub interface is useful in situations where a response for each request is not required:
- Sensor readings
- Log info
A Simple Publisher
We use rospy
, but roscpp
is fine as well. We create a new file in our
workshop package workshop/src/talker.py
:
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
pub = rospy.Publisher('my_topic', String, queue_size=10) # initializes topic
rospy.init_node('talker', anonymous=True) # required to talk to Master
while not rospy.is_shutdown():
pub.publish("Hello")
Executing the Publisher Node
We need to make our Python file executable:
chmod +x talker.py
rosrun workshop talker.py
Exercise: monitor the output. What’s wrong? (hint: Hz)
Setting the rate of publishing
We use the Rate
object, and the rate.sleep()
to set the rate of
publishing:
rate = rospy.Rate(10) # 10 hz
# ...
rate.sleep()
# ...
Good Practice
We often wrap all our logic in a function, and catch the
ROSInterruptException
exception:
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
def talker():
pub = rospy.Publisher('my_topic', String, queue_size=10) # initializes topic
# ...
try:
talker()
except rospy.ROSInterruptException:
pass
Exercise: Write a time publisher (5 minutes)
Goal: publish the current date-time onto a topic /datetime.
Hint: Python has a datetime
library.
Subscriber
We create a listener in workshop/src/listener.py
#!/usr/bin/env python
import rospy
from std_msg.msg import String
def echo(data):
print(data.data)
def listener():
rospy.init_node("listener", anonymous=True)
rospy.Subscriber("my_topic", String, echo)
rospy.spin() # prevents python from exiting
listener()
Summary
rospy.init_node(name) # create node
rospy.Publisher(topic_name, msg_type) # create publisher
rospy.Subscriber(topic_name, msg_type, callback) # create subscriber
rospy.Rate(10) # rate object
rospy.spin() # spin
Services
Msg and Srv
- msg
- message files that define the format of a ROS message. These generate source code for different languages (think Apache Thrift, Protobuf).
- srv
- describes a service (request/response)
Creating a msg
mkdir -p workshop/msg
Create a file workshop/msg/Num.msg:
int64 num
Compiling the msg
In package.xml
:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
In CMakeLists.txt:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
...)
add_message_files(
FILES
Num.msg
)
generate_messages()
Compile the message:
cd ~/catkin_ws
catkin_make
catkin_make install
# ...
# [100%] Built target workshop_generate_messages_cpp
# [100%] Built target workshop_generate_messages_py
# [100%] Built target workshop_generate_messages_eus
# Scanning dependencies of target workshop_generate_messages
# [100%] Built target workshop_generate_messages
Using the ROS msg
rosmsg list # ... workshop/Num
rosmsg show workshop/Num # int64 num
Creating a ROS srv
mkdir -p workshop/srv
In workshop/srv/SumInts.srv
:
int64 a
int64 b
---
int64 sum
Compiling the ROS srv
Since srv files are also compiled, the setup is similar to compiling msgs.
Writing a Service Node
We can create a server that uses the service file we defined earlier:
#!/usr/bin/env python
from workshop.srv import SumInts, SumIntsResponse
import rospy
def handler(req):
return SumIntsResponse(req.a + req.b)
def sumints_server():
rospy.init_node("sumints_server")
s = rospy.Service("sumints", SumInts, handler)
rospy.spin()
sumints_server()
Writing a Client
#!/usr/bin/env python
import sys
import rospy
from workshop.srv import SumInts
def sumints_client(x, y):
rospy.wait_for_service('sumints')
try:
sumints = rospy.ServiceProxy('sumints', SumInts)
resp1 = sumints(x, y)
return resp1.sum
except rospy.ServiceException, e:
print "Service call failed: %s"%e
x = int(sys.argv[1])
y = int(sys.argv[2])
print "%s + %s = %s"%(x, y, sumints_client(x, y))
rosrun workshop sumint_client.py 1 2
# 1 + 2 = 3
Exercise: Time Service (15 minutes)
Write a service that:
- requests nothing
- responds with the current time
Write a client that sends the request and prints this response.
What’s Next?
What’s Next?
- Run a simulator, model the robot using URDF
- Look at community ROS packages
- tf2: maintain robotic coordinate frames (pose estimation)
- gmapping/slam etc.: navigation
- Look at ROS 2
Appendix
Common Pitfalls
- Not sourcing your
devel/setup.bash
:
source devel/setup.bash
- This is necessary to make available all the C++ and python ROS packages that you have built
- I recommend using direnv, and sourcing it every time you enter the Catkin workspace.
ROS Installation
Ubuntu
Follow the instructions on ROS Wiki.
VM
Download the VM image and load it.