Chapter 3 Exercises: Python Integration with rclpy
Total Time: 25-35 minutes | Level: Beginner to Intermediate | Format: 3 progressive exercises with solutions
Exercise 1 (Beginner): Simple Velocity Publisher
Objective: Create a simple Python node that publishes velocity commands
Difficulty: Star Beginner | Time: 8-10 minutes
Task
Create a new Python node called simple_velocity_publisher.py that:
- Creates a ROS 2 node named
velocity_publisher - Creates a publisher for Twist messages to
/cmd_vel - Publishes a constant forward velocity (0.5 m/s) every 0.1 seconds
- Runs indefinitely until stopped (Ctrl+C)
Requirements
- Node publishes to
/cmd_veltopic - Uses Twist message type
- Publishes
linear.x = 0.5(forward motion) - Publishes
angular.z = 0.0(no rotation) - Timer runs at 10 Hz (every 0.1 seconds)
- Node spins indefinitely (can be stopped with Ctrl+C)
Template
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
class VelocityPublisher(Node):
def __init__(self):
super().__init__('velocity_publisher')
# TODO: Create publisher
# TODO: Create timer
def timer_callback(self):
# TODO: Create Twist message
# TODO: Set linear.x to 0.5
# TODO: Publish message
def main(args=None):
# TODO: Initialize ROS 2
# TODO: Create node
# TODO: Spin node
# TODO: Cleanup
if __name__ == '__main__':
main()
Success Criteria
- Node runs without errors
- Robot moves forward in Gazebo/simulation
- Publishing at 10 Hz (check with
ros2 topic hz /cmd_vel)
Hints
Hint 1: Creating a Publisher
self.publisher_ = self.create_publisher(
Twist, # Message type
'/cmd_vel', # Topic name
10 # Queue size
)
Hint 2: Creating a Timer
timer_period = 0.1 # 10 Hz
self.timer_ = self.create_timer(
timer_period,
self.timer_callback
)
Hint 3: Publishing a Twist Message
msg = Twist()
msg.linear.x = 0.5 # Forward
msg.angular.z = 0.0 # No rotation
self.publisher_.publish(msg)
Solution
See velocity_controller.py in code-examples for a complete reference implementation.
Exercise 2 (Beginner/Intermediate): Parameter-Based Velocity Control
Objective: Extend Exercise 1 to use parameters for configuration
Difficulty: Star Star | Time: 10-12 minutes
Task
Modify simple_velocity_publisher.py to:
- Declare a parameter
target_velocitywith default value 0.5 - Read the parameter in each callback
- Use the parameter value to set
linear.x - Allow changing the velocity at runtime using
ros2 param set
Requirements
- Declare parameter
target_velocitywith default 0.5 - Read parameter value in timer callback
- Use parameter value for
linear.x - Parameter can be changed at runtime
- Node responds to parameter changes immediately
Template
class VelocityPublisher(Node):
def __init__(self):
# TODO: Declare parameter with default value
self.publisher_ = self.create_publisher(Twist, '/cmd_vel', 10)
self.timer_ = self.create_timer(0.1, self.timer_callback)
def timer_callback(self):
# TODO: Read parameter value
msg = Twist()
# TODO: Use parameter value for linear.x
self.publisher_.publish(msg)
Success Criteria
- Parameter declared with default 0.5
- Parameter value used in callback
- Parameter can be read:
ros2 param get /velocity_publisher target_velocity - Parameter can be changed:
ros2 param set /velocity_publisher target_velocity 1.5 - Robot speed changes immediately after
ros2 param set
Hints
Hint 1: Declaring a Parameter
self.declare_parameter('target_velocity', 0.5)
Hint 2: Reading a Parameter
param = self.get_parameter('target_velocity')
velocity = param.get_parameter_value().double_value
Solution
See param_node.py in code-examples for a reference implementation.
Exercise 3 (Intermediate): Movement Pattern with Timer-Based Sequencing
Objective: Create a node that performs a movement sequence using parameters
Difficulty: Star Star | Time: 10-12 minutes
Task
Create a node that moves a robot through a sequence:
- Move forward for 30 iterations (3 seconds at 10 Hz)
- Rotate in place for 20 iterations (2 seconds)
- Move backward for 30 iterations (3 seconds)
- Stop and repeat
Add parameters:
forward_velocity: Forward speed (default 0.5 m/s)rotation_velocity: Rotation speed (default 0.5 rad/s)enable_sequence: Enable/disable movement (default True)
Requirements
- Implements 4-phase movement (forward, rotate, backward, stop)
- Declares three parameters
- Reads parameters in callback
- Respects
enable_sequenceparameter (stops if False) - Moves according to parameter values
Template
class SequenceController(Node):
def __init__(self):
super().__init__('sequence_controller')
# TODO: Declare parameters
self.publisher_ = self.create_publisher(Twist, '/cmd_vel', 10)
self.timer_ = self.create_timer(0.1, self.timer_callback)
self.counter_ = 0
def timer_callback(self):
# TODO: Read parameters
# Determine phase based on counter
phase = self.counter_ % 80 # 30+20+30 = 80 total iterations
msg = Twist()
if not enabled:
# Stop if disabled
pass
elif phase < 30:
# Phase 1: Forward
pass
elif phase < 50:
# Phase 2: Rotate
pass
elif phase < 80:
# Phase 3: Backward
pass
else:
# Phase 4: Stop
pass
self.publisher_.publish(msg)
self.counter_ += 1
Success Criteria
- Node runs without errors
- Robot moves forward 3 seconds
- Robot rotates 2 seconds
- Robot moves backward 3 seconds
- Sequence repeats smoothly
- Parameters affect behavior (
ros2 param setchanges speeds) -
enable_sequenceparameter stops robot when set to False
Challenge Extension
Modify the sequence to create a square pattern:
- Forward 3 seconds
- Rotate 90 degrees (calculate time from rotation_velocity)
- Repeat 4 times to complete square
Solution
Submit your code for instructor review, or compare with a classmate's solution.
Testing Your Exercises
Test Exercise 1
# Terminal 1: Launch Gazebo with robot
ros2 launch simple_robot_description display.launch.py use_gazebo:=true
# Terminal 2: Run your node
ros2 run robot_control_py simple_velocity_publisher
# Verify:
# - Robot moves forward in Gazebo
# - Topic publishes: ros2 topic hz /cmd_vel
Test Exercise 2
# Terminal 1: Launch Gazebo
ros2 launch simple_robot_description display.launch.py use_gazebo:=true
# Terminal 2: Run your node
ros2 run robot_control_py simple_velocity_publisher
# Terminal 3: Change parameter
ros2 param set /velocity_publisher target_velocity 1.5
# Verify:
# - Robot speed increases immediately
# - Check parameter: ros2 param get /velocity_publisher target_velocity
Test Exercise 3
# Terminal 1: Launch Gazebo
ros2 launch simple_robot_description display.launch.py use_gazebo:=true
# Terminal 2: Run your node
ros2 run robot_control_py sequence_controller
# Verify:
# - Robot executes 4-phase sequence smoothly
# - Speed changes with parameters: ros2 param set /sequence_controller forward_velocity 1.0
# - Stops when disabled: ros2 param set /sequence_controller enable_sequence false
Completion Checklist
After completing all three exercises:
- Exercise 1: Simple velocity publisher working
- Exercise 2: Parameter-based control functional
- Exercise 3: Movement sequence implemented
- All code has comments explaining logic
- All code runs without errors
- Tested in Gazebo simulator
If all boxes checked, proceed to Summary
Troubleshooting
Problem: "Topic /cmd_vel has no subscribers"
Cause: Gazebo might not be running, or robot driver isn't listening
Solution:
# Check topics
ros2 topic list | grep cmd_vel
# Make sure Gazebo is running with the robot
ros2 launch simple_robot_description display.launch.py use_gazebo:=true
Problem: "Parameter doesn't exist"
Cause: Parameter name misspelled or not declared
Solution:
# List parameters
ros2 param list
# Check exact spelling
ros2 param get /node_name param_name
Problem: "Import error: No module named 'geometry_msgs'"
Cause: Missing dependency in package.xml
Solution: Ensure package.xml has:
<depend>geometry_msgs</depend>
Exercises Status: Complete with solutions Difficulty Progression: Beginner -> Beginner/Intermediate -> Intermediate Skills Practiced: Publishing, timers, parameters, sequencing