| Deliverable | Due Date |
|---|---|
log.npf Gradescope Submission |
Tuesday, February 17th at 1:00PM EST |
| Manual grader Gradescope Submission | Tuesday, February 17th at 1:00PM EST |
| In-person Lab Checkoffs | Tuesday, February 17th, during lab |
- Introduction
- Submission
- References
- ROS Exercises
- Create a Package and Build It
- Question 1: Create a Simple Publisher (Python)
- Question 2: Create a Simple Subscriber (Python)
- Question 3: Create a More Complex Publisher (Python)
- Question 4: Create a More Complex Subscriber (Python)
- Question 5: Create a Custom Message and Publish It
- Question 6: Using Launch Files
- Question 7: Use ROS Parameters
- Question 8: Playing with Bag Files
- Question 9: TF Exercises
- Debugging Hints
- Frequently Used Instructions
Before this lab: Make sure you have completed the base installation of the racecar Docker container.
The objective of the following exercises is to help you practice the most commonly used concepts in ROS. We provide a quick reference document and pointers to relevant documentation. Feel free to use all the resources available to you to learn the concepts as thoroughly as possible and complete the exercises as efficiently as possible.
Although you're encouraged to collaborate with others if you are stuck, the lab should be completed individually so you can get practice with skills that will be essential later on in the course when you are in teams. If you have general questions, please post them on Piazza so other students can benefit from the answer. If you have a question about your individual submission, please make a private post.
You are meant to complete this lab by testing your code and verifying the results on your own, as you would do in real life.
Once you are confident in your answers, your lab will be graded against our automated tests. Please refer to the next section,
Automated Tests for instructions on how to run these tests. You will be submitting the resulting log.npf file to
Lab 1C: Intro to ROS -- log.npf submission on Gradescope in addition to your code, screenshot, and videos to
Lab 1C: Intro to ROS -- Manual grader.
You can download the test binary to check your solutions locally by going to the release page
and downloading the run_tests binary. Run this autograder AFTER you have completed questions 1-6. Most people will probably need to use
the amd64 version, but if you have an Apple Silicon chip, you'll need to use the arm64 version.
Once you download the test binary, move it into ~/racecar_ws, and open a new terminal in your Docker container.
Then, make it executable with chmod:
chmod +x run_tests_*
After completing questions 1-6, run the following in a new terminal to begin testing:
./run_tests_*
You should be graded on the completion of 6 tests. Once you are happy with your score, submit the resulting log.npf file
to Lab 1C: Intro to ROS -- log.npf submission on Gradescope.
This table shows how the different tasks will be graded by the automated grader.
| Task | Grade |
|---|---|
| Create a Simple Publisher | 0.5 points |
| Create a Simple Subscriber | 0.5 Points |
| Create a More Complex Publisher | 0.5 Points |
| Create a More Complex Subscriber | 0.5 Points |
| Create a Custom Message and Publish It | 0.5 Points |
| Using Launch Files | 0.5 Points |
| Total | 3.0 Points |
Throughout the exercises, submit your screenshots, videos, and code to the Gradescope assignment Lab 1C: Intro to ROS -- Manual grader whenever a problem requests submission. Questions 7-9 are all manually graded.
This table shows the grading breakdown of the manually graded portion of the lab.
| Task | Grade |
|---|---|
| Create a Simple Publisher | 0.1 Points |
| Create a Simple Subscriber | 0.4 Points |
| Create a More Complex Publisher | 0.2 Points |
| Create a Custom Message and Publish It | 0.2 Points |
| Using Launch Files | 0.3 Points |
| Use ROS Parameters | 0.3 Points |
| Playing with Bag Files | 0.3 Points |
| TF Exercises | 3.2 Points |
| Total | 5.0 Points |
The following are selected chapters from the ROS2 Documentation and Tutorials. If you understand the concepts covered in these tutorials, you will be ready for the exercises of this lab and most of the ROS-related tasks you will be performing throughout the first few labs.
Additionally, a useful ROS2 cheatsheet can be found here.
This class uses ROS2 Humble with colcon. Be careful when searching online: much of the available documentation is for the deprecated ROS1 system, which uses different commands and concepts. Always verify you're reading ROS2 Humble documentation.
- Packages
a. Managing a colcon workspace
b. Create a ROS Python package - Messages
a. ROS messages
b. Creating custom messages
c. Common ROS message categories - Topics
- Nodes
a. ROS nodes
b. Writing publishers and subscribers (Python) - Launch Files
a. ROS launch
b. Launch system - Parameters
- Visualization Tools
a. rqt
b. rqt_graph
c. RViz - TF2
- Rosbag
Note: Some useful debugging hints are provided at the end of this document.
The racecar Docker image already has a workspace installed called racecar_ws.
Inside your workspace, create a directory inside /src named lab1c. Inside this new lab1c directory, create a new
Python based (not C++ based) package (this means that the keyword ament_python will be in the command to create the package),
named ros_exercises, which should depend on rclpy, std_msgs, and sensor_msgs. Your ros_exercises package should live in the
~/racecar_ws/src/lab1c/ directory.
~/racecar_ws (your colcon workspace)
├── src
| ├── lab1c
| | ├── ros_exercises (your package)
| | └── custom_msgs (created later)
| └── (other ros related files and packages if any)
├── install (auto-generated during build)
├── log (auto-generated during build)
└── build (auto-generated during build)
Every time you add a file or make a new package, make sure to run colcon build --symlink-install to build your package.
ALWAYS run this command from the ~/racecar_ws directory. If you run colcon build --symlink-install from a different directory,
delete the auto-generated install, log and build directories. Additionally, whenever you open a new terminal or run
colcon build --symlink-install, source your setup.bash file so you use the underlaid build of your package:
source install/setup.bash
Your task in this exercise is to create a simple ROS node that publishes a random number between 0.0 and 10.0. Note that this file
will reside inside of the ros_exercises/ros_exercises/ folder (i.e. inside the ros_exercises/ folder in your ros_exercises package).
- Description: Publishes a random number between 0 and 10.
- File name:
simple_publisher.py - Node Name:
simple_publisher - Published topic names:
my_random_float - Message type: Float32
- Subscription topic names: None
- Publish rate: 20hz
When your node works properly, run it (remember to update your setup.py, colcon build --symlink-install, and source install/setup.bash)
and take a screenshot of an rqt_graph visualization of your node(s) and topic(s). On rqt_graph, make sure to select Nodes/Topics (all) on the
top-left to view both nodes and topics, and uncheck the hide Dead sinks and Leaf topics options. Upload the screenshot to Gradescope.
IMPORTANT NOTE: For grading purposes, always name the script executable's name the same as its corresponding node's name.
In this exercise, you will write a listener (subscriber) that listens to the topic my_random_float, which is published by the previous node.
The new node takes the natural log of the message in the my_random_float topic and publishes it to random_float_log.
- Description: Subscribes to the topic published on by the
simple_publisherand publishes the natural log of the received messages. - File name:
simple_subscriber.py - Node Name:
simple_subscriber - Published topic names:
random_float_log - Message type: Float32
- Subscription topic names:
my_random_float
Let's run both the subscriber and the publisher at the same time using tmuxp! Create a YAML tmux configuration file named
simple_pubsub_tmux.yaml. Running tmuxp load simple_pubsub_tmux.yaml should launch a tmux session with the following structure:
- Session name:
rss_lab1c - Window name:
simple_pubsub - Layout: tiled
- Pane 1: runs the
simple_publishernode - Pane 2: runs the
simple_subscribernode - Pane 3: runs the command
ros2 topic echo /my_random_float - Pane 4: runs the command
ros2 topic hz /my_random_float
On the bottom-left pane, you should see the output of ros2 topic echo /my_random_float. This is a valuable debugging tool
that lets you monitor messages on any topic in real-time, helping you verify that nodes are publishing correctly, check message
content and frequency, and troubleshoot communication issues between nodes.
On the bottom-right pane, ros2 topic hz /my_random_float monitors the message publishing frequency, which can be useful for
confirming your node is publishing at the expected rate and diagnosing timing issues. If you wrote simple_publisher correctly,
you should see your node is publishing /my_random_float at 20 Hz.
Take and save a screenshot of rqt_graph showing your nodes running. Again, on rqt_graph, select Nodes/Topics (all) on the top-left to
view both nodes and topics, and uncheck the hide Dead sinks and Leaf topics options.
Lastly, take and save a screenshot of rqt_plot, selecting /random_float_log/data as the Topic on the top-left corner of the window.
rqt_plot is a tool that visualizes ROS2 topic data as a line graph, allowing you to see how values change over time. This is especially
useful for future labs with briefings and reports, where presenting quantitative data is essential.
Upload the rqt_graph screenshot, rqt_plot screenshot, and simple_pubsub_tmux.yaml to Gradescope.
Hint: You can run rqt_plot using
ros2 run rqt_plot rqt_plot.
In this exercise, you will write a node that publishes fake LaserScan data as specified below. Additionally, you will publish the
length of the ranges array of the LaserScan message.
- Description: Publish a LaserScan message with random ranges
between 1.0 and 10.0. Also publish a Float32 message with the length of the
rangesarray. - File name:
fake_scan_publisher.py - Node Name:
fake_scan_publisher - Published topic names:
fake_scan,range_test - Message type: LaserScan, Float32 (respectively)
- Subscription topic names: None
- Publish Rate: 20hz
ROS is very particular about its message types. Make sure you convert the length of the
rangesarray to afloatbefore publishing.
- Header:
- Timestamp: Current ROS time
- Frame_id:
base_link
- Angle_min: (-2/3)pi
- Angle_max: (2/3)pi
- Angle_increment: (1/300)pi
- Time_increment: Leave it unset if you wish
- Scan_time: The time difference in seconds between consecutive scans.
- Range_min: 1.0
- Range_max: 10.0
- Ranges: One dimensional array with elements of random floats between
range_minandrange_max. Useangle_min,angle_max, andangle_incrementto determine the length. Be careful of an off-by-1 error! There should be an element atangle_minandangle_max. - Intensities: Leave it unset if you wish
When your node works properly, visualize the published LaserScan data using RViz. Take a screenshot of your visualized LaserScan data and upload it to Gradescope.
If you can't see anything in RViz, it's probably because you're publishing your LaserScan message with the header
base_linkbut RViz is visualizing relative to themapframe. In the panel on the left, under global options, changeFixed Frame: maptoFixed Frame: base_link.
Create a node that subscribes to the fake LaserScan data and outputs the longest range from the LaserScan ranges and its corresponding angle.
- Description: Subscribes to the
fake_scantopic published by thefake_scan_publisherfrom the previous exercise, finds the range element with the greatest value, and publishes the value with its corresponding angle. - File name:
open_space_publisher.py - Node Name:
open_space_publisher - Published topic names:
open_space/distanceandopen_space/angle - Message type: Float32
- Subscription topic names:
fake_scan
In the previous exercise, the publisher sent two related data points on separate topics: open_space/angle and open_space/distance.
For this exercise, you'll create a custom message that bundles both pieces of data together, similar to how the LaserScan message type
consolidates multiple data fields. Start by creating a new CMake package called custom_msgs that contains your custom message file
OpenSpace.msg. OpenSpace.msg should include two float32 fields: angle and distance. Once you've created and compiled this custom
message, update the publisher from the previous exercise to publish this new message type on a single topic called open_space,
replacing the two separate topic publications.
Take a screenshot of rqt_graph showing both fake_scan_publisher.py and open_space_publisher.py
running. Again, select Nodes/Topics (all) to view both nodes and topics, and uncheck the hide Dead sinks and
Leaf topics options. Upload the screenshot and OpenSpace.msg to Gradescope.
If you have been running your publisher(s) and subscriber(s) separately using the ros2 run command, there's a more organized way
to run multiple nodes at the same time with one command.
In this exercise, we ask you to write a single launch file called my_first_launch.launch.xml which launches all four nodes that
you have written thus far. Once completed, upload my_first_launch.launch.xml to Gradescope.
When writing the last publisher (fake_scan_publisher), you had a couple of variables with default values including angle_min,
angle_max, range_min, range_max, and angle_increment. With the current setup, if you want to change the value of one of those variables,
you will have to edit the Python code. For hundreds of lines of code, finding where each of such variables is defined can be tedious.
The rosparam server provides a way to set those parameters in the terminal, in launch files, and/or in config files. Your task
here is to parameterize the following variables from the last two nodes:
- Fake Scan Publisher
- Publish rate
fake_scantopicangle_minangle_maxrange_minrange_maxangle_increment
- Open Space Publisher
- Subscriber topic
- Publisher topic
Your node should be able to start (e.g. via ros2 run) even if not all of these parameters have been set.
If a parameter has not been set, your node should default to using the values given in the previous question.
This link may be helpful.
This way of falling back to hard-coded defaults can still be useful, especially in cases where a sensible default for
a parameter is known and changes would only have to be made rarely.
Upload fake_scan_publisher.py to Gradescope.
In Question 3, we asked you to visualize your LaserScan data on RViz. The RViz visualization was probably meaningless and ugly because you were publishing random data. Don't be alarmed; real LaserScan data is a lot prettier and more informative. Download these bagfiles, follow the instructions here, and visualize the LaserScan data on RViz. Try it with multiple coordinate frames.
The provided bag files are from our cars driving around in the basement of Stata Center.
Recording your own bagfiles is also very important for reproducing test results and debugging your code.
Re-launch my_first_launch.launch.xml from Question 6, and run ros2 topic list. This is a useful command to see the list of
active topics. Now, while my_first_launch.launch.xml is launched, record a short (~4 seconds) rosbag named my_first_bag which
contains the topics /fake_scan, /my_random_float, and /open_space.
Once you've finished recording, stop my_first_launch.launch.xml and run ros2 bag play my_first_bag -l. The -l flag enables looping,
so the rosbag automatically restarts from the beginning when it reaches the end.
Open a new terminal and run ros2 topic list. You should see /fake_scan, /my_random_float, and /open_space
being published by the rosbag player.
Next, open another terminal and run your simple_subscriber node. You should observe that /random_float_log is now being published,
with the subscriber processing the /my_random_float data from the bagfile. This demonstrates how rosbags allow you to replay
identical input data for consistent debugging, testing, and evaluation.
Take a screenshot of rqt_graph showing:
- The
rosbag2_playernode publishing/fake_scan,/my_random_float, and/open_space - Your
simple_subscribernode running, subscribing to/my_random_float, and publishing to/random_float_log
Again, make sure to select Nodes/Topics (all) to display both nodes and topics, and uncheck Hide Dead sinks and Hide Leaf topics.
Upload the screenshot to Gradescope.
In this question, we will use the one_loop bagfile from Question 8. Note that instead of map, this rosbag uses odom.
Begin by developing a ROS node that publishes the correct TF of the left and right cameras to the TF tree.
Name this node dynamic_tf_cam_publisher.py.
The left camera is located 0.05m to the left of the base_link position, and the right camera is located 0.05m to the right of
the base_link position. Both cameras have identity rotation relative to the base_link pose. In this case, the "forward" direction
is the positive-x axis, and the "left" direction is the positive-y axis.
Precompute the relative transforms between the cameras and base_link as 4x4 numpy arrays using the above information. Refer to
the lecture notes for the typical 4x4 formulation.
At each time step, your node should:
- Get the current transform of the robot w.r.t. (with respect to)
odom. Use the TF tree! - Convert the robot's transform to a 4x4 numpy array.
- Compute the current transform of the left camera w.r.t.
odomby composing the precomputedbase_link->cameratransform with theodom->base_linktransform. - Compute the current transform of the right camera w.r.t the left camera by composing the relevant matrices.
- Broadcast the computed transforms for the cameras to the TF tree. The left camera's TF should be broadcast on the
left_camframe, and the right camera's TF goes onright_cam.
Save a short (~4 seconds) video of RViz as the rosbag plays with your node running. Since this bagfile uses the odom frame, you
need to click the panel on the left and under global options, change Fixed Frame: map to Fixed Frame: odom. This changes the
"origin" of the RViz grid to the odom frame instead of the map frame (which doesn't exist in this bagfile). Additionally, make sure
we can see the base_link frame and both the left and right camera frames moving around by clicking Add -> By display type -> TF,
and make the camera follow base_link by changing Target Frame: <Fixed Frame> to Target Frame: base_link on the right panel.
Lastly, generate a PDF of your TF tree using ros2 run tf2_tools view_frames. Save and upload the video, PDF, and
dynamic_tf_cam_publisher.py to Gradescope. Please submit the video as an MP4 or GIF! Gradescope does not work
well with other video file formats.
- Note 1: Don't worry if the new TF frames are jittery and/or don't follow the
base_linkframe fast enough; this should be fixed in part 2. - Note 2: You will be doing some transformations in your ROS node. You may find SciPy spatial transforms to be useful.
- Note 3: Remember: your
left_camtransform is defined relative toodom, and yourright_camtransform is defined relative toleft_cam. These require slightly different equations!
- Write a ROS node that publishes the relative pose between each camera and the robot as a static transform. The static
transforms to broadcast are
base_link->left_camandleft_cam->right_cam. The node should only broadcast each transform once. Name this nodestatic_tf_cam_publisher.py. - Write a ROS launch file that runs
static_tf_cam_publisher.py. Name this launch filestatic_tf_publisher.launch.xml.
Do NOT call
sendTransformmultiple times when broadcasting multiple static transforms. Instead, pass all of them as a list to a singlesendTransformcall.
Save a short (~4 seconds) video of RViz just as in part 1, but with your static_tf_cam_publisher.py node running. This should
look much smoother than in part 1. Upload the video and static_tf_cam_publisher.py to Gradescope. Again please submit the video
as an MP4 or GIF!
Write a new ROS node called base_link_tf_pub.py. This new node must listen to the transform between left_cam and odom and
then broadcast the transform from odom to base_link on a new frame: base_link_2. The transform you publish must be computed by
composing the transform between left_cam and base_link with the transform you are listening for. In other words, this cannot
be a static transform.
Add this node to your static_tf_publisher.launch.xml file, such that both the static transforms for the two cameras and
base_link_2 are broadcasted when the launch file is executed. You should be able to visualize both base_link and base_link_2
on top of each other in RViz.
Save a short MP4 or GIF video of RViz again, and make sure you can see both base_link_2 and base_link on top of each other.
Upload the video, static_tf_publisher.launch.xml, and base_link_tf_pub.py to Gradescope.
Hint: Use
numpy.linalg.inv()!
Here are some helpful tools to use when trying to debug your code.
Lists currently active topics which are being subscribed to, published to, or both.
Lists currently active nodes.
Once you know that a command is being published/subscribed to, you can echo its contents, which can show whether or not any information is being passed across this topic or if you are not sending what you expect.
This is a tool to display graphs of running ROS nodes with connecting topics and package dependencies. Allows you to visualize your entire framework!
This is a powerful 3D visualization tool for displaying sensor data and state information from ROS. You will be using it more extensively in future labs.
This is a plotting tool that visualizes ROS2 topic data as a line graph, allowing you to see how values change over time.
This creates a PDF file with a visualization of your TF tree.
To find more ways to use ros2 topic, type ros2 topic --help in the command line. There are many other helpful command line tools to
help you out too. See here.