## 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, w fields in a Quaternion have nothing to do with x, y, z in Euler angles. * See [this](https://www.andre-gaschler.com/rotationconverter/) for a nice tool to help you visualize them.
### Concurrency, threading, race conditions, etc
NB
ROS 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). * Don't create threads yourself! If you feel the need to do so, figure out how to break the algorithm into multiple nodes.
### Callbacks and race conditions * Do not use rate.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 running a single ros node * Put the code for each node in a file called *.py * You can also have regular .py programs which are imported, as usual * 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 * Put the code for each node in a file called *.py * 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](http://wiki.ros.org/roslaunch/XML) * The most important purpose of a launch file is to launch multiple nodes * `roslaunch package-name filename.launch`
### Sources and references (quality varies a lot!) * [List of other Best Practices](https://github.com/leggedrobotics/ros_best_practices/wiki) * [ROS Best Practices](http://wiki.ros.org/Tutorials/Best%20Practices) * [ROS Patterns](http://wiki.ros.org/ROS/Patterns) * [More ROS Best Practices](http://wiki.ros.org/BestPractices) * [Python Code Smells](https://towardsdatascience.com/5-python-code-smells-you-should-be-wary-of-c48cc0eb9d8b) * [Several Ways of Writing a ROS Node](https://yuzhangbit.github.io/tools/several-ways-of-writing-a-ros-node/) * [Threading in ROS](https://github.com/m-elwin/me495_threads)