Lab1: ROS2 Publish and Subscribe with Touch Sensors

Introduction

In this lab, you will learn the fundamentals of ROS2 (Robot Operating System 2) communication by creating a simple publisher and subscriber. The publisher will read touch sensor data from the Mini Pupper and publish it as ROS2 messages. The subscriber will receive these messages and react accordingly.

Prerequisites

  • Mini Pupper with ROS2 installed
  • Basic understanding of Python
  • Completed Jupyter Lab4 (Touch Sensors)

ROS2 Concepts

Topics

Topics are named buses over which nodes exchange messages. A node can publish messages to a topic or subscribe to receive messages from a topic.

Publisher

A publisher sends messages to a topic. In this lab, we’ll publish touch sensor states.

Subscriber

A subscriber receives messages from a topic. We’ll subscribe to touch sensor data and print the results.

Getting Started

Step 1: Create a ROS2 Package

SSH into your Mini Pupper and create a new ROS2 package:

cd ~/ros2_ws/src
ros2 pkg create --build-type ament_python touch_sensor_pkg --dependencies rclpy std_msgs

This creates a package named touch_sensor_pkg with Python support.

Step 2: Package Structure

Your package should have this structure:

~/ros2_ws/src/touch_sensor_pkg/
├── package.xml
├── setup.py
├── setup.cfg
├── resource/
│   └── touch_sensor_pkg
└── touch_sensor_pkg/
    ├── __init__.py
    ├── touch_publisher.py
    └── touch_subscriber.py

Creating the Publisher

Step 3: Create the Touch Sensor Publisher

Create the file ~/ros2_ws/src/touch_sensor_pkg/touch_sensor_pkg/touch_publisher.py:

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
import RPi.GPIO as GPIO
import time

class TouchPublisher(Node):
    def __init__(self):
        super().__init__('touch_publisher')
        
        # Create publisher
        self.publisher_ = self.create_publisher(String, 'touch_sensor', 10)
        
        # Timer to publish at 10 Hz
        timer_period = 0.1  # seconds
        self.timer = self.create_timer(timer_period, self.timer_callback)
        
        # Setup GPIO for touch sensors
        self.touchPin_Front = 6
        self.touchPin_Left = 3
        self.touchPin_Right = 16
        self.touchPin_Back = 2
        
        GPIO.setmode(GPIO.BCM)
        GPIO.setwarnings(False)
        GPIO.setup(self.touchPin_Front, GPIO.IN)
        GPIO.setup(self.touchPin_Left, GPIO.IN)
        GPIO.setup(self.touchPin_Right, GPIO.IN)
        GPIO.setup(self.touchPin_Back, GPIO.IN)
        
        self.get_logger().info('Touch Publisher Node Started')

    def timer_callback(self):
        # Read touch sensor values
        front = not GPIO.input(self.touchPin_Front)
        back = not GPIO.input(self.touchPin_Back)
        left = not GPIO.input(self.touchPin_Left)
        right = not GPIO.input(self.touchPin_Right)
        
        # Build message string
        touched = []
        if front:
            touched.append('Front')
        if back:
            touched.append('Back')
        if left:
            touched.append('Left')
        if right:
            touched.append('Right')
        
        msg = String()
        if touched:
            msg.data = ','.join(touched)
        else:
            msg.data = 'None'
        
        # Publish the message
        self.publisher_.publish(msg)
        
        # Log only when touched
        if touched:
            self.get_logger().info(f'Publishing: {msg.data}')

    def destroy_node(self):
        GPIO.cleanup()
        super().destroy_node()


def main(args=None):
    rclpy.init(args=args)
    touch_publisher = TouchPublisher()
    
    try:
        rclpy.spin(touch_publisher)
    except KeyboardInterrupt:
        pass
    finally:
        touch_publisher.destroy_node()
        rclpy.shutdown()


if __name__ == '__main__':
    main()

Creating the Subscriber

Step 4: Create the Touch Sensor Subscriber

Create the file ~/ros2_ws/src/touch_sensor_pkg/touch_sensor_pkg/touch_subscriber.py:

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_msgs.msg import String


class TouchSubscriber(Node):
    def __init__(self):
        super().__init__('touch_subscriber')
        
        # Create subscription
        self.subscription = self.create_subscription(
            String,
            'touch_sensor',
            self.listener_callback,
            10
        )
        self.subscription  # prevent unused variable warning
        
        self.get_logger().info('Touch Subscriber Node Started')
        self.get_logger().info('Waiting for touch sensor data...')

    def listener_callback(self, msg):
        if msg.data != 'None':
            self.get_logger().info(f'Touch detected: {msg.data}')
            
            # React to different touches
            if 'Front' in msg.data:
                self.get_logger().info('  -> Action: Move Forward!')
            if 'Back' in msg.data:
                self.get_logger().info('  -> Action: Move Backward!')
            if 'Left' in msg.data:
                self.get_logger().info('  -> Action: Turn Left!')
            if 'Right' in msg.data:
                self.get_logger().info('  -> Action: Turn Right!')


def main(args=None):
    rclpy.init(args=args)
    touch_subscriber = TouchSubscriber()
    
    try:
        rclpy.spin(touch_subscriber)
    except KeyboardInterrupt:
        pass
    finally:
        touch_subscriber.destroy_node()
        rclpy.shutdown()


if __name__ == '__main__':
    main()

Configuring the Package

Step 5: Update setup.py

Edit ~/ros2_ws/src/touch_sensor_pkg/setup.py:

from setuptools import setup

package_name = 'touch_sensor_pkg'

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='ubuntu',
    maintainer_email='ubuntu@minipupper.local',
    description='Touch sensor publisher and subscriber for Mini Pupper',
    license='Apache License 2.0',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'touch_publisher = touch_sensor_pkg.touch_publisher:main',
            'touch_subscriber = touch_sensor_pkg.touch_subscriber:main',
        ],
    },
)

Step 6: Update package.xml

Edit ~/ros2_ws/src/touch_sensor_pkg/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>touch_sensor_pkg</name>
  <version>0.0.1</version>
  <description>Touch sensor publisher and subscriber for Mini Pupper</description>
  <maintainer email="ubuntu@minipupper.local">ubuntu</maintainer>
  <license>Apache License 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>

Building and Running

Step 7: Build the Package

cd ~/ros2_ws
colcon build --packages-select touch_sensor_pkg

Step 8: Source the Workspace

source ~/ros2_ws/install/setup.bash

Add this to your ~/.bashrc for automatic sourcing:

echo "source ~/ros2_ws/install/setup.bash" >> ~/.bashrc

Step 9: Run the Publisher

In Terminal 1:

ros2 run touch_sensor_pkg touch_publisher

Step 10: Run the Subscriber

In Terminal 2 (new SSH session):

source ~/ros2_ws/install/setup.bash
ros2 run touch_sensor_pkg touch_subscriber

Testing

View the Topic

List all active topics:

ros2 topic list

You should see /touch_sensor in the list.

Echo the Topic

View raw messages on the topic:

ros2 topic echo /touch_sensor

Check Topic Info

ros2 topic info /touch_sensor

Example Output

When you touch the sensors, you should see:

Publisher Terminal:

[INFO] [touch_publisher]: Touch Publisher Node Started
[INFO] [touch_publisher]: Publishing: Front
[INFO] [touch_publisher]: Publishing: Front,Left

Subscriber Terminal:

[INFO] [touch_subscriber]: Touch Subscriber Node Started
[INFO] [touch_subscriber]: Waiting for touch sensor data...
[INFO] [touch_subscriber]: Touch detected: Front
[INFO] [touch_subscriber]:   -> Action: Move Forward!
[INFO] [touch_subscriber]: Touch detected: Front,Left
[INFO] [touch_subscriber]:   -> Action: Move Forward!
[INFO] [touch_subscriber]:   -> Action: Turn Left!

ROS2 Architecture Diagram

┌─────────────────────┐         ┌─────────────────────┐
│   Touch Publisher   │         │   Touch Subscriber  │
│                     │         │                     │
│  ┌───────────────┐  │         │  ┌───────────────┐  │
│  │ GPIO Read     │  │         │  │ Callback      │  │
│  │ Touch Sensors │  │         │  │ Function      │  │
│  └───────┬───────┘  │         │  └───────▲───────┘  │
│          │          │         │          │          │
│  ┌───────▼───────┐  │  Topic  │  ┌───────┴───────┐  │
│  │  Publisher    │──┼────────►│──│  Subscriber   │  │
│  │  (10 Hz)      │  │/touch_  │  │               │  │
│  └───────────────┘  │ sensor  │  └───────────────┘  │
└─────────────────────┘         └─────────────────────┘

Exercises

Exercise 1: Add Timestamp

Modify the publisher to include a timestamp with each message using builtin_interfaces/Time.

Exercise 2: Custom Message Type

Create a custom message type TouchState.msg with boolean fields for each sensor:

bool front
bool back
bool left
bool right

Exercise 3: Multi-Touch Counter

Modify the subscriber to count how many times each sensor has been touched and display statistics.

Exercise 4: Integration with Dance

Combine this with Lab5 (Dance) to trigger dance moves when specific touch patterns are detected.

Troubleshooting

Issue Solution
Package not found Run source ~/ros2_ws/install/setup.bash
GPIO permission denied Run with sudo or add user to gpio group
No messages received Check both nodes are running and topic names match
Build fails Check setup.py entry_points syntax

Summary

In this lab, you learned:

  • How to create a ROS2 Python package
  • How to write a publisher node
  • How to write a subscriber node
  • How to build and run ROS2 nodes
  • How to use ros2 topic commands for debugging
  • How to integrate hardware (touch sensors) with ROS2

Reference