ROS2 Humble: OzU Racing Autonomous
EDIT MODE: Click any text to edit. Press E or click top-left to exit.
OzU Racing

ROS2 Humble

Hands-On Workshop

FS-AI | March 2026

02
INTROCORECODEBUILDDEMO

What is ROS2?

Not an OS

Robot Operating System 2 is a middleware framework: a set of tools, libraries, and conventions for building robot software.

  • Standardized communication between processes
  • Language-agnostic (Python, C++, and more)
  • Built on DDS (Data Distribution Service)
  • Real-time capable, production-ready

Why ROS2 for FS?

  • Team standard: Nearly all FS-AI teams use ROS2
  • Modular architecture: Swap out different perception, planning, or control modules without rewriting the pipeline
  • Simulation ready: Integrates directly with IPG CarMaker
  • Ready-made piping: Built-in pub/sub, services, and actions: no need to build your own IPC from scratch
03
INTROCORECODEBUILDDEMO

Why Humble Hawksbill?

  • LTS release: Long Term Support until May 2027; stability matters for a multi-year FS project
  • Ubuntu 22.04 locked: Each ROS2 release targets a specific Ubuntu version; Humble requires Jammy (22.04)
  • IPG CarMaker compatibility: CarMaker's ROS2 interface is validated on Ubuntu 22.04, matching Humble's target
  • Ecosystem maturity: Most ROS2 packages and drivers have stable Humble branches

Our Stack

  • Ubuntu 22.04 Jammy
  • ROS2 Humble Hawksbill
  • IPG CarMaker 2024+
04
INTROCORECODEBUILDDEMO
Core Concept

Nodes

A node is a single process that performs one specific task. Each node is independent, runs in its own process, and communicates with others.

FS-AI Example

  • cone_detector: finds bounding boxes using YOLO inference
  • slam: does simultaneous localization and mapping
  • path_planner: plans the path trajectory
  • control: calculates steering and gas/brake commands
# Creating a node in Python
import rclpy
from rclpy.node import Node

class MyNode(Node):
  def __init__(self):
    super().__init__('my_node')
    self.get_logger().info('Started!')

def main():
  rclpy.init()
  node = MyNode()
  rclpy.spin(node)
  rclpy.shutdown()
05
INTROCORECODEBUILDDEMO
Core Concept

Topics

A topic is a named bus for messages. Nodes publish to or subscribe to topics. It's a one-to-many or many-to-many communication pattern.

Key Properties

  • Asynchronous: publisher doesn't wait
  • Decoupled: pub/sub don't know each other
  • Typed: each topic has a message type
  • Named: e.g. /camera/image_raw
Camera Client
Node
/front_camera_rgb/camera_info
/front_camera_rgb/image_raw
/front_camera_depth/camera_info
/front_camera_depth/image_raw
Cone Detector
Node

One publisher, multiple topics, one subscriber

SLAM
Node
/odom
Path Planner Node
Control Node
Visualization Node

One publisher, one topic, many subscribers

06
INTROCORECODEBUILDDEMO

Publisher & Subscriber

Publisher
# Create publisher
self.pub = self.create_publisher(
  String,      # msg type
  'my_topic', # topic name
  10)         # queue size

# Publish on timer (1 Hz)
self.timer = self.create_timer(
  1.0, self.timer_cb)

def timer_cb(self):
  msg = String()
  msg.data = 'Hello ROS2!'
  self.pub.publish(msg)
Subscriber
# Create subscriber
self.sub = self.create_subscription(
  String,      # msg type
  'my_topic', # topic name
  self.cb,     # callback
  10)         # queue size


def cb(self, msg):
  self.get_logger().info(
    f'Got: {msg.data}')
07
INTROCORECODEBUILDDEMO
Core Concept

Messages

Messages are typed data structures that flow through topics. ROS2 provides built-in types and you can define custom ones.

Common Built-in Types

  • std_msgs/String: text data
  • std_msgs/Int32: integer values
  • std_msgs/Float64: floating-point values
  • sensor_msgs/Image: camera
Custom Message
# msg/Cone.msg
string color   # "blue", "yellow"
float64 x     # meters
float64 y     # meters

# msg/ConeArray.msg
std_msgs/Header header
ozu_msgs/Cone[] cones

Custom messages are defined in .msg files and auto-generated into Python/C++ code.

08
INTROCORECODEBUILDDEMO
Core Concept

Services

Services are request/response communication. Unlike topics (continuous stream), services are for one-time calls: like asking a question and getting an answer.

Topics vs Services

  • Topic: "Here's sensor data" (streaming)
  • Service: "Switch to manual mode" (one-shot)
Client Node (Caller)
Request →
/set_mode
← Response
Server Node (Handler)
Service Definition
# srv/SetMode.srv
string mode # request
---
bool success # response
string message
# CLI: Call a service
ros2 service call /set_mode \
  ozu_msgs/srv/SetMode \
  "{mode: 'autonomous'}"
09
INTROCORECODEBUILDDEMO

Service Server & Client

Server
# Create service server
self.srv = self.create_service(
  SetMode,
  'set_mode',
  self.handle_set_mode)

def handle_set_mode(self, req, res):
  self.mode = req.mode
  res.success = True
  res.message = f'Now: {req.mode}'
  return res
Client (async)
# Create client
self.cli = self.create_client(
  SetMode, 'set_mode')

# Wait for service
self.cli.wait_for_service()

# Send request
req = SetMode.Request()
req.mode = 'autonomous'
future = self.cli.call_async(req)
10
INTROCORECODEBUILDDEMO
Core Concept

Parameters

Parameters are per-node configuration values that can be set at launch and changed at runtime. No need to recompile!

FS-AI Use Cases

  • model_path: YOLO .pt model path
  • confidence_threshold: YOLO detection threshold
  • use_sim_time: use simulation time
  • max_speed: speed limit
# Declare in node
self.declare_parameter(
  'noise_level', 0.1)

# Read parameter
noise = self.get_parameter(
  'noise_level'
).get_parameter_value()
 .double_value
# Set from CLI
ros2 param set /sensor_sim \
  noise_level 0.5

# List all params
ros2 param list
11
INTROCORECODEBUILDDEMO
Core Concept

Quality of Service (QoS)

QoS profiles control how messages are delivered. Choose the right profile for your data type.

PolicyOptionsWhen to use
ReliabilityRELIABLE / BEST_EFFORTReliable for commands; best-effort for sensors
DurabilityVOLATILE / TRANSIENT_LOCALTransient for late-joining subscribers
HistoryKEEP_LAST(N) / KEEP_ALLKeep last for sensors; keep all for logs
DepthQueue size (1, 5, 10...)Small for real-time; larger for buffering
from rclpy.qos import QoSProfile, ReliabilityPolicy, DurabilityPolicy
qos = QoSProfile(depth=10, reliability=ReliabilityPolicy.RELIABLE,
                 durability=DurabilityPolicy.VOLATILE)
12
INTROCORECODEBUILDDEMO
Build System

Workspace & Packages

A workspace is your project directory. It contains one or more packages: the unit of organization in ROS2.

ros2_ws/
  src/
    ozu_msgs/        # CMake pkg
      CMakeLists.txt
      package.xml
      msg/  srv/
    ozu_racing_demo/ # Python pkg
      setup.py
      package.xml
      ozu_racing_demo/
      launch/
  build/  install/  log/

CMake Package (ament_cmake)

For C++ code and message/service definitions. Uses CMakeLists.txt.

Python Package (ament_python)

For Python code. Uses setup.py instead of CMake.

Build & Source

colcon build: builds everything
source install/setup.bash: loads built packages

13
INTROCORECODEBUILDDEMO

package.xml

Every package has a package.xml: the package manifest. It declares metadata and dependencies.

<?xml version="1.0"?>
<package format="3">
  <name>ozu_msgs</name>
  <version>0.1.0</version>
  <description>Custom messages for OzU Racing</description>

  <!-- Build type: cmake or python -->
  <buildtool_depend>ament_cmake</buildtool_depend>
  <buildtool_depend>rosidl_default_generators</buildtool_depend>

  <!-- Dependencies: what this package needs -->
  <depend>std_msgs</depend>
  <depend>builtin_interfaces</depend>

  <exec_depend>rosidl_default_runtime</exec_depend>
  <member_of_group>rosidl_interface_packages</member_of_group>
</package>
14
INTROCORECODEBUILDDEMO

CMakeLists.txt

The build recipe for CMake packages. For ozu_msgs, it generates code from .msg and .srv files.

cmake_minimum_required(VERSION 3.8)
project(ozu_msgs)

# Find dependencies
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(std_msgs REQUIRED)
find_package(builtin_interfaces REQUIRED)

# Generate message & service interfaces
rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/Cone.msg"
  "msg/ConeArray.msg"
  "msg/VehicleCommand.msg"
  "srv/SetMode.srv"
  DEPENDENCIES std_msgs builtin_interfaces
)

ament_package()
15
INTROCORECODEBUILDDEMO

setup.py (Python Package)

Python packages use setup.py instead of CMakeLists. The key part: entry_points: this is how ROS2 discovers your nodes.

from setuptools import setup

setup(
  name='ozu_racing_demo',
  packages=['ozu_racing_demo'],
  data_files=[
    # Install launch files
    ('share/.../launch', ['launch/demo_launch.py']),
  ],
  # THIS registers your nodes as executables
  entry_points={
    'console_scripts': [
      'sensor_simulator = ozu_racing_demo.sensor_simulator:main',
      'cone_detector = ozu_racing_demo.cone_detector:main',
      'path_planner = ozu_racing_demo.path_planner:main',
    ],
  },
)
16
INTROCORECODEBUILDDEMO
Build System

Launch Files

Launch files start multiple nodes at once with configured parameters. Written in Python for ROS2.

What launch files do

  • Start multiple nodes together
  • Set parameters per node
  • Define launch arguments (CLI overrides)
  • Set up remappings and namespaces
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
  return LaunchDescription([
    Node(
      package='ozu_racing_demo',
      executable='sensor_simulator',
      parameters=[{
        'noise_level': 0.1
      }],
    ),
  ])
# Run it:
ros2 launch ozu_racing_demo \
  demo_launch.py noise_level:=0.2
17
INTROCORECODEBUILDDEMO
Live Demo

Our Demo Architecture

A simplified autonomous driving pipeline for Formula Student: sensor to steering.

Sensor
Simulator
/raw_cones
Cone
Detector
/detected_cones
Path
Planner
/vehicle_cmd
Vehicle
Controller

Pre-Built

Sensor Simulator
Cone Detector

Live Coding

Path Planner
Vehicle Controller

Service

/set_mode
autonomous / manual

18
INTROCORECODEBUILDDEMO

ROS2 CLI Cheat Sheet

Nodes
ros2 node list
ros2 node info /sensor_simulator
Topics
ros2 topic list
ros2 topic echo /raw_cones
ros2 topic hz /raw_cones
ros2 topic info /raw_cones
Services
ros2 service list
ros2 service call /set_mode \
  ozu_msgs/srv/SetMode \
  "{mode: 'manual'}"
Parameters
ros2 param list
ros2 param get /sensor_sim noise_level
ros2 param set /sensor_sim noise_level 0.5
19
INTROCORECODEBUILDDEMO

Debugging & Visualization

rqt_graph

Visualize node connections and topic flow as a graph.

ros2 run rqt_graph rqt_graph

rqt_console

View all log messages from all nodes in one place.

ros2 run rqt_console rqt_console

ros2 bag

Record and replay topic data for testing and debugging.

ros2 bag record -a
ros2 bag play my_bag/

Pro tip: Run rqt_graph while our demo is running to see the full pipeline visualized as a node graph.

Live Coding Time

Let's Build
The Planner

Sensor Sim
Cone Detector
Path Planner
Vehicle Ctrl

Open the guidebook and follow along!

21
INTROCORECODEBUILDDEMO

Recap & Resources

What We Covered

  • Nodes: independent processes
  • Topics: pub/sub data streams
  • Messages: typed data structures
  • Services: request/response calls
  • Parameters: runtime configuration
  • QoS: delivery guarantees
  • Launch files: multi-node startup
  • Workspace & build system

Official Docs

docs.ros.org: Tutorials, API reference

Navigation2

nav2.org: Full autonomous nav stack

Autoware

autoware.org: Open-source self-driving

OzU Racing

Questions?

Guidebook, demo code, and install instructions
are all in the ros_lecture/ folder.

Let's build something fast.