Exercises#

This page contains four take-home exercises that reinforce the concepts from Lecture 14. 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 in the appropriate packages (lifecycle_demo, bag_demo, or your own extension package).

Exercise 1 – Implement a CLI-Driven Lifecycle Node

Goal

Implement a Python lifecycle node that publishes a counter string at 2 Hz, but only while in the Active state. Drive the transitions externally with ros2 lifecycle set.


Specification

  1. Create a node counter_publisher extending LifecycleNode from rclpy_lifecycle.

  2. Allocate the publisher in on_configure using create_lifecycle_publisher for std_msgs/String on the topic /counter.

  3. Create the timer in on_activate (and call super().on_activate(state) first). Cancel it in on_deactivate.

  4. In on_cleanup, drop the publisher reference and reset the counter to 0.

  5. Publish a string of the form "count=<n>" where <n> is an incrementing integer.

Verification

  • ros2 lifecycle get /counter_publisher shows unconfigured at startup.

  • After configure then activate, ros2 topic echo /counter prints data: 'count=0', count=1, … at 2 Hz.

  • After deactivate, /counter stops publishing without the node exiting.

  • After cleanup then configure then activate, the counter restarts at 0.

Exercise 2 – Add a Programmatic Self-Cycle

Goal

Extend Exercise 1 so the node can drive its own state transitions by calling its own change_state service.


Specification

  1. Add a regular timer (not lifecycle-managed) that fires every 5.0 seconds.

  2. Maintain an index that walks through the cycle [CONFIGURE, ACTIVATE, DEACTIVATE, CLEANUP] from lifecycle_msgs.msg.Transition.

  3. In the timer callback, call the /counter_publisher/change_state service asynchronously (call_async) with the next transition id.

  4. Register a done callback on the future that logs whether result.success is True.

  5. Provide a launch file that starts the node already in self-cycling mode (no external ros2 lifecycle set calls needed).

Verification

  • Running ros2 lifecycle get /counter_publisher repeatedly shows the state advancing through Unconfigured \(\to\) Inactive \(\to\) Active \(\to\) Inactive \(\to\) Unconfigured every 5 s.

  • ros2 topic echo /counter shows messages appearing only during the Active windows.

  • The done callback log line confirms each transition succeeded.

Exercise 3 – Record and Inspect a Navigation Bag

Goal

Record a short Nav2 navigation run as an MCAP bag, inspect it with ros2 bag info, and replay it with the live simulation closed.


Specification

  1. Make sure the MCAP plugin is installed:

    sudo apt install ros-jazzy-rosbag2-storage-mcap
    
  2. In one terminal, launch the simulation:

    ros2 launch rosbot_gazebo husarion_world.launch.py rviz:=False
    
  3. In a second terminal, start recording:

    ros2 bag record -o nav_run --storage mcap \
        /odometry/filtered /scan /cmd_vel /tf /tf_static /map
    
  4. In a third terminal, launch Nav2 and send a goal:

    ros2 launch nav_demo map_nav.launch.py mode:=navigation
    
  5. After the robot reaches the goal, stop recording with Ctrl+C.

  6. Inspect the resulting bag:

    ros2 bag info nav_run
    

Verification

  • All six topics appear in the ros2 bag info output.

  • /tf_static has at least one message.

  • The bag duration matches the wall-clock duration of the run (within 1 s).

  • The nav_run directory contains a metadata.yaml and one or more .mcap files.

Exercise 4 – Visualize the Bag in Foxglove Studio

Goal

Open the recorded MCAP bag in Foxglove Studio and build a multi-panel layout that shows the LiDAR scan, the trajectory, and the velocity commands together.


Specification

  1. Install Foxglove Studio (Debian package, snap, or browser).

  2. Open nav_run_0.mcap (the file inside the bag directory, not the directory itself).

  3. Build a layout with at least three panels:

    • 3D panel: display frame map; enable /scan, /tf, and /map so the robot, its laser hits, and the static map appear together.

    • Plot panel: plot /odometry/filtered.pose.pose.position.x against ...y to draw the parametric trajectory.

    • Raw Messages panel: subscribe to /cmd_vel so you can scrub through individual TwistStamped messages.

  4. Export the layout via Layouts \(\to\) Export layout to file and save it as nav_run_layout.json inside your bag_demo package.

Verification

  • All three panels stay synchronized to the playhead while the bag plays.

  • The 3D scene shows the robot moving across the saved map and the laser hits update with each /scan message.

  • The plot draws a smooth XY trajectory matching the path the robot actually drove.

  • The exported layout reopens correctly from a fresh Foxglove session.