Jethro's Braindump

Robot Operating System (ROS)

Introduction to ROS

What is ROS? (nil, nil)

  • Left :B_column:
-   Meta-operating system, providing low level services:
    -   process communication over a network
    -   device control
    -   hardware abstraction
-   Distributed framework of processes
  • Right :B_column:

Why use ROS?

  • “Lightweight” framework that speeds up large-scale robotic development
  • Many libraries developed on top of this framework that can be reused:

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](http://wiki.ros.org/rospy), [roscpp](http://wiki.ros.org/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

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.

```bash
rospack find roscpp # /opt/ros/melodic/share/roscpp
```
  • roscd
roscd changes you to the directory of the ros package.

```bash
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:

```bash
rosnode list                    # /rosout
rosnode info /rosout
```

What happens if master is not running?

```bash
rosnode list               # ERROR: Unable to communicate with master!
```
  • Running a ROS node
A ROS package may contain many ROS nodes.

```bash
rosrun turtlesim <TAB>
# draw_square        mimic              turtlesim_node     turtle_teleop_key
```

```bash
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:

<!--listend-->

```bash
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.

<!--listend-->

```bash
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.

<!--listend-->

```bash
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)

<!--listend-->

```bash
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 (nil, nil)

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 (nil, nil)

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

  1. Not sourcing your devel/setup.bash:
source devel/setup.bash
  1. This is necessary to make available all the C++ and python ROS packages that you have built
  2. I recommend using direnv, and sourcing it every time you enter the Catkin workspace.

ROS Installation

  • Ubuntu
Follow the instructions on ROS Wiki. <a id="74cf41bb1b0292dffcecca4fe9f40ef7" href="#nilil_instal_ubunt_ros_wiki">(nil, nil)</a>
  • VM
[Download the VM image](https://drive.google.com/file/d/1SFU4rhWyAd2mlmDAIh0pvXkOvpxO5QuT/view) and load it.

References

Bibliography

nil, (nil). Ros/introduction - ros wiki. Retrieved from http://wiki.ros.org/ROS/Introduction. Online; accessed 15 October 2019.

nil, (nil). Ros/tutorials/writingpublishersubscriber(python) - ros wiki. Retrieved from [http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber %28python %29](http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber %28python %29). Online; accessed 17 October 2019.

nil, (nil). Ros/tutorials/writingserviceclient(python) - ros wiki. Retrieved from [http://wiki.ros.org/ROS/Tutorials/WritingServiceClient %28python %29](http://wiki.ros.org/ROS/Tutorials/WritingServiceClient %28python %29). Online; accessed 17 October 2019.

nil, (nil). Melodic/installation/ubuntu - ros wiki. Retrieved from http://wiki.ros.org/melodic/Installation/Ubuntu. Online; accessed 16 October 2019.


  1. Almost all these commands have tab completion! ↩︎

  2. can also be done programatically ↩︎

Icon by Laymik from The Noun Project. Website built with ♥ with Org-mode, Hugo, and Netlify.