Lab 1: ROS2 Environment Setup and First Nodes
Objective
Set up your ROS2 development environment and create your first publisher-subscriber system.
Learning Outcomes
By completing this lab, you will:
- ✅ Install and configure ROS2 Humble
- ✅ Create a ROS2 workspace
- ✅ Build your first ROS2 package
- ✅ Implement publisher and subscriber nodes
- ✅ Use ROS2 command-line tools
Prerequisites
- Ubuntu 22.04 (or Docker)
- Basic Python knowledge
- Terminal/command line skills
Estimated Time
⏱️ 3-4 hours
Part 1: Environment Setup (60 minutes)
Step 1: Install ROS2 Humble
# Update system
sudo apt update && sudo apt upgrade -y
# Set locale
sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8
# Add ROS2 repository
sudo apt install software-properties-common
sudo add-apt-repository universe
sudo apt update && sudo apt install curl -y
# Add ROS2 GPG key
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
# Add repository to sources list
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
# Install ROS2 Humble Desktop
sudo apt update
sudo apt install ros-humble-desktop -y
# Install development tools
sudo apt install ros-dev-tools -y
sudo apt install python3-colcon-common-extensions -y
Step 2: Configure Environment
Add to ~/.bashrc:
# ROS2 Humble
source /opt/ros/humble/setup.bash
# Auto-source workspace (add after creating workspace)
# source ~/ros2_ws/install/setup.bash
# Helpful aliases
alias cb='cd ~/ros2_ws && colcon build'
alias cs='cd ~/ros2_ws && source install/setup.bash'
Apply changes:
source ~/.bashrc
Step 3: Verify Installation
# Check ROS2 version
ros2 --version
# Test with demo nodes
# Terminal 1:
ros2 run demo_nodes_cpp talker
# Terminal 2:
ros2 run demo_nodes_cpp listener
Part 2: Create Workspace and Package (45 minutes)
Step 1: Create Workspace
# Create workspace directory
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
# Build empty workspace
colcon build
# Source workspace
source install/setup.bash
Step 2: Create Python Package
cd ~/ros2_ws/src
# Create package
ros2 pkg create --build-type ament_python robot_basics \
--dependencies rclpy std_msgs
cd robot_basics
Package Structure:
robot_basics/
├── package.xml
├── setup.py
├── setup.cfg
├── resource/
└── robot_basics/
└── __init__.py
Step 3: Configure Package
Edit package.xml:
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>robot_basics</name>
<version>0.0.1</version>
<description>ROS2 basics - publisher and subscriber</description>
<maintainer email="you@example.com">Your Name</maintainer>
<license>Apache-2.0</license>
<depend>rclpy</depend>
<depend>std_msgs</depend>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
Part 3: Create Publisher Node (60 minutes)
Task 3.1: Robot Status Publisher
Create robot_basics/status_publisher.py:
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
import random
class RobotStatusPublisher(Node):
"""
Publishes robot status messages periodically
"""
def __init__(self):
super().__init__('robot_status_publisher')
# Create publisher
self.publisher = self.create_publisher(
String,
'robot_status',
10 # QoS queue size
)
# Create timer (1 Hz = 1 second)
self.timer = self.create_timer(1.0, self.publish_status)
# Robot states
self.states = ['IDLE', 'MOVING', 'CHARGING', 'ERROR']
self.counter = 0
self.get_logger().info('Robot Status Publisher started')
def publish_status(self):
"""Publish robot status message"""
msg = String()
# Simulate changing states
state = self.states[self.counter % len(self.states)]
msg.data = f'Robot Status: {state} (Count: {self.counter})'
# Publish message
self.publisher.publish(msg)
# Log
self.get_logger().info(f'Publishing: "{msg.data}"')
self.counter += 1
def main(args=None):
rclpy.init(args=args)
node = RobotStatusPublisher()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Part 4: Create Subscriber Node (60 minutes)
Task 4.1: Status Monitor
Create robot_basics/status_subscriber.py:
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class RobotStatusSubscriber(Node):
"""
Subscribes to robot status and logs information
"""
def __init__(self):
super().__init__('robot_status_subscriber')
# Create subscription
self.subscription = self.create_subscription(
String,
'robot_status',
self.status_callback,
10
)
self.message_count = 0
self.get_logger().info('Robot Status Subscriber started')
def status_callback(self, msg):
"""Handle incoming status messages"""
self.message_count += 1
# Parse status
status_text = msg.data
# Different logging levels based on status
if 'ERROR' in status_text:
self.get_logger().error(f'[{self.message_count}] {status_text}')
elif 'CHARGING' in status_text:
self.get_logger().warn(f'[{self.message_count}] {status_text}')
else:
self.get_logger().info(f'[{self.message_count}] {status_text}')
def main(args=None):
rclpy.init(args=args)
node = RobotStatusSubscriber()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Part 5: Build and Run (30 minutes)
Step 1: Update setup.py
Edit setup.py:
from setuptools import setup
package_name = 'robot_basics'
setup(
name=package_name,
version='0.0.1',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='Your Name',
maintainer_email='you@example.com',
description='ROS2 basics package',
license='Apache-2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'status_publisher = robot_basics.status_publisher:main',
'status_subscriber = robot_basics.status_subscriber:main',
],
},
)
Step 2: Build Package
cd ~/ros2_ws
colcon build --packages-select robot_basics
source install/setup.bash
Step 3: Run Nodes
Terminal 1 - Publisher:
ros2 run robot_basics status_publisher
Terminal 2 - Subscriber:
ros2 run robot_basics status_subscriber
Step 4: Inspect with CLI Tools
Terminal 3 - Debugging:
# List active nodes
ros2 node list
# Get node info
ros2 node info /robot_status_publisher
# List topics
ros2 topic list
# Echo topic messages
ros2 topic echo /robot_status
# Topic info
ros2 topic info /robot_status
# Topic bandwidth
ros2 topic bw /robot_status
# Topic frequency
ros2 topic hz /robot_status
Part 6: Visualization with rqt (30 minutes)
Launch rqt_graph
# Install rqt tools
sudo apt install ros-humble-rqt*
# Launch node graph
rqt_graph
Use rqt_console for Logging
ros2 run rqt_console rqt_console
Deliverables
Submit the following:
-
Screenshots:
- ROS2 version output
- Both nodes running
- rqt_graph showing connections
- Topic echo output
-
Source Code:
status_publisher.pystatus_subscriber.pypackage.xmlsetup.py
-
Lab Report (
lab1_report.md):- Installation steps completed
- Challenges faced and solutions
- Output of
ros2 topic info /robot_status - Explanation of publisher-subscriber pattern
Grading Rubric
| Criteria | Points | Description |
|---|---|---|
| Installation | 20 | ROS2 properly installed |
| Package Creation | 20 | Workspace and package set up |
| Publisher Node | 25 | Working publisher implementation |
| Subscriber Node | 25 | Working subscriber implementation |
| Documentation | 10 | Clear report with screenshots |
| Total | 100 |
Bonus Challenges (+10 points each)
- Multi-Publisher: Create a second publisher on a different topic
- Message Filtering: Subscriber only logs ERROR states
- Custom Message: Define and use a custom message type
- Launch File: Create launch file to start both nodes
Troubleshooting
Common Issues:
"Package not found":
source ~/ros2_ws/install/setup.bash
"Permission denied":
chmod +x robot_basics/*.py
Build errors:
cd ~/ros2_ws
rm -rf build install log
colcon build
Resources
Congratulations on creating your first ROS2 nodes! 🤖✨
Next: Lab 2 - Services and Actions