【ROS2笔记六】ROS2中自定义接口

6.ROS2中自定义接口

文章目录

在ROS2中接口interface是一种定义消息、服务或动作的规范,用于描述数据结构、字段和数据类型。ROS2中的接口可以分为以下的几种消息类型:

  1. 消息接口:消息接口定义了一种数据结构,用于在ROS 2节点之间传递信息。消息接口通常用于发布者(publishers)和订阅者(subscribers)之间的通信。消息接口由一组字段组成,每个字段都有一个名称和一个数据类型。ROS 2使用消息接口来实现发布-订阅模式。
  2. 服务接口:服务接口定义了一种客户端(client)和服务器(server)之间的通信协议。服务接口由请求(request)和响应(response)两部分组成。客户端发送请求给服务器,并等待服务器返回响应。服务接口在ROS 2中用于实现请求-响应模式。
  3. 动作接口:动作接口是ROS 2中的一种高级通信模式,它扩展了服务接口,允许在执行期间传输连续的反馈信息。动作接口由一个目标(goal)、一个反馈(feedback)和一个结果(result)组成。客户端向服务器发送目标,服务器执行相应的操作,并提供反馈信息。动作接口用于实现高级的行为控制和任务执行。

接口在ROS2中以.msg、.srv和.action文件的形式定义,分别对应消息、服务和动作。这些文件包含了接口的定义,包括字段名称和数据类型。通过使用接口,ROS2节点可以进行灵活的通信,并与其他节点共享数据和执行任务。

6.1接口常用的CLI

(1)查看接口列表

bash 复制代码
ros2 interface list

(2)查看某个具体接口的内容

bash 复制代码
ros2 interface show xxxx

6.2标准的接口形式

下面对三种接口类型.msg、.srv、.action都进行举例说明

(1)消息Message

plaintext 复制代码
int32 x
int32 y

这个消息定义了两个int32的字段x和y,

(2)服务Service

plaintext 复制代码
int32 a
int32 b
---
int32 sum

这个服务定义了一个请求包含两个整型字段a和b,以及一个响应包含一个整型字段sum。

(3)动作Action

plaintext 复制代码
int32 order
---
int32 progress
---
int32 result

这个动作定义了一个目标包含一个整型字段order,一个反馈包含一个整型字段progress,以及一个结果包含一个整型字段result。

6.3接口的数据类型

  1. 基本数据类型 :
    • 整型:int8, int16, int32, int64, uint8, uint16, uint32, uint64
    • 浮点型:float32, float64
    • 布尔型:bool
    • 字符型:char
  2. 数组和序列 :
    • 数组:使用方括号表示,例如int32[3]表示包含3个int32元素的数组。
    • 序列:使用尖括号表示,例如std_msgs/String[]表示包含多个std_msgs/String消息的序列。
  3. 字符串 :
    • 字符串类型:string表示一个字符串。
  4. 时间和持续时间 :
    • 时间:builtin_interfaces/Time表示一个时间戳。
    • 持续时间:builtin_interfaces/Duration表示一个时间间隔。
  5. 其他消息类型 :
    • 其他消息类型:你可以使用其他消息类型作为字段类型,以创建更复杂的消息结构。例如,geometry_msgs/Point表示一个三维点的消息类型。

6.4自定义接口

这里我的工作空间名为colcon_test_ws,我们首先在这个工作空间目录下创建一个新的功能包custom_interfaces

bash 复制代码
ros2 pkg create custom_interfaces --build-type ament_cmake --license Apache-2.0 --dependencies rosidl_default_generators

进入功能包,然后创建msg和srv目录

bash 复制代码
cd custom_interfaces
mkdir msg srv

目录结构如下:

bash 复制代码
.
├── CMakeLists.txt
├── include
│   └── custom_interfaces
├── LICENSE
├── msg
├── package.xml
├── src
└── srv

5 directories, 3 files

(1)自定义构建msg

进入custom_interfaces/msg新建一个Num.msg文件,然后写入以下内容:

msg 复制代码
int64 num

这里构建了一个自定义的消息,消息的内容是64整型的int

(2)自定义构建srv

进入custom_interfaces/srv新建一个AddThreeInts.srv文件,然后写入以下内容:

srv 复制代码
int64 a
int64 b
int64 c
---
int64 sum

这里构建了一个自定义的服务消息,request包含三个数a、b、c,response包含一个数sum

(3)修改配置文件CMakeLists.txt

cmake 复制代码
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/Num.msg"
  "srv/AddThreeInts.srv"
  DEPENDENCIES # Add packages that above messages depend on
)

(4)修改配置文件package.xml

xml 复制代码
  <buildtool_depend>ament_cmake</buildtool_depend>
  <!-- 添加以下三行 -->
  <build_depend>rosidl_default_generators</build_depend>
  <exec_depend>rosidl_default_runtime</exec_depend>
  <member_of_group>rosidl_interface_packages</member_of_group>  

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

然后进行编译

bash 复制代码
colcon build --packages-select example_custom_interfaces

然后查看自定义的消息接口

复制代码
source install/setup.bash
ros2 interface show example_custom_interfaces/msg/Num

Output:

bash 复制代码
int64 num

再输入:

bash 复制代码
ros2 interface show example_custom_interfaces/srv/AddThreeInts

Output:

bash 复制代码
int64 a
int64 b
int64 c
---
int64 sum

这样我们就能够在colcon_ws/install/custom_interfaces/include/example_custom_interfaces/example_custom_interfaces/msg/num.hpp看到编译好的msg头文件了,在colcon_test_ws/install/custom_interfaces/include/example_custom_interfaces/example_custom_interfaces/srv/add_three_ints.hpp中看到编译好的srv头文件

这里可以使用自定义的服务接口类型,把【ROS2笔记五】ROS2服务通信中使用的服务数据类型修改为自定义的,如下:

service_client_01.cpp

cpp 复制代码
#include "rclcpp/rclcpp.hpp"
#include "custom_interfaces/srv/add_three_ints.hpp"

class ServiceClient01: public rclcpp::Node{
public:
    ServiceClient01(std::string name) : Node(name){
        RCLCPP_INFO(this->get_logger(), "Node: %s has been launched", name.c_str());
        // 创建客户端
        client_ = this->create_client<custom_interfaces::srv::AddThreeInts>("add_two_ints_srv");
    }

    void send_request(int a, int b, int c){
        RCLCPP_INFO(this->get_logger(), "Calculate %d + %d + %d", a, b, c);

        // 等待服务上线
        while (!client_->wait_for_service(std::chrono::seconds(1))){
            if (!rclcpp::ok()){
                RCLCPP_ERROR(this->get_logger(), "Waiting for service to be interrupted");
                return;
            }
            RCLCPP_INFO(this->get_logger(), "Waiting for service");
        }

        auto request = std::make_shared<custom_interfaces::srv::AddThreeInts_Request>();
        request->a = a;
        request->b = b;
        request->c = c;

        client_->async_send_request(
            request, std::bind(&ServiceClient01::result_callback_, this, std::placeholders::_1));
    }

private:
    // 声明客户端
    rclcpp::Client<custom_interfaces::srv::AddThreeInts>::SharedPtr client_;
    void result_callback_(
        rclcpp::Client<custom_interfaces::srv::AddThreeInts>::SharedFuture result_future){
            auto response = result_future.get();
            RCLCPP_INFO(this->get_logger(), "Result: %ld", response->sum);
    }
};


int main(int argc, char** argv){
    rclcpp::init(argc, argv);
    auto node = std::make_shared<ServiceClient01>("service_client_01");
    // 调用服务
    node->send_request(5, 6, 7);
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

service_server_01.cpp

cpp 复制代码
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "custom_interfaces/srv/add_three_ints.hpp"

class ServiceServer01: public rclcpp::Node{
public:
    ServiceServer01(std::string name) : Node(name){
        RCLCPP_INFO(this->get_logger(), "Node: %s has been launched", name.c_str());
        // 创建服务
        add_ints_server_ = this->create_service<custom_interfaces::srv::AddThreeInts>(
            "add_two_ints_srv",
            std::bind(&ServiceServer01::handle_add_three_ints, this, 
                    std::placeholders::_1, std::placeholders::_2));
    }
private:
    // 在私有域中再次声明服务
    rclcpp::Service<custom_interfaces::srv::AddThreeInts>::SharedPtr add_ints_server_;

    // 服务的处理函数
    void handle_add_three_ints(
        const std::shared_ptr<custom_interfaces::srv::AddThreeInts::Request> request,
        std::shared_ptr<custom_interfaces::srv::AddThreeInts::Response> response){
        RCLCPP_INFO(this->get_logger(), "Recieve a: %ld b: %ld c: %ld", request->a, request->b, request->c);
        response->sum = request->a + request->b + request->c;
    };

};

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;
}

修改CMakeLists.txt

cmake 复制代码
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
# 新加这一行
find_package(custom_interfaces REQUIRED)  

add_executable(service_server_01 src/service_server_01.cpp)
ament_target_dependencies(service_server_01 rclcpp custom_interfaces) #修改这里

add_executable(service_client_01 src/service_client_01.cpp)
ament_target_dependencies(service_client_01 rclcpp custom_interfaces) #修改这里

install(TARGETS
  service_server_01
  service_client_01
  DESTINATION lib/${PROJECT_NAME}
)

然后编译,运行

bash 复制代码
colcon build --packages-select example_service_rclcpp
source install/setup.bash
ros2 run example_service_rclcpp service_client_01

ros2 run example_service_rclcpp service_server_01 

结果如下:

Reference

1d2lros2

2ROS2 Tutorial Official

相关推荐
Li_RuiQi19 分钟前
DAgger方法演进_独立学习笔记
笔记·学习·计算机视觉
Li_RuiQi5 小时前
DAgger与交互式模仿学习_ACT学习笔记
笔记·学习
He BianGu5 小时前
【笔记】C# 中BindingFlags的详细作用
笔记·c#
AI视觉网奇5 小时前
turtle 入门教程笔记
笔记
跨知易知识产权6 小时前
美国专利确权是什么,和专利诉讼有什么不一样?
笔记
杨逢昌工厂6S管理6 小时前
126-杨逢昌6S三环体系:越做越乱的现场熵增机理与基准化治理——一线现场诊断笔记
经验分享·笔记·职场和发展·学习方法
悟道书生6 小时前
若依 RBAC 权限控制学习笔记
java·网络·笔记·sql·学习·vue
陈哈哈11116 小时前
折腾了半天,总算把 Vim 9.2 装明白了
笔记
陈哈哈11116 小时前
折腾了半天,总算把 OpenGL 的开发环境给装明白了
笔记
深圳老胡7 小时前
STM32F103 硬件 SPI 与软件 IO 模拟 SPI 最高速度差异详解
笔记·stm32·单片机·代码规范·1024程序员节