Position in the full course
This is the first step before learning TF2.
The full learning order is:
1. ROS 2 node/topic/launch review
2. TF2 frame concept
3. Static sensor frames
4. Dynamic odometry frame
5. URDF + robot_state_publisher
6. SLAM mapping
7. AMCL localization
8. Nav2 path planning
9. RViz goal navigation
10. TF2 debugging in Nav2
This module prepares you for TF2 because TF2 also uses ROS 2 nodes, topics, messages, and launch files.
1. Learning objectives
After this module, you should be able to:
- Explain what a ROS 2 node is.
- Explain what a ROS 2 topic is.
- Run multiple ROS 2 nodes from the terminal.
- Use command-line tools to inspect nodes and topics.
- Create a simple Python publisher node.
- Create a simple Python subscriber node.
- Build and run a Python ROS 2 package.
- Create a simple Python launch file.
- Understand why these skills are needed before learning TF2.
2. Teaching duration
Suggested duration: 2 hours
| Time | Activity | | ----------- | ------------------------------------ | | 0β15 min | Concept review: node, topic, message | | 15β35 min | Run turtlesim and inspect ROS graph | | 35β70 min | Create Python publisher/subscriber | | 70β95 min | Create launch file | | 95β110 min | Practice | | 110β120 min | Summary and assignment explanation |
3. Big picture explanation
In ROS 2, a robot system is usually divided into many small programs.
Each small program is called a node.
For example, an AMR may contain:
camera_node
lidar_node
imu_node
wheel_odometry_node
navigation_node
controller_node
These nodes need to exchange information.
For example:
lidar_node publishes laser scan data
navigation_node subscribes to laser scan data
controller_node publishes velocity command
motor_driver_node subscribes to velocity command
The communication channel is called a topic.
A simple ROS 2 communication structure is:
Publisher Node ---> Topic ---> Subscriber Node
Example:
/lidar_node ---> /scan ---> /navigation_node
For mobile robot navigation, this idea is very important.
Later in TF2, you will see that TF information is also published and received by ROS 2 nodes.
4. Basic ROS 2 commands
4.1 Source ROS 2 Jazzy
Open a terminal:
source /opt/ros/jazzy/setup.bash
If you use your own workspace later:
source ~/ros2_ws/install/setup.bash
4.2 Check ROS 2 environment
printenv | grep ROS
Expected result may include:
ROS_VERSION=2
ROS_DISTRO=jazzy
5. Practice 1: Run turtlesim nodes
Terminal 1: Start turtlesim
ros2 run turtlesim turtlesim_node
Terminal 2: Start keyboard control
ros2 run turtlesim turtle_teleop_key
Now you can control the turtle using keyboard arrow keys.
6. Practice 2: Inspect running nodes
Open a new terminal:
source /opt/ros/jazzy/setup.bash
List all running nodes:
ros2 node list
Expected result:
/teleop_turtle
/turtlesim
Check information about the turtlesim node:
ros2 node info /turtlesim
You should observe that a node may have:
Publishers
Subscribers
Services
Action servers
Action clients
Teaching point:
A ROS 2 node is not isolated. It communicates with other nodes through topics, services, actions, and parameters.
7. Practice 3: Inspect topics
List all topics:
ros2 topic list
Expected topics may include:
/turtle1/cmd_vel
/turtle1/pose
Check topic type:
ros2 topic type /turtle1/cmd_vel
Expected result:
geometry_msgs/msg/Twist
Check live topic data:
ros2 topic echo /turtle1/pose
Move the turtle and observe the output.
Teaching point:
A topic has a message type. Publisher and subscriber must use the same message type.
8. Practice 4: Publish velocity command manually
Stop the teleop node or leave it unused.
Publish a velocity command manually:
ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 1.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.5}}"
The turtle should move forward and rotate.
Publish continuously at 1 Hz:
ros2 topic pub --rate 1 /turtle1/cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 1.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.5}}"
Teaching point:
cmd_velis also commonly used in mobile robot navigation. Nav2 finally sends velocity commands to make the robot move.
9. Create a Python package
9.1 Create workspace
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
9.2 Create package
ros2 pkg create --build-type ament_python --license Apache-2.0 basic_ros2_review
Go into the package:
cd ~/ros2_ws/src/basic_ros2_review
The package structure should look like:
basic_ros2_review/
βββ basic_ros2_review/
β βββ __init__.py
βββ package.xml
βββ setup.py
βββ setup.cfg
βββ resource/
10. Create a simple Python publisher
Create a file:
cd ~/ros2_ws/src/basic_ros2_review/basic_ros2_review
nano simple_publisher.py
Paste the following code:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class SimplePublisher(Node):
def __init__(self):
super().__init__('simple_publisher')
self.publisher_ = self.create_publisher(String, 'robot_status', 10)
self.timer = self.create_timer(1.0, self.timer_callback)
self.count = 0
def timer_callback(self):
msg = String()
msg.data = 'Robot is alive: %d' % self.count
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.count += 1
def main(args=None):
rclpy.init(args=args)
node = SimplePublisher()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Explanation:
simple_publisher is the node name.
robot_status is the topic name.
String is the message type.
The node publishes one message every second.
11. Create a simple Python subscriber
Create another file:
nano simple_subscriber.py
Paste the following code:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class SimpleSubscriber(Node):
def __init__(self):
super().__init__('simple_subscriber')
self.subscription = self.create_subscription(
String,
'robot_status',
self.listener_callback,
10
)
def listener_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)
def main(args=None):
rclpy.init(args=args)
node = SimpleSubscriber()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Explanation:
simple_subscriber is the node name.
It subscribes to robot_status.
When a message arrives, listener_callback() is executed.
12. Modify setup.py
Open setup.py:
cd ~/ros2_ws/src/basic_ros2_review
nano setup.py
Find entry_points and modify it:
entry_points={
'console_scripts': [
'simple_publisher = basic_ros2_review.simple_publisher:main',
'simple_subscriber = basic_ros2_review.simple_subscriber:main',
],
},
13. Build the package
cd ~/ros2_ws
colcon build --packages-select basic_ros2_review
Source the workspace:
source install/setup.bash
14. Run publisher and subscriber
Terminal 1
source ~/ros2_ws/install/setup.bash
ros2 run basic_ros2_review simple_publisher
Terminal 2
source ~/ros2_ws/install/setup.bash
ros2 run basic_ros2_review simple_subscriber
Expected result:
Publisher terminal:
Publishing: "Robot is alive: 0"
Publishing: "Robot is alive: 1"
Publishing: "Robot is alive: 2"
Subscriber terminal:
I heard: "Robot is alive: 0"
I heard: "Robot is alive: 1"
I heard: "Robot is alive: 2"
15. Inspect the custom topic
List topics:
ros2 topic list
You should see:
/robot_status
Check topic type:
ros2 topic type /robot_status
Expected result:
std_msgs/msg/String
Echo the topic:
ros2 topic echo /robot_status
Check publisher/subscriber information:
ros2 topic info /robot_status
Expected result:
Publisher count: 1
Subscription count: 1
16. Create a launch file
16.1 Create launch folder
cd ~/ros2_ws/src/basic_ros2_review
mkdir launch
Create a launch file:
nano launch/basic_review_launch.py
Paste the following code:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
publisher_node = Node(
package='basic_ros2_review',
executable='simple_publisher',
name='simple_publisher'
)
subscriber_node = Node(
package='basic_ros2_review',
executable='simple_subscriber',
name='simple_subscriber'
)
return LaunchDescription([
publisher_node,
subscriber_node
])
17. Modify setup.py to install launch file
Open setup.py:
nano setup.py
Add these imports at the top:
import os
from glob import glob
Find data_files and modify it like this:
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share', package_name, 'launch'), glob('launch/*.py')),
],
18. Rebuild and run launch file
cd ~/ros2_ws
colcon build --packages-select basic_ros2_review
source install/setup.bash
Run the launch file:
ros2 launch basic_ros2_review basic_review_launch.py
Expected result:
simple_publisher publishes messages.
simple_subscriber receives messages.
Both nodes start from one launch command.
Teaching point:
Launch files are useful because real robot systems usually need many nodes running together.
For example, a Nav2 system may start:
robot_state_publisher
lidar_driver
camera_driver
slam_toolbox or amcl
controller_server
planner_server
bt_navigator
rviz2
Running each one manually is not convenient, so launch files are necessary.
19. Connection to TF2
This review prepares you for TF2.
In the next step, you will learn that TF2 also uses ROS 2 nodes.
For example:
TF broadcaster node ---> /tf topic
Static TF broadcaster ---> /tf_static topic
TF listener node ---> receives transform information
The idea is similar to todayβs publisher/subscriber example.
Today:
simple_publisher ---> /robot_status ---> simple_subscriber
Next TF2 lesson:
tf_broadcaster ---> /tf ---> tf_listener
20. Practice
Practice A
Run turtlesim and answer:
- What nodes are running?
- What topics are available?
- What is the message type of
/turtle1/cmd_vel? - What is the message type of
/turtle1/pose?
Commands:
ros2 node list
ros2 topic list
ros2 topic type /turtle1/cmd_vel
ros2 topic type /turtle1/pose
Practice B
Modify the publisher message.
Original:
msg.data = 'Robot is alive: %d' % self.count
Change it to:
msg.data = 'AMR navigation system is ready: %d' % self.count
Then rebuild and run again:
cd ~/ros2_ws
colcon build --packages-select basic_ros2_review
source install/setup.bash
ros2 launch basic_ros2_review basic_review_launch.py
Practice C
Change the topic name from:
robot_status
to:
amr_status
You must modify both:
simple_publisher.py
simple_subscriber.py
Then verify:
ros2 topic list
ros2 topic echo /amr_status
21. Common problems and solutions
Problem 1: package not found
Error:
Package 'basic_ros2_review' not found
Possible reason:
The workspace has not been sourced.
Solution:
source ~/ros2_ws/install/setup.bash
Problem 2: executable not found
Error:
No executable found
Possible reasons:
The entry point in setup.py is wrong.
The package was not rebuilt.
Solution:
cd ~/ros2_ws
colcon build --packages-select basic_ros2_review
source install/setup.bash
Problem 3: subscriber receives nothing
Possible reasons:
The publisher is not running.
The topic names are different.
The message types are different.
Check:
ros2 topic list
ros2 topic info /robot_status
22. Homework
Create a new publisher/subscriber pair for AMR battery status.
Requirements
Publisher node name:
battery_publisher
Subscriber node name:
battery_monitor
Topic name:
/battery_status
Message type:
std_msgs/msg/String
Example published message:
Battery level: 90%
Battery level: 80%
Battery level: 70%
You should submit:
- Screenshot of publisher output.
- Screenshot of subscriber output.
- Screenshot of
ros2 topic list. - Screenshot of
ros2 topic echo /battery_status. - Short explanation of publisher, topic, and subscriber.
23. Mini quiz
- What is a ROS 2 node?
- What is a ROS 2 topic?
- What command lists all running nodes?
- What command lists all active topics?
- What command shows the message type of a topic?
- Why do we need launch files?
- In mobile robot navigation, what is
/cmd_velusually used for? - Why is this lesson important before learning TF2?
24. Key summary
In this module, you reviewed the basic ROS 2 communication structure:
Node ---> Topic ---> Node
A publisher sends messages to a topic.
A subscriber receives messages from a topic.
A launch file starts multiple nodes together.
This foundation is necessary before learning TF2 because TF2 also works through ROS 2 nodes and topics.
In the next module, you will learn:
TF2 frame concept
map, odom, base_link, sensor frames
TF tree
parent frame and child frame