ROS v1: Robot Operating System
What is ROS?
ROS is an open-source framework built for Robotics applications. The goal is to provide standards for robotic software that developers can use and reuse for any robot. The more you will learn about ROS the easier it will be for you to program and understand the code for robots. It also provides many reusable modules to write applications for the robots.
When to use ROS?
Robot programs using Arduino board or custom code with just a sensor and servo motor are very simple to begin with. Slowly more sensors, actuators controllers are added to robots which makes the program complex and a headache to write code to program robot.
ROS helps to develop powerful and scalable robotics applications. It easily manages a lot of communications between subprograms.
ROS big picture
- It allows to separation of code into reusable blocks.
- Provides a set of tools to easily communicate between sub-programs. This helps to keep separate code and communication tools.
- It is language agnostic to allow one part to write in Python and the second part in C++ and both can communicate with each other using ROS communication protocol.
Let’s say to write an application for the robotics arm you can design your code using ROS as below.
- Create a sub-program called Node for your camera.
- Another sub-program for motion planning.
- Another is for hardware drivers.
- Another for joystick etc
- Use ROS to communicate between these nodes.
- ROS provides plug and play libraries to design your app. For example, a library to compute inverse kinematics or plan a trajectory for an arm, etc.
ROS1 vs ROS2
The difference between ROS1 and ROS2 can be categories as below.
Architecture
Changes to the Middleware
- ROS 1 uses the ROS Master-Slave Architecture and the XML-RPC middleware. ROS 2 uses Data Distribution Service (DDS), which is designed to provide higher efficiency and reliability, low latency, and scalability, as well as configurable quality of service (QoS) parameters.
- XML-RPC is better for simple remote procedure calls, while DDS’s added complexity allows it to better support real-time systems.
- Because of its distributed nature, DDS also helps remove communications single points of failure from ROS 2 systems.
Changes to the ROS API
- ROS 1 has two separate libraries — roscpp for C++ and rospy for Python — that are not at feature parity with each other.
- In contrast, ROS 2 has a base library written in C — rcl (ROS client library) — with libraries built on top of it.
- This ensures that core functionality is available in different APIs sooner.
- This is one of the key reasons that ROS 2 is able to offer more language support than just Python and C++, such as Java and C#.
Changes to the Data Format
- ROS2 rosbags allow for more flexibility with regard to serialization as compared to ROS 1, which uses its own serialization format.
- The biggest impact of changes to the rosbag format from ROS 1 to ROS 2 is potential incompatibility of ROS 1 bag tooling with ROS 2 rosbags and subsequent impacts to developer workflows.
Features
QoS
- ROS 2 allows for data flow configuration, affecting how data is sent and received.
- This includes settings for message reliability, deadline, and priority, which can ensure that critical messages are delivered on time.
Multi-Threaded Execution
- ROS 2 supports multiple nodes truly running in parallel, allowing it to leverage modern multi-core processors far better than ROS 1.
Real-Time Processing
- The summation of the features above, along with the use of DDS, allows ROS 2 to be superior at real-time processing, especially when deterministic, low-latency communication is needed.
Tooling
- Catkin is gone, replaced with Ament as a build system
- Overlays allow you to have a secondary workspace that doesn’t affect your primary workspace — this is helpful when you want to experiment with new packages without affecting your base configuration (called an “underlay”).
Ecosystem
- ROS 2 is not backward compatible with ROS 1.
- ROS 1 was primarily built for Ubuntu.ROS 2 runs on MacOS, Windows, Ubuntu, and other operating systems.
ROS Nodes
- ROS Node is an executable program running inside a robot.
- An application can have many nodes which will be put into a package.
- The nodes will then communicate with each other.
- It reduces code complexity.
- Provides fault tolerance on crash of specific node.
- Node can be written in any language. Either python or C++.
- Two nodes can not have same name.
Hello world ROS Node
import rospy
if __name__ == '__main__':
rospy.init_node('my_first_ros_node')
rospy.loginfo('This node has been started')
rate = rospy.Rate(10)
while not rospy.is_shutdown():
rospy.loginfo('Hello')
rate.sleep()
- To start
master
node runroscore
- Start worker node
python my_first_ros_node.py
- To get the list of running nodes
rosnode list
rosout
is always running a node for logging and some other functionality.- If you try to start the same node again then the previously running node will be shut down as at a time we can only have one node with the same name.
ROS Commands
- To run nodes either written in Python or C++ we can use
rosrun
command to make it language agnostic. rosrun <package_name
returns the list of either Python or C++ files for the node.rosrun <package_name> <node_file_name>
runs the node.rosnode info <node_name>
to get more details about the node.rosnode kill <node_name>
to stop the running node.rosnode ping <node_name>
to check the health of the running node.
Visualize ROS nodes with rqt
Run rosrun rqt_graph rqt_graph
Learn ROS with turtlesim package
- To install
sudo apt-get install ros-kinetics-turtlesim
- To see a list of nodes with
turtlesim
runrosrun turtlesim
- Run it in the main window
rosrun turtlesim turtlesim_node
4. To add key control with turtle rosrun turtlesim turtle_teleop_key
. Now you can use arrow key to move turtle.
Here we have two nodes, one for the turtle and a second to capture the keyboard control to move the turtle.
ROS — Topics for communication
Following is one example to showcase the publisher, topic, and subscribers concept.
- A topic is the named bus over which nodes exchange messages. Messages are sent over TCP/IP. Nodes are already provided with abstraction to read these messages therefore you don’t have to deal with the lower layer of TCP/IP.
- These are unidirectional data streams. Some nodes can publish to the topic and some nodes can subscribe to the topic. There is no response from the subscriber to the publisher. Data is only going one way.
- Publishers and subscribers are anonymous to each other.
- The topic has a message type. All subscribers and publishers must use a message type associated with the topic.
- ROS Topic can be written in Python or C++.
- Ros master helps nodes find needed topics. Ros master acts as a DNA server for nodes to find out where to communicate.
- A node can have many publishers/subscribers for many different topics.
Publisher example in Python
import rospy
from std_msgs.msg import String
if __name__ == "__main__":
rospy.init_node("robot_news_radio_transmitter")
pub = rospy.Publisher("/robot/news/radio", String, queue_size=10)
rate = rospy.Rate(2)
while not rospy.is_shutdown():
msg = String()
msg.data = "Hi, this radio news 152.5"
pub.publish(msg)
rate.sleep()
rospy.loginfo("Node is stopped")
To view the ros topics list, run rostopic list
. To listen to the topic you can run rostopic echo /robot/news/radio
to print the incoming messages.
Subscriber example in Python
import rospy
from std_msgs.msg import String
def callback_receive_radio_data(msg):
rospy.loginfo("Message received")
rospy.loginfo(msg)
if __name__ == "__main__":
rospy.init_node("smartphone")
sub = rospy.Subscriber("/robot/news/radio", String, callback_receive_radio_data)
rospy.spin()
ROS Services
- ROS Service is a client/server system.
- It is synchronous
- One message type is for requests and the second type is for responses.
- A service server can exist once but can have multiple clients.
Python Service Server
import rospy
from rospy_tutorials.srv import AddTwoInts
def handle_add_two_ints(req):
result = req.a + req.b
rospy.loginfo("Sum of a= "+str(req.a)+" b="+str(req.b)+" is " + str(result))
return result
if __name__ == "__main__":
rospy.init_node("add_two_ints_server")
rospy.loginfo("Add two ints server is created")
service = rospy.Service("/add_two_ints",AddTwoInts, handle_add_two_ints)
service.spin()
We can manually test the service using the command line rosservice call /add_two_ints “a:4 b:5”
.
Python Service Client
import rospy
from rospy_tutorials.srv import AddTwoInts
if __name__ == "__main__":
rospy.init_node("add_two_ints_client")
rospy.wait_for_service("/add_two_ints")
try:
add_two_ints = rospy.ServiceProxy("/add_two_ints", AddTwoInts)
response = add_two_ints(2,6)
rospy.loginfo("Sum is " + str(response))
except rospy.ServiceException as e:
rospy.logwarn("Service failed "+str(e))
ROS Message and Service Definition
Two things define a Ros topic
- Name, for example
/number_count
- Message definition, for example
std_msgs/int64
Similarly, service is defined by two things.
- Name, for example
/reset_counter
- Service definition, for example
std_srvs/SetBool
. Definition for request and response.
Message definition is defined once and the build system generates code for Python or C++.
- Use primitive types to create message definitions.
- You can create a message definition using another message definition.
- There are few existing message definitions for example
std_msgs
,sensor_msgs
,geometry_msg
etc. - Similarly few existing service definitions for example
std_srvs
etc. - To learn more about message types, please refer to https://wiki.ros.org/msg
Custom message type
#File name HardwareStats.msg
int64 temperature
bool are_motor_up
string debug_message
Custom service type
#File name ComputeDiskArea.srv
# Request
float64 radious
---
# Response
float64 area
ROS Parameters and Launch files
You would like to use global config to control robot options for example robot name, sensor read frequency, simulation mode, etc.
Ros Parameter Server
It is automatically created inside the ros master server. It is a dictionary and accessible globally from any node.
ROS Launch file
- Launch file allows to creation of all nodes with parameters from a single file.
- You can also merge multiple launch files to create a single launch file.
Following is a sample launch file for reference.
<launch>
<arg name="port" default="$(optenv HUSKY_PORT /dev/prolific)" />
<node pkg="clearpath_base" type="kinematic_node" name="husky_kinematic" ns="husky">
<param name="port" value="$(arg port)" />
<rosparam>
cmd_fill: True
data:
system_status: 10
safety_status: 10
encoders: 10
differential_speed: 10
differential_output: 10
power_status: 1
</rosparam>
</node>
<!-- Publish diagnostics information from low-level MCU outputs -->
<node pkg="husky_base" name="husky_base_diagnostics" type="diagnostics_publisher" />
<!-- Publish wheel odometry from MCU encoder data -->
<node pkg="husky_base" name="husky_basic_odom" type="basic_odom_publisher" />
<!-- Diagnostic Aggregator -->
<node pkg="diagnostic_aggregator" type="aggregator_node" name="diagnostic_aggregator">
<rosparam command="load" file="$(find husky_base)/config/diagnostics.yaml"/>
</node>
</launch>
Happy Robotics learning :-)
This blog is based on the course https://www.udemy.com/course/ros-for-beginners. The instructor of this course is great, feel free to buy this course to learn more about ROS v1.