Earlier people had to write a large amount of code ranging from low-level driver functions to high-level control algorithms for their robots. I too experienced this pain when I started working on the underwater vehicle project in my undergraduate university. This approach sometimes made changing even one sensor on our system a daunting task. However, things changed when we started using Robot Operating System (ROS) as the framework for our robot’s software stack. The large open sourced community for ROS has made it possible to implement novel algorithms on the robot without worrying too much about the hardware-software integration.
Although the ROS tutorials introduces various core concepts of ROS, it takes a bit of hard work to develop a better comprehension of the entire robot software architecture. Even after going through tutorials, I struggled to write my first ROS node. (Could be I am a slow learner? :P ) Having said that, the post highlights a few interesting ROS concepts and packages that a beginner might find useful in his journey as a robotics developer.
DISCLAIMER: Some of the points have been taken from ROS Answers and ROS Documentation. The blog mainly aims to put all the relevant sources together for a beginner to learn about this amazing framework smoothly.
Nodes, topics, services, and parameters are referred to as graph resources in ROS. Each of these is identified with a unique graph resource name within the ROS computation graph. The naming scheme is hierarchical in nature. In general, there are three different naming systems followed:
/turtle1/pose belong to the namespace
turtle1 with the base names
camera/rgb/img_raw are relative names. To map to the global name, suppose name of default namespace is
/alpha. Conseqently the global names would
Example: For a node with global name
/zonePublisher, if it has a private parameter
~land_site then its global name would become
NOTE: To know more about graph resource names, refer to the book chapter here.
In ROS each node runs as a single process. The nodes communicate with each other using the TCPROS protocol (which uses the standard TCP/IP Sockets). This usually suffices for most of the data transfer that needs to be done between nodes. However, when data is large (such as laser scans or point clouds), it is faster to send a pointer to the data location instead of sending the entire data in form of packets through the TCP protocol. In cases like these, nodelets prove to be useful.
Nodelets allow running multiple algorithms in a thread, with each algorithm running as a thread in the process.
roscpp provides optimizations that allow pointers to be passed between publisher and subscriber calls within a node without the need of copying data from one memory location to another (also called zero copying). The ROS documentation here provides a nice overview on how to write nodelets.
The table below concisely describes how topics, services, and actionlib differ. More information about this is available in the ROSWiki documentation on Communication Patterns.
|Used for continuous data streams (like sensor data, robot state)||Used for remote procedure calls that terminate quickly, mainly query based actions (like performing inverse kinematics calculation)||Used for any discrete behavior that moves a robot or that runs for a long time and feedback is required during execution|
|Continuous data flow is allowed with many-to-many connections feasible||Simple blocking call for processing requests||More complex non-blocking background processing for real-world actions|
Yes, it is possible to do this through the
screerun package in ROS. The node
screenrun parse over the commands written in a YAML file and push them onto a virtual terminal as if you have typed them. However, only those commands that end with
\015 (the octal literal for
Enter) are executed.
This comes in handy when you have to deal with large project repositories. Although running nodes by using launch files is common (and recommended), the
screenrun package provides more flexibility over the general terminals commands that one might need to execute.
A sample config file
config.yaml is as follows:
To run the node, you could either use
rosrun screenrun screenrun [b] or launch it through a launch file:
NOTE: The argument
b is optional. If
b is passed, byobu is used instead of screen.
ros::spinOnce() is important when you start writing your nodes. Quoting Patrick’s
answer for significance of
In the background, ROS monitors socket connections for any topics you’ve subscribed to. When a message arrives, ROS pushes the subscriber callback onto a queue. It does not call it immediately. ROS only processes the callbacks when you tell it to with
ros::spinOnce(). This is all part of roscpp’s ” toolbox, not framework” philosophy. roscpp does not mandate a particular threading model for your node, nor does it demand to wrap your
ros::spin()is purely a convenience, a main loop for ROS that repeatedly calls
ros::spinOnce()until your node is shut down.
If we dig a bit deeper through documentation on callbacks and spinning, the answer by Patrick is verified through the code snippets given below:
In above procedures, the call to
ros::getGlobalCallbackQueue() gets the global queue in which all callbacks are assigned to by default. The
callAvailable() method pops everything present in the queue and invokes all of them. It has an optional timeout argument given above using
ros::WallDuration(..). If there are no callbacks in the queue and the timeout is set to 0, then the method returns immediately.
ros::spinOnce() is used when the program has to perform certain actions other than responding to callbacks. These include when the rate at which a particular action is performed needs to be controlled. For instance, publishing data onto a topic at a particular frequency. Omitting either
ros::spinOnce() would make the code behave undesirably. Deleting
ros::spin() from a subscriber node would close the execution after a while, while removal of
ros::spinOnce() would make it appear as if no messages are being received. Thus, utmost care must be taken while writing your ROS node.
Running ROS commands through the terminal isn’t really a bad practice. However, if you are like me, then you’d prefer GUIs more any particular day. ROS actually provides its own Qt-based GUI tool called rqt. In the rqt_gui, various plugins can be imported to do a variety of things. The ones available include publishing to a topic, visualizing on rviz, robot monitor and many other given here. In fact, if required you could design your own rqt plugin by following the tutorial here.
Well, the one which I grew a fancy for is particularly the
node_manager_fkie. The interface makes it easier to launch bodes and monitor their health and view the topics, services, and parameters being published. Thus, saving the time to write terminal commands every time.
This can’t be emphasized enough but claiming to know ROS by just having done the tutorials is equivalent to saying that one has learned how to code after just seeing the syntax of a programming language. Learning can be faster if you have an application in mind. If you don’t already, consider the following challenges:
NOTE: The above list is open to additions. If you would like to add more to it, feel free to comment below on this post.
Sinking in all the above information might take a while but once you have understood them, I hope using ROS for your robot becomes easier.
To be honest, this framework has much more to it than just the above-mentioned points. There are a few more interesting concepts like message filters, setting up diagnostics for your robot which I would strongly recommend looking into when you get the time.
If you enjoyed this post, it would be great if you would share it with your robotics-loving friends. Thank you!