Requirements#
Your Tasks#
At a high level, your group must complete the following tasks. Each is spelled out in detail in the sections below.
Create two ROS 2 packages inside the pre-existing
~/enpm605_ws/src/gp2/folder:group<N>_gp2_interfaces(CMake) containing theNavigateToGoal.actiondefinition.group<N>_gp2(ament_python) containing the action server, the action client, the launch file, and the goals YAML.
Register both packages as
<exec_depend>entries in~/enpm605_ws/src/gp2_meta/package.xmlso thatcolcon build --packages-up-to gp2_metapicks them up.Define the
NavigateToGoalaction interface (goal, result, feedback) ingroup<N>_gp2_interfacesexactly as specified.Implement the action server (
navigate_to_goal_server). Port the two-phase proportional controller fromrobot_control_demo/p_controller_demo.pyinto theexecute_callback. The server subscribes to/odometry/filtered, publishes to/cmd_vel, emits feedback during execution, and returns a result on success, cancel, or failure.Implement the action client (
navigate_to_goal_client). The client’s job, in order, is:Read the three goals from
config/goals.yaml(the named blocksgoal1,goal2,goal3, each with fieldsx,y, andfinal_heading).Validate that all three goals were loaded successfully.
Wait for the action server to become available.
Task the robot to go to each goal, sequentially: send goal
i, wait for its result, log feedback and the outcome, and only then send goali+1. Never queue or dispatch in parallel.Log a final summary once all three goals have succeeded.
Write the launch file (
gp2.launch.py) that starts the server and the client, loadsgoals.yamlinto the client, and exposesgoal_toleranceandyaw_toleranceas launch arguments forwarded to the server.Document contributions in
README.md(one short paragraph per group member summarizing what they worked on).Submit by zipping the
~/enpm605_ws/src/gp2/folder and uploading it to Canvas asgroup<N>_gp2.zip(see Submission for the exact command).
The remainder of this page details each of these tasks.
Package Structure#
Your submission must contain two ROS 2 packages, both placed
inside the pre-existing ~/enpm605_ws/src/gp2/ folder. Replace
<N> with your group number.
Folder layout in the workspace:
~/enpm605_ws/src/gp2/
|-- group<N>_gp2_interfaces/
|-- group<N>_gp2/
You will zip and submit the gp2/ folder itself (see
Submission).
Package 1. Action interface (CMake):
group<N>_gp2_interfaces/
|-- action/
| |-- NavigateToGoal.action
|-- CMakeLists.txt
|-- package.xml
Package 2. Nodes and launch (ament_python):
group<N>_gp2/
|-- group<N>_gp2/
| |-- __init__.py
| |-- navigate_to_goal_server.py
| |-- navigate_to_goal_client.py
| |-- scripts/
| |-- __init__.py
| |-- main_navigate_to_goal_server.py
| |-- main_navigate_to_goal_client.py
|-- launch/
| |-- gp2.launch.py
|-- config/
| |-- goals.yaml
|-- resource/
| |-- group<N>_gp2
|-- test/
|-- package.xml
|-- setup.py
|-- setup.cfg
|-- README.md
Package metadata: both package.xml files and the
setup.py must include a meaningful description, a license
(e.g., Apache-2.0), and all group members listed as
maintainers with their email addresses.
Update the gp2_meta metapackage at
~/enpm605_ws/src/gp2_meta/package.xml. Two edits are required:
Replace the three placeholder
<maintainer>tags with your group members. The file ships with three maintainer slots (to accommodate groups of 2 or 3). Fill them with your real names and UMD emails, and delete any extra slots your group does not need.<!-- For a group of 2, delete the third line. --> <maintainer email="alice@umd.edu">Alice Smith</maintainer> <maintainer email="bob@umd.edu">Bob Jones</maintainer> <maintainer email="carol@umd.edu">Carol Lee</maintainer>
Uncomment the two
<exec_depend>lines near the bottom of the file, replacing<N>with your group number:<exec_depend>group<N>_gp2_interfaces</exec_depend> <exec_depend>group<N>_gp2</exec_depend>
Together these edits let colcon build --packages-up-to gp2_meta
pick up your packages, and make gp2_meta a valid package
attributed to your group.
Action Interface#
Define the custom action at
group<N>_gp2_interfaces/action/NavigateToGoal.action exactly as
follows:
# Goal
geometry_msgs/Point goal_position
float64 final_heading
---
# Result
bool success
float64 total_distance
float64 elapsed_time
---
# Feedback
geometry_msgs/Pose current_pose
float64 distance_remaining
Semantics:
goal_position.x/goal_position.yare the target position in theodomframe (meters).goal_position.zis ignored.final_headingis the desired yaw at the goal (radians).successisTrueif both position and orientation tolerances are satisfied,Falseon cancellation or abort.total_distanceis the cumulative path length traveled during execution (meters).elapsed_timeis the wall-clock duration of the goal (seconds).distance_remainingis the straight-line distance from the current pose to the goal position (meters).
CMake/package configuration (follow the template in
lecture10/custom_interfaces):
CMakeLists.txtmust callrosidl_generate_interfacesonaction/NavigateToGoal.actionand depend ongeometry_msgs.package.xmlmust include the<buildtool_depend>rosidl_default_generators</buildtool_depend>,<exec_depend>rosidl_default_runtime</exec_depend>,<member_of_group>rosidl_interface_packages</member_of_group>, and<depend>geometry_msgs</depend>entries.
Action Server#
Implement a node called navigate_to_goal_server that exposes an
ActionServer on the action name navigate_to_goal using the
NavigateToGoal interface. The server itself drives the
robot. Do not delegate to any other controller node.
The execute callback must:
Accept a goal in
goal_callbackonly if the goal position is reachable (e.g., accept all for this assignment, or reject on obviously invalid inputs such as NaNs). Log the decision.Subscribe to
/odometry/filteredand maintain the current(x, y, yaw)as internal state, independent of the action lifecycle.Publish to
/cmd_vel(geometry_msgs/TwistStamped) inside a 20 Hz control loop (driven by the execute callback or by a timer that runs during execution).Implement the two-phase P-controller (phase 1 = position, phase 2 = orientation) ported from
robot_control_demo/p_controller_demo.py. Use the gains and tolerances as parameters (see below).Publish feedback at a regular rate (no faster than 5 Hz, no slower than 1 Hz) containing:
current_pose(the latest pose from odometry),distance_remaining(sqrt(dx^2 + dy^2)to the goal position).
Check for cancellation each control iteration. On cancellation, send a zero-velocity stop command, call
goal_handle.canceled(), and return a result withsuccess=Falsepopulated with the partial totals.Succeed when both position and orientation tolerances are satisfied: send a zero-velocity stop command, call
goal_handle.succeed(), and return a result withsuccess=Trueand the finaltotal_distance/elapsed_time.
Server parameters (declared in __init__):
Parameter |
Default |
Description |
|---|---|---|
|
0.4 |
Proportional gain on distance (linear velocity). |
|
0.8 |
Proportional gain on heading error (phase 1). |
|
0.8 |
Proportional gain on yaw error (phase 2). |
|
0.10 |
Position tolerance (meters). |
|
0.05 |
Yaw tolerance (radians). |
Subscriptions and publications:
Direction |
Topic / Type |
Description |
|---|---|---|
Subscribes |
|
Robot pose feedback. |
Publishes |
|
Velocity commands. |
Action server |
|
Receives goals, publishes feedback, returns the result. |
Action Client#
Implement a node called navigate_to_goal_client that owns an
ActionClient on navigate_to_goal and sends three goals
sequentially.
The client must:
Declare and read the three goals from the YAML file (see Simulation and Provided Code). Each goal is a named block (
goal1,goal2,goal3) with three fields (x,y,final_heading), so the client declares nine parameters in total using dot-namespaced names such asgoal1.xandgoal1.final_heading. Validate that all three goals were loaded successfully and log them at startup.Wait for the action server to become available using
self._action_client.wait_for_server().Send goals one at a time. The client sends goal
i+1only after the result for goalihas been received (i.e., never sends in parallel and never queues ahead).Log each feedback message at
infolevel with the current pose and distance remaining, throttled to at most once per second (throttle_duration_sec=1.0).Log each result with
success,total_distance, andelapsed_time. If the result issuccess=False, abort the sequence: log an error and do not send the remaining goals.When all three goals have completed successfully, log a mission-complete summary that lists each goal and its
total_distance/elapsed_time.
Client parameters (loaded from config/goals.yaml):
Parameter |
Type |
Description |
|---|---|---|
|
|
X coordinate of goal |
|
|
Y coordinate of goal |
|
|
Desired yaw at goal |
Launch File#
Write a Python launch file at launch/gp2.launch.py that starts
both your nodes and loads the parameter file.
Required nodes:
Executable |
Package |
Notes |
|---|---|---|
|
|
Controller gains and tolerances come from launch arguments. |
|
|
Loads |
Launch file requirements:
Both nodes must use
output="screen"andemulate_tty=True.Load
config/goals.yamlfor the client node usingget_package_share_directory()and theparametersfield.Declare at least two launch arguments:
goal_tolerance(default0.10), passed to the server.yaw_tolerance(default0.05), passed to the server.
The simulation launch (
ros2 launch rosbot_gazebo gp2_world.launch.py) is not included in your launch file; the user starts it in a separate terminal.
What your launch file should do, in order:
Resolve the path to
config/goals.yamlinside the installed share directory ofgroup<N>_gp2(so it is found at runtime regardless of where the user launched from).Declare the two launch arguments
goal_toleranceandyaw_tolerancewith their default values and short descriptions.Build a
Nodeaction for the action server, forwarding the two launch arguments into itsparameters.Build a
Nodeaction for the action client, passing the resolvedgoals.yamlpath into itsparameters.Assemble a
LaunchDescriptionthat registers the two launch arguments and bothNodeactions, and return it fromgenerate_launch_description().
Lecture 8 and Lecture 10 launch files (e.g.,
parameters_demo/launch/demo3.launch.py) show each of these
pieces in isolation; use them as reference material rather than
as copy-paste templates.
README.md#
Your README.md must contain a single section describing each
group member’s contributions to the project. For every member,
list their name and a short (2 to 4 sentence) summary of what they
personally wrote, debugged, tested, or documented.
No other sections are required. Do not include system architecture diagrams, design write-ups, or build instructions in the README; keep it focused on who did what.
Code Quality#
Warning
The following are mandatory and will result in point deductions if missing.
Docstrings: Every class and every method must have a Google-style docstring.
Type hints: All method parameters and return types must have type annotations.
Inline comments: Include comments that explain non-obvious logic (e.g., quaternion conversion, two-phase transition, cancellation handling).
Naming conventions:
snake_casefor topics, services, actions, methods, and variables.CamelCasefor class names.Logging: Use the ROS 2 logger exclusively. Never use
print(). Use the appropriate severity level:info()for normal operation,warn()for recoverable issues,error()for failures.Linting: Ensure Ruff is enabled and no errors appear.