ROS Best Practices and Patterns

ROS Best Practices and Patterns

  • These are best practices not ironclad rules
  • Rules of thumb are good when you are starting
  • When you know more you will know when to break them :)

Python

  • We use ROS1 Noetic which requires Python 3.x
  • You can choose to use classes or just top level functions
  • If you find yourself using global often, it’s a hint that you should be using classes
  • Other code smells to watch out for are: Functions of more than 20 or so lines, and classes of more than 100 or so lines,

ROS Nodes, Topics, Messages

  • There’s rarely a need to design a custom message (.msg)
  • If you need one it’s ok but you should try and avoid it if possible.
  • As a general rule organize your functionality into multiple separate ROS nodes (python modules)
  • As a general rule you can get nicer ROS code if you break the functionality into python classes and in separate files (modules)
  • This works especially well if you can identify bits functionality that may be reused in other applications
  • Don’t assume that /scan topic’s LaserScan message has 360 items in the ranges array.
    • Use len(msg.ranges) to figure out how many items there are.
  • Make sure you know how to work with Quaternions. The x,y,z fields in a Quaternion have nothing to do with x, y, z in Euler angles.
    • See this for a nice tool to help you visualize them.

Concurrency, threading, race conditions, etc

NBROS is heavily concurrent which affects how you structure your code. Your logic may look right but cause misbehavior because of concurrency.
  • Each ROS node (i.e. something you run with rosrun or roslaunch) is a single linux Process. (Think of it like any other python program.)
  • Each distinct topic subscription within a node forms a single Thread for the callback.
  • Like any other thread you need to be aware of the Python GIL (Global Interpreter Lock).

Callbacks and race conditions

  • Do not use sleep() inside a callback. This is for many reasons, including race conditions.
  • Be careful using globals for communicating between your callback and your main loop. It may lead to a race condition.
  • Your main program should do a rospy.spin() or rospy.is_shutdown() to make sure that you can ^c out of it.

Package Structure

  • Create properly structured packages so they can easily be added to other workspaces.
  • Use the create_package utility to create a correct package
  • This will create a package.xml and CMakeFile.txt
  • You rarely (but not never) need to edit either one of them. Eventually you will have to learn them.
  • Name the directory with the programs src and the directory with launch files launch
  • Other directories should be present only if you use them
  • It’s a good idea to include a readme.md and a license.md

Writing and rsunning a single ros node

  • Put the code in a file called *.py
  • The first line should be a “shebang”: #!/usr/bin/env python
  • Set it to be excecutable (chmod +x filename.py)
  • Put it in a package
  • rosrun package-name filename.py

Using Launch Files to run multiple nodes

  • As you know oftena ROS application contains multiple nodes.
  • You can run them all at once with a launch file.
  • A launch file is an .xml formatt file with the extension .xml
  • You can find detailed documentation here
  • The most important purpose of a launch file is to launch multiple nodes
  • roslaunch package-name filename.launch

Starter Code

  • As you know there are lots of examples in prrexamples
  • You can find [some general starts here]](/tree/master/src/pa_starters)
  • Beware that you should understand what it is that you’re using!

Sources and references (quality varies a lot!)