Exercises#
This page contains four take-home exercises that reinforce the concepts from Lecture 10. Each exercise asks you to write code from scratch based on a specification – no starter code is provided.
All files should be created inside your ~/enpm605_ws/src/ workspace
using the appropriate demo packages.
Exercise 1 – Parameter-Driven Controller
Goal
Build a node that uses parameters to configure a simulated proportional controller, with runtime parameter updates and a YAML configuration file.
Specification
Create the file param_demo/param_demo/controller_node.py that
implements the following.
``ControllerNode(Node)`` class:
__init__(self): callssuper().__init__("controller"), declares the following parameters with descriptors:kp(float, default1.0): proportional gain with aFloatingPointRangeof[0.0, 50.0].setpoint(float, default10.0): target value.update_rate(float, default2.0): timer frequency in Hz.
Registers a parameter callback that logs every parameter change and rejects negative
kpvalues.Creates a timer at
update_rateHz that simulates a control loop.
Timer callback
_control_loop(self):Reads the current
kpandsetpointvalues.Computes
error = setpoint - self._current_valuewhereself._current_valuestarts at0.0and is updated each tick:self._current_value += kp * error * dt.Logs the current value, error, and control output.
YAML parameter file
config/controller_params.yaml:/controller: ros__parameters: kp: 2.5 setpoint: 20.0 update_rate: 5.0
Register the entry point in
setup.pyand install the config directory.
Expected behavior
The node starts and converges toward the setpoint.
Changing
kpviaros2 param setimmediately affects the convergence rate.Setting
kpto a negative value is rejected by the callback.
Verification
# Start with YAML config
ros2 run param_demo controller_node --ros-args --params-file \
~/enpm605_ws/src/param_demo/config/controller_params.yaml
# In another terminal, modify kp
ros2 param set /controller kp 5.0
# Try an invalid value
ros2 param set /controller kp -1.0
# Inspect all parameters
ros2 param list /controller
ros2 param get /controller kp
Exercise 2 – Custom Interface Package
Goal
Create a complete custom interface package with a message, a service, and an action definition. Build it and verify all interfaces are accessible.
Specification
Create the package custom_interfaces (if not already present)
with the following interface definitions.
Message
msg/RobotStatus.msg:std_msgs/Header header string robot_id float64 battery_level float64[3] position bool is_active uint8 ERROR_NONE=0 uint8 ERROR_LOW_BATTERY=1 uint8 ERROR_COLLISION=2 uint8 error_code
Service
srv/SetSpeed.srv:# Request float64 linear_speed float64 angular_speed --- # Response bool success string message
Action
action/Patrol.action:# Goal geometry_msgs/Point[] waypoints float64 speed --- # Result bool completed uint32 waypoints_visited float64 total_time --- # Feedback uint32 current_waypoint_index float64 distance_to_next float64 elapsed_time
Update
CMakeLists.txtandpackage.xmlwith all required dependencies (std_msgs,geometry_msgs,action_msgs,rosidl_default_generators).
Expected behavior
After building, all three interfaces should appear in
ros2 interface list and their definitions should be viewable.
Verification
colcon build --symlink-install --packages-select custom_interfaces
source install/setup.bash
ros2 interface show custom_interfaces/msg/RobotStatus
ros2 interface show custom_interfaces/srv/SetSpeed
ros2 interface show custom_interfaces/action/Patrol
Exercise 3 – Service Server and Client
Goal
Write a service server that validates and applies speed commands, and an asynchronous client that sends requests and handles responses.
Specification
Create the following files in service_demo/.
Server
service_demo/speed_server.py:Node name:
speed_serverService name:
/set_speedService type:
custom_interfaces/srv/SetSpeedCallback validates the request:
Rejects
linear_speed > 5.0orangular_speed > 3.0(setssuccess=Falsewith an appropriate message).Otherwise sets
success=Trueand logs the applied speeds.
Async client
service_demo/speed_client.py:Node name:
speed_clientWaits for the
/set_speedservice to become available.Sends a request with
linear_speed=2.0andangular_speed=0.5.Uses
call_async()withadd_done_callback().Logs the response success status and message.
After the response, sends a second request with
linear_speed=10.0to test rejection.
Register both entry points in
setup.py.
Expected behavior
The first request succeeds; the second is rejected with a descriptive message.
The server logs each incoming request.
The client logs both responses.
Verification
# Terminal 1
ros2 run service_demo speed_server
# Terminal 2
ros2 run service_demo speed_client
# CLI test
ros2 service call /set_speed custom_interfaces/srv/SetSpeed \
"{linear_speed: 2.0, angular_speed: 0.5}"
Exercise 4 – Action Server and Client with Cancellation
Goal
Write an action server that simulates a patrol mission and an action client that sends a goal, monitors feedback, and cancels the goal mid-execution.
Specification
Create the following files in action_demo/.
Server
action_demo/patrol_server.py:Node name:
patrol_serverAction name:
/patrolAction type:
custom_interfaces/action/Patrol_goal_callback: accepts all goals with at least one waypoint; rejects goals with an empty waypoint list._cancel_callback: always accepts cancellation._execute_callback:Iterates through each waypoint in
goal.waypoints.For each waypoint, sleeps 2 seconds to simulate movement.
Publishes feedback after each waypoint (
current_waypoint_index,distance_to_next).Checks
goal_handle.is_cancel_requestedat each iteration.On completion: sets
completed=True,waypoints_visited=N,total_time.On cancellation: sets
completed=Falsewith partial results.
Client
action_demo/patrol_client.py:Node name:
patrol_clientSends a goal with 5 waypoints:
[(1,0,0), (2,1,0), (3,2,0), (4,1,0), (5,0,0)].Logs every feedback message.
After 5 seconds (using a one-shot timer), cancels the goal.
Logs the final result (completed or canceled, waypoints visited, total time).
Register both entry points in
setup.py.
Expected behavior
The server begins visiting waypoints and publishing feedback.
After 5 seconds the client cancels the goal.
The server acknowledges the cancellation and returns partial results (2-3 waypoints visited).
Both nodes log the outcome.
Verification
# Terminal 1
ros2 run action_demo patrol_server
# Terminal 2
ros2 run action_demo patrol_client
# CLI test with feedback
ros2 action send_goal /patrol custom_interfaces/action/Patrol \
"{waypoints: [{x: 1.0, y: 0.0, z: 0.0}, {x: 2.0, y: 1.0, z: 0.0}], speed: 1.0}" \
--feedback