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 topiccommands for debugging - How to integrate hardware (touch sensors) with ROS2