###### tags: `Robotics` # ROS2 QoS ## Overview 來玩玩 ROS2 的 QoS 設定囉,Qos 讓使用者可以根據自己的應用微調傳輸行為,也能設定使用者定義的 callback function,來接收來自 ROS2 的事件。 若要使用到完整 QoS 建議使用最新版 ROS2 (目前為 Foxy Fitzroy),雖然某些 QoS 在 Dashing 就已經引入,但因為不同 DDS 版本的 rmw_implementation 的實作進度都不一樣,舊版的 ROS2 可能會直接忽略某些 QoS 設定,例如 cyclonedds 在 Dashing 釋出版本中就不支援 deadline, lifespan, liveliness。這裡的不支援指的是對 ROS2 而言,底層的 DDS 可能早就有實作出來,只是沒有對接到 ROS2 層。 目前 ROS2 提供的 QoS 整理如下 | Policy | Description | | -------- | ----------- | | History | ==Keep last==: only store up to N samples, configurable via the queue <ins>**depth**</ins> option. <p></p> ==Keep all==: store all samples, subject to the configured resource limits of the underlying middleware. | Depth | Depth of history queue when specifying **Keep last** | | Reliability | ==Best effort==: attempt to deliver samples, but may lose them if the network is not robust. <p></p> ==Reliable==: guarantee that samples are delivered, may retry multiple times. | Durability | ==Transient local==: the publisher becomes responsible for persisting samples for “<ins>late-joining</ins>” subscribers. <p></p> ==Volatile==: no attempt is made to persist samples. | Dashing 新加入的 QoS | Policy | Description | | -------- | ----------- | | Deadline | Duration: the expected maximum amount of time between subsequent messages being published to a topic | Lifespan | 傳送的訊息可以存活多久。Duration: the maximum amount of time between the publishing and the reception of a message without the message being considered stale or expired (expired messages are silently dropped and are effectively never received). | | Liveliness | 設定 Lease Duration,超過一定的時間就將 publisher 認定為下線 <br><br> ==Automatic==: the system will consider all of the node’s publishers to be alive for another “lease duration” when any one of its publishers has published a message. <br><br> ==Manual by topic==: the system will consider the publisher to be alive for another “lease duration” if it manually asserts that it is still alive (via a call to the publisher API). | :::info 以上 ROS2 支援的 QoS, 事實上只佔 DDS 標準 (OMG DDS 1.4) 中定義的一小部份。 而每種 DDS 實作支援的 QoS ==參數==也不盡相同,標準中只要求所有 DDS 都必須實作出 Minimum profile 定義的 QoS 參數。 ::: ### Request-Offer Model ![](https://i.imgur.com/obDAyQM.png) 要讓 Publisher 和 Subscriber 能建立連線,兩者設定的 QoS 必須要相容,DDS 採用的是 Request-Offer 的模型,簡單來說 Publisher 提供的通訊等級必須大於或等於 Subscriber 要求的。詳細的相容表可參考 ROS2 官方文件。 https://index.ros.org/doc/ros2/Concepts/About-Quality-of-Service-Settings/#qos-compatibilities 舉例來說 Reliability QoS 之中若使用 Reliable 會確保訊息一定會被對方收到,而 Best Effort 則不會管這件事。所以直覺上來說 Reliable 提供的等級會比 Best Effort 來的高,如果 Publisher 只能提供 Best Effort 但是 Subscriber 卻要求 Reliable 的傳輸,這時候兩者就不會相容。 | Publisher QoS | Subscriber QoS| Compatible? | | --------- | -------- | -------- | | Reliable | Reliable | :heavy_check_mark: | | Reliable | Best Effort | :heavy_check_mark: | | Best Effort | Reliable | :x: | | Best Effort | Best Effort | :heavy_check_mark: | ### Lifespan ROS2 文件的定義 > Duration: the maximum amount of time between the publishing and the reception of a message without the message being considered stale or expired (expired messages are silently dropped and are effectively never ## QoS Profiles ROS2 為四種類型的通訊提供了預設的 QoS ### Default profile ROS2 Publisher, Subscriber 預設使用的 QoS | Policy | Description | | -------- | ----------- | | History | Keep Last | | Depth | 10 | | Reliability | Reliable | | Durability | Volatile | 其他 ROS2 預設提供的 QoS: ### Services / Clients Services, Clients 為 ROS2 的遠端程序呼叫 (RPC) 的 Server 和 Client,關於 ROS2 的通訊模型的簡介[可參考這篇文章](https://pojenlai.wordpress.com/2012/11/03/ros-topic-service-and-actionlib/)。ROS2 中有為 Service 設定預設使用 QoS。(跟 default profile 完全一樣) ### Sensor data 有一些資料比起確實傳到對方,應用程式會想在最短的時間內獲得資料,或者是允許中途失去資料,如影像、Lidar 等資訊,可以直些套用 Sensor Data Profile 定義的 QoS。 ### Parameter ### ROS2 QoS Definition 以下為 ROS2 幾個預先定義的 QoS Profile ```c++ SensorDataQoS::SensorDataQoS(const QoSInitialization & qos_initialization) : QoS(qos_initialization, rmw_qos_profile_sensor_data) {} ParametersQoS::ParametersQoS(const QoSInitialization & qos_initialization) : QoS(qos_initialization, rmw_qos_profile_parameters) {} ServicesQoS::ServicesQoS(const QoSInitialization & qos_initialization) : QoS(qos_initialization, rmw_qos_profile_services_default) {} ParameterEventsQoS::ParameterEventsQoS(const QoSInitialization & qos_initialization) : QoS(qos_initialization, rmw_qos_profile_parameter_events) {} SystemDefaultsQoS::SystemDefaultsQoS(const QoSInitialization & qos_initialization) : QoS(qos_initialization, rmw_qos_profile_system_default) {} ``` ## Examples ### rclcpp [rclcpp QoS API Reference](http://docs.ros2.org/latest/api/rclcpp/classrclcpp_1_1QoS.html) OSRF 推薦使用 `rclcpp::QoS` 的類別控制 ROS2 Publisher, Subscriber 的 QoS。 `rclcpp::QoS` 的 constructor 的參數為 Depth,要設定其他 QoS 請使用 cascading 的程式語言技巧,如以下範例指定 Reliabilty 為 Reliable; Durability 為 Transient Local ```cpp rclcpp::QoS(10).reliable().transient_local() ``` 以下示範設定 Publisher, Subscriber 的 QoS,Publisher 設定 Reliable,Subscriber 設定 Best Effort #### Publisher ```cpp #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" class Talker : public rclcpp::Node { public: Talker() : Node("talker") { pub_ = this->create_publisher<std_msgs::msg::String>("chatter", rclcpp::QoS(10).reliable()) } rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_; }; ``` #### Subscriber ```cpp #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" // Create a Listener class that subclasses the generic rclcpp::Node base class. // The main function below will instantiate the class as a ROS node. class Listener : public rclcpp::Node { public: Listener() : Node("listener") { sub_ = this->create_subscription<std_msgs::msg::String>( "chatter", rclcpp::QoS(10).best_effort(), std::bind(&Listener::callback, this, std::placeholders::_1)); } private: void callback(const std_msgs::msg::String::SharedPtr msg) { RCLCPP_INFO(this->get_logger(), "I heard: [%s]", msg->data.c_str()); } rclcpp::Subscription<std_msgs::msg::String>::SharedPtr sub_; }; ``` 如果設定其他預設的 QoS 只需要帶入第二個參數,如 `rclcpp::SensorDataQoS()` 物件等。 ### rclpy #### Resources and sample code - [rclpy QoS API Reference](http://docs.ros2.org/latest/api/rclpy/api/qos.html) - 官方寫的範例 code - https://github.com/ros2/examples/tree/master/rclpy/topics (簡單設定 Depth) - https://github.com/ros2/demos/tree/master/demo_nodes_py/demo_nodes_py/topics (設定 QoS) :::warning 若要寫一個使用 rclpy 開發的 package,可以參考上面兩個範例的目錄結構,注意到 setup.py 和 __init__.py 這兩個檔案是必須要建立的,這樣才能夠用 `ros2 run` 命令執行你寫的 python 程式。 ::: 使用 `rclpy.qos.QoSProfile` 類別設定 QoS,以下跟剛剛 rclcpp 的範例一樣建立一個 QoSProfile,並設定 Depth 為 10,Reliability 為 Reliable,Durability 為 Transient Local。要看所有可使用的參數請查閱 rclpy API。 ```py qos = QoSProfile( depth=10, reliability=QoSReliabilityPolicy.RMW_QOS_POLICY_RELIABILITY_RELIABLE, durability=QoSDurabilityPolicy.RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL) ``` 建立好 QoSProfile 就可以帶入 Publisher 或是 Subscriber 囉,以下程式碼示範在 python 使用 ROS2 建立一個 Subscriber 並套用剛剛設定的 QoS ```python import rclpy from rclpy.qos import QoSProfile from rclpy.qos import QoSReliabilityPolicy from rclpy.qos import QoSDurabilityPolicy from rclpy.node import Node from std_msgs.msg import String class MinimalSubscriber(Node): def __init__(self): super().__init__('minimal_subscriber') qos = QoSProfile( depth=10, reliability=QoSReliabilityPolicy.RMW_QOS_POLICY_RELIABILITY_RELIABLE, durability=QoSDurabilityPolicy.RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL) self.subscription = self.create_subscription( String, 'chatter', self.listener_callback, qos) def listener_callback(self, msg): self.get_logger().info('I heard: "%s"' % msg.data) ``` ## Why should I care about QoS? 1. Performance 2. Behaviour ## References - https://index.ros.org/doc/ros2/Concepts/About-Quality-of-Service-Settings/ - Quality of Service Policies for ROS2 Communications: https://roscon.ros.org/2019/talks/roscon2019_qos.pdf - https://www.slideshare.net/thomas.moulard/roscon-fr-is-ros-2-ready-for-production - https://design.ros2.org/articles/qos_deadline_liveliness_lifespan.html