###### tags: `Robotics` # ROS2 msg src action / 2019-10-01 ## ROS2 Misc - Message: `.msg` 檔通常會放到 `/msg` 目錄 - Services: `.srv` 檔通常會放到 `/srv` 目錄 - Actions: .action - [ ] Execution model - [ ] ROS2 launch ... ### **`ROS_DOMAIN_ID`** 在同一網路內,為了不要和其他使用者的節點產生衝突,可以設定這個環境變數,以區隔其他在網路上節點。 ```bash $ export ROS_DOMAIN_ID=1 ``` `ROS_DOMAIN_ID` 的資料型態為無號數,範圍為 0~65535,當使用者沒設定這個環境變數會預設為 0,設定不同 `ROS_DOMAIN_ID` 的 shell 無法看到對方的節點、topic 等。 以下為對應 rwm impl. 中的實作 [rmw_fastrtps_shared_cpp/src/rmw_node.cpp](https://github.com/ros2/rmw_fastrtps/blob/f8aec9bf904edc2e5a4739071df83f69d538e814/rmw_fastrtps_shared_cpp/src/rmw_node.cpp#L235) ```cpp participantAttrs.rtps.builtin.domainId = static_cast<uint32_t>(domain_id); ``` ### Message .msg 檔寫法幾乎與 ROS1 相同,使用相同資料型態的命名,並相較於 ROS1 多了新的功能。`rosidl` 這個 package 會將 .msg 檔轉換成 hpp 標頭檔和 DDS IDL 檔。 .msg 格式為: ``` <type> <name> <optional_default_value> ``` Example: sensor_msgs/LaserScan.msg ``` std_msgs/Header header float32 angle_min float32 angle_max float32 angle_increment float32 time_increment float32 scan_time float32 range_min float32 range_max float32[] ranges float32[] intensities ``` ROS2 新增的功能: 1. Upper boundaries: 指定陣列大小、上界,字串長度上界 ```cpp int32[5] five_integers_array int32[<=5] up_to_five_integers_array string<=10 up_to_ten_characters_string ``` 2. Default values ```cpp uint8 x 42 int16 y -2000 string full_name "John Doe" int32[] samples [-200, -100, 0, 100, 200] ``` Message field type 與不同語言的映射可參閱 [About ROS Interfaces](https://index.ros.org/doc/ros2/Concepts/About-ROS-Interfaces/) | Type name | C++ | Python | DDS type | | -------- | --- | ------ | ------- | | bool | bool | builtins.bool | boolean | | float64 | double | builtins.float* | double | | uint64 | uint64_t | builtins.int* | unsigned long long | |...|||| | static array | std::array<T, N> | builtins.list* | T[N] | | string | std::string | builtins.str* | string | __unbounded dynamic array__ | std::vector | builtins.list | sequence | | bounded dynamic array | custom_class<T, N> | builtins.list* | sequence<T, N> | #### Write a custom message :::warning 幾項限制: 1. Custom message name (檔名) 必須以大寫開頭 2. Field names 只能有小寫、數字、和底線 ::: <details><summary>修改 package.xml 和 CMakeLists.txt</summary> <p> directory tree: ``` ros_course_demo/ ├── msg │ └── Custom.msg ├── CMakeLists.txt ├── package.xml ``` 在 package.xml 加入以下幾行: ```xml <buildtool_depend>rosidl_default_generators</buildtool_depend> <exec_depend>rosidl_default_runtime</exec_depend> ``` CMakeLists.txt: ``` rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files}) set(msg_files "msg/Custom.msg") ament_export_dependencies(rosidl_default_runtime) ``` :::info 目前在同一個 package 內編譯時無法找到標頭檔,之後再查閱 CMakeList.txt 問題。 References: https://index.ros.org/doc/ros2/Tutorials/Rosidl-Tutorial/ ::: </p> </details> <br> 使用自己定義的 message ```cpp #include "ros_course_demo/msg/custom.hpp" auto node2 = std::make_shared<rclcpp::Node>("custom node publisher"); auto pub_ = node2->create_publisher<ros_course_demo::msg::Custom>("custom_topic"); ``` ### Service Request–response communication 語法和 msg 一樣,虛線上方為 request,下方為 response Example of a srv file: ``` float64 A float64 B --- float64 Sum ``` 在 client 端使用以下函式發送 request ```cpp http://docs.ros2.org/dashing/api/rclcpp/classrclcpp_1_1Client.html#a7567297f43b72f96e8ec57fa7ff2f4e1 SharedFuture rclcpp::Client<ServiceT>::async_send_request(SharedRequest request) ``` 在 server 端註冊 callback 有兩個參數: ```cpp // callback prototype void handle_service( const std::shared_ptr<rmw_request_id_t> request_header, const std::shared_ptr<AddTwoInts::Request> request, const std::shared_ptr<AddTwoInts::Response> response) ``` :::info 範例待補 ::: ### Actions Long running Request–response communication with feedback. Actions 是設計給可能會在 server 上長時間 (幾秒甚至數分鐘) 執行的操作,除了基本的 request-response,還可以讓 server 決定是否要接受 request,回傳目前狀態 (feedback),或是 client 向 server 請求取消 request。 以下為 .action 檔的範例 [Fibonacci.action](https://github.com/ros2/example_interfaces/blob/master/action/Fibonacci.action),.action 檔分為三個部份,上面兩個部份跟 service 一樣,分別是 request (在 action 中稱為 goal) 和 response,最後一部分則是 feedback,是在回傳 result 之前 server 向 client 傳送的資料。 Example of a .action file ``` Fibonacci.action # Goal int32 order --- # Result int32[] sequence --- # Feedback int32[] sequence ``` State machine in Action: ![](https://i.imgur.com/JRFdsvX.png) 白色區塊為 active state,藉由回傳 `rclcpp_action` 定義的常數轉移狀態,以下為 `rclcpp::action` 定義的常數。 ![](https://i.imgur.com/VolhEfH.png) 三個在 server 上重要 callback (必須為 non-blocking): - handle_goal: 決定是否要接受 Request - handle_cancel: 決定是否要取消目標 (goal) - handle_accepted: give the user a handle to the goal. (因為必須是 non-blocking,比較常用的作法是用 thread 執行工作) 三個 active states: - ACCEPTED: 目標已被接受正在等待執行 - EXECUTING: 目標正在執行 - CANCELING: 取消的要求已被接受,正在處理 clean up 等動作 client 端,`async_send_goal` 和 `async_get_result` 回傳的資料型態為 `std::shared_future`,處理非同步操作,多個執行序可以呼叫 `get()` 等待結果。 ```cpp= auto action_client = rclcpp_action::create_client<Fibonacci>(g_node, "fibonacci"); auto goal_msg = Fibonacci::Goal(); goal_msg.order = 10; auto send_goal_options = rclcpp_action::Client<Fibonacci>::SendGoalOptions(); send_goal_options.feedback_callback = feedback_callback; auto goal_handle_future = action_client->async_send_goal(goal_msg, send_goal_options); ... auto result_future = action_client->async_get_result(goal_handle); ``` server 端,註冊所有 callback ```cpp auto action_server = rclcpp_action::create_server<Fibonacci>( node, "fibonacci", handle_goal, handle_cancel, handle_accepted); ``` :::info 範例待補 ::: ### Execution model The Executor coordinates the execution of all callbacks issued by these nodes by checking for available work (**timers**, **services**, **messages**, **subscriptions**, etc.) from the DDS queue and dispatching it to one or more threads.[^1] ![inheritance diagram of `Executor`](https://i.imgur.com/FHFqZOW.png) [^1]: [Analysis of rclcpp standard executor](https://micro-ros.github.io/docs/concepts/client_library/real-time_executor/#analysis-of-rclcpp-standard-executor) - SingleThreadedExecutor - MultiThreadedExecutor #### **`spin()`** API Overview | Function | Description | | -------- | -------- | | [`spin()`](http://docs.ros2.org/dashing/api/rclcpp/namespacerclcpp.html#a21e13577f5bcc5992de1d7dd08d8652b) | 在一個節點上 spin,為 **blocking** call 直到收到 `SIGINT` 訊號 | | `spin_once()` | 只執行一個可用的工作 | | [`spin_some()`](http://docs.ros2.org/dashing/api/rclcpp/namespacerclcpp.html#ad48c7a9cc4fa34989a0849d708d8f7de) | 執行所有可用的工作,但不會執行在呼叫 spin_some() 時進來的新工作[^2] | | [`spin_until_future_complete()` ](http://docs.ros2.org/dashing/api/rclcpp/namespacerclcpp.html#aa700b1a030143a2eef597d1aae519600) | 在 future (實作為 `std::shared_future`) 的結果為有效之前,持續 spin,也可以指定 timeout 時間[^3] | [^2]: https://answers.ros.org/question/296480/functional-difference-between-ros2-spin_some-spin_once-and-spin_until_future/ [^3]: http://docs.ros2.org/dashing/api/rclcpp/namespacerclcpp_1_1executors.html#ab2c1d20615d8e5023393295a23212237 #### call of rclcpp::spin() [`rclcpp::spin()`](https://github.com/ros2/rclcpp/blob/dashing/rclcpp/src/rclcpp/executors.cpp#L40) -> [`rclcpp::executors::SingleThreadedExecutor::spin` ](https://github.com/ros2/rclcpp/blob/dashing/rclcpp/src/rclcpp/executors/single_threaded_executor.cpp#L27)-> [`Executor::execute_any_executable`](https://github.com/ros2/rclcpp/blob/dashing/rclcpp/src/rclcpp/executor.cpp#L279) -> ... -> [`rcl_wait()`](https://github.com/ros2/rcl/blob/dashing/rcl/src/rcl/wait.c#L515) -> [`rmw_wait()`](https://github.com/ros2/rmw_fastrtps/blob/dashing/rmw_fastrtps_cpp/src/rmw_wait.cpp#L23) declared in [ros2/rmw/](https://github.com/ros2/rmw/blob/dashing/rmw/include/rmw/rmw.h#L810) -> [`rmw_fastrtps_shared_cpp::__rmw_wait()`](https://github.com/ros2/rmw_fastrtps/blob/dashing/rmw_fastrtps_shared_cpp/src/rmw_wait.cpp#L92)