-
-
Notifications
You must be signed in to change notification settings - Fork 2
4. Fundamentals of ROS
Here are some things to know about ROS
The top level organization of ROS code is a package. A package is simply a folder with a collection of ROS code. This git repository is the mrover package. Within it is both library code and executable for our Rover's localization, perception, navigation, and simulation. Packages often contain one or more nodes. Nodes are simply executable processes that connect to the ROS network. For example, one node that we have is the state machine node that takes in various perception and localization data, makes decisions, and outputs a drive command for the rover. Nodes talk to each other via topics. A topic is simply a channel where nodes can publish or subscribe to data messages. The topic will have a particular message format. An example of a topic is /camera/rgb
. This topic has a message type of Image
. A camera driver node might publish image messages to this topic and perception code might subscribe to the topic to process the images. See here for rules on topic names.
Another thing to be aware of is launch files. Launch files are an XML specification for how to launch and configure a series of ROS nodes. Some old-timers might remember the days of running ./jarvis exec jetson/<project>
over and over again in a big tmux session to individually bring up percep
, gps
, nav
, loc
and a handful of other processes that autonomy needed. ROS launch files elegantly solve this problem. Additionally, you might remember having to SSH between various jetsons to start processes on different processors. Launch files can also handle this.
-
roscore
starts a ROS master service -
rosnode list
shows you all the running nodes on the network -
rostopic list
shows you all the topics. You can get further info about topics like the message type with otherrostopic
commands. -
rosrun <package-name> <node>
will run a particular node from the specified package -
roslaunch <package-name> <launchfile>
will run a particular launch file in the specified package -
roscd <package>
will navigate to where a package actually lives on your system -
apt install ros-<version>-<package>
will let you install existing ROS packages that are in the package repositories. For example,apt install ros-noetic-moveit
installs the moveit package for ROS1 on Ubuntu 20.04. Note that some packages are not in the repositories and will have to be built from sources. More on this later. -
rosrun rqt_tf_tree rqt_tf_tree
shows the hierarchy of defined transformations
For ROS packages that are not packaged with apt we can instead build them from source. This is also the workflow for our Mrover package since we are always actively developing and are not creating releases. ROS packages must be built in a catkin workspace, which is a special directory structure that the catkin build system uses. A brand new catkin workspace can be created by the following:
-
mkdir mrover-catkin
(or whatever you want to call the workspace) cd mrover-catkin
mkdir src
catkin init
-
git clone [email protected]:umrover/mrover-ros.git
intosrc
-
catkin build
At this point, you should see that catkin has generated some new directories. The build directory contains
-
src
which is where you should clone packages you want to develop with -
build
which has files for the build system -
devel
which is where targets go and has a very specific directory structure
To start developing a package, simply clone the package directory into src
and run catkin build
. Note that catkin build invokes the compiler and some other catkin initialization for your package. For python packages you do not have to catkin build for each change to .py
files since python is interpreted.
In order to actually execute code in your workspace you must run source devel/setup.bash
. This sets environment variables like PYTHON_PATH
to point toward the devel directory so that your code can correctly resolve dependencies. It is important to do this each time you want to run code in the workspace. Note that once you've sourced the setup.bash
you can do things like rosrun mrover navigation.py
and ROS is aware of the mrover
package.
One useful thing about catkin workspaces is that they provide isolation. Because we have to source the setup.bash for each indiviudal workspace, I could have different versions of the same package in different catkin workspaces for different projects without them interfering with each other.
One common paradigm in interprocess communication libraries is the publish-subscribe pattern. Processes are able to publish messages to certain topics, and subscribes can subscribe to those topics to receive messages. The publisher has no knowledge of who its subscribers are, and there can be many subscribers to one publisher. This is used frequently in ROS. Consider a node that acts as a driver for a camera. When it reads an image, it broadcasts it on a topic named image
and any other processes that needs visual data can receive the image by subscribing to the topic.
It is useful to have a model of the robot for things like simulation, visualization, and inverse kinematics. In ROS it is typical to specify this in a URDF or Xacro file. These are essentially XML files that specify a robot as a collection of joints and links. Joints are used to connect and move links. Links have an associated physical and visual geometry. Here is a URDF of our rover visualized:
You can see that there are links for each wheel that are connected to the chassis link via revolute joints.
In robotics we are interested in many frames of reference. Sometimes we want to know where something is relative to the frame of our rover's base. Other times we might have a static reference frame and we want to find the rover within that. We also often need to convert between frames. While I might detect AR tags relative to the frame of the rover's camera, for pathing it is most useful to know where they are relative to the world reference frame. TF is a library that stores information about the hierarchy of frames and allows us to convert coordinates between them. TF will store a tree of frames like this one:
We can add new frames to the TF tree by publishing a transform that defines a new frame relative to one of the existing frames in the tree.
While a URDF specifies how the pieces of a robot are connected, it does not inherently contain a 'state' for the robot. In order for visualization programs to render the robot something must publish transforms for each link to the TF tree. This is achieved via the robot_state_publisher node. The robot state publisher depends on joint_states however in order to create these transforms. As such, the joint states are published by the joint_state_publisher. The joint state_publisher simply ensures that when not overridden, a default state will be published for each joint. Without this we would have to make sure to repeatedly publish a state for every joint manually in our own code.
RViz is a super handy visualization program. You can use it to visualize and debug things. Before writing your own debugging tools, check if RViz already does what you need. It likely does! For instance, here is us tracking the trajectory of the simulated rover with RViz. .
Rviz can also:
- Visualize camera images
- Visualize point clouds
If you are ever trying to visualize something, you can probably publish a certain message type to RViz.
Gazebo is a simulation environment that plays nicely with ROS. It can be launched through ROS launchfiles and runs as a ROS node. It supports software plugins that are configured via xacro, the same language used for the URDF. We use Gazebo extensively to validate our code in simulation before running it on a real robot.
Since ROS communicates over UDP, it is very easy to get it working on a system with multiple devices. Any ROS system can only have one ROS master node, and in order to have multiple devices communicate, they all need to know which IP that master node is running on. They also need to tell other devices what their IP is. To accomplish this, you have to set these two environment variables on every device in the system:
export ROS_MASTER_URI=http://<IP of device running master>
This tells ROS what IP the master node is running on. In our case, this should be the IP of the Jetson.
export ROS_IP=<IP of this device>
This tells ROS what IP to broadcast as its own from this device, so it should just be that device's IP.
These can be put in a bash script and then you can run source script_name
before running each time, or in our case, we can just add these to each device's .bashrc
file since we won't need to run ROS in a different configuration.
see this page for a tutorial on rosbag and rqt_bag.