Browse Source

[feat]:完成到服务介绍

鱼香ROS 3 years ago
parent
commit
1017a76379
36 changed files with 945 additions and 2 deletions
  1. 1 1
      docs/_sidebar.md
  2. 5 1
      docs/humble/chapt2/advanced/2.使用面向对象方式编写ROS2节点.md
  3. 203 0
      docs/humble/chapt3/get_started/1.ROS2话题入门.md
  4. BIN
      docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803113450234.png
  5. BIN
      docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803114102048.png
  6. BIN
      docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803114705943.png
  7. BIN
      docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803114756448.png
  8. BIN
      docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803115124591.png
  9. BIN
      docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803115320265.png
  10. BIN
      docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803115726942.png
  11. BIN
      docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803115906116.png
  12. 456 0
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现.md
  13. BIN
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220604202219516.png
  14. BIN
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220604203349982.png
  15. BIN
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220604203512857.png
  16. BIN
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605145907843.png
  17. BIN
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605155135956.png
  18. BIN
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605164019985.png
  19. BIN
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605165400876.png
  20. BIN
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605165753271.png
  21. BIN
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605170006221.png
  22. BIN
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605170035287.png
  23. BIN
      docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605170254540.png
  24. 139 0
      docs/humble/chapt3/get_started/3.话题之RCLPY实现.md
  25. BIN
      docs/humble/chapt3/get_started/3.话题之RCLPY实现/imgs/image-20220605174740555.png
  26. BIN
      docs/humble/chapt3/get_started/3.话题之RCLPY实现/imgs/image-20220605175201183.png
  27. 117 0
      docs/humble/chapt3/get_started/4.ROS2服务入门.md
  28. BIN
      docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/Service-MultipleServiceClient.gif
  29. BIN
      docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/Service-SingleServiceClient.gif
  30. BIN
      docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810104048993.png
  31. BIN
      docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810113831471.png
  32. BIN
      docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810115216800.png
  33. BIN
      docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810115316799.png
  34. BIN
      docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810115428267.png
  35. BIN
      docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810115552147.png
  36. 24 0
      docs/humble/chapt3/章节导读.md

+ 1 - 1
docs/_sidebar.md

@@ -54,7 +54,7 @@
       -  [2.使用面向对象方式编写ROS2节点](humble/chapt2/advanced/2.使用面向对象方式编写ROS2节点.md) 
       -  [3.Colcon使用进阶](humble/chapt2/advanced/3.Colcon使用进阶.md) 
       -  [4.ROS2节点发现与多机通信](humble/chapt2/advanced/4.ROS2节点发现与多机通信.md) 
-  - 第 3 章 ROS2通信之话题与服务
+  - 第 3 章 ROS2节点通信之话题与服务
     - 章节导读
     - 基础篇-中间件与面向对象基础
       - 常见通信方式与原理

+ 5 - 1
docs/humble/chapt2/advanced/2.使用面向对象方式编写ROS2节点.md

@@ -108,4 +108,8 @@ source install/setup.bash
 ros2 run example_py node_04
 ```
 
-![image-20220604003653900](2.使用面向对象方式编写ROS2节点/imgs/image-20220604003653900.png)
+![image-20220604003653900](2.使用面向对象方式编写ROS2节点/imgs/image-20220604003653900.png)
+
+## 3.总结
+
+把节点写成一个类的形式对我们组织代码和使用ROS2的新特性有很多的好处,后面我们将以此种方式(用类建立节点)来学习后续内容。

+ 203 - 0
docs/humble/chapt3/get_started/1.ROS2话题入门.md

@@ -0,0 +1,203 @@
+# 1.ROS2话题入门
+
+话题是ROS2中最常用的通信方式之一,话题通信采取的是订阅发布模型。
+
+
+
+## 1.订阅发布模型
+
+一个节点发布数据到某个话题上,另外一个节点就可以通过订阅话题拿到数据。
+
+```mermaid
+graph LR
+A[节点1] --发布-->B[topic_name]
+B -- 订阅 --> C[节点2]
+```
+
+
+
+除了上述这种一个节点发布,一个节点接受的形式外,ROS2话题通信其实还可以是`1对n`,`n对1`,`n对n`的。
+
+1对n
+
+```mermaid
+graph LR
+A[节点1] --发布-->B[topic_name]
+B -- 订阅 --> C[节点2]
+B -- 订阅 --> D[节点3]
+B -- 订阅 --> E[节点4]
+```
+
+n对1(同一个话题可以有多个发布者)
+
+```mermaid
+graph LR
+A[节点1] --发布-->B[topic_name]
+D[节点2] --发布-->B[topic_name]
+B -- 订阅 --> C[节点2]
+```
+
+n对n
+
+```mermaid
+graph LR
+A[节点1] --发布-->B[topic_name]
+F[节点2] --发布-->B[topic_name]
+G[节点3] --发布-->B[topic_name]
+B -- 订阅 --> C[节点4]
+B -- 订阅 --> D[节点5]
+B -- 订阅 --> E[节点6]
+```
+
+还有一种就是ROS2节点可以订阅本身发布的话题
+
+```mermaid
+graph LR
+A[节点] --发布-->B[topic_name]
+B -- 订阅 --> A[节点]
+```
+
+
+
+## 2. 消息接口
+
+为了方便发送者和接收者进行数据的交换,ROS2帮我们在数据传递时做好了消息的序列化和反序列化(有关消息序列化相关内容请参考本章基础篇),而且ROS2的消息序列化与反序列化通信是可以做到跨编程语言、跨平台和跨设备之间的。
+
+ROS2如何做到跨编程语言、跨平台和跨设备之间的数据收发呢?这就得益于通过定义消息接口文件了。
+
+因为跨平台和设备实现较为复杂,小鱼这里简单说一下如何实现跨语言的。当我们定义好消息接口后,ROS2会根据消息接口内容生成不同语言的接口类,在不同编程语言中调用相同的类即可实现无感的消息序列化和反序列化。
+
+通过对消息接口介绍,相信你肯定能猜到这样一条规则:**同一个话题,所有的发布者和接收者必须使用相同消息接口**。
+
+
+
+## 3. ROS2话题工具
+
+#### 3.1 GUI工具
+
+#### 3.1.1 RQT工具之rqt_graph
+
+
+ROS2作为一个强大的工具,在运行过程中,我们是可以通过命令来看到节点和节点之间的数据关系的。
+
+运行我们第二章中的你说我听小demo。依次打开三个终端,分别输入下面三个命令。
+
+```
+ros2 run demo_nodes_py listener
+ros2 run demo_nodes_cpp talker
+rqt_graph
+```
+
+你将看到下面这张图
+
+![](1.ROS2话题入门/imgs/image-20210803113450234.png)
+
+
+> 你可以尝试改变菜单栏的Hide或者Group选项,看一看下面图的变化,感受一下rqt_graph工具的强大。
+
+这是一个很重要的工具,小鱼在学习和使用ROS2的过程中经常会用到它,来看一看数据到底是怎么走的,它可以帮我们搞清楚一个节点的输入和输出是什么。
+
+### 3.2 CLI工具
+
+还记得上一章的ros2 node指令吗?ros2也支持很多强大的topic指令。可以使用下面的指令查看。
+
+```
+ros2 topic -h
+```
+
+![image-20210803114102048](1.ROS2话题入门/imgs/image-20210803114102048.png)
+
+
+本着学以致用的目的,小鱼先对比较常用的几个命令进行介绍,其他的我们用到的时候小鱼再介绍(现在介绍反而不好理解)。
+
+#### 3.2.1 ros2 topic list 返回系统中当前活动的所有主题的列表
+
+命令
+
+```
+ros2 topic list
+```
+
+结果
+
+![image-20210803114705943](1.ROS2话题入门/imgs/image-20210803114705943.png)
+
+#### 3.2.2 ros2 topic list -t 增加消息类型
+
+命令
+
+```
+ros2 topic list -t
+```
+
+结果
+
+![image-20210803114756448](1.ROS2话题入门/imgs/image-20210803114756448.png)
+
+
+
+#### 3.2.3 ros2 topic echo <topic_name> 打印实时话题内容
+
+命令
+
+```
+ros2 topic echo /chatter
+```
+
+结果
+
+![image-20210803115124591](1.ROS2话题入门/imgs/image-20210803115124591.png)
+
+#### 3.2,4 ros2 topic info <topic_name> 查看主题信息
+
+命令
+
+```
+ros2 topic info  /chatter
+```
+
+结果
+
+![image-20210803115320265](1.ROS2话题入门/imgs/image-20210803115320265.png)
+
+#### 3.2.5 ros2 interface show 查看消息类型
+
+上面一个指令告诉大家这个消息是std_msgs/msg/String,那String里面有什么呢?不妨来试一试。
+
+命令
+
+```
+ros2 interface show std_msgs/msg/String
+```
+
+结果
+
+![image-20210803115726942](1.ROS2话题入门/imgs/image-20210803115726942.png)
+
+#### 3.2.6 ros2 topic pub <topic_name> <msg_type>  arg 手动发布命令
+
+关闭发布者,我们受到来发布
+
+命令
+
+```
+ros2 topic pub /chatter std_msgs/msg/String 'data: "123"'
+```
+
+结果
+
+![image-20210803115906116](1.ROS2话题入门/imgs/image-20210803115906116.png)
+
+
+
+## 4.最后
+
+了解完话题,下一节小鱼就会带大家来动手写一写话题通信代码。
+
+
+
+
+------
+
+参考链接:[Understanding ROS 2 topics — ROS 2 Documentation: Foxy documentation](http://docs.ros.org/en/foxy/Tutorials/Topics/Understanding-ROS2-Topics.html)
+

BIN
docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803113450234.png


BIN
docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803114102048.png


BIN
docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803114705943.png


BIN
docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803114756448.png


BIN
docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803115124591.png


BIN
docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803115320265.png


BIN
docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803115726942.png


BIN
docs/humble/chapt3/get_started/1.ROS2话题入门/imgs/image-20210803115906116.png


+ 456 - 0
docs/humble/chapt3/get_started/2.话题之RCLCPP实现.md

@@ -0,0 +1,456 @@
+# 2.话题之RCLCPP实现
+
+本节我们学习使用ROS2的RCLCPP客户端库来实现话题通信。
+
+RCLCPP为Node类提供了丰富的API接口,其中就包括创建话题发布者和创建话题订阅者。
+
+## 1.创建节点
+
+本节我们将创建一个`控制节点`和一个`被控节点`。
+
+控制节点创建一个话题发布者,发布控制命令(command)话题,接口类型为字符串(string),控制接点通过发布者发布控制命令(前进、后退、左转、右转、停止)。
+
+被控节点创建一个订阅者,订阅控制命令,收到控制命令后根据命令内容打印对应速度出来。
+
+```mermaid
+graph LR;
+A[控制节点]--command-->B[被控节点]
+```
+
+依次输入下面的命令,创建`chapt3_ws`工作空间、`example_topic_rclcpp`功能包和`topic_publisher_01.cpp`。
+
+```shell
+cd d2lros2/
+mkdir -p chapt3/chapt3_ws/src
+cd chapt3/chapt3_ws/src
+ros2 pkg create example_topic_rclcpp --build-type ament_cmake --dependencies rclcpp
+touch example_topic_rclcpp/src/topic_publisher_01.cpp
+```
+
+完成后目录结构
+
+```
+.
+└── src
+    └── example_topic_rclcpp
+        ├── CMakeLists.txt
+        ├── include
+        │   └── example_topic_rclcpp
+        ├── package.xml
+        └── src
+            └── topic_publisher_01.cpp
+
+5 directories, 3 files
+```
+
+接着采用面向对象方式写一个最简单的节点。
+
+```
+#include "rclcpp/rclcpp.hpp"
+
+class TopicPublisher01 : public rclcpp::Node
+{
+public:
+    // 构造函数,有一个参数为节点名称
+    TopicPublisher01(std::string name) : Node(name)
+    {
+        RCLCPP_INFO(this->get_logger(), "大家好,我是%s.", name.c_str());
+    }
+
+private:
+    // 声明节点
+};
+
+int main(int argc, char **argv)
+{
+    rclcpp::init(argc, argv);
+    /*产生一个的节点*/
+    auto node = std::make_shared<TopicPublisher01>("topic_publisher_01");
+    /* 运行节点,并检测退出信号*/
+    rclcpp::spin(node);
+    rclcpp::shutdown();
+    return 0;
+}
+```
+
+修改CMakeLists.txt
+
+```
+add_executable(topic_publisher_01 src/topic_publisher_01.cpp)
+ament_target_dependencies(topic_publisher_01 rclcpp)
+
+install(TARGETS
+  topic_publisher_01
+  DESTINATION lib/${PROJECT_NAME}
+)
+```
+
+接着可以编译测试下,注意运行colcon的目录。
+
+```shell
+cd chapt3/chapt3_ws/
+colcon build --packages-select example_topic_rclcpp
+source install/setup.bash
+ros2 run example_topic_rclcpp topic_publisher_01
+```
+
+![image-20220604202219516](2.话题之RCLCPP实现/imgs/image-20220604202219516.png)
+
+## 2.编写发布者
+
+### 2.1 学习使用API文档
+
+想要创建发布者,只需要调用`node`的成员函数`create_publisher`并传入对应的参数即可。
+
+有关API文档详细内容可以访问:[rclcpp: rclcpp: ROS Client Library for C++ (ros2.org)](https://docs.ros2.org/latest/api/rclcpp/)
+
+![image-20220604203349982](2.话题之RCLCPP实现/imgs/image-20220604203349982.png)
+
+打开主页,可以看到创建发布者的函数,进去即可看到参数和详细解释。
+
+![image-20220604203512857](2.话题之RCLCPP实现/imgs/image-20220604203512857.png)
+
+通过文档可以看出,我们至少需要传入消息类型(msgT)、话题名称(topic_name)和 服务指令(qos)。
+
+### 2.2 导入消息接口
+
+消息接口是ROS2通信时必须的一部分,通过消息接口ROS2才能完成消息的序列化和反序列化。ROS2为我们定义好了常用的消息接口,并生成了C++和Python的依赖文件,我们可以直接在程序中进行导入。
+
+`ament_cmake`类型功能包导入消息接口分为三步:
+
+1. 在`CMakeLists.txt`中导入,具体是先`find_packages`再`ament_target_dependencies`。
+2. 在`packages.xml`中导入,具体是添加`depend`标签并将消息接口写入。
+3. 在代码中导入,C++中是`#include"消息功能包/xxx/xxx.hpp"`。
+
+我们依次做完这三步后文件内容如下:
+
+CMakeLists.txt
+
+```
+find_package(rclcpp REQUIRED)
+find_package(std_msgs REQUIRED)
+
+add_executable(topic_publisher_01 src/topic_publisher_01.cpp)
+ament_target_dependencies(topic_publisher_01 rclcpp std_msgs)
+```
+
+packages.xml
+
+```
+  <buildtool_depend>ament_cmake</buildtool_depend>
+
+  <depend>rclcpp</depend>
+  <depend>std_msgs</depend>
+
+  <test_depend>ament_lint_auto</test_depend>
+  <test_depend>ament_lint_common</test_depend>
+```
+
+代码文件`topic_publisher_01.cpp`
+
+```
+#include "rclcpp/rclcpp.hpp"
+#include "std_msgs/msg/string.hpp"
+
+class TopicPublisher01 : public rclcpp::Node
+```
+
+### 2.3 创建发布者
+
+根据ROS2的RCLCPPAPI文档可以看出,我们需要提供消息接口、话题名称和服务质量Qos。
+
+- 消息接口上面我们已经导入了,是`std_msgs/msg/string.h`。
+- 话题名称(topic_name),我们就用`control_command`。
+- Qos,Qos支持直接指定一个数字,这个数字对应的是`KeepLast`队列长度。一般设置成10,即如果一次性有100条消息,默认保留最新的10个(90-100)其余的都扔掉。
+
+接着我们可以编写发布者的代码了。
+
+```c++
+#include "rclcpp/rclcpp.hpp"
+#include "std_msgs/msg/string.hpp"
+
+class TopicPublisher01 : public rclcpp::Node
+{
+public:
+    // 构造函数,有一个参数为节点名称
+    TopicPublisher01(std::string name) : Node(name)
+    {
+        RCLCPP_INFO(this->get_logger(), "大家好,我是%s.", name.c_str());
+        // 创建发布者
+        command_publisher_ = this->create_publisher<std_msgs::msg::String>("command", 10);
+    }
+
+private:
+    // 声明话题发布者
+    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr command_publisher_;
+};
+```
+
+### 2.4 使用定时器定时发布数据
+
+#### 2.4.1 查看定时器API
+
+虽然编写好了发布者,但是我们还没有发布数据,我们需要通过ROS2中的定时器来设置指定的周期调用回调函数,在回调函数里实现发布数据功能。
+
+> 有关回调函数和定时器相关内容请参考基础篇多线程与回调函数相关内容。
+
+再次找到RCLCPP文档,找到创建定时器函数,观察参数
+
+![image-20220605145907843](2.话题之RCLCPP实现/imgs/image-20220605145907843.png)
+
+- period,回调函数调用周期。
+- callback,回调函数。
+- group,调用回调函数所在的回调组,默认为nullptr。
+
+#### 2.4.2 编写代码
+
+```c++
+#include "rclcpp/rclcpp.hpp"
+#include "std_msgs/msg/string.hpp"
+
+class TopicPublisher01 : public rclcpp::Node
+{
+public:
+    // 构造函数,有一个参数为节点名称
+    TopicPublisher01(std::string name) : Node(name)
+    {
+        RCLCPP_INFO(this->get_logger(), "大家好,我是%s.", name.c_str());
+        // 创建发布者
+        command_publisher_ = this->create_publisher<std_msgs::msg::String>("command", 10);
+        // 创建定时器,500ms为周期,定时发布
+        timer_ = this->create_wall_timer(std::chrono::milliseconds(500), std::bind(&TopicPublisher01::timer_callback, this));
+    }
+
+private:
+    void timer_callback()
+    {
+        // 创建消息
+        std_msgs::msg::String message;
+        message.data = "forward";
+        // 日志打印
+        RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
+        // 发布消息
+        command_publisher_->publish(message);
+    }
+    // 声名定时器指针
+    rclcpp::TimerBase::SharedPtr timer_;
+    // 声明话题发布者指针
+    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr command_publisher_;
+};
+```
+
+#### 2.4.3 代码讲解
+
+**定时器**
+
+定时器是ROS2中的另外一个常用功能,通过定时器可以实现按照一定周期调用某个函数以实现定时发布等逻辑。
+
+定时器对应的类是` rclcpp::TimerBase`,调用`create_wall_timer`将返回其共享指针。
+
+创建定时器时传入了两个参数,这两个参数都利用了C++11的新特性。
+
+- `std::chrono::milliseconds(500)`,代表500ms,chrono是c++ 11中的时间库,提供计时,时钟等功能。
+- `std::bind(&TopicPublisher01::timer_callback, this)`,bind() 函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的。
+
+**创建发布消息**
+
+`std_msgs::msg::String`是通过ROS2的消息文件自动生成的类,其原始消息文件内容可以通过命令行查询
+
+```
+ros2 interface show std_msgs/msg/String 
+```
+
+结果
+
+```
+# This was originally provided as an example message.
+# It is deprecated as of Foxy
+# It is recommended to create your own semantically meaningful message.
+# However if you would like to continue using this please use the equivalent in example_msgs.
+
+string data
+```
+
+可以看到其内部包含了一个`string data`,**ROS2会将消息文件转换成一个类,并把其中的定义转换成类的成员函数。**
+
+### 2.5 运行测试
+
+编译,source,运行
+
+```
+cd chapt3/chapt3_ws/
+colcon build --packages-select example_topic_rclcpp
+source install/setup.bash
+ros2 run example_topic_rclcpp topic_publisher_01
+```
+
+测试
+
+```
+# 查看列表
+ros2 topic list
+# 输出内容
+ros2 
+```
+
+![image-20220605155135956](2.话题之RCLCPP实现/imgs/image-20220605155135956.png)
+
+## 3.编写订阅者
+
+之所以我们可以用命令行看到数据,原因在于CLI创建了一个订阅者来订阅`/command`指令。接下来我们将要手动创建一个节点订阅并处理数据。
+
+### 3.1 创建订阅节点
+
+```shell
+cd chapt3_ws/src/example_topic_rclcpp
+touch src/topic_subscribe_01.cpp
+```
+
+编写代码
+
+```c++
+#include "rclcpp/rclcpp.hpp"
+
+class TopicSubscribe01 : public rclcpp::Node
+{
+public:
+    // 构造函数,有一个参数为节点名称
+    TopicSubscribe01(std::string name) : Node(name)
+    {
+        RCLCPP_INFO(this->get_logger(), "大家好,我是%s.", name.c_str());
+    }
+
+private:
+    // 声明节点
+};
+
+int main(int argc, char **argv)
+{
+    rclcpp::init(argc, argv);
+    /*产生一个的节点*/
+    auto node = std::make_shared<TopicSubscribe01>("topic_subscribe_01");
+    /* 运行节点,并检测退出信号*/
+    rclcpp::spin(node);
+    rclcpp::shutdown();
+    return 0;
+}
+```
+
+CMakeLists.txt
+
+```cmake
+add_executable(topic_subscribe_01 src/topic_subscribe_01.cpp)
+ament_target_dependencies(topic_subscribe_01 rclcpp)
+
+install(TARGETS
+topic_subscribe_01
+  DESTINATION lib/${PROJECT_NAME}
+)
+```
+
+编译测试
+
+```shell
+cd chapt3/chapt3_ws/
+colcon build --packages-select example_topic_rclcpp
+source install/setup.bash
+ros2 run example_topic_rclcpp topic_subscribe_01
+```
+
+![image-20220605165400876](2.话题之RCLCPP实现/imgs/image-20220605165400876.png)
+
+### 3.2 查看订阅者API文档
+
+看API文档或者看头文件中关于函数的定义这是一个好习惯。
+
+![image-20220605164019985](2.话题之RCLCPP实现/imgs/image-20220605164019985.png)
+
+五个参数,但后面两个都是默认的参数,我们只需要有话题名称、Qos和回调函数即可。
+
+### 3.3 编写代码
+
+```
+#include "rclcpp/rclcpp.hpp"
+#include "std_msgs/msg/string.hpp"
+
+class TopicSubscribe01 : public rclcpp::Node
+{
+public:
+    TopicSubscribe01(std::string name) : Node(name)
+    {
+        RCLCPP_INFO(this->get_logger(), "大家好,我是%s.", name.c_str());
+          // 创建一个订阅者订阅话题
+        command_subscribe_ = this->create_subscription<std_msgs::msg::String>("sexy_girl", 10, std::bind(&TopicSubscribe01::command_callback, this, std::placeholders::_1));
+    }
+
+private:
+     // 声明一个订阅者
+    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr command_subscribe_;
+     // 收到话题数据的回调函数
+    void command_callback(const std_msgs::msg::String::SharedPtr msg)
+    {
+        double speed = 0.0f;
+        if(msg->data == "forward")
+        {
+            speed = 0.2f;
+        }
+        RCLCPP_INFO(this->get_logger(), "收到[%s]指令,发送速度 %f", msg->data.c_str(),speed);
+    };
+};
+```
+
+依然的需要在`CMakeLists.txt`添加下`std_msgs`依赖
+
+```cmake
+ament_target_dependencies(topic_subscribe_01 rclcpp std_msgs)
+```
+
+`packages.xml`就不需要了,同一个功能包,已经添加了。
+
+### 3.4 运行测试
+
+编译运行订阅节点
+
+```
+cd chapt3/chapt3_ws/
+colcon build --packages-select example_topic_rclcpp
+source install/setup.bash
+ros2 run example_topic_rclcpp topic_subscribe_01
+```
+
+手动发布数据测试订阅者
+
+```
+ros2 topic pub /command std_msgs/msg/String "{data: forward}"
+```
+
+![image-20220605170254540](2.话题之RCLCPP实现/imgs/image-20220605170254540.png)
+
+## 4.订阅发布测试
+
+关闭上面启动的终端,重新运行指令
+
+```
+cd chapt3/chapt3_ws/
+source install/setup.bash
+ros2 run example_topic_rclcpp topic_subscribe_01
+```
+
+运行发布节点
+
+```
+cd chapt3/chapt3_ws/
+source install/setup.bash
+ros2 run example_topic_rclcpp topic_publisher_01
+```
+
+运行结果
+
+![image-20220605165753271](2.话题之RCLCPP实现/imgs/image-20220605165753271.png)
+
+查看计算图`rqt`
+
+打开RQT、选择Node Graph、点击![image-20220605170035287](2.话题之RCLCPP实现/imgs/image-20220605170035287.png)刷新下
+
+![image-20220605170006221](2.话题之RCLCPP实现/imgs/image-20220605170006221.png)
+

BIN
docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220604202219516.png


BIN
docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220604203349982.png


BIN
docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220604203512857.png


BIN
docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605145907843.png


BIN
docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605155135956.png


BIN
docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605164019985.png


BIN
docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605165400876.png


BIN
docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605165753271.png


BIN
docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605170006221.png


BIN
docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605170035287.png


BIN
docs/humble/chapt3/get_started/2.话题之RCLCPP实现/imgs/image-20220605170254540.png


+ 139 - 0
docs/humble/chapt3/get_started/3.话题之RCLPY实现.md

@@ -0,0 +1,139 @@
+# 3.话题之RCLPY实现
+
+有了前面的经验,实现Python版本的发布订阅也非常的轻松了,因为ROS2的API一致性保持的很好,这点值得点赞。
+
+- [Node — rclpy 0.6.1 documentation (ros2.org)](https://docs.ros2.org/latest/api/rclpy/api/node.html)
+
+## 1.创建功能包和节点
+
+创建功能包
+
+```
+cd chapt3/chapt3_ws/src/
+ros2 pkg create example_topic_rclpy  --build-type ament_python --dependencies rclpy
+```
+
+创建节点文件
+
+```
+cd example_topic_rclpy/example_topic_rclpy
+touch topic_subscribe_02.py
+touch topic_publisher_02.py
+```
+
+简单编写下代码,依然采用类的形式
+
+发布者
+
+```
+#!/usr/bin/env python3
+import rclpy
+from rclpy.node import Node
+
+class NodePublisher02(Node):
+    def __init__(self,name):
+        super().__init__(name)
+        self.get_logger().info("大家好,我是%s!" % name)
+
+def main(args=None):
+    rclpy.init(args=args) # 初始化rclpy
+    node = NodePublisher02("topic_publisher_02")  # 新建一个节点
+    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
+    rclpy.shutdown() # 关闭rclpy
+```
+
+订阅节点
+
+```
+#!/usr/bin/env python3
+import rclpy
+from rclpy.node import Node
+
+class NodeSubscribe02(Node):
+    def __init__(self,name):
+        super().__init__(name)
+        self.get_logger().info("大家好,我是%s!" % name)
+
+def main(args=None):
+    rclpy.init(args=args) # 初始化rclpy
+    node = NodeSubscribe02("topic_subscribe_02")  # 新建一个节点
+    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
+    rclpy.shutdown() # 关闭rclpy
+```
+
+setup.py
+
+```
+    entry_points={
+        'console_scripts': [
+            "topic_publisher_02 = example_topic_rclpy.topic_publisher_02:main",
+            "topic_subscribe_02 = example_topic_rclpy.topic_subscribe_02:main"
+        ],
+    },
+```
+
+## 2.编写订阅者
+
+```
+from std_msgs.msg import String
+
+
+class NodeSubscribe02(Node):
+    def __init__(self,name):
+        super().__init__(name)
+        self.get_logger().info("大家好,我是%s!" % name)
+        # 创建订阅者
+        self.command_subscribe_ = self.create_subscription(String,"command",self.command_callback,10)
+
+    def command_callback(self,msg):
+        speed = 0.0
+        if msg.data=="backup":
+            speed = -0.2
+        self.get_logger().info(f'收到[{msg.data}]命令,发送速度{speed}')
+```
+
+## 3.编写发布者
+
+```
+from std_msgs.msg import String
+
+class NodePublisher02(Node):
+    def __init__(self,name):
+        super().__init__(name)
+        self.get_logger().info("大家好,我是%s!" % name)
+        self.command_publisher_ = self.create_publisher(String,"command", 10) 
+        self.timer = self.create_timer(0.5, self.timer_callback)
+    
+    def timer_callback(self):
+        """
+        定时器回调函数
+        """
+        msg = String()
+        msg.data = 'backup'
+        self.command_publisher_.publish(msg) 
+        self.get_logger().info(f'发布了指令:{msg.data}')    #打印一下发布的数据
+```
+
+## 4.运行测试
+
+### 4.1 发布节点
+
+```
+cd chapt3/chapt3_ws/
+source install/setup.bash
+ros2 run example_topic_rclpy topic_publisher_02
+```
+
+### 4.1 订阅节点
+
+```
+cd chapt3/chapt3_ws/
+source install/setup.bash
+ros2 run example_topic_rclpy topic_subscribe_02
+```
+
+### ![image-20220605174740555](3.话题之RCLPY实现/imgs/image-20220605174740555.png)
+
+### 4.2 RQT
+
+![image-20220605175201183](3.话题之RCLPY实现/imgs/image-20220605175201183.png)

BIN
docs/humble/chapt3/get_started/3.话题之RCLPY实现/imgs/image-20220605174740555.png


BIN
docs/humble/chapt3/get_started/3.话题之RCLPY实现/imgs/image-20220605175201183.png


+ 117 - 0
docs/humble/chapt3/get_started/4.ROS2服务入门.md

@@ -0,0 +1,117 @@
+# 4.ROS2服务入门
+
+大家好,帅鱼又蹬蹬蹬的游回来了。本节小鱼将要带大家一起了解一下什么是服务。
+
+## 1.服务通信介绍
+
+服务分为客户端和服务端,平时我们用的手机APP都可以成为客户端,而APP服务器对于软件来说就是服务端。
+
+客户端发送请求给服务端,服务端可以根据客户端的请求做一些处理,然后返回结果给客户端。
+
+```mermaid
+graph 
+A[服务端-李四] --发送响应-->B[客户端-李三]
+B[客户端]  --发送请求--> A[服务端]
+```
+
+所以服务-客户端模型,也可以成为请求-响应模型。
+
+> 不知道你有没有感觉到服务和话题的不同之处,话题是没有返回的,适用于单向或大量的数据传递。而服务是双向的,客户端发送请求,服务端响应请求。
+
+同时服务还是有一些注意事项:
+
+- 同一个服务(名称相同)有且只能有一个节点来提供
+- 同一个服务可以被多个客户端调用
+
+放两张官方形象的动图:
+
+<img src="4.ROS2服务入门/imgs/Service-SingleServiceClient.gif" alt="../../_images/Service-SingleServiceClient.gif" style="zoom: 67%;" />
+
+<img src="4.ROS2服务入门/imgs/Service-MultipleServiceClient.gif" alt="../../_images/Service-MultipleServiceClient.gif" style="zoom:67%;" />
+
+## 2.体验服务
+
+在我们安装ROS2的时候其实系统为我们安装了一些样例程序,其中就有服务使用样例,我们可以先来体验一下。
+
+### 2.1 启动服务端
+
+打开终端,运行下面的命令,这个命令用于运行一个服务节点,这个服务的功能是将两个数字相加,给定a,b两个数,返回sum也就是ab之和。
+
+```
+ros2 run examples_rclpy_minimal_service service
+```
+
+### 2.2 使用命令查看服务列表
+
+```
+ros2 service list
+```
+
+![image-20210810104048993](4.ROS2服务入门/imgs/image-20210810104048993.png)
+
+### 2.3手动调用服务
+
+再启动一个终端,输入下面的命令(注意a:、b:后的空格)。
+
+```
+ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"
+```
+
+![image-20210810113831471](4.ROS2服务入门/imgs/image-20210810113831471.png)
+
+我们可以看到客户端请求两个数字5+10,服务端返回15。
+
+## 3.ROS2服务常用命令
+
+ROS2的命令行工具,小鱼觉得还是非常值得一学的,毕竟确实很实用(装X),之前已经给大家讲过了关于节点、话题、接口相关的命令了,现在小鱼说一下关于服务的那些命令行。
+
+### 3.1查看服务列表
+
+```
+ros2 service list
+```
+
+![image-20210810115216800](4.ROS2服务入门/imgs/image-20210810115216800.png)
+
+### 3.2手动调用服务
+
+```
+ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"
+```
+
+![image-20210810115316799](4.ROS2服务入门/imgs/image-20210810115316799.png)
+
+如果不写参数值调用会怎么样?比如下面这种,大家可以尝试下。
+
+```
+ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts
+```
+
+### 3.3 查看服务接口类型
+
+```
+ros2 service type /add_two_ints
+```
+
+![image-20210810115428267](4.ROS2服务入门/imgs/image-20210810115428267.png)
+
+### 4.4查找使用某一接口的服务
+
+这个命令看起来和4.3刚好相反。
+
+```
+ros2 service find example_interfaces/srv/AddTwoInts
+```
+
+![image-20210810115552147](4.ROS2服务入门/imgs/image-20210810115552147.png)
+
+
+
+## 4.总结
+
+本节大家和小鱼一起又多认识了一个小伙伴,ROS2的服务。
+
+下一节我们将学习使用RCL在节点里创建服务端和客户端。
+
+
+

BIN
docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/Service-MultipleServiceClient.gif


BIN
docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/Service-SingleServiceClient.gif


BIN
docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810104048993.png


BIN
docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810113831471.png


BIN
docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810115216800.png


BIN
docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810115316799.png


BIN
docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810115428267.png


BIN
docs/humble/chapt3/get_started/4.ROS2服务入门/imgs/image-20210810115552147.png


+ 24 - 0
docs/humble/chapt3/章节导读.md

@@ -1,3 +1,27 @@
+# 第 3 章 ROS2节点通信之话题与服务
+
+## 1.章节介绍
+
+上一节我们对ROS2的基本概念、构建工具以及采用Python和C++调用ROS2的客户端库完成了节点的编写。 
+
+本节我们将重心放回节点和节点之间的数据传递上来,因为ROS2给我们带来的最大遍历除了调试工具就是通信机制上了。
+
+同样的,本章的内容也分为三部分。
+
+- 入门篇,主要介绍通信的原理以及编程语言的一些特性,这些其实属于计算机的基础部分知识,但在学校里可能又很难学到的部分。
+- 基础篇,该篇主要分为话题通信、服务通信和自定义通信接口上来。
+- 进阶篇,通过对DDS原生API的学习,带你深入了解ROS2的通信原理,为你深入使用ROS2打牢基础。
+
+## 2.食用方法
+
+如果你对通信的底层原理以及编程语言中多线程Lambda等不熟悉,建议你从基础章节开始学习。
+
+如果你对ROS2的话题服务API使用以及自定义接口比较熟悉可以直接跳过入门篇。
+
+
+
+## 3.章节目录
+
 - 章节导读
 - 基础篇-中间件与面向对象基础
   - 1.从底层理解通信