← Back to Course
Lab 1

Lab 1 - ROS2 Environment Setup and First Nodes

Lab Repository

yourusername/ros2-lab1-setup

Quick Start

git clone https://github.com/yourusername/ros2-lab1-setup.git

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:

  1. Screenshots:

    • ROS2 version output
    • Both nodes running
    • rqt_graph showing connections
    • Topic echo output
  2. Source Code:

    • status_publisher.py
    • status_subscriber.py
    • package.xml
    • setup.py
  3. 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)

  1. Multi-Publisher: Create a second publisher on a different topic
  2. Message Filtering: Subscriber only logs ERROR states
  3. Custom Message: Define and use a custom message type
  4. 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