Docs menu
Hands-On ROS 2 Primer: Package to Rosbag in Five Checkpoints
A hands-on ROS 2 primer for Racerbot members: your first package, node, topic, launch file and rosbag, each with a working checkpoint, ending where the first autonomous lab begins.
This primer teaches ROS 2 by building, not reading. Five skills, five checkpoints, each one a real thing you run inside your workspace container: a package, a node, a topic, a launch file and a rosbag recording. By the end you have done everything Lab 1 asks for, and you know the moving parts Lab 2 builds on.
It is deliberately short. The official ROS 2 Humble tutorials go much deeper; links to the matching official pages sit beside each section for when you want more.
Before you start: inside the container, source the environment in every terminal you open:
source /opt/ros/humble/setup.bash
source /racerbot_ws/install/local_setup.bash1. Your first package
A package is the unit ROS 2 builds and ships code in. Everything you write lives in one.
cd /racerbot_ws/src
ros2 pkg create primer_pkg --build-type ament_cmake --dependencies rclcpp std_msgs
cd /racerbot_ws
colcon build
source install/local_setup.bashCheckpoint: ros2 pkg list | grep primer_pkg prints primer_pkg.
More: official “Creating your first ROS 2 package”, colcon tutorial.
2. Your first node
A node is one running process that does one job - reads a sensor, plans a path, drives a motor. Create src/primer_pkg/src/talker.cpp:
#include <chrono>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
class Talker : public rclcpp::Node {
public:
Talker() : Node("talker"), count_(0) {
pub_ = this->create_publisher<std_msgs::msg::String>("/chatter", 10);
timer_ = this->create_wall_timer(500ms, [this]() {
auto msg = std_msgs::msg::String();
msg.data = "lap " + std::to_string(count_++);
pub_->publish(msg);
});
}
private:
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_;
rclcpp::TimerBase::SharedPtr timer_;
size_t count_;
};
int main(int argc, char ** argv) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<Talker>());
rclcpp::shutdown();
return 0;
}Wire it into src/primer_pkg/CMakeLists.txt (add at the end):
add_executable(talker src/talker.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
install(TARGETS talker DESTINATION lib/${PROJECT_NAME})Rebuild and run:
cd /racerbot_ws && colcon build && source install/local_setup.bash
ros2 run primer_pkg talkerCheckpoint: in a second sourced terminal, ros2 node list shows /talker.
More: official “Writing a simple publisher and subscriber (C++)”, Understanding nodes.
3. Your first topic
Topics are the named channels nodes communicate on. Your talker is already publishing one, /chatter. Listen to it:
ros2 topic echo /chatterCheckpoint: lap 0, lap 1, … streaming in. Then inspect it: ros2 topic list, ros2 topic info /chatter, ros2 topic hz /chatter (should report about 2 Hz - the 500 ms timer).
The simulator from the quickstart speaks exactly this language: it publishes /scan and /ego_racecar/odom, and listens on /drive. ros2 topic echo /scan against a running sim is worth a look - that wall of numbers is what your Lab 2 code will parse.
More: Understanding topics.
4. Your first launch file
Launch files start several nodes at once with their parameters - how real runs begin. Create src/primer_pkg/launch/primer.launch.py:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package="primer_pkg",
executable="talker",
name="talker",
output="screen",
),
])Tell CMake to install it - in CMakeLists.txt:
install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})Rebuild, then:
ros2 launch primer_pkg primer.launch.pyCheckpoint: the talker starts under launch (lap messages scrolling, Ctrl-C stops it). Compare with ros2 launch f1tenth_gym_ros gym_bridge_launch.py - the simulator starts the same way, just with more nodes and a YAML parameter file.
More: official “Creating launch files”.
5. Your first rosbag
A rosbag records topics to disk so you can replay a run without rerunning it. The team uses bags constantly: record the car (or sim), debug at your desk.
ros2 bag record /chatterLet it run ten seconds, Ctrl-C, then replay with the talker stopped:
ros2 bag play rosbag2_*
ros2 topic echo /chatter # in a second terminalCheckpoint: the echoed messages replay from the recording, not from a live node. Record the simulator’s /scan the same way and you have captured sensor data you can develop against offline.
More: official “Recording and playing back data”.
Where this leads
You have now done, by hand, everything Lab 1 assesses: workspace, package, publisher, launch, build. The sequence from here:
- Lab 1 (Stage 1 onboarding) - the assessed version of what you just did, in the sim.
- Lab 2: Automatic Emergency Braking - subscribe to
/scan, compute time-to-collision, publish brake commands on/drive. Nodes, topics and launch files doing something that matters.
When the CLI side of this page felt unfamiliar - paths, tabs, tmux, editing files in a terminal - the terminal foundations guide is the catch-up.
Sources
- ROS 2 Humble tutorials - each section links the matching official page.
- sfu-racerbot/racerbot-docs: WORKFLOW.md - the team’s package/build/run loop this primer mirrors.