Skip to content
Bharath Kalyan edited this page Jul 3, 2018 · 4 revisions

R2C2 Developer's Guide

These guidelines are a collection of best practices and conventions for ROS. It collates the coding standards and conventions from official ROS documentation and various 3rd party sources listed in the references section.

Coding Guidelines

Coding style is important. A clean, consistent style leads to code that is more readable, debuggable, and maintainable. These are guidelines, not rules. When deviating from the guidelines given here, just be sure to consider your options carefully, and to document your reasoning, in the code. Above all else, be consistent.

  • Python code should follow PEP 8. A quick summary of PEP 8 is:
    • package_name
    • ClassName
    • method_name
    • field_name
    • _private_something
    • self.__really_private_field
    • _global
    • 4 space indentation
  • C++ code should follow Google C++ style guide

Units and Coordinate Conventions

Standard units and coordinate conventions for use in ROS have been formalized in REP-0103.

Naming ROS Resources

Names play an important role in ROS.

Packages

ROS packages should have a flat namespace (Standards on package naming in REP-144).

  • Package names should follow common C variable naming conventions: lower case, start with a letter, use underscore separators, e.g. laser_viewer
  • Package names should be specific enough to identify what the package does. For example, a motion planner is not called planner.
  • Using catchall names such as utils should be avoided as they do not scope what goes into the package or what should be outside the package.
  • To check whether a name is taken, consult http://www.ros.org/browse.
  • Prefixing a package name is recommended only when the package is not meant to be used more widely (e.g., packages that are specific to the PR2 robot use the 'pr2_' prefix). Prefixing a package name with 'ros' is redundant for a ROS package. This is not recommended except for very core packages.

Topics/Services

Topic and service names live in a hierarchical namespace, and client libraries provide mechanisms for remapping them at runtime, so there is more flexibility than with packages. 

  • Topic and service names should follow common C variable naming conventions: lower case, with underscore separators, e.g. laser_scan
  • Topic and service names should be reasonably descriptive. If a planner node publishes a message containing its current state, the associated topic should be called planner_state**, not just** state**.**

Messages

  • Message files are used to determine the class name of the autogenerated code. As such, they must be CamelCased. e.g. LaserScan.msg
  • Message fields should be lowercase with underscore separation. e.g. range_min
  • Use standard data types whenever possible.

Nodes

Nodes have both a type and name. The type is the name of the executable to launch the node. The name is what is passed to other ROS nodes when it starts up. We separate these two concepts because names must be unique, whereas you may have multiple nodes of the same type. For example, if your laser_scan package has a viewer for laser scans, simply call it view (instead of laser_scan_viewer). Thus, when you run it with rosrun, you would type:

rosrun laser_scan view

Node Handles

There are four main types of node handles:

  1. Base node handle: nh_ = ros::NodeHandle();
  2. Private node handle: nh_private_ = ros::NodeHandle(“~”);
  3. Relative node handle: nh_foo_ = ros::NodeHandle(“foo”);
  4. Global node handle: nh_global_ = ros::NodeHandle(“/“); (You probably shouldn’t use this ever.)

By default, resolution is done relative to the node's namespace. For example, the node /wg/node1 has the namespace /wg, so the name node2 will resolve to /wg/node2. Names with no namespace qualifiers whatsoever are base names. Base names are actually a subclass of relative names and have the same resolution rules. Base names are most frequently used to initialize the node name. Names that start with a "/" are global -- they are considered fully resolved. Global names should be avoided as much as possible as they limit code portability. Names that start with a "~" are private.

To explain what these do and how they should be used, let’s assume your ROS node is named node_x, in namespace wg, and you are trying to look up the name topic. Here is what they will resolve to using all 4 node handles:

  1. /wg/topic
  2. /wg/node_x/topic
  3. /wg/node_x/foo/topic
  4. /topic

Node Handles: Usage Guidelines

These are just general guidelines, but when possible, prefer to use the following in each case:

  • Subscribers - usually public node handles.
  • Publishers - usually private node handles for most output/visualization, occasionally necessary to use public for globally-used data (i.e., /odom topic).
  • Parameters - almost always private node handle.
  • Never use global names.

TF Frame_ID

Transforms are specified in the direction such that they will transform the coordinate frame "frame_id" into "child_frame_id" in tf::StampedTransform and geometry_msgs/TransformStamped. This is the same transform which will take data from "child_frame_id" into "frame_id".

  • tf_prefix is designed for operating multiple similar robots in the same environment. 
  • All frame_ids should be resolved when created such that all stored and sent frame_ids are globally unique.
  • All code should reference the frame name and resolve using the tf_prefix into a frame_id, unless the code is specifically calling to full tf_prefix possibly between robots.

Global Executables

Executables that go into the global $PATH may have one of two prefixes:

  • ros (e.g. rostopicroscd)
  • rqt_ (e.g. rqt_console)

The prefix naming enables easy tab completion for finding ROS tools.

Reference: ROS Concepts, ASL Wiki

File/Folder Structure for Packages

Software in ROS is organized in packages. A package might contain ROS nodes, a ROS-independent library, a dataset, configuration files, a third-party piece of software, or anything else that logically constitutes a useful module.  ROS packages tend to follow a common structure. Here are some of the directories and files you may notice.

  • config: Configuration parameters for robots and sensors
  • include/package_name: C++ include headers (make sure to export in the CMakeLists.txt)
  • launch: files usually bring up a set of nodes for the package that provide some aggregate functionality.
  • msg/: Folder containing Message (msg) types
  • src/package_name/: Source files, especially Python source that are exported to other packages.
  • srv/: Folder containing Service (srv) types
  • scripts/: executable scripts
  • CMakeLists.txt: CMake build file (see catkin/CMakeLists.txt)
  • package.xml: Package catkin/package.xml
  • CHANGELOG.rst: Many packages will define a changelog which can be automatically injected into binary packaging and into the wiki page for the package
package_name
|— config
	|— robots
		|— z_boat.yaml
	|— sensors
		|— gemini.yaml
		|— sidescan_slant_range.yaml
|— include/package_name
	|— Class1.hpp
	|— Class2.hpp
|— launch
	|— node1_name.launch
	|— node2_name.launch
|— msg
	|— MyMessage.msg
|— src
	|— Class1.cpp
	|— Class2.cpp
	|— node1_name_node.cpp
	|— node2_name_node.cpp
|— srv
	|— MyService.srv
|— scripts
	|— my_script.py    
|test
	|— Class1Test.cpp
	|— Class2Test.cpp
	|— test_package_name.cpp
|— CMakeLists.txt
|— package.xml
|— CHANGELOG.rst

Packaging

The ROS package and build system relies on package.xml files.

  • Every package must have a package.xml file, located in the package's top directory.

  • The package manifest file must contain:

    • <name>
    • <version> - The version number of the package (required to be 3 dot-separated integers)
    • <description>
    • <author>
    • <license>

Here is the template (with optional tags such as <url>, <depend>) for ropsy nodes:

<package>
<name>foo_core</name>
<version>1.2.4</version>
<description brief="BRIEF DESCRIPTION">
   LONGER DESCRIPTION
</description>
<author>You/[email protected]</author>
<license>BSD 3-Clause License</license>
<url>https://github.com/org-arl/r2c2/YOURPACKAGE</url>
<depend package="rospy"/>
</package>

The package manifest with minimal tags need not specify any dependencies on other packages. Packages can have six types of dependencies and can be specifed using the following tags:

  • <depend> specifies that a dependency is a build, export, and execution dependency. This is the most commonly used dependency tag.
  • <buildtool_depend>
  • <build_depend>
  • <build_export_depend>
  • <exec_depend>
  • <test_depend>
  • <doc_depend>

Metapackages

It is often convenient to group multiple packages as a single logical package. This can be accomplished through metapackages. A metapackage is a normal package with the following export tag in the package.xml:

 <export>
 <metapackage />
 </export>

Command-line Tools

Packages are a very central concept to how files in ROS are organized, so there are quite a few tools in ROS that help you manage them. This includes:

  • rospack: find and retrieve information about packages
  • catkin_create_pkg: create a new package
  • catkin_make: build a workspace of packages
  • rosdep: install system dependencies of a package
  • rqt: In rqt there is a plugin called "Introspection/Package Graph", which visualizes package dependencies as a graph

Reference: Common_Files_and_Directories

Communication

Topics vs Services vs Actionlib vs Parameters vs Dynamic Parameters

  • Use topics for publishing continuous streams of data, e.g. sensor data, continuous detection results.
  • Use services only for short calculations.
  • Use actions for all longer running processes, e.g. grasping, 
navigation, perception.
  • Use parameters for values which are known at launch and are not likely to change during run time.
  • Use dynamic parameters (dynamic_reconfigure) for parameter which are likely to change during run time.

References: ASL Wiki, ROS Patterns-Communication

Dependencies

  • Only depend on what you need.
  • Specify all dependencies.
  • Do not use implicit dependencies.

If multiple runs of catkin_make are required for your workspace to be built, something needs fixing!

Building

  • The core build tool ROS uses is CMake. Starting with ROS groovycatkin is the recommended build system for new packages.
  • Every package that has a build step must have a CMakeLists.txt file in the package's top directory.
  • Never call cmake inside a package folder.

References: Ros Build System, Catkin

Testing

TODO

References: Unit Testing

Roslaunch Guidelines

The focus is on how to structure launch files so they may be reused as much as possible in different situations. 

TODO

References:ROS Launch tips.

Documentation

  • Create a short README.md for each package that includes
    • documentation what the node does,
    • documentation on topics, services and actions that are required and provided,
    • documentation on ROS parameters and their default values.
    • A template for the README.md is provided here.
  • Follow documentation guidelines according to QAProcess, if possible.

Converting between ROS Messages and DSAAV Messages

TODO

References

Official ROS Documentation:

3rd party references: