From 2b928d55e354b163fdd9c36631d33d19407e74e2 Mon Sep 17 00:00:00 2001 From: Arjo Chakravarty Date: Tue, 21 Jul 2026 07:24:18 +0000 Subject: [PATCH 1/3] fix(qos): align QoS reliability, durability, and history policies across nodes Signed-off-by: Arjo Chakravarty --- bevy_ros2/src/lib.rs | 4 ++-- path_server/rmf_path_server/src/lib.rs | 4 ++-- .../rmf_path_server/tests/test_map_subscription.rs | 3 ++- .../rmf_path_server_test/test/test_path_server_follow.py | 7 ++++--- .../test/test_path_server_scenario.py | 9 +++++---- path_server/rmf_plan_executor/src/main.rs | 4 ++-- .../rmf_reservation_tests/test/test_overlap.py | 1 + .../rmf_reservation_tests/test/test_path_server.py | 3 ++- .../rmf_reservation_tests/test/test_queue.py | 1 + .../rmf_reservation_tests/test/test_reservation.py | 1 + .../rmf_reservation_tests/test/test_safe_sets.py | 1 + .../rmf_mock_robot_sim/mock_robot_sim_node.py | 5 +++-- rmf_participant_discovery/src/lib.rs | 2 +- 13 files changed, 27 insertions(+), 18 deletions(-) diff --git a/bevy_ros2/src/lib.rs b/bevy_ros2/src/lib.rs index 0ec5779..4c83fc7 100644 --- a/bevy_ros2/src/lib.rs +++ b/bevy_ros2/src/lib.rs @@ -75,7 +75,7 @@ impl RosSubscription { let subscriber = node .create_subscription( - topic.clone().reliable().transient_local().keep_all(), + topic.clone().reliable().transient_local().keep_last(10), move |msg: T| { debug!("Received a message on [{}]: {:?}", topic, msg); *data_clone.lock().unwrap() = Some(msg.clone()); @@ -110,7 +110,7 @@ impl RosPublisher { pub fn new_transient_local(node: &Arc, topic: String) -> Self { let publisher = node - .create_publisher(topic.reliable().transient_local().keep_all()) + .create_publisher(topic.reliable().transient_local().keep_last(10)) .unwrap(); Self { publisher } diff --git a/path_server/rmf_path_server/src/lib.rs b/path_server/rmf_path_server/src/lib.rs index abaab44..0022cde 100644 --- a/path_server/rmf_path_server/src/lib.rs +++ b/path_server/rmf_path_server/src/lib.rs @@ -537,7 +537,7 @@ pub fn start_path_server( let footprints_clone2 = Arc::clone(&footprints_clone); let list_subscription = discovery_worker .create_subscription::( - "/destination/discovery", + "/destination/discovery".transient_local().reliable(), move |_server: &mut DiscoveryServer

, msg: rmf_prototype_msgs::msg::ParticipantList| { if let Ok(mut map) = footprints_clone2.lock() { @@ -590,7 +590,7 @@ pub fn start_path_server( let odom_sub = match server .destinations_worker .create_subscription::( - odom_topic.as_str(), + odom_topic.as_str().reliable(), move |dest_server: &mut PlanServer

, odom_msg: Odometry| { dest_server.handle_odometry(&robot_id_clone2, odom_msg); }, diff --git a/path_server/rmf_path_server/tests/test_map_subscription.rs b/path_server/rmf_path_server/tests/test_map_subscription.rs index accab0b..0781d3f 100644 --- a/path_server/rmf_path_server/tests/test_map_subscription.rs +++ b/path_server/rmf_path_server/tests/test_map_subscription.rs @@ -72,7 +72,8 @@ fn test_map_subscription() -> Result<(), Box> { let robot_id = "robot1"; let odom_topic = format!("{}/odom", robot_id); - let odom_pub = test_node.create_publisher::(odom_topic.as_str())?; + let odom_pub = test_node + .create_publisher::(odom_topic.as_str().reliable())?; let dest_topic = format!("{}/destination", robot_id); let dest_pub = test_node .create_publisher::(dest_topic.as_str().transient_local().reliable())?; diff --git a/path_server/rmf_path_server_test/test/test_path_server_follow.py b/path_server/rmf_path_server_test/test/test_path_server_follow.py index 535af8a..3f6db24 100644 --- a/path_server/rmf_path_server_test/test/test_path_server_follow.py +++ b/path_server/rmf_path_server_test/test/test_path_server_follow.py @@ -176,11 +176,11 @@ def r2_plan_cb(msg): self.node.create_subscription( Odometry, 'robot_1/odom', r1_odom_cb, - qos_profile=reliable_transient_qos + qos_profile=10 ) self.node.create_subscription( Odometry, 'robot_2/odom', r2_odom_cb, - qos_profile=reliable_transient_qos + qos_profile=10 ) self.node.create_subscription( Plan, 'robot_1/plan', r1_plan_cb, @@ -207,7 +207,8 @@ def r2_plan_cb(msg): discovery_qos = QoSProfile( depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL, - history=HistoryPolicy.KEEP_LAST + history=HistoryPolicy.KEEP_LAST, + reliability=ReliabilityPolicy.RELIABLE, ) discovery_pub = self.node.create_publisher( diff --git a/path_server/rmf_path_server_test/test/test_path_server_scenario.py b/path_server/rmf_path_server_test/test/test_path_server_scenario.py index ebf93b5..ac2faed 100644 --- a/path_server/rmf_path_server_test/test/test_path_server_scenario.py +++ b/path_server/rmf_path_server_test/test/test_path_server_scenario.py @@ -21,7 +21,7 @@ from nav_msgs.msg import Odometry import pytest import rclpy -from rclpy.qos import DurabilityPolicy, HistoryPolicy, QoSProfile +from rclpy.qos import DurabilityPolicy, HistoryPolicy, QoSProfile, ReliabilityPolicy from rmf_prototype_msgs.msg import ( Destination, DestinationConstraints, @@ -141,14 +141,14 @@ def r2_cb(msg): Odometry, 'robot_1/odom', r1_cb, - 10 + qos_profile=10 ) self.node.create_subscription( Odometry, 'robot_2/odom', r2_cb, - 10 + qos_profile=10 ) # Publishers for destinations @@ -174,7 +174,8 @@ def r2_cb(msg): discovery_qos = QoSProfile( depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL, - history=HistoryPolicy.KEEP_LAST + history=HistoryPolicy.KEEP_LAST, + reliability=ReliabilityPolicy.RELIABLE, ) discovery_pub = self.node.create_publisher( diff --git a/path_server/rmf_plan_executor/src/main.rs b/path_server/rmf_plan_executor/src/main.rs index 1bb2c1d..ced6991 100644 --- a/path_server/rmf_plan_executor/src/main.rs +++ b/path_server/rmf_plan_executor/src/main.rs @@ -59,7 +59,7 @@ fn main() -> Result<(), Box> { "/destination/discovery" .transient_local() .reliable() - .keep_all(), + .keep_last(10), move |executor: &mut PlanExecutor, msg: ParticipantList| { let (added, removed) = tracker.update(&msg); for robot_id in removed { @@ -91,7 +91,7 @@ fn main() -> Result<(), Box> { let plan_topic = robot_id.to_string() + "/plan"; let odom_sub = match server.executor_worker.create_subscription::( - odom_topic.as_str(), + odom_topic.as_str().reliable(), move |executor: &mut PlanExecutor, msg: Odometry| { executor.handle_odometry(&robot_id_clone, msg); }, diff --git a/reservation_system/rmf_reservation_tests/test/test_overlap.py b/reservation_system/rmf_reservation_tests/test/test_overlap.py index 03c2e1d..03d2d90 100644 --- a/reservation_system/rmf_reservation_tests/test/test_overlap.py +++ b/reservation_system/rmf_reservation_tests/test/test_overlap.py @@ -117,6 +117,7 @@ def test_overlapping_reservation(self): depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL, history=HistoryPolicy.KEEP_LAST, + reliability=ReliabilityPolicy.RELIABLE, ) discovery_pub = self.node.create_publisher( ParticipantList, diff --git a/reservation_system/rmf_reservation_tests/test/test_path_server.py b/reservation_system/rmf_reservation_tests/test/test_path_server.py index ecdd80c..ca763dc 100644 --- a/reservation_system/rmf_reservation_tests/test/test_path_server.py +++ b/reservation_system/rmf_reservation_tests/test/test_path_server.py @@ -118,6 +118,7 @@ def test_plan_generation_and_publication(self): depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL, history=HistoryPolicy.KEEP_LAST, + reliability=ReliabilityPolicy.RELIABLE, ) discovery_pub = self.node.create_publisher( ParticipantList, @@ -142,7 +143,7 @@ def test_plan_generation_and_publication(self): odom = self.create_odom(0.0, 0.0) start_time = time.time() - timeout = 8.0 + timeout = 15.0 while time.time() - start_time < timeout: discovery_pub.publish(parts) dest_pub.publish(dest) diff --git a/reservation_system/rmf_reservation_tests/test/test_queue.py b/reservation_system/rmf_reservation_tests/test/test_queue.py index 1947944..33d6e43 100644 --- a/reservation_system/rmf_reservation_tests/test/test_queue.py +++ b/reservation_system/rmf_reservation_tests/test/test_queue.py @@ -78,6 +78,7 @@ def setUpClass(cls): depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL, history=HistoryPolicy.KEEP_LAST, + reliability=ReliabilityPolicy.RELIABLE, ) cls.destinations = {name: [] for name in ROBOT_NAMES} diff --git a/reservation_system/rmf_reservation_tests/test/test_reservation.py b/reservation_system/rmf_reservation_tests/test/test_reservation.py index e25e497..c0e2da3 100644 --- a/reservation_system/rmf_reservation_tests/test/test_reservation.py +++ b/reservation_system/rmf_reservation_tests/test/test_reservation.py @@ -105,6 +105,7 @@ def test_single_reservation(self): depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL, history=HistoryPolicy.KEEP_LAST, + reliability=ReliabilityPolicy.RELIABLE, ) discovery_pub = self.node.create_publisher( ParticipantList, diff --git a/reservation_system/rmf_reservation_tests/test/test_safe_sets.py b/reservation_system/rmf_reservation_tests/test/test_safe_sets.py index beb1da4..7cb4148 100644 --- a/reservation_system/rmf_reservation_tests/test/test_safe_sets.py +++ b/reservation_system/rmf_reservation_tests/test/test_safe_sets.py @@ -87,6 +87,7 @@ def setUpClass(cls): depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL, history=HistoryPolicy.KEEP_LAST, + reliability=ReliabilityPolicy.RELIABLE, ) # Per-robot message buffers and publishers. diff --git a/rmf_mock_robot_sim/rmf_mock_robot_sim/mock_robot_sim_node.py b/rmf_mock_robot_sim/rmf_mock_robot_sim/mock_robot_sim_node.py index d86dc77..db9653e 100644 --- a/rmf_mock_robot_sim/rmf_mock_robot_sim/mock_robot_sim_node.py +++ b/rmf_mock_robot_sim/rmf_mock_robot_sim/mock_robot_sim_node.py @@ -81,7 +81,8 @@ def __init__(self): discovery_qos = QoSProfile( depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL, - history=HistoryPolicy.KEEP_LAST + history=HistoryPolicy.KEEP_LAST, + reliability=ReliabilityPolicy.RELIABLE, ) reliable_transient_qos = QoSProfile( @@ -95,7 +96,7 @@ def __init__(self): self.odom_pub = self.create_publisher( Odometry, f'{self.robot_name}/odom', - qos_profile=reliable_transient_qos + qos_profile=10 ) self.progress_pub = self.create_publisher( diff --git a/rmf_participant_discovery/src/lib.rs b/rmf_participant_discovery/src/lib.rs index 89d4fac..92d4ee9 100644 --- a/rmf_participant_discovery/src/lib.rs +++ b/rmf_participant_discovery/src/lib.rs @@ -83,7 +83,7 @@ where { let mut tracker = ParticipantTracker::new(); worker.create_subscription::( - topic.transient_local().reliable().keep_all(), + topic.transient_local().reliable().keep_last(10), move |server: &mut T, msg: ParticipantList| { let (added, removed) = tracker.update(&msg); From 0f214aa29b2e5cfb84d638aace98b4ff1bb1be9e Mon Sep 17 00:00:00 2001 From: Arjo Chakravarty Date: Tue, 21 Jul 2026 09:01:48 +0000 Subject: [PATCH 2/3] ci: add cyclone dds to test matrix alongside fastrtps Signed-off-by: Arjo Chakravarty --- .github/workflows/ci.yml | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee381e2..cfd3079 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,17 +2,25 @@ name: Next-Gen Prototype CI on: push: - branches: [ main ] + branches: [ main, epic/next-gen-base ] pull_request: - branches: [ main ] + branches: [ main, epic/next-gen-base ] + workflow_dispatch: jobs: ci-check: - name: Colcon Overlay Build & Test Suite + name: Colcon Overlay Build & Test Suite (${{ matrix.rmw_implementation }}) runs-on: ubuntu-latest container: image: ghcr.io/open-rmf/ros2_rust_base:jazzy + strategy: + fail-fast: false + matrix: + rmw_implementation: + - rmw_fastrtps_cpp + - rmw_cyclonedds_cpp + steps: - name: Checkout Prototype Repository uses: actions/checkout@v4 @@ -25,6 +33,9 @@ jobs: source /opt/ros/jazzy/setup.bash rosdep update apt update + if [ "${{ matrix.rmw_implementation }}" = "rmw_cyclonedds_cpp" ]; then + apt install -y ros-jazzy-rmw-cyclonedds-cpp + fi rosdep install --from-paths . --ignore-src -y --skip-keys="rclrs ament_cargo ament_python" colcon build shell: bash @@ -34,6 +45,9 @@ jobs: run: | source /opt/ros/jazzy/setup.bash source install/setup.bash + export ROS_LOCALHOST_ONLY=1 + export RMW_IMPLEMENTATION=${{ matrix.rmw_implementation }} colcon test --executor sequential --packages-select rmf_path_server_test rmf_reservation_tests --event-handlers console_direct+ colcon test-result --all shell: bash + From 13c30fb286bd0135b6c4f993f1f34d6e48d28033 Mon Sep 17 00:00:00 2001 From: Arjo Chakravarty Date: Wed, 22 Jul 2026 01:49:14 +0000 Subject: [PATCH 3/3] Fix QoS setting in demo server Signed-off-by: Arjo Chakravarty --- .../rmf_path_server_demo/rmf_path_server_demo/robot_spawner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path_server/rmf_path_server_demo/rmf_path_server_demo/robot_spawner.py b/path_server/rmf_path_server_demo/rmf_path_server_demo/robot_spawner.py index 5b5e6eb..2e38656 100644 --- a/path_server/rmf_path_server_demo/rmf_path_server_demo/robot_spawner.py +++ b/path_server/rmf_path_server_demo/rmf_path_server_demo/robot_spawner.py @@ -410,7 +410,7 @@ def odom_callback(msg, r_name=name): Odometry, f'{name}/odom', odom_callback, - qos_profile=self.reliable_transient_qos + qos_profile=10 ) def subscribe_to_plan(self, name):