Reminder: Readings are your responsibility. You will be expected to come to class prepared, having read the material, and ready to participate in the discussion
Logistics
- PA Basic Control
- PA Wall follow
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 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).
- 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
- 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!)
Thank you. Questions? (random Image from picsum.photos)