ソースを参照

[feat]:完成3.5-3.7

鱼香ROS 3 年 前
コミット
8fc0fce2e8
20 ファイル変更790 行追加4 行削除
  1. 363 0
      docs/humble/chapt3/get_started/5.服务之RCLCPP实现.md
  2. BIN
      docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606222615670.png
  3. BIN
      docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606224245543.png
  4. BIN
      docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606224628001.png
  5. BIN
      docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606224824684.png
  6. BIN
      docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606225411304.png
  7. BIN
      docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606225526647.png
  8. BIN
      docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606230244527.png
  9. BIN
      docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606231019702.png
  10. BIN
      docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606231650401.png
  11. 180 3
      docs/humble/chapt3/get_started/6.服务之RCLPY实现.md
  12. BIN
      docs/humble/chapt3/get_started/6.服务之RCLPY实现/imgs/image-20220606233039489.png
  13. BIN
      docs/humble/chapt3/get_started/6.服务之RCLPY实现/imgs/image-20220606233619434.png
  14. BIN
      docs/humble/chapt3/get_started/6.服务之RCLPY实现/imgs/image-20220606234546696.png
  15. 247 1
      docs/humble/chapt3/get_started/7.ROS2接口介绍.md
  16. BIN
      docs/humble/chapt3/get_started/7.ROS2接口介绍/imgs/image-20210809161135322.png
  17. BIN
      docs/humble/chapt3/get_started/7.ROS2接口介绍/imgs/image-20210809161530132.png
  18. BIN
      docs/humble/chapt3/get_started/7.ROS2接口介绍/imgs/image-20210809161840684.png
  19. BIN
      docs/humble/chapt3/get_started/7.ROS2接口介绍/imgs/image-20210809161933104.png
  20. BIN
      docs/humble/chapt3/get_started/7.ROS2接口介绍/imgs/image-20210809162247422.png

+ 363 - 0
docs/humble/chapt3/get_started/5.服务之RCLCPP实现.md

@@ -1,5 +1,9 @@
 # 5.服务之RCLCPP实现
 
+因为还没有学习如何自定义接口,所以我们先借着上一节的两数相加的示例接口,利用rclcpp提供的接口实现两数相加的服务端和客户端。
+
+## 1.创建功能包和节点
+
 ```shell
 cd chapt3/chapt3_ws/src
 ros2 pkg create example_service_rclcpp --build-type ament_cmake --dependencies rclcpp
@@ -7,9 +11,368 @@ touch example_service_rclcpp/src/service_server_01.cpp
 touch example_service_rclcpp/src/service_client_01.cpp
 ```
 
+面向对象方式写两个最简单的节点
+
+service_server_01.cpp
+
+```cpp
+#include "rclcpp/rclcpp.hpp"
+
+class ServiceServer01 : public rclcpp::Node {
+public:
+  ServiceServer01(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<ServiceServer01>("service_server_01");
+  rclcpp::spin(node);
+  rclcpp::shutdown();
+  return 0;
+}
+```
+
+service_client_01.cpp
+
+```cpp
+#include "rclcpp/rclcpp.hpp"
+
+class ServiceClient01 : public rclcpp::Node {
+public:
+  // 构造函数,有一个参数为节点名称
+  ServiceClient01(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<ServiceClient01>("service_client_01");
+  /* 运行节点,并检测退出信号*/
+  node->send_request(5, 6);
+  rclcpp::spin(node);
+  rclcpp::shutdown();
+  return 0;
+}
+```
+
+CMakeLists.txt
+
+```shell
+add_executable(service_client_01 src/service_client_01.cpp)
+ament_target_dependencies(service_client_01 rclcpp)
+
+add_executable(service_server_01 src/service_server_01.cpp)
+ament_target_dependencies(service_server_01 rclcpp)
+
+install(TARGETS
+  service_server_01
+  DESTINATION lib/${PROJECT_NAME}
+)
+
+install(TARGETS
+  service_client_01
+  DESTINATION lib/${PROJECT_NAME}
+)
+```
+
+完成上面的步骤,即可编译测试了,相信你已经对这些步骤非常熟悉了。
+
+```shell
+cd chapt3/chapt3_ws/
+colcon build --packages-select example_service_rclcpp
+
+# 运行 service_server_01
+source install/setup.bash
+ros2 run example_service_rclcpp service_server_01
+
+# 打开新终端运行  service_client_01
+source install/setup.bash
+ros2 run example_service_rclcpp service_client_01
+```
+
+## 2.服务端实现
+
+### 2.1 导入接口
+
+两数相加我们需要利用ROS2自带的`example_interfaces`接口,使用命令行可以查看这个接口的定义。
+
+```
+ros2 interface show example_interfaces/srv/AddTwoInts
+```
+
+结果
+
+```
+int64 a
+int64 b
+---
+int64 sum
+```
+
+导入接口的三个步骤不知道你是否还记得。
+
+> `ament_cmake`类型功能包导入消息接口分为三步:
+>
+> 1. 在`CMakeLists.txt`中导入,具体是先`find_packages`再`ament_target_dependencies`。
+> 2. 在`packages.xml`中导入,具体是添加`depend`标签并将消息接口写入。
+> 3. 在代码中导入,C++中是`#include"消息功能包/xxx/xxx.hpp"`。
+
+根据步骤改一下:
+
+`CMakeLists.txt`
+
+```
+# 这里我们一次性把服务端和客户端对example_interfaces的依赖都加上
+find_package(example_interfaces REQUIRED)
+
+add_executable(service_client_01 src/service_client_01.cpp)
+ament_target_dependencies(service_client_01 rclcpp example_interfaces)
+
+add_executable(service_server_01 src/service_server_01.cpp)
+ament_target_dependencies(service_server_01 rclcpp example_interfaces)
+```
+
+`packages.xml`
+
+```
+<depend>example_interfaces</depend>
+```
+
+代码
+
+```
+#include "example_interfaces/srv/add_two_ints.hpp"
+```
+
+### 2.2 编写代码
+
+先看代码再解释
+
+```c++
+#include "example_interfaces/srv/add_two_ints.hpp"
+#include "rclcpp/rclcpp.hpp"
+
+class ServiceServer01 : public rclcpp::Node {
+public:
+  ServiceServer01(std::string name) : Node(name) {
+    RCLCPP_INFO(this->get_logger(), "节点已启动:%s.", name.c_str());
+    // 创建服务
+    add_ints_server_ =
+      this->create_service<example_interfaces::srv::AddTwoInts>(
+        "add_two_ints_srv",
+        std::bind(&ServiceServer01::handle_add_two_ints, this,
+                  std::placeholders::_1, std::placeholders::_2));
+  }
+
+private:
+  // 声明一个服务
+  rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr
+    add_ints_server_;
+
+  // 收到请求的处理函数
+  void handle_add_two_ints(
+    const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request,
+    std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response) {
+    RCLCPP_INFO(this->get_logger(), "收到a: %ld b: %ld", request->a,
+                request->b);
+    response->sum = request->a + request->b;
+  };
+};
+```
+
+create_service,参考rclcpp API文档即可
+
+![image-20220606222615670](5.服务之RCLCPP实现/imgs/image-20220606222615670.png)
+
+- ServiceT,消息接口`example_interfaces::srv::AddTwoInts`
+- service_name,服务名称
+- callback,回调函数,使用成员函数作为回调函数,std::bind进行转换
+- qos_profile,服务质量配置文件,默认`rmw_qos_profile_services_default`
+- group,调用服务的回调组,默认`nullptr`
+
+### 2.3 测试
+
+```
+cd chapt3_ws/
+colcon build --packages-select example_service_rclcpp
+source install/setup.bash
+ros2 run example_service_rclcpp service_server_01
+```
+
+接着打开一个新的终端
+
+```
+# 你应该可以看到我们声明的服务
+ros2 service list
+# 使用命令行进行调用
+ros2 service call /add_two_ints_srv example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"
+```
+
+![image-20220606224245543](5.服务之RCLCPP实现/imgs/image-20220606224245543.png)
+
+## 3.客户端实现
+
+### 3.1 API接口
+
+写代码时看API文档是个好习惯,先看看创建客户端的:[地址](https://docs.ros2.org/latest/api/rclcpp/classrclcpp_1_1Node.html#aed42f345ae1de3a1979d5a8076127199)
+
+#### 3.1.1 create_client
+
+![image-20220606224628001](5.服务之RCLCPP实现/imgs/image-20220606224628001.png)
+
+参数加上ServiceT(接口类型),一共有四个,都是老熟人了,就不介绍了。
+
+#### 3.1.2 async_send_request
+
+接着我们来看看发送请求的API,[地址](https://docs.ros2.org/latest/api/rclcpp/classrclcpp_1_1Client.html#a62e48edd618bcb73538bfdc3ee3d5e63)
+
+![image-20220606224824684](5.服务之RCLCPP实现/imgs/image-20220606224824684.png)
+
+我们这里要用的是这个函数`async_send_request()`同时传入两个参数
+
+- request,请求的消息,这里用于放a,b两个数。
+- CallBack,回调函数,异步接收服务器的返回的函数。
+
+> 至于为什么ROS2中那么多回调函数,以及用回调函数的好处,小鱼这里就不解释了,不清楚的小伙伴可以看看基础篇的内容。
+
+#### 3.1.3 wait_for_service
+
+这个函数是用于等待服务上线的,这个函数并不在rclcpp::Client中定义,而是在其[父类](https://docs.ros2.org/latest/api/rclcpp/classrclcpp_1_1ClientBase.html#a8b4b432338a460ceb26a7fa6ddd59e1d)中定义的。
+
+![image-20220606225526647](5.服务之RCLCPP实现/imgs/image-20220606225526647.png)
+
+上面是继承图,在其父类中有这个函数的解释。
+
+![image-20220606225411304](5.服务之RCLCPP实现/imgs/image-20220606225411304.png)
+
+参数就一个,等待的时间,返回值是bool类型的,上线了就是true,不上线就是false。
+
+之所以会用的这个函数的原因是,再发送请求之前保证服务端启动了,避免发送一个请求出去而无人响应的尴尬局面。
+
+最后还有一些小细节,先看代码小鱼再进一步的解释。
+
+### 3.2 代码
+
+```c++
+#include "example_interfaces/srv/add_two_ints.hpp"
+
+class ServiceClient01 : public rclcpp::Node {
+public:
+  // 构造函数,有一个参数为节点名称
+  ServiceClient01(std::string name) : Node(name) {
+    RCLCPP_INFO(this->get_logger(), "节点已启动:%s.", name.c_str());
+    // 创建客户端
+    client_ = this->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints_srv");
+  }
+
+  void send_request(int a, int b) {
+    RCLCPP_INFO(this->get_logger(), "计算%d+%d", a, b);
+
+    // 1.等待服务端上线
+    while (!client_->wait_for_service(std::chrono::seconds(1))) {
+      //等待时检测rclcpp的状态
+      if (!rclcpp::ok()) {
+        RCLCPP_ERROR(this->get_logger(), "等待服务的过程中被打断...");
+        return;
+      }
+      RCLCPP_INFO(this->get_logger(), "等待服务端上线中");
+    }
+
+    // 2.构造请求的
+    auto request =
+      std::make_shared<example_interfaces::srv::AddTwoInts_Request>();
+    request->a = a;
+    request->b = b;
+
+    // 3.发送异步请求,然后等待返回,返回时调用回调函数
+    client_->async_send_request(
+      request, std::bind(&ServiceClient01::result_callback_, this,
+                         std::placeholders::_1));
+  };
+
+private:
+  // 声明客户端
+  rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client_;
+
+  void result_callback_(
+    rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture
+      result_future) {
+    auto response = result_future.get();
+    RCLCPP_INFO(this->get_logger(), "计算结果:%ld", response->sum);
+  }
+};
+```
+
+这里小鱼需要额外讲解的是回调函数`void result_callback_(rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture result_future)`
+
+这个又臭又长的参数确实让人惊了下,函数的参数是客户端`AddTwoInts`类型的`SharedFuture`对象,这个对象的定义如下
+
+![image-20220606230244527](5.服务之RCLCPP实现/imgs/image-20220606230244527.png)
+
+可以看到其又是利用C++11的新特性`std::shared_future`创建的`SharedResponse`类模板。
+
+类模板 `std::shared_future` 提供访问异步操作结果的机制,类似 [std::future](https://www.apiref.com/cpp-zh/cpp/thread/future.html) ,除了允许多个线程等候同一共享状态。
+
+我们具体看看[std::shared_future的API](https://www.apiref.com/cpp-zh/cpp/thread/shared_future.html)
+
+![image-20220606231019702](5.服务之RCLCPP实现/imgs/image-20220606231019702.png)
+
+可以看到使用`get`函数即可获取结果。所以下面这段代码的意思相信你已经大概理解了。
+
+```
+    auto response = result_future.get();
+    RCLCPP_INFO(this->get_logger(), "计算结果:%ld", response->sum);
+```
+
+### 3.3 测试
+
+最后还要修改下主函数,用于调用服务端发送请求
+
+```c++
+int main(int argc, char** argv) {
+  rclcpp::init(argc, argv);
+  /*产生一个的节点*/
+  auto node = std::make_shared<ServiceClient01>("service_client_01");
+  /* 运行节点,并检测退出信号*/
+  //增加这一行,node->send_request(5, 6);,计算5+6结果
+  node->send_request(5, 6);
+  rclcpp::spin(node);
+  rclcpp::shutdown();
+  return 0;
+}
+```
+
+接着编译运行客户端
+
 ```
+cd chapt3_ws/
+colcon build --packages-select example_service_rclcpp
+source install/setup.bash
+ros2 run example_service_rclcpp service_client_01
+```
+
+打开服务端,让服务上线
 
 ```
+source install/setup.bash
+ros2 run example_service_rclcpp service_server_01
+```
+
+![image-20220606231650401](5.服务之RCLCPP实现/imgs/image-20220606231650401.png)
+
+
+
+## 4.总结
+
+本节我们通过RCLCPP完成了服务服务端和客户端的编写,并学了一些C++语言的新特性。下一节我们学习使用rclpy实现相同的功能。
 
 
 

BIN
docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606222615670.png


BIN
docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606224245543.png


BIN
docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606224628001.png


BIN
docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606224824684.png


BIN
docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606225411304.png


BIN
docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606225526647.png


BIN
docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606230244527.png


BIN
docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606231019702.png


BIN
docs/humble/chapt3/get_started/5.服务之RCLCPP实现/imgs/image-20220606231650401.png


+ 180 - 3
docs/humble/chapt3/get_started/6.服务之RCLPY实现.md

@@ -1,16 +1,193 @@
 #  6.服务之RCLPY实现
 
-```shell
+## 1.创建功能包和节点
+
+事到如今,小鱼也不藏着掖着了,创建功能包其实还可以加上一些参数,让这个过程变得更简单。
+
+先上指令再说
+
+```
 cd chapt3/chapt3_ws/src
-ros2 pkg create example_service_rclpy --build-type ament_python --dependencies rclpy
+ros2 pkg create example_service_rclpy --build-type ament_python --dependencies rclpy example_interfaces  --node-name service_server_02
+```
+
+接着你会惊奇的发现,依赖,setup.py中的安装配置,ROS2都帮你加好了。
+
+这是因为 `--node-name service_server_02`会帮你创建好节点文件和添加执行文件。
+
+但是也有一些限制,比如只支持一个节点文件,所以我们还需要手动创建一个。
+
+```
 cd example_service_rclpy/example_service_rclpy/
-touch service_server_02.py
 touch service_client_02.py
 ```
 
+修改下setup.py
+
+```
+    entry_points={
+        'console_scripts': [
+            "service_client_02 = example_service_rclpy.service_client_02:main",
+            "service_server_02 = example_service_rclpy.service_server_02:main"
+        ],
+    },
+```
+
+接着面向对象来一筐,将两个节点的内容补充一下
+
+service_server_02
+
+```
+#!/usr/bin/env python3
+import rclpy
+from rclpy.node import Node
+
+class ServiceServer02(Node):
+    def __init__(self,name):
+        super().__init__(name)
+        self.get_logger().info("节点已启动:%s!" % name)
+        
+def main(args=None):
+    rclpy.init(args=args) # 初始化rclpy
+    node = ServiceServer02("service_server_02")  # 新建一个节点
+    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
+    rclpy.shutdown() # 关闭rclpy
+
+```
+
+service_client_02
+
+```
+#!/usr/bin/env python3
+import rclpy
+from rclpy.node import Node
+from example_interfaces.srv import AddTwoInts
+
+class ServiceClient02(Node):
+    def __init__(self,name):
+        super().__init__(name)
+        self.get_logger().info("节点已启动:%s!" % name)
+
+def main(args=None):
+    rclpy.init(args=args) # 初始化rclpy
+    node = ServiceClient02("service_client_02")  # 新建一个节点
+    node.send_request(3,4)
+    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
+    rclpy.shutdown() # 关闭rclpy
+```
+
+## 2.服务端实现
+
+### 2.1 看 API
+
+地址小鱼放这里,大家自行看下即可
+
+- [Node — rclpy 0.6.1 documentation (ros2.org)](https://docs.ros2.org/latest/api/rclpy/api/node.html)
+
+
+
+![image-20220606233039489](6.服务之RCLPY实现/imgs/image-20220606233039489.png)
+
+### 2.2 写代码
+
+```
+# 导入接口
+from example_interfaces.srv import AddTwoInts
+
+
+class ServiceServer02(Node):
+    def __init__(self,name):
+        super().__init__(name)
+        self.get_logger().info("节点已启动:%s!" % name)
+        self.add_ints_server_ = self.create_service(AddTwoInts,"add_two_ints_srv", self.handle_add_two_ints) 
+
+    def handle_add_two_ints(self,request, response):
+        self.get_logger().info(f"收到请求,计算{request.a} + {request.b}")
+        response.sum = request.a + request.b
+        return response
+```
+
+### 2.3 测试
+
+```
+colcon build --packages-select example_service_rclpy
+source install/setup.bash
+ros2 run example_service_rclpy service_server_02
+```
+
+打开新终端
+
+```
+ros2 service call /add_two_ints_srv example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"
+```
+
+![image-20220606233619434](6.服务之RCLPY实现/imgs/image-20220606233619434.png)
+
+## 3.客户端实现
+
+### 2.1 API
+
+- [Node — rclpy 0.6.1 documentation (ros2.org)](https://docs.ros2.org/latest/api/rclpy/api/node.html)
+
+### 2.2 写代码
+
+```
+from example_interfaces.srv import AddTwoInts
+
+class ServiceClient02(Node):
+    def __init__(self,name):
+        super().__init__(name)
+        self.get_logger().info("节点已启动:%s!" % name)
+        self.client_ = self.create_client(AddTwoInts,"add_two_ints_srv") 
+
+    def result_callback_(self, result_future):
+        response = result_future.result()
+        self.get_logger().info(f"收到返回结果:{response.sum}")
+    
+    def send_request(self, a, b):
+        while rclpy.ok() and self.client_.wait_for_service(1)==False:
+            self.get_logger().info(f"等待服务端上线....")
+            
+        request = AddTwoInts.Request()
+        request.a = a
+        request.b = b
+        self.client_.call_async(request).add_done_callback(self.result_callback_)
+        
+def main(args=None):
+    rclpy.init(args=args) # 初始化rclpy
+    node = ServiceClient02("service_client_02")  # 新建一个节点
+    # 调用函数发送请求
+    node.send_request(3,4)
+    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
+    rclpy.shutdown() # 关闭rclpy
+```
+
+同样是异步请求,rclpy客户端库定义的是`call_async`并且使用`add_done_callback`添加回调函数。
+
+### 2.3 测试
+
+编译启动客户端
+
+```
+colcon build --packages-select example_service_rclpy
+source install/setup.bash
+ros2 run example_service_rclpy service_client_02
+```
+
+启动服务端
+
+```
+source install/setup.bash
+ros2 run example_service_rclpy service_server_02
+```
+
+![image-20220606234546696](6.服务之RCLPY实现/imgs/image-20220606234546696.png)
 
+## 4.总结
 
+本节我们通过rclpy库实现了节点之间的服务通信。但是我们都是用别人的接口,下一节我们学习自定义接口并在代码中使用。
 
+下面两节含实战内容,一定要跟着动手写哦。
 
 --------------
 

BIN
docs/humble/chapt3/get_started/6.服务之RCLPY实现/imgs/image-20220606233039489.png


BIN
docs/humble/chapt3/get_started/6.服务之RCLPY实现/imgs/image-20220606233619434.png


BIN
docs/humble/chapt3/get_started/6.服务之RCLPY实现/imgs/image-20220606234546696.png


+ 247 - 1
docs/humble/chapt3/get_started/7.ROS2接口介绍.md

@@ -1,6 +1,252 @@
 # 7.ROS2接口介绍
 
+本节小鱼将会带你学习认识一个新的概念,叫做interface,即接口。
 
+## 1.ROS2接口介绍
+
+### 1.1 什么是接口
+
+**接口其实是一种规范**
+
+你还记得前面几节的示例中,李四和王二分别使用了下面两种数据类型来传递小说和钱,这两种数据类型分别代表字符串和32位二进制的整型数据。
+
+```
+std_msgs/msg/String
+std_msgs/msg/UInt32
+```
+
+> 李四所在的李家村是ament_python编译类型,使用的是Python语言,而王二所在的村庄的编译类型是ament_cmake,使用的是C++语言。不同语言对字符串的定义是不同的,而通过接口可以抹平这种语言差异。
+
+### 1.2 为什么使用接口
+
+小鱼举一个雷达的例子,不同的厂家生产出不同的类型的激光雷达,每种雷达驱动方式、扫描速率等等都不相同。
+
+当机器人进行导航时,需要激光雷达的扫描数据,假如没有统一接口,每次更换一个种类的雷达,都需要重新做程序适配。
+
+于是ROS2中定义了一个统一的接口叫做`sensor_msgs/msg/LaserScan`,现在几乎每个雷达的厂家都会编写程序将自己雷达的数据变成`sensor_msgs/msg/LaserScan`格式,提供给用户使用。
+
+如果雷达的例子不好理解,大家可以把雷达换成手机充电器,USB接口是不是也是一种规范,所有的厂家都按照这种接口进行充电器和连接线的生产。
+
+
+### 1.3. ROS2自带的接口
+
+前面话题通信时`std_msgs`功能包是我们安装ROS2的时候ROS2为我们自动安装的,除了`std_msgs`之外,`ROS2`还定义了很多做机器人常用的接口。
+
+> 使用`ros2 interface package sensor_msgs`命令可以查看某一个接口包下所有的接口
+
+比如:传感器类的消息包`sensor_msgs`
+
+```
+打开终端输入:ros2 interface package sensor_msgs
+sensor_msgs/msg/JointState  #机器人关节数据
+sensor_msgs/msg/Temperature #温度数据
+sensor_msgs/msg/Imu #加速度传感器
+sensor_msgs/msg/Image #图像
+sensor_msgs/msg/LaserScan #雷达数据
+......
+```
+
+虽然ROS2为我们定义了大量`有手就行,拿来就用`的接口,但有时候还是不能满足我们的变态想法,所以我们需要掌握自定义接口的方法。
+
+## 2. 接口文件内容
+
+### 2.1 可以定义的接口三种类型
+
+小鱼提到过,ROS2提供了四种通信方式:
+
+- 话题-Topics
+- 服务-Services
+- 动作-Action
+- 参数-Parameters
+
+除了参数之外,话题、服务和动作(Action)都支持自定义接口,每一种通信方式所适用的场景各不相同,所定义的接口也被分为话题接口、服务接口、动作接口三种。
+
+### 2.2 接口形式
+
+这三种接口定义起来有什么不一样的地方呢?小鱼先带大家直观感受一下:
+
+话题接口格式:`xxx.msg`
+
+```
+int64 num
+```
+
+服务接口格式:`xxx.srv`
+
+```
+int64 a
+int64 b
+---
+int64 sum
+```
+
+动作接口格式:`xxx.action`
+
+```
+int32 order
+---
+int32[] sequence
+---
+int32[] partial_sequence
+```
+
+### 2.3 接口数据类型
+
+根据引用方式不同可以分为基础类型和包装类型两类。
+
+基础类型有(同时后面加上[]可形成数组)
+
+```
+bool
+byte
+char
+float32,float64
+int8,uint8
+int16,uint16
+int32,uint32
+int64,uint64
+string
+```
+
+包装类型
+
+即在已有的接口类型上进行包含,比如
+
+```
+uint32 id
+string image_name
+sensor_msgs/Image
+```
+
+### 2.4 接口如何生成代码
+
+有的同学可能会问这样一个问题,我们只是简单的写了一下变量类型和名称,我们在程序里面怎么调用呢?
+
+其实这里有一个转换的过程:将msg、srv、action文件转换为Python和C++的头文件。
+
+```mermaid
+graph LR
+A[msg,srv,action] -->B[ROS2-IDL转换器]
+B  --> C[Python的py,C++的.h头文件]
+```
+
+通过ROS2的IDL模块 产生了头文件,有了头文件,我们就可以在程序里导入并使用这个消息模块。
+
+## 3.自定义接口实践
+
+### 3.1 场景定义
+
+给定一个机器人开发中的常见控制场景,我们设计满足要求的服务接口和话题接口。
+
+设计两个节点
+
+- 一个机器人节点,对外提供移动指定距离服务,移动完成后返回当前位置,同时对外发布机器人的位置和状态(是否在移动)。
+- 机器人控制节点,通过服务控制机器人移动指定距离,并实时获取机器人的当前位置和状态。
+
+假设机器人在坐标轴上,只能前后移动。
+
+### 3.2 定义接口
+
+服务接口`MoveRobot.srv`
+
+```
+# 前进后退的距离
+float32 distance
+---
+# 当前的位置
+float32 pose
+```
+
+话题接口,采用基础类型  `RobotStatus.msg`
+
+```
+uint32 STATUS_MOVEING = 1
+uint32 STATUS_STOP = 1
+uint32  status
+float32 pose
+```
+
+话题接口,混合包装类型 `RobotPose.msg`
+
+```shell
+uint32 STATUS_MOVEING = 1
+uint32 STATUS_STOP = 2
+uint32  status
+geometry_msgs/Pose pose
+```
+
+### 3.3 创建接口功能包编接口
+
+创建功能包
+
+```shell
+ros2 pkg create example_ros2_interfaces --build-type ament_cmake --dependencies rosidl_default_generators geometry_msgs
+```
+
+注意功能包类型必须为:ament_cmake
+
+依赖`rosidl_default_generators`必须添加,`geometry_msgs`视内容情况添加(我们这里有`geometry_msgs/Pose pose`所以要添加)。
+
+接着创建文件夹和文件将3.2中文件写入,注意话题接口放到`msg`文件夹下,以`.msg`结尾。服务接口放到`srv`文件夹下,以`srv`结尾。
+
+```
+.
+├── CMakeLists.txt
+├── msg
+│   ├── RobotPose.msg
+│   └── RobotStatus.msg
+├── package.xml
+└── srv
+    └── MoveRobot.srv
+
+2 directories, 5 files
+```
+
+接着修改`CMakeLists.txt`
+
+```cmake
+find_package(rosidl_default_generators REQUIRED)
+find_package(geometry_msgs REQUIRED)
+# 添加下面的内容
+rosidl_generate_interfaces(${PROJECT_NAME}
+  "msg/RobotPose.msg"
+  "msg/RobotStatus.msg"
+  "srv/MoveRobot.srv"
+  DEPENDENCIES geometry_msgs
+)
 ```
-ros2 pkg create example_ros2_interfaces --build-type ament_cmake --dependencies rosidl_default_generators --destination-directory src
+
+保存即可编译
+
+```shell
+colcon build --packages-select example_ros2_interfaces
 ```
+
+编译完成后在`chapt3_ws/install/example_ros2_interfaces/include`下你应该可以看到C++的头文件。在`chapt3_ws/install/example_ros2_interfaces/local/lib/python3.10/dist-packages`下应该可以看到Python版本的头文件。
+
+接下来的代码里我们就可以通过头文件导入和使用我们定义的接口了。
+
+> @TODO 讲一下为什么要source
+
+## 4.ROS2接口常用CLI命令
+
+最后给大家讲一下ROS2接口相关的常用命令有哪些。
+
+### 4.1查看接口列表
+
+```
+ros2 interface list
+```
+
+![image-20210809161530132](7.ROS2接口介绍/imgs/image-20210809161530132.png)
+
+
+
+### 4.2 查看某一个接口详细的内容
+
+```
+ros2 interface show std_msgs/msg/String
+```
+
+![image-20210809161933104](7.ROS2接口介绍/imgs/image-20210809161933104.png)
+

BIN
docs/humble/chapt3/get_started/7.ROS2接口介绍/imgs/image-20210809161135322.png


BIN
docs/humble/chapt3/get_started/7.ROS2接口介绍/imgs/image-20210809161530132.png


BIN
docs/humble/chapt3/get_started/7.ROS2接口介绍/imgs/image-20210809161840684.png


BIN
docs/humble/chapt3/get_started/7.ROS2接口介绍/imgs/image-20210809161933104.png


BIN
docs/humble/chapt3/get_started/7.ROS2接口介绍/imgs/image-20210809162247422.png