0% found this document useful (0 votes)
9 views47 pages

ROS2 Complete Notes

The document provides comprehensive learning notes for ROS 2, covering setup, nodes, topics, and package creation, aimed at beginners. It explains the advantages of ROS 2 over ROS 1, the structure of a ROS workspace, and the process of creating and managing packages and nodes. Additionally, it includes practical examples and common errors to avoid while working with ROS 2 on Ubuntu 22.04.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views47 pages

ROS2 Complete Notes

The document provides comprehensive learning notes for ROS 2, covering setup, nodes, topics, and package creation, aimed at beginners. It explains the advantages of ROS 2 over ROS 1, the structure of a ROS workspace, and the process of creating and managing packages and nodes. Additionally, it includes practical examples and common errors to avoid while working with ROS 2 on Ubuntu 22.04.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

ROS 2

COMPLETE LEARNING NOTES


From Zero to Building Real Projects

Everything you need to know:


✅ Setting up your ROS 2 Workspace
✅ Understanding Nodes, Topics, Publishers & Subscribers
✅ Creating Packages and Writing Python Nodes
✅ Custom Messages (.msg files)
✅ Real Projects: Publisher/Subscriber + Student Data System
✅ All Errors Explained + How to Fix Them

Written by: mrnobody | ROS 2 (Humble) | Ubuntu 22.04


CHAPTER 1: What is ROS 2?
Before touching a single line of code, let's understand what ROS is and WHY we even need it.
This chapter is for complete beginners — even a 10-year-old should understand this.

1.1 The Robot Problem


Imagine you want to build a robot that:
• Reads data from a camera
• Processes that camera image to detect objects
• Moves motors based on what it sees
• At the same time, reads sensors to avoid walls

Without ROS, you would have to write ALL of this in one giant messy program. That's a
nightmare. 😩

🤔 The Problem Without ROS:

One huge file doing everything

Camera code tangled with motor code

Sensor code mixed with AI code

If one part crashes → everything crashes

Very hard to test or fix individual parts

1.2 ROS to the Rescue!


ROS (Robot Operating System) solves this by letting you break your robot brain into small
programs called NODES. Each node does ONE job.

✅ With ROS:

Camera Node → reads camera


Detector Node → finds objects in image

Motor Node → controls wheels

Sensor Node → reads distance sensors

Each node is separate, small, and easy to fix.

They talk to each other through TOPICS.

1.3 Simple Real-Life Analogy


Think of a restaurant:

Real Life ROS


Restaurant Robot (ROS)
Waiter takes your order Publisher node sends data
Kitchen receives the order Topic carries the message
Chef cooks the food Subscriber node processes data
Multiple waiters & chefs Multiple nodes working together
The restaurant building Your ROS Workspace

1.4 ROS 1 vs ROS 2


You are learning ROS 2 (specifically the version called Humble). Here is why ROS 2 is better:

ROS 1 (Old) ROS 2 (You are learning this)


ROS 1 ROS 2
Old version New, modern version
Works only on Linux Works on Linux, Windows, Mac
Not very secure Secure communication
No real-time support Real-time robot support
Requires a 'ROS Master' No master needed
1.5 Key ROS 2 Vocabulary
Learn these words — you will see them everywhere:

Word Simple Meaning


Word Simple Meaning
Node One small Python program doing one job
Topic A pipe/channel where data flows
Publisher A node that SENDS data
Subscriber A node that RECEIVES data
Message The actual data being sent (like text,
numbers)
Package A folder containing your project code
Workspace Your main project folder (ros2_ws)
Launch File A file that starts multiple nodes at once

1.6 How Nodes Communicate — The Big Picture


Here is the most important concept in ROS. Read this slowly:

Publisher Node Subscriber Node


────────────── ───────────────
│ Sends data │ ──► /topic ──► │ Receives data │
────────────── ───────────────

Example:
You type 'Rohan'


Publisher ──► /name_topic ──► Subscriber


Prints: 'Hi Rohan'

🧠 Remember This Forever:


Node = one program

Topic = the phone line

Publisher = the caller (sends)

Subscriber = the receiver (listens)

Many publishers can publish to same topic.

Many subscribers can listen to same topic.


CHAPTER 2: Setting Up Your ROS 2 Workspace
This is the very first thing you do when starting any ROS project. Think of the workspace as
your 'office' where all your robot code lives.

2.1 What is a Workspace?


A workspace is just a special folder structure that ROS understands. You always work inside it.

📁 Your Workspace Structure:

ros2_ws/ ← The main workspace folder

├── src/ ← Your packages (code) go here

├── build/ ← ROS builds things here (auto-created)

├── install/ ← Final runnable files go here (auto-created)

└── log/ ← Logs go here (auto-created)

You only work inside src/

ROS manages build/, install/, log/ automatically

2.2 Why Do We Need a Workspace?


ROS does NOT work with random folders. It only recognizes your code if it is inside a proper
workspace. This is like:

📚 Analogy: College Assignment

Teacher: 'Submit in the correct format only'

ROS: 'Put your code inside ros2_ws/src only'


If you put code in a random folder → ROS ignores it!

2.3 Creating Your Workspace — Step by Step


This is what YOU did. Let's understand each command:

Step 1: Create the workspace folder


mkdir -p ~/ros2_ws/src

🔍 What this means:

mkdir = make directory (create folder)

-p = also create parent folders if they don't exist

~/ = your home folder (/home/mrnobody/)

ros2_ws = the workspace folder name

/src = the src folder inside it

Result: /home/mrnobody/ros2_ws/src/ is created

Step 2: Go inside your workspace


cd ~/ros2_ws

🔍 What this means:

cd = change directory (go into a folder)

~/ros2_ws = go to your workspace

⚠️YOUR MISTAKE: You once typed cd ~/ros_ws (missing '2')


Always double-check spelling!

Step 3: Build the workspace


colcon build

🔍 What colcon build does:

colcon = ROS build tool (like a compiler for your packages)

build = compile everything inside src/

After this, you will see:

build/ ← intermediate files

install/ ← runnable files

log/ ← build logs

Every time you change code → run colcon build again

Step 4: Source the workspace


source install/[Link]

🔍 What 'source' does:

source = 'activate' your workspace for the current terminal

Without sourcing → ROS cannot find your nodes!

You must run this every time you open a new terminal.
TIP: Add this to ~/.bashrc to run automatically:

echo 'source ~/ros2_ws/install/[Link]' >> ~/.bashrc

2.4 Useful Terminal Commands (Linux Basics)


These are the Linux commands you used — memorize them!

Command What it does


Command What it does
ls List all files in current folder
cd folder_name Go into a folder
cd .. Go back one folder
cd ~ Go to home folder
pwd Show where you currently are
mkdir folder Create a new folder
touch [Link] Create an empty file
nano [Link] Open file in text editor
rm file Delete a file
rm -r folder Delete a folder and everything inside
chmod +x [Link] Make file executable (runnable)
mv source dest Move file/folder to new location

2.5 The Mistake You Made (Important!)


❌ What Went Wrong:

You ran: ros2 pkg create my_comm_pkg

BUT you were NOT inside ~/ros2_ws/src/


So the package was created in your HOME folder:

/home/mrnobody/my_comm_pkg ← WRONG location

✅ The Fix:

mv ~/my_comm_pkg ~/ros2_ws/src/

This moves the package to the correct location.

📌 Golden Rule: ALWAYS do cd ~/ros2_ws/src FIRST!

Then create your package.

# CORRECT ORDER — do this every time:


cd ~/ros2_ws/src # 1. Go to src first
ros2 pkg create my_pkg --build-type ament_python # 2. Create package
cd ~/ros2_ws # 3. Go to workspace root
colcon build # 4. Build
source install/[Link] # 5. Source
CHAPTER 3: ROS 2 Packages
A package is like a mini-project inside your workspace. Everything you build in ROS lives inside
a package.

3.1 What is a Package?


Think of your workspace like a school bag. Inside the bag you have different notebooks for
different subjects. Each notebook = one package.

School Bag ROS


School Bag Analogy ROS
Your school bag ros2_ws (workspace)
Maths notebook my_comm_pkg (package 1)
Science notebook student_nodes (package 2)
History notebook tutorial_config (package 3)

3.2 Creating a Package


# Always run this from inside ~/ros2_ws/src
cd ~/ros2_ws/src

ros2 pkg create my_package --build-type ament_python

🔍 Breaking down the command:

ros2 pkg create = 'create a new package'

my_package = the name you choose

--build-type = how it will be built

ament_python = we are using Python (not C++)

For C++ you would use: --build-type ament_cmake


3.3 What Gets Created Inside a Package
my_package/
├── [Link] ← ID card of the package
├── [Link] ← tells ROS which files to run
├── [Link] ← extra config (don't touch)
├── resource/
│ └── my_package ← marker file (don't touch)
├── test/ ← for testing (ignore for now)
└── my_package/ ← YOUR CODE GOES HERE
└── __init__.py ← makes it a Python module

3.4 Understanding [Link]


This is like the ID card of your package. ROS reads this to know your package exists.

<?xml version='1.0'?>
<package format='3'>
<name>my_comm_pkg</name> ← package name
<version>0.0.0</version> ← version
<description>My first package</description>
<maintainer email='you@[Link]'>your_name</maintainer>
<license>MIT</license>

<buildtool_depend>ament_python</buildtool_depend>

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

<export>
<build_type>ament_python</build_type>
</export>
</package>

Tag Meaning
Tag Meaning
<package format='3'> Using format version 3 — just leave it, don't
change
<name> Your package's name
<version> Package version — start with 0.0.0
<description> Short description of what it does
<maintainer> Who made it (your name + email)
<license> Legal license — type MIT or Apache-2.0
<buildtool_depend> What tool builds this package
<depend> Other packages this package needs

3.5 Understanding [Link]


This is the MOST IMPORTANT file for running your nodes. It tells ROS which Python files are
runnable commands.

from setuptools import setup

package_name = 'my_package'

setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[...],
install_requires=['setuptools'],
zip_safe=True,
maintainer='your_name',
entry_points={
'console_scripts': [
'my_node = my_package.my_node:main',
],
},
)

🔍 The entry_points part — MOST IMPORTANT:


'my_node = my_package.my_node:main'

────── ────────── ─────── ────

│ │ │ │

│ │ │ └─ function to run (main)

│ │ └───────── file name (my_node.py)

│ └─────────────────── package folder name

└─────────────────────────────── command name you type

When you type: ros2 run my_package my_node

ROS goes to: my_package/my_node.py → runs main()

⚠️If you forget [Link] entry:

ros2 run my_package my_node

→ Error: Package 'my_package' not found

Always add your node to [Link] before building!

3.6 The Complete Workflow for Any New Package


Every single time you start a new project, follow these exact steps:

# STEP 1: Navigate to src


cd ~/ros2_ws/src

# STEP 2: Create package


ros2 pkg create my_package --build-type ament_python

# STEP 3: Go to your code folder


cd my_package/my_package
# STEP 4: Create your node file
touch my_node.py
chmod +x my_node.py

# STEP 5: Write your code


nano my_node.py

# STEP 6: Edit [Link]


cd .. (go up one level to my_package/)
nano [Link] (add your node to console_scripts)

# STEP 7: Build
cd ~/ros2_ws
colcon build

# STEP 8: Source
source install/[Link]

# STEP 9: Run!
ros2 run my_package my_node
CHAPTER 4: Nodes — The Heart of ROS
A node is just one Python file doing one specific job. All the actual logic of your robot lives in
nodes.

4.1 What is a Node?


🧠 Simple Definition:

Node = One Python program doing ONE job

Examples:

• camera_node.py → reads camera

• motor_node.py → controls motors

• sensor_node.py → reads distance sensor

• publisher_node.py → sends data

• subscriber_node.py → receives data

4.2 Structure of Every ROS Node


Every ROS 2 Python node follows this exact structure. Memorize it!

#!/usr/bin/env python3 # 1. Tell Linux: use Python 3

import rclpy # 2. Import ROS Python library


from [Link] import Node # 3. Import the Node class

class MyNode(Node): # 4. Create your node class

def __init__(self): # 5. Constructor (runs at start)


super().__init__('my_node') # 6. Give node a name
# 7. Your setup code here...
# 8. Your other functions here...

def main(args=None): # 9. Entry point


[Link](args=args) # 10. Start ROS
node = MyNode() # 11. Create node
[Link](node) # 12. Keep node running
node.destroy_node() # 13. Clean up
[Link]() # 14. Stop ROS

if __name__ == '__main__': # 15. Run if executed directly


main()

4.3 Line-by-Line Explanation of Every Part

Line 1: #!/usr/bin/env python3

This is called a 'shebang' line.

It tells Linux: 'Run this file using Python 3'

Without it, Linux won't know which language to use.

Line 2: import rclpy

rclpy = ROS Client Library for Python

It is the main ROS 2 Python package.

Without it, you cannot use ANY ROS features.

Think of it like: 'import the entire ROS toolbox'

Line 3: from [Link] import Node

Node is a class (blueprint) for making ROS programs.

We import it so we can make our own node.

Your class will EXTEND this Node class.


Line 4: class MyNode(Node):

This creates your node class.

MyNode is your custom name — you can call it anything.

(Node) means: inherit everything from the Node class.

So your class gets all of ROS's built-in features.

Line 5 & 6: __init__ and super().__init__

__init__ runs automatically when you create the node.

It is the constructor — 'start-up code'.

super().__init__('my_node')

→ Calls the parent Node's constructor

→ Registers your node in ROS with the name 'my_node'

→ This name shows up in ros2 node list

Line 10: [Link](args=args)

Starts the ROS 2 communication system.

Must be called FIRST before creating any node.

Think of it as: 'Turn ON ROS'

Line 12: [Link](node)

Keeps your node alive and running forever.

Without this → the node starts and immediately stops.

With this → the node keeps listening/processing.

It's like a while True loop, but ROS-managed.


⚠️Publishers that use input() DON'T need spin.

Subscribers ALWAYS need spin.

Lines 13 & 14: destroy_node and shutdown

node.destroy_node() → cleanly stop the node

[Link]() → turn OFF ROS

Always include these at the end for clean exit.

Line 15: if __name__ == '__main__'

This says: 'Only run main() if this file is run directly'

It prevents main() from running if this file is imported

by another file.

Always include this at the bottom of every node file.

4.4 Useful Node Commands


# List all running nodes:
ros2 node list

# Get info about a specific node:


ros2 node info /my_node

# Run a node:
ros2 run my_package my_node
CHAPTER 5: Publisher and Subscriber Nodes
This is the most fundamental communication pattern in ROS. Everything in robotics uses this
idea.

5.1 The Big Idea


📡 Publisher → Topic → Subscriber

Publisher = Sends data (like a radio station broadcasting)

Topic = The channel/frequency (like a radio frequency)

Subscriber = Receives data (like a radio receiver)

Many publishers can publish to ONE topic.

Many subscribers can listen to ONE topic.

They DON'T need to know about each other — just the topic name!

5.2 PROJECT 1: Hi [Name] System


This is the first real project you built! Let's understand every detail.

🎯 Goal:

• You type a name (e.g., 'Rohan') in the terminal

• Publisher sends the name through a topic

• Subscriber receives it and prints: 'Hi Rohan 👋'

The Publisher Node — publisher_node.py


#!/usr/bin/env python3
# Line 1: Tell Linux → use Python 3
import rclpy
# Line 2: Import ROS Python library

from [Link] import Node


# Line 3: We will create a Node

from std_msgs.msg import String


# Line 4: We will send TEXT data (String type)

class MyPublisher(Node):
# Line 5: Create our publisher node class

def __init__(self):
# Line 6: Constructor — runs at startup

super().__init__('my_publisher')
# Line 7: Register this node in ROS as 'my_publisher'

self.publisher_ = self.create_publisher(String, 'name_topic', 10)


# Line 8: CREATE THE PUBLISHER
# String → type of data (text)
# 'name_topic' → topic name (the channel)
# 10 → queue size (store 10 messages max)

self.get_logger().info('Type a name:')
# Line 9: Print a message in the terminal

while True:
# Line 10: Loop forever — keep asking for input

name = input('Enter name: ')


# Line 11: Ask user to type something

msg = String()
# Line 12: Create an empty message object (like a blank box)

[Link] = name
# Line 13: Put the user's input inside the message box

self.publisher_.publish(msg)
# Line 14: SEND the message to the topic!

self.get_logger().info(f'Publishing: {name}')
# Line 15: Print confirmation in terminal

def main(args=None):
[Link](args=args) # Start ROS
node = MyPublisher() # Create the node (this runs __init__)
node.destroy_node() # After input() loop ends, clean up
[Link]() # Stop ROS

if __name__ == '__main__':
main()

The Subscriber Node — subscriber_node.py


#!/usr/bin/env python3
import rclpy
from [Link] import Node
from std_msgs.msg import String

class MySubscriber(Node):

def __init__(self):
super().__init__('my_subscriber')
# Register as 'my_subscriber'

[Link] = self.create_subscription(
String, # type of message
'name_topic', # which topic to listen to
self.listener_callback, # function to call when data arrives
10 # queue size
)
# This sets up the subscription.
# Whenever a message comes to 'name_topic',
# ROS will automatically call listener_callback()

def listener_callback(self, msg):


# This function runs automatically when data arrives
name = [Link]
# Extract the text from the message

self.get_logger().info(f'Hi {name} 👋')


# Print: Hi Rohan 👋

def main(args=None):
[Link](args=args)
node = MySubscriber()
[Link](node) # ← IMPORTANT: keeps subscriber alive forever
node.destroy_node()
[Link]()

if __name__ == '__main__':
main()

5.3 How to Run Both Nodes


⚠️You need TWO terminals!

Because both nodes run at the same time.

# Terminal 1 — Start Subscriber FIRST:


cd ~/ros2_ws
source install/[Link]
ros2 run my_comm_pkg subscriber_node

# Terminal 2 — Start Publisher:


cd ~/ros2_ws
source install/[Link]
ros2 run my_comm_pkg publisher_node

# Now type in Terminal 2:


Enter name: Rohan

# You will see in Terminal 1:


[INFO] Hi Rohan 👋
5.4 [Link] for This Package
entry_points={
'console_scripts': [
'publisher_node = my_comm_pkg.publisher_node:main',
'subscriber_node = my_comm_pkg.subscriber_node:main',
],
},

5.5 Understanding Topics


🔍 Topics are like group chats:

Topic Name = Group chat name

Publisher = Someone sending a message in the group

Subscriber = Someone reading messages from the group

You don't need to know WHO is sending.

You just subscribe to the topic name you care about.

In our project: topic name = 'name_topic'

Both publisher and subscriber use the SAME topic name.

That's how they connect!

# Check all active topics:


ros2 topic list

# See what data is on a topic:


ros2 topic echo /name_topic

# Publish directly from terminal (without a publisher node):


ros2 topic pub /name_topic std_msgs/String "data: 'Rohan'"
5.6 std_msgs — Standard Message Types
std_msgs is a library of pre-built message types for common data:

Message Type What data it carries


Message Type What data it carries
std_msgs/String Text (words, sentences)
std_msgs/Int32 Integer numbers (1, 2, -5)
std_msgs/Float32 Decimal numbers (3.14, -0.5)
std_msgs/Bool True or False
std_msgs/Float64 More precise decimals

# How to use different message types:


from std_msgs.msg import String # for text
from std_msgs.msg import Int32 # for integers
from std_msgs.msg import Float32 # for decimals
from std_msgs.msg import Bool # for true/false
CHAPTER 6: Custom Messages (.msg files)
Standard messages work for simple data. But what if you want to send a student's name, roll
number AND marks all together in one message? You need a CUSTOM MESSAGE.

6.1 Why Custom Messages?


📦 Problem with standard messages:

If you want to send 3 things (name, roll, marks),

you would need 3 separate topics. Very messy!

Solution: Create ONE custom message that holds all 3.

StudentData message:

string name

int32 roll_no

float32 marks

Now you send ONE message with all data inside!

6.2 Creating a Custom Message Package


Custom messages need a SEPARATE CMake package (not Python). This is what
tutorial_config is.

cd ~/ros2_ws/src
ros2 pkg create tutorial_config --build-type ament_cmake

⚠️Notice: ament_cmake (not ament_python)


Custom messages use CMake because they need to be compiled

into code that multiple languages (Python, C++) can use.

6.3 Creating the .msg File


# Create the msg folder inside the package:
mkdir ~/ros2_ws/src/tutorial_config/msg

# Create the message file:


nano ~/ros2_ws/src/tutorial_config/msg/[Link]

Inside [Link], write:


string name
int32 roll_no
float32 marks

📝 .msg file format:

data_type field_name

Available types:

string → text

int32 → whole numbers

float32 → decimal numbers

bool → true/false

int64 → big whole numbers

float64 → very precise decimals

6.4 Setting Up [Link]


This file tells CMake how to build your custom message:
cmake_minimum_required(VERSION 3.8)
project(tutorial_config)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES 'Clang')


add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
# ↑ This finds the tool that generates message code

rosidl_generate_interfaces(${PROJECT_NAME}
# ↑ NOTE: 'interfaces' with an 's' — common mistake!
'msg/[Link]'
# ↑ Path to your .msg file
)

ament_export_dependencies(rosidl_default_runtime)
ament_package()

❌ ERRORS YOU MADE (learn from them!):

1. rosidl_generate_interface ← WRONG (missing 's')

rosidl_generate_interfaces ← CORRECT

2. 'ms/[Link]' ← WRONG (typo)

'msg/[Link]' ← CORRECT

6.5 Setting Up [Link] for Custom Messages


<?xml version='1.0'?>
<package format='3'>
<name>tutorial_config</name>
<version>0.0.0</version>
<description>Custom messages</description>
<maintainer email='you@[Link]'>your_name</maintainer>
<license>MIT</license>

<buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<!-- ↑ The tool that generates message code -->

<depend>rosidl_default_runtime</depend>
<!-- ↑ Needed at runtime -->

<member_of_group>rosidl_interface_packages</member_of_group>
<!-- ↑ NOTE: 'interface' NOT 'interfaces' — common mistake! -->

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>

❌ Your mistake in [Link]:

rosidl_interfaces_packages ← WRONG (extra 's')

rosidl_interface_packages ← CORRECT

6.6 Building the Custom Message


cd ~/ros2_ws

# Build ONLY the message package first:


colcon build --packages-select tutorial_config

source install/[Link]

# Verify message was created:


ros2 interface show tutorial_config/msg/StudentData
🔥 VERY IMPORTANT:

If you change the .msg file AFTER building,

you MUST clean and rebuild:

cd ~/ros2_ws

rm -rf build install log

colcon build

source install/[Link]

Old compiled message definitions get cached!

Cleaning forces a fresh build.


CHAPTER 7: Project 2 — Student Data System
This is the assignment you built! A complete system using custom messages, publisher,
subscriber, and logic. Let's understand every part.

7.1 What the System Does


🎯 System Goal:

1. User types student's name, roll number, marks

2. Publisher puts data into StudentData message

3. Sends it through topic 'student_topic'

4. Subscriber receives it

5. Prints all the data

6. If marks < 40 → prints FAIL ❌

If marks >= 40 → prints PASS ✅

7.2 Package Structure


ros2_ws/
└── src/
├── tutorial_config/ ← custom message package
│ ├── [Link]
│ ├── [Link]
│ └── msg/
│ └── [Link]

└── student_nodes/ ← Python nodes package
├── [Link]
├── [Link]
└── student_nodes/
├── __init__.py
├── student_publisher.py
└── student_subscriber.py

7.3 [Link]
# File: tutorial_config/msg/[Link]

string name ← student's name (text)


int32 roll_no ← roll number (whole number)
float32 marks ← marks (decimal allowed: 98.5)

7.4 The Publisher — student_publisher.py


#!/usr/bin/env python3

import rclpy
from [Link] import Node
from tutorial_config.msg import StudentData
# ↑ Import YOUR custom message (not std_msgs)

class StudentPublisher(Node):

def __init__(self):
super().__init__('student_publisher')

self.publisher_ = self.create_publisher(
StudentData, # ← our custom message type
'student_topic', # ← topic name
10
)

while True:
# Get input from user
name = input('Enter name: ')
roll = int(input('Enter roll number: '))
# ↑ int() converts typed text to integer number
marks = float(input('Enter marks: '))
# ↑ float() converts typed text to decimal number

# Create message and fill all fields


msg = StudentData()
[Link] = name
msg.roll_no = roll
[Link] = marks

# Send the message


self.publisher_.publish(msg)
self.get_logger().info(f'Sent: {name}, {roll}, {marks}')

def main(args=None):
[Link](args=args)
node = StudentPublisher()
node.destroy_node()
[Link]()

if __name__ == '__main__':
main()

7.5 The Subscriber — student_subscriber.py


#!/usr/bin/env python3

import rclpy
from [Link] import Node
from tutorial_config.msg import StudentData

class StudentSubscriber(Node):

def __init__(self):
super().__init__('student_subscriber')

[Link] = self.create_subscription(
StudentData, # ← custom message type
'student_topic', # ← same topic as publisher!
[Link], # ← function to run on data
10
)

def callback(self, msg):


# This runs automatically when data arrives

# Print all the data


self.get_logger().info(
f'Name: {[Link]}, Roll: {msg.roll_no}, Marks: {[Link]}'
)

# ─── PASS/FAIL LOGIC ───


if [Link] < 40:
self.get_logger().info('FAIL ❌')
else:
self.get_logger().info('PASS ✅')

def main(args=None):
[Link](args=args)
node = StudentSubscriber()
[Link](node) # ← keep subscriber running
node.destroy_node()
[Link]()

if __name__ == '__main__':
main()

7.6 [Link] for student_nodes


entry_points={
'console_scripts': [
'student_pub = student_nodes.student_publisher:main',
'student_sub = student_nodes.student_subscriber:main',
],
},

7.7 [Link] for student_nodes


Because student_nodes uses the tutorial_config message, it must depend on it:
<depend>rclpy</depend>
<depend>tutorial_config</depend>
<!-- ↑ This tells ROS: I need tutorial_config's messages -->
7.8 Building and Running
cd ~/ros2_ws

# Build message package first:


colcon build --packages-select tutorial_config
source install/[Link]

# Then build everything:


colcon build
source install/[Link]

# Terminal 1 — Subscriber:
ros2 run student_nodes student_sub

# Terminal 2 — Publisher:
ros2 run student_nodes student_pub

7.9 Sample Output


# Publisher terminal:
Enter name: Rohan
Enter roll number: 12
Enter marks: 35
[INFO] Sent: Rohan, 12, 35.0

# Subscriber terminal:
[INFO] Name: Rohan, Roll: 12, Marks: 35.0
[INFO] FAIL ❌

# ───────────────────────

# Publisher terminal:
Enter name: Priya
Enter roll number: 7
Enter marks: 85
[INFO] Sent: Priya, 7, 85.0

# Subscriber terminal:
[INFO] Name: Priya, Roll: 7, Marks: 85.0
[INFO] PASS ✅
CHAPTER 8: Errors You Faced & How to Fix Them
Understanding your own errors is the best way to learn. Here is every error you faced,
explained clearly.

Error 1: Pasting Python Code in Terminal


❌ What Happened:

You pasted Python code directly into the terminal.

Terminal ran it as Linux commands → errors everywhere.

Error: 'import' not found

Error: 'from' not found

Error: syntax error near unexpected token '('

✅ Fix:

NEVER paste code in terminal.

Always open the file first: nano my_file.py

Then paste inside the editor.

📌 Rule: Terminal = Linux commands. .py file = Python code.

Error 2: mkdir nano subscriber_node.py


❌ What Happened:

You typed: mkdir nano subscriber_node.py

This created TWO folders instead of files:

- a folder named 'nano'


- a folder named 'subscriber_node.py'

✅ Fix:

rm -r nano

rm -r subscriber_node.py

touch subscriber_node.py (creates the FILE correctly)

📌 Rule:

mkdir = make FOLDER

touch = make FILE

nano = OPEN file (not make folder!)

Error 3: Package created in wrong location


❌ What Happened:

You ran: ros2 pkg create my_comm_pkg

But ~/ros2_ws/src/ did not exist yet.

So package was created in ~ (home folder). WRONG!

✅ Fix:

mkdir -p ~/ros2_ws/src

mv ~/my_comm_pkg ~/ros2_ws/src/

📌 Rule: ALWAYS cd ~/ros2_ws/src FIRST, then create package.


Error 4: module has no attribute 'main'
❌ Error Message:

AttributeError: module 'my_comm_pkg.publisher_node'

has no attribute 'main'

❌ What Happened:

[Link] said: 'run main() in publisher_node.py'

But publisher_node.py had no main() function.

The file was empty or incomplete.

✅ Fix:

Open the file: nano publisher_node.py

Make sure it has: def main(args=None):

Rebuild: colcon build

📌 Rule: Every node file MUST have a def main() function.

Error 5: rosidl_generate_interface (missing 's')


❌ Error Message:

Unknown CMake command 'rosidl_generate_interface'

❌ What Happened:

You wrote: rosidl_generate_interface

Correct is: rosidl_generate_interfaces (with 's' at end)


✅ Fix:

Open [Link]: nano [Link]

Change: rosidl_generate_interface

To: rosidl_generate_interfaces

📌 This is one of the most common ROS mistakes!

Error 6: 'StudentData' has no attribute 'roll_no'


❌ Error Message:

AttributeError: 'StudentData' object has no attribute 'roll_no'

❌ What Happened:

Your .msg file had a different field name.

e.g., the .msg had 'rollno' but code used 'roll_no'.

They must match EXACTLY.

✅ Fix:

Step 1: Check .msg file: nano .../msg/[Link]

Make sure it says exactly: int32 roll_no

Step 2: Clean and rebuild:

cd ~/ros2_ws

rm -rf build install log

colcon build

source install/[Link]
📌 After changing .msg → always clean rebuild!

Error 7: Package 'sudent_nodes' not found


❌ Error Message:

Package 'sudent_nodes' not found

❌ What Happened:

Simple typo: 'sudent_nodes' instead of 'student_nodes'

You missed the 't' in 'student'.

✅ Fix:

ros2 run student_nodes student_sub ← correct spelling

📌 Always check spelling carefully!

8.1 General Debugging Checklist


When something doesn't work, go through this list:

✅ 1. Did you source the workspace?


source ~/ros2_ws/install/[Link]

✅ 2. Did you colcon build after any changes?


cd ~/ros2_ws && colcon build

✅ 3. Is your node in [Link]?


Check the console_scripts section.

✅ 4. Are you in the right folder?


pwd (shows current folder)
ls (shows files)

✅ 5. Does your node file have def main()?

✅ 6. Are field names in .msg and .py code EXACTLY same?

✅ 7. If .msg changed → rm -rf build install log then rebuild


CHAPTER 9: Quick Reference — Commands Cheat
Sheet
Everything you need in one place. Print this page!

9.1 Workspace Commands


mkdir -p ~/ros2_ws/src # Create workspace
cd ~/ros2_ws # Go to workspace
colcon build # Build everything
colcon build --packages-select pkg_name # Build one package
source install/[Link] # Activate workspace
rm -rf build install log # Clean build (full reset)

9.2 Package Commands


# Create Python package:
ros2 pkg create my_pkg --build-type ament_python

# Create CMake package (for custom messages):


ros2 pkg create my_pkg --build-type ament_cmake

# List all packages:


ros2 pkg list

9.3 Running Nodes


ros2 run package_name node_name # Run a node
ros2 node list # List running nodes
ros2 node info /node_name # Info about a node

9.4 Topic Commands


ros2 topic list # List all topics
ros2 topic echo /topic_name # Print topic data
ros2 topic info /topic_name # Topic details
ros2 topic pub /topic std_msgs/String "data: 'hello'"
9.5 Message Commands
ros2 interface list # List all messages
ros2 interface show std_msgs/msg/String # See message structure
ros2 interface show tutorial_config/msg/StudentData

9.6 Every New Project Checklist


□ cd ~/ros2_ws/src
□ ros2 pkg create pkg_name --build-type ament_python
□ cd pkg_name/pkg_name
□ touch node_name.py
□ chmod +x node_name.py
□ nano node_name.py (write code)
□ cd .. (go to package root)
□ nano [Link] (add node to console_scripts)
□ cd ~/ros2_ws
□ colcon build
□ source install/[Link]
□ ros2 run pkg_name node_name

9.7 Publisher Node Template


#!/usr/bin/env python3
import rclpy
from [Link] import Node
from std_msgs.msg import String # change message type as needed

class MyPublisher(Node):
def __init__(self):
super().__init__('my_publisher')
[Link] = self.create_publisher(String, 'my_topic', 10)
# --- your publishing code here ---

def main(args=None):
[Link](args=args)
node = MyPublisher()
node.destroy_node()
[Link]()

if __name__ == '__main__':
main()

9.8 Subscriber Node Template


#!/usr/bin/env python3
import rclpy
from [Link] import Node
from std_msgs.msg import String # change message type as needed

class MySubscriber(Node):
def __init__(self):
super().__init__('my_subscriber')
[Link] = self.create_subscription(
String, 'my_topic', [Link], 10
)

def callback(self, msg):


# This runs when data arrives
self.get_logger().info(f'Received: {[Link]}')

def main(args=None):
[Link](args=args)
node = MySubscriber()
[Link](node) # ← always spin for subscribers
node.destroy_node()
[Link]()

if __name__ == '__main__':
main()

9.9 Summary Table — Everything in One View


Concept One-Line Explanation
Concept One-Line Explanation
Workspace (ros2_ws) Main folder holding all your packages
Package One project folder with all its code
Node One Python file doing one job
Topic Named channel that carries data between
nodes
Publisher Node that sends data to a topic
Subscriber Node that receives data from a topic
Message The data format sent through topics
std_msgs Built-in message types (String, Int32, etc.)
Custom msg (.msg) Your own data type with multiple fields
colcon build Compile all packages in workspace
source [Link] Activate workspace in current terminal
[Link]() Start the ROS 2 system
[Link]() Keep subscriber running forever
[Link]() Stop the ROS 2 system
create_publisher() Create a channel to send data
create_subscription() Create a listener on a topic
publish(msg) Send a message to topic
[Link] ID card of the package
[Link] Tells ROS which files are runnable
[Link] Build instructions for CMake packages

9.10 The Journey You Completed


🏆 What you learned and built:

✅ Set up ROS 2 workspace from scratch

✅ Created Python packages

✅ Wrote Publisher and Subscriber nodes

✅ Built the Hi [Name] communication system

✅ Created custom message (.msg file)


✅ Built the Student Data System with pass/fail logic

✅ Debugged real errors (the best way to learn!)

✅ Understood every line of ROS Python code

You now understand the CORE of ROS 2.

Everything else (sensors, motors, cameras, AI)

is built on top of exactly what you just learned.

Keep building. Keep breaking things. Keep learning. 🚀

You might also like