Scenario 3: Sensor Fusion Pipeline#
Domain#
An autonomous vehicle perception stack fuses data from a camera and a LiDAR sensor. Each sensor publishes at a different rate. A fusion node combines the latest readings from both sensors and publishes a fused report. A logger node records the fused output for offline analysis.
System Architecture
Your system must contain the following nodes and topics.
Nodes
Node |
Description |
|---|---|
|
Publishes |
|
Publishes |
|
Subscribes to |
|
Subscribes to |
|
Subscribes to |
|
Publishes |
Topics
Topic |
Message Type |
QoS |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Specific Requirements
In addition to the common requirements in the main GP 1 page:
QoS – TRANSIENT_LOCAL: The
config_publisherpublishes a single configuration message withTRANSIENT_LOCALdurability. Thefusion_nodeandsafety_monitorsubscribe to/system/configwith matchingTRANSIENT_LOCALdurability and use the configuration values. Demonstrate that nodes starting after the config publisher still receive the config message.QoS – Intentional mismatch: Create a test subscriber (can be in the
safety_monitoror a separate debug node) that subscribes to/sensors/camerawithRELIABLEreliability. The camera publisher usesBEST_EFFORT. This is incompatible and will receive no data. Add a comment block explaining the mismatch and howros2 topic info /sensors/camera -vreveals it.QoS – Sensor depth 1: Both sensor topics use depth 1. This means only the most recent message is buffered. If the fusion node’s callback is slow, intermediate messages are dropped. Document this design choice in
README.md.Fusion node callback groups: The
fusion_nodemust use aMultiThreadedExecutor. The camera and LiDAR subscription callbacks must be in aMutuallyExclusiveCallbackGroupbecause they both write to shared state (the latest camera frame and LiDAR distance). The fused-output publishing timer must be in a separateReentrantCallbackGroupso it can fire on schedule without waiting for a sensor callback to finish.Safety monitor: The
safety_monitorsubscribes to/perception/fused, parses the message, and checks the LiDAR distance. If distance < 2.0 m, it publishes an alert on/perception/alertsand logs withself.get_logger().warn(). Useself.get_logger().info()for normal fused output. The subscription callback and alert publisher can share aMutuallyExclusiveCallbackGroupsince they access the same alert state.Launch files:
system.launch.py: starts the config publisher, both sensor nodes, the fusion node, and the safety monitor.enable_loggerargument (defaultfalse): conditionally starts theloggernode.Sensor nodes (
camera_nodeandlidar_node) grouped in aGroupAction.An
alert_thresholdargument (default2.0) that is passed to the safety monitor as a ROS parameter override (--ros-args -p).
Expected Behavior
Normal operation:
[INFO] [<timestamp>] [config_publisher]: Published system config: {"fusion_rate": 5, "alert_threshold": 2.0}
[INFO] [<timestamp>] [camera_node]: Publishing frame_0001
[INFO] [<timestamp>] [lidar_node]: Publishing distance: 15.3
[INFO] [<timestamp>] [fusion_node]: Fused -- camera: frame_0001, lidar: 15.3 m
[INFO] [<timestamp>] [fusion_node]: Fused -- camera: frame_0005, lidar: 8.7 m
Obstacle detected:
[INFO] [<timestamp>] [fusion_node]: Fused -- camera: frame_0042, lidar: 1.3 m
[WARNING] [<timestamp>] [safety_monitor]: Obstacle at 1.3 m (threshold: 2.0 m)
Late-joining node receives config:
Start the system, then manually start a new subscriber to
/system/config:
ros2 topic echo /system/config --once
The config message should be received immediately.
Verification commands:
ros2 launch group<N>_gp1 system.launch.py
ros2 launch group<N>_gp1 system.launch.py enable_logger:=true
ros2 launch group<N>_gp1 system.launch.py alert_threshold:=5.0
ros2 topic list -t
ros2 topic echo /perception/fused
ros2 topic echo /perception/alerts
ros2 topic hz /sensors/camera
ros2 topic info /sensors/camera -v
rqt_graph
Scenario 3 Grading Rubric
This rubric details how the 50 points from the common rubric map to Scenario 3 deliverables.
Component |
Pts |
Criteria |
|---|---|---|
Node Implementation (16 pts) |
||
|
2 |
Publishes |
|
2 |
Publishes |
|
4 |
Subscribes to |
|
3 |
Subscribes to |
|
2 |
Publishes JSON config |
|
1 |
Subscribes to |
Spinning and lifecycle |
2 |
Proper |
QoS (10 pts) |
||
Sensor topics QoS |
2 |
Camera and LiDAR use |
Fused and alerts QoS |
2 |
|
TRANSIENT_LOCAL demo |
3 |
|
Intentional mismatch |
3 |
|
Launch Files (10 pts) |
||
|
4 |
Starts config publisher, both sensors, fusion node, and
safety monitor. All nodes use |
|
2 |
|
|
2 |
|
Sensor |
2 |
Camera and LiDAR nodes grouped in a |
Executors and Callback Groups (8 pts) |
||
Fusion node executor |
2 |
Uses |
Sensor subscriptions group |
3 |
Camera and LiDAR callbacks in a
|
Fused output timer group |
3 |
Fused publisher timer in a |
Documentation and Quality (6 pts) |
||
README.md |
3 |
Group members, scenario summary, node graph, design decisions (including depth-1 sensor choice), build/run instructions. |
Code quality |
3 |
Type hints, docstrings, ROS 2 logger with correct severity levels, consistent naming, no linting errors. |
TOTAL |
50 |